From 2eae21f85663ce43c734f027961c934833a4f928 Mon Sep 17 00:00:00 2001 From: princetonbishop Date: Mon, 29 Apr 2024 00:32:31 +0200 Subject: [PATCH] vote token tests --- .../templegold/IStakedTempleVoteToken.sol | 1 - .../templegold/StakedTempleVoteToken.sol | 8 +- .../templegold/StakedTempleVoteToken.t.sol | 189 ++++++++++++++++++ 3 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 protocol/test/forge/templegold/StakedTempleVoteToken.t.sol diff --git a/protocol/contracts/interfaces/templegold/IStakedTempleVoteToken.sol b/protocol/contracts/interfaces/templegold/IStakedTempleVoteToken.sol index e9e0d9736..471088c3e 100644 --- a/protocol/contracts/interfaces/templegold/IStakedTempleVoteToken.sol +++ b/protocol/contracts/interfaces/templegold/IStakedTempleVoteToken.sol @@ -7,7 +7,6 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IStakedTempleVoteToken is IERC20 { event StakingSet(address staking); event AuthoritySet(address indexed authority, bool authorized); - event DelegateSet(address indexed from, address prevDelegate, address currentDelegate); error NonTransferrable(); error NotImplemented(); diff --git a/protocol/contracts/templegold/StakedTempleVoteToken.sol b/protocol/contracts/templegold/StakedTempleVoteToken.sol index 8805001cb..50e014144 100644 --- a/protocol/contracts/templegold/StakedTempleVoteToken.sol +++ b/protocol/contracts/templegold/StakedTempleVoteToken.sol @@ -67,7 +67,7 @@ contract StakedTempleVoteToken is IStakedTempleVoteToken, TempleElevatedAccess, } /// @notice override to stop holders burning vote tokens themselves - function burn(uint256 /*amount*/) public virtual override(IStakedTempleVoteToken, ERC20Burnable) onlyAuthorized whenNotPaused { + function burn(uint256 /*amount*/) public virtual override(IStakedTempleVoteToken, ERC20Burnable) { /// @dev not implemented revert NotImplemented(); } @@ -83,7 +83,7 @@ contract StakedTempleVoteToken is IStakedTempleVoteToken, TempleElevatedAccess, * - the caller must have allowance for ``accounts``'s tokens of at least * `value`. */ - function burnFrom(address account, uint256 value) public virtual override(IStakedTempleVoteToken, ERC20Burnable) onlyAuthorized { + function burnFrom(address account, uint256 value) public virtual override(IStakedTempleVoteToken, ERC20Burnable) onlyAuthorized whenNotPaused { _burn(account, value); } @@ -198,7 +198,9 @@ contract StakedTempleVoteToken is IStakedTempleVoteToken, TempleElevatedAccess, */ function _update(address from, address to, uint256 amount) internal override { /// @notice Non-transferrable. Only by staking - if(!authorized[from] && !authorized[to]) { revert NonTransferrable(); } + if (from != address(0) && to != address(0)) { + if(!authorized[from] && !authorized[to]) { revert NonTransferrable(); } + } super._update(from, to, amount); } diff --git a/protocol/test/forge/templegold/StakedTempleVoteToken.t.sol b/protocol/test/forge/templegold/StakedTempleVoteToken.t.sol new file mode 100644 index 000000000..8c84a7fd5 --- /dev/null +++ b/protocol/test/forge/templegold/StakedTempleVoteToken.t.sol @@ -0,0 +1,189 @@ +pragma solidity 0.8.20; +// SPDX-License-Identifier: AGPL-3.0-or-later +// (tests/forge/templegold/StakedTempleVoteToken.t.sol) + + +import { TempleGoldCommon } from "./TempleGoldCommon.t.sol"; +import { StakedTempleVoteToken } from "contracts/templegold/StakedTempleVoteToken.sol"; +import { FakeERC20 } from "contracts/fakes/FakeERC20.sol"; +import { TempleGold } from "contracts/templegold/TempleGold.sol"; +import { TempleGoldStaking } from "contracts/templegold/TempleGoldStaking.sol"; +import { ITempleGold } from "contracts/interfaces/templegold/ITempleGold.sol"; +import { CommonEventsAndErrors } from "contracts/common/CommonEventsAndErrors.sol"; +import { IStakedTempleVoteToken } from "contracts/interfaces/templegold/IStakedTempleVoteToken.sol"; +import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol"; + +contract StakedTempleVoteTokenTestBase is TempleGoldCommon { + event StakingSet(address _staking); + event Paused(address account); + event Unpaused(address account); + event AuthoritySet(address indexed authority, bool authorized); + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); + + FakeERC20 public templeToken; + StakedTempleVoteToken public voteToken; + TempleGoldStaking public staking; + TempleGold public templeGold; + + string public constant NAME = "Staked Temple Vote Token"; + string public constant SYMBOL = "stTemple"; + function setUp() public { + fork("arbitrum_one", forkBlockNumber); + + ITempleGold.InitArgs memory initArgs; + initArgs.executor = executor; + initArgs.staking = address(0); + initArgs.escrow = address(0); + initArgs.gnosis = teamGnosis; + initArgs.layerZeroEndpoint = layerZeroEndpointArbitrumOne; + initArgs.mintChainId = arbitrumOneChainId; + initArgs.name = TEMPLE_GOLD_NAME; + initArgs.symbol = TEMPLE_GOLD_SYMBOL; + + templeGold = new TempleGold(initArgs); + templeToken = new FakeERC20("Temple Token", "TEMPLE", executor, 1000 ether); + staking = new TempleGoldStaking(rescuer, executor, address(templeToken), address(templeGold), address(0)); + voteToken = new StakedTempleVoteToken(rescuer, executor,address(staking), NAME, SYMBOL); + } + + function test_initialization() public { + assertEq(voteToken.rescuer(), rescuer); + assertEq(voteToken.executor(), executor); + assertEq(voteToken.staking(), address(staking)); + assertEq(voteToken.name(), NAME); + assertEq(voteToken.symbol(), SYMBOL); + } +} + +contract StakedTempleVoteTokenAccessTest is StakedTempleVoteTokenTestBase { + function test_setStaking_voteToken() public { + vm.startPrank(unauthorizedUser); + vm.expectRevert(abi.encodeWithSelector(CommonEventsAndErrors.InvalidAccess.selector)); + voteToken.setStaking(address(staking)); + } + + function test_setAuthorized_voteToken() public { + vm.startPrank(unauthorizedUser); + vm.expectRevert(abi.encodeWithSelector(CommonEventsAndErrors.InvalidAccess.selector)); + voteToken.setAuthorized(alice, true); + } +} + +contract StakedTempleVoteTokenTest is StakedTempleVoteTokenTestBase { + function test_setStaking() public { + vm.startPrank(executor); + vm.expectRevert(abi.encodeWithSelector(CommonEventsAndErrors.InvalidAddress.selector)); + voteToken.setStaking(address(0)); + + vm.expectEmit(address(voteToken)); + emit StakingSet(address(staking)); + voteToken.setStaking(address(staking)); + assertEq(voteToken.staking(), address(staking)); + } + + function test_setAuthorized() public { + vm.startPrank(executor); + vm.expectRevert(abi.encodeWithSelector(CommonEventsAndErrors.InvalidAddress.selector)); + voteToken.setAuthorized(address(0), false); + + vm.expectEmit(address(voteToken)); + emit AuthoritySet(alice, true); + voteToken.setAuthorized(alice, true); + assertEq(voteToken.authorized(alice), true); + vm.expectEmit(address(voteToken)); + emit AuthoritySet(alice, false); + voteToken.setAuthorized(alice, false); + assertEq(voteToken.authorized(alice), false); + + } + + function test_mint_voteToken() public { + vm.startPrank(executor); + vm.expectRevert(abi.encodeWithSelector(CommonEventsAndErrors.InvalidAccess.selector)); + voteToken.mint(address(0), 100 ether); + + voteToken.setAuthorized(executor, true); + voteToken.pause(); + vm.expectRevert(abi.encodeWithSelector(Pausable.EnforcedPause.selector)); + voteToken.mint(address(0), 100 ether); + + voteToken.unpause(); + vm.expectEmit(address(voteToken)); + emit Transfer(address(0), alice, 1 ether); + voteToken.mint(alice, 1 ether); + assertEq(voteToken.balanceOf(alice), 1 ether); + + vm.expectEmit(address(voteToken)); + emit Transfer(address(0), bob, 1 ether); + voteToken.mint(bob, 1 ether); + assertEq(voteToken.balanceOf(bob), 1 ether); + + vm.startPrank(alice); + vm.expectRevert(abi.encodeWithSelector(IStakedTempleVoteToken.NonTransferrable.selector)); + voteToken.transfer(bob, 1 ether); + + vm.startPrank(bob); + vm.expectRevert(abi.encodeWithSelector(IStakedTempleVoteToken.NonTransferrable.selector)); + voteToken.transfer(alice, 1 ether); + } + + function test_burn_voteToken() public { + vm.startPrank(alice); + vm.expectRevert(abi.encodeWithSelector(IStakedTempleVoteToken.NotImplemented.selector)); + voteToken.burn(1 ether); + } + + function test_burnFrom_voteToken() public { + vm.startPrank(alice); + vm.expectRevert(abi.encodeWithSelector(CommonEventsAndErrors.InvalidAccess.selector)); + voteToken.burnFrom(alice, 1 ether); + + vm.startPrank(executor); + voteToken.pause(); + voteToken.setAuthorized(executor, true); + vm.expectRevert(abi.encodeWithSelector(Pausable.EnforcedPause.selector)); + voteToken.burnFrom(alice, 1 ether); + voteToken.unpause(); + voteToken.mint(alice, 1 ether); + + vm.expectEmit(address(voteToken)); + emit Transfer(alice, address(0), 1 ether); + voteToken.burnFrom(alice, 1 ether); + assertEq(voteToken.balanceOf(alice), 0); + } + + function test_transfer_voteToken() public { + /// @dev tested in mint. + // testing not when paused + vm.startPrank(executor); + voteToken.pause(); + voteToken.setAuthorized(executor, true); + vm.expectRevert(abi.encodeWithSelector(Pausable.EnforcedPause.selector)); + voteToken.transfer(alice, 1 ether); + vm.expectRevert(abi.encodeWithSelector(Pausable.EnforcedPause.selector)); + voteToken.transferFrom(alice, bob, 1 ether); + } + + function test_approve_voteToken() public { + /// @dev testing approve when not paused + // testing not when paused + vm.startPrank(executor); + voteToken.pause(); + voteToken.setAuthorized(executor, true); + vm.expectRevert(abi.encodeWithSelector(Pausable.EnforcedPause.selector)); + voteToken.approve(alice, 1 ether); + } + + function test_pauseUnpause_voteToken() public { + /// @dev testing approve when not paused + // testing not when paused + vm.startPrank(executor); + vm.expectEmit(address(voteToken)); + emit Paused(executor); + voteToken.pause(); + vm.expectEmit(address(voteToken)); + emit Unpaused(executor); + voteToken.unpause(); + } +}