diff --git a/contracts/extension/Staking20Upgradeable.sol b/contracts/extension/Staking20Upgradeable.sol index 647f7853f..50f43355b 100644 --- a/contracts/extension/Staking20Upgradeable.sol +++ b/contracts/extension/Staking20Upgradeable.sol @@ -278,7 +278,7 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 } /// @dev Set staking conditions. - function _setStakingCondition( + function _setStakingCondition( uint80 _timeUnit, uint256 _numerator, uint256 _denominator diff --git a/lib/aa-benchmark b/lib/aa-benchmark new file mode 160000 index 000000000..4b8e548ef --- /dev/null +++ b/lib/aa-benchmark @@ -0,0 +1 @@ +Subproject commit 4b8e548ef6b004069cff599347a2afb9ac837e54 diff --git a/lib/solady b/lib/solady new file mode 160000 index 000000000..77809c18e --- /dev/null +++ b/lib/solady @@ -0,0 +1 @@ +Subproject commit 77809c18e010b914dde9518956a4ae7cb507d383 diff --git a/src/test/staking/tokenstake-BTT/depositRewardTokens/depositRewardTokens.tree b/src/test/staking/tokenstake-BTT/depositRewardTokens/depositRewardTokens.tree new file mode 100644 index 000000000..faa19175d --- /dev/null +++ b/src/test/staking/tokenstake-BTT/depositRewardTokens/depositRewardTokens.tree @@ -0,0 +1,6 @@ +function depositRewardTokens(uint256 _amount) +# when msg.sender does not have DEFAULT_ADMIN_ROLE +## it should revert +# when msg.sender does have DEFAULT_ADMIN_ROLE +## when reward token is native token +### it \ No newline at end of file diff --git a/src/test/staking/tokenstake-BTT/initialize/initialize.t.sol b/src/test/staking/tokenstake-BTT/initialize/initialize.t.sol new file mode 100644 index 000000000..72a25a770 --- /dev/null +++ b/src/test/staking/tokenstake-BTT/initialize/initialize.t.sol @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import "../../../utils/BaseTest.sol"; + +import { TWProxy } from "contracts/infra/TWProxy.sol"; +import { IStaking20 } from "contracts/extension/interface/IStaking20.sol"; + +contract TokenReturnZeroDecimals { + function decimals() public pure returns (uint8) { + return 0; + } +} + +contract TokenStakeTest_Initialize is BaseTest { + address public implementation; + address public proxy; + + address public stakingToken; + address public rewardToken; + + uint256 public rewardRatioDenominator; + uint80 public timeUnit; + + event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); + event ContractURIUpdated(string prevURI, string newURI); + + function setUp() public override { + super.setUp(); + + stakingToken = address(erc20Aux); + rewardToken = address(erc20); + + rewardRatioDenominator = 50; + timeUnit = 60; + + // Deploy implementation. + implementation = address(new TokenStake(address(weth))); + + // Deploy proxy pointing to implementaion. + vm.prank(deployer); + proxy = address( + new TWProxy( + implementation, + abi.encodeCall( + TokenStake.initialize, + ( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ) + ) + ) + ); + } + + function test_initialize_initializingImplementation() public { + vm.expectRevert("Initializable: contract is already initialized"); + TokenStake(payable(implementation)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + } + + modifier whenNotImplementation() { + _; + } + + function test_initialize_proxyAlreadyInitialized() public whenNotImplementation { + vm.expectRevert("Initializable: contract is already initialized"); + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + } + + modifier whenProxyNotInitialized() { + proxy = address(new TWProxy(implementation, "")); + _; + } + + modifier whenRewardStakingTokenSame() { + rewardToken = stakingToken; + _; + } + + function test_initialize_rewardTokenStakingTokenSame() + public + whenNotImplementation + whenProxyNotInitialized + whenRewardStakingTokenSame + { + vm.expectRevert("Reward Token and Staking Token can't be same."); + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + } + + modifier whenRewardStakingTokenNotSame() { + _; + } + + modifier whenStakingTokenZeroAddress() { + stakingToken = address(0); + _; + } + + function test_initialize_stakingTokenZeroAddress() + public + whenNotImplementation + whenProxyNotInitialized + whenRewardStakingTokenNotSame + whenStakingTokenZeroAddress + { + vm.expectRevert(); + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + } + + modifier whenStakingTokenNotZeroAddress() { + _; + } + + modifier whenStakingDecimalsEqualZero() { + stakingToken = address(new TokenReturnZeroDecimals()); + _; + } + + function test_intialize_stakingDecimalsZero() + public + whenNotImplementation + whenProxyNotInitialized + whenRewardStakingTokenNotSame + whenStakingTokenNotZeroAddress + whenStakingDecimalsEqualZero + { + vm.expectRevert("decimals 0"); + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + } + + modifier whenStakingDecimalsDoNotEqualZero() { + _; + } + + modifier whenRewardTokenDecimalsEqualZero() { + rewardToken = address(new TokenReturnZeroDecimals()); + _; + } + + function test_initialize_rewardTokenDecimalsEqualZero() + public + whenNotImplementation + whenProxyNotInitialized + whenRewardStakingTokenNotSame + whenStakingTokenNotZeroAddress + whenStakingDecimalsDoNotEqualZero + whenRewardTokenDecimalsEqualZero + { + vm.expectRevert("decimals 0"); + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + } + + modifier whenRewardTokenDecimalsNotEqualZero() { + _; + } + + modifier whenRewardRatioDenominatorEqualsZero() { + rewardRatioDenominator = 0; + _; + } + + function test_initialize_rewardRatioDenominatorEqualsZero() + public + whenNotImplementation + whenProxyNotInitialized + whenRewardStakingTokenNotSame + whenStakingTokenNotZeroAddress + whenStakingDecimalsDoNotEqualZero + whenRewardTokenDecimalsNotEqualZero + whenRewardRatioDenominatorEqualsZero + { + vm.expectRevert("divide by 0"); + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + } + + modifier whenRewardRatioDenominatorDoesNotEqualZero() { + _; + } + + modifier whenStakingTokenIsNativeToken() { + stakingToken = NATIVE_TOKEN; + _; + } + + function test_initialize_stakingTokenNativeToken() + public + whenNotImplementation + whenProxyNotInitialized + whenRewardStakingTokenNotSame + whenStakingTokenNotZeroAddress + whenStakingDecimalsDoNotEqualZero + whenRewardTokenDecimalsNotEqualZero + whenStakingTokenIsNativeToken + { + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + + TokenStake stakingContract = TokenStake(payable(proxy)); + + uint256 stakingTokenDecimals = stakingContract.stakingTokenDecimals(); + assertEq(stakingTokenDecimals, 18); + } + + modifier whenStakingTokenIsNotNativeToken() { + _; + } + + modifier whenRewardTokenIsNativeToken() { + rewardToken = NATIVE_TOKEN; + _; + } + + function test_initialize_rewardTokenNativeToken() + public + whenNotImplementation + whenProxyNotInitialized + whenRewardStakingTokenNotSame + whenStakingTokenNotZeroAddress + whenStakingDecimalsDoNotEqualZero + whenRewardTokenDecimalsNotEqualZero + whenStakingTokenIsNotNativeToken + whenRewardTokenIsNativeToken + { + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + + TokenStake stakingContract = TokenStake(payable(proxy)); + + uint256 rewardTokenDecimals = stakingContract.rewardTokenDecimals(); + assertEq(rewardTokenDecimals, 18); + } + + modifier whenRewardTokenIsNotNativeToken() { + _; + } + + modifier whenTimeUnitEqualsZero() { + timeUnit = 0; + _; + } + + function test_initialize_timeUnitEqualsZero() + public + whenNotImplementation + whenProxyNotInitialized + whenRewardStakingTokenNotSame + whenStakingTokenNotZeroAddress + whenStakingDecimalsDoNotEqualZero + whenRewardTokenDecimalsNotEqualZero + whenStakingTokenIsNotNativeToken + whenRewardTokenIsNotNativeToken + whenTimeUnitEqualsZero + { + vm.expectRevert("time-unit can't be 0"); + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + } + + modifier whenTimeUnitDoesNotEqualZero() { + _; + } + + function test_initialize() + public + whenNotImplementation + whenProxyNotInitialized + whenRewardStakingTokenNotSame + whenStakingTokenNotZeroAddress + whenStakingDecimalsDoNotEqualZero + whenRewardTokenDecimalsNotEqualZero + whenStakingTokenIsNotNativeToken + whenRewardTokenIsNotNativeToken + whenTimeUnitDoesNotEqualZero + { + bytes32 _defaultAdminRole = bytes32(0x00); + + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + + TokenStake stakingContract = TokenStake(payable(proxy)); + + address[] memory _trustedForwarders = forwarders(); + for (uint256 i = 0; i < _trustedForwarders.length; i++) { + assertTrue(stakingContract.isTrustedForwarder(_trustedForwarders[i])); + } + + assertEq(stakingContract.contractURI(), CONTRACT_URI); + + assertEq(stakingContract.rewardToken(), rewardToken); + assertEq(stakingContract.stakingToken(), stakingToken); + + assertEq(ERC20(rewardToken).decimals(), stakingContract.rewardTokenDecimals()); + assertEq(ERC20(stakingToken).decimals(), stakingContract.stakingTokenDecimals()); + + assertEq(stakingContract.getTimeUnit(), timeUnit); + + (uint256 numerator, uint256 denominator) = stakingContract.getRewardRatio(); + assertEq(numerator, 3); + assertEq(denominator, rewardRatioDenominator); + + assertEq(stakingContract.hasRole(_defaultAdminRole, deployer), true); + } + + function test_initialize_emitRoleGranted_DefaultAdmin() + public + whenNotImplementation + whenProxyNotInitialized + whenRewardStakingTokenNotSame + whenStakingTokenNotZeroAddress + whenStakingDecimalsDoNotEqualZero + whenRewardTokenDecimalsNotEqualZero + whenStakingTokenIsNotNativeToken + whenRewardTokenIsNotNativeToken + whenTimeUnitDoesNotEqualZero + { + bytes32 _defaultAdminRole = bytes32(0x00); + vm.prank(deployer); + vm.expectEmit(true, true, true, false); + emit RoleGranted(_defaultAdminRole, deployer, deployer); + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + } + + function test_initialize_emitContractURIUpdated() + public + whenNotImplementation + whenProxyNotInitialized + whenRewardStakingTokenNotSame + whenStakingTokenNotZeroAddress + whenStakingDecimalsDoNotEqualZero + whenRewardTokenDecimalsNotEqualZero + whenStakingTokenIsNotNativeToken + whenRewardTokenIsNotNativeToken + whenTimeUnitDoesNotEqualZero + { + vm.expectEmit(true, true, true, false); + emit ContractURIUpdated("", CONTRACT_URI); + TokenStake(payable(proxy)).initialize( + deployer, + CONTRACT_URI, + forwarders(), + rewardToken, + stakingToken, + timeUnit, + 3, + rewardRatioDenominator + ); + } +} diff --git a/src/test/staking/tokenstake-BTT/initialize/initialize.tree b/src/test/staking/tokenstake-BTT/initialize/initialize.tree new file mode 100644 index 000000000..9cd35bd7b --- /dev/null +++ b/src/test/staking/tokenstake-BTT/initialize/initialize.tree @@ -0,0 +1,46 @@ +function initialize( + address _defaultAdmin, + string memory _contractURI, + address[] memory _trustedForwarders, + address _rewardToken, + address _stakingToken, + uint80 _timeUnit, + uint256 _rewardRatioNumerator, + uint256 _rewardRatioDenominator +) +├── when _trustedForwarders.length > 0 +│ └── it should set _trustedForwarder[_trustedForwarders[i]] as true for each address in _trustedForwarders ✅ +├── when _rewardToken is the same as _stakingtoken +│ └── it should revert ✅ +└── when _rewardToken is not the same as _stakingtoken + ├── it should set rewardToken as _rewardToken ✅ + ├── when _stakingToken is equal to address(0) + │ └── it should revert ✅ + └── when _stakingToken is not equal to address(0) + ├── when _stakingTokenDecimals is equal to 0 + │ └── it should revert ✅ + └── when _stakingTokenDecimals is not equal to 0 + ├── when _rewardsTokenDecimals is equal to 0 + │ └── it should revert ✅ + └── when _rewardTokenDecimals is not equal to 0 + ├── it should set stakingToken as _stakingtoken ✅ + ├── when _stakingToken is equal to nativeToken + │ └── it should set staking token decimals as 18 ✅ + ├── when _stakingToken is not equal to nativeToken + │ └── it should set staking token decimals as _stakingToken.decimals() ✅ + ├── when _rewardToken is equal to nativeToken + │ └── it should set reward token decimals as 18 ✅ + ├── when _rewardToken is not equal to nativeToken + │ └── it should set reward token decimals to _rewardtoken.decimals() ✅ + ├── when _rewardRatioDenominator is equal to zero + │ └── it should revert ✅ + └── when _rewardRatioDenominator is not equal to zero + ├── when _timeUnit is equal to zero + │ └── it should revert ✅ + └── when _timeUnit is not equal to zero + ├── it should increment nextConditionId by 1 + ├── it should set stakingConditions[0] as a StakingCondition struct with params: _timeUnit, _numerator, _denominator, block.timestamp, 0 ✅ + ├── it should set contractURI as _contractURI ✅ + ├── it should emit ContractURIUpdated with the parameters: prevURI, _uri ✅ + ├── it should assign the role DEFAULT_ADMIN_ROLE to _defaultAdmin ✅ + └── it should emit RoleGranted with the parameters: DEFAULT_ADMIN_ROLE, _defaultAdmin, msg.sender ✅ \ No newline at end of file diff --git a/src/test/staking/tokenstake-BTT/misc/misc.t.sol b/src/test/staking/tokenstake-BTT/misc/misc.t.sol new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/staking/tokenstake-BTT/misc/misc.tree b/src/test/staking/tokenstake-BTT/misc/misc.tree new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/staking/tokenstake-BTT/withdrawRewardTokens/withdrawRewardTokens.tree b/src/test/staking/tokenstake-BTT/withdrawRewardTokens/withdrawRewardTokens.tree new file mode 100644 index 000000000..e69de29bb