Skip to content

Commit

Permalink
Merge pull request #17 from shapeshift/upgradeable-contract-3
Browse files Browse the repository at this point in the history
chore: upgrade testing
  • Loading branch information
gomesalexandre authored Apr 11, 2024
2 parents 51d7580 + a28df23 commit 14c2976
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 36 deletions.
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();
}

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
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);
}

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)
);
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);

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

// Attempt to perform the upgrade, but as a non-owner
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

0 comments on commit 14c2976

Please sign in to comment.