Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: upgrade testing #17

Merged
merged 3 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions foundry/src/FoxStaking.sol → foundry/src/FoxStakingV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import {IFoxStaking, StakingInfo} from "./IFoxStaking.sol";

contract FoxStaking is
contract FoxStakingV1 is
Initializable,
PausableUpgradeable,
UUPSUpgradeable,
OwnableUpgradeable
{
using SafeERC20 for IERC20;
uint256 public version;
IERC20 public foxToken;
mapping(address => StakingInfo) public stakingInfo;
bool public stakingPaused;
Expand Down Expand Up @@ -47,7 +46,6 @@ contract FoxStaking is
__Ownable_init(msg.sender);
__UUPSUpgradeable_init();
__Pausable_init();
version = 1;
foxToken = IERC20(foxTokenAddress);
stakingPaused = false;
withdrawalsPaused = false;
Expand All @@ -59,6 +57,10 @@ contract FoxStaking is
address newImplementation
) internal override onlyOwner {}

function version() external view returns (uint256) {
return _getInitializedVersion();
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
}

function pauseStaking() external onlyOwner {
stakingPaused = true;
}
Expand Down
131 changes: 120 additions & 11 deletions foundry/test/FoxStakingTestUpgrades.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,138 @@
pragma solidity ^0.8.25;

import "forge-std/Test.sol";
import "../src/FoxStaking.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
import {FoxStakingV1} from "../src/FoxStakingV1.sol";
import {MockFOXToken} from "./MockFOXToken.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IFoxStaking, StakingInfo} from "../src/IFoxStaking.sol";

contract FOXStakingTestUpgrades is Test {
FoxStaking public foxStaking;
/// @custom:oz-upgrades-from FoxStakingV1
contract MockFoxStakingV2 is
Initializable,
PausableUpgradeable,
UUPSUpgradeable,
OwnableUpgradeable
{
using SafeERC20 for IERC20;
IERC20 public foxToken;
mapping(address => StakingInfo) public stakingInfo;
bool public stakingPaused;
bool public withdrawalsPaused;
bool public unstakingPaused;
uint256 public cooldownPeriod;

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function initialize() external reinitializer(2) {}

function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}

function version() external view returns (uint256) {
return _getInitializedVersion();
}

/// New function in v2
function newV2Function() public pure returns (string memory) {
return "new v2 function";
}
}

contract UpgradeHelper is Test {
/// @dev Wrapper to perform upgrades pranking the owner. Required to make revert reasons
/// consistent - otherwise vm.expectRevert actually reverts with a different reason when present
/// versus when not present.
/// https://github.com/foundry-rs/foundry/issues/5454
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
function doUpgrade(address prankOwner, address proxy) public {
vm.startPrank(prankOwner);
Upgrades.upgradeProxy(
proxy,
"FoxStakingTestUpgrades.t.sol:MockFoxStakingV2",
abi.encodeCall(MockFoxStakingV2.initialize, ())
);
vm.stopPrank;
}
}

