ERC-20
Overview
Max Total Supply
2,069,131.578947368421052631 HAThopr
Holders
4
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 18 Decimals)
Balance
16,000 HAThoprValue
$0.00Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Minimal Proxy Contract for 0x028a7c6873dfa8357c9dcf9c9d76ef2abb66256e
Contract Name:
HATVault
Compiler Version
v0.8.16+commit.07a7930e
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT // Disclaimer https://github.com/hats-finance/hats-contracts/blob/main/DISCLAIMER.md pragma solidity 0.8.16; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./tokenlock/TokenLockFactory.sol"; import "./interfaces/IHATVault.sol"; import "./interfaces/IRewardController.sol"; import "./HATVaultsRegistry.sol"; /** @title A Hats.finance vault which holds the funds for a specific project's * bug bounties * @author Hats.finance * @notice The HATVault can be deposited into in a permissionless manner using * the vault’s native token. When a bug is submitted and approved, the bounty * is paid out using the funds in the vault. Bounties are paid out as a * percentage of the vault. The percentage is set according to the severity of * the bug. Vaults have regular safety periods (typically for an hour twice a * day) which are time for the committee to make decisions. * * In addition to the roles defined in the HATVaultsRegistry, every HATVault * has the roles: * Committee - The only address which can submit a claim for a bounty payout * and set the maximum bounty. * User - Anyone can deposit the vault's native token into the vault and * recieve shares for it. Shares represent the user's relative part in the * vault, and when a bounty is paid out, users lose part of their deposits * (based on percentage paid), but keep their share of the vault. * Users also receive rewards for their deposits, which can be claimed at any * time. * To withdraw previously deposited tokens, a user must first send a withdraw * request, and the withdrawal will be made available after a pending period. * Withdrawals are not permitted during safety periods or while there is an * active claim for a bounty payout. * * Bounties are payed out distributed between a few channels, and that * distribution is set upon creation (the hacker gets part in direct transfer, * part in vested reward and part in vested HAT token, part gets rewarded to * the committee, part gets swapped to HAT token and burned and/or sent to Hats * governance). * * This project is open-source and can be found at: * https://github.com/hats-finance/hats-contracts */ contract HATVault is IHATVault, ERC4626Upgradeable, OwnableUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20 for IERC20; using MathUpgradeable for uint256; struct Claim { bytes32 claimId; address beneficiary; uint16 bountyPercentage; // the address of the committee at the time of the submission, so that this committee will // be paid their share of the bounty in case the committee changes before claim approval address committee; uint32 createdAt; uint32 challengedAt; uint256 bountyGovernanceHAT; uint256 bountyHackerHATVested; address arbitrator; uint32 challengePeriod; uint32 challengeTimeOutPeriod; bool arbitratorCanChangeBounty; } struct PendingMaxBounty { uint16 maxBounty; uint32 timestamp; } uint256 public constant MAX_UINT = type(uint256).max; uint16 public constant NULL_UINT16 = type(uint16).max; uint32 public constant NULL_UINT32 = type(uint32).max; address public constant NULL_ADDRESS = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; uint256 public constant HUNDRED_PERCENT = 1e4; uint256 public constant HUNDRED_PERCENT_SQRD = 1e8; uint256 public constant MAX_BOUNTY_LIMIT = 90e2; // Max bounty can be up to 90% uint256 public constant MAX_WITHDRAWAL_FEE = 2e2; // Max fee is 2% uint256 public constant MAX_COMMITTEE_BOUNTY = 10e2; // Max committee bounty can be up to 10% uint256 public constant MINIMAL_AMOUNT_OF_SHARES = 1e3; // to reduce rounding errors, the number of shares is either 0, or > than this number HATVaultsRegistry public registry; ITokenLockFactory public tokenLockFactory; Claim public activeClaim; IRewardController[] public rewardControllers; IHATVault.BountySplit public bountySplit; uint16 public maxBounty; uint32 public vestingDuration; uint32 public vestingPeriods; address public committee; bool public committeeCheckedIn; bool public depositPause; uint256 public withdrawalFee; uint256 internal nonce; PendingMaxBounty public pendingMaxBounty; // Time of when withdrawal period starts for every user that has an // active withdraw request. (time when last withdraw request pending // period ended, or 0 if last action was deposit or withdraw) mapping(address => uint256) public withdrawEnableStartTime; // 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; // address of the arbitrator - which can dispute claims and override the committee's decisions address internal arbitrator; // time during which a claim can be challenged by the arbitrator uint32 internal challengePeriod; // time after which a challenged claim is automatically dismissed uint32 internal challengeTimeOutPeriod; // whether the arbitrator can change bounty of claims ArbitratorCanChangeBounty internal arbitratorCanChangeBounty; bool private _isEmergencyWithdraw; modifier onlyRegistryOwner() { if (registry.owner() != msg.sender) revert OnlyRegistryOwner(); _; } modifier onlyFeeSetter() { if (registry.feeSetter() != msg.sender) revert OnlyFeeSetter(); _; } modifier onlyCommittee() { if (committee != msg.sender) revert OnlyCommittee(); _; } modifier notEmergencyPaused() { if (registry.isEmergencyPaused()) revert SystemInEmergencyPause(); _; } modifier noSafetyPeriod() { uint256 _withdrawPeriod = registry.getWithdrawPeriod(); // disable withdraw for safetyPeriod (e.g 1 hour) after each withdrawPeriod(e.g 11 hours) // solhint-disable-next-line not-rely-on-time if (block.timestamp % (_withdrawPeriod + registry.getSafetyPeriod()) >= _withdrawPeriod) revert SafetyPeriod(); _; } modifier noActiveClaim() { if (activeClaim.createdAt != 0) revert ActiveClaimExists(); _; } modifier isActiveClaim(bytes32 _claimId) { if (activeClaim.createdAt == 0) revert NoActiveClaimExists(); if (activeClaim.claimId != _claimId) revert ClaimIdIsNotActive(); _; } /** @notice See {IHATVault-initialize}. */ function initialize(IHATVault.VaultInitParams calldata _params) external initializer { if (_params.maxBounty > MAX_BOUNTY_LIMIT) revert MaxBountyCannotBeMoreThanMaxBountyLimit(); _validateSplit(_params.bountySplit); __ERC20_init(string.concat("Hats Vault ", _params.name), string.concat("HAT", _params.symbol)); __ERC4626_init(IERC20MetadataUpgradeable(address(_params.asset))); rewardControllers = _params.rewardControllers; _setVestingParams(_params.vestingDuration, _params.vestingPeriods); HATVaultsRegistry _registry = HATVaultsRegistry(msg.sender); maxBounty = _params.maxBounty; bountySplit = _params.bountySplit; committee = _params.committee; depositPause = _params.isPaused; registry = _registry; __ReentrancyGuard_init(); _transferOwnership(_params.owner); tokenLockFactory = _registry.tokenLockFactory(); // Set vault to use default registry values where applicable arbitrator = NULL_ADDRESS; bountyGovernanceHAT = NULL_UINT16; bountyHackerHATVested = NULL_UINT16; arbitratorCanChangeBounty = ArbitratorCanChangeBounty.DEFAULT; challengePeriod = NULL_UINT32; challengeTimeOutPeriod = NULL_UINT32; emit SetVaultDescription(_params.descriptionHash); } /* ---------------------------------- Claim --------------------------------------- */ /** @notice See {IHATVault-submitClaim}. */ function submitClaim(address _beneficiary, uint16 _bountyPercentage, string calldata _descriptionHash) external onlyCommittee noActiveClaim notEmergencyPaused returns (bytes32 claimId) { HATVaultsRegistry _registry = registry; uint256 withdrawPeriod = _registry.getWithdrawPeriod(); // require we are in safetyPeriod // solhint-disable-next-line not-rely-on-time if (block.timestamp % (withdrawPeriod + _registry.getSafetyPeriod()) < withdrawPeriod) revert NotSafetyPeriod(); if (_bountyPercentage > maxBounty) revert BountyPercentageHigherThanMaxBounty(); claimId = keccak256(abi.encodePacked(address(this), ++nonce)); activeClaim = Claim({ claimId: claimId, beneficiary: _beneficiary, bountyPercentage: _bountyPercentage, committee: msg.sender, // solhint-disable-next-line not-rely-on-time createdAt: uint32(block.timestamp), challengedAt: 0, bountyGovernanceHAT: getBountyGovernanceHAT(), bountyHackerHATVested: getBountyHackerHATVested(), arbitrator: getArbitrator(), challengePeriod: getChallengePeriod(), challengeTimeOutPeriod: getChallengeTimeOutPeriod(), arbitratorCanChangeBounty: getArbitratorCanChangeBounty() }); emit SubmitClaim( claimId, msg.sender, _beneficiary, _bountyPercentage, _descriptionHash ); } function challengeClaim(bytes32 _claimId) external isActiveClaim(_claimId) { if (msg.sender != activeClaim.arbitrator && msg.sender != registry.owner()) revert OnlyArbitratorOrRegistryOwner(); // solhint-disable-next-line not-rely-on-time if (block.timestamp >= activeClaim.createdAt + activeClaim.challengePeriod) revert ChallengePeriodEnded(); if (activeClaim.challengedAt != 0) { revert ClaimAlreadyChallenged(); } // solhint-disable-next-line not-rely-on-time activeClaim.challengedAt = uint32(block.timestamp); emit ChallengeClaim(_claimId); } /** @notice See {IHATVault-approveClaim}. */ function approveClaim(bytes32 _claimId, uint16 _bountyPercentage) external nonReentrant isActiveClaim(_claimId) { Claim memory _claim = activeClaim; delete activeClaim; // solhint-disable-next-line not-rely-on-time if (block.timestamp >= _claim.createdAt + _claim.challengePeriod + _claim.challengeTimeOutPeriod) { // cannot approve an expired claim revert ClaimExpired(); } if (_claim.challengedAt != 0) { // the claim was challenged, and only the arbitrator can approve it, within the timeout period if ( msg.sender != _claim.arbitrator || // solhint-disable-next-line not-rely-on-time block.timestamp >= _claim.challengedAt + _claim.challengeTimeOutPeriod ) revert ChallengedClaimCanOnlyBeApprovedByArbitratorUntilChallengeTimeoutPeriod(); // the arbitrator can update the bounty if needed if (_claim.arbitratorCanChangeBounty && _bountyPercentage != 0) { _claim.bountyPercentage = _bountyPercentage; } } else { // the claim can be approved by anyone if the challengePeriod passed without a challenge if ( // solhint-disable-next-line not-rely-on-time block.timestamp <= _claim.createdAt + _claim.challengePeriod ) revert UnchallengedClaimCanOnlyBeApprovedAfterChallengePeriod(); } address tokenLock; IHATVault.ClaimBounty memory claimBounty = _calcClaimBounty( _claim.bountyPercentage, _claim.bountyGovernanceHAT, _claim.bountyHackerHATVested ); IERC20 _asset = IERC20(asset()); if (claimBounty.hackerVested > 0) { //hacker gets part of bounty to a vesting contract tokenLock = tokenLockFactory.createTokenLock( address(_asset), 0x0000000000000000000000000000000000000000, //this address as owner, so it can do nothing. _claim.beneficiary, claimBounty.hackerVested, // solhint-disable-next-line not-rely-on-time block.timestamp, //start // solhint-disable-next-line not-rely-on-time block.timestamp + vestingDuration, //end vestingPeriods, 0, //no release start 0, //no cliff ITokenLock.Revocability.Disabled, false ); _asset.safeTransfer(tokenLock, claimBounty.hackerVested); } _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. HATVaultsRegistry _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); emit ApproveClaim( _claimId, msg.sender, _claim.beneficiary, _claim.bountyPercentage, tokenLock, claimBounty ); } /** @notice See {IHATVault-dismissClaim}. */ function dismissClaim(bytes32 _claimId) external isActiveClaim(_claimId) { uint256 _challengeTimeOutPeriod = activeClaim.challengeTimeOutPeriod; uint256 _challengedAt = activeClaim.challengedAt; // solhint-disable-next-line not-rely-on-time if (block.timestamp <= activeClaim.createdAt + activeClaim.challengePeriod + _challengeTimeOutPeriod) { if (_challengedAt == 0) revert OnlyCallableIfChallenged(); if ( // solhint-disable-next-line not-rely-on-time block.timestamp <= _challengedAt + _challengeTimeOutPeriod && msg.sender != activeClaim.arbitrator ) revert OnlyCallableByArbitratorOrAfterChallengeTimeOutPeriod(); } // else the claim is expired and should be dismissed delete activeClaim; emit DismissClaim(_claimId); } /* -------------------------------------------------------------------------------- */ /* ---------------------------------- Params -------------------------------------- */ /** @notice See {IHATVault-setCommittee}. */ function setCommittee(address _committee) external { // vault owner can update committee only if committee was not checked in yet. if (msg.sender == owner() && committee != msg.sender) { if (committeeCheckedIn) revert CommitteeAlreadyCheckedIn(); } else { if (committee != msg.sender) revert OnlyCommittee(); } committee = _committee; emit SetCommittee(_committee); } /** @notice See {IHATVault-setVestingParams}. */ function setVestingParams(uint32 _duration, uint32 _periods) external onlyOwner { _setVestingParams(_duration, _periods); } /** @notice See {IHATVault-setBountySplit}. */ function setBountySplit(IHATVault.BountySplit calldata _bountySplit) external onlyOwner noActiveClaim noSafetyPeriod { _validateSplit(_bountySplit); bountySplit = _bountySplit; emit SetBountySplit(_bountySplit); } /** @notice See {IHATVault-setWithdrawalFee}. */ function setWithdrawalFee(uint256 _fee) external onlyFeeSetter { if (_fee > MAX_WITHDRAWAL_FEE) revert WithdrawalFeeTooBig(); withdrawalFee = _fee; emit SetWithdrawalFee(_fee); } /** @notice See {IHATVault-committeeCheckIn}. */ function committeeCheckIn() external onlyCommittee { committeeCheckedIn = true; emit CommitteeCheckedIn(); } /** @notice See {IHATVault-setPendingMaxBounty}. */ function setPendingMaxBounty(uint16 _maxBounty) external onlyOwner noActiveClaim { if (_maxBounty > MAX_BOUNTY_LIMIT) revert MaxBountyCannotBeMoreThanMaxBountyLimit(); pendingMaxBounty.maxBounty = _maxBounty; // solhint-disable-next-line not-rely-on-time pendingMaxBounty.timestamp = uint32(block.timestamp); emit SetPendingMaxBounty(_maxBounty); } /** @notice See {IHATVault-setMaxBounty}. */ function setMaxBounty() external onlyOwner noActiveClaim { PendingMaxBounty memory _pendingMaxBounty = pendingMaxBounty; if (_pendingMaxBounty.timestamp == 0) revert NoPendingMaxBounty(); // solhint-disable-next-line not-rely-on-time if (block.timestamp - _pendingMaxBounty.timestamp < registry.getSetMaxBountyDelay()) revert DelayPeriodForSettingMaxBountyHadNotPassed(); uint16 _maxBounty = pendingMaxBounty.maxBounty; maxBounty = _maxBounty; delete pendingMaxBounty; emit SetMaxBounty(_maxBounty); } /** @notice See {IHATVault-setDepositPause}. */ function setDepositPause(bool _depositPause) external onlyOwner { depositPause = _depositPause; emit SetDepositPause(_depositPause); } /** @notice See {IHATVault-setVaultDescription}. */ function setVaultDescription(string calldata _descriptionHash) external onlyRegistryOwner { emit SetVaultDescription(_descriptionHash); } /** @notice See {IHATVault-addRewardController}. */ function addRewardController(IRewardController _rewardController) external onlyRegistryOwner noActiveClaim { for (uint256 i = 0; i < rewardControllers.length;) { if (_rewardController == rewardControllers[i]) revert DuplicatedRewardController(); unchecked { ++i; } } rewardControllers.push(_rewardController); emit AddRewardController(_rewardController); } /** @notice See {IHATVault-setHATBountySplit}. */ function setHATBountySplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) external onlyRegistryOwner { bountyGovernanceHAT = _bountyGovernanceHAT; bountyHackerHATVested = _bountyHackerHATVested; registry.validateHATSplit(getBountyGovernanceHAT(), getBountyHackerHATVested()); emit SetHATBountySplit(_bountyGovernanceHAT, _bountyHackerHATVested); } /** @notice See {IHATVault-setArbitrator}. */ function setArbitrator(address _arbitrator) external onlyRegistryOwner { arbitrator = _arbitrator; emit SetArbitrator(_arbitrator); } /** @notice See {IHATVault-setChallengePeriod}. */ function setChallengePeriod(uint32 _challengePeriod) external onlyRegistryOwner { if (_challengePeriod != NULL_UINT32) { registry.validateChallengePeriod(_challengePeriod); } challengePeriod = _challengePeriod; emit SetChallengePeriod(_challengePeriod); } /** @notice See {IHATVault-setChallengeTimeOutPeriod}. */ function setChallengeTimeOutPeriod(uint32 _challengeTimeOutPeriod) external onlyRegistryOwner { if (_challengeTimeOutPeriod != NULL_UINT32) { registry.validateChallengeTimeOutPeriod(_challengeTimeOutPeriod); } challengeTimeOutPeriod = _challengeTimeOutPeriod; emit SetChallengeTimeOutPeriod(_challengeTimeOutPeriod); } /** @notice See {IHATVault-setArbitratorCanChangeBounty}. */ function setArbitratorCanChangeBounty(ArbitratorCanChangeBounty _arbitratorCanChangeBounty) external onlyRegistryOwner { arbitratorCanChangeBounty = _arbitratorCanChangeBounty; emit SetArbitratorCanChangeBounty(_arbitratorCanChangeBounty); } /* -------------------------------------------------------------------------------- */ /* ---------------------------------- Vault --------------------------------------- */ /** @notice See {IHATVault-withdrawRequest}. */ function withdrawRequest() external nonReentrant { // set the withdrawEnableStartTime time to be withdrawRequestPendingPeriod from now // solhint-disable-next-line not-rely-on-time uint256 _withdrawEnableStartTime = block.timestamp + registry.getWithdrawRequestPendingPeriod(); address msgSender = _msgSender(); withdrawEnableStartTime[msgSender] = _withdrawEnableStartTime; emit WithdrawRequest(msgSender, _withdrawEnableStartTime); } /** @notice See {IHATVault-withdrawAndClaim}. */ function withdrawAndClaim(uint256 assets, address receiver, address owner) external returns (uint256 shares) { shares = withdraw(assets, receiver, owner); for (uint256 i = 0; i < rewardControllers.length;) { rewardControllers[i].claimReward(address(this), owner); unchecked { ++i; } } } /** @notice See {IHATVault-redeemAndClaim}. */ function redeemAndClaim(uint256 shares, address receiver, address owner) external returns (uint256 assets) { assets = redeem(shares, receiver, owner); for (uint256 i = 0; i < rewardControllers.length;) { rewardControllers[i].claimReward(address(this), owner); unchecked { ++i; } } } /** @notice See {IHATVault-emergencyWithdraw}. */ function emergencyWithdraw(address receiver) external returns (uint256 assets) { _isEmergencyWithdraw = true; address msgSender = _msgSender(); assets = redeem(balanceOf(msgSender), receiver, msgSender); _isEmergencyWithdraw = false; } /** @notice See {IHATVault-withdraw}. */ function withdraw(uint256 assets, address receiver, address owner) public override(IHATVault, ERC4626Upgradeable) virtual returns (uint256) { (uint256 _shares, uint256 _fee) = previewWithdrawAndFee(assets); _withdraw(_msgSender(), receiver, owner, assets, _shares, _fee); return _shares; } /** @notice See {IHATVault-redeem}. */ function redeem(uint256 shares, address receiver, address owner) public override(IHATVault, ERC4626Upgradeable) virtual returns (uint256) { (uint256 _assets, uint256 _fee) = previewRedeemAndFee(shares); _withdraw(_msgSender(), receiver, owner, _assets, shares, _fee); return _assets; } /** @notice See {IHATVault-deposit}. */ function deposit(uint256 assets, address receiver) public override(IHATVault, ERC4626Upgradeable) virtual returns (uint256) { return super.deposit(assets, receiver); } /** @notice See {IHATVault-withdraw}. */ function withdraw(uint256 assets, address receiver, address owner, uint256 maxShares) public virtual returns (uint256) { uint256 shares = withdraw(assets, receiver, owner); if (shares > maxShares) revert WithdrawSlippageProtection(); return shares; } /** @notice See {IHATVault-redeem}. */ function redeem(uint256 shares, address receiver, address owner, uint256 minAssets) public virtual returns (uint256) { uint256 assets = redeem(shares, receiver, owner); if (assets < minAssets) revert RedeemSlippageProtection(); return assets; } /** @notice See {IHATVault-withdrawAndClaim}. */ function withdrawAndClaim(uint256 assets, address receiver, address owner, uint256 maxShares) external returns (uint256 shares) { shares = withdraw(assets, receiver, owner, maxShares); for (uint256 i = 0; i < rewardControllers.length;) { rewardControllers[i].claimReward(address(this), owner); unchecked { ++i; } } } /** @notice See {IHATVault-redeemAndClaim}. */ function redeemAndClaim(uint256 shares, address receiver, address owner, uint256 minAssets) external returns (uint256 assets) { assets = redeem(shares, receiver, owner, minAssets); for (uint256 i = 0; i < rewardControllers.length;) { rewardControllers[i].claimReward(address(this), owner); unchecked { ++i; } } } /** @notice See {IHATVault-deposit}. */ function deposit(uint256 assets, address receiver, uint256 minShares) external virtual returns (uint256) { uint256 shares = deposit(assets, receiver); if (shares < minShares) revert DepositSlippageProtection(); return shares; } /** @notice See {IHATVault-mint}. */ function mint(uint256 shares, address receiver, uint256 maxAssets) external virtual returns (uint256) { uint256 assets = mint(shares, receiver); if (assets > maxAssets) revert MintSlippageProtection(); return assets; } /** @notice See {IERC4626Upgradeable-maxDeposit}. */ function maxDeposit(address) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256) { return depositPause ? 0 : MAX_UINT; } /** @notice See {IERC4626Upgradeable-maxMint}. */ function maxMint(address) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256) { return depositPause ? 0 : MAX_UINT; } /** @notice See {IERC4626Upgradeable-maxWithdraw}. */ function maxWithdraw(address owner) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256) { if (activeClaim.createdAt != 0 || !_isWithdrawEnabledForUser(owner)) return 0; return previewRedeem(balanceOf(owner)); } /** @notice See {IERC4626Upgradeable-maxRedeem}. */ function maxRedeem(address owner) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256) { if (activeClaim.createdAt != 0 || !_isWithdrawEnabledForUser(owner)) return 0; return balanceOf(owner); } /** @notice See {IERC4626Upgradeable-previewWithdraw}. */ function previewWithdraw(uint256 assets) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256 shares) { (shares,) = previewWithdrawAndFee(assets); } /** @notice See {IERC4626Upgradeable-previewRedeem}. */ function previewRedeem(uint256 shares) public view virtual override(IERC4626Upgradeable, ERC4626Upgradeable) returns (uint256 assets) { (assets,) = previewRedeemAndFee(shares); } /** @notice See {IHATVault-previewWithdrawAndFee}. */ function previewWithdrawAndFee(uint256 assets) public view returns (uint256 shares, uint256 fee) { uint256 _withdrawalFee = withdrawalFee; fee = assets.mulDiv(_withdrawalFee, (HUNDRED_PERCENT - _withdrawalFee)); shares = _convertToShares(assets + fee, MathUpgradeable.Rounding.Up); } /** @notice See {IHATVault-previewRedeemAndFee}. */ function previewRedeemAndFee(uint256 shares) public view returns (uint256 assets, uint256 fee) { uint256 _assetsPlusFee = _convertToAssets(shares, MathUpgradeable.Rounding.Down); fee = _assetsPlusFee.mulDiv(withdrawalFee, HUNDRED_PERCENT); unchecked { // fee will always be maximun 20% of _assetsPlusFee assets = _assetsPlusFee - fee; } } /* -------------------------------------------------------------------------------- */ /* --------------------------------- Getters -------------------------------------- */ /** @notice See {IHATVault-getBountyGovernanceHAT}. */ function getBountyGovernanceHAT() public view returns(uint16) { uint16 _bountyGovernanceHAT = bountyGovernanceHAT; if (_bountyGovernanceHAT != NULL_UINT16) { return _bountyGovernanceHAT; } else { return registry.defaultBountyGovernanceHAT(); } } /** @notice See {IHATVault-getBountyHackerHATVested}. */ function getBountyHackerHATVested() public view returns(uint16) { uint16 _bountyHackerHATVested = bountyHackerHATVested; if (_bountyHackerHATVested != NULL_UINT16) { return _bountyHackerHATVested; } else { return registry.defaultBountyHackerHATVested(); } } /** @notice See {IHATVault-getArbitrator}. */ function getArbitrator() public view returns(address) { address _arbitrator = arbitrator; if (_arbitrator != NULL_ADDRESS) { return _arbitrator; } else { return registry.defaultArbitrator(); } } /** @notice See {IHATVault-getChallengePeriod}. */ function getChallengePeriod() public view returns(uint32) { uint32 _challengePeriod = challengePeriod; if (_challengePeriod != NULL_UINT32) { return _challengePeriod; } else { return registry.defaultChallengePeriod(); } } /** @notice See {IHATVault-getChallengeTimeOutPeriod}. */ function getChallengeTimeOutPeriod() public view returns(uint32) { uint32 _challengeTimeOutPeriod = challengeTimeOutPeriod; if (_challengeTimeOutPeriod != NULL_UINT32) { return _challengeTimeOutPeriod; } else { return registry.defaultChallengeTimeOutPeriod(); } } /** @notice See {IHATVault-getArbitratorCanChangeBounty}. */ function getArbitratorCanChangeBounty() public view returns(bool) { ArbitratorCanChangeBounty _arbitratorCanChangeBounty = arbitratorCanChangeBounty; if (_arbitratorCanChangeBounty != ArbitratorCanChangeBounty.DEFAULT) { return _arbitratorCanChangeBounty == ArbitratorCanChangeBounty.YES; } else { return registry.defaultArbitratorCanChangeBounty(); } } /* -------------------------------------------------------------------------------- */ /* --------------------------------- Helpers -------------------------------------- */ /** * @dev Deposit funds to the vault. Can only be called if the committee had * checked in and deposits are not paused. * @param caller Caller of the action (msg.sender) * @param receiver Reciever of the shares from the deposit * @param assets Amount of vault's native token to deposit * @param shares Respective amount of shares to be received */ function _deposit( address caller, address receiver, uint256 assets, uint256 shares ) internal override virtual nonReentrant { if (!committeeCheckedIn) revert CommitteeNotCheckedInYet(); if (receiver == caller && withdrawEnableStartTime[receiver] != 0 ) { // clear withdraw request if caller deposits in her own account withdrawEnableStartTime[receiver] = 0; } super._deposit(caller, receiver, assets, shares); } // amount of shares correspond with assets + fee function _withdraw( address _caller, address _receiver, address _owner, uint256 _assets, uint256 _shares, uint256 _fee ) internal nonReentrant { if (_assets == 0) revert WithdrawMustBeGreaterThanZero(); if (_caller != _owner) { _spendAllowance(_owner, _caller, _shares); } _burn(_owner, _shares); IERC20 _asset = IERC20(asset()); if (_fee > 0) { _asset.safeTransfer(registry.owner(), _fee); } _asset.safeTransfer(_receiver, _assets); emit Withdraw(_caller, _receiver, _owner, _assets, _shares); } function _beforeTokenTransfer( address _from, address _to, uint256 _amount ) internal virtual override { if (_amount == 0) revert AmountCannotBeZero(); if (_from == _to) revert CannotTransferToSelf(); // deposit/mint/transfer if (_to != address(0)) { HATVaultsRegistry _registry = registry; if (_registry.isEmergencyPaused()) revert SystemInEmergencyPause(); // Cannot transfer or mint tokens to a user for which an active withdraw request exists // because then we would need to reset their withdraw request uint256 _withdrawEnableStartTime = withdrawEnableStartTime[_to]; if (_withdrawEnableStartTime != 0) { // solhint-disable-next-line not-rely-on-time if (block.timestamp <= _withdrawEnableStartTime + _registry.getWithdrawRequestEnablePeriod()) revert CannotTransferToAnotherUserWithActiveWithdrawRequest(); } for (uint256 i = 0; i < rewardControllers.length;) { rewardControllers[i].commitUserBalance(_to, _amount, true); unchecked { ++i; } } } // withdraw/redeem/transfer if (_from != address(0)) { if (_amount > maxRedeem(_from)) revert RedeemMoreThanMax(); // if all is ok and withdrawal can be made - // reset withdrawRequests[_pid][msg.sender] so that another withdrawRequest // will have to be made before next withdrawal withdrawEnableStartTime[_from] = 0; if (!_isEmergencyWithdraw) { for (uint256 i = 0; i < rewardControllers.length;) { rewardControllers[i].commitUserBalance(_from, _amount, false); unchecked { ++i; } } } } } function _afterTokenTransfer(address, address, uint256) internal virtual override { if (totalSupply() > 0 && totalSupply() < MINIMAL_AMOUNT_OF_SHARES) { revert AmountOfSharesMustBeMoreThanMinimalAmount(); } } function _setVestingParams(uint32 _duration, uint32 _periods) internal { if (_duration > 120 days) revert VestingDurationTooLong(); if (_periods == 0) revert VestingPeriodsCannotBeZero(); if (_duration < _periods) revert VestingDurationSmallerThanPeriods(); vestingDuration = _duration; vestingPeriods = _periods; emit SetVestingParams(_duration, _periods); } /** * @dev Checks that the given user can perform a withdraw at this time * @param _user Address of the user to check */ function _isWithdrawEnabledForUser(address _user) internal view returns(bool) { HATVaultsRegistry _registry = registry; uint256 _withdrawPeriod = _registry.getWithdrawPeriod(); // disable withdraw for safetyPeriod (e.g 1 hour) after each withdrawPeriod (e.g 11 hours) // solhint-disable-next-line not-rely-on-time if (block.timestamp % (_withdrawPeriod + _registry.getSafetyPeriod()) >= _withdrawPeriod) return false; // check that withdrawRequestPendingPeriod had passed uint256 _withdrawEnableStartTime = withdrawEnableStartTime[_user]; // solhint-disable-next-line not-rely-on-time return (block.timestamp >= _withdrawEnableStartTime && // check that withdrawRequestEnablePeriod had not passed and that the // last action was withdrawRequest (and not deposit or withdraw, which // reset withdrawRequests[_user] to 0) // solhint-disable-next-line not-rely-on-time block.timestamp <= _withdrawEnableStartTime + _registry.getWithdrawRequestEnablePeriod()); } /** * @dev calculate the specific bounty payout distribution, according to the * 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 * @return claimBounty The bounty distribution for this specific claim */ function _calcClaimBounty( uint256 _bountyPercentage, uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVested ) internal view returns(IHATVault.ClaimBounty memory claimBounty) { uint256 _totalAssets = totalAssets(); if (_totalAssets == 0) { return claimBounty; } if (_bountyPercentage > maxBounty) revert BountyPercentageHigherThanMaxBounty(); uint256 _totalBountyAmount = _totalAssets * _bountyPercentage; uint256 _governanceHatAmount = _totalBountyAmount.mulDiv(_bountyGovernanceHAT, HUNDRED_PERCENT_SQRD); uint256 _hackerHatVestedAmount = _totalBountyAmount.mulDiv(_bountyHackerHATVested, HUNDRED_PERCENT_SQRD); _totalBountyAmount -= (_governanceHatAmount + _hackerHatVestedAmount) * HUNDRED_PERCENT; claimBounty.governanceHat = _governanceHatAmount; claimBounty.hackerHatVested = _hackerHatVestedAmount; uint256 _hackerVestedAmount = _totalBountyAmount.mulDiv(bountySplit.hackerVested, HUNDRED_PERCENT_SQRD); uint256 _hackerAmount = _totalBountyAmount.mulDiv(bountySplit.hacker, HUNDRED_PERCENT_SQRD); _totalBountyAmount -= (_hackerVestedAmount + _hackerAmount) * HUNDRED_PERCENT; claimBounty.hackerVested = _hackerVestedAmount; claimBounty.hacker = _hackerAmount; // give all the tokens left to the committee to avoid rounding errors claimBounty.committee = _totalBountyAmount / HUNDRED_PERCENT; } /** * @dev Check that a given bounty split is legal, meaning that: * Each entry is a number between 0 and `HUNDRED_PERCENT`. * Except committee part which is capped at maximum of * `MAX_COMMITTEE_BOUNTY`. * Total splits should be equal to `HUNDRED_PERCENT`. * function will revert in case the bounty split is not legal. * @param _bountySplit The bounty split to check */ function _validateSplit(IHATVault.BountySplit calldata _bountySplit) internal pure { if (_bountySplit.committee > MAX_COMMITTEE_BOUNTY) revert CommitteeBountyCannotBeMoreThanMax(); if (_bountySplit.hackerVested + _bountySplit.hacker + _bountySplit.committee != HUNDRED_PERCENT) revert TotalSplitPercentageShouldBeHundredPercent(); } /* -------------------------------------------------------------------------------- */ }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal onlyInitializing { __Ownable_init_unchained(); } function __Ownable_init_unchained() internal onlyInitializing { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol) pragma solidity ^0.8.0; import "../token/ERC20/IERC20Upgradeable.sol"; import "../token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; /** * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. * * _Available since v4.7._ */ interface IERC4626Upgradeable is IERC20Upgradeable, IERC20MetadataUpgradeable { event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view returns (address assetTokenAddress); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view returns (uint256 totalManagedAssets); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw( uint256 assets, address receiver, address owner ) external returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem( uint256 shares, address receiver, address owner ) external returns (uint256 assets); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20Upgradeable.sol"; import "./extensions/IERC20MetadataUpgradeable.sol"; import "../../utils/ContextUpgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing { __ERC20_init_unchained(name_, symbol_); } function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom( address from, address to, uint256 amount ) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer( address from, address to, uint256 amount ) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance( address owner, address spender, uint256 amount ) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[45] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.1) (token/ERC20/extensions/ERC4626.sol) pragma solidity ^0.8.0; import "../ERC20Upgradeable.sol"; import "../utils/SafeERC20Upgradeable.sol"; import "../../../interfaces/IERC4626Upgradeable.sol"; import "../../../utils/math/MathUpgradeable.sol"; import "../../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626]. * * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this * contract and not the "assets" token which is an independent contract. * * CAUTION: When the vault is empty or nearly empty, deposits are at high risk of being stolen through frontrunning with * a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may * similarly be affected by slippage. Users can protect against this attack as well unexpected slippage in general by * verifying the amount received is as expected, using a wrapper that performs these checks such as * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router]. * * _Available since v4.7._ */ abstract contract ERC4626Upgradeable is Initializable, ERC20Upgradeable, IERC4626Upgradeable { using MathUpgradeable for uint256; IERC20Upgradeable private _asset; uint8 private _decimals; /** * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777). */ function __ERC4626_init(IERC20Upgradeable asset_) internal onlyInitializing { __ERC4626_init_unchained(asset_); } function __ERC4626_init_unchained(IERC20Upgradeable asset_) internal onlyInitializing { (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_); _decimals = success ? assetDecimals : super.decimals(); _asset = asset_; } /** * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way. */ function _tryGetAssetDecimals(IERC20Upgradeable asset_) private view returns (bool, uint8) { (bool success, bytes memory encodedDecimals) = address(asset_).staticcall( abi.encodeWithSelector(IERC20MetadataUpgradeable.decimals.selector) ); if (success && encodedDecimals.length >= 32) { uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256)); if (returnedDecimals <= type(uint8).max) { return (true, uint8(returnedDecimals)); } } return (false, 0); } /** * @dev Decimals are read from the underlying asset in the constructor and cached. If this fails (e.g., the asset * has not been created yet), the cached value is set to a default obtained by `super.decimals()` (which depends on * inheritance but is most likely 18). Override this function in order to set a guaranteed hardcoded value. * See {IERC20Metadata-decimals}. */ function decimals() public view virtual override(IERC20MetadataUpgradeable, ERC20Upgradeable) returns (uint8) { return _decimals; } /** @dev See {IERC4626-asset}. */ function asset() public view virtual override returns (address) { return address(_asset); } /** @dev See {IERC4626-totalAssets}. */ function totalAssets() public view virtual override returns (uint256) { return _asset.balanceOf(address(this)); } /** @dev See {IERC4626-convertToShares}. */ function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) { return _convertToShares(assets, MathUpgradeable.Rounding.Down); } /** @dev See {IERC4626-convertToAssets}. */ function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) { return _convertToAssets(shares, MathUpgradeable.Rounding.Down); } /** @dev See {IERC4626-maxDeposit}. */ function maxDeposit(address) public view virtual override returns (uint256) { return _isVaultCollateralized() ? type(uint256).max : 0; } /** @dev See {IERC4626-maxMint}. */ function maxMint(address) public view virtual override returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxWithdraw}. */ function maxWithdraw(address owner) public view virtual override returns (uint256) { return _convertToAssets(balanceOf(owner), MathUpgradeable.Rounding.Down); } /** @dev See {IERC4626-maxRedeem}. */ function maxRedeem(address owner) public view virtual override returns (uint256) { return balanceOf(owner); } /** @dev See {IERC4626-previewDeposit}. */ function previewDeposit(uint256 assets) public view virtual override returns (uint256) { return _convertToShares(assets, MathUpgradeable.Rounding.Down); } /** @dev See {IERC4626-previewMint}. */ function previewMint(uint256 shares) public view virtual override returns (uint256) { return _convertToAssets(shares, MathUpgradeable.Rounding.Up); } /** @dev See {IERC4626-previewWithdraw}. */ function previewWithdraw(uint256 assets) public view virtual override returns (uint256) { return _convertToShares(assets, MathUpgradeable.Rounding.Up); } /** @dev See {IERC4626-previewRedeem}. */ function previewRedeem(uint256 shares) public view virtual override returns (uint256) { return _convertToAssets(shares, MathUpgradeable.Rounding.Down); } /** @dev See {IERC4626-deposit}. */ function deposit(uint256 assets, address receiver) public virtual override returns (uint256) { require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max"); uint256 shares = previewDeposit(assets); _deposit(_msgSender(), receiver, assets, shares); return shares; } /** @dev See {IERC4626-mint}. * * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero. * In this case, the shares will be minted without requiring any assets to be deposited. */ function mint(uint256 shares, address receiver) public virtual override returns (uint256) { require(shares <= maxMint(receiver), "ERC4626: mint more than max"); uint256 assets = previewMint(shares); _deposit(_msgSender(), receiver, assets, shares); return assets; } /** @dev See {IERC4626-withdraw}. */ function withdraw( uint256 assets, address receiver, address owner ) public virtual override returns (uint256) { require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max"); uint256 shares = previewWithdraw(assets); _withdraw(_msgSender(), receiver, owner, assets, shares); return shares; } /** @dev See {IERC4626-redeem}. */ function redeem( uint256 shares, address receiver, address owner ) public virtual override returns (uint256) { require(shares <= maxRedeem(owner), "ERC4626: redeem more than max"); uint256 assets = previewRedeem(shares); _withdraw(_msgSender(), receiver, owner, assets, shares); return assets; } /** * @dev Internal conversion function (from assets to shares) with support for rounding direction. * * Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset * would represent an infinite amount of shares. */ function _convertToShares(uint256 assets, MathUpgradeable.Rounding rounding) internal view virtual returns (uint256 shares) { uint256 supply = totalSupply(); return (assets == 0 || supply == 0) ? _initialConvertToShares(assets, rounding) : assets.mulDiv(supply, totalAssets(), rounding); } /** * @dev Internal conversion function (from assets to shares) to apply when the vault is empty. * * NOTE: Make sure to keep this function consistent with {_initialConvertToAssets} when overriding it. */ function _initialConvertToShares( uint256 assets, MathUpgradeable.Rounding /*rounding*/ ) internal view virtual returns (uint256 shares) { return assets; } /** * @dev Internal conversion function (from shares to assets) with support for rounding direction. */ function _convertToAssets(uint256 shares, MathUpgradeable.Rounding rounding) internal view virtual returns (uint256 assets) { uint256 supply = totalSupply(); return (supply == 0) ? _initialConvertToAssets(shares, rounding) : shares.mulDiv(totalAssets(), supply, rounding); } /** * @dev Internal conversion function (from shares to assets) to apply when the vault is empty. * * NOTE: Make sure to keep this function consistent with {_initialConvertToShares} when overriding it. */ function _initialConvertToAssets( uint256 shares, MathUpgradeable.Rounding /*rounding*/ ) internal view virtual returns (uint256 assets) { return shares; } /** * @dev Deposit/mint common workflow. */ function _deposit( address caller, address receiver, uint256 assets, uint256 shares ) internal virtual { // If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the // assets are transferred and before the shares are minted, which is a valid state. // slither-disable-next-line reentrancy-no-eth SafeERC20Upgradeable.safeTransferFrom(_asset, caller, address(this), assets); _mint(receiver, shares); emit Deposit(caller, receiver, assets, shares); } /** * @dev Withdraw/redeem common workflow. */ function _withdraw( address caller, address receiver, address owner, uint256 assets, uint256 shares ) internal virtual { if (caller != owner) { _spendAllowance(owner, caller, shares); } // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the // shares are burned and after the assets are transferred, which is a valid state. _burn(owner, shares); SafeERC20Upgradeable.safeTransfer(_asset, receiver, assets); emit Withdraw(caller, receiver, owner, assets, shares); } /** * @dev Checks if vault is "healthy" in the sense of having assets backing the circulating shares. */ function _isVaultCollateralized() private view returns (bool) { return totalAssets() > 0 || totalSupply() == 0; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20MetadataUpgradeable is IERC20Upgradeable { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20PermitUpgradeable { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; import "../extensions/draft-IERC20PermitUpgradeable.sol"; import "../../../utils/AddressUpgradeable.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20Upgradeable { using AddressUpgradeable for address; function safeTransfer( IERC20Upgradeable token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20Upgradeable token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20Upgradeable token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20PermitUpgradeable token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library MathUpgradeable { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10**64) { value /= 10**64; result += 64; } if (value >= 10**32) { value /= 10**32; result += 32; } if (value >= 10**16) { value /= 10**16; result += 16; } if (value >= 10**8) { value /= 10**8; result += 8; } if (value >= 10**4) { value /= 10**4; result += 4; } if (value >= 10**2) { value /= 10**2; result += 2; } if (value >= 10**1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol) pragma solidity ^0.8.0; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. * * _Available since v3.4._ */ library Clones { /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create opcode, which should never revert. */ function clone(address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create(0, 0x09, 0x37) } require(instance != address(0), "ERC1167: create failed"); } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `implementation` and `salt` multiple time will revert, since * the clones cannot be deployed twice at the same address. */ function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create2(0, 0x09, 0x37, salt) } require(instance != address(0), "ERC1167: create2 failed"); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := keccak256(add(ptr, 0x43), 0x55) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress(address implementation, bytes32 salt) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10**64) { value /= 10**64; result += 64; } if (value >= 10**32) { value /= 10**32; result += 32; } if (value >= 10**16) { value /= 10**16; result += 16; } if (value >= 10**8) { value /= 10**8; result += 8; } if (value >= 10**4) { value /= 10**4; result += 4; } if (value >= 10**2) { value /= 10**2; result += 2; } if (value >= 10**1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // Disclaimer https://github.com/hats-finance/hats-contracts/blob/main/DISCLAIMER.md pragma solidity 0.8.16; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/proxy/Clones.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import "./tokenlock/TokenLockFactory.sol"; import "./interfaces/IHATVaultsRegistry.sol"; import "./HATVault.sol"; /** @title Registry to deploy Hats.finance vaults and manage shared parameters * @author Hats.finance * @notice Hats.finance is a proactive bounty protocol for white hat hackers and * security experts, where projects, community members, and stakeholders * incentivize protocol security and responsible disclosure. * Hats create scalable vaults using the project’s own token. The value of the * bounty increases with the success of the token and project. * * The owner of the registry has the permission to set time limits and bounty * parameters and change vaults' info, and to set the other registry roles - * fee setter and arbitrator. * The arbitrator can challenge submitted claims for bounty payouts made by * vaults' committees, approve them with a different bounty percentage or * dismiss them. * The fee setter can set the fee on withdrawals on all vaults. * * This project is open-source and can be found at: * https://github.com/hats-finance/hats-contracts * * @dev New hats.finance vaults should be created through a call to {createVault} * so that they are linked to the registry */ 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; address public immutable hatVaultImplementation; address[] public hatVaults; // 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 // 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; address public defaultArbitrator; bool public defaultArbitratorCanChangeBounty; bool public isEmergencyPaused; uint32 public defaultChallengePeriod; uint32 public defaultChallengeTimeOutPeriod; /** * @notice initialize - * @param _hatVaultImplementation The hat vault 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 _tokenLockFactory Address of the token lock factory to be used * to create a vesting contract for the approved claim reporter. */ constructor( address _hatVaultImplementation, address _hatGovernance, address _defaultArbitrator, address _HAT, uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested, ITokenLockFactory _tokenLockFactory ) { _transferOwnership(_hatGovernance); hatVaultImplementation = _hatVaultImplementation; HAT = IERC20(_HAT); validateHATSplit(_bountyGovernanceHAT, _bountyHackerHATVested); tokenLockFactory = _tokenLockFactory; generalParameters = IHATVaultsRegistry.GeneralParameters({ hatVestingDuration: 90 days, hatVestingPeriods: 90, withdrawPeriod: 11 hours, safetyPeriod: 1 hours, setMaxBountyDelay: 2 days, withdrawRequestEnablePeriod: 7 days, withdrawRequestPendingPeriod: 7 days, claimFee: 0 }); defaultBountyGovernanceHAT = _bountyGovernanceHAT; defaultBountyHackerHATVested = _bountyHackerHATVested; defaultArbitrator = _defaultArbitrator; defaultChallengePeriod = 3 days; defaultChallengeTimeOutPeriod = 5 weeks; defaultArbitratorCanChangeBounty = true; emit RegistryCreated( _hatVaultImplementation, _HAT, address(_tokenLockFactory), generalParameters, _bountyGovernanceHAT, _bountyHackerHATVested, _hatGovernance, _defaultArbitrator, defaultChallengePeriod, defaultChallengeTimeOutPeriod, defaultArbitratorCanChangeBounty ); } /** @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; emit SetEmergencyPaused(_isEmergencyPaused); } /** @notice See {IHATVaultsRegistry-logClaim}. */ function logClaim(string calldata _descriptionHash) external payable { uint256 _claimFee = generalParameters.claimFee; if (_claimFee > 0) { if (msg.value < _claimFee) revert NotEnoughFeePaid(); // solhint-disable-next-line avoid-low-level-calls (bool success,) = payable(owner()).call{value: msg.value}(""); if (!success) revert ClaimFeeTransferFailed(); } 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-setDefaultArbitrator}. */ function setDefaultArbitrator(address _defaultArbitrator) external onlyOwner { defaultArbitrator = _defaultArbitrator; emit SetDefaultArbitrator(_defaultArbitrator); } /** @notice See {IHATVaultsRegistry-setDefaultChallengePeriod}. */ function setDefaultChallengePeriod(uint32 _defaultChallengePeriod) external onlyOwner { validateChallengePeriod(_defaultChallengePeriod); defaultChallengePeriod = _defaultChallengePeriod; emit SetDefaultChallengePeriod(_defaultChallengePeriod); } /** @notice See {IHATVaultsRegistry-setDefaultChallengeTimeOutPeriod}. */ function setDefaultChallengeTimeOutPeriod(uint32 _defaultChallengeTimeOutPeriod) external onlyOwner { validateChallengeTimeOutPeriod(_defaultChallengeTimeOutPeriod); defaultChallengeTimeOutPeriod = _defaultChallengeTimeOutPeriod; emit SetDefaultChallengeTimeOutPeriod(_defaultChallengeTimeOutPeriod); } /** @notice See {IHATVaultsRegistry-setDefaultArbitratorCanChangeBounty}. */ function setDefaultArbitratorCanChangeBounty(bool _defaultArbitratorCanChangeBounty) external onlyOwner { defaultArbitratorCanChangeBounty = _defaultArbitratorCanChangeBounty; emit SetDefaultArbitratorCanChangeBounty(_defaultArbitratorCanChangeBounty); } /** @notice See {IHATVaultsRegistry-setFeeSetter}. */ function setFeeSetter(address _feeSetter) external onlyOwner { feeSetter = _feeSetter; emit SetFeeSetter(_feeSetter); } /** @notice See {IHATVaultsRegistry-setWithdrawRequestParams}. */ function setWithdrawRequestParams(uint32 _withdrawRequestPendingPeriod, uint32 _withdrawRequestEnablePeriod) external onlyOwner { if (_withdrawRequestPendingPeriod > 90 days) revert WithdrawRequestPendingPeriodTooLong(); if (_withdrawRequestEnablePeriod < 6 hours) revert WithdrawRequestEnabledPeriodTooShort(); if (_withdrawRequestEnablePeriod > 100 days) revert WithdrawRequestEnabledPeriodTooLong(); generalParameters.withdrawRequestPendingPeriod = _withdrawRequestPendingPeriod; generalParameters.withdrawRequestEnablePeriod = _withdrawRequestEnablePeriod; emit SetWithdrawRequestParams(_withdrawRequestPendingPeriod, _withdrawRequestEnablePeriod); } /** @notice See {IHATVaultsRegistry-setClaimFee}. */ function setClaimFee(uint256 _fee) external onlyOwner { generalParameters.claimFee = _fee; emit SetClaimFee(_fee); } /** @notice See {IHATVaultsRegistry-setWithdrawSafetyPeriod}. */ function setWithdrawSafetyPeriod(uint32 _withdrawPeriod, uint32 _safetyPeriod) external onlyOwner { if (_withdrawPeriod < 1 hours) revert WithdrawPeriodTooShort(); if (_safetyPeriod > 6 hours) revert SafetyPeriodTooLong(); generalParameters.withdrawPeriod = _withdrawPeriod; generalParameters.safetyPeriod = _safetyPeriod; emit SetWithdrawSafetyPeriod(_withdrawPeriod, _safetyPeriod); } /** @notice See {IHATVaultsRegistry-setHatVestingParams}. */ function setHatVestingParams(uint32 _duration, uint32 _periods) external onlyOwner { if (_duration >= 180 days) revert HatVestingDurationTooLong(); if (_periods == 0) revert HatVestingPeriodsCannotBeZero(); if (_duration < _periods) revert HatVestingDurationSmallerThanPeriods(); generalParameters.hatVestingDuration = _duration; generalParameters.hatVestingPeriods = _periods; emit SetHatVestingParams(_duration, _periods); } /** @notice See {IHATVaultsRegistry-setMaxBountyDelay}. */ function setMaxBountyDelay(uint32 _delay) external onlyOwner { if (_delay < 2 days) revert DelayTooShort(); generalParameters.setMaxBountyDelay = _delay; emit SetMaxBountyDelay(_delay); } /** @notice See {IHATVaultsRegistry-createVault}. */ function createVault(IHATVault.VaultInitParams calldata _params) external returns(address vault) { vault = Clones.clone(hatVaultImplementation); HATVault(vault).initialize(_params); hatVaults.push(vault); emit VaultCreated(vault, _params); } /** @notice See {IHATVaultsRegistry-setVaultVisibility}. */ function setVaultVisibility(address _vault, bool _visible) external onlyOwner { isVaultVisible[_vault] = _visible; 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 ITokenLock.Revocability.Disabled, 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) { return generalParameters.withdrawPeriod; } /** @notice See {IHATVaultsRegistry-getSafetyPeriod}. */ function getSafetyPeriod() external view returns (uint256) { return generalParameters.safetyPeriod; } /** @notice See {IHATVaultsRegistry-getWithdrawRequestEnablePeriod}. */ function getWithdrawRequestEnablePeriod() external view returns (uint256) { return generalParameters.withdrawRequestEnablePeriod; } /** @notice See {IHATVaultsRegistry-getWithdrawRequestPendingPeriod}. */ function getWithdrawRequestPendingPeriod() external view returns (uint256) { return generalParameters.withdrawRequestPendingPeriod; } /** @notice See {IHATVaultsRegistry-getSetMaxBountyDelay}. */ function getSetMaxBountyDelay() external view returns (uint256) { return generalParameters.setMaxBountyDelay; } /** @notice See {IHATVaultsRegistry-getNumberOfVaults}. */ function getNumberOfVaults() external view returns(uint256) { return hatVaults.length; } /** @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(); if (_challengePeriod > 5 days) revert ChallengePeriodTooLong(); } /** @notice See {IHATVaultsRegistry-validateChallengeTimeOutPeriod}. */ function validateChallengeTimeOutPeriod(uint32 _challengeTimeOutPeriod) public pure { if (_challengeTimeOutPeriod < 2 days) revert ChallengeTimeOutPeriodTooShort(); if (_challengeTimeOutPeriod > 85 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); } }
// SPDX-License-Identifier: MIT // Disclaimer https://github.com/hats-finance/hats-contracts/blob/main/DISCLAIMER.md pragma solidity 0.8.16; import "./IRewardController.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC4626Upgradeable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** @title Interface for Hats.finance Vaults * @author Hats.finance * @notice A HATVault holds the funds for a specific project's bug bounties. * Anyone can permissionlessly deposit into the HATVault using * the vault’s native token. When a bug is submitted and approved, the bounty * is paid out using the funds in the vault. Bounties are paid out as a * percentage of the vault. The percentage is set according to the severity of * the bug. Vaults have regular safety periods (typically for an hour twice a * day) which are time for the committee to make decisions. * * In addition to the roles defined in the HATVaultsRegistry, every HATVault * has the roles: * Committee - The only address which can submit a claim for a bounty payout * and set the maximum bounty. * User - Anyone can deposit the vault's native token into the vault and * recieve shares for it. Shares represent the user's relative part in the * vault, and when a bounty is paid out, users lose part of their deposits * (based on percentage paid), but keep their share of the vault. * Users also receive rewards for their deposits, which can be claimed at any * time. * To withdraw previously deposited tokens, a user must first send a withdraw * request, and the withdrawal will be made available after a pending period. * Withdrawals are not permitted during safety periods or while there is an * active claim for a bounty payout. * * Bounties are payed out distributed between a few channels, and that * distribution is set upon creation (the hacker gets part in direct transfer, * part in vested reward and part in vested HAT token, part gets rewarded to * the committee, part gets swapped to HAT token and burned and/or sent to Hats * governance). * * NOTE: Vaults should not use tokens which do not guarantee that the amount * specified is the amount transferred * * This project is open-source and can be found at: * https://github.com/hats-finance/hats-contracts */ interface IHATVault is IERC4626Upgradeable { enum ArbitratorCanChangeBounty{ NO, YES, DEFAULT } // How to divide the bounty - after deducting the part that is swapped to // HAT tokens (and then sent to governance and vested to the hacker) // values are in percentages and should add up to 100% (defined as 10000) struct BountySplit { // the percentage of reward sent to the hacker via vesting contract uint16 hackerVested; // the percentage of tokens that are sent directly to the hacker uint16 hacker; // the percentage sent to the committee uint16 committee; } // How to divide a bounty for a claim that has been approved // used to keep track of payouts, amounts are in vault's native token struct ClaimBounty { uint256 hacker; uint256 hackerVested; uint256 committee; uint256 hackerHatVested; uint256 governanceHat; } /** * @notice Initialization parameters for the vault * @param name The vault's name (concatenated as "Hats Vault " + name) * @param symbol The vault's symbol (concatenated as "HAT" + symbol) * @param rewardController The reward controller for the vault * @param vestingDuration Duration of the vesting period of the vault's * token vested part of the bounty * @param vestingPeriods The number of vesting periods of the vault's token * vested part of the bounty * @param maxBounty The maximum percentage of the vault that can be paid * out as a bounty * @param bountySplit The way to split the bounty between the hacker, * hacker vested, and committee. * Each entry is a number between 0 and `HUNDRED_PERCENT`. * Total splits should be equal to `HUNDRED_PERCENT`. * @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 * @param isPaused Whether to initialize the vault with deposits disabled * @dev Needed to avoid a "stack too deep" error */ struct VaultInitParams { string name; string symbol; IRewardController[] rewardControllers; uint32 vestingDuration; uint32 vestingPeriods; uint16 maxBounty; IHATVault.BountySplit bountySplit; IERC20 asset; address owner; address committee; bool isPaused; string descriptionHash; } // Only committee error OnlyCommittee(); // Active claim exists error ActiveClaimExists(); // Safety period error SafetyPeriod(); // Not safety period error NotSafetyPeriod(); // Bounty percentage is higher than the max bounty error BountyPercentageHigherThanMaxBounty(); // Only callable by arbitrator or after challenge timeout period error OnlyCallableByArbitratorOrAfterChallengeTimeOutPeriod(); // No active claim exists error NoActiveClaimExists(); // Claim Id specified is not the active claim Id error ClaimIdIsNotActive(); // Not enough fee paid error NotEnoughFeePaid(); // No pending max bounty error NoPendingMaxBounty(); // Delay period for setting max bounty had not passed error DelayPeriodForSettingMaxBountyHadNotPassed(); // Committee already checked in error CommitteeAlreadyCheckedIn(); // Total bounty split % should be `HUNDRED_PERCENT` error TotalSplitPercentageShouldBeHundredPercent(); // Vesting duration is too long error VestingDurationTooLong(); // Vesting periods cannot be zero error VestingPeriodsCannotBeZero(); // Vesting duration smaller than periods error VestingDurationSmallerThanPeriods(); // Max bounty cannot be more than `MAX_BOUNTY_LIMIT` error MaxBountyCannotBeMoreThanMaxBountyLimit(); // Committee bounty split cannot be more than `MAX_COMMITTEE_BOUNTY` error CommitteeBountyCannotBeMoreThanMax(); // Only registry owner error OnlyRegistryOwner(); // Only fee setter error OnlyFeeSetter(); // Fee must be less than or equal to 2% error WithdrawalFeeTooBig(); // Set shares arrays must have same length error SetSharesArraysMustHaveSameLength(); // Committee not checked in yet error CommitteeNotCheckedInYet(); // Not enough user balance error NotEnoughUserBalance(); // Only arbitrator or registry owner error OnlyArbitratorOrRegistryOwner(); // Unchalleged claim can only be approved if challenge period is over error UnchallengedClaimCanOnlyBeApprovedAfterChallengePeriod(); // Challenged claim can only be approved by arbitrator before the challenge timeout period error ChallengedClaimCanOnlyBeApprovedByArbitratorUntilChallengeTimeoutPeriod(); // Claim has expired error ClaimExpired(); // Challenge period is over error ChallengePeriodEnded(); // Claim can be challenged only once error ClaimAlreadyChallenged(); // Only callable if challenged error OnlyCallableIfChallenged(); // Cannot deposit to another user with withdraw request error CannotTransferToAnotherUserWithActiveWithdrawRequest(); // Withdraw amount must be greater than zero error WithdrawMustBeGreaterThanZero(); // Redeem amount cannot be more than maximum for user error RedeemMoreThanMax(); // System is in an emergency pause error SystemInEmergencyPause(); // Cannot set a reward controller that was already used in the past error CannotSetToPerviousRewardController(); // Cannot mint burn or transfer 0 amount of shares error AmountCannotBeZero(); // Cannot transfer shares to self error CannotTransferToSelf(); // First deposit must return at least MINIMAL_AMOUNT_OF_SHARES error AmountOfSharesMustBeMoreThanMinimalAmount(); // Deposit passed max slippage error DepositSlippageProtection(); // Mint passed max slippage error MintSlippageProtection(); // Withdraw passed max slippage error WithdrawSlippageProtection(); // Redeem passed max slippage error RedeemSlippageProtection(); // Cannot add the same reward controller more than once error DuplicatedRewardController(); event SubmitClaim( bytes32 indexed _claimId, address indexed _committee, address indexed _beneficiary, uint256 _bountyPercentage, string _descriptionHash ); event ChallengeClaim(bytes32 indexed _claimId); event ApproveClaim( bytes32 indexed _claimId, address indexed _committee, address indexed _beneficiary, uint256 _bountyPercentage, address _tokenLock, ClaimBounty _claimBounty ); event DismissClaim(bytes32 indexed _claimId); event SetCommittee(address indexed _committee); event SetVestingParams( uint256 _duration, uint256 _periods ); event SetBountySplit(BountySplit _bountySplit); event SetWithdrawalFee(uint256 _newFee); event CommitteeCheckedIn(); event SetPendingMaxBounty(uint256 _maxBounty); event SetMaxBounty(uint256 _maxBounty); event AddRewardController(IRewardController indexed _newRewardController); event SetDepositPause(bool _depositPause); event SetVaultDescription(string _descriptionHash); event SetHATBountySplit(uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVested); event SetArbitrator(address indexed _arbitrator); event SetChallengePeriod(uint256 _challengePeriod); event SetChallengeTimeOutPeriod(uint256 _challengeTimeOutPeriod); event SetArbitratorCanChangeBounty(ArbitratorCanChangeBounty _arbitratorCanChangeBounty); event WithdrawRequest( address indexed _beneficiary, uint256 _withdrawEnableTime ); /** * @notice Initialize a vault instance * @param _params The vault initialization parameters * @dev See {IHATVault-VaultInitParams} for more details * @dev Called when the vault is created in {IHATVaultsRegistry-createVault} */ function initialize(VaultInitParams calldata _params) external; /* -------------------------------------------------------------------------------- */ /* ---------------------------------- Claim --------------------------------------- */ /** * @notice Called by the committee to submit a claim for a bounty payout. * This function should be called only on a safety period, when withdrawals * are disabled, and while there's no other active claim. Cannot be called * when the registry is in an emergency pause. * Upon a call to this function by the committee the vault's withdrawals * will be disabled until the claim is approved or dismissed. Also from the * time of this call the arbitrator will have a period of * {HATVaultsRegistry.challengePeriod} to challenge the claim. * @param _beneficiary The submitted claim's beneficiary * @param _bountyPercentage The submitted claim's bug requested reward percentage */ function submitClaim( address _beneficiary, uint16 _bountyPercentage, string calldata _descriptionHash ) external returns (bytes32 claimId); /** * @notice Called by the arbitrator or governance to challenge a claim for a bounty * payout that had been previously submitted by the committee. * Can only be called during the challenge period after submission of the * claim. * @param _claimId The claim ID */ function challengeClaim(bytes32 _claimId) external; /** * @notice Approve a claim for a bounty submitted by a committee, and * pay out bounty to hacker and committee. Also transfer to the * HATVaultsRegistry the part of the bounty that will be swapped to HAT * tokens. * If the claim had been previously challenged, this is only callable by * the arbitrator. Otherwise, callable by anyone after challengePeriod had * passed. * @param _claimId The claim ID * @param _bountyPercentage The percentage of the vault's balance that will * be sent as a bounty. This value will be ignored if the caller is not the * arbitrator. */ function approveClaim(bytes32 _claimId, uint16 _bountyPercentage) external; /** * @notice Dismiss the active claim for bounty payout submitted by the * committee. * Called either by the arbitrator, or by anyone if the claim has timed out. * @param _claimId The claim ID */ function dismissClaim(bytes32 _claimId) external; /* -------------------------------------------------------------------------------- */ /* ---------------------------------- Params -------------------------------------- */ /** * @notice Set new committee address. Can be called by existing committee, * or by the the vault's owner in the case that the committee hadn't checked in * yet. * @param _committee The address of the new committee */ function setCommittee(address _committee) external; /** * @notice Called by the vault's owner to set the vesting params for the * part of the bounty that the hacker gets vested in the vault's native * token * @param _duration Duration of the vesting period. Must be smaller than * 120 days and bigger than `_periods` * @param _periods Number of vesting periods. Cannot be 0. */ function setVestingParams(uint32 _duration, uint32 _periods) external; /** * @notice Called by the vault's owner to set the vault token bounty split * upon an approval. * Can only be called if is no active claim and not during safety periods. * @param _bountySplit The bounty split */ function setBountySplit(BountySplit calldata _bountySplit) external; /** * @notice Called by the registry's fee setter to set the fee for * withdrawals from the vault. * @param _fee The new fee. Must be smaller than or equal to `MAX_WITHDRAWAL_FEE` */ function setWithdrawalFee(uint256 _fee) external; /** * @notice Called by the vault's committee to claim it's role. * Deposits are enabled only after committee check in. */ function committeeCheckIn() external; /** * @notice Called by the vault's owner to set a pending request for the * maximum percentage of the vault that can be paid out as a bounty. * Cannot be called if there is an active claim that has been submitted. * Max bounty should be less than or equal to 90% (defined as 9000). * The pending value can be set by the owner after the time delay (of * {HATVaultsRegistry.generalParameters.setMaxBountyDelay}) had passed. * @param _maxBounty The maximum bounty percentage that can be paid out */ function setPendingMaxBounty(uint16 _maxBounty) external; /** * @notice Called by the vault's owner to set the vault's max bounty to * the already pending max bounty. * Cannot be called if there are active claims that have been submitted. * Can only be called if there is a max bounty pending approval, and the * time delay since setting the pending max bounty had passed. */ function setMaxBounty() external; /** * @notice Called by the vault's owner to disable all deposits to the vault * @param _depositPause Are deposits paused */ function setDepositPause(bool _depositPause) external; /** * @notice Called by the registry's owner to change the description of the * vault in the Hats.finance UI * @param _descriptionHash the hash of the vault's description */ function setVaultDescription(string calldata _descriptionHash) external; /** * @notice Called by the registry's owner to add a reward controller to the vault * @param _newRewardController The new reward controller to add */ function addRewardController(IRewardController _newRewardController) external; /** * @notice 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. * @param _bountyGovernanceHAT The HAT bounty for governance * @param _bountyHackerHATVested The HAT bounty vested for the hacker */ function setHATBountySplit( uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested ) external; /** * @notice Called by the registry's owner to set the vault arbitrator * If the value passed is the special "null" value the vault will use the * registry's default value. * @param _arbitrator The address of vault's arbitrator */ function setArbitrator(address _arbitrator) external; /** * @notice Called by the registry's owner to set the period of time after * a claim for a bounty payout has been submitted that it can be challenged * by the arbitrator. * If the value passed is the special "null" value the vault will use the * registry's default value. * @param _challengePeriod The vault's challenge period */ function setChallengePeriod(uint32 _challengePeriod) external; /** * @notice Called by the registry's owner to set the period of time after * which a claim for a bounty payout can be dismissed by anyone. * If the value passed is the special "null" value the vault will use the * registry's default value. * @param _challengeTimeOutPeriod The vault's challenge timeout period */ function setChallengeTimeOutPeriod(uint32 _challengeTimeOutPeriod) external; /** * @notice Called by the registry's owner to set whether the arbitrator * can change a claim bounty percentage * If the value passed is the special "null" value the vault will use the * registry's default value. * @param _arbitratorCanChangeBounty Whether the arbitrator can change a claim bounty percentage */ function setArbitratorCanChangeBounty(ArbitratorCanChangeBounty _arbitratorCanChangeBounty) external; /* -------------------------------------------------------------------------------- */ /* ---------------------------------- Vault --------------------------------------- */ /** * @notice Submit a request to withdraw funds from the vault. * The request will only be approved if there is no previous active * withdraw request. * The request will be pending for a period of * {HATVaultsRegistry.generalParameters.withdrawRequestPendingPeriod}, * after which a withdraw will be possible for a duration of * {HATVaultsRegistry.generalParameters.withdrawRequestEnablePeriod} */ function withdrawRequest() external; /** * @notice Withdraw previously deposited funds from the vault and claim * the HAT reward that the user has earned. * Can only be performed if a withdraw request has been previously * submitted, and the pending period had passed, and while the withdraw * enabled timeout had not passed. Withdrawals are not permitted during * safety periods or while there is an active claim for a bounty payout. * @param assets Amount of tokens to withdraw * @param receiver Address of receiver of the funds * @param owner Address of owner of the funds * @dev See {IERC4626-withdraw}. */ function withdrawAndClaim(uint256 assets, address receiver, address owner) external returns (uint256 shares); /** * @notice Redeem shares in the vault for the respective amount * of underlying assets and claim the HAT reward that the user has earned. * Can only be performed if a withdraw request has been previously * submitted, and the pending period had passed, and while the withdraw * enabled timeout had not passed. Withdrawals are not permitted during * safety periods or while there is an active claim for a bounty payout. * @param shares Amount of shares to redeem * @param receiver Address of receiver of the funds * @param owner Address of owner of the funds * @dev See {IERC4626-redeem}. */ function redeemAndClaim(uint256 shares, address receiver, address owner) external returns (uint256 assets); /** * @notice Redeem all of the user's shares in the vault for the respective amount * of underlying assets without calling the reward controller, meaning user renounces * their uncommited part of the reward. * Can only be performed if a withdraw request has been previously * submitted, and the pending period had passed, and while the withdraw * enabled timeout had not passed. Withdrawals are not permitted during * safety periods or while there is an active claim for a bounty payout. * @param receiver Address of receiver of the funds */ function emergencyWithdraw(address receiver) external returns (uint256 assets); /** * @notice Withdraw previously deposited funds from the vault, without * transferring the accumulated rewards. * Can only be performed if a withdraw request has been previously * submitted, and the pending period had passed, and while the withdraw * enabled timeout had not passed. Withdrawals are not permitted during * safety periods or while there is an active claim for a bounty payout. * @param assets Amount of tokens to withdraw * @param receiver Address of receiver of the funds * @param owner Address of owner of the funds * @dev See {IERC4626-withdraw}. */ function withdraw(uint256 assets, address receiver, address owner) external returns (uint256); /** * @notice Redeem shares in the vault for the respective amount * of underlying assets, without transferring the accumulated reward. * Can only be performed if a withdraw request has been previously * submitted, and the pending period had passed, and while the withdraw * enabled timeout had not passed. Withdrawals are not permitted during * safety periods or while there is an active claim for a bounty payout. * @param shares Amount of shares to redeem * @param receiver Address of receiver of the funds * @param owner Address of owner of the funds * @dev See {IERC4626-redeem}. */ function redeem(uint256 shares, address receiver, address owner) external returns (uint256); /** * @dev Deposit funds to the vault. Can only be called if the committee had * checked in and deposits are not paused, and the registry is not in an emergency pause. * @param receiver Reciever of the shares from the deposit * @param assets Amount of vault's native token to deposit * @dev See {IERC4626-deposit}. */ function deposit(uint256 assets, address receiver) external returns (uint256); /** * @dev Deposit funds to the vault. Can only be called if the committee had * checked in and deposits are not paused, and the registry is not in an emergency pause. * Allows to specify minimum shares to be minted for slippage protection. * @param receiver Reciever of the shares from the deposit * @param assets Amount of vault's native token to deposit * @param minShares Minimum amount of shares to minted for the assets */ function deposit(uint256 assets, address receiver, uint256 minShares) external returns (uint256); /** * @dev Deposit funds to the vault based on the amount of shares to mint specified. * Can only be called if the committee had checked in and deposits are not paused, * and the registry is not in an emergency pause. * Allows to specify maximum assets to be deposited for slippage protection. * @param receiver Reciever of the shares from the deposit * @param shares Amount of vault's shares to mint * @param maxAssets Maximum amount of assets to deposit for the shares */ function mint(uint256 shares, address receiver, uint256 maxAssets) external returns (uint256); /** * @notice Withdraw previously deposited funds from the vault, without * transferring the accumulated HAT reward. * Can only be performed if a withdraw request has been previously * submitted, and the pending period had passed, and while the withdraw * enabled timeout had not passed. Withdrawals are not permitted during * safety periods or while there is an active claim for a bounty payout. * Allows to specify maximum shares to be burnt for slippage protection. * @param assets Amount of tokens to withdraw * @param receiver Address of receiver of the funds * @param owner Address of owner of the funds * @param maxShares Maximum amount of shares to burn for the assets */ function withdraw(uint256 assets, address receiver, address owner, uint256 maxShares) external returns (uint256); /** * @notice Redeem shares in the vault for the respective amount * of underlying assets, without transferring the accumulated reward. * Can only be performed if a withdraw request has been previously * submitted, and the pending period had passed, and while the withdraw * enabled timeout had not passed. Withdrawals are not permitted during * safety periods or while there is an active claim for a bounty payout. * Allows to specify minimum assets to be received for slippage protection. * @param shares Amount of shares to redeem * @param receiver Address of receiver of the funds * @param owner Address of owner of the funds * @param minAssets Minimum amount of assets to receive for the shares */ function redeem(uint256 shares, address receiver, address owner, uint256 minAssets) external returns (uint256); /** * @notice Withdraw previously deposited funds from the vault and claim * the HAT reward that the user has earned. * Can only be performed if a withdraw request has been previously * submitted, and the pending period had passed, and while the withdraw * enabled timeout had not passed. Withdrawals are not permitted during * safety periods or while there is an active claim for a bounty payout. * Allows to specify maximum shares to be burnt for slippage protection. * @param assets Amount of tokens to withdraw * @param receiver Address of receiver of the funds * @param owner Address of owner of the funds * @param maxShares Maximum amount of shares to burn for the assets * @dev See {IERC4626-withdraw}. */ function withdrawAndClaim(uint256 assets, address receiver, address owner, uint256 maxShares) external returns (uint256 shares); /** * @notice Redeem shares in the vault for the respective amount * of underlying assets and claim the HAT reward that the user has earned. * Can only be performed if a withdraw request has been previously * submitted, and the pending period had passed, and while the withdraw * enabled timeout had not passed. Withdrawals are not permitted during * safety periods or while there is an active claim for a bounty payout. * Allows to specify minimum assets to be received for slippage protection. * @param shares Amount of shares to redeem * @param receiver Address of receiver of the funds * @param owner Address of owner of the funds * @param minAssets Minimum amount of assets to receive for the shares * @dev See {IERC4626-redeem}. */ function redeemAndClaim(uint256 shares, address receiver, address owner, uint256 minAssets) external returns (uint256 assets); /* -------------------------------------------------------------------------------- */ /* --------------------------------- Getters -------------------------------------- */ /** * @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 * 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 */ function getBountyHackerHATVested() external view returns(uint16); /** * @notice Returns the address of the vault's arbitrator * If no specific value for this vault has been set, the registry's default * value will be returned. * @return The address of the vault's arbitrator */ function getArbitrator() external view returns(address); /** * @notice 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. * @return The vault's challenge period */ function getChallengePeriod() external view returns(uint32); /** * @notice 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. * @return The vault's challenge timeout period */ function getChallengeTimeOutPeriod() external view returns(uint32); /** * @notice Returns the amount of shares to be burned to give the user the exact * amount of assets requested plus cover for the fee. Also returns the amount assets * to be paid as fee. * @return shares The amount of shares to be burned to get the requested amount of assets * @return fee The amount of assets that will be paid as fee */ function previewWithdrawAndFee(uint256 assets) external view returns (uint256 shares, uint256 fee); /** * @notice Returns the amount of assets to be sent to the user for the exact * amount of shares to redeem. Also returns the amount assets to be paid as fee. * @return assets amount of assets to be sent in exchange for the amount of shares specified * @return fee The amount of assets that will be paid as fee */ function previewRedeemAndFee(uint256 shares) external view returns (uint256 assets, uint256 fee); }
// SPDX-License-Identifier: MIT // Disclaimer https://github.com/hats-finance/hats-contracts/blob/main/DISCLAIMER.md pragma solidity 0.8.16; import "./IHATVault.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** @title Interface for the Hats.finance Vault Registry * @author hats.finance * @notice The Hats.finance Vault Registry is used to deploy Hats.finance * vaults and manage shared parameters. * * Hats.finance is a proactive bounty protocol for white hat hackers and * security experts, where projects, community members, and stakeholders * incentivize protocol security and responsible disclosure. * Hats create scalable vaults using the project’s own token. The value of the * bounty increases with the success of the token and project. * * The owner of the registry has the permission to set time limits and bounty * parameters and change vaults' info, and to set the other registry roles - * fee setter and arbitrator. * The arbitrator can challenge submitted claims for bounty payouts made by * vaults' committees, approve them with a different bounty percentage or * dismiss them. * The fee setter can set the fee on withdrawals on all vaults. * * This project is open-source and can be found at: * https://github.com/hats-finance/hats-contracts * * @dev New hats.finance vaults should be created through a call to {createVault} * so that they are linked to the registry */ interface IHATVaultsRegistry { // a struct with parameters for all vaults struct GeneralParameters { // vesting duration for the part of the bounty given to the hacker in HAT tokens uint32 hatVestingDuration; // vesting periods for the part of the bounty given to the hacker in HAT tokens uint32 hatVestingPeriods; // withdraw enable period. safetyPeriod starts when finished. uint32 withdrawPeriod; // withdraw disable period - time for the committee to gather and decide on actions, // withdrawals are not possible in this time. withdrawPeriod starts when finished. uint32 safetyPeriod; // period of time after withdrawRequestPendingPeriod where it is possible to withdraw // (after which withdrawals are not possible) uint32 withdrawRequestEnablePeriod; // period of time that has to pass after withdraw request until withdraw is possible uint32 withdrawRequestPendingPeriod; // period of time that has to pass after setting a pending max // bounty before it can be set as the new max bounty uint32 setMaxBountyDelay; // fee in ETH to be transferred with every logging of a claim uint256 claimFee; } /** * @notice Raised on {setWithdrawSafetyPeriod} if the withdraw period to * be set is shorter than 1 hour */ error WithdrawPeriodTooShort(); /** * @notice Raised on {setWithdrawSafetyPeriod} if the safety period to * be set is longer than 6 hours */ error SafetyPeriodTooLong(); /** * @notice Raised on {setWithdrawRequestParams} if the withdraw request * pending period to be set is shorter than 3 months */ error WithdrawRequestPendingPeriodTooLong(); /** * @notice Raised on {setWithdrawRequestParams} if the withdraw request * enabled period to be set is shorter than 6 hours */ error WithdrawRequestEnabledPeriodTooShort(); /** * @notice Raised on {setWithdrawRequestParams} if the withdraw request * enabled period to be set is longer than 100 days */ error WithdrawRequestEnabledPeriodTooLong(); /** * @notice Raised on {setHatVestingParams} if the vesting duration to be * set is longer than 180 days */ error HatVestingDurationTooLong(); /** * @notice Raised on {setHatVestingParams} if the vesting periods to be * set is 0 */ error HatVestingPeriodsCannotBeZero(); /** * @notice Raised on {setHatVestingParams} if the vesting duration is * smaller than the vesting periods */ error HatVestingDurationSmallerThanPeriods(); /** * @notice Raised on {setMaxBountyDelay} if the max bounty to be set is * shorter than 2 days */ error DelayTooShort(); /** * @notice Raised on {swapAndSend} if the amount to swap is zero */ 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(); /** * @notice Raised on {setDefaultChallengePeriod} if the challenge period * to be set is shorter than 1 day */ error ChallengePeriodTooShort(); /** * @notice Raised on {setDefaultChallengePeriod} if the challenge period * to be set is longer than 5 days */ error ChallengePeriodTooLong(); /** * @notice Raised on {setDefaultChallengeTimeOutPeriod} if the challenge * timeout period to be set is shorter than 1 day */ error ChallengeTimeOutPeriodTooShort(); /** * @notice Raised on {setDefaultChallengeTimeOutPeriod} if the challenge * timeout period to be set is longer than 85 days */ error ChallengeTimeOutPeriodTooLong(); /** * @notice Raised on {LogClaim} if the transaction was not sent with the * amount of ETH specified as {generalParameters.claimFee} */ error NotEnoughFeePaid(); /** * @notice Raised on {LogClaim} if the transfer of the claim fee failed */ error ClaimFeeTransferFailed(); /** * @notice Emitted on deployment of the registry * @param _hatVaultImplementation The HATVault 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 _hatGovernance The registry's governance * @param _defaultChallengePeriod The new default challenge period * @param _defaultChallengeTimeOutPeriod The new default challenge timeout * @param _defaultArbitratorCanChangeBounty Whether the arbitrator can change bounty percentage of claims */ event RegistryCreated( address _hatVaultImplementation, address _HAT, address _tokenLockFactory, GeneralParameters _generalParameters, uint256 _bountyGovernanceHAT, uint256 _bountyHackerHATVested, address _hatGovernance, address _defaultArbitrator, uint256 _defaultChallengePeriod, uint256 _defaultChallengeTimeOutPeriod, bool _defaultArbitratorCanChangeBounty ); /** * @notice Emitted when a claim is logged * @param _claimer The address of the claimer * @param _descriptionHash - a hash of an ipfs encrypted file which * describes the claim. */ event LogClaim(address indexed _claimer, string _descriptionHash); /** * @notice Emitted when a new fee setter is set * @param _feeSetter The address of the new fee setter */ event SetFeeSetter(address indexed _feeSetter); /** * @notice Emitted when new withdraw request time limits are set * @param _withdrawRequestPendingPeriod Time period where the withdraw * request is pending * @param _withdrawRequestEnablePeriod Time period after the peding period * has ended during which withdrawal is enabled */ event SetWithdrawRequestParams( uint256 _withdrawRequestPendingPeriod, uint256 _withdrawRequestEnablePeriod ); /** * @notice Emitted when a new fee for logging a claim for a bounty is set * @param _fee Claim fee in ETH to be transferred on any call of {logClaim} */ event SetClaimFee(uint256 _fee); /** * @notice Emitted when new durations are set for withdraw period and * safety period * @param _withdrawPeriod Amount of time during which withdrawals are * enabled, and the bounty split can be changed by the governance * @param _safetyPeriod Amount of time during which claims for bounties * can be submitted and withdrawals are disabled */ event SetWithdrawSafetyPeriod( uint256 _withdrawPeriod, uint256 _safetyPeriod ); /** * @notice Emitted when new HAT vesting parameters are set * @param _duration The duration of the vesting period * @param _periods The number of vesting periods */ event SetHatVestingParams(uint256 _duration, uint256 _periods); /** * @notice Emitted when a new timelock delay for setting the * max bounty is set * @param _delay The time period for the delay */ event SetMaxBountyDelay(uint256 _delay); /** * @notice Emitted when the UI visibility of a vault is changed * @param _vault The address of the vault to update * @param _visible Is this vault visible in the UI */ event SetVaultVisibility(address indexed _vault, bool indexed _visible); /** @dev Emitted when a new vault is created * @param _vault The address of the vault to add to the registry * @param _params The vault initialization parameters */ event VaultCreated(address indexed _vault, IHATVault.VaultInitParams _params); /** @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 */ event SetDefaultHATBountySplit(uint256 _defaultBountyGovernanceHAT, uint256 _defaultBountyHackerHATVested); /** * @notice Emitted when a new default arbitrator is set * @param _defaultArbitrator The address of the new arbitrator */ event SetDefaultArbitrator(address indexed _defaultArbitrator); /** * @notice Emitted when a new default challenge period is set * @param _defaultChallengePeriod The new default challenge period */ event SetDefaultChallengePeriod(uint256 _defaultChallengePeriod); /** * @notice Emitted when a new default challenge timeout period is set * @param _defaultChallengeTimeOutPeriod The new default challenge timeout * period */ event SetDefaultChallengeTimeOutPeriod(uint256 _defaultChallengeTimeOutPeriod); /** * @notice Emitted when the default arbitrator can change bounty is set * @param _defaultArbitratorCanChangeBounty Whether the arbitrator can change bounty of claims */ event SetDefaultArbitratorCanChangeBounty(bool _defaultArbitratorCanChangeBounty); /** @notice Emitted when the system is put into emergency pause/unpause * @param _isEmergencyPaused Is the system in an emergency pause */ 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 Called by governance to pause/unpause the system in case of an * emergency * @param _isEmergencyPaused Is the system in an emergency pause */ 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 Emit an event that includes the given _descriptionHash * This can be used by the claimer as evidence that she had access to the * information at the time of the call * if a {generalParameters.claimFee} > 0, the caller must send that amount * of ETH for the claim to succeed * @param _descriptionHash - a hash of an IPFS encrypted file which * describes the claim. */ 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 */ function validateHATSplit(uint16 _bountyGovernanceHAT, uint16 _bountyHackerHATVested) external pure; /** * @notice Called by governance to set the default arbitrator. * @param _defaultArbitrator The default arbitrator address */ function setDefaultArbitrator(address _defaultArbitrator) external; /** * @notice Called by governance to set the default challenge period * @param _defaultChallengePeriod The default challenge period */ function setDefaultChallengePeriod(uint32 _defaultChallengePeriod) external; /** * @notice Called by governance to set the default challenge timeout * @param _defaultChallengeTimeOutPeriod The Default challenge timeout */ function setDefaultChallengeTimeOutPeriod( uint32 _defaultChallengeTimeOutPeriod ) external; /** * @notice Called by governance to set Whether the arbitrator can change bounty of claims. * @param _defaultArbitratorCanChangeBounty The default for whether the arbitrator can change bounty of claims */ function setDefaultArbitratorCanChangeBounty(bool _defaultArbitratorCanChangeBounty) external; /** * @notice Check that the given challenge period is legal, meaning that it * is greater than 1 day and less than 5 days. * @param _challengePeriod The challenge period to check */ function validateChallengePeriod(uint32 _challengePeriod) external pure; /** * @notice Check that the given challenge timeout period is legal, meaning * that it is greater than 2 days and less than 85 days. * @param _challengeTimeOutPeriod The challenge timeout period to check */ function validateChallengeTimeOutPeriod(uint32 _challengeTimeOutPeriod) external pure; /** * @notice Called by governance to set the fee setter role * @param _feeSetter Address of new fee setter */ function setFeeSetter(address _feeSetter) external; /** * @notice Called by governance to set time limits for withdraw requests * @param _withdrawRequestPendingPeriod Time period where the withdraw * request is pending * @param _withdrawRequestEnablePeriod Time period after the peding period * has ended during which withdrawal is enabled */ function setWithdrawRequestParams( uint32 _withdrawRequestPendingPeriod, uint32 _withdrawRequestEnablePeriod ) external; /** * @notice Called by governance to set the fee for logging a claim for a * bounty in any vault. * @param _fee Claim fee in ETH to be transferred on any call of * {logClaim} */ function setClaimFee(uint256 _fee) external; /** * @notice Called by governance to set the withdraw period and safety * period, which are always interchanging. * The safety period is time that the committee can submit claims for * bounty payouts, and during which withdrawals are disabled and the * bounty split cannot be changed. * @param _withdrawPeriod Amount of time during which withdrawals are * enabled, and the bounty split can be changed by the governance. Must be * at least 1 hour. * @param _safetyPeriod Amount of time during which claims for bounties * can be submitted and withdrawals are disabled. Must be at most 6 hours. */ function setWithdrawSafetyPeriod( uint32 _withdrawPeriod, uint32 _safetyPeriod ) external; /** * @notice Called by governance to set vesting params for rewarding hackers * with rewardToken, for all vaults * @param _duration Duration of the vesting period. Must be less than 180 * days. * @param _periods The number of vesting periods. Must be more than 0 and * less then the vesting duration. */ function setHatVestingParams(uint32 _duration, uint32 _periods) external; /** * @notice Called by governance to set the timelock delay for setting the * max bounty (the time between setPendingMaxBounty and setMaxBounty) * @param _delay The time period for the delay. Must be at least 2 days. */ function setMaxBountyDelay(uint32 _delay) external; /** * @notice Create a new vault * NOTE: Vaults should not use tokens which do not guarantee that the * amount specified is the amount transferred * @param _params The vault initialization parameters * @return vault The address of the new vault */ function createVault(IHATVault.VaultInitParams calldata _params) external returns(address vault); /** * @notice Called by governance to change the UI visibility of a vault * @param _vault The address of the vault to update * @param _visible Is this vault visible in the UI * This parameter can be used by the UI to include or exclude the vault */ 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. * @return Withdraw enable period for all vaults */ function getWithdrawPeriod() external view returns (uint256); /** * @notice Returns the withdraw disable period - time for the committee to * gather and decide on actions, withdrawals are not possible in this * time. The withdraw period starts when finished. * @return Safety period for all vaults */ function getSafetyPeriod() external view returns (uint256); /** * @notice Returns the withdraw request enable period for all vaults - * period of time after withdrawRequestPendingPeriod where it is possible * to withdraw, and after which withdrawals are not possible. * @return Withdraw request enable period for all vaults */ function getWithdrawRequestEnablePeriod() external view returns (uint256); /** * @notice Returns the withdraw request pending period for all vaults - * period of time that has to pass after withdraw request until withdraw * is possible * @return Withdraw request pending period for all vaults */ function getWithdrawRequestPendingPeriod() external view returns (uint256); /** * @notice Returns the set max bounty delay for all vaults - period of * time that has to pass after setting a pending max bounty before it can * be set as the new max bounty * @return Set max bounty delay for all vaults */ function getSetMaxBountyDelay() external view returns (uint256); /** * @notice Returns the number of vaults that have been previously created * @return The number of vaults in the registry */ function getNumberOfVaults() external view returns(uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; interface IRewardController { error EpochLengthZero(); // Not enough rewards to transfer to user error NotEnoughRewardsToTransferToUser(); event RewardControllerCreated( address _rewardToken, address _governance, uint256 _startBlock, uint256 _epochLength, uint256[24] _epochRewardPerBlock ); event SetEpochRewardPerBlock(uint256[24] _epochRewardPerBlock); event SetAllocPoint(address indexed _vault, uint256 _prevAllocPoint, uint256 _allocPoint); event VaultUpdated(address indexed _vault, uint256 _rewardPerShare, uint256 _lastProcessedVaultUpdate); event UserBalanceCommitted(address indexed _vault, address indexed _user, uint256 _unclaimedReward, uint256 _rewardDebt); event ClaimReward(address indexed _vault, address indexed _user, uint256 _amount); /** * @notice Initializes the reward controller * @param _rewardToken The address of the ERC20 token to be distributed as rewards * @param _governance The hats governance address, to be given ownership of the reward controller * @param _startRewardingBlock The block number from which to start rewarding * @param _epochLength The length of a rewarding epoch * @param _epochRewardPerBlock The reward per block for each of the 24 epochs */ function initialize( address _rewardToken, address _governance, uint256 _startRewardingBlock, uint256 _epochLength, uint256[24] calldata _epochRewardPerBlock ) external; /** * @notice Called by the owner to set the allocation points for a vault, meaning the * vault's relative share of the total rewards * @param _vault The address of the vault * @param _allocPoint The allocation points for the vault */ function setAllocPoint(address _vault, uint256 _allocPoint) external; /** * @notice Update the vault's reward per share, not more then once per block * @param _vault The vault's address */ function updateVault(address _vault) external; /** * @notice Called by the owner to set reward per epoch * Reward can only be set for epochs which have not yet started * @param _epochRewardPerBlock reward per block for each epoch */ function setEpochRewardPerBlock(uint256[24] calldata _epochRewardPerBlock) external; /** * @notice Called by the vault to update a user claimable reward after deposit or withdraw. * This call should never revert. * @param _user The user address to updare rewards for * @param _sharesChange The user of shared the user deposited or withdrew * @param _isDeposit Whether user deposited or withdrew */ function commitUserBalance(address _user, uint256 _sharesChange, bool _isDeposit) external; /** * @notice Transfer to the specified user their pending share of rewards. * @param _vault The vault address * @param _user The user address to claim for */ function claimReward(address _vault, address _user) external; /** * @notice Calculate rewards for a vault by iterating over the history of totalAllocPoints updates, * and sum up all rewards periods from vault.lastRewardBlock until current block number. * @param _vault The vault address * @param _fromBlock The block from which to start calculation * @return reward The amount of rewards for the vault */ function getVaultReward(address _vault, uint256 _fromBlock) external view returns(uint256 reward); /** * @notice Calculate the amount of rewards a user can claim for having contributed to a specific vault * @param _vault The vault address * @param _user The user for which the reward is calculated */ function getPendingReward(address _vault, address _user) external view returns (uint256); /** * @notice Called by the owner to transfer any tokens held in this contract to the owner * @param _token The token to sweep * @param _amount The amount of token to sweep */ function sweepToken(IERC20Upgradeable _token, uint256 _amount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; pragma experimental ABIEncoderV2; interface ITokenLock { enum Revocability { NotSet, Enabled, Disabled } // -- Value Transfer -- function release() external; function withdrawSurplus(uint256 _amount) external; function revoke() external; // -- Balances -- function currentBalance() external view returns (uint256); // -- Time & Periods -- function currentTime() external view returns (uint256); function duration() external view returns (uint256); function sinceStartTime() external view returns (uint256); function amountPerPeriod() external view returns (uint256); function periodDuration() external view returns (uint256); function currentPeriod() external view returns (uint256); function passedPeriods() external view returns (uint256); // -- Locking & Release Schedule -- function availableAmount() external view returns (uint256); function vestedAmount() external view returns (uint256); function releasableAmount() external view returns (uint256); function totalOutstandingAmount() external view returns (uint256); function surplusAmount() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; import "./ITokenLock.sol"; interface ITokenLockFactory { // -- Factory -- function setMasterCopy(address _masterCopy) external; function createTokenLock( address _token, address _owner, address _beneficiary, uint256 _managedAmount, uint256 _startTime, uint256 _endTime, uint256 _periods, uint256 _releaseStartTime, uint256 _vestingCliffTime, ITokenLock.Revocability _revocable, bool _canDelegate ) external returns(address contractAddress); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; import "@openzeppelin/contracts/proxy/Clones.sol"; import "./ITokenLockFactory.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; /** * @title TokenLockFactory * a factory of TokenLock contracts. * * This contract receives funds to make the process of creating TokenLock contracts * easier by distributing them the initial tokens to be managed. */ contract TokenLockFactory is ITokenLockFactory, Ownable { // -- State -- address public masterCopy; // -- Events -- event MasterCopyUpdated(address indexed masterCopy); event TokenLockCreated( address indexed contractAddress, bytes32 indexed initHash, address indexed beneficiary, address token, uint256 managedAmount, uint256 startTime, uint256 endTime, uint256 periods, uint256 releaseStartTime, uint256 vestingCliffTime, ITokenLock.Revocability revocable, bool canDelegate ); /** * Constructor. * @param _masterCopy Address of the master copy to use to clone proxies * @param _governance Owner of the factory */ constructor(address _masterCopy, address _governance) { setMasterCopy(_masterCopy); _transferOwnership(_governance); } // -- Factory -- /** * @notice Creates and fund a new token lock wallet using a minimum proxy * @param _token token to time lock * @param _owner Address of the contract owner * @param _beneficiary Address of the beneficiary of locked tokens * @param _managedAmount Amount of tokens to be managed by the lock contract * @param _startTime Start time of the release schedule * @param _endTime End time of the release schedule * @param _periods Number of periods between start time and end time * @param _releaseStartTime Override time for when the releases start * @param _revocable Whether the contract is revocable * @param _canDelegate Whether the contract should call delegate */ function createTokenLock( address _token, address _owner, address _beneficiary, uint256 _managedAmount, uint256 _startTime, uint256 _endTime, uint256 _periods, uint256 _releaseStartTime, uint256 _vestingCliffTime, ITokenLock.Revocability _revocable, bool _canDelegate ) external override returns(address contractAddress) { // Create contract using a minimal proxy and call initializer bytes memory initializer = abi.encodeWithSignature( "initialize(address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,uint8,bool)", _owner, _beneficiary, _token, _managedAmount, _startTime, _endTime, _periods, _releaseStartTime, _vestingCliffTime, _revocable, _canDelegate ); contractAddress = deployProxyPrivate(initializer, _beneficiary, _token, _managedAmount, _startTime, _endTime, _periods, _releaseStartTime, _vestingCliffTime, _revocable, _canDelegate); } /** * @notice Sets the masterCopy bytecode to use to create clones of TokenLock contracts * @param _masterCopy Address of contract bytecode to factory clone */ function setMasterCopy(address _masterCopy) public override onlyOwner { require(_masterCopy != address(0), "MasterCopy cannot be zero"); masterCopy = _masterCopy; emit MasterCopyUpdated(_masterCopy); } //this private function is to handle stack too deep issue function deployProxyPrivate( bytes memory _initializer, address _beneficiary, address _token, uint256 _managedAmount, uint256 _startTime, uint256 _endTime, uint256 _periods, uint256 _releaseStartTime, uint256 _vestingCliffTime, ITokenLock.Revocability _revocable, bool _canDelegate ) private returns (address contractAddress) { contractAddress = Clones.clone(masterCopy); Address.functionCall(contractAddress, _initializer); emit TokenLockCreated( contractAddress, keccak256(_initializer), _beneficiary, _token, _managedAmount, _startTime, _endTime, _periods, _releaseStartTime, _vestingCliffTime, _revocable, _canDelegate ); } }
{ "evmVersion": "london", "libraries": {}, "metadata": { "bytecodeHash": "ipfs", "useLiteralContent": true }, "optimizer": { "enabled": true, "runs": 200 }, "remappings": [], "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
[{"inputs":[],"name":"ActiveClaimExists","type":"error"},{"inputs":[],"name":"AmountCannotBeZero","type":"error"},{"inputs":[],"name":"AmountOfSharesMustBeMoreThanMinimalAmount","type":"error"},{"inputs":[],"name":"BountyPercentageHigherThanMaxBounty","type":"error"},{"inputs":[],"name":"CannotSetToPerviousRewardController","type":"error"},{"inputs":[],"name":"CannotTransferToAnotherUserWithActiveWithdrawRequest","type":"error"},{"inputs":[],"name":"CannotTransferToSelf","type":"error"},{"inputs":[],"name":"ChallengePeriodEnded","type":"error"},{"inputs":[],"name":"ChallengedClaimCanOnlyBeApprovedByArbitratorUntilChallengeTimeoutPeriod","type":"error"},{"inputs":[],"name":"ClaimAlreadyChallenged","type":"error"},{"inputs":[],"name":"ClaimExpired","type":"error"},{"inputs":[],"name":"ClaimIdIsNotActive","type":"error"},{"inputs":[],"name":"CommitteeAlreadyCheckedIn","type":"error"},{"inputs":[],"name":"CommitteeBountyCannotBeMoreThanMax","type":"error"},{"inputs":[],"name":"CommitteeNotCheckedInYet","type":"error"},{"inputs":[],"name":"DelayPeriodForSettingMaxBountyHadNotPassed","type":"error"},{"inputs":[],"name":"DepositSlippageProtection","type":"error"},{"inputs":[],"name":"DuplicatedRewardController","type":"error"},{"inputs":[],"name":"MaxBountyCannotBeMoreThanMaxBountyLimit","type":"error"},{"inputs":[],"name":"MintSlippageProtection","type":"error"},{"inputs":[],"name":"NoActiveClaimExists","type":"error"},{"inputs":[],"name":"NoPendingMaxBounty","type":"error"},{"inputs":[],"name":"NotEnoughFeePaid","type":"error"},{"inputs":[],"name":"NotEnoughUserBalance","type":"error"},{"inputs":[],"name":"NotSafetyPeriod","type":"error"},{"inputs":[],"name":"OnlyArbitratorOrRegistryOwner","type":"error"},{"inputs":[],"name":"OnlyCallableByArbitratorOrAfterChallengeTimeOutPeriod","type":"error"},{"inputs":[],"name":"OnlyCallableIfChallenged","type":"error"},{"inputs":[],"name":"OnlyCommittee","type":"error"},{"inputs":[],"name":"OnlyFeeSetter","type":"error"},{"inputs":[],"name":"OnlyRegistryOwner","type":"error"},{"inputs":[],"name":"RedeemMoreThanMax","type":"error"},{"inputs":[],"name":"RedeemSlippageProtection","type":"error"},{"inputs":[],"name":"SafetyPeriod","type":"error"},{"inputs":[],"name":"SetSharesArraysMustHaveSameLength","type":"error"},{"inputs":[],"name":"SystemInEmergencyPause","type":"error"},{"inputs":[],"name":"TotalSplitPercentageShouldBeHundredPercent","type":"error"},{"inputs":[],"name":"UnchallengedClaimCanOnlyBeApprovedAfterChallengePeriod","type":"error"},{"inputs":[],"name":"VestingDurationSmallerThanPeriods","type":"error"},{"inputs":[],"name":"VestingDurationTooLong","type":"error"},{"inputs":[],"name":"VestingPeriodsCannotBeZero","type":"error"},{"inputs":[],"name":"WithdrawMustBeGreaterThanZero","type":"error"},{"inputs":[],"name":"WithdrawSlippageProtection","type":"error"},{"inputs":[],"name":"WithdrawalFeeTooBig","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IRewardController","name":"_newRewardController","type":"address"}],"name":"AddRewardController","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_claimId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"_committee","type":"address"},{"indexed":true,"internalType":"address","name":"_beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"_bountyPercentage","type":"uint256"},{"indexed":false,"internalType":"address","name":"_tokenLock","type":"address"},{"components":[{"internalType":"uint256","name":"hacker","type":"uint256"},{"internalType":"uint256","name":"hackerVested","type":"uint256"},{"internalType":"uint256","name":"committee","type":"uint256"},{"internalType":"uint256","name":"hackerHatVested","type":"uint256"},{"internalType":"uint256","name":"governanceHat","type":"uint256"}],"indexed":false,"internalType":"struct IHATVault.ClaimBounty","name":"_claimBounty","type":"tuple"}],"name":"ApproveClaim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_claimId","type":"bytes32"}],"name":"ChallengeClaim","type":"event"},{"anonymous":false,"inputs":[],"name":"CommitteeCheckedIn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_claimId","type":"bytes32"}],"name":"DismissClaim","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_arbitrator","type":"address"}],"name":"SetArbitrator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum IHATVault.ArbitratorCanChangeBounty","name":"_arbitratorCanChangeBounty","type":"uint8"}],"name":"SetArbitratorCanChangeBounty","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint16","name":"hackerVested","type":"uint16"},{"internalType":"uint16","name":"hacker","type":"uint16"},{"internalType":"uint16","name":"committee","type":"uint16"}],"indexed":false,"internalType":"struct IHATVault.BountySplit","name":"_bountySplit","type":"tuple"}],"name":"SetBountySplit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_challengePeriod","type":"uint256"}],"name":"SetChallengePeriod","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_challengeTimeOutPeriod","type":"uint256"}],"name":"SetChallengeTimeOutPeriod","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_committee","type":"address"}],"name":"SetCommittee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"_depositPause","type":"bool"}],"name":"SetDepositPause","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyGovernanceHAT","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_bountyHackerHATVested","type":"uint256"}],"name":"SetHATBountySplit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_maxBounty","type":"uint256"}],"name":"SetMaxBounty","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_maxBounty","type":"uint256"}],"name":"SetPendingMaxBounty","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"_descriptionHash","type":"string"}],"name":"SetVaultDescription","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_duration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_periods","type":"uint256"}],"name":"SetVestingParams","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_newFee","type":"uint256"}],"name":"SetWithdrawalFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_claimId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"_committee","type":"address"},{"indexed":true,"internalType":"address","name":"_beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"_bountyPercentage","type":"uint256"},{"indexed":false,"internalType":"string","name":"_descriptionHash","type":"string"}],"name":"SubmitClaim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"_withdrawEnableTime","type":"uint256"}],"name":"WithdrawRequest","type":"event"},{"inputs":[],"name":"HUNDRED_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"HUNDRED_PERCENT_SQRD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BOUNTY_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_COMMITTEE_BOUNTY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_UINT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_WITHDRAWAL_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINIMAL_AMOUNT_OF_SHARES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NULL_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NULL_UINT16","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NULL_UINT32","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeClaim","outputs":[{"internalType":"bytes32","name":"claimId","type":"bytes32"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"uint16","name":"bountyPercentage","type":"uint16"},{"internalType":"address","name":"committee","type":"address"},{"internalType":"uint32","name":"createdAt","type":"uint32"},{"internalType":"uint32","name":"challengedAt","type":"uint32"},{"internalType":"uint256","name":"bountyGovernanceHAT","type":"uint256"},{"internalType":"uint256","name":"bountyHackerHATVested","type":"uint256"},{"internalType":"address","name":"arbitrator","type":"address"},{"internalType":"uint32","name":"challengePeriod","type":"uint32"},{"internalType":"uint32","name":"challengeTimeOutPeriod","type":"uint32"},{"internalType":"bool","name":"arbitratorCanChangeBounty","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IRewardController","name":"_rewardController","type":"address"}],"name":"addRewardController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_claimId","type":"bytes32"},{"internalType":"uint16","name":"_bountyPercentage","type":"uint16"}],"name":"approveClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bountySplit","outputs":[{"internalType":"uint16","name":"hackerVested","type":"uint16"},{"internalType":"uint16","name":"hacker","type":"uint16"},{"internalType":"uint16","name":"committee","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_claimId","type":"bytes32"}],"name":"challengeClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"committee","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"committeeCheckIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"committeeCheckedIn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"minShares","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositPause","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_claimId","type":"bytes32"}],"name":"dismissClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"emergencyWithdraw","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getArbitrator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getArbitratorCanChangeBounty","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBountyGovernanceHAT","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBountyHackerHATVested","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChallengePeriod","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getChallengeTimeOutPeriod","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"contract IRewardController[]","name":"rewardControllers","type":"address[]"},{"internalType":"uint32","name":"vestingDuration","type":"uint32"},{"internalType":"uint32","name":"vestingPeriods","type":"uint32"},{"internalType":"uint16","name":"maxBounty","type":"uint16"},{"components":[{"internalType":"uint16","name":"hackerVested","type":"uint16"},{"internalType":"uint16","name":"hacker","type":"uint16"},{"internalType":"uint16","name":"committee","type":"uint16"}],"internalType":"struct IHATVault.BountySplit","name":"bountySplit","type":"tuple"},{"internalType":"contract IERC20","name":"asset","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"committee","type":"address"},{"internalType":"bool","name":"isPaused","type":"bool"},{"internalType":"string","name":"descriptionHash","type":"string"}],"internalType":"struct IHATVault.VaultInitParams","name":"_params","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxBounty","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"maxAssets","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingMaxBounty","outputs":[{"internalType":"uint16","name":"maxBounty","type":"uint16"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeemAndFee","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdrawAndFee","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"minAssets","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"minAssets","type":"uint256"}],"name":"redeemAndClaim","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeemAndClaim","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract HATVaultsRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardControllers","outputs":[{"internalType":"contract IRewardController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_arbitrator","type":"address"}],"name":"setArbitrator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum IHATVault.ArbitratorCanChangeBounty","name":"_arbitratorCanChangeBounty","type":"uint8"}],"name":"setArbitratorCanChangeBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"hackerVested","type":"uint16"},{"internalType":"uint16","name":"hacker","type":"uint16"},{"internalType":"uint16","name":"committee","type":"uint16"}],"internalType":"struct IHATVault.BountySplit","name":"_bountySplit","type":"tuple"}],"name":"setBountySplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_challengePeriod","type":"uint32"}],"name":"setChallengePeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_challengeTimeOutPeriod","type":"uint32"}],"name":"setChallengeTimeOutPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_committee","type":"address"}],"name":"setCommittee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_depositPause","type":"bool"}],"name":"setDepositPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_bountyGovernanceHAT","type":"uint16"},{"internalType":"uint16","name":"_bountyHackerHATVested","type":"uint16"}],"name":"setHATBountySplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setMaxBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_maxBounty","type":"uint16"}],"name":"setPendingMaxBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_descriptionHash","type":"string"}],"name":"setVaultDescription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_duration","type":"uint32"},{"internalType":"uint32","name":"_periods","type":"uint32"}],"name":"setVestingParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setWithdrawalFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"uint16","name":"_bountyPercentage","type":"uint16"},{"internalType":"string","name":"_descriptionHash","type":"string"}],"name":"submitClaim","outputs":[{"internalType":"bytes32","name":"claimId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenLockFactory","outputs":[{"internalType":"contract ITokenLockFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vestingDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingPeriods","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"maxShares","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"maxShares","type":"uint256"}],"name":"withdrawAndClaim","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdrawAndClaim","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawEnableStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.