From e923bcc67815cd8e02f6b17e17ef335298bd1e07 Mon Sep 17 00:00:00 2001 From: benk10 Date: Fri, 22 Dec 2023 19:59:25 -0300 Subject: [PATCH] Remove HAT bounty and use vault token for fee --- config.js | 50 +- contracts/HATClaimsManager.sol | 90 +- contracts/HATTimelockController.sol | 19 - contracts/HATVaultsRegistry.sol | 209 +- contracts/interfaces/IHATClaimsManager.sol | 38 +- contracts/interfaces/IHATVaultsRegistry.sol | 164 +- contracts/mocks/HatVaultForConnectorMock.sol | 6 +- contracts/mocks/ISwapRouter.sol | 67 - contracts/mocks/PoolsManagerMock.sol | 1 + contracts/mocks/UniSwapV3RouterMock.sol | 86 - deploy/008_deploy_hatvaultsregistry.js | 22 +- deploy/011_verify_deployment.js | 28 +- docs/dodoc/HATClaimsManager.md | 71 +- docs/dodoc/HATTimelockController.md | 21 - docs/dodoc/HATVaultsRegistry.md | 315 +-- docs/dodoc/interfaces/IHATClaimsManager.md | 68 +- docs/dodoc/interfaces/IHATVaultsRegistry.md | 266 +-- docs/dodoc/mocks/HATClaimsManagerV2Mock.md | 71 +- docs/dodoc/mocks/HatVaultForConnectorMock.md | 5 +- docs/dodoc/mocks/ISwapRouter.md | 120 - docs/dodoc/mocks/UniSwapV3RouterMock.md | 104 - scripts/deployments/config.json | 12 +- scripts/deployments/hats-verify.js | 13 +- .../deployments/hatvaultsregistry-deploy.js | 13 +- test/common.js | 21 +- test/hattimelockcontroller.js | 134 +- test/hatvaults.js | 2066 +++-------------- 27 files changed, 744 insertions(+), 3336 deletions(-) delete mode 100644 contracts/mocks/ISwapRouter.sol delete mode 100644 contracts/mocks/UniSwapV3RouterMock.sol delete mode 100644 docs/dodoc/mocks/ISwapRouter.md delete mode 100644 docs/dodoc/mocks/UniSwapV3RouterMock.md diff --git a/config.js b/config.js index ff31df2f..0978d874 100644 --- a/config.js +++ b/config.js @@ -14,9 +14,7 @@ module.exports = { "rewardControllersConf": [], "hatToken": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F", // USDC "hatVaultsRegistryConf": { - "swapToken": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F", // USDC - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "0" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -39,9 +37,7 @@ module.exports = { "rewardControllersConf": [], "hatToken": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F", // USDC "hatVaultsRegistryConf": { - "swapToken": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F", // USDC - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "0" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -86,8 +82,7 @@ module.exports = { ] }], "hatVaultsRegistryConf": { - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "500" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -129,9 +124,7 @@ module.exports = { "rewardToken": "HATToken" }], "hatVaultsRegistryConf": { - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "500", - "swapToken": "HATToken" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -146,8 +139,7 @@ module.exports = { "rewardControllersConf": [], // no reward controllers "hatToken": "", // deploy a fresh HATToken contract "hatVaultsRegistryConf": { - "bountyGovernanceHAT": "0", - "bountyHackerHATVested": "0" + "governanceFee": "1000" } }, "polygon": { @@ -165,9 +157,7 @@ module.exports = { "rewardControllersConf": [], "hatToken": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // USDC "hatVaultsRegistryConf": { - "swapToken": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // USDC - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "0" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -190,9 +180,7 @@ module.exports = { "rewardControllersConf": [], "hatToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC "hatVaultsRegistryConf": { - "swapToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "0" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -215,9 +203,7 @@ module.exports = { "rewardControllersConf": [], "hatToken": "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", // USDC "hatVaultsRegistryConf": { - "swapToken": "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", // USDC - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "0" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -240,9 +226,7 @@ module.exports = { "rewardControllersConf": [], "hatToken": "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", // USDC "hatVaultsRegistryConf": { - "swapToken": "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", // USDC - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "0" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -265,9 +249,7 @@ module.exports = { "rewardControllersConf": [], "hatToken": "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", // USDC "hatVaultsRegistryConf": { - "swapToken": "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", // USDC - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "0" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -290,9 +272,7 @@ module.exports = { "rewardControllersConf": [], "hatToken": "0x4200000000000000000000000000000000000006", // WETH "hatVaultsRegistryConf": { - "swapToken": "0x4200000000000000000000000000000000000006", // WETH - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "0" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -315,9 +295,7 @@ module.exports = { "rewardControllersConf": [], "hatToken": "NEED ADDRESS", // USDC "hatVaultsRegistryConf": { - "swapToken": "NEED ADDRESS", // USDC - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "0" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -340,9 +318,7 @@ module.exports = { "rewardControllersConf": [], "hatToken": "0xd86e243fc0007e6226b07c9a50c9d70d78299eb5", // USDC "hatVaultsRegistryConf": { - "swapToken": "0xd86e243fc0007e6226b07c9a50c9d70d78299eb5", // USDC - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "0" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", diff --git a/contracts/HATClaimsManager.sol b/contracts/HATClaimsManager.sol index 072a4412..dc794642 100644 --- a/contracts/HATClaimsManager.sol +++ b/contracts/HATClaimsManager.sol @@ -63,10 +63,8 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu PendingMaxBounty public pendingMaxBounty; - // the percentage of the total bounty to be swapped to HATs and sent to governance (out of {HUNDRED_PERCENT}) - uint16 internal bountyGovernanceHAT; - // the percentage of the total bounty to be swapped to HATs and sent to the hacker via vesting contract (out of {HUNDRED_PERCENT}) - uint16 internal bountyHackerHATVested; + // the fee percentage of the total bounty to be paid to the governance + uint16 internal governanceFee; // address of the arbitrator - which can dispute claims and override the committee's decisions address internal arbitrator; @@ -122,11 +120,14 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu function initialize(IHATVault _vault, IHATClaimsManager.ClaimsManagerInitParams calldata _params) external initializer { if (_params.maxBounty > MAX_BOUNTY_LIMIT && _params.maxBounty != HUNDRED_PERCENT) revert MaxBountyCannotBeMoreThanMaxBountyLimit(); + HATVaultsRegistry _registry = HATVaultsRegistry(msg.sender); + if (_params.governanceFee > _registry.MAX_GOVERNANCE_FEE() && _params.governanceFee != NULL_UINT16) + revert FeeCannotBeMoreThanMaxFee(); _validateSplit(_params.bountySplit); _setVestingParams(_params.vestingDuration, _params.vestingPeriods); - HATVaultsRegistry _registry = HATVaultsRegistry(msg.sender); maxBounty = _params.maxBounty; bountySplit = _params.bountySplit; + governanceFee = _params.governanceFee; committee = _params.committee; registry = _registry; vault = _vault; @@ -140,8 +141,6 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu isTokenLockRevocable = _params.isTokenLockRevocable; // Set vault to use default registry values where applicable - bountyGovernanceHAT = NULL_UINT16; - bountyHackerHATVested = NULL_UINT16; challengePeriod = NULL_UINT32; challengeTimeOutPeriod = NULL_UINT32; } @@ -176,8 +175,7 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu // solhint-disable-next-line not-rely-on-time createdAt: uint32(block.timestamp), challengedAt: 0, - bountyGovernanceHAT: getBountyGovernanceHAT(), - bountyHackerHATVested: getBountyHackerHATVested(), + governanceFee: getGovernanceFee(), arbitrator: arbitratorAddress, challengePeriod: getChallengePeriod(), challengeTimeOutPeriod: getChallengeTimeOutPeriod(), @@ -255,17 +253,12 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu address tokenLock; - IHATClaimsManager.ClaimBounty memory claimBounty = _calcClaimBounty( - _claim.bountyPercentage, - _claim.bountyGovernanceHAT, - _claim.bountyHackerHATVested - ); + IHATClaimsManager.ClaimBounty memory claimBounty = _calcClaimBounty(_claim.bountyPercentage, _claim.governanceFee); vault.makePayout( claimBounty.committee + - claimBounty.governanceHat + + claimBounty.governanceFee + claimBounty.hacker + - claimBounty.hackerHatVested + claimBounty.hackerVested ); @@ -293,19 +286,7 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu _asset.safeTransfer(_claim.beneficiary, claimBounty.hacker); _asset.safeTransfer(_claim.committee, claimBounty.committee); - // send to the registry the amount of tokens which should be swapped - // to HAT so it could call swapAndSend in a separate tx. - IHATVaultsRegistry _registry = registry; - _asset.safeApprove(address(_registry), claimBounty.hackerHatVested + claimBounty.governanceHat); - _registry.addTokensToSwap( - _asset, - _claim.beneficiary, - claimBounty.hackerHatVested, - claimBounty.governanceHat - ); - - // make sure to reset approval - _asset.safeApprove(address(_registry), 0); + _asset.safeTransfer(registry.governanceFeeReceiver(), claimBounty.governanceFee); emit ApproveClaim( _claimId, @@ -400,14 +381,15 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu emit SetMaxBounty(_maxBounty); } - /** @notice See {IHATClaimsManager-setHATBountySplit}. */ - function setHATBountySplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) external onlyRegistryOwner { - bountyGovernanceHAT = _bountyGovernanceHAT; - bountyHackerHATVested = _bountyHackerHATVested; + /** @notice See {IHATClaimsManager-setGoveranceFee}. */ + function setGovernanceFee(uint16 _governanceFee) external onlyRegistryOwner { + if (_governanceFee > registry.MAX_GOVERNANCE_FEE() && _governanceFee != NULL_UINT16) { + revert FeeCannotBeMoreThanMaxFee(); + } - registry.validateHATSplit(getBountyGovernanceHAT(), getBountyHackerHATVested()); + governanceFee = _governanceFee; - emit SetHATBountySplit(_bountyGovernanceHAT, _bountyHackerHATVested); + emit SetGovernanceFee(_governanceFee); } /** @notice See {IHATClaimsManager-setArbitrator}. */ @@ -458,23 +440,13 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu /* --------------------------------- Getters -------------------------------------- */ - /** @notice See {IHATClaimsManager-getBountyGovernanceHAT}. */ - function getBountyGovernanceHAT() public view returns(uint16) { - uint16 _bountyGovernanceHAT = bountyGovernanceHAT; - if (_bountyGovernanceHAT != NULL_UINT16) { - return _bountyGovernanceHAT; - } else { - return registry.defaultBountyGovernanceHAT(); - } - } - - /** @notice See {IHATClaimsManager-getBountyHackerHATVested}. */ - function getBountyHackerHATVested() public view returns(uint16) { - uint16 _bountyHackerHATVested = bountyHackerHATVested; - if (_bountyHackerHATVested != NULL_UINT16) { - return _bountyHackerHATVested; + /** @notice See {IHATClaimsManager-getGovernanceFee}. */ + function getGovernanceFee() public view returns(uint16) { + uint16 _getGovernanceFee = governanceFee; + if (_getGovernanceFee != NULL_UINT16) { + return _getGovernanceFee; } else { - return registry.defaultBountyHackerHATVested(); + return registry.defaultGovernanceFee(); } } @@ -531,14 +503,12 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu * predefined bounty split and the given bounty percentage * @param _bountyPercentage The percentage of the vault's funds to be paid * out as bounty - * @param _bountyGovernanceHAT The bountyGovernanceHAT at the time the claim was submitted - * @param _bountyHackerHATVested The bountyHackerHATVested at the time the claim was submitted + * @param _governanceFee The governanceFee at the time the claim was submitted * @return claimBounty The bounty distribution for this specific claim */ function _calcClaimBounty( - uint256 _bountyPercentage, - uint256 _bountyGovernanceHAT, - uint256 _bountyHackerHATVested + uint16 _bountyPercentage, + uint16 _governanceFee ) internal view returns(IHATClaimsManager.ClaimBounty memory claimBounty) { uint256 _totalAssets = vault.totalAssets(); if (_totalAssets == 0) { @@ -551,13 +521,11 @@ contract HATClaimsManager is IHATClaimsManager, OwnableUpgradeable, ReentrancyGu uint256 _totalBountyAmount = _totalAssets * _bountyPercentage; - uint256 _governanceHatAmount = _totalBountyAmount.mulDiv(_bountyGovernanceHAT, HUNDRED_PERCENT_SQRD); - uint256 _hackerHatVestedAmount = _totalBountyAmount.mulDiv(_bountyHackerHATVested, HUNDRED_PERCENT_SQRD); + uint256 _governanceFeeAmount = _totalBountyAmount.mulDiv(_governanceFee, HUNDRED_PERCENT_SQRD); - _totalBountyAmount -= (_governanceHatAmount + _hackerHatVestedAmount) * HUNDRED_PERCENT; + _totalBountyAmount -= _governanceFeeAmount * HUNDRED_PERCENT; - claimBounty.governanceHat = _governanceHatAmount; - claimBounty.hackerHatVested = _hackerHatVestedAmount; + claimBounty.governanceFee = _governanceFeeAmount; uint256 _hackerVestedAmount = _totalBountyAmount.mulDiv(bountySplit.hackerVested, HUNDRED_PERCENT_SQRD); uint256 _hackerAmount = _totalBountyAmount.mulDiv(bountySplit.hacker, HUNDRED_PERCENT_SQRD); diff --git a/contracts/HATTimelockController.sol b/contracts/HATTimelockController.sol index 0b9ffa5f..09e0f3ea 100644 --- a/contracts/HATTimelockController.sol +++ b/contracts/HATTimelockController.sol @@ -46,25 +46,6 @@ contract HATTimelockController is TimelockController { _rewardController.setAllocPoint(address(_vault), _allocPoint); } - function swapAndSend( - IHATVaultsRegistry _registry, - address _asset, - address[] calldata _beneficiaries, - uint256 _amountOutMinimum, - address _routingContract, - bytes calldata _routingPayload - ) - external - onlyRole(PROPOSER_ROLE) { - _registry.swapAndSend( - _asset, - _beneficiaries, - _amountOutMinimum, - _routingContract, - _routingPayload - ); - } - function setEmergencyPaused(IHATVaultsRegistry _registry, bool _isEmergencyPaused) external onlyRole(PROPOSER_ROLE) { _registry.setEmergencyPaused(_isEmergencyPaused); } diff --git a/contracts/HATVaultsRegistry.sol b/contracts/HATVaultsRegistry.sol index c5e3a0cd..798ca36e 100644 --- a/contracts/HATVaultsRegistry.sol +++ b/contracts/HATVaultsRegistry.sol @@ -38,21 +38,9 @@ contract HATVaultsRegistry is IHATVaultsRegistry, Ownable { using SafeERC20 for IERC20; using Math for uint256; - // Used in {swapAndSend} to avoid a "stack too deep" error - struct SwapData { - uint256 amount; - uint256 amountUnused; - uint256 hatsReceived; - uint256 totalHackerReward; - uint256 governanceAmountSwapped; - uint256[] hackerRewards; - uint256 governanceHatReward; - uint256 usedPart; - } - uint16 public constant HUNDRED_PERCENT = 10000; - // the maximum percentage of the bounty that will be converted in HATs - uint16 public constant MAX_HAT_SPLIT = 2000; + // the maximum percentage of the fees + uint16 public constant MAX_GOVERNANCE_FEE = 3500; address public hatVaultImplementation; address public hatClaimsManagerImplementation; @@ -60,29 +48,19 @@ contract HATVaultsRegistry is IHATVaultsRegistry, Ownable { // vault address => is visible mapping(address => bool) public isVaultVisible; - // asset => hacker address => amount - mapping(address => mapping(address => uint256)) public hackersHatReward; - // asset => amount - mapping(address => uint256) public governanceHatReward; // PARAMETERS FOR ALL VAULTS IHATVaultsRegistry.GeneralParameters public generalParameters; ITokenLockFactory public immutable tokenLockFactory; - - // the token into which a part of the the bounty will be swapped into - IERC20 public HAT; // feeSetter sets the withdrawal fee address public feeSetter; - // How the bountyGovernanceHAT and bountyHackerHATVested set how to divide the hats - // bounties of the vault, in percentages (out of `HUNDRED_PERCENT`) - // The precentages are taken from the total bounty + // feeReceiver recives the governance fees from the vaults payouts + address public governanceFeeReceiver; - // the default percentage of the total bounty to be swapped to HATs and sent to governance - uint16 public defaultBountyGovernanceHAT; - // the default percentage of the total bounty to be swapped to HATs and sent to the hacker via vesting contract - uint16 public defaultBountyHackerHATVested; + // the default fee percentage out of the total bounty + uint16 public defaultGovernanceFee; address public defaultArbitrator; @@ -95,13 +73,8 @@ contract HATVaultsRegistry is IHATVaultsRegistry, Ownable { * @param _hatVaultImplementation The hat vault implementation address. * @param _hatClaimsManagerImplementation The hat claims manager implementation address. * @param _hatGovernance The governance address. - * @param _HAT the HAT token address - * @param _bountyGovernanceHAT The default percentage of a claim's total - * bounty to be swapped for HAT and sent to the governance - * @param _bountyHackerHATVested The default percentage of a claim's total - * bounty to be swapped for HAT and sent to a vesting contract for the hacker - * _bountyGovernanceHAT + _bountyHackerHATVested must be less - * than `HUNDRED_PERCENT`. + * @param _governanceFee The default percentage of a claim's total + * bounty to paid as fee. Must be less than `MAX_GOVERNANCE_FEE`. * @param _tokenLockFactory Address of the token lock factory to be used * to create a vesting contract for the approved claim reporter. */ @@ -110,17 +83,17 @@ contract HATVaultsRegistry is IHATVaultsRegistry, Ownable { address _hatClaimsManagerImplementation, address _hatGovernance, address _defaultArbitrator, - address _HAT, - uint16 _bountyGovernanceHAT, - uint16 _bountyHackerHATVested, + uint16 _governanceFee, + address _governanceFeeReceiver, ITokenLockFactory _tokenLockFactory ) { _transferOwnership(_hatGovernance); hatVaultImplementation = _hatVaultImplementation; hatClaimsManagerImplementation = _hatClaimsManagerImplementation; - HAT = IERC20(_HAT); - validateHATSplit(_bountyGovernanceHAT, _bountyHackerHATVested); + if (_governanceFee > MAX_GOVERNANCE_FEE) { + revert FeeCannotBeMoreThanMaxFee(); + } tokenLockFactory = _tokenLockFactory; generalParameters = IHATVaultsRegistry.GeneralParameters({ hatVestingDuration: 90 days, @@ -133,19 +106,18 @@ contract HATVaultsRegistry is IHATVaultsRegistry, Ownable { claimFee: 0 }); - defaultBountyGovernanceHAT = _bountyGovernanceHAT; - defaultBountyHackerHATVested = _bountyHackerHATVested; + defaultGovernanceFee = _governanceFee; + governanceFeeReceiver = _governanceFeeReceiver; defaultArbitrator = _defaultArbitrator; defaultChallengePeriod = 3 days; defaultChallengeTimeOutPeriod = 125 days; emit RegistryCreated( _hatVaultImplementation, _hatClaimsManagerImplementation, - _HAT, address(_tokenLockFactory), generalParameters, - _bountyGovernanceHAT, - _bountyHackerHATVested, + _governanceFee, + _governanceFeeReceiver, _hatGovernance, _defaultArbitrator, defaultChallengePeriod, @@ -165,12 +137,6 @@ contract HATVaultsRegistry is IHATVaultsRegistry, Ownable { emit SetHATClaimsManagerImplementation(_hatClaimsManagerImplementation); } - /** @notice See {IHATVaultsRegistry-setSwapToken}. */ - function setSwapToken(address _swapToken) external onlyOwner { - HAT = IERC20(_swapToken); - emit SetSwapToken(_swapToken); - } - /** @notice See {IHATVaultsRegistry-setEmergencyPaused}. */ function setEmergencyPaused(bool _isEmergencyPaused) external onlyOwner { isEmergencyPaused = _isEmergencyPaused; @@ -190,15 +156,13 @@ contract HATVaultsRegistry is IHATVaultsRegistry, Ownable { emit LogClaim(msg.sender, _descriptionHash); } - /** @notice See {IHATVaultsRegistry-setDefaultHATBountySplit}. */ - function setDefaultHATBountySplit( - uint16 _defaultBountyGovernanceHAT, - uint16 _defaultBountyHackerHATVested - ) external onlyOwner { - validateHATSplit(_defaultBountyGovernanceHAT, _defaultBountyHackerHATVested); - defaultBountyGovernanceHAT = _defaultBountyGovernanceHAT; - defaultBountyHackerHATVested = _defaultBountyHackerHATVested; - emit SetDefaultHATBountySplit(_defaultBountyGovernanceHAT, _defaultBountyHackerHATVested); + /** @notice See {IHATVaultsRegistry-setDefaultGovernanceFee}. */ + function setDefaultGovernanceFee(uint16 _defaultGovernanceFee) external onlyOwner { + if (_defaultGovernanceFee > MAX_GOVERNANCE_FEE) { + revert FeeCannotBeMoreThanMaxFee(); + } + defaultGovernanceFee = _defaultGovernanceFee; + emit SetDefaultGovernanceFee(_defaultGovernanceFee); } @@ -276,6 +240,12 @@ contract HATVaultsRegistry is IHATVaultsRegistry, Ownable { emit SetMaxBountyDelay(_delay); } + /** @notice See {IHATVaultsRegistry-setGovernanceFeeReceiver}. */ + function setGovernanceFeeReceiver(address _governanceFeeReceiver) external onlyOwner { + governanceFeeReceiver = _governanceFeeReceiver; + emit SetGovernanceFeeReceiver(_governanceFeeReceiver); + } + /** @notice See {IHATVaultsRegistry-createVault}. */ function createVault( IHATVault.VaultInitParams calldata _vaultParams, @@ -298,81 +268,8 @@ contract HATVaultsRegistry is IHATVaultsRegistry, Ownable { emit SetVaultVisibility(_vault, _visible); } - /** @notice See {IHATVaultsRegistry-addTokensToSwap}. */ - function addTokensToSwap( - IERC20 _asset, - address _hacker, - uint256 _hackersHatReward, - uint256 _governanceHatReward - ) external { - hackersHatReward[address(_asset)][_hacker] += _hackersHatReward; - governanceHatReward[address(_asset)] += _governanceHatReward; - _asset.safeTransferFrom(msg.sender, address(this), _hackersHatReward + _governanceHatReward); - } - - /** @notice See {IHATVaultsRegistry-swapAndSend}. */ - function swapAndSend( - address _asset, - address[] calldata _beneficiaries, - uint256 _amountOutMinimum, - address _routingContract, - bytes calldata _routingPayload - ) external onlyOwner { - // Needed to avoid a "stack too deep" error - SwapData memory _swapData; - _swapData.hackerRewards = new uint256[](_beneficiaries.length); - _swapData.governanceHatReward = governanceHatReward[_asset]; - _swapData.amount = _swapData.governanceHatReward; - for (uint256 i = 0; i < _beneficiaries.length;) { - _swapData.hackerRewards[i] = hackersHatReward[_asset][_beneficiaries[i]]; - hackersHatReward[_asset][_beneficiaries[i]] = 0; - _swapData.amount += _swapData.hackerRewards[i]; - unchecked { ++i; } - } - if (_swapData.amount == 0) revert AmountToSwapIsZero(); - IERC20 _HAT = HAT; - (_swapData.hatsReceived, _swapData.amountUnused) = _swapTokenForHAT(IERC20(_asset), _swapData.amount, _amountOutMinimum, _routingContract, _routingPayload); - - _swapData.usedPart = (_swapData.amount - _swapData.amountUnused); - _swapData.governanceAmountSwapped = _swapData.usedPart.mulDiv(_swapData.governanceHatReward, _swapData.amount); - governanceHatReward[_asset] = _swapData.amountUnused.mulDiv(_swapData.governanceHatReward, _swapData.amount); - - for (uint256 i = 0; i < _beneficiaries.length;) { - uint256 _hackerReward = _swapData.hatsReceived.mulDiv(_swapData.hackerRewards[i], _swapData.amount); - uint256 _hackerAmountSwapped = _swapData.usedPart.mulDiv(_swapData.hackerRewards[i], _swapData.amount); - _swapData.totalHackerReward += _hackerReward; - hackersHatReward[_asset][_beneficiaries[i]] = _swapData.amountUnused.mulDiv(_swapData.hackerRewards[i], _swapData.amount); - address _tokenLock; - if (_hackerReward > 0) { - // hacker gets her reward via vesting contract - _tokenLock = tokenLockFactory.createTokenLock( - address(_HAT), - 0x0000000000000000000000000000000000000000, //this address as owner, so it can do nothing. - _beneficiaries[i], - _hackerReward, - // solhint-disable-next-line not-rely-on-time - block.timestamp, //start - // solhint-disable-next-line not-rely-on-time - block.timestamp + generalParameters.hatVestingDuration, //end - generalParameters.hatVestingPeriods, - 0, // no release start - 0, // no cliff - false, // not revocable - true - ); - _HAT.safeTransfer(_tokenLock, _hackerReward); - } - emit SwapAndSend(_beneficiaries[i], _hackerAmountSwapped, _hackerReward, _tokenLock); - unchecked { ++i; } - } - address _owner = owner(); - uint256 _amountToOwner = _swapData.hatsReceived - _swapData.totalHackerReward; - _HAT.safeTransfer(_owner, _amountToOwner); - emit SwapAndSend(_owner, _swapData.governanceAmountSwapped, _amountToOwner, address(0)); - } - /** @notice See {IHATVaultsRegistry-getWithdrawPeriod}. */ - function getWithdrawPeriod() external view returns (uint256) { + function getWithdrawPeriod() external view returns (uint256) { return generalParameters.withdrawPeriod; } @@ -405,12 +302,6 @@ contract HATVaultsRegistry is IHATVaultsRegistry, Ownable { return Ownable.owner(); } - /** @notice See {IHATVaultsRegistry-validateHATSplit}. */ - function validateHATSplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) public pure { - if (_bountyGovernanceHAT + _bountyHackerHATVested > MAX_HAT_SPLIT) - revert TotalHatsSplitPercentageShouldBeUpToMaxHATSplit(); - } - /** @notice See {IHATVaultsRegistry-validateChallengePeriod}. */ function validateChallengePeriod(uint32 _challengePeriod) public pure { if (_challengePeriod < 1 days) revert ChallengePeriodTooShort(); @@ -422,42 +313,4 @@ contract HATVaultsRegistry is IHATVaultsRegistry, Ownable { if (_challengeTimeOutPeriod < 2 days) revert ChallengeTimeOutPeriodTooShort(); if (_challengeTimeOutPeriod > 125 days) revert ChallengeTimeOutPeriodTooLong(); } - - /** - * @dev Use the given routing contract to swap the given token to HAT token - * @param _asset The token to swap - * @param _amount Amount of token to swap - * @param _amountOutMinimum Minimum amount of HAT tokens at swap - * @param _routingContract Routing contract to call for the swap - * @param _routingPayload Payload to send to the _routingContract for the - * swap - */ - function _swapTokenForHAT( - IERC20 _asset, - uint256 _amount, - uint256 _amountOutMinimum, - address _routingContract, - bytes calldata _routingPayload) - internal - returns (uint256 hatsReceived, uint256 amountUnused) - { - IERC20 _HAT = HAT; - if (_asset == _HAT) { - return (_amount, 0); - } - - IERC20(_asset).safeApprove(_routingContract, _amount); - uint256 _balanceBefore = _HAT.balanceOf(address(this)); - uint256 _assetBalanceBefore = _asset.balanceOf(address(this)); - - // solhint-disable-next-line avoid-low-level-calls - (bool success,) = _routingContract.call(_routingPayload); - if (!success) revert SwapFailed(); - hatsReceived = _HAT.balanceOf(address(this)) - _balanceBefore; - amountUnused = _amount - (_assetBalanceBefore - _asset.balanceOf(address(this))); - if (hatsReceived < _amountOutMinimum) - revert AmountSwappedLessThanMinimum(); - - IERC20(_asset).safeApprove(address(_routingContract), 0); - } } diff --git a/contracts/interfaces/IHATClaimsManager.sol b/contracts/interfaces/IHATClaimsManager.sol index b419506c..8de995a0 100644 --- a/contracts/interfaces/IHATClaimsManager.sol +++ b/contracts/interfaces/IHATClaimsManager.sol @@ -66,8 +66,7 @@ interface IHATClaimsManager { uint256 hacker; uint256 hackerVested; uint256 committee; - uint256 hackerHatVested; - uint256 governanceHat; + uint256 governanceFee; } struct Claim { @@ -79,8 +78,7 @@ interface IHATClaimsManager { address committee; uint32 createdAt; uint32 challengedAt; - uint256 bountyGovernanceHAT; - uint256 bountyHackerHATVested; + uint16 governanceFee; address arbitrator; uint32 challengePeriod; uint32 challengeTimeOutPeriod; @@ -108,6 +106,7 @@ interface IHATClaimsManager { * hacker vested, and committee. * Each entry is a number between 0 and `HUNDRED_PERCENT`. * Total splits should be equal to `HUNDRED_PERCENT`. + * @param governanceFee the fee to be sent to governace of the total payout * @param asset The vault's native token * @param owner The address of the vault's owner * @param committee The address of the vault's committee @@ -123,6 +122,7 @@ interface IHATClaimsManager { uint32 vestingPeriods; uint16 maxBounty; BountySplit bountySplit; + uint16 governanceFee; address owner; address committee; address arbitrator; @@ -194,6 +194,8 @@ interface IHATClaimsManager { error CannotSetToPerviousRewardController(); // Payout must either be 100%, or up to the MAX_BOUNTY_LIMIT error PayoutMustBeUpToMaxBountyLimitOrHundredPercent(); + // Cannot set fee greater than the max fee + error FeeCannotBeMoreThanMaxFee(); event SubmitClaim( @@ -224,7 +226,7 @@ interface IHATClaimsManager { event CommitteeCheckedIn(); event SetPendingMaxBounty(uint256 _maxBounty); event SetMaxBounty(uint256 _maxBounty); - event SetHATBountySplit(uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVested); + event SetGovernanceFee(uint16 _governanceFee); event SetArbitrator(address indexed _arbitrator); event SetChallengePeriod(uint256 _challengePeriod); event SetChallengeTimeOutPeriod(uint256 _challengeTimeOutPeriod); @@ -358,18 +360,12 @@ interface IHATClaimsManager { function setMaxBounty() external; /** - * @notice Called by the registry's owner to set the vault HAT token bounty - * split upon an approval. + * @notice Called by the registry's owner to set the fee percentage for payouts * If the value passed is the special "null" value the vault will use the * registry's default value. - * @param _bountyGovernanceHAT The HAT bounty for governance - * @param _bountyHackerHATVested The HAT bounty vested for the hacker + * @param _governanceFee The fee percentage for governance */ - function setHATBountySplit( - uint16 _bountyGovernanceHAT, - uint16 _bountyHackerHATVested - ) - external; + function setGovernanceFee(uint16 _governanceFee) external; /** * @notice Called by the registry's owner to set the vault arbitrator @@ -443,20 +439,12 @@ interface IHATClaimsManager { function getActiveClaim() external view returns(Claim memory); /** - * @notice Returns the vault HAT bounty split part that goes to the governance - * If no specific value for this vault has been set, the registry's default - * value will be returned. - * @return The vault's HAT bounty split part that goes to the governance - */ - function getBountyGovernanceHAT() external view returns(uint16); - - /** - * @notice Returns the vault HAT bounty split part that is vested for the hacker + * @notice Returns the vault fee split that goes to the governance * If no specific value for this vault has been set, the registry's default * value will be returned. - * @return The vault's HAT bounty split part that is vested for the hacker + * @return The vault's fee split that goes to the governance */ - function getBountyHackerHATVested() external view returns(uint16); + function getGovernanceFee() external view returns(uint16); /** * @notice Returns the address of the vault's arbitrator diff --git a/contracts/interfaces/IHATVaultsRegistry.sol b/contracts/interfaces/IHATVaultsRegistry.sol index aec5d44a..48c34379 100644 --- a/contracts/interfaces/IHATVaultsRegistry.sol +++ b/contracts/interfaces/IHATVaultsRegistry.sol @@ -112,27 +112,10 @@ interface IHATVaultsRegistry { error DelayTooShort(); /** - * @notice Raised on {swapAndSend} if the amount to swap is zero + * @notice Raised on {setDefaultGovernanceFee} if the fee to be set is + * greater than 35% (defined as 3500) */ - error AmountToSwapIsZero(); - - /** - * @notice Raised on {swapAndSend} if the swap was not successful - */ - error SwapFailed(); - // Wrong amount received - - /** - * @notice Raised on {swapAndSend} if the amount that was recieved in - * the swap was less than the minimum amount specified - */ - error AmountSwappedLessThanMinimum(); - - /** - * @notice Raised on {setDefaultHATBountySplit} if the split to be set is - * greater than 20% (defined as 2000) - */ - error TotalHatsSplitPercentageShouldBeUpToMaxHATSplit(); + error FeeCannotBeMoreThanMaxFee(); /** * @notice Raised on {setDefaultChallengePeriod} if the challenge period @@ -173,11 +156,10 @@ interface IHATVaultsRegistry { * @notice Emitted on deployment of the registry * @param _hatVaultImplementation The HATVault implementation address * @param _hatClaimsManagerImplementation The HATClaimsManager implementation address - * @param _HAT The HAT token address * @param _tokenLockFactory The token lock factory address * @param _generalParameters The registry's general parameters - * @param _bountyGovernanceHAT The HAT bounty for governance - * @param _bountyHackerHATVested The HAT bounty vested for the hacker + * @param _defaultGovernanceFee The fee percentage for governance + * @param _governanceFeeReceiver The fee receiver address * @param _hatGovernance The registry's governance * @param _defaultChallengePeriod The new default challenge period * @param _defaultChallengeTimeOutPeriod The new default challenge timeout @@ -185,11 +167,10 @@ interface IHATVaultsRegistry { event RegistryCreated( address _hatVaultImplementation, address _hatClaimsManagerImplementation, - address _HAT, address _tokenLockFactory, GeneralParameters _generalParameters, - uint256 _bountyGovernanceHAT, - uint256 _bountyHackerHATVested, + uint16 _defaultGovernanceFee, + address _governanceFeeReceiver, address _hatGovernance, address _defaultArbitrator, uint256 _defaultChallengePeriod, @@ -255,6 +236,12 @@ interface IHATVaultsRegistry { */ event SetMaxBountyDelay(uint256 _delay); + /** + * @notice Emitted when a new fee receiver address is set + * @param _governaceFeeReceiver The receiver of the fee from the payouts of the vaults + */ + event SetGovernanceFeeReceiver(address indexed _governaceFeeReceiver); + /** * @notice Emitted when the UI visibility of a vault is changed * @param _vault The address of the vault to update @@ -274,28 +261,12 @@ interface IHATVaultsRegistry { IHATVault.VaultInitParams _vaultParams, IHATClaimsManager.ClaimsManagerInitParams _claimsManagerParams ); - - /** @notice Emitted when a swap of vault tokens to HAT tokens is done and - * the HATS tokens are sent to beneficiary through vesting contract - * @param _beneficiary Address of beneficiary - * @param _amountSwapped Amount of vault's native tokens that was swapped - * @param _amountSent Amount of HAT tokens sent to beneficiary - * @param _tokenLock Address of the token lock contract that holds the HAT - * tokens (address(0) if no token lock is used) - */ - event SwapAndSend( - address indexed _beneficiary, - uint256 _amountSwapped, - uint256 _amountSent, - address indexed _tokenLock - ); /** - * @notice Emitted when a new default HAT bounty split is set - * @param _defaultBountyGovernanceHAT The new default HAT bounty part sent to governance - * @param _defaultBountyHackerHATVested The new default HAT bounty part vseted for the hacker + * @notice Emitted when a new default governance fee is set + * @param _defaultGovernanceFee The new default fee part sent to governance */ - event SetDefaultHATBountySplit(uint256 _defaultBountyGovernanceHAT, uint256 _defaultBountyHackerHATVested); + event SetDefaultGovernanceFee(uint16 _defaultGovernanceFee); /** * @notice Emitted when a new default arbitrator is set @@ -321,12 +292,6 @@ interface IHATVaultsRegistry { */ event SetEmergencyPaused(bool _isEmergencyPaused); - /** - * @notice Emitted when a new swap token is set - * @param _swapToken The new swap token address - */ - event SetSwapToken(address indexed _swapToken); - /** * @notice Emitted when a new HATVault implementation is set * @param _hatVaultImplementation The address of the new HATVault implementation @@ -346,12 +311,6 @@ interface IHATVaultsRegistry { */ function setEmergencyPaused(bool _isEmergencyPaused) external; - /** - * @notice Called by governance to set a new swap token - * @param _swapToken the new swap token address - */ - function setSwapToken(address _swapToken) external; - /** * @notice Called by governance to set a new HATVault and HATVault implementation to be * used by the registry for creating new vaults @@ -372,28 +331,10 @@ interface IHATVaultsRegistry { function logClaim(string calldata _descriptionHash) external payable; /** - * @notice Called by governance to set the default percentage of each claim bounty - * that will be swapped for hats and sent to the governance or vested for the hacker - * @param _defaultBountyGovernanceHAT The HAT bounty for governance - * @param _defaultBountyHackerHATVested The HAT bounty vested for the hacker - */ - function setDefaultHATBountySplit( - uint16 _defaultBountyGovernanceHAT, - uint16 _defaultBountyHackerHATVested - ) - external; - - /** - * @dev Check that a given hats bounty split is legal, meaning that: - * Each entry is a number between 0 and less than `MAX_HAT_SPLIT`. - * Total splits should be less than `MAX_HAT_SPLIT`. - * function will revert in case the bounty split is not legal. - * @param _bountyGovernanceHAT The HAT bounty for governance - * @param _bountyHackerHATVested The HAT bounty vested for the hacker + * @notice Called by governance to set the default fee percentage + * @param _defaultGovernanceFee The fee for governance */ - function validateHATSplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) - external - pure; + function setDefaultGovernanceFee(uint16 _defaultGovernanceFee) external; /** * @notice Called by governance to set the default arbitrator. @@ -493,6 +434,12 @@ interface IHATVaultsRegistry { */ function setMaxBountyDelay(uint32 _delay) external; + /** + * @notice Called by governance to set the fee receiver address + * @param _governanceFeeReceiver The receiver of the fees from the payouts of the vaults + */ + function setGovernanceFeeReceiver(address _governanceFeeReceiver) external; + /** * @notice Create a new vault * NOTE: Vaults should not use tokens which do not guarantee that the @@ -514,43 +461,6 @@ interface IHATVaultsRegistry { */ function setVaultVisibility(address _vault, bool _visible) external; - /** - * @notice Transfer the part of the bounty that is supposed to be swapped - * into HAT tokens from the HATVault to the registry, and keep track of - * the amounts to be swapped and sent/burnt in a later transaction - * @param _asset The vault's native token - * @param _hacker The address of the beneficiary of the bounty - * @param _hackersHatReward The amount of the vault's native token to be - * swapped to HAT tokens and sent to the hacker via a vesting contract - * @param _governanceHatReward The amount of the vault's native token to - * be swapped to HAT tokens and sent to governance - */ - function addTokensToSwap( - IERC20 _asset, - address _hacker, - uint256 _hackersHatReward, - uint256 _governanceHatReward - ) external; - - /** - * @notice Called by governance to swap the given asset to HAT tokens and - * distribute the HAT tokens: Send to governance their share and send to - * beneficiaries their share through a vesting contract. - * @param _asset The address of the token to be swapped to HAT tokens - * @param _beneficiaries Addresses of beneficiaries - * @param _amountOutMinimum Minimum amount of HAT tokens at swap - * @param _routingContract Routing contract to call for the swap - * @param _routingPayload Payload to send to the _routingContract for the - * swap - */ - function swapAndSend( - address _asset, - address[] calldata _beneficiaries, - uint256 _amountOutMinimum, - address _routingContract, - bytes calldata _routingPayload - ) external; - /** * @notice Returns the withdraw enable period for all vaults. The safety * period starts when finished. @@ -602,6 +512,12 @@ interface IHATVaultsRegistry { */ function feeSetter() external view returns(address); + /** + * @notice Get the fee receiver address + * @return The address of the fee receiver + */ + function governanceFeeReceiver() external view returns(address); + /** * @notice Get whether the system is in an emergency pause * @return Whether the system is in an emergency pause @@ -615,16 +531,10 @@ interface IHATVaultsRegistry { function owner() external view returns(address); /** - * @notice Get the default percentage of the total bounty to be swapped to HATs and sent to governance - * @return The default percentage of the total bounty to be swapped to HATs and sent to governance + * @notice Get the default fee percentage of the total bounty + * @return The default fee percentage of the total bounty */ - function defaultBountyGovernanceHAT() external view returns(uint16); - - /** - * @notice Get the default percentage of the total bounty to be swapped to HATs and sent to the hacker via vesting contract - * @return The default percentage of the total bounty to be swapped to HATs and sent to the hacker via vesting contract - */ - function defaultBountyHackerHATVested() external view returns(uint16); + function defaultGovernanceFee() external view returns(uint16); /** * @notice Get the default arbitrator address @@ -643,4 +553,10 @@ interface IHATVaultsRegistry { * @return The default challenge time out period */ function defaultChallengeTimeOutPeriod() external view returns(uint32); + + /** + * @notice Get the max governance fee + * @return The max governance fee + */ + function MAX_GOVERNANCE_FEE() external view returns(uint16); } diff --git a/contracts/mocks/HatVaultForConnectorMock.sol b/contracts/mocks/HatVaultForConnectorMock.sol index 13e11a96..208411f1 100644 --- a/contracts/mocks/HatVaultForConnectorMock.sol +++ b/contracts/mocks/HatVaultForConnectorMock.sol @@ -11,8 +11,7 @@ contract HatVaultForConnectorMock { address committee; uint32 createdAt; uint32 challengedAt; - uint256 bountyGovernanceHAT; - uint256 bountyHackerHATVested; + uint256 governanceFee; address arbitrator; uint32 challengePeriod; uint32 challengeTimeOutPeriod; @@ -67,8 +66,7 @@ contract HatVaultForConnectorMock { // solhint-disable-next-line not-rely-on-time createdAt: uint32(block.timestamp), challengedAt: 0, - bountyGovernanceHAT: 0, - bountyHackerHATVested: 0, + governanceFee: 0, arbitrator: arbitrator, challengePeriod: challengePeriod, challengeTimeOutPeriod: CHALLENGE_TIMEOUT_PERIOD, diff --git a/contracts/mocks/ISwapRouter.sol b/contracts/mocks/ISwapRouter.sol deleted file mode 100644 index 3125f48d..00000000 --- a/contracts/mocks/ISwapRouter.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -/// @title Router token swapping functionality -/// @notice Functions for swapping tokens via Uniswap V3 -interface ISwapRouter { - struct ExactInputSingleParams { - address tokenIn; - address tokenOut; - uint24 fee; - address recipient; - uint256 deadline; - uint256 amountIn; - uint256 amountOutMinimum; - uint160 sqrtPriceLimitX96; - } - - struct ExactInputParams { - bytes path; - address recipient; - uint256 deadline; - uint256 amountIn; - uint256 amountOutMinimum; - } - - struct ExactOutputSingleParams { - address tokenIn; - address tokenOut; - uint24 fee; - address recipient; - uint256 deadline; - uint256 amountOut; - uint256 amountInMaximum; - uint160 sqrtPriceLimitX96; - } - - struct ExactOutputParams { - bytes path; - address recipient; - uint256 deadline; - uint256 amountOut; - uint256 amountInMaximum; - } - - /// @notice Swaps `amountIn` of one token for as much as possible of another token - /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata - /// @return amountOut The amount of the received token - function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); - - /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path - /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata - /// @return amountOut The amount of the received token - function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut); - - /// @notice Swaps as little as possible of one token for `amountOut` of another token - /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata - /// @return amountIn The amount of the input token - function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn); - - /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) - /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata - /// @return amountIn The amount of the input token - function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn); - - // solhint-disable-next-line func-name-mixedcase - function WETH9() external pure returns (address); -} diff --git a/contracts/mocks/PoolsManagerMock.sol b/contracts/mocks/PoolsManagerMock.sol index d118ea34..9316d0e7 100644 --- a/contracts/mocks/PoolsManagerMock.sol +++ b/contracts/mocks/PoolsManagerMock.sol @@ -42,6 +42,7 @@ contract VaultsManagerMock { isTokenLockRevocable: false, maxBounty: _maxBounty, bountySplit: _bountySplit, + governanceFee: type(uint16).max, vestingDuration: 86400, vestingPeriods: 10 })); diff --git a/contracts/mocks/UniSwapV3RouterMock.sol b/contracts/mocks/UniSwapV3RouterMock.sol deleted file mode 100644 index b8102621..00000000 --- a/contracts/mocks/UniSwapV3RouterMock.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "./ISwapRouter.sol"; - - -contract UniSwapV3RouterMock { - - enum ReturnType {ONE_TO_ONE, MINIMUM, BELOW_MINIMUM} - - /// @notice The length of the bytes encoded address - uint256 private constant ADDR_SIZE = 20; - /// @notice The length of the bytes encoded fee - uint256 private constant FEE_SIZE = 3; - /// @notice The offset of a single token address and pool fee - uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE; - - ReturnType public returnType; - address public immutable WETH9; - - bool public usePartialAmountFlag; - - constructor( - ReturnType _returnType, - address _weth9 - // solhint-disable-next-line func-visibility - ) { - returnType = _returnType; - WETH9 = _weth9; - } - - function setUsePartialAmountFlag(bool _usePartialAmountFlag) external { - usePartialAmountFlag = _usePartialAmountFlag; - } - - function exactInput( - ISwapRouter.ExactInputParams memory _params - ) external returns (uint256 amount) { - uint256 amountToSendBack; - - if (usePartialAmountFlag) { - _params.amountIn = _params.amountIn * 80 / 100; - } - - if (returnType == ReturnType.ONE_TO_ONE) { - amountToSendBack = _params.amountIn; - } - - if (returnType == ReturnType.MINIMUM) { - amountToSendBack = _params.amountOutMinimum; - } - - if (returnType == ReturnType.BELOW_MINIMUM) { - amountToSendBack = _params.amountOutMinimum - 1; - } - address tokennIn = toAddress(_params.path, 0); - ERC20(tokennIn).transferFrom(msg.sender, address(this), _params.amountIn); - //swap 1 to 1... - address tokenOut = toAddress(_params.path, NEXT_OFFSET*numPools(_params.path)); - IERC20(tokenOut).transfer(_params.recipient, amountToSendBack); - return amountToSendBack; - } - - function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { - require(_start + 20 >= _start, "toAddress_overflow"); - require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); - address tempAddress; - - // solhint-disable-next-line no-inline-assembly - assembly { - tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) - } - - return tempAddress; - } - - /// @notice Returns the number of pools in the path - /// @param path The encoded swap path - /// @return The number of pools in the path - function numPools(bytes memory path) internal pure returns (uint256) { - // Ignore the first token address. From then on every fee and token offset indicates a pool. - return ((path.length - ADDR_SIZE) / NEXT_OFFSET); - } - -} diff --git a/deploy/008_deploy_hatvaultsregistry.js b/deploy/008_deploy_hatvaultsregistry.js index 59c1835c..6cd667f8 100644 --- a/deploy/008_deploy_hatvaultsregistry.js +++ b/deploy/008_deploy_hatvaultsregistry.js @@ -7,12 +7,17 @@ const func = async function (hre) { const { deploy } = deployments; const { deployer } = await getNamedAccounts(); + + let governance = config["governance"]; + if (!governance && network.name === "hardhat") { + governance = deployer; + } + + let governanceFee = config.hatVaultsRegistryConf.governanceFee; + let governanceFeeReceiver = config.hatVaultsRegistryConf.governanceFeeReceiver; - let bountyGovernanceHAT = config.hatVaultsRegistryConf.bountyGovernanceHAT; - let bountyHackerHATVested = config.hatVaultsRegistryConf.bountyHackerHATVested; - let swapToken = config.hatVaultsRegistryConf.swapToken; - if (!swapToken || swapToken === "HATToken") { - swapToken = (await deployments.get('HATToken')).address; + if (!governanceFeeReceiver) { + governanceFeeReceiver = governance; } await deploy('HATVaultsRegistry', { @@ -22,10 +27,9 @@ const func = async function (hre) { (await deployments.get('HATClaimsManager')).address, (await deployments.get('HATTimelockController')).address, (await deployments.get('HATGovernanceArbitrator')).address, - swapToken, - bountyGovernanceHAT, - bountyHackerHATVested, - (await deployments.get('TokenLockFactory')).address, + governanceFee, + governanceFeeReceiver, + (await deployments.get('TokenLockFactory')).address ], log: true, }); diff --git a/deploy/011_verify_deployment.js b/deploy/011_verify_deployment.js index febee764..45db6dfe 100644 --- a/deploy/011_verify_deployment.js +++ b/deploy/011_verify_deployment.js @@ -184,11 +184,12 @@ const func = async function (hre) { // Verify HATVaultsRegistry - let bountyGovernanceHAT = config["hatVaultsRegistryConf"]["bountyGovernanceHAT"]; - let bountyHackerHATVested = config["hatVaultsRegistryConf"]["bountyHackerHATVested"]; - let swapToken = config["hatVaultsRegistryConf"]["swapToken"]; - if (!swapToken || swapToken === "HATToken") { - swapToken = (await deployments.get('HATToken')).address; + let governanceFee = config["hatVaultsRegistryConf"]["governanceFee"]; + + let governanceFeeReceiver = config["hatVaultsRegistryConf"]["governanceFeeReceiver"]; + + if (!governanceFeeReceiver) { + governanceFeeReceiver = governance; } verify( @@ -212,24 +213,15 @@ const func = async function (hre) { ); verify( - (await read('HATVaultsRegistry', {}, 'tokenLockFactory')).toLowerCase() === (await deployments.get('TokenLockFactory')).address.toLowerCase(), - "HATVaultsRegistry TokenLockFactory is correct" + (await read('HATVaultsRegistry', {}, 'defaultGovernanceFee')).toString() === governanceFee.toString(), + "HATVaultsRegistry default governanceFee is correct (" + governanceFee + ")" ); verify( - (await read('HATVaultsRegistry', {}, 'HAT')).toLowerCase() === swapToken.toLowerCase(), - "HATVaultsRegistry swap token is correct (" + swapToken + ")" + (await read('HATVaultsRegistry', {}, 'governanceFeeReceiver')).toString() === governanceFeeReceiver.toString(), + "HATVaultsRegistry governanceFeeReceiver is correct (" + governanceFeeReceiver + ")" ); - verify( - (await read('HATVaultsRegistry', {}, 'defaultBountyGovernanceHAT')).toString() === bountyGovernanceHAT.toString(), - "HATVaultsRegistry default bountyGovernanceHAT is correct (" + bountyGovernanceHAT + ")" - ); - - verify( - (await read('HATVaultsRegistry', {}, 'defaultBountyHackerHATVested')).toString() === bountyHackerHATVested.toString(), - "HATVaultsRegistry default bountyHackerHATVested is correct (" + bountyHackerHATVested + ")" - ); if (failures > 0) { throw Error(`${failures} checks failed!`); } diff --git a/docs/dodoc/HATClaimsManager.md b/docs/dodoc/HATClaimsManager.md index 65de80fa..9cd6752a 100644 --- a/docs/dodoc/HATClaimsManager.md +++ b/docs/dodoc/HATClaimsManager.md @@ -149,7 +149,7 @@ Returns the claims manager's version ### activeClaim ```solidity -function activeClaim() external view returns (bytes32 claimId, address beneficiary, uint16 bountyPercentage, address committee, uint32 createdAt, uint32 challengedAt, uint256 bountyGovernanceHAT, uint256 bountyHackerHATVested, address arbitrator, uint32 challengePeriod, uint32 challengeTimeOutPeriod, bool arbitratorCanChangeBounty, bool arbitratorCanChangeBeneficiary) +function activeClaim() external view returns (bytes32 claimId, address beneficiary, uint16 bountyPercentage, address committee, uint32 createdAt, uint32 challengedAt, uint16 governanceFee, address arbitrator, uint32 challengePeriod, uint32 challengeTimeOutPeriod, bool arbitratorCanChangeBounty, bool arbitratorCanChangeBeneficiary) ``` @@ -167,8 +167,7 @@ function activeClaim() external view returns (bytes32 claimId, address beneficia | committee | address | undefined | | createdAt | uint32 | undefined | | challengedAt | uint32 | undefined | -| bountyGovernanceHAT | uint256 | undefined | -| bountyHackerHATVested | uint256 | undefined | +| governanceFee | uint16 | undefined | | arbitrator | address | undefined | | challengePeriod | uint32 | undefined | | challengeTimeOutPeriod | uint32 | undefined | @@ -374,30 +373,13 @@ See {IHATClaimsManager-getArbitrator}. |---|---|---| | _0 | address | undefined | -### getBountyGovernanceHAT - -```solidity -function getBountyGovernanceHAT() external view returns (uint16) -``` - -See {IHATClaimsManager-getBountyGovernanceHAT}. - - - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | uint16 | undefined | - -### getBountyHackerHATVested +### getChallengePeriod ```solidity -function getBountyHackerHATVested() external view returns (uint16) +function getChallengePeriod() external view returns (uint32) ``` -See {IHATClaimsManager-getBountyHackerHATVested}. +See {IHATClaimsManager-getChallengePeriod}. @@ -406,15 +388,15 @@ See {IHATClaimsManager-getBountyHackerHATVested}. | Name | Type | Description | |---|---|---| -| _0 | uint16 | undefined | +| _0 | uint32 | undefined | -### getChallengePeriod +### getChallengeTimeOutPeriod ```solidity -function getChallengePeriod() external view returns (uint32) +function getChallengeTimeOutPeriod() external view returns (uint32) ``` -See {IHATClaimsManager-getChallengePeriod}. +See {IHATClaimsManager-getChallengeTimeOutPeriod}. @@ -425,13 +407,13 @@ See {IHATClaimsManager-getChallengePeriod}. |---|---|---| | _0 | uint32 | undefined | -### getChallengeTimeOutPeriod +### getGovernanceFee ```solidity -function getChallengeTimeOutPeriod() external view returns (uint32) +function getGovernanceFee() external view returns (uint16) ``` -See {IHATClaimsManager-getChallengeTimeOutPeriod}. +See {IHATClaimsManager-getGovernanceFee}. @@ -440,7 +422,7 @@ See {IHATClaimsManager-getChallengeTimeOutPeriod}. | Name | Type | Description | |---|---|---| -| _0 | uint32 | undefined | +| _0 | uint16 | undefined | ### initialize @@ -654,13 +636,13 @@ See {IHATClaimsManager-setCommittee}. |---|---|---| | _committee | address | undefined | -### setHATBountySplit +### setGovernanceFee ```solidity -function setHATBountySplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) external nonpayable +function setGovernanceFee(uint16 _governanceFee) external nonpayable ``` -See {IHATClaimsManager-setHATBountySplit}. +See {IHATClaimsManager-setGoveranceFee}. @@ -668,8 +650,7 @@ See {IHATClaimsManager-setHATBountySplit}. | Name | Type | Description | |---|---|---| -| _bountyGovernanceHAT | uint16 | undefined | -| _bountyHackerHATVested | uint16 | undefined | +| _governanceFee | uint16 | undefined | ### setMaxBounty @@ -1023,10 +1004,10 @@ event SetCommittee(address indexed _committee) |---|---|---| | _committee `indexed` | address | undefined | -### SetHATBountySplit +### SetGovernanceFee ```solidity -event SetHATBountySplit(uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVested) +event SetGovernanceFee(uint16 _governanceFee) ``` @@ -1037,8 +1018,7 @@ event SetHATBountySplit(uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVe | Name | Type | Description | |---|---|---| -| _bountyGovernanceHAT | uint256 | undefined | -| _bountyHackerHATVested | uint256 | undefined | +| _governanceFee | uint16 | undefined | ### SetMaxBounty @@ -1235,6 +1215,17 @@ error DelayPeriodForSettingMaxBountyHadNotPassed() +### FeeCannotBeMoreThanMaxFee + +```solidity +error FeeCannotBeMoreThanMaxFee() +``` + + + + + + ### MaxBountyCannotBeMoreThanMaxBountyLimit ```solidity diff --git a/docs/dodoc/HATTimelockController.md b/docs/dodoc/HATTimelockController.md index 475c101c..a649bc0d 100644 --- a/docs/dodoc/HATTimelockController.md +++ b/docs/dodoc/HATTimelockController.md @@ -706,27 +706,6 @@ function supportsInterface(bytes4 interfaceId) external view returns (bool) |---|---|---| | _0 | bool | undefined | -### swapAndSend - -```solidity -function swapAndSend(contract IHATVaultsRegistry _registry, address _asset, address[] _beneficiaries, uint256 _amountOutMinimum, address _routingContract, bytes _routingPayload) external nonpayable -``` - - - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _registry | contract IHATVaultsRegistry | undefined | -| _asset | address | undefined | -| _beneficiaries | address[] | undefined | -| _amountOutMinimum | uint256 | undefined | -| _routingContract | address | undefined | -| _routingPayload | bytes | undefined | - ### updateDelay ```solidity diff --git a/docs/dodoc/HATVaultsRegistry.md b/docs/dodoc/HATVaultsRegistry.md index 6ba85b1f..b246a64b 100644 --- a/docs/dodoc/HATVaultsRegistry.md +++ b/docs/dodoc/HATVaultsRegistry.md @@ -10,23 +10,6 @@ Hats.finance is a proactive bounty protocol for white hat hackers and security e ## Methods -### HAT - -```solidity -function HAT() external view returns (contract IERC20) -``` - - - - - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | contract IERC20 | undefined | - ### HUNDRED_PERCENT ```solidity @@ -44,13 +27,13 @@ function HUNDRED_PERCENT() external view returns (uint16) |---|---|---| | _0 | uint16 | undefined | -### MAX_HAT_SPLIT +### MAX_GOVERNANCE_FEE ```solidity -function MAX_HAT_SPLIT() external view returns (uint16) +function MAX_GOVERNANCE_FEE() external view returns (uint16) ``` - +Get the max governance fee @@ -59,26 +42,7 @@ function MAX_HAT_SPLIT() external view returns (uint16) | Name | Type | Description | |---|---|---| -| _0 | uint16 | undefined | - -### addTokensToSwap - -```solidity -function addTokensToSwap(contract IERC20 _asset, address _hacker, uint256 _hackersHatReward, uint256 _governanceHatReward) external nonpayable -``` - -See {IHATVaultsRegistry-addTokensToSwap}. - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _asset | contract IERC20 | undefined | -| _hacker | address | undefined | -| _hackersHatReward | uint256 | undefined | -| _governanceHatReward | uint256 | undefined | +| _0 | uint16 | The max governance fee | ### createVault @@ -121,30 +85,13 @@ Get the default arbitrator address |---|---|---| | _0 | address | The default arbitrator address | -### defaultBountyGovernanceHAT - -```solidity -function defaultBountyGovernanceHAT() external view returns (uint16) -``` - -Get the default percentage of the total bounty to be swapped to HATs and sent to governance - - - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | uint16 | The default percentage of the total bounty to be swapped to HATs and sent to governance | - -### defaultBountyHackerHATVested +### defaultChallengePeriod ```solidity -function defaultBountyHackerHATVested() external view returns (uint16) +function defaultChallengePeriod() external view returns (uint32) ``` -Get the default percentage of the total bounty to be swapped to HATs and sent to the hacker via vesting contract +Get the default challenge period @@ -153,15 +100,15 @@ Get the default percentage of the total bounty to be swapped to HATs and sent to | Name | Type | Description | |---|---|---| -| _0 | uint16 | The default percentage of the total bounty to be swapped to HATs and sent to the hacker via vesting contract | +| _0 | uint32 | The default challenge period | -### defaultChallengePeriod +### defaultChallengeTimeOutPeriod ```solidity -function defaultChallengePeriod() external view returns (uint32) +function defaultChallengeTimeOutPeriod() external view returns (uint32) ``` -Get the default challenge period +Get the default challenge time out period @@ -170,15 +117,15 @@ Get the default challenge period | Name | Type | Description | |---|---|---| -| _0 | uint32 | The default challenge period | +| _0 | uint32 | The default challenge time out period | -### defaultChallengeTimeOutPeriod +### defaultGovernanceFee ```solidity -function defaultChallengeTimeOutPeriod() external view returns (uint32) +function defaultGovernanceFee() external view returns (uint16) ``` -Get the default challenge time out period +Get the default fee percentage of the total bounty @@ -187,7 +134,7 @@ Get the default challenge time out period | Name | Type | Description | |---|---|---| -| _0 | uint32 | The default challenge time out period | +| _0 | uint16 | The default fee percentage of the total bounty | ### feeSetter @@ -332,50 +279,22 @@ See {IHATVaultsRegistry-getWithdrawRequestPendingPeriod}. |---|---|---| | _0 | uint256 | undefined | -### governanceHatReward - -```solidity -function governanceHatReward(address) external view returns (uint256) -``` - - - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _0 | address | undefined | - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | uint256 | undefined | - -### hackersHatReward +### governanceFeeReceiver ```solidity -function hackersHatReward(address, address) external view returns (uint256) +function governanceFeeReceiver() external view returns (address) ``` +Get the fee receiver address -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _0 | address | undefined | -| _1 | address | undefined | - #### Returns | Name | Type | Description | |---|---|---| -| _0 | uint256 | undefined | +| _0 | address | The address of the fee receiver | ### hatClaimsManagerImplementation @@ -580,13 +499,13 @@ See {IHATVaultsRegistry-setDefaultChallengeTimeOutPeriod}. |---|---|---| | _defaultChallengeTimeOutPeriod | uint32 | undefined | -### setDefaultHATBountySplit +### setDefaultGovernanceFee ```solidity -function setDefaultHATBountySplit(uint16 _defaultBountyGovernanceHAT, uint16 _defaultBountyHackerHATVested) external nonpayable +function setDefaultGovernanceFee(uint16 _defaultGovernanceFee) external nonpayable ``` -See {IHATVaultsRegistry-setDefaultHATBountySplit}. +See {IHATVaultsRegistry-setDefaultGovernanceFee}. @@ -594,8 +513,7 @@ See {IHATVaultsRegistry-setDefaultHATBountySplit}. | Name | Type | Description | |---|---|---| -| _defaultBountyGovernanceHAT | uint16 | undefined | -| _defaultBountyHackerHATVested | uint16 | undefined | +| _defaultGovernanceFee | uint16 | undefined | ### setEmergencyPaused @@ -629,13 +547,13 @@ See {IHATVaultsRegistry-setFeeSetter}. |---|---|---| | _feeSetter | address | undefined | -### setHatVestingParams +### setGovernanceFeeReceiver ```solidity -function setHatVestingParams(uint32 _duration, uint32 _periods) external nonpayable +function setGovernanceFeeReceiver(address _governanceFeeReceiver) external nonpayable ``` -See {IHATVaultsRegistry-setHatVestingParams}. +See {IHATVaultsRegistry-setGovernanceFeeReceiver}. @@ -643,16 +561,15 @@ See {IHATVaultsRegistry-setHatVestingParams}. | Name | Type | Description | |---|---|---| -| _duration | uint32 | undefined | -| _periods | uint32 | undefined | +| _governanceFeeReceiver | address | undefined | -### setMaxBountyDelay +### setHatVestingParams ```solidity -function setMaxBountyDelay(uint32 _delay) external nonpayable +function setHatVestingParams(uint32 _duration, uint32 _periods) external nonpayable ``` -See {IHATVaultsRegistry-setMaxBountyDelay}. +See {IHATVaultsRegistry-setHatVestingParams}. @@ -660,15 +577,16 @@ See {IHATVaultsRegistry-setMaxBountyDelay}. | Name | Type | Description | |---|---|---| -| _delay | uint32 | undefined | +| _duration | uint32 | undefined | +| _periods | uint32 | undefined | -### setSwapToken +### setMaxBountyDelay ```solidity -function setSwapToken(address _swapToken) external nonpayable +function setMaxBountyDelay(uint32 _delay) external nonpayable ``` -See {IHATVaultsRegistry-setSwapToken}. +See {IHATVaultsRegistry-setMaxBountyDelay}. @@ -676,7 +594,7 @@ See {IHATVaultsRegistry-setSwapToken}. | Name | Type | Description | |---|---|---| -| _swapToken | address | undefined | +| _delay | uint32 | undefined | ### setVaultImplementations @@ -746,26 +664,6 @@ See {IHATVaultsRegistry-setWithdrawSafetyPeriod}. | _withdrawPeriod | uint32 | undefined | | _safetyPeriod | uint32 | undefined | -### swapAndSend - -```solidity -function swapAndSend(address _asset, address[] _beneficiaries, uint256 _amountOutMinimum, address _routingContract, bytes _routingPayload) external nonpayable -``` - -See {IHATVaultsRegistry-swapAndSend}. - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _asset | address | undefined | -| _beneficiaries | address[] | undefined | -| _amountOutMinimum | uint256 | undefined | -| _routingContract | address | undefined | -| _routingPayload | bytes | undefined | - ### tokenLockFactory ```solidity @@ -831,23 +729,6 @@ See {IHATVaultsRegistry-validateChallengeTimeOutPeriod}. |---|---|---| | _challengeTimeOutPeriod | uint32 | undefined | -### validateHATSplit - -```solidity -function validateHATSplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) external pure -``` - -See {IHATVaultsRegistry-validateHATSplit}. - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _bountyGovernanceHAT | uint16 | undefined | -| _bountyHackerHATVested | uint16 | undefined | - ## Events @@ -889,7 +770,7 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn ### RegistryCreated ```solidity -event RegistryCreated(address _hatVaultImplementation, address _hatClaimsManagerImplementation, address _HAT, address _tokenLockFactory, IHATVaultsRegistry.GeneralParameters _generalParameters, uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVested, address _hatGovernance, address _defaultArbitrator, uint256 _defaultChallengePeriod, uint256 _defaultChallengeTimeOutPeriod) +event RegistryCreated(address _hatVaultImplementation, address _hatClaimsManagerImplementation, address _tokenLockFactory, IHATVaultsRegistry.GeneralParameters _generalParameters, uint16 _defaultGovernanceFee, address _governanceFeeReceiver, address _hatGovernance, address _defaultArbitrator, uint256 _defaultChallengePeriod, uint256 _defaultChallengeTimeOutPeriod) ``` Emitted on deployment of the registry @@ -902,11 +783,10 @@ Emitted on deployment of the registry |---|---|---| | _hatVaultImplementation | address | undefined | | _hatClaimsManagerImplementation | address | undefined | -| _HAT | address | undefined | | _tokenLockFactory | address | undefined | | _generalParameters | IHATVaultsRegistry.GeneralParameters | undefined | -| _bountyGovernanceHAT | uint256 | undefined | -| _bountyHackerHATVested | uint256 | undefined | +| _defaultGovernanceFee | uint16 | undefined | +| _governanceFeeReceiver | address | undefined | | _hatGovernance | address | undefined | | _defaultArbitrator | address | undefined | | _defaultChallengePeriod | uint256 | undefined | @@ -976,13 +856,13 @@ Emitted when a new default challenge timeout period is set |---|---|---| | _defaultChallengeTimeOutPeriod | uint256 | undefined | -### SetDefaultHATBountySplit +### SetDefaultGovernanceFee ```solidity -event SetDefaultHATBountySplit(uint256 _defaultBountyGovernanceHAT, uint256 _defaultBountyHackerHATVested) +event SetDefaultGovernanceFee(uint16 _defaultGovernanceFee) ``` -Emitted when a new default HAT bounty split is set +Emitted when a new default governance fee is set @@ -990,8 +870,7 @@ Emitted when a new default HAT bounty split is set | Name | Type | Description | |---|---|---| -| _defaultBountyGovernanceHAT | uint256 | undefined | -| _defaultBountyHackerHATVested | uint256 | undefined | +| _defaultGovernanceFee | uint16 | undefined | ### SetEmergencyPaused @@ -1025,6 +904,22 @@ Emitted when a new fee setter is set |---|---|---| | _feeSetter `indexed` | address | undefined | +### SetGovernanceFeeReceiver + +```solidity +event SetGovernanceFeeReceiver(address indexed _governaceFeeReceiver) +``` + +Emitted when a new fee receiver address is set + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _governaceFeeReceiver `indexed` | address | undefined | + ### SetHATClaimsManagerImplementation ```solidity @@ -1090,22 +985,6 @@ Emitted when a new timelock delay for setting the max bounty is set |---|---|---| | _delay | uint256 | undefined | -### SetSwapToken - -```solidity -event SetSwapToken(address indexed _swapToken) -``` - -Emitted when a new swap token is set - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _swapToken `indexed` | address | undefined | - ### SetVaultVisibility ```solidity @@ -1157,25 +1036,6 @@ Emitted when new durations are set for withdraw period and safety period | _withdrawPeriod | uint256 | undefined | | _safetyPeriod | uint256 | undefined | -### SwapAndSend - -```solidity -event SwapAndSend(address indexed _beneficiary, uint256 _amountSwapped, uint256 _amountSent, address indexed _tokenLock) -``` - -Emitted when a swap of vault tokens to HAT tokens is done and the HATS tokens are sent to beneficiary through vesting contract - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _beneficiary `indexed` | address | undefined | -| _amountSwapped | uint256 | undefined | -| _amountSent | uint256 | undefined | -| _tokenLock `indexed` | address | undefined | - ### VaultCreated ```solidity @@ -1199,28 +1059,6 @@ event VaultCreated(address indexed _vault, address indexed _claimsManager, IHATV ## Errors -### AmountSwappedLessThanMinimum - -```solidity -error AmountSwappedLessThanMinimum() -``` - -Raised on {swapAndSend} if the amount that was recieved in the swap was less than the minimum amount specified - - - - -### AmountToSwapIsZero - -```solidity -error AmountToSwapIsZero() -``` - -Raised on {swapAndSend} if the amount to swap is zero - - - - ### ChallengePeriodTooLong ```solidity @@ -1287,6 +1125,17 @@ Raised on {setMaxBountyDelay} if the max bounty to be set is shorter than 2 days +### FeeCannotBeMoreThanMaxFee + +```solidity +error FeeCannotBeMoreThanMaxFee() +``` + +Raised on {setDefaultGovernanceFee} if the fee to be set is greater than 35% (defined as 3500) + + + + ### HatVestingDurationSmallerThanPeriods ```solidity @@ -1342,28 +1191,6 @@ Raised on {setWithdrawSafetyPeriod} if the safety period to be set is longer tha -### SwapFailed - -```solidity -error SwapFailed() -``` - -Raised on {swapAndSend} if the swap was not successful - - - - -### TotalHatsSplitPercentageShouldBeUpToMaxHATSplit - -```solidity -error TotalHatsSplitPercentageShouldBeUpToMaxHATSplit() -``` - -Raised on {setDefaultHATBountySplit} if the split to be set is greater than 20% (defined as 2000) - - - - ### WithdrawPeriodTooShort ```solidity diff --git a/docs/dodoc/interfaces/IHATClaimsManager.md b/docs/dodoc/interfaces/IHATClaimsManager.md index 08794a35..84a69227 100644 --- a/docs/dodoc/interfaces/IHATClaimsManager.md +++ b/docs/dodoc/interfaces/IHATClaimsManager.md @@ -139,30 +139,13 @@ Returns the address of the vault's arbitrator If no specific value for this |---|---|---| | _0 | address | The address of the vault's arbitrator | -### getBountyGovernanceHAT - -```solidity -function getBountyGovernanceHAT() external view returns (uint16) -``` - -Returns the vault HAT bounty split part that goes to the governance If no specific value for this vault has been set, the registry's default value will be returned. - - - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | uint16 | The vault's HAT bounty split part that goes to the governance | - -### getBountyHackerHATVested +### getChallengePeriod ```solidity -function getBountyHackerHATVested() external view returns (uint16) +function getChallengePeriod() external view returns (uint32) ``` -Returns the vault HAT bounty split part that is vested for the hacker If no specific value for this vault has been set, the registry's default value will be returned. +Returns the period of time after a claim for a bounty payout has been submitted that it can be challenged by the arbitrator. If no specific value for this vault has been set, the registry's default value will be returned. @@ -171,15 +154,15 @@ Returns the vault HAT bounty split part that is vested for the hacker If no spec | Name | Type | Description | |---|---|---| -| _0 | uint16 | The vault's HAT bounty split part that is vested for the hacker | +| _0 | uint32 | The vault's challenge period | -### getChallengePeriod +### getChallengeTimeOutPeriod ```solidity -function getChallengePeriod() external view returns (uint32) +function getChallengeTimeOutPeriod() external view returns (uint32) ``` -Returns the period of time after a claim for a bounty payout has been submitted that it can be challenged by the arbitrator. If no specific value for this vault has been set, the registry's default value will be returned. +Returns the period of time after which a claim for a bounty payout can be dismissed by anyone. If no specific value for this vault has been set, the registry's default value will be returned. @@ -188,15 +171,15 @@ Returns the period of time after a claim for a bounty payout has been submitted | Name | Type | Description | |---|---|---| -| _0 | uint32 | The vault's challenge period | +| _0 | uint32 | The vault's challenge timeout period | -### getChallengeTimeOutPeriod +### getGovernanceFee ```solidity -function getChallengeTimeOutPeriod() external view returns (uint32) +function getGovernanceFee() external view returns (uint16) ``` -Returns the period of time after which a claim for a bounty payout can be dismissed by anyone. If no specific value for this vault has been set, the registry's default value will be returned. +Returns the vault fee split that goes to the governance If no specific value for this vault has been set, the registry's default value will be returned. @@ -205,7 +188,7 @@ Returns the period of time after which a claim for a bounty payout can be dismis | Name | Type | Description | |---|---|---| -| _0 | uint32 | The vault's challenge timeout period | +| _0 | uint16 | The vault's fee split that goes to the governance | ### initialize @@ -356,13 +339,13 @@ Set new committee address. Can be called by existing committee, or by the the va |---|---|---| | _committee | address | The address of the new committee | -### setHATBountySplit +### setGovernanceFee ```solidity -function setHATBountySplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) external nonpayable +function setGovernanceFee(uint16 _governanceFee) external nonpayable ``` -Called by the registry's owner to set the vault HAT token bounty split upon an approval. If the value passed is the special "null" value the vault will use the registry's default value. +Called by the registry's owner to set the fee percentage for payouts If the value passed is the special "null" value the vault will use the registry's default value. @@ -370,8 +353,7 @@ Called by the registry's owner to set the vault HAT token bounty split upon | Name | Type | Description | |---|---|---| -| _bountyGovernanceHAT | uint16 | The HAT bounty for governance | -| _bountyHackerHATVested | uint16 | The HAT bounty vested for the hacker | +| _governanceFee | uint16 | The fee percentage for governance | ### setMaxBounty @@ -608,10 +590,10 @@ event SetCommittee(address indexed _committee) |---|---|---| | _committee `indexed` | address | undefined | -### SetHATBountySplit +### SetGovernanceFee ```solidity -event SetHATBountySplit(uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVested) +event SetGovernanceFee(uint16 _governanceFee) ``` @@ -622,8 +604,7 @@ event SetHATBountySplit(uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVe | Name | Type | Description | |---|---|---| -| _bountyGovernanceHAT | uint256 | undefined | -| _bountyHackerHATVested | uint256 | undefined | +| _governanceFee | uint16 | undefined | ### SetMaxBounty @@ -820,6 +801,17 @@ error DelayPeriodForSettingMaxBountyHadNotPassed() +### FeeCannotBeMoreThanMaxFee + +```solidity +error FeeCannotBeMoreThanMaxFee() +``` + + + + + + ### MaxBountyCannotBeMoreThanMaxBountyLimit ```solidity diff --git a/docs/dodoc/interfaces/IHATVaultsRegistry.md b/docs/dodoc/interfaces/IHATVaultsRegistry.md index 1604fa09..2b77687a 100644 --- a/docs/dodoc/interfaces/IHATVaultsRegistry.md +++ b/docs/dodoc/interfaces/IHATVaultsRegistry.md @@ -10,24 +10,22 @@ The Hats.finance Vault Registry is used to deploy Hats.finance vaults and manage ## Methods -### addTokensToSwap +### MAX_GOVERNANCE_FEE ```solidity -function addTokensToSwap(contract IERC20 _asset, address _hacker, uint256 _hackersHatReward, uint256 _governanceHatReward) external nonpayable +function MAX_GOVERNANCE_FEE() external view returns (uint16) ``` -Transfer the part of the bounty that is supposed to be swapped into HAT tokens from the HATVault to the registry, and keep track of the amounts to be swapped and sent/burnt in a later transaction +Get the max governance fee -#### Parameters + +#### Returns | Name | Type | Description | |---|---|---| -| _asset | contract IERC20 | The vault's native token | -| _hacker | address | The address of the beneficiary of the bounty | -| _hackersHatReward | uint256 | The amount of the vault's native token to be swapped to HAT tokens and sent to the hacker via a vesting contract | -| _governanceHatReward | uint256 | The amount of the vault's native token to be swapped to HAT tokens and sent to governance | +| _0 | uint16 | The max governance fee | ### createVault @@ -70,30 +68,13 @@ Get the default arbitrator address |---|---|---| | _0 | address | The default arbitrator address | -### defaultBountyGovernanceHAT - -```solidity -function defaultBountyGovernanceHAT() external view returns (uint16) -``` - -Get the default percentage of the total bounty to be swapped to HATs and sent to governance - - - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | uint16 | The default percentage of the total bounty to be swapped to HATs and sent to governance | - -### defaultBountyHackerHATVested +### defaultChallengePeriod ```solidity -function defaultBountyHackerHATVested() external view returns (uint16) +function defaultChallengePeriod() external view returns (uint32) ``` -Get the default percentage of the total bounty to be swapped to HATs and sent to the hacker via vesting contract +Get the default challenge period @@ -102,15 +83,15 @@ Get the default percentage of the total bounty to be swapped to HATs and sent to | Name | Type | Description | |---|---|---| -| _0 | uint16 | The default percentage of the total bounty to be swapped to HATs and sent to the hacker via vesting contract | +| _0 | uint32 | The default challenge period | -### defaultChallengePeriod +### defaultChallengeTimeOutPeriod ```solidity -function defaultChallengePeriod() external view returns (uint32) +function defaultChallengeTimeOutPeriod() external view returns (uint32) ``` -Get the default challenge period +Get the default challenge time out period @@ -119,15 +100,15 @@ Get the default challenge period | Name | Type | Description | |---|---|---| -| _0 | uint32 | The default challenge period | +| _0 | uint32 | The default challenge time out period | -### defaultChallengeTimeOutPeriod +### defaultGovernanceFee ```solidity -function defaultChallengeTimeOutPeriod() external view returns (uint32) +function defaultGovernanceFee() external view returns (uint16) ``` -Get the default challenge time out period +Get the default fee percentage of the total bounty @@ -136,7 +117,7 @@ Get the default challenge time out period | Name | Type | Description | |---|---|---| -| _0 | uint32 | The default challenge time out period | +| _0 | uint16 | The default fee percentage of the total bounty | ### feeSetter @@ -257,6 +238,23 @@ Returns the withdraw request pending period for all vaults - period of time that |---|---|---| | _0 | uint256 | Withdraw request pending period for all vaults | +### governanceFeeReceiver + +```solidity +function governanceFeeReceiver() external view returns (address) +``` + +Get the fee receiver address + + + + +#### Returns + +| Name | Type | Description | +|---|---|---| +| _0 | address | The address of the fee receiver | + ### isEmergencyPaused ```solidity @@ -371,13 +369,13 @@ Called by governance to set the default challenge timeout |---|---|---| | _defaultChallengeTimeOutPeriod | uint32 | The Default challenge timeout | -### setDefaultHATBountySplit +### setDefaultGovernanceFee ```solidity -function setDefaultHATBountySplit(uint16 _defaultBountyGovernanceHAT, uint16 _defaultBountyHackerHATVested) external nonpayable +function setDefaultGovernanceFee(uint16 _defaultGovernanceFee) external nonpayable ``` -Called by governance to set the default percentage of each claim bounty that will be swapped for hats and sent to the governance or vested for the hacker +Called by governance to set the default fee percentage @@ -385,8 +383,7 @@ Called by governance to set the default percentage of each claim bounty that wil | Name | Type | Description | |---|---|---| -| _defaultBountyGovernanceHAT | uint16 | The HAT bounty for governance | -| _defaultBountyHackerHATVested | uint16 | The HAT bounty vested for the hacker | +| _defaultGovernanceFee | uint16 | The fee for governance | ### setEmergencyPaused @@ -420,13 +417,13 @@ Called by governance to set the fee setter role |---|---|---| | _feeSetter | address | Address of new fee setter | -### setHatVestingParams +### setGovernanceFeeReceiver ```solidity -function setHatVestingParams(uint32 _duration, uint32 _periods) external nonpayable +function setGovernanceFeeReceiver(address _governanceFeeReceiver) external nonpayable ``` -Called by governance to set vesting params for rewarding hackers with rewardToken, for all vaults +Called by governance to set the fee receiver address @@ -434,16 +431,15 @@ Called by governance to set vesting params for rewarding hackers with rewardToke | Name | Type | Description | |---|---|---| -| _duration | uint32 | Duration of the vesting period. Must be less than 180 days. | -| _periods | uint32 | The number of vesting periods. Must be more than 0 and less then the vesting duration. | +| _governanceFeeReceiver | address | The receiver of the fees from the payouts of the vaults | -### setMaxBountyDelay +### setHatVestingParams ```solidity -function setMaxBountyDelay(uint32 _delay) external nonpayable +function setHatVestingParams(uint32 _duration, uint32 _periods) external nonpayable ``` -Called by governance to set the timelock delay for setting the max bounty (the time between setPendingMaxBounty and setMaxBounty) +Called by governance to set vesting params for rewarding hackers with rewardToken, for all vaults @@ -451,15 +447,16 @@ Called by governance to set the timelock delay for setting the max bounty (the t | Name | Type | Description | |---|---|---| -| _delay | uint32 | The time period for the delay. Must be at least 2 days. | +| _duration | uint32 | Duration of the vesting period. Must be less than 180 days. | +| _periods | uint32 | The number of vesting periods. Must be more than 0 and less then the vesting duration. | -### setSwapToken +### setMaxBountyDelay ```solidity -function setSwapToken(address _swapToken) external nonpayable +function setMaxBountyDelay(uint32 _delay) external nonpayable ``` -Called by governance to set a new swap token +Called by governance to set the timelock delay for setting the max bounty (the time between setPendingMaxBounty and setMaxBounty) @@ -467,7 +464,7 @@ Called by governance to set a new swap token | Name | Type | Description | |---|---|---| -| _swapToken | address | the new swap token address | +| _delay | uint32 | The time period for the delay. Must be at least 2 days. | ### setVaultImplementations @@ -537,26 +534,6 @@ Called by governance to set the withdraw period and safety period, which are alw | _withdrawPeriod | uint32 | Amount of time during which withdrawals are enabled, and the bounty split can be changed by the governance. Must be at least 1 hour. | | _safetyPeriod | uint32 | Amount of time during which claims for bounties can be submitted and withdrawals are disabled. Must be at most 6 hours. | -### swapAndSend - -```solidity -function swapAndSend(address _asset, address[] _beneficiaries, uint256 _amountOutMinimum, address _routingContract, bytes _routingPayload) external nonpayable -``` - -Called by governance to swap the given asset to HAT tokens and distribute the HAT tokens: Send to governance their share and send to beneficiaries their share through a vesting contract. - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _asset | address | The address of the token to be swapped to HAT tokens | -| _beneficiaries | address[] | Addresses of beneficiaries | -| _amountOutMinimum | uint256 | Minimum amount of HAT tokens at swap | -| _routingContract | address | Routing contract to call for the swap | -| _routingPayload | bytes | Payload to send to the _routingContract for the swap | - ### validateChallengePeriod ```solidity @@ -589,23 +566,6 @@ Check that the given challenge timeout period is legal, meaning that it is great |---|---|---| | _challengeTimeOutPeriod | uint32 | The challenge timeout period to check | -### validateHATSplit - -```solidity -function validateHATSplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) external pure -``` - - - -*Check that a given hats bounty split is legal, meaning that: Each entry is a number between 0 and less than `MAX_HAT_SPLIT`. Total splits should be less than `MAX_HAT_SPLIT`. function will revert in case the bounty split is not legal.* - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _bountyGovernanceHAT | uint16 | The HAT bounty for governance | -| _bountyHackerHATVested | uint16 | The HAT bounty vested for the hacker | - ## Events @@ -630,7 +590,7 @@ Emitted when a claim is logged ### RegistryCreated ```solidity -event RegistryCreated(address _hatVaultImplementation, address _hatClaimsManagerImplementation, address _HAT, address _tokenLockFactory, IHATVaultsRegistry.GeneralParameters _generalParameters, uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVested, address _hatGovernance, address _defaultArbitrator, uint256 _defaultChallengePeriod, uint256 _defaultChallengeTimeOutPeriod) +event RegistryCreated(address _hatVaultImplementation, address _hatClaimsManagerImplementation, address _tokenLockFactory, IHATVaultsRegistry.GeneralParameters _generalParameters, uint16 _defaultGovernanceFee, address _governanceFeeReceiver, address _hatGovernance, address _defaultArbitrator, uint256 _defaultChallengePeriod, uint256 _defaultChallengeTimeOutPeriod) ``` Emitted on deployment of the registry @@ -643,11 +603,10 @@ Emitted on deployment of the registry |---|---|---| | _hatVaultImplementation | address | The HATVault implementation address | | _hatClaimsManagerImplementation | address | The HATClaimsManager implementation address | -| _HAT | address | The HAT token address | | _tokenLockFactory | address | The token lock factory address | | _generalParameters | IHATVaultsRegistry.GeneralParameters | The registry's general parameters | -| _bountyGovernanceHAT | uint256 | The HAT bounty for governance | -| _bountyHackerHATVested | uint256 | The HAT bounty vested for the hacker | +| _defaultGovernanceFee | uint16 | The fee percentage for governance | +| _governanceFeeReceiver | address | The fee receiver address | | _hatGovernance | address | The registry's governance | | _defaultArbitrator | address | undefined | | _defaultChallengePeriod | uint256 | The new default challenge period | @@ -717,13 +676,13 @@ Emitted when a new default challenge timeout period is set |---|---|---| | _defaultChallengeTimeOutPeriod | uint256 | The new default challenge timeout period | -### SetDefaultHATBountySplit +### SetDefaultGovernanceFee ```solidity -event SetDefaultHATBountySplit(uint256 _defaultBountyGovernanceHAT, uint256 _defaultBountyHackerHATVested) +event SetDefaultGovernanceFee(uint16 _defaultGovernanceFee) ``` -Emitted when a new default HAT bounty split is set +Emitted when a new default governance fee is set @@ -731,8 +690,7 @@ Emitted when a new default HAT bounty split is set | Name | Type | Description | |---|---|---| -| _defaultBountyGovernanceHAT | uint256 | The new default HAT bounty part sent to governance | -| _defaultBountyHackerHATVested | uint256 | The new default HAT bounty part vseted for the hacker | +| _defaultGovernanceFee | uint16 | The new default fee part sent to governance | ### SetEmergencyPaused @@ -766,6 +724,22 @@ Emitted when a new fee setter is set |---|---|---| | _feeSetter `indexed` | address | The address of the new fee setter | +### SetGovernanceFeeReceiver + +```solidity +event SetGovernanceFeeReceiver(address indexed _governaceFeeReceiver) +``` + +Emitted when a new fee receiver address is set + + + +#### Parameters + +| Name | Type | Description | +|---|---|---| +| _governaceFeeReceiver `indexed` | address | The receiver of the fee from the payouts of the vaults | + ### SetHATClaimsManagerImplementation ```solidity @@ -831,22 +805,6 @@ Emitted when a new timelock delay for setting the max bounty is set |---|---|---| | _delay | uint256 | The time period for the delay | -### SetSwapToken - -```solidity -event SetSwapToken(address indexed _swapToken) -``` - -Emitted when a new swap token is set - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _swapToken `indexed` | address | The new swap token address | - ### SetVaultVisibility ```solidity @@ -898,25 +856,6 @@ Emitted when new durations are set for withdraw period and safety period | _withdrawPeriod | uint256 | Amount of time during which withdrawals are enabled, and the bounty split can be changed by the governance | | _safetyPeriod | uint256 | Amount of time during which claims for bounties can be submitted and withdrawals are disabled | -### SwapAndSend - -```solidity -event SwapAndSend(address indexed _beneficiary, uint256 _amountSwapped, uint256 _amountSent, address indexed _tokenLock) -``` - -Emitted when a swap of vault tokens to HAT tokens is done and the HATS tokens are sent to beneficiary through vesting contract - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _beneficiary `indexed` | address | Address of beneficiary | -| _amountSwapped | uint256 | Amount of vault's native tokens that was swapped | -| _amountSent | uint256 | Amount of HAT tokens sent to beneficiary | -| _tokenLock `indexed` | address | Address of the token lock contract that holds the HAT tokens (address(0) if no token lock is used) | - ### VaultCreated ```solidity @@ -940,28 +879,6 @@ event VaultCreated(address indexed _vault, address indexed _claimsManager, IHATV ## Errors -### AmountSwappedLessThanMinimum - -```solidity -error AmountSwappedLessThanMinimum() -``` - -Raised on {swapAndSend} if the amount that was recieved in the swap was less than the minimum amount specified - - - - -### AmountToSwapIsZero - -```solidity -error AmountToSwapIsZero() -``` - -Raised on {swapAndSend} if the amount to swap is zero - - - - ### ChallengePeriodTooLong ```solidity @@ -1028,6 +945,17 @@ Raised on {setMaxBountyDelay} if the max bounty to be set is shorter than 2 days +### FeeCannotBeMoreThanMaxFee + +```solidity +error FeeCannotBeMoreThanMaxFee() +``` + +Raised on {setDefaultGovernanceFee} if the fee to be set is greater than 35% (defined as 3500) + + + + ### HatVestingDurationSmallerThanPeriods ```solidity @@ -1083,28 +1011,6 @@ Raised on {setWithdrawSafetyPeriod} if the safety period to be set is longer tha -### SwapFailed - -```solidity -error SwapFailed() -``` - -Raised on {swapAndSend} if the swap was not successful - - - - -### TotalHatsSplitPercentageShouldBeUpToMaxHATSplit - -```solidity -error TotalHatsSplitPercentageShouldBeUpToMaxHATSplit() -``` - -Raised on {setDefaultHATBountySplit} if the split to be set is greater than 20% (defined as 2000) - - - - ### WithdrawPeriodTooShort ```solidity diff --git a/docs/dodoc/mocks/HATClaimsManagerV2Mock.md b/docs/dodoc/mocks/HATClaimsManagerV2Mock.md index ae4fe147..d5b5d28a 100644 --- a/docs/dodoc/mocks/HATClaimsManagerV2Mock.md +++ b/docs/dodoc/mocks/HATClaimsManagerV2Mock.md @@ -149,7 +149,7 @@ Returns the claims manager's version ### activeClaim ```solidity -function activeClaim() external view returns (bytes32 claimId, address beneficiary, uint16 bountyPercentage, address committee, uint32 createdAt, uint32 challengedAt, uint256 bountyGovernanceHAT, uint256 bountyHackerHATVested, address arbitrator, uint32 challengePeriod, uint32 challengeTimeOutPeriod, bool arbitratorCanChangeBounty, bool arbitratorCanChangeBeneficiary) +function activeClaim() external view returns (bytes32 claimId, address beneficiary, uint16 bountyPercentage, address committee, uint32 createdAt, uint32 challengedAt, uint16 governanceFee, address arbitrator, uint32 challengePeriod, uint32 challengeTimeOutPeriod, bool arbitratorCanChangeBounty, bool arbitratorCanChangeBeneficiary) ``` @@ -167,8 +167,7 @@ function activeClaim() external view returns (bytes32 claimId, address beneficia | committee | address | undefined | | createdAt | uint32 | undefined | | challengedAt | uint32 | undefined | -| bountyGovernanceHAT | uint256 | undefined | -| bountyHackerHATVested | uint256 | undefined | +| governanceFee | uint16 | undefined | | arbitrator | address | undefined | | challengePeriod | uint32 | undefined | | challengeTimeOutPeriod | uint32 | undefined | @@ -374,30 +373,13 @@ See {IHATClaimsManager-getArbitrator}. |---|---|---| | _0 | address | undefined | -### getBountyGovernanceHAT - -```solidity -function getBountyGovernanceHAT() external view returns (uint16) -``` - -See {IHATClaimsManager-getBountyGovernanceHAT}. - - - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | uint16 | undefined | - -### getBountyHackerHATVested +### getChallengePeriod ```solidity -function getBountyHackerHATVested() external view returns (uint16) +function getChallengePeriod() external view returns (uint32) ``` -See {IHATClaimsManager-getBountyHackerHATVested}. +See {IHATClaimsManager-getChallengePeriod}. @@ -406,15 +388,15 @@ See {IHATClaimsManager-getBountyHackerHATVested}. | Name | Type | Description | |---|---|---| -| _0 | uint16 | undefined | +| _0 | uint32 | undefined | -### getChallengePeriod +### getChallengeTimeOutPeriod ```solidity -function getChallengePeriod() external view returns (uint32) +function getChallengeTimeOutPeriod() external view returns (uint32) ``` -See {IHATClaimsManager-getChallengePeriod}. +See {IHATClaimsManager-getChallengeTimeOutPeriod}. @@ -425,13 +407,13 @@ See {IHATClaimsManager-getChallengePeriod}. |---|---|---| | _0 | uint32 | undefined | -### getChallengeTimeOutPeriod +### getGovernanceFee ```solidity -function getChallengeTimeOutPeriod() external view returns (uint32) +function getGovernanceFee() external view returns (uint16) ``` -See {IHATClaimsManager-getChallengeTimeOutPeriod}. +See {IHATClaimsManager-getGovernanceFee}. @@ -440,7 +422,7 @@ See {IHATClaimsManager-getChallengeTimeOutPeriod}. | Name | Type | Description | |---|---|---| -| _0 | uint32 | undefined | +| _0 | uint16 | undefined | ### getVersion @@ -671,13 +653,13 @@ See {IHATClaimsManager-setCommittee}. |---|---|---| | _committee | address | undefined | -### setHATBountySplit +### setGovernanceFee ```solidity -function setHATBountySplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) external nonpayable +function setGovernanceFee(uint16 _governanceFee) external nonpayable ``` -See {IHATClaimsManager-setHATBountySplit}. +See {IHATClaimsManager-setGoveranceFee}. @@ -685,8 +667,7 @@ See {IHATClaimsManager-setHATBountySplit}. | Name | Type | Description | |---|---|---| -| _bountyGovernanceHAT | uint16 | undefined | -| _bountyHackerHATVested | uint16 | undefined | +| _governanceFee | uint16 | undefined | ### setMaxBounty @@ -1040,10 +1021,10 @@ event SetCommittee(address indexed _committee) |---|---|---| | _committee `indexed` | address | undefined | -### SetHATBountySplit +### SetGovernanceFee ```solidity -event SetHATBountySplit(uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVested) +event SetGovernanceFee(uint16 _governanceFee) ``` @@ -1054,8 +1035,7 @@ event SetHATBountySplit(uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVe | Name | Type | Description | |---|---|---| -| _bountyGovernanceHAT | uint256 | undefined | -| _bountyHackerHATVested | uint256 | undefined | +| _governanceFee | uint16 | undefined | ### SetMaxBounty @@ -1252,6 +1232,17 @@ error DelayPeriodForSettingMaxBountyHadNotPassed() +### FeeCannotBeMoreThanMaxFee + +```solidity +error FeeCannotBeMoreThanMaxFee() +``` + + + + + + ### MaxBountyCannotBeMoreThanMaxBountyLimit ```solidity diff --git a/docs/dodoc/mocks/HatVaultForConnectorMock.md b/docs/dodoc/mocks/HatVaultForConnectorMock.md index de295522..46d379f5 100644 --- a/docs/dodoc/mocks/HatVaultForConnectorMock.md +++ b/docs/dodoc/mocks/HatVaultForConnectorMock.md @@ -47,7 +47,7 @@ function MAX_BOUNTY_LIMIT() external view returns (uint16) ### activeClaim ```solidity -function activeClaim() external view returns (bytes32 claimId, address beneficiary, uint16 bountyPercentage, address committee, uint32 createdAt, uint32 challengedAt, uint256 bountyGovernanceHAT, uint256 bountyHackerHATVested, address arbitrator, uint32 challengePeriod, uint32 challengeTimeOutPeriod, bool arbitratorCanChangeBounty, bool arbitratorCanChangeBeneficiary) +function activeClaim() external view returns (bytes32 claimId, address beneficiary, uint16 bountyPercentage, address committee, uint32 createdAt, uint32 challengedAt, uint256 governanceFee, address arbitrator, uint32 challengePeriod, uint32 challengeTimeOutPeriod, bool arbitratorCanChangeBounty, bool arbitratorCanChangeBeneficiary) ``` @@ -65,8 +65,7 @@ function activeClaim() external view returns (bytes32 claimId, address beneficia | committee | address | undefined | | createdAt | uint32 | undefined | | challengedAt | uint32 | undefined | -| bountyGovernanceHAT | uint256 | undefined | -| bountyHackerHATVested | uint256 | undefined | +| governanceFee | uint256 | undefined | | arbitrator | address | undefined | | challengePeriod | uint32 | undefined | | challengeTimeOutPeriod | uint32 | undefined | diff --git a/docs/dodoc/mocks/ISwapRouter.md b/docs/dodoc/mocks/ISwapRouter.md deleted file mode 100644 index b9172c4e..00000000 --- a/docs/dodoc/mocks/ISwapRouter.md +++ /dev/null @@ -1,120 +0,0 @@ -# ISwapRouter - - - -> Router token swapping functionality - -Functions for swapping tokens via Uniswap V3 - - - -## Methods - -### WETH9 - -```solidity -function WETH9() external pure returns (address) -``` - - - - - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | address | undefined | - -### exactInput - -```solidity -function exactInput(ISwapRouter.ExactInputParams params) external payable returns (uint256 amountOut) -``` - - - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| params | ISwapRouter.ExactInputParams | undefined | - -#### Returns - -| Name | Type | Description | -|---|---|---| -| amountOut | uint256 | undefined | - -### exactInputSingle - -```solidity -function exactInputSingle(ISwapRouter.ExactInputSingleParams params) external payable returns (uint256 amountOut) -``` - - - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| params | ISwapRouter.ExactInputSingleParams | undefined | - -#### Returns - -| Name | Type | Description | -|---|---|---| -| amountOut | uint256 | undefined | - -### exactOutput - -```solidity -function exactOutput(ISwapRouter.ExactOutputParams params) external payable returns (uint256 amountIn) -``` - - - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| params | ISwapRouter.ExactOutputParams | undefined | - -#### Returns - -| Name | Type | Description | -|---|---|---| -| amountIn | uint256 | undefined | - -### exactOutputSingle - -```solidity -function exactOutputSingle(ISwapRouter.ExactOutputSingleParams params) external payable returns (uint256 amountIn) -``` - - - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| params | ISwapRouter.ExactOutputSingleParams | undefined | - -#### Returns - -| Name | Type | Description | -|---|---|---| -| amountIn | uint256 | undefined | - - - - diff --git a/docs/dodoc/mocks/UniSwapV3RouterMock.md b/docs/dodoc/mocks/UniSwapV3RouterMock.md deleted file mode 100644 index 96af400b..00000000 --- a/docs/dodoc/mocks/UniSwapV3RouterMock.md +++ /dev/null @@ -1,104 +0,0 @@ -# UniSwapV3RouterMock - - - - - - - - - -## Methods - -### WETH9 - -```solidity -function WETH9() external view returns (address) -``` - - - - - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | address | undefined | - -### exactInput - -```solidity -function exactInput(ISwapRouter.ExactInputParams _params) external nonpayable returns (uint256 amount) -``` - - - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _params | ISwapRouter.ExactInputParams | undefined | - -#### Returns - -| Name | Type | Description | -|---|---|---| -| amount | uint256 | undefined | - -### returnType - -```solidity -function returnType() external view returns (enum UniSwapV3RouterMock.ReturnType) -``` - - - - - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | enum UniSwapV3RouterMock.ReturnType | undefined | - -### setUsePartialAmountFlag - -```solidity -function setUsePartialAmountFlag(bool _usePartialAmountFlag) external nonpayable -``` - - - - - -#### Parameters - -| Name | Type | Description | -|---|---|---| -| _usePartialAmountFlag | bool | undefined | - -### usePartialAmountFlag - -```solidity -function usePartialAmountFlag() external view returns (bool) -``` - - - - - - -#### Returns - -| Name | Type | Description | -|---|---|---| -| _0 | bool | undefined | - - - - diff --git a/scripts/deployments/config.json b/scripts/deployments/config.json index 6ec6d368..65dfb18c 100644 --- a/scripts/deployments/config.json +++ b/scripts/deployments/config.json @@ -36,8 +36,7 @@ ] }], "hatVaultsRegistryConf": { - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "500" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -82,8 +81,7 @@ ] }], "hatVaultsRegistryConf": { - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "500" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -123,8 +121,7 @@ ] }], "hatVaultsRegistryConf": { - "bountyGovernanceHAT": "1000", - "bountyHackerHATVested": "500" + "governanceFee": "1000" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", @@ -139,8 +136,7 @@ "rewardControllersConf": [], "hatToken": "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", "hatVaultsRegistryConf": { - "bountyGovernanceHAT": "0", - "bountyHackerHATVested": "0" + "governanceFee": "0" }, "hatVaultsNFTConf": { "merkleTreeIPFSRef": "", diff --git a/scripts/deployments/hats-verify.js b/scripts/deployments/hats-verify.js index 195c2e97..c6366eae 100644 --- a/scripts/deployments/hats-verify.js +++ b/scripts/deployments/hats-verify.js @@ -47,8 +47,12 @@ async function main(addresses, config) { let rewardControllerImplementations = addresses["rewardControllerImplementations"]; let hatVaultImplementation = addresses["hatVaultImplementation"]; - let bountyGovernanceHAT = config["hatVaultsRegistryConf"]["bountyGovernanceHAT"]; - let bountyHackerHATVested = config["hatVaultsRegistryConf"]["bountyHackerHATVested"]; + let governanceFee = config["hatVaultsRegistryConf"]["governanceFee"]; + let governanceFeeReceiver = config["hatVaultsRegistryConf"]["governanceFeeReceiver"]; + + if (!governanceFeeReceiver) { + governanceFeeReceiver = governance; + } await verifyContract(arbitrator, []); for (const rewardControllerImplementation of rewardControllerImplementations ) { @@ -59,9 +63,8 @@ async function main(addresses, config) { hatVaultImplementation, hatTimelockController, arbitrator, - hatToken, - bountyGovernanceHAT, - bountyHackerHATVested, + governanceFee, + governanceFeeReceiver, tokenLockFactory ]); diff --git a/scripts/deployments/hatvaultsregistry-deploy.js b/scripts/deployments/hatvaultsregistry-deploy.js index b7254ae4..a2cf0d86 100644 --- a/scripts/deployments/hatvaultsregistry-deploy.js +++ b/scripts/deployments/hatvaultsregistry-deploy.js @@ -88,8 +88,12 @@ async function main(config) { tokenLockFactory = "0x6E6578bC77984A1eF3469af009cFEC5529aEF9F3"; } - let bountyGovernanceHAT = config["hatVaultsRegistryConf"]["bountyGovernanceHAT"]; - let bountyHackerHATVested = config["hatVaultsRegistryConf"]["bountyHackerHATVested"]; + let governanceFee = config["hatVaultsRegistryConf"]["governanceFee"]; + let governanceFeeReceiver = config["hatVaultsRegistryConf"]["governanceFeeReceiver"]; + + if (!governanceFeeReceiver) { + governanceFeeReceiver = governance; + } const HATVaultsRegistry = await ethers.getContractFactory("HATVaultsRegistry"); const hatVaultsRegistry = await HATVaultsRegistry.deploy( @@ -97,9 +101,8 @@ async function main(config) { hatClaimsManagerImplementation.address, governance, arbitrator, - hatToken, - bountyGovernanceHAT, - bountyHackerHATVested, + governanceFee, + governanceFeeReceiver, tokenLockFactory, ); diff --git a/test/common.js b/test/common.js index c570ae05..c34c1f13 100644 --- a/test/common.js +++ b/test/common.js @@ -3,7 +3,6 @@ const HATClaimsManager = artifacts.require("./HATClaimsManager.sol"); const HATVaultsRegistry = artifacts.require("./HATVaultsRegistry.sol"); const HATTokenMock = artifacts.require("./HATTokenMock.sol"); const ERC20Mock = artifacts.require("./ERC20Mock.sol"); -const UniSwapV3RouterMock = artifacts.require("./UniSwapV3RouterMock.sol"); const TokenLockFactory = artifacts.require("./TokenLockFactory.sol"); const HATTokenLock = artifacts.require("./HATTokenLock.sol"); const RewardController = artifacts.require("./RewardController.sol"); @@ -53,8 +52,8 @@ function assertVMException(error, expectedError = "") { expectedError + "'"; if (expectedError) { assert( - error.message === expectedErrorMessage || - error.message === expectedReasonString || + error.message.includes(expectedErrorMessage) || + error.message.includes(expectedReasonString) || // Needed for now because hardhat doesn't fully support the viaIR compiler setting error.message === "Returned error: VM Exception while processing transaction: revert with unrecognized return data or custom error" || error.message === "Transaction reverted and Hardhat couldn't infer the reason.", @@ -75,11 +74,9 @@ const setup = async function( startBlock : (await web3.eth.getBlock("latest")).number, maxBounty : 8000, bountySplit : [7500, 2000, 500], - hatBountySplit : [1500, 500], + governanceFee : 2000, halvingAfterBlock : 10, - routerReturnType : 0, allocPoint : 100, - weth : false, rewardInVaults : 2500000, challengePeriod: 60 * 60 * 24, setDefaultArbitrator: true, @@ -90,11 +87,6 @@ const setup = async function( hatToken = await HATTokenMock.new(accounts[0]); await hatToken.setTransferable({from: accounts[0]}); stakingToken = await ERC20Mock.new("Staking", "STK"); - let wethAddress = utils.NULL_ADDRESS; - if (options.weth) { - wethAddress = stakingToken.address; - } - router = await UniSwapV3RouterMock.new(options.routerReturnType, wethAddress); var tokenLock = await HATTokenLock.new(); tokenLockFactory = await TokenLockFactory.new(tokenLock.address, accounts[0]); @@ -109,8 +101,7 @@ const setup = async function( epochRewardPerBlock }], hatVaultsRegistryConf: { - bountyGovernanceHAT: options.hatBountySplit[0], - bountyHackerHATVested: options.hatBountySplit[1] + governanceFee: options.governanceFee }, silent: true }); @@ -125,7 +116,7 @@ const setup = async function( accounts[0], web3.utils.toWei((2500000 + options.rewardInVaults).toString()) ); - await hatToken.mint(router.address, web3.utils.toWei("2500000")); + if (options.rewardInVaults > 0) { await hatToken.mint(accounts[0], web3.utils.toWei(options.rewardInVaults.toString())); await hatToken.transfer( @@ -156,6 +147,7 @@ const setup = async function( isTokenLockRevocable: options.isTokenLockRevocable, maxBounty: options.maxBounty, bountySplit: options.bountySplit, + governanceFee: options.governanceFee, vestingDuration: 86400, vestingPeriods: 10 } @@ -191,7 +183,6 @@ const setup = async function( tokenLockFactory, rewardController, hatVaultsExpectedHatsBalance, - router, someAccount: accounts[5], // an account without any special role stakingToken, vault, diff --git a/test/hattimelockcontroller.js b/test/hattimelockcontroller.js index afb3f5b0..1cc6f8f6 100644 --- a/test/hattimelockcontroller.js +++ b/test/hattimelockcontroller.js @@ -4,12 +4,10 @@ const HATClaimsManager = artifacts.require("./HATClaimsManager.sol"); const HATTimelockController = artifacts.require("./HATTimelockController.sol"); const HATTokenMock = artifacts.require("./HATTokenMock.sol"); const ERC20Mock = artifacts.require("./ERC20Mock.sol"); -const UniSwapV3RouterMock = artifacts.require("./UniSwapV3RouterMock.sol"); const TokenLockFactory = artifacts.require("./TokenLockFactory.sol"); const HATTokenLock = artifacts.require("./HATTokenLock.sol"); const RewardController = artifacts.require("./RewardController.sol"); const HATGovernanceArbitrator = artifacts.require("./HATGovernanceArbitrator.sol"); -const utils = require("./utils.js"); const { deployHATVaults } = require("../scripts/deployments/hatvaultsregistry-deploy"); @@ -19,7 +17,6 @@ var claimsManager; var rewardController; var hatTimelockController; var hatToken; -var router; var stakingToken; var tokenLockFactory; var arbitratorContract; @@ -39,21 +36,15 @@ const setup = async function( startBlock = 0, maxBounty = 8000, bountySplit = [7000, 2500, 500], - hatBountySplit = [1000, 500], + governanceFee = 1000, halvingAfterBlock = 10, - routerReturnType = 0, allocPoint = 100, - weth = true, rewardInVaults = 2500000 ) { hatToken = await HATTokenMock.new(accounts[0]); await hatToken.setTransferable({from: accounts[0]}); stakingToken = await ERC20Mock.new("Staking", "STK"); - var wethAddress = utils.NULL_ADDRESS; - if (weth) { - wethAddress = stakingToken.address; - } - router = await UniSwapV3RouterMock.new(routerReturnType, wethAddress); + var tokenLock = await HATTokenLock.new(); tokenLockFactory = await TokenLockFactory.new(tokenLock.address, accounts[0]); let deployment = await deployHATVaults({ @@ -66,8 +57,7 @@ const setup = async function( epochRewardPerBlock }], hatVaultsRegistryConf: { - bountyGovernanceHAT: hatBountySplit[0], - bountyHackerHATVested: hatBountySplit[1] + governanceFee: governanceFee }, silent: true }); @@ -86,7 +76,7 @@ const setup = async function( accounts[0], web3.utils.toWei((2500000 + rewardInVaults).toString()) ); - await hatToken.mint(router.address, web3.utils.toWei("2500000")); + await hatToken.mint(accounts[0], web3.utils.toWei(rewardInVaults.toString())); await hatToken.transfer( rewardController.address, @@ -113,6 +103,7 @@ const setup = async function( isTokenLockRevocable: false, maxBounty: maxBounty, bountySplit: bountySplit, + governanceFee: governanceFee, vestingDuration: 86400, vestingPeriods: 10 } @@ -291,36 +282,25 @@ contract("HatTimelockController", (accounts) => { await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); }); - it("swapAndSend", async () => { + it("challenge - approve Claim ", async () => { await setup(accounts); - var staker = accounts[4]; - var staker2 = accounts[3]; + const staker = accounts[1]; + await advanceToSafetyPeriod(hatVaultsRegistry); + // we send some funds to the vault so we can pay out later when approveClaim is called + await stakingToken.mint(staker, web3.utils.toWei("2")); await stakingToken.approve(vault.address, web3.utils.toWei("1"), { from: staker, }); - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker2, - }); - await stakingToken.mint(staker, web3.utils.toWei("1")); - await stakingToken.mint(staker2, web3.utils.toWei("1")); - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); + await rewardController.updateVault(vault.address); - assert.equal(await hatToken.balanceOf(staker), 0); - await utils.increaseTime(7 * 24 * 3600); - await advanceToSafetyPeriod(hatVaultsRegistry); - const bountyPercentage = 300; - let tx = await claimsManager.submitClaim( - accounts[2], - bountyPercentage, - "description hash", - { - from: accounts[1], - } - ); + let claimId = await submitClaim(claimsManager, { accounts }); - let claimId = tx.logs[0].args._claimId; + await assertFunctionRaisesException( + claimsManager.challengeClaim(claimId), + "OnlyArbitratorOrRegistryOwner" + ); try { await arbitratorContract.approveClaim(claimsManager.address, claimId); @@ -339,86 +319,6 @@ contract("HatTimelockController", (accounts) => { } await hatTimelockController.approveClaim(arbitratorContract.address, claimsManager.address, claimId); - - let path = ethers.utils.solidityPack( - ["address", "uint24", "address"], - [stakingToken.address, 0, hatToken.address] - ); - let amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[1] - ); - let amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - let ISwapRouter = new ethers.utils.Interface(UniSwapV3RouterMock.abi); - let payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - - try { - await hatTimelockController.swapAndSend( - hatVaultsRegistry.address, - stakingToken.address, - [accounts[1]], - 0, - router.address, - payload, - { - from: accounts[3], - } - ); - assert(false, "only gov"); - } catch (ex) { - assertVMException(ex); - } - - try { - await hatVaultsRegistry.swapAndSend(stakingToken.address, [accounts[1]], 0, router.address, payload); - assert(false, "only gov"); - } catch (ex) { - assertVMException(ex); - } - - tx = await hatTimelockController.swapAndSend( - hatVaultsRegistry.address, - stakingToken.address, - [accounts[1]], - 0, - router.address, - payload, - { from: accounts[0] } - ); - - let log = ( - await hatVaultsRegistry.getPastEvents("SwapAndSend", { - fromBlock: tx.blockNumber, - toBlock: "latest", - }) - )[0]; - assert.equal(log.event, "SwapAndSend"); - assert.equal(log.args._amountSent.toString(), "0"); - }); - - it("challenge - approve Claim ", async () => { - await setup(accounts); - const staker = accounts[1]; - await advanceToSafetyPeriod(hatVaultsRegistry); - - // we send some funds to the vault so we can pay out later when approveClaim is called - await stakingToken.mint(staker, web3.utils.toWei("2")); - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker, - }); - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - await rewardController.updateVault(vault.address); - - let claimId = await submitClaim(claimsManager, { accounts }); - - await assertFunctionRaisesException( - claimsManager.challengeClaim(claimId), - "OnlyArbitratorOrRegistryOwner" - ); - - await hatTimelockController.approveClaim(arbitratorContract.address, claimsManager.address, claimId); }); it("challenge - dismiss claim", async () => { @@ -457,6 +357,7 @@ contract("HatTimelockController", (accounts) => { //creat another vault with a different committee let maxBounty = 8000; let bountySplit = [7000, 2500, 500]; + let governanceFee = 65535; var stakingToken2 = await ERC20Mock.new("Staking", "STK"); let tx = await hatVaultsRegistry.createVault( { @@ -478,6 +379,7 @@ contract("HatTimelockController", (accounts) => { isTokenLockRevocable: false, maxBounty: maxBounty, bountySplit: bountySplit, + governanceFee: governanceFee, vestingDuration: 86400, vestingPeriods: 10 } diff --git a/test/hatvaults.js b/test/hatvaults.js index 676095cc..46ba0a13 100644 --- a/test/hatvaults.js +++ b/test/hatvaults.js @@ -7,14 +7,12 @@ const HATPaymentSplitterFactory = artifacts.require("./HATPaymentSplitterFactory const HATVaultV2Mock = artifacts.require("./HATVaultV2Mock.sol"); const HATClaimsManagerV2Mock = artifacts.require("./HATClaimsManagerV2Mock.sol"); const ERC20Mock = artifacts.require("./ERC20Mock.sol"); -const UniSwapV3RouterMock = artifacts.require("./UniSwapV3RouterMock.sol"); const TokenLockFactory = artifacts.require("./TokenLockFactory.sol"); const HATTokenLock = artifacts.require("./HATTokenLock.sol"); const VaultsManagerMock = artifacts.require("./VaultsManagerMock.sol"); const EtherTransferFail = artifacts.require("./EtherTransferFail.sol"); const RewardController = artifacts.require("./RewardController.sol"); const utils = require("./utils.js"); -const ISwapRouter = new ethers.utils.Interface(UniSwapV3RouterMock.abi); const { deployHATVaults } = require("../scripts/deployments/hatvaultsregistry-deploy"); const { @@ -31,13 +29,11 @@ const { assert } = require("chai"); const { web3 } = require("hardhat"); let hatVaultImplementation; -let tokenLockFactory; let hatVaultsRegistry; let vault; let claimsManager; let rewardController; let hatToken; -let router; let stakingToken; let safeWithdrawBlocksIncrement = 3; let rewardControllerExpectedHatsBalance; @@ -67,11 +63,9 @@ const setUpGlobalVars = async function( startBlock = 0, maxBounty = 8000, bountySplit = [7500, 2000, 500], - hatBountySplit = [1500, 500], + governanceFee = 2000, halvingAfterBlock = 10, - routerReturnType = 0, allocPoint = 100, - weth = false, rewardInVaults = 2500000, challengePeriod = 60 * 60 * 24, isTokenLockRevocable = false @@ -83,11 +77,9 @@ const setUpGlobalVars = async function( startBlock, maxBounty, bountySplit, - hatBountySplit, + governanceFee, halvingAfterBlock, - routerReturnType, allocPoint, - weth, rewardInVaults, challengePeriod, setDefaultArbitrator: false, @@ -102,7 +94,6 @@ const setUpGlobalVars = async function( claimsManager = setupVars.claimsManager; stakingToken = setupVars.stakingToken; hatToken = setupVars.hatToken; - router = setupVars.router; rewardController = setupVars.rewardController; hatVaultsExpectedHatsBalance = setupVars.hatVaultsExpectedHatsBalance; rewardControllerExpectedHatsBalance = setupVars.rewardControllerExpectedHatsBalance; @@ -246,11 +237,9 @@ contract("HatVaults", (accounts) => { 50, 8000, [7500, 2000, 500], - [1500, 500], + 2000, 20, - 0, 100, - false, 2500000, false ); @@ -265,8 +254,6 @@ contract("HatVaults", (accounts) => { assert.equal(logs[0].event, "RegistryCreated"); assert.equal(logs[0].args._hatVaultImplementation, hatVaultImplementation.address); - assert.equal(logs[0].args._HAT, hatToken.address); - assert.equal(logs[0].args._tokenLockFactory, tokenLockFactory.address); let generalParameters = await hatVaultsRegistry.generalParameters(); assert.equal(logs[0].args._generalParameters.hatVestingDuration, generalParameters.hatVestingDuration.toString()); assert.equal(logs[0].args._generalParameters.hatVestingPeriods, generalParameters.hatVestingPeriods.toString()); @@ -276,8 +263,7 @@ contract("HatVaults", (accounts) => { assert.equal(logs[0].args._generalParameters.withdrawRequestPendingPeriod, generalParameters.withdrawRequestPendingPeriod.toString()); assert.equal(logs[0].args._generalParameters.setMaxBountyDelay, generalParameters.setMaxBountyDelay.toString()); assert.equal(logs[0].args._generalParameters.claimFee, generalParameters.claimFee.toString()); - assert.equal(logs[0].args._bountyGovernanceHAT, "1500"); - assert.equal(logs[0].args._bountyHackerHATVested, "500"); + assert.equal(logs[0].args._defaultGovernanceFee, "2000"); assert.equal(logs[0].args._hatGovernance, accounts[0]); assert.equal(logs[0].args._defaultArbitrator, await hatVaultsRegistry.defaultArbitrator()); assert.equal(logs[0].args._defaultChallengePeriod.toString(), (await hatVaultsRegistry.defaultChallengePeriod()).toString()); @@ -332,6 +318,7 @@ contract("HatVaults", (accounts) => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 } @@ -387,8 +374,7 @@ contract("HatVaults", (accounts) => { assert.equal(activeClaim.committee, accounts[1]); assert.equal(activeClaim.createdAt, (await web3.eth.getBlock(tx.receipt.blockNumber)).timestamp); assert.equal(activeClaim.challengedAt, "0"); - assert.equal(activeClaim.bountyGovernanceHAT, "1500"); - assert.equal(activeClaim.bountyHackerHATVested, "500"); + assert.equal(activeClaim.governanceFee, "2000"); assert.equal(activeClaim.arbitrator, accounts[0]); assert.equal(activeClaim.challengePeriod, "86400"); assert.equal(activeClaim.challengeTimeOutPeriod, "10800000"); @@ -779,6 +765,7 @@ contract("HatVaults", (accounts) => { isTokenLockRevocable: false, maxBounty: maxBounty, bountySplit: bountySplit, + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 } @@ -841,6 +828,7 @@ contract("HatVaults", (accounts) => { arbitrator: accounts[2], maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, arbitratorCanChangeBounty: true, arbitratorCanChangeBeneficiary: false, arbitratorCanSubmitClaims: false, @@ -878,6 +866,7 @@ contract("HatVaults", (accounts) => { arbitrator: accounts[2], maxBounty: maxBounty, bountySplit: bountySplit, + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 } @@ -927,6 +916,7 @@ contract("HatVaults", (accounts) => { isTokenLockRevocable: false, maxBounty: maxBounty, bountySplit: bountySplit, + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 } @@ -983,7 +973,7 @@ contract("HatVaults", (accounts) => { it("dismiss can be called by anyone after 125 days delay", async () => { var staker = accounts[1]; - await setUpGlobalVars(accounts, 0, 9000, [9000, 0, 1000], [1000, 500], 10, 0, 100, false, 2500000, 60 * 60 * 24 * 3); + await setUpGlobalVars(accounts, 0, 9000, [9000, 0, 1000], 1000, 10, 100, 2500000, 60 * 60 * 24 * 3); await advanceToSafetyPeriod(); await stakingToken.approve(vault.address, web3.utils.toWei("1"), { @@ -1018,43 +1008,64 @@ contract("HatVaults", (accounts) => { assert.equal(tx.logs[0].event, "DismissClaim"); }); - it("custom bountySplit, hatBountySplit, and max bounty", async () => { + it("custom bountySplit, governanceFee, and max bounty", async () => { try { - await setUpGlobalVars(accounts, 0, 9000, [9000, 0, 1000], [5000, 5000]); - assert(false, "cannot init with hat bounty split <= 10000"); + await setUpGlobalVars(accounts, 0, 9000, [9000, 1, 1000], 100); + assert(false, "cannot init with rewardSplit > 10000"); } catch (ex) { - assertVMException(ex); + assertVMException(ex, "TotalSplitPercentageShouldBeHundredPercent"); } try { - await setUpGlobalVars(accounts, 0, 9000, [9000, 0, 1000], [5000, 5001]); - assert(false, "cannot init with hat bounty split <= 10000"); + await setUpGlobalVars(accounts, 0, 9000, [9000, 0, 999], 100); + assert(false, "cannot init with rewardSplit < 10000"); } catch (ex) { - assertVMException(ex); + assertVMException(ex, "TotalSplitPercentageShouldBeHundredPercent"); } try { - await setUpGlobalVars(accounts, 0, 9000, [9000, 1, 1000], [100, 800]); - assert(false, "cannot init with rewardSplit > 10000"); + await setUpGlobalVars(accounts, 0, 9901, [8000, 1000, 1000], 100); + assert(false, "cannot init with max bounty > 10000"); } catch (ex) { - assertVMException(ex, "TotalSplitPercentageShouldBeHundredPercent"); + assertVMException(ex, "MaxBountyCannotBeMoreThanMaxBountyLimit"); } try { - await setUpGlobalVars(accounts, 0, 9000, [9000, 0, 999], [100, 800]); - assert(false, "cannot init with rewardSplit < 10000"); + await setUpGlobalVars(accounts, 0, 9000, [8000, 1000, 1000], 3501); + assert(false, "cannot init with default governance fee > 3500"); } catch (ex) { - assertVMException(ex, "TotalSplitPercentageShouldBeHundredPercent"); + assertVMException(ex, "FeeCannotBeMoreThanMaxFee"); } + await setUpGlobalVars(accounts, 0, 9000, [8000, 1500, 500], 200, 10, 100, 2500000, 60 * 60 * 24 * 3); + try { - await setUpGlobalVars(accounts, 0, 9901, [8000, 1000, 1000], [100, 800]); - assert(false, "cannot init with max bounty > 10000"); + await hatVaultsRegistry.createVault( + { + asset: stakingToken.address, + name: "VAULT", + symbol: "VLT", + rewardControllers: [rewardController.address], + owner: await hatVaultsRegistry.owner(), + isPaused: false, + descriptionHash: "_descriptionHash1", + }, + { + owner: await hatVaultsRegistry.owner(), + committee: accounts[3], + arbitrator: accounts[2], + maxBounty: 8000, + bountySplit: [8000, 1500, 500], + governanceFee: 3501, + vestingDuration: 86400, + vestingPeriods: 10 + } + ); + assert(false, "cannot init with governance fee > 3500"); } catch (ex) { - assertVMException(ex, "MaxBountyCannotBeMoreThanMaxBountyLimit"); + assertVMException(ex, "FeeCannotBeMoreThanMaxFee"); } - await setUpGlobalVars(accounts, 0, 9000, [8000, 1500, 500], [200, 700], 10, 0, 100, false, 2500000, 60 * 60 * 24 * 3); assert.equal((await claimsManager.maxBounty()).toString(), "9000"); assert.equal( (await claimsManager.bountySplit()).hacker.toString(), @@ -1069,13 +1080,9 @@ contract("HatVaults", (accounts) => { "500" ); assert.equal( - (await claimsManager.getBountyGovernanceHAT()).toString(), + (await claimsManager.getGovernanceFee()).toString(), "200" ); - assert.equal( - (await claimsManager.getBountyHackerHATVested()).toString(), - "700" - ); try { await claimsManager.setPendingMaxBounty(9001); @@ -1143,24 +1150,10 @@ contract("HatVaults", (accounts) => { } try { - await claimsManager.setHATBountySplit(1000, 1001); - assert(false, "cannot set hat bounty split to more than 2000"); - } catch (ex) { - assertVMException(ex, "TotalHatsSplitPercentageShouldBeUpToMaxHATSplit"); - } - - try { - await claimsManager.setHATBountySplit(2001, 0); - assert(false, "cannot set hat bounty split to more than 2000"); + await claimsManager.setGovernanceFee(3501); + assert(false, "cannot set governance fee to more than 3500"); } catch (ex) { - assertVMException(ex, "TotalHatsSplitPercentageShouldBeUpToMaxHATSplit"); - } - - try { - await claimsManager.setHATBountySplit(0, 2001); - assert(false, "cannot set hat bounty split to more than 2000"); - } catch (ex) { - assertVMException(ex, "TotalHatsSplitPercentageShouldBeUpToMaxHATSplit"); + assertVMException(ex, "FeeCannotBeMoreThanMaxFee"); } try { @@ -1171,17 +1164,16 @@ contract("HatVaults", (accounts) => { } try { - await claimsManager.setHATBountySplit(0, 800, { from: accounts[1] }); + await claimsManager.setGovernanceFee(0, { from: accounts[1] }); assert(false, "only registry owner"); } catch (ex) { assertVMException(ex, "OnlyRegistryOwner"); } await claimsManager.setBountySplit([6800, 2200, 1000]); - tx = await claimsManager.setHATBountySplit(0, 800); - assert.equal(tx.logs[0].event, "SetHATBountySplit"); - assert.equal(tx.logs[0].args._bountyGovernanceHAT, "0"); - assert.equal(tx.logs[0].args._bountyHackerHATVested, "800"); + tx = await claimsManager.setGovernanceFee(0); + assert.equal(tx.logs[0].event, "SetGovernanceFee"); + assert.equal(tx.logs[0].args._governanceFee, "0"); assert.equal( (await claimsManager.maxBounty()).toString(), @@ -1201,13 +1193,10 @@ contract("HatVaults", (accounts) => { "1000" ); assert.equal( - (await claimsManager.getBountyGovernanceHAT()).toString(), + (await claimsManager.getGovernanceFee()).toString(), "0" ); - assert.equal( - (await claimsManager.getBountyHackerHATVested()).toString(), - "800" - ); + await advanceToSafetyPeriod(); tx = await claimsManager.submitClaim( accounts[2], @@ -1253,10 +1242,9 @@ contract("HatVaults", (accounts) => { await claimsManager.setBountySplit([6000, 3000, 1000]); - tx = await claimsManager.setHATBountySplit(1, 800); - assert.equal(tx.logs[0].event, "SetHATBountySplit"); - assert.equal(tx.logs[0].args._bountyGovernanceHAT, "1"); - assert.equal(tx.logs[0].args._bountyHackerHATVested, "800"); + tx = await claimsManager.setGovernanceFee(1); + assert.equal(tx.logs[0].event, "SetGovernanceFee"); + assert.equal(tx.logs[0].args._governanceFee, "1"); await claimsManager.setPendingMaxBounty(8000); @@ -1266,7 +1254,7 @@ contract("HatVaults", (accounts) => { }); it("max bounty can be 100%", async () => { - await setUpGlobalVars(accounts, 0, 10000, [9000, 0, 1000], [100, 800]); + await setUpGlobalVars(accounts, 0, 10000, [9000, 0, 1000], 100); // bountylevel can be 10000 without throwing an error let tx = await claimsManager.setPendingMaxBounty(10000); @@ -1372,70 +1360,43 @@ contract("HatVaults", (accounts) => { } }); - it("update default hatBountySplit", async () => { + it("update default governanceFee", async () => { await setUpGlobalVars(accounts); assert.equal( - (await claimsManager.getBountyGovernanceHAT()).toString(), - "1500" - ); - assert.equal( - (await claimsManager.getBountyHackerHATVested()).toString(), - "500" + (await claimsManager.getGovernanceFee()).toString(), + "2000" ); assert.equal( - (await hatVaultsRegistry.defaultBountyGovernanceHAT()).toString(), - "1500" - ); - assert.equal( - (await hatVaultsRegistry.defaultBountyHackerHATVested()).toString(), - "500" + (await hatVaultsRegistry.defaultGovernanceFee()).toString(), + "2000" ); try { - await hatVaultsRegistry.setDefaultHATBountySplit(2001, 0); - assert(false, "cannot set hat bounty split to more than 2000"); - } catch (ex) { - assertVMException(ex, "TotalHatsSplitPercentageShouldBeUpToMaxHATSplit"); - } - - try { - await hatVaultsRegistry.setDefaultHATBountySplit(0, 2001); - assert(false, "cannot set hat bounty split to more than 2000"); - } catch (ex) { - assertVMException(ex, "TotalHatsSplitPercentageShouldBeUpToMaxHATSplit"); - } - - try { - await hatVaultsRegistry.setDefaultHATBountySplit(1001, 1000); - assert(false, "cannot set hat bounty split to more than 2000"); + await hatVaultsRegistry.setDefaultGovernanceFee(3501); + assert(false, "cannot set governance fee to more than 2000"); } catch (ex) { - assertVMException(ex, "TotalHatsSplitPercentageShouldBeUpToMaxHATSplit"); + assertVMException(ex, "FeeCannotBeMoreThanMaxFee"); } try { - await hatVaultsRegistry.setDefaultHATBountySplit(200, 800, { from: accounts[1] }); + await hatVaultsRegistry.setDefaultGovernanceFee(200, { from: accounts[1] }); assert(false, "only owner"); } catch (ex) { assertVMException(ex, "Ownable: caller is not the owner"); } - await claimsManager.setHATBountySplit(1500, 500); + await claimsManager.setGovernanceFee(1500); - let tx = await hatVaultsRegistry.setDefaultHATBountySplit(200, 800); - assert.equal(tx.logs[0].event, "SetDefaultHATBountySplit"); - assert.equal(tx.logs[0].args._defaultBountyGovernanceHAT.toString(), "200"); - assert.equal(tx.logs[0].args._defaultBountyHackerHATVested.toString(), "800"); + let tx = await hatVaultsRegistry.setDefaultGovernanceFee(200); + assert.equal(tx.logs[0].event, "SetDefaultGovernanceFee"); + assert.equal(tx.logs[0].args._defaultGovernanceFee.toString(), "200"); assert.equal( - (await hatVaultsRegistry.defaultBountyGovernanceHAT()).toString(), + (await hatVaultsRegistry.defaultGovernanceFee()).toString(), "200" ); - assert.equal( - (await hatVaultsRegistry.defaultBountyHackerHATVested()).toString(), - "800" - ); tx = await hatVaultsRegistry.createVault( { @@ -1457,6 +1418,7 @@ contract("HatVaults", (accounts) => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 }, @@ -1466,63 +1428,41 @@ contract("HatVaults", (accounts) => { let newClaimsManager = await HATClaimsManager.at(tx.logs[2].args._claimsManager); assert.equal( - (await newClaimsManager.getBountyGovernanceHAT()).toString(), + (await newClaimsManager.getGovernanceFee()).toString(), "200" ); - assert.equal( - (await newClaimsManager.getBountyHackerHATVested()).toString(), - "800" - ); assert.equal( - (await claimsManager.getBountyGovernanceHAT()).toString(), + (await claimsManager.getGovernanceFee()).toString(), "1500" ); - assert.equal( - (await claimsManager.getBountyHackerHATVested()).toString(), - "500" - ); - tx = await claimsManager.setHATBountySplit(await claimsManager.NULL_UINT16(), await claimsManager.NULL_UINT16()); - assert.equal(tx.logs[0].event, "SetHATBountySplit"); - assert.equal(tx.logs[0].args._bountyGovernanceHAT.toString(), (await claimsManager.NULL_UINT16()).toString()); - assert.equal(tx.logs[0].args._bountyHackerHATVested.toString(), (await claimsManager.NULL_UINT16()).toString()); + tx = await claimsManager.setGovernanceFee(await claimsManager.NULL_UINT16()); + assert.equal(tx.logs[0].event, "SetGovernanceFee"); + assert.equal(tx.logs[0].args._governanceFee.toString(), (await claimsManager.NULL_UINT16()).toString()); assert.equal( - (await claimsManager.getBountyGovernanceHAT()).toString(), + (await claimsManager.getGovernanceFee()).toString(), "200" ); - assert.equal( - (await claimsManager.getBountyHackerHATVested()).toString(), - "800" - ); - tx = await hatVaultsRegistry.setDefaultHATBountySplit(300, 700); - assert.equal(tx.logs[0].event, "SetDefaultHATBountySplit"); - assert.equal(tx.logs[0].args._defaultBountyGovernanceHAT.toString(), "300"); - assert.equal(tx.logs[0].args._defaultBountyHackerHATVested.toString(), "700"); + tx = await hatVaultsRegistry.setDefaultGovernanceFee(300); + assert.equal(tx.logs[0].event, "SetDefaultGovernanceFee"); + assert.equal(tx.logs[0].args._defaultGovernanceFee.toString(), "300"); assert.equal( - (await newClaimsManager.getBountyGovernanceHAT()).toString(), + (await newClaimsManager.getGovernanceFee()).toString(), "300" ); - assert.equal( - (await newClaimsManager.getBountyHackerHATVested()).toString(), - "700" - ); assert.equal( - (await claimsManager.getBountyGovernanceHAT()).toString(), + (await claimsManager.getGovernanceFee()).toString(), "300" ); - assert.equal( - (await claimsManager.getBountyHackerHATVested()).toString(), - "700" - ); }); it("zero totalAllocPoints", async () => { - await setUpGlobalVars(accounts, 0, 9000, [8000, 1000, 1000], [1000, 500], 10, 0, 0); + await setUpGlobalVars(accounts, 0, 9000, [8000, 1000, 1000], 1000, 10, 0); var staker = accounts[1]; @@ -1570,6 +1510,7 @@ contract("HatVaults", (accounts) => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 }, @@ -1736,7 +1677,7 @@ contract("HatVaults", (accounts) => { }); it("withdraw procedure is sane", async () => { - await setUpGlobalVars(accounts, 0, 8000, [7000, 2500, 500], [1000, 500], 10, 0, 100, false, 2500000, 60 * 60 * 24 * 3); + await setUpGlobalVars(accounts, 0, 8000, [7000, 2500, 500], 1000, 10, 100, 2500000, 60 * 60 * 24 * 3); var staker = accounts[1]; await stakingToken.approve(vault.address, web3.utils.toWei("1"), { @@ -1870,7 +1811,7 @@ contract("HatVaults", (accounts) => { }); it("cannot withdraw if there is an active claim", async () => { - const { committee, someAccount }= await setUpGlobalVars(accounts, 0, 8000, [7000, 2500, 500], [1000, 500], 10, 0, 100, false, 2500000, 60 * 60 * 24 * 3); + const { committee, someAccount }= await setUpGlobalVars(accounts, 0, 8000, [7000, 2500, 500], 1000, 10, 100, 2500000, 60 * 60 * 24 * 3); const staker = accounts[1]; let claimId; @@ -1940,7 +1881,7 @@ contract("HatVaults", (accounts) => { }); it("setWithdrawSafetyPeriod", async () => { - await setUpGlobalVars(accounts, 0, 8000, [7000, 2500, 500], [1000, 500], 10, 0, 100, false, 2500000, 60 * 60 * 24 * 3); + await setUpGlobalVars(accounts, 0, 8000, [7000, 2500, 500], 1000, 10, 100, 2500000, 60 * 60 * 24 * 3); try { await hatVaultsRegistry.setWithdrawSafetyPeriod(60 * 60, 60 * 30, { from: accounts[1], @@ -2632,7 +2573,7 @@ contract("HatVaults", (accounts) => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); var staker = accounts[1]; @@ -2675,7 +2616,7 @@ contract("HatVaults", (accounts) => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); var staker = accounts[1]; @@ -2740,7 +2681,7 @@ contract("HatVaults", (accounts) => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); var vaultsManagerMock = await VaultsManagerMock.new(); @@ -2780,7 +2721,7 @@ contract("HatVaults", (accounts) => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); var staker = accounts[1]; @@ -2876,7 +2817,7 @@ contract("HatVaults", (accounts) => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); var staker = accounts[1]; @@ -2948,7 +2889,7 @@ contract("HatVaults", (accounts) => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); var staker = accounts[1]; @@ -3043,7 +2984,7 @@ contract("HatVaults", (accounts) => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); var staker = accounts[1]; @@ -3152,11 +3093,9 @@ contract("HatVaults", (accounts) => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000, - 0, 100, - false, 0 ); var staker = accounts[1]; @@ -3283,22 +3222,18 @@ it("getVaultReward - no vault updates will return 0 ", async () => { it("getVaultReward - no vault updates will retrun 0 ", async () => { await setUpGlobalVars(accounts); let hatToken1 = await HATTokenMock.new(accounts[0]); - var tokenLock1 = await HATTokenLock.new(); - let tokenLockFactory1 = await TokenLockFactory.new(tokenLock1.address, accounts[0]); var vaultsManager = await VaultsManagerMock.new(); let deployment = await deployHATVaults({ governance: vaultsManager.address, arbitrator: vaultsManager.address, hatToken: hatToken1.address, - tokenLockFactory: tokenLockFactory1.address, rewardControllersConf: [{ startBlock: 1, epochLength: 10, epochRewardPerBlock }], hatVaultsRegistryConf: { - bountyGovernanceHAT: 1000, - bountyHackerHATVested: 500 + governanceFee: 1000 }, silent: true }); @@ -3659,7 +3594,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { }); it("getPendingReward + getRewardPerBlock", async () => { - await setUpGlobalVars(accounts, 0, 8000, [7500, 2000, 500], [1500, 500], 100); + await setUpGlobalVars(accounts, 0, 8000, [7500, 2000, 500], 1500, 100); var staker = accounts[1]; assert.equal((await rewardController.getPendingReward(vault.address, staker)).toNumber(), 0); await stakingToken.approve(vault.address, web3.utils.toWei("4"), { @@ -3710,11 +3645,9 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000, - 0, 100, - false, 2500000, 60 * 60 * 24 * 3 ); @@ -3872,7 +3805,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { }); it("approve+ stake simple check rewards", async () => { - await setUpGlobalVars(accounts, 0, 8000, [7000, 2500, 500], [1000, 500], 10000); + await setUpGlobalVars(accounts, 0, 8000, [7000, 2500, 500], 1000, 10000); var staker = accounts[4]; await stakingToken.approve(vault.address, web3.utils.toWei("1"), { from: staker, @@ -3920,7 +3853,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { }); it("withdraw all after approve and check reward", async () => { - await setUpGlobalVars(accounts, 0, 8000, [7000, 2500, 500], [1000, 500], 10000); + await setUpGlobalVars(accounts, 0, 8000, [7000, 2500, 500], 1000, 10000); var staker = accounts[1]; var staker2 = accounts[3]; await stakingToken.approve(vault.address, web3.utils.toWei("2"), { @@ -3968,7 +3901,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); currentBlockNumber = (await web3.eth.getBlock("latest")).number; @@ -4111,7 +4044,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); currentBlockNumber = (await web3.eth.getBlock("latest")).number; @@ -4203,7 +4136,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); currentBlockNumber = (await web3.eth.getBlock("latest")).number; @@ -4267,7 +4200,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); currentBlockNumber = (await web3.eth.getBlock("latest")).number; @@ -4331,7 +4264,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); currentBlockNumber = (await web3.eth.getBlock("latest")).number; @@ -4397,7 +4330,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); currentBlockNumber = (await web3.eth.getBlock("latest")).number; @@ -4496,11 +4429,9 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000, - 0, 100, - false, 2500000, 60 * 60 * 24 * 3 ); @@ -4639,7 +4570,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); currentBlockNumber = (await web3.eth.getBlock("latest")).number; @@ -4836,135 +4767,63 @@ it("getVaultReward - no vault updates will return 0 ", async () => { ); }); - it("approve + swapAndSend", async () => { - await setUpGlobalVars(accounts, 0, 8000, [8000, 2000, 0], [550, 450]); - var staker = accounts[4]; - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.mint(staker, web3.utils.toWei("1")); - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - assert.equal(await hatToken.balanceOf(staker), 0); - await utils.increaseTime(7 * 24 * 3600); - let path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, hatToken.address] - ); - let amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[2] - ); - let amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - let payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); + it("hatpaymentsplitter predict bad splitter", async () => { + let hatPaymentSplitterImplementation = await HATPaymentSplitter.new(); + let hatPaymentSplitterFactory = await HATPaymentSplitterFactory.new(hatPaymentSplitterImplementation.address); try { - await hatVaultsRegistry.swapAndSend(stakingToken.address, [accounts[2]], 0, router.address, payload); - assert(false, "cannot swapAndSend before approve"); + await hatPaymentSplitterFactory.predictSplitterAddress( + [accounts[2], accounts[3]], + [5000] + ); + assert(false, "array length mismatch"); } catch (ex) { - assertVMException(ex, "AmountToSwapIsZero"); + assertVMException(ex, "ArrayLengthMismatch"); } - await advanceToSafetyPeriod(); - let tx = await claimsManager.submitClaim( - accounts[2], - 8000, - "description hash", - { - from: accounts[1], - } - ); - let claimId = tx.logs[0].args._claimId; - await utils.increaseTime(60 * 60 * 24); - await claimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); - - await stakingToken.approveDisable(true); try { - await hatVaultsRegistry.swapAndSend(stakingToken.address, [accounts[2]], 0, router.address, payload); - assert(false, "approve disabled"); + await hatPaymentSplitterFactory.predictSplitterAddress( + [], + [] + ); + assert(false, "no payees"); } catch (ex) { - assertVMException(ex, "SafeERC20: ERC20 operation did not succeed"); + assertVMException(ex, "NoPayees"); } - await stakingToken.approveDisable(false); - await stakingToken.approveZeroDisable(true); + try { - await hatVaultsRegistry.swapAndSend(stakingToken.address, [accounts[2]], 0, router.address, payload); - assert(false, "approve to 0 disabled"); + await hatPaymentSplitterFactory.predictSplitterAddress( + [ZERO_ADDRESS], + [5000] + ); + assert(false, "zero address"); } catch (ex) { - assertVMException(ex, "SafeERC20: ERC20 operation did not succeed"); + assertVMException(ex, "ZeroAddress"); } - await stakingToken.approveZeroDisable(false); - amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[2] - ); - amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2]], - 0, - router.address, - payload - ); - assert.equal( - await stakingToken.allowance(hatVaultsRegistry.address, await router.address), - 0 - ); - assert.equal(tx.logs[1].event, "SwapAndSend"); - var vestingTokenLock = await HATTokenLock.at(tx.logs[1].args._tokenLock); - assert.equal( - await vestingTokenLock.owner(), - "0x0000000000000000000000000000000000000000" - ); - assert.equal( - (await hatToken.balanceOf(vestingTokenLock.address)).toString(), - tx.logs[1].args._amountSent.toString() - ); - var expectedHackerReward = new web3.utils.BN(web3.utils.toWei("0.8")) - .mul(new web3.utils.BN(9)) - .div(new web3.utils.BN(2)) - .div(new web3.utils.BN(100)); - assert.equal( - tx.logs[1].args._amountSent.toString(), - expectedHackerReward.toString() - ); - assert.equal(await vestingTokenLock.canDelegate(), true); - await vestingTokenLock.delegate(accounts[4], { from: accounts[2] }); + try { - await vestingTokenLock.cancelLock(); - assert(false, "cannot cancel lock"); + await hatPaymentSplitterFactory.predictSplitterAddress( + [accounts[2]], + [0] + ); + assert(false, "zero shares"); } catch (ex) { - assertVMException(ex); + assertVMException(ex, "ZeroShares"); } - assert.equal( - await hatToken.delegates(vestingTokenLock.address), - accounts[4] - ); + try { - await hatVaultsRegistry.swapAndSend(stakingToken.address, [accounts[2]], 0, router.address, payload); - assert(false, "cannot swapAndSend twice"); + await hatPaymentSplitterFactory.predictSplitterAddress( + [accounts[2], accounts[2]], + [5000, 2000] + ); + assert(false, "dulpicated payee"); } catch (ex) { - assertVMException(ex, "AmountToSwapIsZero"); + assertVMException(ex, "DuplicatedPayee"); } }); - it("approve + swapAndSend weth vault", async () => { - await setUpGlobalVars( - accounts, - 0, - 8000, - [8000, 2000, 0], - [600, 400], - 10, - 0, - 100, - utils.NULL_ADDRESS - ); - assert.equal(await router.WETH9(), stakingToken.address); + it("hatpaymentsplitter withdraw from tokenlock", async () => { + await setUpGlobalVars(accounts, 0, 8000, [1000, 9000, 0], 0); var staker = accounts[4]; await stakingToken.approve(vault.address, web3.utils.toWei("1"), { from: staker, @@ -4972,10 +4831,45 @@ it("getVaultReward - no vault updates will return 0 ", async () => { await stakingToken.mint(staker, web3.utils.toWei("1")); await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); assert.equal(await hatToken.balanceOf(staker), 0); - await utils.increaseTime(7 * 24 * 3600); + + let hatPaymentSplitterImplementation = await HATPaymentSplitter.new(); + let hatPaymentSplitterFactory = await HATPaymentSplitterFactory.new(hatPaymentSplitterImplementation.address); + + let splitterAddress = await hatPaymentSplitterFactory.predictSplitterAddress( + [accounts[2], accounts[3]], + [5000, 5000] + ); + + let tx = await hatPaymentSplitterFactory.createHATPaymentSplitter( + [accounts[2], accounts[3]], + [5000, 5000] + ); + + assert.equal(tx.logs[0].event, "HATPaymentSplitterCreated"); + assert.equal(tx.logs[0].args._hatPaymentSplitter, splitterAddress); + let hatPaymentSplitter = await HATPaymentSplitter.at(splitterAddress); + + try { + await hatPaymentSplitter.initialize([accounts[2], accounts[3]], [5000, 5000]); + tx = await hatPaymentSplitterFactory.createHATPaymentSplitter( + [accounts[2], accounts[3]], + [5000, 5000] + ); + assert(false, "already exists"); + } catch (ex) { + assertVMException(ex, "Initializable: contract is already initialized"); + } + + try { + await hatPaymentSplitter.initialize([accounts[2], accounts[3]], [5000, 5000]); + assert(false, "already initialized"); + } catch (ex) { + assertVMException(ex, "Initializable: contract is already initialized"); + } + await advanceToSafetyPeriod(); - let tx = await claimsManager.submitClaim( - accounts[2], + tx = await claimsManager.submitClaim( + hatPaymentSplitter.address, 8000, "description hash", { @@ -4985,1496 +4879,103 @@ it("getVaultReward - no vault updates will return 0 ", async () => { let claimId = tx.logs[0].args._claimId; await utils.increaseTime(60 * 60 * 24); - await claimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); + tx = await claimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); + + var vestingTokenLock = await HATTokenLock.at(tx.logs[1].args._tokenLock); + + await stakingToken.mint(vestingTokenLock.address, web3.utils.toWei("1")); - await stakingToken.approveDisable(true); + await hatPaymentSplitter.withdrawSurplusFromTokenLock(vestingTokenLock.address, web3.utils.toWei("1"), { from: accounts[3] }); - await stakingToken.approveDisable(false); - let path = ethers.utils.solidityPack( - ["address", "uint24", "address"], - [stakingToken.address, 0, hatToken.address] + assert.equal( + (await stakingToken.balanceOf(hatPaymentSplitter.address)).toString(), + new web3.utils.BN(web3.utils.toWei("1.72")).toString() ); - let amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[2] + + let accidentToken = await ERC20Mock.new("Accident", "ACT"); + await accidentToken.mint(vestingTokenLock.address, web3.utils.toWei("1")); + + await hatPaymentSplitter.sweepTokenFromTokenLock(vestingTokenLock.address, accidentToken.address, { from: accounts[2] }); + + assert.equal( + (await accidentToken.balanceOf(hatPaymentSplitter.address)).toString(), + web3.utils.toWei("1").toString() ); - let amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - let payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2]], - 0, - router.address, - payload + + await utils.increaseTime(60 * 60 * 60 * 24 * 90); + + await hatPaymentSplitter.releaseFromTokenLock(vestingTokenLock.address, { from: accounts[2] }); + + assert.equal((await stakingToken.balanceOf(hatPaymentSplitter.address)).toString(), web3.utils.toWei("1.8")); + + await hatPaymentSplitter.release(stakingToken.address, accounts[2]); + await hatPaymentSplitter.release(stakingToken.address, accounts[3]); + + assert.equal( + (await stakingToken.balanceOf(hatPaymentSplitter.address)).toString(), + "0" ); - assert.equal(tx.logs[1].event, "SwapAndSend"); - var vestingTokenLock = await HATTokenLock.at(tx.logs[1].args._tokenLock); + assert.equal( - (await hatToken.balanceOf(vestingTokenLock.address)).toString(), - tx.logs[1].args._amountSent.toString() + (await stakingToken.balanceOf(accounts[2])).toString(), + new web3.utils.BN(web3.utils.toWei("0.9")).toString() ); - var expectedHackerReward = new web3.utils.BN(web3.utils.toWei("0.8")) - .mul(new web3.utils.BN(4)) - .div(new web3.utils.BN(100)); + assert.equal( - tx.logs[1].args._amountSent.toString(), - expectedHackerReward.toString() - ); - }); - - it("approve+ swapAndSend with HAT vault", async () => { - await setUpGlobalVars(accounts); - var staker = accounts[4]; - let tx = await hatVaultsRegistry.createVault( - { - asset: hatToken.address, - name: "VAULT", - symbol: "VLT", - rewardControllers: [rewardController.address], - owner: await hatVaultsRegistry.owner(), - isPaused: false, - descriptionHash: "_descriptionHash", - }, - { - owner: await hatVaultsRegistry.owner(), - committee: accounts[1], - arbitrator: "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF", - arbitratorCanChangeBounty: true, - arbitratorCanChangeBeneficiary: false, - arbitratorCanSubmitIssues: false, - isTokenLockRevocable: false, - maxBounty: 8000, - bountySplit: [7000, 2500, 500], - vestingDuration: 86400, - vestingPeriods: 10 - } - ); - - let newVault = await HATVault.at(tx.logs[2].args._vault); - let newClaimsManager = await HATClaimsManager.at(tx.logs[2].args._claimsManager); - - await hatVaultsRegistry.setDefaultChallengePeriod(60 * 60 * 24); - - await rewardController.setAllocPoint( - newVault.address, - 100 - ); - - await hatToken.approve(newVault.address, web3.utils.toWei("1"), { - from: staker, - }); - await hatToken.setMinter(accounts[0], web3.utils.toWei("1")); - await hatToken.mint(staker, web3.utils.toWei("1")); - await newClaimsManager.committeeCheckIn({ from: accounts[1] }); - await newVault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - assert.equal(await hatToken.balanceOf(staker), 0); - assert.equal( - await hatToken.balanceOf(newVault.address), - web3.utils.toWei("1") - ); - - await utils.increaseTime(7 * 24 * 3600); - let path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, hatToken.address] - ); - let amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - hatToken.address, - accounts[2] - ); - let amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(hatToken.address)); - let payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - try { - await hatVaultsRegistry.swapAndSend(hatToken.address, [accounts[2]], 0, router.address, payload); - assert(false, "cannot swapAndSend before approve"); - } catch (ex) { - assertVMException(ex, "AmountToSwapIsZero"); - } - await advanceToSafetyPeriod(); - tx = await newClaimsManager.submitClaim( - accounts[2], - 8000, - "description hash", - { - from: accounts[1], - } - ); - - let claimId = tx.logs[0].args._claimId; - await utils.increaseTime(60 * 60 * 24); - await newClaimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); - - assert.equal(await hatToken.balanceOf(accounts[0]), 0); - amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - hatToken.address, - accounts[2] - ); - amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(hatToken.address)); - payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - tx = await hatVaultsRegistry.swapAndSend( - hatToken.address, - [accounts[2]], - 0, - router.address, - payload - ); - //gov gets 15% out of 80% of the vault value - assert.equal( - (await hatToken.balanceOf(accounts[0])).toString(), - web3.utils.toWei("0.12") - ); - assert.equal(tx.logs[1].event, "SwapAndSend"); - var vestingTokenLock = await HATTokenLock.at(tx.logs[1].args._tokenLock); - assert.equal( - (await hatToken.balanceOf(vestingTokenLock.address)).toString(), - tx.logs[1].args._amountSent.toString() - ); - var expectedHackerReward = new web3.utils.BN(web3.utils.toWei("1")) - .mul(new web3.utils.BN(4)) - .div(new web3.utils.BN(100)); - assert.equal( - tx.logs[1].args._amountSent.toString(), - expectedHackerReward.toString() - ); - assert.equal(await vestingTokenLock.canDelegate(), true); - await vestingTokenLock.delegate(accounts[4], { from: accounts[2] }); - assert.equal( - await hatToken.delegates(vestingTokenLock.address), - accounts[4] - ); - try { - await hatVaultsRegistry.swapAndSend(hatToken.address, [accounts[2]], 0, router.address, payload); - assert(false, "cannot swapAndSend twice"); - } catch (ex) { - assertVMException(ex, "AmountToSwapIsZero"); - } - }); - - it("approve + swapAndSend 2 vaults with same token", async () => { - await setUpGlobalVars(accounts, 0, 8000, [8000, 2000, 0], [600, 400]); - - let tx = await hatVaultsRegistry.createVault( - { - asset: stakingToken.address, - name: "VAULT", - symbol: "VLT", - rewardControllers: [rewardController.address], - owner: await hatVaultsRegistry.owner(), - isPaused: false, - descriptionHash: "_descriptionHash", - }, - { - owner: await hatVaultsRegistry.owner(), - committee: accounts[1], - arbitrator: "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF", - arbitratorCanChangeBounty: true, - arbitratorCanChangeBeneficiary: false, - arbitratorCanSubmitIssues: false, - isTokenLockRevocable: false, - maxBounty: 8000, - bountySplit: [7000, 2500, 500], - vestingDuration: 86400, - vestingPeriods: 10 - } - ); - - let newVault = await HATVault.at(tx.logs[2].args._vault); - let newClaimsManager = await HATClaimsManager.at(tx.logs[2].args._claimsManager); - - await newClaimsManager.setHATBountySplit(500, 400); - - await rewardController.setAllocPoint( - newVault.address, - 100 - ); - - await newClaimsManager.committeeCheckIn({ from: accounts[1] }); - - var staker = accounts[3]; - var beneficiary1 = accounts[4]; - var beneficiary2 = accounts[5]; - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.approve(newVault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.mint(staker, web3.utils.toWei("2")); - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - await newVault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - assert.equal(await hatToken.balanceOf(staker), 0); - await utils.increaseTime(7 * 24 * 3600); - - await advanceToSafetyPeriod(); - tx = await claimsManager.submitClaim( - beneficiary1, - 8000, - "description hash", - { - from: accounts[1], - } - ); - - let claimId1 = tx.logs[0].args._claimId; - - tx = await newClaimsManager.submitClaim( - beneficiary2, - 4000, - "description hash", - { - from: accounts[1], - } - ); - - let claimId2 = tx.logs[0].args._claimId; - - await claimsManager.challengeClaim(claimId1); - await newClaimsManager.challengeClaim(claimId2); - - await claimsManager.approveClaim(claimId1, 8000, ZERO_ADDRESS); - await newClaimsManager.approveClaim(claimId2, 4000, ZERO_ADDRESS); - - let path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, hatToken.address] - ); - let amountForHackersHatRewards = (await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - beneficiary1 - )).add(await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - beneficiary2 - )); - let amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - let payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [beneficiary1, beneficiary2], - 0, - router.address, - payload - ); - assert.equal( - await stakingToken.allowance(hatVaultsRegistry.address, await router.address), - 0 - ); - assert.equal(tx.logs[1].event, "SwapAndSend"); - assert.equal(tx.logs[1].args._beneficiary, beneficiary1); - let vestingTokenLock = await HATTokenLock.at(tx.logs[1].args._tokenLock); - assert.equal( - await vestingTokenLock.owner(), - "0x0000000000000000000000000000000000000000" - ); - assert.equal( - await vestingTokenLock.beneficiary(), - beneficiary1 - ); - assert.equal( - (await hatToken.balanceOf(vestingTokenLock.address)).toString(), - tx.logs[1].args._amountSent.toString() - ); - let expectedHackerReward = new web3.utils.BN(web3.utils.toWei("0.8")) - .mul(new web3.utils.BN(4)) - .div(new web3.utils.BN(100)); - assert.equal( - tx.logs[1].args._amountSent.toString(), - expectedHackerReward.toString() - ); - - assert.equal(tx.logs[3].event, "SwapAndSend"); - assert.equal(tx.logs[3].args._beneficiary, beneficiary2); - vestingTokenLock = await HATTokenLock.at(tx.logs[3].args._tokenLock); - assert.equal( - await vestingTokenLock.owner(), - "0x0000000000000000000000000000000000000000" - ); - assert.equal( - await vestingTokenLock.beneficiary(), - beneficiary2 - ); - assert.equal( - (await hatToken.balanceOf(vestingTokenLock.address)).toString(), - tx.logs[3].args._amountSent.toString() - ); - expectedHackerReward = new web3.utils.BN(web3.utils.toWei("0.4")) - .mul(new web3.utils.BN(4)) - .div(new web3.utils.BN(100)); - assert.equal( - tx.logs[3].args._amountSent.toString(), - expectedHackerReward.toString() - ); - - assert.equal(tx.logs[4].event, "SwapAndSend"); - assert.equal(tx.logs[4].args._beneficiary, accounts[0]); - assert.equal(tx.logs[4].args._tokenLock, "0x0000000000000000000000000000000000000000"); - var expectedHatGovernanceReward = new web3.utils.BN(web3.utils.toWei("0.8")) - .mul(new web3.utils.BN("600")) - .div(new web3.utils.BN(10000)).add(new web3.utils.BN(web3.utils.toWei("0.4")) - .mul(new web3.utils.BN("500")) - .div(new web3.utils.BN(10000))); - assert.equal( - (await hatToken.balanceOf(accounts[0])).toString(), - expectedHatGovernanceReward.toString() - ); - assert.equal( - tx.logs[4].args._amountSent.toString(), - expectedHatGovernanceReward.toString() - ); - }); - - it("approve + swapAndSend change swap token", async () => { - await setUpGlobalVars(accounts, 0, 8000, [8000, 2000, 0], [550, 450]); - var staker = accounts[4]; - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.mint(staker, web3.utils.toWei("1")); - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - assert.equal(await hatToken.balanceOf(staker), 0); - - await advanceToSafetyPeriod(); - let tx = await claimsManager.submitClaim( - accounts[2], - 8000, - "description hash", - { - from: accounts[1], - } - ); - - let claimId = tx.logs[0].args._claimId; - await utils.increaseTime(60 * 60 * 24); - await claimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); - - let path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, hatToken.address] - ); - - let amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[2] - ); - let amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - let payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2]], - 0, - router.address, - payload - ); - assert.equal( - await stakingToken.allowance(hatVaultsRegistry.address, router.address), - 0 - ); - assert.equal(tx.logs[1].event, "SwapAndSend"); - var vestingTokenLock = await HATTokenLock.at(tx.logs[1].args._tokenLock); - assert.equal( - await vestingTokenLock.owner(), - "0x0000000000000000000000000000000000000000" - ); - assert.equal( - (await hatToken.balanceOf(vestingTokenLock.address)).toString(), - tx.logs[1].args._amountSent.toString() - ); - var expectedHackerReward = new web3.utils.BN(web3.utils.toWei("0.8")) - .mul(new web3.utils.BN(9)) - .div(new web3.utils.BN(2)) - .div(new web3.utils.BN(100)); - assert.equal( - tx.logs[1].args._amountSent.toString(), - expectedHackerReward.toString() - ); - - await stakingToken.approve(vault.address, web3.utils.toWei("0.8"), { - from: staker, - }); - await stakingToken.mint(staker, web3.utils.toWei("0.8")); - await vault.deposit(web3.utils.toWei("0.8"), staker, { from: staker }); - - let stakingToken2 = await ERC20Mock.new("Staking2", "STK2"); - await stakingToken2.mint(router.address, web3.utils.toWei("2500000")); - - await advanceToSafetyPeriod(); - tx = await claimsManager.submitClaim( - accounts[2], - 8000, - "description hash", - { - from: accounts[1], - } - ); - - claimId = tx.logs[0].args._claimId; - await utils.increaseTime(60 * 60 * 24); - await claimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); - - try { - await hatVaultsRegistry.setSwapToken(stakingToken2.address, { from: accounts[1] }); - assert(false, "only owner"); - } catch (ex) { - assertVMException(ex, "Ownable: caller is not the owner"); - } - - await hatVaultsRegistry.setSwapToken(stakingToken2.address); - - path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, stakingToken2.address] - ); - - amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[2] - ); - amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2]], - 0, - router.address, - payload - ); - assert.equal( - await stakingToken.allowance(hatVaultsRegistry.address, router.address), - 0 - ); - assert.equal(tx.logs[1].event, "SwapAndSend"); - vestingTokenLock = await HATTokenLock.at(tx.logs[1].args._tokenLock); - assert.equal( - await vestingTokenLock.owner(), - "0x0000000000000000000000000000000000000000" - ); - assert.equal( - (await hatToken.balanceOf(vestingTokenLock.address)).toString(), - "0" - ); - assert.equal( - (await stakingToken2.balanceOf(vestingTokenLock.address)).toString(), - tx.logs[1].args._amountSent.toString() - ); - expectedHackerReward = new web3.utils.BN(web3.utils.toWei("0.8")) - .mul(new web3.utils.BN(9)) - .div(new web3.utils.BN(2)) - .div(new web3.utils.BN(100)); - assert.equal( - tx.logs[1].args._amountSent.toString(), - expectedHackerReward.toString() - ); - }); - - it("hatpaymentsplitter predict bad splitter", async () => { - let hatPaymentSplitterImplementation = await HATPaymentSplitter.new(); - let hatPaymentSplitterFactory = await HATPaymentSplitterFactory.new(hatPaymentSplitterImplementation.address); - - try { - await hatPaymentSplitterFactory.predictSplitterAddress( - [accounts[2], accounts[3]], - [5000] - ); - assert(false, "array length mismatch"); - } catch (ex) { - assertVMException(ex, "ArrayLengthMismatch"); - } - - try { - await hatPaymentSplitterFactory.predictSplitterAddress( - [], - [] - ); - assert(false, "no payees"); - } catch (ex) { - assertVMException(ex, "NoPayees"); - } - - try { - await hatPaymentSplitterFactory.predictSplitterAddress( - [ZERO_ADDRESS], - [5000] - ); - assert(false, "zero address"); - } catch (ex) { - assertVMException(ex, "ZeroAddress"); - } - - try { - await hatPaymentSplitterFactory.predictSplitterAddress( - [accounts[2]], - [0] - ); - assert(false, "zero shares"); - } catch (ex) { - assertVMException(ex, "ZeroShares"); - } - - try { - await hatPaymentSplitterFactory.predictSplitterAddress( - [accounts[2], accounts[2]], - [5000, 2000] - ); - assert(false, "dulpicated payee"); - } catch (ex) { - assertVMException(ex, "DuplicatedPayee"); - } - }); - - it("hatpaymentsplitter withdraw from tokenlock", async () => { - await setUpGlobalVars(accounts, 0, 8000, [8000, 2000, 0], [550, 450]); - var staker = accounts[4]; - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.mint(staker, web3.utils.toWei("1")); - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - assert.equal(await hatToken.balanceOf(staker), 0); - - let hatPaymentSplitterImplementation = await HATPaymentSplitter.new(); - let hatPaymentSplitterFactory = await HATPaymentSplitterFactory.new(hatPaymentSplitterImplementation.address); - - let splitterAddress = await hatPaymentSplitterFactory.predictSplitterAddress( - [accounts[2], accounts[3]], - [5000, 5000] - ); - - let tx = await hatPaymentSplitterFactory.createHATPaymentSplitter( - [accounts[2], accounts[3]], - [5000, 5000] - ); - - assert.equal(tx.logs[0].event, "HATPaymentSplitterCreated"); - assert.equal(tx.logs[0].args._hatPaymentSplitter, splitterAddress); - let hatPaymentSplitter = await HATPaymentSplitter.at(splitterAddress); - - try { - await hatPaymentSplitter.initialize([accounts[2], accounts[3]], [5000, 5000]); - tx = await hatPaymentSplitterFactory.createHATPaymentSplitter( - [accounts[2], accounts[3]], - [5000, 5000] - ); - assert(false, "already exists"); - } catch (ex) { - assertVMException(ex, "Initializable: contract is already initialized"); - } - - try { - await hatPaymentSplitter.initialize([accounts[2], accounts[3]], [5000, 5000]); - assert(false, "already initialized"); - } catch (ex) { - assertVMException(ex, "Initializable: contract is already initialized"); - } - - await advanceToSafetyPeriod(); - tx = await claimsManager.submitClaim( - hatPaymentSplitter.address, - 8000, - "description hash", - { - from: accounts[1], - } - ); - - let claimId = tx.logs[0].args._claimId; - await utils.increaseTime(60 * 60 * 24); - await claimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); - - let path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, hatToken.address] - ); - let amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - hatPaymentSplitter.address - ); - let amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - let payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [hatPaymentSplitter.address], - 0, - router.address, - payload - ); - - var vestingTokenLock = await HATTokenLock.at(tx.logs[1].args._tokenLock); - - assert.equal( - (await hatToken.balanceOf(vestingTokenLock.address)).toString(), - tx.logs[1].args._amountSent.toString() - ); - var expectedHackerReward = new web3.utils.BN(web3.utils.toWei("0.8")) - .mul(new web3.utils.BN(9)) - .div(new web3.utils.BN(2)) - .div(new web3.utils.BN(100)); - assert.equal( - tx.logs[1].args._amountSent.toString(), - expectedHackerReward.toString() - ); - - await hatToken.setMinter(accounts[0], web3.utils.toWei("1")); - await hatToken.mint(vestingTokenLock.address, web3.utils.toWei("1")); - - await hatPaymentSplitter.withdrawSurplusFromTokenLock(vestingTokenLock.address, web3.utils.toWei("1"), { from: accounts[3] }); - - assert.equal( - (await hatToken.balanceOf(hatPaymentSplitter.address)).toString(), - new web3.utils.BN(web3.utils.toWei("1")).toString() - ); - - let accidentToken = await ERC20Mock.new("Accident", "ACT"); - await accidentToken.mint(vestingTokenLock.address, web3.utils.toWei("1")); - - await hatPaymentSplitter.sweepTokenFromTokenLock(vestingTokenLock.address, accidentToken.address, { from: accounts[2] }); - - assert.equal( - (await accidentToken.balanceOf(hatPaymentSplitter.address)).toString(), - web3.utils.toWei("1").toString() - ); - - await utils.increaseTime(60 * 60 * 60 * 24 * 90); - - await hatPaymentSplitter.releaseFromTokenLock(vestingTokenLock.address, { from: accounts[2] }); - - assert.equal( - (await hatToken.balanceOf(hatPaymentSplitter.address)).toString(), - new web3.utils.BN(web3.utils.toWei("1")).add(expectedHackerReward).toString() - ); - - await hatPaymentSplitter.release(hatToken.address, accounts[2]); - await hatPaymentSplitter.release(hatToken.address, accounts[3]); - - assert.equal( - (await hatToken.balanceOf(hatPaymentSplitter.address)).toString(), - "0" - ); - - assert.equal( - (await hatToken.balanceOf(accounts[2])).toString(), - new web3.utils.BN(web3.utils.toWei("1")).add(expectedHackerReward).div(new web3.utils.BN(2)).toString() - ); - - assert.equal( - (await hatToken.balanceOf(accounts[3])).toString(), - new web3.utils.BN(web3.utils.toWei("1")).add(expectedHackerReward).div(new web3.utils.BN(2)).toString() - ); - - assert.equal( - (await accidentToken.balanceOf(hatPaymentSplitter.address)).toString(), - new web3.utils.BN(web3.utils.toWei("1")).toString() - ); - - await hatPaymentSplitter.release(accidentToken.address, accounts[2]); - await hatPaymentSplitter.release(accidentToken.address, accounts[3]); - - assert.equal( - (await accidentToken.balanceOf(hatPaymentSplitter.address)).toString(), - "0" - ); - - assert.equal( - (await accidentToken.balanceOf(accounts[3])).toString(), - new web3.utils.BN(web3.utils.toWei("0.5")).toString() - ); - - assert.equal( - (await accidentToken.balanceOf(accounts[3])).toString(), - new web3.utils.BN(web3.utils.toWei("0.5")).toString() - ); - - }); - - it("swapAndSend with duplicate beneficiary", async () => { - await setUpGlobalVars(accounts, 0, 8000, [8000, 2000, 0], [550, 450]); - var staker = accounts[4]; - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.mint(staker, web3.utils.toWei("1")); - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - await utils.increaseTime(7 * 24 * 3600); - let path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, hatToken.address] - ); - - await advanceToSafetyPeriod(); - let tx = await claimsManager.submitClaim( - accounts[2], - 8000, - "description hash", - { - from: accounts[1], - } - ); - - let claimId = tx.logs[0].args._claimId; - await utils.increaseTime(60 * 60 * 24); - await claimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); - - amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[2] - ); - amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2], accounts[2]], - 0, - router.address, - payload - ); - assert.equal( - await stakingToken.allowance(hatVaultsRegistry.address, await router.address), - 0 - ); - assert.equal(tx.logs[1].event, "SwapAndSend"); - var vestingTokenLock = await HATTokenLock.at(tx.logs[1].args._tokenLock); - assert.equal( - await vestingTokenLock.owner(), - "0x0000000000000000000000000000000000000000" - ); - assert.equal( - (await hatToken.balanceOf(vestingTokenLock.address)).toString(), - tx.logs[1].args._amountSent.toString() - ); - var expectedHackerReward = new web3.utils.BN(web3.utils.toWei("0.8")) - .mul(new web3.utils.BN(9)) - .div(new web3.utils.BN(2)) - .div(new web3.utils.BN(100)); - assert.equal( - tx.logs[1].args._amountSent.toString(), - expectedHackerReward.toString() - ); - }); - - it("Update vault description", async () => { - await setUpGlobalVars(accounts); - assert.equal(await hatVaultsRegistry.isVaultVisible(vault.address), false); - - try { - await hatVaultsRegistry.setVaultVisibility(vault.address, true, { from: accounts[1] }); - assert(false, "only gov"); - } catch (ex) { - assertVMException(ex, "Ownable: caller is not the owner"); - } - - let tx = await hatVaultsRegistry.setVaultVisibility(vault.address, true); - assert.equal(tx.logs[0].event, "SetVaultVisibility"); - assert.equal(tx.logs[0].args._visible, true); - assert.equal(await hatVaultsRegistry.isVaultVisible(vault.address), true); - - try { - await vault.setVaultDescription("_descriptionHash", { from: accounts[1] }); - assert(false, "only gov"); - } catch (ex) { - assertVMException(ex, "OnlyRegistryOwner"); - } - tx = await vault.setVaultDescription("_descriptionHash"); - assert.equal(tx.logs[0].event, "SetVaultDescription"); - assert.equal(tx.logs[0].args._descriptionHash, "_descriptionHash"); - }); - - it("swapAndSend", async () => { - await setUpGlobalVars(accounts); - var staker = accounts[4]; - var staker2 = accounts[3]; - - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker2, - }); - await stakingToken.mint(staker, web3.utils.toWei("1")); - await stakingToken.mint(staker2, web3.utils.toWei("1")); - - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - - assert.equal(await hatToken.balanceOf(staker), 0); - await utils.increaseTime(7 * 24 * 3600); - await advanceToSafetyPeriod(); - let tx = await claimsManager.submitClaim( - accounts[2], - 8000, - "description hash", - { - from: accounts[1], - } - ); - - let claimId = tx.logs[0].args._claimId; - await utils.increaseTime(60 * 60 * 24); - await claimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); - - let path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, hatToken.address] - ); - let amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[1] - ); - let amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - let payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - - try { - await hatVaultsRegistry.swapAndSend(stakingToken.address, [accounts[1]], 0, router.address, payload, { - from: accounts[3], - }); - assert(false, "only gov"); - } catch (ex) { - assertVMException(ex, "Ownable: caller is not the owner"); - } - - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[1]], - 0, - router.address, - payload, - { - from: accounts[0], - } - ); - assert.equal(tx.logs[0].event, "SwapAndSend"); - assert.equal(tx.logs[0].args._amountSent.toString(), "0"); - // Not real beneficiary should not get tokens - let afterBountyBalance = ( - await hatToken.balanceOf(tx.logs[0].args._tokenLock) - ).toString(); - assert.equal( - tx.logs[0].args._tokenLock, - "0x0000000000000000000000000000000000000000" - ); - - amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[2] - ); - amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2]], - 0, - router.address, - payload, - { - from: accounts[0], - } - ); - - assert.equal( - tx.logs[1].args._amountSent.toString(), - new web3.utils.BN(web3.utils.toWei("0.8")) - .mul( - new web3.utils.BN( - (await claimsManager.getBountyHackerHATVested()) - ) - ) - .div(new web3.utils.BN("10000")) - .toString() - ); - afterBountyBalance = ( - await hatToken.balanceOf(tx.logs[1].args._tokenLock) - ).toString(); - assert.equal( - tx.logs[1].args._amountSent.toString(), - afterBountyBalance - ); - - try { - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[1]], - 0, - router.address, - payload, - { - from: accounts[0], - } - ); - assert(false, "can claim only once, nothing to redeem"); - } catch (ex) { - assertVMException(ex, "AmountToSwapIsZero"); - } - - try { - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2]], - 0, - router.address, - payload, - { - from: accounts[0], - } - ); - assert(false, "can claim only once, nothing to redeem"); - } catch (ex) { - assertVMException(ex, "AmountToSwapIsZero"); - } - }); - - it("swapAndSend router uses partial amount", async () => { - await setUpGlobalVars(accounts); - var staker = accounts[4]; - var staker2 = accounts[3]; - - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker2, - }); - await stakingToken.mint(staker, web3.utils.toWei("1")); - await stakingToken.mint(staker2, web3.utils.toWei("1")); - - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - - assert.equal(await hatToken.balanceOf(staker), 0); - await utils.increaseTime(7 * 24 * 3600); - await advanceToSafetyPeriod(); - let tx = await claimsManager.submitClaim( - accounts[2], - 8000, - "description hash", - { - from: accounts[1], - } - ); - - let claimId = tx.logs[0].args._claimId; - await utils.increaseTime(60 * 60 * 24); - await claimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); - - let path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, hatToken.address] - ); - let amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[2] - ); - - let govHatReward = await hatVaultsRegistry.governanceHatReward(stakingToken.address); - let amount = amountForHackersHatRewards.add(govHatReward); - let payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - - await router.setUsePartialAmountFlag(true); - - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2]], - 0, - router.address, - payload, - { - from: accounts[0], - } - ); - - assert.equal( - (await hatVaultsRegistry.governanceHatReward(stakingToken.address)).toString(), - govHatReward.mul(new web3.utils.BN(20)).div(new web3.utils.BN(100)).toString() - ); - - assert.equal( - (await hatVaultsRegistry.hackersHatReward(stakingToken.address, accounts[2])).toString(), - amountForHackersHatRewards.mul(new web3.utils.BN(20)).div(new web3.utils.BN(100)).toString() - ); - - assert.equal( - tx.logs[1].args._amountSent.toString(), - new web3.utils.BN(web3.utils.toWei("0.8")).mul(new web3.utils.BN(80)).div(new web3.utils.BN(100)) - .mul( - new web3.utils.BN( - (await claimsManager.getBountyHackerHATVested()) - ) - ) - .div(new web3.utils.BN("10000")) - .toString() - ); - - assert.equal( - tx.logs[1].args._amountSent.toString(), - (await hatToken.balanceOf(tx.logs[1].args._tokenLock)).toString() - ); - - let govBalance = await hatToken.balanceOf(accounts[0]); - assert.equal( - govBalance.toString(), - new web3.utils.BN(web3.utils.toWei("0.8")).mul(new web3.utils.BN(80)).div(new web3.utils.BN(100)) - .mul( - new web3.utils.BN( - (await claimsManager.getBountyGovernanceHAT()) - ) - ) - .div(new web3.utils.BN("10000")) - .toString() - ); - - amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[2] - ); - - govHatReward = await hatVaultsRegistry.governanceHatReward(stakingToken.address); - amount = amountForHackersHatRewards.add(govHatReward); - payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2]], - 0, - router.address, - payload, - { - from: accounts[0], - } - ); - - assert.equal( - (await hatVaultsRegistry.governanceHatReward(stakingToken.address)).toString(), - govHatReward.mul(new web3.utils.BN(20)).div(new web3.utils.BN(100)).toString() - ); - - assert.equal( - (await hatVaultsRegistry.hackersHatReward(stakingToken.address, accounts[2])).toString(), - amountForHackersHatRewards.mul(new web3.utils.BN(20)).div(new web3.utils.BN(100)).toString() - ); - - assert.equal( - tx.logs[1].args._amountSent.toString(), - new web3.utils.BN(web3.utils.toWei("0.16")).mul(new web3.utils.BN(80)).div(new web3.utils.BN(100)) - .mul( - new web3.utils.BN( - (await claimsManager.getBountyHackerHATVested()) - ) - ) - .div(new web3.utils.BN("10000")) - .toString() - ); - - assert.equal( - tx.logs[1].args._amountSent.toString(), - (await hatToken.balanceOf(tx.logs[1].args._tokenLock)).toString() - ); - - assert.equal( - (await hatToken.balanceOf(accounts[0])).sub(govBalance).toString(), - new web3.utils.BN(web3.utils.toWei("0.16")).mul(new web3.utils.BN(80)).div(new web3.utils.BN(100)) - .mul( - new web3.utils.BN( - (await claimsManager.getBountyGovernanceHAT()) - ) - ) - .div(new web3.utils.BN("10000")) - .toString() - ); - - await router.setUsePartialAmountFlag(false); - - govBalance = await hatToken.balanceOf(accounts[0]); - - amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[2] - ); - - govHatReward = await hatVaultsRegistry.governanceHatReward(stakingToken.address); - amount = amountForHackersHatRewards.add(govHatReward); - payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2]], - 0, - router.address, - payload, - { - from: accounts[0], - } + (await stakingToken.balanceOf(accounts[3])).toString(), + new web3.utils.BN(web3.utils.toWei("0.9")).toString() ); assert.equal( - (await hatVaultsRegistry.governanceHatReward(stakingToken.address)).toString(), - "0" + (await accidentToken.balanceOf(hatPaymentSplitter.address)).toString(), + new web3.utils.BN(web3.utils.toWei("1")).toString() ); - assert.equal( - (await hatVaultsRegistry.hackersHatReward(stakingToken.address, accounts[2])).toString(), - "0" - ); + await hatPaymentSplitter.release(accidentToken.address, accounts[2]); + await hatPaymentSplitter.release(accidentToken.address, accounts[3]); assert.equal( - tx.logs[1].args._amountSent.toString(), - new web3.utils.BN(web3.utils.toWei("0.032")) - .mul( - new web3.utils.BN( - (await claimsManager.getBountyHackerHATVested()) - ) - ) - .div(new web3.utils.BN("10000")) - .toString() + (await accidentToken.balanceOf(hatPaymentSplitter.address)).toString(), + "0" ); assert.equal( - tx.logs[1].args._amountSent.toString(), - (await hatToken.balanceOf(tx.logs[1].args._tokenLock)).toString() + (await accidentToken.balanceOf(accounts[3])).toString(), + new web3.utils.BN(web3.utils.toWei("0.5")).toString() ); assert.equal( - (await hatToken.balanceOf(accounts[0])).sub(govBalance).toString(), - new web3.utils.BN(web3.utils.toWei("0.032")) - .mul( - new web3.utils.BN( - (await claimsManager.getBountyGovernanceHAT()) - ) - ) - .div(new web3.utils.BN("10000")) - .toString() + (await accidentToken.balanceOf(accounts[3])).toString(), + new web3.utils.BN(web3.utils.toWei("0.5")).toString() ); - - try { - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2]], - 0, - router.address, - payload, - { - from: accounts[0], - } - ); - assert(false, "can claim only once, nothing to redeem"); - } catch (ex) { - assertVMException(ex, "AmountToSwapIsZero"); - } + }); - it("swapAndSend 2 vaults with same token", async () => { + it("Update vault description", async () => { await setUpGlobalVars(accounts); + assert.equal(await hatVaultsRegistry.isVaultVisible(vault.address), false); - let tx = await hatVaultsRegistry.createVault( - { - asset: stakingToken.address, - name: "VAULT", - symbol: "VLT", - rewardControllers: [rewardController.address], - owner: await hatVaultsRegistry.owner(), - isPaused: false, - descriptionHash: "_descriptionHash", - }, - { - owner: await hatVaultsRegistry.owner(), - committee: accounts[1], - arbitrator: "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF", - arbitratorCanChangeBounty: true, - arbitratorCanChangeBeneficiary: false, - arbitratorCanSubmitIssues: false, - isTokenLockRevocable: false, - maxBounty: 8000, - bountySplit: [8400, 1500, 100], - vestingDuration: 86400, - vestingPeriods: 10 - } - ); - - let newVault = await HATVault.at(tx.logs[2].args._vault); - let newClaimsManager = await HATClaimsManager.at(tx.logs[2].args._claimsManager); - - await rewardController.setAllocPoint( - newVault.address, - 100 - ); - - await newClaimsManager.committeeCheckIn({ from: accounts[1] }); - - var staker = accounts[4]; - var staker2 = accounts[3]; - - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.approve(newVault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker2, - }); - await stakingToken.approve(newVault.address, web3.utils.toWei("1"), { - from: staker2, - }); - await stakingToken.mint(staker, web3.utils.toWei("2")); - await stakingToken.mint(staker2, web3.utils.toWei("2")); - - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - await newVault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - - assert.equal(await hatToken.balanceOf(staker), 0); - await utils.increaseTime(7 * 24 * 3600); - await advanceToSafetyPeriod(); - tx = await claimsManager.submitClaim( - accounts[2], - 8000, - "description hash", - { - from: accounts[1], - } - ); - - let claimId1 = tx.logs[0].args._claimId; - - tx = await newClaimsManager.submitClaim(accounts[2], 8000, "description hash", { - from: accounts[1], - }); - - let claimId2 = tx.logs[0].args._claimId; - - await utils.increaseTime(60 * 60 * 24); - await claimsManager.approveClaim(claimId1, 8000, ZERO_ADDRESS); - await utils.increaseTime(60 * 60 * 24 * 2); - await newClaimsManager.approveClaim(claimId2, 8000, ZERO_ADDRESS); - - let path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, hatToken.address] - ); - let amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[1] - ); - let amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - let payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[1]], - 0, - router.address, - payload, - { - from: accounts[0], - } - ); - assert.equal(tx.logs[0].event, "SwapAndSend"); - assert.equal(tx.logs[0].args._amountSent.toString(), "0"); - // Not real beneficiary should not get tokens - let afterBountyBalance = ( - await hatToken.balanceOf(tx.logs[0].args._tokenLock) - ).toString(); - assert.equal( - tx.logs[0].args._tokenLock, - "0x0000000000000000000000000000000000000000" - ); - assert.equal( - tx.logs[0].args._amountSent.toString(), - afterBountyBalance - ); - - path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, hatToken.address] - ); - amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[2] - ); - amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - tx = await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[2]], - 0, - router.address, - payload, - { - from: accounts[0], - } - ); - - assert.equal( - tx.logs[1].args._amountSent.toString(), - new web3.utils.BN(web3.utils.toWei("0.8")) - .mul( - new web3.utils.BN( - (await claimsManager.getBountyHackerHATVested()) - ) - ) - .div(new web3.utils.BN("10000")).add(new web3.utils.BN(web3.utils.toWei("0.8")) - .mul( - new web3.utils.BN( - (await newClaimsManager.getBountyHackerHATVested()) - ) - ) - .div(new web3.utils.BN("10000"))) - .toString() - ); - afterBountyBalance = ( - await hatToken.balanceOf(tx.logs[1].args._tokenLock) - ).toString(); - assert.equal( - tx.logs[1].args._amountSent.toString(), - afterBountyBalance - ); - }); - - it("swapAndSend return below than minimum should revert", async () => { - await setUpGlobalVars( - accounts, - (await web3.eth.getBlock("latest")).number, - 9000, - [8400, 1500, 100], - [200, 700], - 2 - ); - - var staker = accounts[4]; - var staker2 = accounts[3]; - - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker2, - }); - await stakingToken.mint(staker, web3.utils.toWei("1")); - await stakingToken.mint(staker2, web3.utils.toWei("1")); - - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - - assert.equal(await hatToken.balanceOf(staker), 0); - await utils.increaseTime(7 * 24 * 3600); - await advanceToSafetyPeriod(); - let tx = await claimsManager.submitClaim( - accounts[2], - 8000, - "description hash", - { - from: accounts[1], - } - ); - - let claimId = tx.logs[0].args._claimId; - await utils.increaseTime(60 * 60 * 24); - await claimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); - - let path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [stakingToken.address, 0, utils.NULL_ADDRESS, 0, hatToken.address] - ); - let amountForHackersHatRewards = await hatVaultsRegistry.hackersHatReward( - stakingToken.address, - accounts[1] - ); - let amount = amountForHackersHatRewards.add(await hatVaultsRegistry.governanceHatReward(stakingToken.address)); - let payload = ISwapRouter.encodeFunctionData("exactInput", [ - [path, hatVaultsRegistry.address, 0, amount.toString(), 0], - ]); - try { - await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[1]], - web3.utils.toWei("1"), - router.address, - payload, - { from: accounts[0] } - ); - assert(false, "router return less than minimum"); + try { + await hatVaultsRegistry.setVaultVisibility(vault.address, true, { from: accounts[1] }); + assert(false, "only gov"); } catch (ex) { - assertVMException(ex, "AmountSwappedLessThanMinimum"); + assertVMException(ex, "Ownable: caller is not the owner"); } - }); - it("swapAndSend with bad call should revert", async () => { - await setUpGlobalVars(accounts, (await web3.eth.getBlock("latest")).number, 9000, - [ - 8400, - 1500, - 100 - ], - [ - 200, - 700 - ]); - - var staker = accounts[4]; - var staker2 = accounts[3]; - - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker, - }); - await stakingToken.approve(vault.address, web3.utils.toWei("1"), { - from: staker2, - }); - await stakingToken.mint(staker, web3.utils.toWei("1")); - await stakingToken.mint(staker2, web3.utils.toWei("1")); - - await vault.deposit(web3.utils.toWei("1"), staker, { from: staker }); - - assert.equal(await hatToken.balanceOf(staker), 0); - await utils.increaseTime(7 * 24 * 3600); - await advanceToSafetyPeriod(); - let tx = await claimsManager.submitClaim( - accounts[2], - 8000, - "description hash", - { - from: accounts[1], - } - ); - - let claimId = tx.logs[0].args._claimId; - await utils.increaseTime(60 * 60 * 24); - await claimsManager.approveClaim(claimId, 8000, ZERO_ADDRESS); + let tx = await hatVaultsRegistry.setVaultVisibility(vault.address, true); + assert.equal(tx.logs[0].event, "SetVaultVisibility"); + assert.equal(tx.logs[0].args._visible, true); + assert.equal(await hatVaultsRegistry.isVaultVisible(vault.address), true); - let payload = "0x00000000000000000000000000000000000001"; try { - await hatVaultsRegistry.swapAndSend( - stakingToken.address, - [accounts[1]], - web3.utils.toWei("1"), - router.address, - payload, - { from: accounts[0] } - ); - assert(false, "swap should not be successful"); + await vault.setVaultDescription("_descriptionHash", { from: accounts[1] }); + assert(false, "only gov"); } catch (ex) { - assertVMException(ex, "SwapFailed"); + assertVMException(ex, "OnlyRegistryOwner"); } + tx = await vault.setVaultDescription("_descriptionHash"); + assert.equal(tx.logs[0].event, "SetVaultDescription"); + assert.equal(tx.logs[0].args._descriptionHash, "_descriptionHash"); }); it("log claim", async () => { @@ -6549,11 +5050,9 @@ it("getVaultReward - no vault updates will return 0 ", async () => { 0, 8000, [7500, 2000, 500], - [1500, 500], + 2000, 10, - 0, 100, - false, 2500000, 60 * 60 * 24, true @@ -6754,7 +5253,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { }); it("no vesting", async () => { - await setUpGlobalVars(accounts, 0, 8000, [0, 10000, 0], [0, 0]); + await setUpGlobalVars(accounts, 0, 8000, [0, 10000, 0], 0); var staker = accounts[4]; await stakingToken.approve(vault.address, web3.utils.toWei("1"), { @@ -6933,6 +5432,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 } @@ -6990,6 +5490,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 } @@ -7022,7 +5523,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); @@ -7054,6 +5555,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, vestingDuration: 10, vestingPeriods: 86400 } @@ -7084,6 +5586,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, vestingDuration: 121 * 24 * 3600, vestingPeriods: 10 } @@ -7114,6 +5617,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 0 } @@ -7142,6 +5646,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 } @@ -7194,8 +5699,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { epochRewardPerBlock }], hatVaultsRegistryConf: { - bountyGovernanceHAT: 1000, - bountyHackerHATVested: 500 + governanceFee: 1000 }, silent: true }); @@ -7227,6 +5731,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [8400, 1500, 100], + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 } @@ -7257,8 +5762,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { epochRewardPerBlock }], hatVaultsRegistryConf: { - bountyGovernanceHAT: 1000, - bountyHackerHATVested: 500 + governanceFee: 1000 }, silent: true }); @@ -7308,11 +5812,9 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10, - 0, 100, - false, 88260 ); @@ -7362,7 +5864,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { (await web3.eth.getBlock("latest")).number, 8000, [7000, 2500, 500], - [1000, 500], + 1000, 10000 ); @@ -7400,6 +5902,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 } @@ -7484,6 +5987,32 @@ it("getVaultReward - no vault updates will return 0 ", async () => { ); }); + it("set governance fee receiver", async () => { + await setUpGlobalVars(accounts); + try { + await hatVaultsRegistry.setGovernanceFeeReceiver(accounts[2], { + from: accounts[1], + }); + assert(false, "only gov"); + } catch (ex) { + assertVMException(ex, "Ownable: caller is not the owner"); + } + + assert.equal( + await hatVaultsRegistry.governanceFeeReceiver(), + accounts[0] + ); + var tx = await hatVaultsRegistry.setGovernanceFeeReceiver(accounts[2], { + from: accounts[0], + }); + assert.equal(tx.logs[0].event, "SetGovernanceFeeReceiver"); + assert.equal(tx.logs[0].args._governaceFeeReceiver, accounts[2]); + assert.equal( + await hatVaultsRegistry.governanceFeeReceiver(), + accounts[2] + ); + }); + it("withdraw+ deposit + addition HAT ", async () => { await setUpGlobalVars(accounts, (await web3.eth.getBlock("latest")).number); var staker = accounts[1]; @@ -7508,6 +6037,7 @@ it("getVaultReward - no vault updates will return 0 ", async () => { isTokenLockRevocable: false, maxBounty: 8000, bountySplit: [7000, 2500, 500], + governanceFee: 65535, vestingDuration: 86400, vestingPeriods: 10 } @@ -7582,8 +6112,6 @@ it("getVaultReward - no vault updates will return 0 ", async () => { undefined, undefined, undefined, - undefined, - undefined, 1 ); var staker = accounts[4];