From 891d152da3cd781d1adf245eff0ff2b3f9bdd63e Mon Sep 17 00:00:00 2001 From: Pote <81638931+willpote@users.noreply.github.com> Date: Tue, 30 Sep 2025 11:49:51 -0500 Subject: [PATCH 1/5] Gnosis support for upgrade staking --- foundry.toml | 2 +- script/Update.s.sol | 3 -- script/Upgrade.s.sol | 92 +++++++++++++++++++++++++++++++------------- 3 files changed, 67 insertions(+), 30 deletions(-) diff --git a/foundry.toml b/foundry.toml index a48a131..941852f 100644 --- a/foundry.toml +++ b/foundry.toml @@ -42,7 +42,7 @@ fs_permissions = [ # Invariant testing configuration [invariant] -runs = 50 # Faster execution while maintaining good coverage +runs = 50 # Faster execution while maintaining good coverage depth = 50 # Sufficient depth for complex state evolution fail_on_revert = false # Don't fail on individual reverts call_override = false # Use default call behavior diff --git a/script/Update.s.sol b/script/Update.s.sol index 533c347..372d26d 100644 --- a/script/Update.s.sol +++ b/script/Update.s.sol @@ -1121,7 +1121,6 @@ contract RemoveAdminAll is BaseDeployment { console2.log(""); // ZKC - ZKC zkcContract = ZKC(config.zkc); bytes32 zkcAdminRole = zkcContract.ADMIN_ROLE(); bytes memory zkcRevokeRoleCallData = abi.encodeWithSignature("revokeRole(bytes32,address)", zkcAdminRole, adminToRemove); @@ -1137,7 +1136,6 @@ contract RemoveAdminAll is BaseDeployment { console2.log(""); // veZKC - veZKC veZKCContract = veZKC(config.veZKC); bytes32 veZKCAdminRole = veZKCContract.ADMIN_ROLE(); bytes memory veZKCRevokeRoleCallData = abi.encodeWithSignature("revokeRole(bytes32,address)", veZKCAdminRole, adminToRemove); @@ -1153,7 +1151,6 @@ contract RemoveAdminAll is BaseDeployment { console2.log(""); // StakingRewards - StakingRewards stakingRewardsContract = StakingRewards(config.stakingRewards); bytes32 stakingAdminRole = stakingRewardsContract.ADMIN_ROLE(); bytes memory stakingRevokeRoleCallData = abi.encodeWithSignature("revokeRole(bytes32,address)", stakingAdminRole, adminToRemove); diff --git a/script/Upgrade.s.sol b/script/Upgrade.s.sol index 769daa2..bb89f98 100644 --- a/script/Upgrade.s.sol +++ b/script/Upgrade.s.sol @@ -427,9 +427,8 @@ contract UpgradeStakingRewards is BaseDeployment { (DeploymentConfig memory config, string memory deploymentKey) = ConfigLoader.loadDeploymentConfig(vm); require(config.stakingRewards != address(0), "StakingRewards not deployed"); - vm.startBroadcast(); - - // Check for skip safety checks flag + // Check for deployment mode flags + bool gnosisExecute = vm.envOr("GNOSIS_EXECUTE", false); bool skipSafetyChecks = vm.envOr("SKIP_SAFETY_CHECKS", false); // Prepare upgrade options with reference contract @@ -449,38 +448,79 @@ contract UpgradeStakingRewards is BaseDeployment { console2.log("Using reference build directory: ", referenceBuildDir); } - console2.log("Upgrading StakingRewards at: ", config.stakingRewards); address currentImpl = _getImplementationAddress(config.stakingRewards); - console2.log("Current implementation: ", currentImpl); + address newImpl; - // Perform safe upgrade - Upgrades.upgradeProxy( - config.stakingRewards, - "StakingRewards.sol:StakingRewards", - "", // No reinitializer - opts - ); + if (gnosisExecute) { + console2.log("GNOSIS_EXECUTE=true: Deploying new implementation for Safe upgrade"); + console2.log("Target proxy address: ", config.stakingRewards); + console2.log("Current implementation: ", currentImpl); - address newImpl = Upgrades.getImplementationAddress(config.stakingRewards); - console2.log("Upgraded StakingRewards implementation to: ", newImpl); + vm.startBroadcast(); - vm.stopBroadcast(); + // Use prepareUpgrade for validation + deployment + newImpl = Upgrades.prepareUpgrade("StakingRewards.sol:StakingRewards", opts); + console2.log("New implementation deployed: ", newImpl); + + vm.stopBroadcast(); + + // Print Gnosis Safe transaction info + bytes memory callData = abi.encodeWithSignature("upgradeTo(address)", newImpl); + console2.log("================================"); + console2.log("================================"); + console2.log("=== GNOSIS SAFE UPGRADE INFO ==="); + console2.log("Target Address (To): ", config.stakingRewards); + console2.log("Function: upgradeTo(address)"); + console2.log("New Implementation: ", newImpl); + console2.log("Calldata:"); + console2.logBytes(callData); + console2.log(""); + console2.log("Expected Events on Successful Execution:"); + console2.log("1. Upgraded(address indexed implementation)"); + console2.log(" - implementation: ", newImpl); + console2.log("================================"); + } else { + console2.log("Upgrading StakingRewards at: ", config.stakingRewards); + console2.log("Current implementation: ", currentImpl); + + vm.startBroadcast(); + + // Perform safe upgrade + Upgrades.upgradeProxy( + config.stakingRewards, + "StakingRewards.sol:StakingRewards", + "", // No reinitializer + opts + ); + + newImpl = Upgrades.getImplementationAddress(config.stakingRewards); + console2.log("Upgraded StakingRewards implementation to: ", newImpl); + + vm.stopBroadcast(); + } // Update deployment.toml with new implementation and store previous _updateDeploymentConfig(deploymentKey, "staking-rewards-impl-prev", currentImpl); _updateDeploymentConfig(deploymentKey, "staking-rewards-impl", newImpl); _updateStakingRewardsCommit(deploymentKey); - // Verify upgrade - StakingRewards stakingRewardsContract = StakingRewards(config.stakingRewards); - console2.log("Proxy still points to StakingRewards: ", address(stakingRewardsContract) == config.stakingRewards); - console2.log("Implementation updated: ", newImpl != config.stakingRewardsImpl); - console2.log("ZKC configured: ", address(stakingRewardsContract.zkc())); - console2.log("veZKC configured: ", address(stakingRewardsContract.veZKC())); - console2.log("ZKC token still configured: ", address(stakingRewardsContract.zkc()) == config.zkc); - console2.log("veZKC still configured: ", address(stakingRewardsContract.veZKC()) == config.veZKC); - console2.log("================================================"); - console2.log("StakingRewards Upgrade Complete"); - console2.log("New Implementation: ", newImpl); + // Verify results + if (gnosisExecute) { + console2.log("================================================"); + console2.log("StakingRewards Implementation Deployment Complete"); + console2.log("New Implementation: ", newImpl); + console2.log("Proxy NOT upgraded - use Gnosis Safe to complete upgrade"); + } else { + StakingRewards stakingRewardsContract = StakingRewards(config.stakingRewards); + console2.log("Proxy still points to StakingRewards: ", address(stakingRewardsContract) == config.stakingRewards); + console2.log("Implementation updated: ", newImpl != config.stakingRewardsImpl); + console2.log("ZKC configured: ", address(stakingRewardsContract.zkc())); + console2.log("veZKC configured: ", address(stakingRewardsContract.veZKC())); + console2.log("ZKC token still configured: ", address(stakingRewardsContract.zkc()) == config.zkc); + console2.log("veZKC still configured: ", address(stakingRewardsContract.veZKC()) == config.veZKC); + console2.log("================================================"); + console2.log("StakingRewards Upgrade Complete"); + console2.log("New Implementation: ", newImpl); + } } } From bac0f29681cd021250ffa54af5a212412e4cf5dc Mon Sep 17 00:00:00 2001 From: Pote <81638931+willpote@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:56:13 -0500 Subject: [PATCH 2/5] Gnosis support for upgrading staking contract --- deployment.toml | 6 ++--- script/BaseDeployment.s.sol | 36 +++++++++++++++++++++++++ script/Upgrade.s.sol | 53 +------------------------------------ 3 files changed, 40 insertions(+), 55 deletions(-) diff --git a/deployment.toml b/deployment.toml index 446c847..b17e921 100644 --- a/deployment.toml +++ b/deployment.toml @@ -80,15 +80,15 @@ vezkc = "0xb7c63161da06ec56840465308e24c70df65abc56" vezkc-impl = "0xd2ba61a24ef93c97db0117899d27a91829406dac" vezkc-deployer = "0x210173ceff92264c565229e0c9ce02df4c1ba5ad" staking-rewards = "0x881fe38501d869bdb8d747a3506562c73413cabe" -staking-rewards-impl = "0x6d3e2c923b65e5da16b4b8f89f573bb111e618c2" +staking-rewards-impl = "0x70ad46dea8fc9be6bb9de10c5d9d371feb8d4751" staking-rewards-deployer = "0x210173ceff92264c565229e0c9ce02df4c1ba5ad" zkc-commit = "99f8f78" vezkc-commit = "f79a4bb" -staking-rewards-commit = "f79a4bb" +staking-rewards-commit = "891d152" povw-minter = "0x8c2a4a28116ed8b8fe559bc53e30b6a056c5d3c9" staking-minter = "0x881fe38501d869bdb8d747a3506562c73413cabe" vezkc-impl-prev = "0x0fb7eb63e1c1de4e6b4c30e07ece3595f74dbffe" -staking-rewards-impl-prev = "0xc4ad34bd30ce917d94d89c18a800dc45a9774e79" +staking-rewards-impl-prev = "0x6d3e2c923b65e5da16b4b8f89f573bb111e618c2" zkc-impl-prev = "0xa7e703df0575ea226f01a5d1494a97c4cfafbb34" ################################################################################# diff --git a/script/BaseDeployment.s.sol b/script/BaseDeployment.s.sol index 5af6601..268813e 100644 --- a/script/BaseDeployment.s.sol +++ b/script/BaseDeployment.s.sol @@ -176,4 +176,40 @@ abstract contract BaseDeployment is Script { size := extcodesize(addr) } } + + /// @notice Print Gnosis Safe transaction information for manual upgrades + /// @param proxyAddress The proxy contract address (target for Gnosis Safe) + /// @param newImpl The new implementation address + /// @param initializerData The initializer call data (if any) + function _printGnosisSafeInfo(address proxyAddress, address newImpl, bytes memory initializerData) internal pure { + console2.log("================================"); + console2.log("================================"); + console2.log("=== GNOSIS SAFE UPGRADE INFO ==="); + console2.log("Target Address (To): ", proxyAddress); + + if (initializerData.length > 0) { + bytes memory callData = abi.encodeWithSignature("upgradeToAndCall(address,bytes)", newImpl, initializerData); + console2.log("Function: upgradeToAndCall(address,bytes)"); + console2.log("New Implementation: ", newImpl); + console2.log("Calldata:"); + console2.logBytes(callData); + console2.log(""); + console2.log("Expected Events on Successful Execution:"); + console2.log("1. Upgraded(address indexed implementation)"); + console2.log(" - implementation: ", newImpl); + console2.log("2. Initialized(uint8 version)"); + console2.log(" - version: depends on initializer function"); + } else { + bytes memory callData = abi.encodeWithSignature("upgradeTo(address)", newImpl); + console2.log("Function: upgradeTo(address)"); + console2.log("New Implementation: ", newImpl); + console2.log("Calldata:"); + console2.logBytes(callData); + console2.log(""); + console2.log("Expected Events on Successful Execution:"); + console2.log("1. Upgraded(address indexed implementation)"); + console2.log(" - implementation: ", newImpl); + } + console2.log("================================"); + } } diff --git a/script/Upgrade.s.sol b/script/Upgrade.s.sol index bb89f98..2e23119 100644 --- a/script/Upgrade.s.sol +++ b/script/Upgrade.s.sol @@ -100,44 +100,6 @@ abstract contract BaseZKCUpgrade is BaseDeployment { return newImpl; } - - /// @notice Print Gnosis Safe transaction information for manual upgrades - /// @param proxyAddress The proxy contract address (target for Gnosis Safe) - /// @param newImpl The new implementation address - /// @param initializerData The initializer call data (if any) - function _printGnosisSafeInfo(address proxyAddress, address newImpl, bytes memory initializerData) internal pure { - console2.log("================================"); - console2.log("================================"); - console2.log("=== GNOSIS SAFE UPGRADE INFO ==="); - console2.log("Target Address (To): ", proxyAddress); - - if (initializerData.length > 0) { - // For upgradeToAndCall - bytes memory callData = abi.encodeWithSignature("upgradeToAndCall(address,bytes)", newImpl, initializerData); - console2.log("Function: upgradeToAndCall(address,bytes)"); - console2.log("New Implementation: ", newImpl); - console2.log("Calldata:"); - console2.logBytes(callData); - console2.log(""); - console2.log("Expected Events on Successful Execution:"); - console2.log("1. Upgraded(address indexed implementation)"); - console2.log(" - implementation: ", newImpl); - console2.log("2. Initialized(uint8 version)"); - console2.log(" - version: depends on initializer function"); - } else { - // For upgradeTo - bytes memory callData = abi.encodeWithSignature("upgradeTo(address)", newImpl); - console2.log("Function: upgradeTo(address)"); - console2.log("New Implementation: ", newImpl); - console2.log("Calldata:"); - console2.logBytes(callData); - console2.log(""); - console2.log("Expected Events on Successful Execution:"); - console2.log("1. Upgraded(address indexed implementation)"); - console2.log(" - implementation: ", newImpl); - } - console2.log("================================"); - } } contract UpgradeZKC is BaseZKCUpgrade { @@ -465,20 +427,7 @@ contract UpgradeStakingRewards is BaseDeployment { vm.stopBroadcast(); // Print Gnosis Safe transaction info - bytes memory callData = abi.encodeWithSignature("upgradeTo(address)", newImpl); - console2.log("================================"); - console2.log("================================"); - console2.log("=== GNOSIS SAFE UPGRADE INFO ==="); - console2.log("Target Address (To): ", config.stakingRewards); - console2.log("Function: upgradeTo(address)"); - console2.log("New Implementation: ", newImpl); - console2.log("Calldata:"); - console2.logBytes(callData); - console2.log(""); - console2.log("Expected Events on Successful Execution:"); - console2.log("1. Upgraded(address indexed implementation)"); - console2.log(" - implementation: ", newImpl); - console2.log("================================"); + _printGnosisSafeInfo(config.stakingRewards, newImpl, ""); } else { console2.log("Upgrading StakingRewards at: ", config.stakingRewards); console2.log("Current implementation: ", currentImpl); From 98b3abf35f9bf47a80f6ec559160c8ee3f462373 Mon Sep 17 00:00:00 2001 From: Pote <81638931+willpote@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:44:39 -0500 Subject: [PATCH 3/5] Upgrade Staking contract for value recipient --- deployment.toml | 16 ++++++++-------- script/BaseDeployment.s.sol | 38 ++++++++++++++++++------------------- script/Ops.s.sol | 2 +- script/Update.s.sol | 2 +- 4 files changed, 28 insertions(+), 30 deletions(-) diff --git a/deployment.toml b/deployment.toml index b17e921..94659e5 100644 --- a/deployment.toml +++ b/deployment.toml @@ -20,8 +20,8 @@ vezkc-impl = "0xd40ffc5910aa6e520c9fca68424a17404040af06" vezkc-impl-prev = "" vezkc-deployer = "0xb13573c6ceb505a7bdd4fa3ad7b473c5c5d36b19" staking-rewards = "0x459d87d54808fac136ddcf439fcc1d8a238311c7" -staking-rewards-impl = "0x8e65304a2379ca08689c5046c00ff9d53cf73320" -staking-rewards-impl-prev = "0x2d57bc44e3c1fe4ceec8f2bf9ea0ac954b527e43" +staking-rewards-impl = "0x6bfe251c37f17c53d1c02db6c9bbc28bf8c50c11" +staking-rewards-impl-prev = "0x8e65304a2379ca08689c5046c00ff9d53cf73320" staking-rewards-deployer = "0xb13573c6ceb505a7bdd4fa3ad7b473c5c5d36b19" povw-minter = "0xbfce7c2d5e7eddeab71b3eeed770713c8b755397" staking-minter = "0x459d87d54808fac136ddcf439fcc1d8a238311c7" @@ -31,7 +31,7 @@ circulating-zkc-admin = "0xb13573c6ceb505a7bdd4fa3ad7b473c5c5d36b19" circulating-zkc-commit = "9d0b6a9b541d3b6980893b3385d33d12b173f232" zkc-commit = "5b8310b" vezkc-commit = "caa29df" -staking-rewards-commit = "24e6b5a" +staking-rewards-commit = "bac0f29" [deployment.ethereum-sepolia] name = "Ethereum Sepolia" @@ -51,14 +51,14 @@ vezkc-impl = "0x9990fa62cd1abda69e21f94f325640bdd61c6ef3" vezkc-impl-prev = "0x31c72d15741a9b6e1c0531248a3610a11c943264" vezkc-deployer = "0xb13573c6ceb505a7bdd4fa3ad7b473c5c5d36b19" staking-rewards = "0x226439b7750bc0770fbeeedc619e6cb258aa656a" -staking-rewards-impl = "0xf5584355d8259e4272413df05c0e7c8e822e0dd7" -staking-rewards-impl-prev = "0x1ff6f81ea7f5e6feafaafb0fc5983576096ded1d" +staking-rewards-impl = "0xb0bfe179450972f5cd6e8821d6f98bdf8b91f127" +staking-rewards-impl-prev = "0xf5584355d8259e4272413df05c0e7c8e822e0dd7" staking-rewards-deployer = "0xb13573c6ceb505a7bdd4fa3ad7b473c5c5d36b19" povw-minter = "0xc98218aafe225035a34795bf4f6777b7d541e326" staking-minter = "0x226439b7750bc0770fbeeedc619e6cb258aa656a" zkc-commit = "99f8f78" vezkc-commit = "99f8f78" -staking-rewards-commit = "a38381d" +staking-rewards-commit = "bac0f29" ################################################################################# # STAGING # @@ -80,11 +80,11 @@ vezkc = "0xb7c63161da06ec56840465308e24c70df65abc56" vezkc-impl = "0xd2ba61a24ef93c97db0117899d27a91829406dac" vezkc-deployer = "0x210173ceff92264c565229e0c9ce02df4c1ba5ad" staking-rewards = "0x881fe38501d869bdb8d747a3506562c73413cabe" -staking-rewards-impl = "0x70ad46dea8fc9be6bb9de10c5d9d371feb8d4751" +staking-rewards-impl = "0x80595f523e17045fd3ceee28174a5b8677150204" staking-rewards-deployer = "0x210173ceff92264c565229e0c9ce02df4c1ba5ad" zkc-commit = "99f8f78" vezkc-commit = "f79a4bb" -staking-rewards-commit = "891d152" +staking-rewards-commit = "bac0f29" povw-minter = "0x8c2a4a28116ed8b8fe559bc53e30b6a056c5d3c9" staking-minter = "0x881fe38501d869bdb8d747a3506562c73413cabe" vezkc-impl-prev = "0x0fb7eb63e1c1de4e6b4c30e07ece3595f74dbffe" diff --git a/script/BaseDeployment.s.sol b/script/BaseDeployment.s.sol index 268813e..118dfe0 100644 --- a/script/BaseDeployment.s.sol +++ b/script/BaseDeployment.s.sol @@ -187,29 +187,27 @@ abstract contract BaseDeployment is Script { console2.log("=== GNOSIS SAFE UPGRADE INFO ==="); console2.log("Target Address (To): ", proxyAddress); + bytes memory callData = abi.encodeWithSignature("upgradeToAndCall(address,bytes)", newImpl, initializerData); + console2.log("Function: upgradeToAndCall(address,bytes)"); + console2.log("New Implementation: ", newImpl); + + if (initializerData.length > 0) { + console2.log("Initializer Data:"); + console2.logBytes(initializerData); + } else { + console2.log("Initializer Data: 0x (empty - no function call)"); + } + + console2.log("Calldata:"); + console2.logBytes(callData); + console2.log(""); + console2.log("Expected Events on Successful Execution:"); + console2.log("1. Upgraded(address indexed implementation)"); + console2.log(" - implementation: ", newImpl); + if (initializerData.length > 0) { - bytes memory callData = abi.encodeWithSignature("upgradeToAndCall(address,bytes)", newImpl, initializerData); - console2.log("Function: upgradeToAndCall(address,bytes)"); - console2.log("New Implementation: ", newImpl); - console2.log("Calldata:"); - console2.logBytes(callData); - console2.log(""); - console2.log("Expected Events on Successful Execution:"); - console2.log("1. Upgraded(address indexed implementation)"); - console2.log(" - implementation: ", newImpl); console2.log("2. Initialized(uint8 version)"); console2.log(" - version: depends on initializer function"); - } else { - bytes memory callData = abi.encodeWithSignature("upgradeTo(address)", newImpl); - console2.log("Function: upgradeTo(address)"); - console2.log("New Implementation: ", newImpl); - console2.log("Calldata:"); - console2.logBytes(callData); - console2.log(""); - console2.log("Expected Events on Successful Execution:"); - console2.log("1. Upgraded(address indexed implementation)"); - console2.log(" - implementation: ", newImpl); } - console2.log("================================"); } } diff --git a/script/Ops.s.sol b/script/Ops.s.sol index 5cb79a2..1024c55 100644 --- a/script/Ops.s.sol +++ b/script/Ops.s.sol @@ -27,7 +27,7 @@ contract Dev_InitialMintToSelf is BaseDeployment { function run() public { vm.startBroadcast(); - (DeploymentConfig memory config, string memory deploymentKey) = ConfigLoader.loadDeploymentConfig(vm); + (DeploymentConfig memory config,) = ConfigLoader.loadDeploymentConfig(vm); require(config.zkc != address(0), "ZKC address not set in deployment.toml"); uint256 mintAmount = vm.envUint("MINT_AMOUNT"); diff --git a/script/Update.s.sol b/script/Update.s.sol index 372d26d..d6c839e 100644 --- a/script/Update.s.sol +++ b/script/Update.s.sol @@ -221,7 +221,7 @@ contract UpdateCirculatingUnlocked is BaseDeployment { function setUp() public {} function run() public { - (DeploymentConfig memory config, string memory deploymentKey) = ConfigLoader.loadDeploymentConfig(vm); + (DeploymentConfig memory config,) = ConfigLoader.loadDeploymentConfig(vm); require(config.circulatingZKC != address(0), "CirculatingZKC address not set in deployment.toml"); // Get new unlocked value from environment From c8d0a74bb72ab204c770a3d7e74724d41649b994 Mon Sep 17 00:00:00 2001 From: Pote <81638931+willpote@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:44:48 -0500 Subject: [PATCH 4/5] Fmt --- script/Upgrade.s.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/script/Upgrade.s.sol b/script/Upgrade.s.sol index 2e23119..8eeea9f 100644 --- a/script/Upgrade.s.sol +++ b/script/Upgrade.s.sol @@ -461,7 +461,9 @@ contract UpgradeStakingRewards is BaseDeployment { console2.log("Proxy NOT upgraded - use Gnosis Safe to complete upgrade"); } else { StakingRewards stakingRewardsContract = StakingRewards(config.stakingRewards); - console2.log("Proxy still points to StakingRewards: ", address(stakingRewardsContract) == config.stakingRewards); + console2.log( + "Proxy still points to StakingRewards: ", address(stakingRewardsContract) == config.stakingRewards + ); console2.log("Implementation updated: ", newImpl != config.stakingRewardsImpl); console2.log("ZKC configured: ", address(stakingRewardsContract.zkc())); console2.log("veZKC configured: ", address(stakingRewardsContract.veZKC())); From 1bbdd150fed8ed23a255e7ed3701cf0a2b289cce Mon Sep 17 00:00:00 2001 From: Pote <81638931+willpote@users.noreply.github.com> Date: Tue, 30 Sep 2025 19:36:19 -0500 Subject: [PATCH 5/5] Upgrade StakingRewards --- deployment.toml | 3 +- test-deployment/DeploymentTest.t.sol | 103 ++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/deployment.toml b/deployment.toml index 94659e5..10ac5d5 100644 --- a/deployment.toml +++ b/deployment.toml @@ -77,7 +77,7 @@ zkc = "0x99a52662b576f4b2d4ffbc4504331a624a7b2846" zkc-impl = "0xbd1f65405e7cd9972fd7ae81635532a6000d018f" zkc-deployer = "0x210173ceff92264c565229e0c9ce02df4c1ba5ad" vezkc = "0xb7c63161da06ec56840465308e24c70df65abc56" -vezkc-impl = "0xd2ba61a24ef93c97db0117899d27a91829406dac" +vezkc-impl = "0x0fb7eb63e1c1De4e6B4c30E07eCe3595f74DBFfe" vezkc-deployer = "0x210173ceff92264c565229e0c9ce02df4c1ba5ad" staking-rewards = "0x881fe38501d869bdb8d747a3506562c73413cabe" staking-rewards-impl = "0x80595f523e17045fd3ceee28174a5b8677150204" @@ -87,7 +87,6 @@ vezkc-commit = "f79a4bb" staking-rewards-commit = "bac0f29" povw-minter = "0x8c2a4a28116ed8b8fe559bc53e30b6a056c5d3c9" staking-minter = "0x881fe38501d869bdb8d747a3506562c73413cabe" -vezkc-impl-prev = "0x0fb7eb63e1c1de4e6b4c30e07ece3595f74dbffe" staking-rewards-impl-prev = "0x6d3e2c923b65e5da16b4b8f89f573bb111e618c2" zkc-impl-prev = "0xa7e703df0575ea226f01a5d1494a97c4cfafbb34" diff --git a/test-deployment/DeploymentTest.t.sol b/test-deployment/DeploymentTest.t.sol index 3bb06bb..ce4ea05 100644 --- a/test-deployment/DeploymentTest.t.sol +++ b/test-deployment/DeploymentTest.t.sol @@ -373,11 +373,11 @@ contract DeploymentTest is Test { function _getProxyImplementation(address proxy) internal view returns (address impl) { // ERC1967 implementation slot: keccak256("eip1967.proxy.implementation") - 1 bytes32 slot = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - + // Use vm.load to read the storage slot from the proxy contract bytes32 data = VM.load(proxy, slot); impl = address(uint160(uint256(data))); - + // If still zero, try calling implementation() function if (impl == address(0)) { (bool success, bytes memory returnData) = proxy.staticcall( @@ -388,4 +388,103 @@ contract DeploymentTest is Test { } } } + + function testStakeAndClaimWithValueRecipient() external { + require(deployment.zkc != address(0), "ZKC must be deployed"); + require(deployment.veZKC != address(0), "veZKC must be deployed"); + require(deployment.stakingRewards != address(0), "StakingRewards must be deployed"); + + // Find a ZKC whale by checking the ZKC admin address (which should have tokens) + address whale = deployment.zkcAdmin; + if (whale == address(0)) { + whale = deployment.zkcAdmin2; + } + require(whale != address(0), "Could not find whale address"); + + uint256 whaleBalance = zkc.balanceOf(whale); + address[3] memory whales = [deployment.zkcAdmin, address(0xb13573C6CEB505A7BDD4Fa3AD7b473c5c5d36b19), address(0x28C6c06298d514Db089934071355E5743bf21d60)]; + for (uint256 i = 0; i < whales.length; i++) { + if (whales[i] != address(0)) { + whale = whales[i]; + whaleBalance = zkc.balanceOf(whale); + if (whaleBalance > 0) { + break; + } + } + } + require(whaleBalance > 0, "Whale must have ZKC tokens"); + + // Use 10% of whale's balance for staking (or 100k ZKC, whichever is smaller) + uint256 stakeAmount = whaleBalance / 10; + if (stakeAmount > 100_000e18) { + stakeAmount = 100_000e18; + } + require(stakeAmount > 0, "Stake amount must be positive"); + + // Impersonate the whale + vm.startPrank(whale); + + // Approve veZKC to spend ZKC + zkc.approve(deployment.veZKC, stakeAmount); + + // Stake tokens + uint256 tokenId = veZKCContract.stake(stakeAmount); + console2.log("Staked with token ID:", tokenId); + + vm.stopPrank(); + + // Get current epoch before warping + uint256 currentEpoch = zkc.getCurrentEpoch(); + console2.log("Current epoch:", currentEpoch); + + // Warp forward past the current epoch (2 days + 1 hour to be safe) + vm.warp(block.timestamp + 2 days + 1 hours); + + uint256 newEpoch = zkc.getCurrentEpoch(); + console2.log("New epoch after warp:", newEpoch); + require(newEpoch > currentEpoch, "Must advance to next epoch"); + + // Set up a value recipient address + address valueRecipient = address(0xBEEF); + uint256 recipientBalanceBefore = zkc.balanceOf(valueRecipient); + console2.log("Value recipient balance before claim:", recipientBalanceBefore); + + // Claim rewards to the value recipient + vm.startPrank(whale); + uint256[] memory epochs = new uint256[](1); + epochs[0] = currentEpoch; + + uint256 claimedAmount = stakingRewards.claimRewardsToRecipient(epochs, valueRecipient); + console2.log("Claimed amount:", claimedAmount); + + vm.stopPrank(); + + // Verify rewards were sent to value recipient + uint256 recipientBalanceAfter = zkc.balanceOf(valueRecipient); + console2.log("Value recipient balance after claim:", recipientBalanceAfter); + + assertEq(recipientBalanceAfter - recipientBalanceBefore, claimedAmount, "Recipient should receive claimed rewards"); + assertTrue(claimedAmount > 0, "Should have claimed some rewards"); + + console2.log("Successfully claimed", claimedAmount, "ZKC to value recipient"); + + // Attempt to claim again for the same epoch - should revert with AlreadyClaimed + vm.startPrank(whale); + vm.expectRevert(abi.encodeWithSignature("AlreadyClaimed(uint256)", currentEpoch)); + stakingRewards.claimRewardsToRecipient(epochs, valueRecipient); + vm.stopPrank(); + + // Verify that attempting to claim to a different recipient also fails + address differentRecipient = address(0xCAFE); + vm.startPrank(whale); + vm.expectRevert(abi.encodeWithSignature("AlreadyClaimed(uint256)", currentEpoch)); + stakingRewards.claimRewardsToRecipient(epochs, differentRecipient); + vm.stopPrank(); + + // Verify balances haven't changed after failed claim attempts + assertEq(zkc.balanceOf(valueRecipient), recipientBalanceAfter, "Value recipient balance should not change after failed claim"); + assertEq(zkc.balanceOf(differentRecipient), 0, "Different recipient should have zero balance"); + + console2.log("Verified that double-claiming is prevented"); + } } \ No newline at end of file