contract FoxStakingTestUpgrades is Test {
address public owner = address(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045);
address public foxStakingProxy;
FoxStakingV1 public foxStakingV1;
MockFOXToken public foxToken;
address user = address(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045);
UpgradeHelper public upgradeHelper;

function setUp() public {
upgradeHelper = new UpgradeHelper();
foxToken = new MockFOXToken();
address foxStakingProxy = Upgrades.deployUUPSProxy(
"FoxStaking.sol",
abi.encodeCall(FoxStaking.initialize, (address(foxToken)))

vm.startPrank(owner);
foxStakingProxy = Upgrades.deployUUPSProxy(
"FoxStakingV1.sol",
abi.encodeCall(FoxStakingV1.initialize, (address(foxToken)))
);
foxStaking = FoxStaking(foxStakingProxy);
vm.stopPrank();

foxStakingV1 = FoxStakingV1(foxStakingProxy);
}

function testDeployerIsOwner() public view {
assertEq(Ownable(foxStakingProxy).owner(), owner);
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
}

function testCanDeploy() public view {
uint256 expectedVersion = 1;
assertEq(foxStaking.version(), expectedVersion);
function testOwnerCanUpgrade() public {
// Check the current version
uint256 expectedCurrentVersion = 1;
assertEq(foxStakingV1.version(), expectedCurrentVersion);

// Check we cannot call the new function
vm.expectRevert();
MockFoxStakingV2 fakeUpgradedFoxStakingV1 = MockFoxStakingV2(
address(foxStakingV1)
);
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
fakeUpgradedFoxStakingV1.newV2Function();

// Perform the upgrade
upgradeHelper.doUpgrade(owner, foxStakingProxy);

MockFoxStakingV2 foxStakingV2 = MockFoxStakingV2(foxStakingProxy);

// Check the new version
uint256 expectedUpgradedVersion = 2;
assertEq(foxStakingV2.version(), expectedUpgradedVersion);

// Check we can call the new function
string memory result = foxStakingV2.newV2Function();
assertEq(result, "new v2 function");
}

function testNonOwnerCannotUpgrade() public {
// Check the current version
uint256 expectedCurrentVersion = 1;
assertEq(foxStakingV1.version(), expectedCurrentVersion);

address nonOwner = address(0xBADD1E);
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved

vm.expectRevert(
abi.encodeWithSelector(
Ownable.OwnableUnauthorizedAccount.selector,
address(nonOwner)
)
);

// Perform the upgrade, but as a non-owner
gomesalexandre marked this conversation as resolved.
Show resolved Hide resolved
upgradeHelper.doUpgrade(nonOwner, foxStakingProxy);
}
}
44 changes: 22 additions & 22 deletions foundry/test/FoxStaking.t.sol → foundry/test/FoxStakingV1.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
pragma solidity ^0.8.25;

import "forge-std/Test.sol";
import "../src/FoxStaking.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";
import {FoxStakingV1} from "../src/FoxStakingV1.sol";

contract MockFOXToken is ERC20 {
constructor() ERC20("Mock FOX Token", "FOX") {
Expand All @@ -21,17 +21,17 @@ contract MockFOXToken is ERC20 {
}

contract FOXStakingTestRuneAddress is Test {
FoxStaking public foxStaking;
FoxStakingV1 public foxStaking;
MockFOXToken public foxToken;
address user = address(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045);

function setUp() public {
foxToken = new MockFOXToken();
address foxStakingProxy = Upgrades.deployUUPSProxy(
"FoxStaking.sol",
abi.encodeCall(FoxStaking.initialize, (address(foxToken)))
"FoxStakingV1.sol",
abi.encodeCall(FoxStakingV1.initialize, (address(foxToken)))
);
foxStaking = FoxStaking(foxStakingProxy);
foxStaking = FoxStakingV1(foxStakingProxy);
}

function testCanSetRuneAddress() public {
Expand Down Expand Up @@ -87,17 +87,17 @@ contract FOXStakingTestRuneAddress is Test {
}

contract FOXStakingTestOwnership is Test {
FoxStaking public foxStaking;
FoxStakingV1 public foxStaking;
MockFOXToken public foxToken;
address nonOwner = 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045;

function setUp() public {
foxToken = new MockFOXToken();
address foxStakingProxy = Upgrades.deployUUPSProxy(
"FoxStaking.sol",
abi.encodeCall(FoxStaking.initialize, (address(foxToken)))
"FoxStakingV1.sol",
abi.encodeCall(FoxStakingV1.initialize, (address(foxToken)))
);
foxStaking = FoxStaking(foxStakingProxy);
foxStaking = FoxStakingV1(foxStakingProxy);
}

function testOwnerCanUpdateCooldownPeriod() public {
Expand Down Expand Up @@ -126,16 +126,16 @@ contract FOXStakingTestOwnership is Test {
}

contract FOXStakingTestStaking is Test {
FoxStaking public foxStaking;
FoxStakingV1 public foxStaking;
MockFOXToken public foxToken;

function setUp() public {
foxToken = new MockFOXToken();
address foxStakingProxy = Upgrades.deployUUPSProxy(
"FoxStaking.sol",
abi.encodeCall(FoxStaking.initialize, (address(foxToken)))
"FoxStakingV1.sol",
abi.encodeCall(FoxStakingV1.initialize, (address(foxToken)))
);
foxStaking = FoxStaking(foxStakingProxy);
foxStaking = FoxStakingV1(foxStakingProxy);
}

function testCannotStakeWhenStakingPaused() public {
Expand Down Expand Up @@ -349,7 +349,7 @@ contract FOXStakingTestStaking is Test {
}

contract FOXStakingTestUnstake is Test {
FoxStaking public foxStaking;
FoxStakingV1 public foxStaking;
MockFOXToken public foxToken;
address user = address(0xBEEF);
uint256 amount = 1000;
Expand All @@ -359,10 +359,10 @@ contract FOXStakingTestUnstake is Test {
function setUp() public {
foxToken = new MockFOXToken();
address foxStakingProxy = Upgrades.deployUUPSProxy(
"FoxStaking.sol",
abi.encodeCall(FoxStaking.initialize, (address(foxToken)))
"FoxStakingV1.sol",
abi.encodeCall(FoxStakingV1.initialize, (address(foxToken)))
);
foxStaking = FoxStaking(foxStakingProxy);
foxStaking = FoxStakingV1(foxStakingProxy);

// Free FOX tokens for user
foxToken.makeItRain(user, amount);
Expand Down Expand Up @@ -634,7 +634,7 @@ contract FOXStakingTestUnstake is Test {
}

contract FOXStakingTestWithdraw is Test {
FoxStaking public foxStaking;
FoxStakingV1 public foxStaking;
MockFOXToken public foxToken;
address user = address(0xBEEF);
uint256 amount = 1000;
Expand All @@ -644,10 +644,10 @@ contract FOXStakingTestWithdraw is Test {
function setUp() public {
foxToken = new MockFOXToken();
address foxStakingProxy = Upgrades.deployUUPSProxy(
"FoxStaking.sol",
abi.encodeCall(FoxStaking.initialize, (address(foxToken)))
"FoxStakingV1.sol",
abi.encodeCall(FoxStakingV1.initialize, (address(foxToken)))
);
foxStaking = FoxStaking(foxStakingProxy);
foxStaking = FoxStakingV1(foxStakingProxy);

// Free FOX tokens for user
foxToken.makeItRain(user, amount);
Expand Down