diff --git a/contracts/governance/Staking/interfaces/IStaking.sol b/contracts/governance/Staking/interfaces/IStaking.sol index 21b54ce74..99a8b27b9 100644 --- a/contracts/governance/Staking/interfaces/IStaking.sol +++ b/contracts/governance/Staking/interfaces/IStaking.sol @@ -416,7 +416,7 @@ interface IStaking { function numVestingCheckpoints(uint256 date) external view returns (uint32 checkpointsQty); ///@notice vesting registry contract PROXY address - function vestingRegistryLogic() external view returns (address); + function vestingRegistry() external view returns (address); /// @dev user => flag whether user has pauser role. function pausers(address isPauser) external view returns (bool); diff --git a/contracts/governance/Staking/modules/StakingStorageModule.sol b/contracts/governance/Staking/modules/StakingStorageModule.sol index 6dc468854..2ed316206 100644 --- a/contracts/governance/Staking/modules/StakingStorageModule.sol +++ b/contracts/governance/Staking/modules/StakingStorageModule.sol @@ -95,7 +95,7 @@ contract StakingStorageModule is IFunctionsList, StakingStorageShared { functionsList[24] = this.vestingCodeHashes.selector; functionsList[25] = this.vestingCheckpoints.selector; functionsList[26] = this.numVestingCheckpoints.selector; - functionsList[27] = this.vestingRegistryLogic.selector; + functionsList[27] = this.vestingRegistry.selector; functionsList[28] = this.pausers.selector; functionsList[29] = this.paused.selector; functionsList[30] = this.frozen.selector; diff --git a/contracts/governance/Staking/modules/StakingVestingModule.sol b/contracts/governance/Staking/modules/StakingVestingModule.sol index bd325553b..df2b80527 100644 --- a/contracts/governance/Staking/modules/StakingVestingModule.sol +++ b/contracts/governance/Staking/modules/StakingVestingModule.sol @@ -20,7 +20,7 @@ contract StakingVestingModule is IFunctionsList, StakingShared { * various other functionalities without the necessity of linking it with Vesting Registry */ function setVestingRegistry(address _vestingRegistryProxy) external onlyOwner whenNotFrozen { - vestingRegistryLogic = IVestingRegistry(_vestingRegistryProxy); + vestingRegistry = IVestingRegistry(_vestingRegistryProxy); } /** @@ -278,8 +278,8 @@ contract StakingVestingModule is IFunctionsList, StakingShared { function isVestingContract(address stakerAddress) external view returns (bool) { bool isVesting; bytes32 codeHash = _getCodeHash(stakerAddress); - if (address(vestingRegistryLogic) != address(0)) { - isVesting = vestingRegistryLogic.isVestingAddress(stakerAddress); + if (address(vestingRegistry) != address(0)) { + isVesting = vestingRegistry.isVestingAddress(stakerAddress); } if (isVesting) return true; diff --git a/contracts/governance/Staking/modules/StakingWithdrawModule.sol b/contracts/governance/Staking/modules/StakingWithdrawModule.sol index 0abb1708c..8c24deef7 100644 --- a/contracts/governance/Staking/modules/StakingWithdrawModule.sol +++ b/contracts/governance/Staking/modules/StakingWithdrawModule.sol @@ -77,7 +77,7 @@ contract StakingWithdrawModule is IFunctionsList, StakingShared, CheckpointsShar uint256 startFrom ) external onlyAuthorized whenNotFrozen { /// require the caller only for team vesting contract. - require(vestingRegistryLogic.isTeamVesting(vesting), "Only team vesting allowed"); + require(vestingRegistry.isTeamVesting(vesting), "Only team vesting allowed"); _cancelTeamVesting(vesting, receiver, startFrom); } @@ -340,7 +340,7 @@ contract StakingWithdrawModule is IFunctionsList, StakingShared, CheckpointsShar address vesting, address receiver ) public onlyAuthorized whenNotFrozen { - require(vestingRegistryLogic.isTeamVesting(vesting), "Only team vesting allowed"); + require(vestingRegistry.isTeamVesting(vesting), "Only team vesting allowed"); ITeamVesting teamVesting = ITeamVesting(vesting); uint256 teamVestingStartDate = teamVesting.startDate(); diff --git a/contracts/governance/Staking/modules/shared/StakingShared.sol b/contracts/governance/Staking/modules/shared/StakingShared.sol index a40bb4963..6532af61f 100644 --- a/contracts/governance/Staking/modules/shared/StakingShared.sol +++ b/contracts/governance/Staking/modules/shared/StakingShared.sol @@ -214,8 +214,8 @@ contract StakingShared is StakingStorageShared, SafeMath96 { assembly { codeHash := extcodehash(stakerAddress) } - if (address(vestingRegistryLogic) != address(0)) { - isVesting = vestingRegistryLogic.isVestingAddress(stakerAddress); + if (address(vestingRegistry) != address(0)) { + isVesting = vestingRegistry.isVestingAddress(stakerAddress); } if (isVesting) return true; diff --git a/contracts/governance/Staking/modules/shared/StakingStorageShared.sol b/contracts/governance/Staking/modules/shared/StakingStorageShared.sol index 7476ca1cb..0a5ae2823 100644 --- a/contracts/governance/Staking/modules/shared/StakingStorageShared.sol +++ b/contracts/governance/Staking/modules/shared/StakingStorageShared.sol @@ -139,7 +139,7 @@ contract StakingStorageShared is Ownable { mapping(uint256 => uint32) public numVestingCheckpoints; ///@notice vesting registry contract - IVestingRegistry public vestingRegistryLogic; + IVestingRegistry public vestingRegistry; /// @dev user => flag whether user has pauser role. mapping(address => bool) public pausers; diff --git a/contracts/governance/Vesting/OriginInvestorsClaim.sol b/contracts/governance/Vesting/OriginInvestorsClaim.sol index 9e4202d2e..5ed5cfe25 100644 --- a/contracts/governance/Vesting/OriginInvestorsClaim.sol +++ b/contracts/governance/Vesting/OriginInvestorsClaim.sol @@ -3,6 +3,7 @@ pragma experimental ABIEncoderV2; import "./VestingRegistry.sol"; import "../Staking/interfaces/IStaking.sol"; +import "../../locked/ILockedSOV.sol"; /** * @title Origin investors claim vested cSOV tokens. @@ -193,8 +194,9 @@ contract OriginInvestorsClaim is Ownable { * are sent to vesting contract. * */ function createVesting() internal { - uint256 cliff = vestingTerm.sub(now); - uint256 duration = cliff; + ILockedSOV lockedSOV = vestingRegistry.lockedSOV(); + uint256 cliff = lockedSOV.cliff(); + uint256 duration = lockedSOV.duration(); uint256 amount = investorsAmountsList[msg.sender]; address vestingContractAddress; diff --git a/contracts/governance/Vesting/VestingCreator.sol b/contracts/governance/Vesting/VestingCreator.sol index 9da1c6055..2272447fe 100644 --- a/contracts/governance/Vesting/VestingCreator.sol +++ b/contracts/governance/Vesting/VestingCreator.sol @@ -2,7 +2,7 @@ pragma solidity ^0.5.17; import "../../interfaces/IERC20.sol"; import "../../utils/AdminRole.sol"; -import "./VestingRegistryLogic.sol"; +import "./VestingRegistry.sol"; import "./VestingLogic.sol"; import "../../openzeppelin/SafeMath.sol"; @@ -19,7 +19,7 @@ contract VestingCreator is AdminRole { IERC20 public SOV; ///@notice the vesting registry contract - VestingRegistryLogic public vestingRegistryLogic; + VestingRegistry public vestingRegistry; ///@notice Holds Vesting Data struct VestingData { @@ -44,7 +44,7 @@ contract VestingCreator is AdminRole { require(_vestingRegistryProxy != address(0), "Vesting registry address invalid"); SOV = IERC20(_SOV); - vestingRegistryLogic = VestingRegistryLogic(_vestingRegistryProxy); + vestingRegistry = VestingRegistry(_vestingRegistryProxy); } /** @@ -245,7 +245,7 @@ contract VestingCreator is AdminRole { VestingData memory vestingData ) internal returns (address vesting) { if (vestingData.governanceControl) { - vestingRegistryLogic.createTeamVesting( + vestingRegistry.createTeamVesting( vestingData.tokenOwner, vestingData.amount, vestingData.cliff, @@ -253,7 +253,7 @@ contract VestingCreator is AdminRole { vestingData.vestingCreationType ); } else { - vestingRegistryLogic.createVestingAddr( + vestingRegistry.createVestingAddr( vestingData.tokenOwner, vestingData.amount, vestingData.cliff, @@ -282,14 +282,14 @@ contract VestingCreator is AdminRole { uint256 _vestingCreationType ) internal view returns (address vestingAddress) { if (_governanceControl) { - vestingAddress = vestingRegistryLogic.getTeamVesting( + vestingAddress = vestingRegistry.getTeamVesting( _tokenOwner, _cliff, _duration, _vestingCreationType ); } else { - vestingAddress = vestingRegistryLogic.getVestingAddr( + vestingAddress = vestingRegistry.getVestingAddr( _tokenOwner, _cliff, _duration, diff --git a/contracts/governance/Vesting/VestingRegistry.sol b/contracts/governance/Vesting/VestingRegistry.sol index 9ef9040fd..42ab559b2 100644 --- a/contracts/governance/Vesting/VestingRegistry.sol +++ b/contracts/governance/Vesting/VestingRegistry.sol @@ -1,426 +1,243 @@ pragma solidity ^0.5.17; +pragma experimental ABIEncoderV2; -import "../../openzeppelin/Ownable.sol"; import "../../interfaces/IERC20.sol"; -import "../Staking/interfaces/IStaking.sol"; import "../IFeeSharingCollector.sol"; -import "./IVestingFactory.sol"; import "./IVesting.sol"; import "./ITeamVesting.sol"; -import "../../openzeppelin/SafeMath.sol"; +import "./VestingRegistryStorage.sol"; /** - * @title Vesting Registry contract. - * - * @notice On January 25, 2020, Sovryn launched the Genesis Reservation system. - * Sovryn community members who controlled a special NFT were granted access to - * stake BTC or rBTC for cSOV tokens at a rate of 2500 satoshis per cSOV. Per - * SIP-0003, up to 2,000,000 cSOV were made available in the Genesis event, - * which will be redeemable on a 1:1 basis for cSOV, subject to approval by - * existing SOV holders. - * - * On 15 Feb 2021 Sovryn is taking another step in its journey to decentralized - * financial sovereignty with the vote on SIP 0005. This proposal will enable - * participants of the Genesis Reservation system to redeem their reserved cSOV - * tokens for SOV. They will also have the choice to redeem cSOV for rBTC if - * they decide to exit the system. - * - * This contract deals with the vesting and redemption of cSOV tokens. - * */ -contract VestingRegistry is Ownable { - using SafeMath for uint256; - - /* Storage */ - - /// @notice Constant used for computing the vesting dates. - uint256 public constant FOUR_WEEKS = 4 weeks; - - uint256 public constant CSOV_VESTING_CLIFF = FOUR_WEEKS; - uint256 public constant CSOV_VESTING_DURATION = 10 * FOUR_WEEKS; - - IVestingFactory public vestingFactory; - - /// @notice The SOV token contract. - address public SOV; - - /// @notice The cSOV token contracts. - address[] public CSOVtokens; - - uint256 public priceSats; - - /// @notice The staking contract address. - address public staking; - - /// @notice Fee sharing proxy. - address public feeSharingCollector; - - /// @notice The vesting owner (e.g. governance timelock address). - address public vestingOwner; - - /// @dev TODO: Add to the documentation: address can have only one vesting of each type. - /// @dev user => vesting type => vesting contract. - mapping(address => mapping(uint256 => address)) public vestingContracts; - - /** - * @dev Struct can be created to save storage slots, but it doesn't make - * sense. We don't have a lot of blacklisted accounts or account with - * locked amount. - * */ - - /// @dev user => flag whether user has already exchange cSOV or got a reimbursement. - mapping(address => bool) public processedList; - - /// @dev user => flag whether user shouldn't be able to exchange or reimburse. - mapping(address => bool) public blacklist; - - /// @dev user => amount of tokens should not be processed. - mapping(address => uint256) public lockedAmount; - - /// @dev user => flag whether user has admin role. - mapping(address => bool) public admins; - - enum VestingType { - TeamVesting, // MultisigVesting - Vesting // TokenHolderVesting - } - - /* Events */ - - event CSOVReImburse(address from, uint256 CSOVamount, uint256 reImburseAmount); - event CSOVTokensExchanged(address indexed caller, uint256 amount); + * @dev This is the logic / implementation contract of vesting registry + * previously was named as VestingRegistryLogic + */ +contract VestingRegistry is VestingRegistryStorage { event SOVTransferred(address indexed receiver, uint256 amount); event VestingCreated( address indexed tokenOwner, address vesting, uint256 cliff, uint256 duration, - uint256 amount + uint256 amount, + uint256 vestingCreationType ); event TeamVestingCreated( address indexed tokenOwner, address vesting, uint256 cliff, uint256 duration, - uint256 amount + uint256 amount, + uint256 vestingCreationType ); event TokensStaked(address indexed vesting, uint256 amount); - event AdminAdded(address admin); - event AdminRemoved(address admin); - - /* Functions */ + event VestingCreationAndTypesSet( + address indexed vesting, + VestingCreationAndTypeDetails vestingCreationAndType + ); /** - * @notice Contract deployment settings. - * @param _vestingFactory The address of vesting factory contract. - * @param _SOV The SOV token address. - * @param _CSOVtokens The array of cSOV tokens. - * @param _priceSats The price of cSOV tokens in satoshis. - * @param _staking The address of staking contract. - * @param _feeSharingCollector The address of fee sharing collector proxy contract. - * @param _vestingOwner The address of an owner of vesting contract. - * @dev On Sovryn the vesting owner is Exchequer Multisig. - * According to SIP-0007 The Exchequer Multisig is designated to hold - * certain funds in the form of rBTC and SOV, in order to allow for - * flexible deployment of such funds on: - * + facilitating rBTC redemptions for Genesis pre-sale participants. - * + deploying of SOV for the purposes of exchange listings, market - * making, and partnerships with third parties. + * @notice Replace constructor with initialize function for Upgradable Contracts + * This function will be called only once by the owner * */ - constructor( + function initialize( address _vestingFactory, address _SOV, - address[] memory _CSOVtokens, - uint256 _priceSats, address _staking, address _feeSharingCollector, - address _vestingOwner - ) public { + address _vestingOwner, + address _lockedSOV, + address[] calldata _vestingRegistries + ) external onlyOwner initializer { require(_SOV != address(0), "SOV address invalid"); require(_staking != address(0), "staking address invalid"); require(_feeSharingCollector != address(0), "feeSharingCollector address invalid"); require(_vestingOwner != address(0), "vestingOwner address invalid"); + require(_lockedSOV != address(0), "LockedSOV address invalid"); _setVestingFactory(_vestingFactory); - _setCSOVtokens(_CSOVtokens); - SOV = _SOV; - priceSats = _priceSats; staking = _staking; feeSharingCollector = _feeSharingCollector; vestingOwner = _vestingOwner; - } - - //---ACL------------------------------------------------------------------ - - /** - * @dev Throws if called by any account other than the owner or admin. - * TODO: This ACL logic should be available on OpenZeppeling Ownable.sol - * or on our own overriding sovrynOwnable. This same logic is repeated - * on OriginInvestorsClaim.sol, TokenSender.sol and VestingRegistry2.sol - */ - modifier onlyAuthorized() { - require(isOwner() || admins[msg.sender], "unauthorized"); - _; - } - - /** - * @notice Add account to ACL. - * @param _admin The addresses of the account to grant permissions. - * */ - function addAdmin(address _admin) public onlyOwner { - admins[_admin] = true; - emit AdminAdded(_admin); - } - - /** - * @notice Remove account from ACL. - * @param _admin The addresses of the account to revoke permissions. - * */ - function removeAdmin(address _admin) public onlyOwner { - admins[_admin] = false; - emit AdminRemoved(_admin); - } - - //---PostCSOV-------------------------------------------------------------- - - modifier isNotProcessed() { - require(!processedList[msg.sender], "Address cannot be processed twice"); - _; - } - - modifier isNotBlacklisted() { - require(!blacklist[msg.sender], "Address blacklisted"); - _; - } - - /** - * @notice cSOV payout to sender with rBTC currency. - * 1.- Check holder cSOV balance by adding up every cSOV token balance. - * 2.- ReImburse rBTC if funds available. - * 3.- And store holder address in processedList. - */ - function reImburse() public isNotProcessed isNotBlacklisted { - uint256 CSOVAmountWei = 0; - for (uint256 i = 0; i < CSOVtokens.length; i++) { - address CSOV = CSOVtokens[i]; - uint256 balance = IERC20(CSOV).balanceOf(msg.sender); - CSOVAmountWei = CSOVAmountWei.add(balance); + lockedSOV = LockedSOV(_lockedSOV); + for (uint256 i = 0; i < _vestingRegistries.length; i++) { + require(_vestingRegistries[i] != address(0), "Vesting registry address invalid"); + vestingRegistries.push(IVestingRegistry(_vestingRegistries[i])); } - - require(CSOVAmountWei > lockedAmount[msg.sender], "holder has no CSOV"); - CSOVAmountWei -= lockedAmount[msg.sender]; - processedList[msg.sender] = true; - - /** - * @dev Found and fixed the SIP-0007 bug on VestingRegistry::reImburse formula. - * More details at Documenting Code issues at point 11 in - * https://docs.google.com/document/d/10idTD1K6JvoBmtPKGuJ2Ub_mMh6qTLLlTP693GQKMyU/ - * Previous buggy code: uint256 reImburseAmount = (CSOVAmountWei.mul(priceSats)).div(10**10); - * */ - uint256 reImburseAmount = (CSOVAmountWei.mul(priceSats)).div(10 ** 8); - require(address(this).balance >= reImburseAmount, "Not enough funds to reimburse"); - msg.sender.transfer(reImburseAmount); - - emit CSOVReImburse(msg.sender, CSOVAmountWei, reImburseAmount); } /** - * @notice Get contract balance. - * @return The token balance of the contract. - * */ - function budget() external view returns (uint256) { - uint256 SCBudget = address(this).balance; - return SCBudget; - } - - /** - * @notice Deposit function to receiving value (rBTC). - * */ - function deposit() public payable {} - - /** - * @notice Send all contract balance to an account. - * @param to The account address to send the balance to. - * */ - function withdrawAll(address payable to) public onlyOwner { - to.transfer(address(this).balance); - } - - //-------------------------------------------------------------------------------------------------------------------------------------- - - /** - * @notice Sets vesting factory address. High level endpoint. - * @param _vestingFactory The address of vesting factory contract. - * - * @dev Splitting code on two functions: high level and low level - * is a pattern that makes easy to extend functionality in a readable way, - * without accidentally breaking the actual action being performed. - * For example, checks should be done on high level endpoint, while core - * functionality should be coded on the low level function. - * */ - function setVestingFactory(address _vestingFactory) public onlyOwner { + * @notice sets vesting factory address + * @param _vestingFactory the address of vesting factory contract + */ + function setVestingFactory(address _vestingFactory) external onlyOwner { _setVestingFactory(_vestingFactory); } /** - * @notice Sets vesting factory address. Low level core function. - * @param _vestingFactory The address of vesting factory contract. - * */ + * @notice Internal function that sets vesting factory address + * @param _vestingFactory the address of vesting factory contract + */ function _setVestingFactory(address _vestingFactory) internal { require(_vestingFactory != address(0), "vestingFactory address invalid"); vestingFactory = IVestingFactory(_vestingFactory); } /** - * @notice Sets cSOV tokens array. High level endpoint. - * @param _CSOVtokens The array of cSOV tokens. - * */ - function setCSOVtokens(address[] memory _CSOVtokens) public onlyOwner { - _setCSOVtokens(_CSOVtokens); - } - - /** - * @notice Sets cSOV tokens array by looping through input. Low level function. - * @param _CSOVtokens The array of cSOV tokens. - * */ - function _setCSOVtokens(address[] memory _CSOVtokens) internal { - for (uint256 i = 0; i < _CSOVtokens.length; i++) { - require(_CSOVtokens[i] != address(0), "CSOV address invalid"); - } - CSOVtokens = _CSOVtokens; - } - - /** - * @notice Set blacklist flag (true/false). - * @param _account The address to be blacklisted. - * @param _blacklisted The flag to add/remove to/from a blacklist. - * */ - function setBlacklistFlag(address _account, bool _blacklisted) public onlyOwner { - require(_account != address(0), "account address invalid"); - - blacklist[_account] = _blacklisted; - } - - /** - * @notice Set amount to be subtracted from user token balance. - * @param _account The address with locked amount. - * @param _amount The amount to be locked. - * */ - function setLockedAmount(address _account, uint256 _amount) public onlyOwner { - require(_account != address(0), "account address invalid"); - require(_amount != 0, "amount invalid"); - - lockedAmount[_account] = _amount; - } - - /** - * @notice Transfer SOV tokens to given address. - * - * @dev This is a wrapper for ERC-20 transfer function w/ - * additional checks and triggering an event. - * - * @param _receiver The address of the SOV receiver. - * @param _amount The amount to be transferred. - * */ - function transferSOV(address _receiver, uint256 _amount) public onlyOwner { + * @notice transfers SOV tokens to given address + * @param _receiver the address of the SOV receiver + * @param _amount the amount to be transferred + */ + function transferSOV(address _receiver, uint256 _amount) external onlyOwner { require(_receiver != address(0), "receiver address invalid"); require(_amount != 0, "amount invalid"); - - IERC20(SOV).transfer(_receiver, _amount); + require(IERC20(SOV).transfer(_receiver, _amount), "transfer failed"); emit SOVTransferred(_receiver, _amount); } /** - * @notice Exchange cSOV to SOV with 1:1 rate + * @notice adds vestings that were deployed in previous vesting registries + * @dev migration of data from previous vesting registy contracts */ - function exchangeAllCSOV() public isNotProcessed isNotBlacklisted { - processedList[msg.sender] = true; - - uint256 amount = 0; - for (uint256 i = 0; i < CSOVtokens.length; i++) { - address CSOV = CSOVtokens[i]; - uint256 balance = IERC20(CSOV).balanceOf(msg.sender); - amount += balance; + function addDeployedVestings( + address[] calldata _tokenOwners, + uint256[] calldata _vestingCreationTypes + ) external onlyAuthorized { + for (uint256 i = 0; i < _tokenOwners.length; i++) { + require(_tokenOwners[i] != address(0), "token owner cannot be 0 address"); + require(_vestingCreationTypes[i] > 0, "vesting creation type must be greater than 0"); + _addDeployedVestings(_tokenOwners[i], _vestingCreationTypes[i]); } - - require(amount > lockedAmount[msg.sender], "amount invalid"); - amount -= lockedAmount[msg.sender]; - - _createVestingForCSOV(amount); - } - - /** - * @notice cSOV tokens are moved and staked on Vesting contract. - * @param _amount The amount of tokens to be vested. - * */ - function _createVestingForCSOV(uint256 _amount) internal { - address vesting = _getOrCreateVesting( - msg.sender, - CSOV_VESTING_CLIFF, - CSOV_VESTING_DURATION - ); - - IERC20(SOV).approve(vesting, _amount); - IVesting(vesting).stakeTokens(_amount); - - emit CSOVTokensExchanged(msg.sender, _amount); } /** - * @notice Check a token address is among the cSOV token addresses. - * @param _CSOV The cSOV token address. - * */ - function _validateCSOV(address _CSOV) internal view { - bool isValid = false; - for (uint256 i = 0; i < CSOVtokens.length; i++) { - if (_CSOV == CSOVtokens[i]) { - isValid = true; - break; - } + * @notice adds four year vestings to vesting registry logic + * @param _tokenOwners array of token owners + * @param _vestingAddresses array of vesting addresses + */ + function addFourYearVestings( + address[] calldata _tokenOwners, + address[] calldata _vestingAddresses + ) external onlyAuthorized { + require(_tokenOwners.length == _vestingAddresses.length, "arrays mismatch"); + uint256 vestingCreationType = 4; + uint256 cliff = 4 weeks; + uint256 duration = 156 weeks; + for (uint256 i = 0; i < _tokenOwners.length; i++) { + require(!isVesting[_vestingAddresses[i]], "vesting exists"); + require(_tokenOwners[i] != address(0), "token owner cannot be 0 address"); + require(_vestingAddresses[i] != address(0), "vesting cannot be 0 address"); + uint256 uid = uint256( + keccak256( + abi.encodePacked( + _tokenOwners[i], + uint256(VestingType.Vesting), + cliff, + duration, + vestingCreationType + ) + ) + ); + vestings[uid] = Vesting( + uint256(VestingType.Vesting), + vestingCreationType, + _vestingAddresses[i] + ); + vestingsOf[_tokenOwners[i]].push(uid); + isVesting[_vestingAddresses[i]] = true; } - require(isValid, "wrong CSOV address"); } /** - * @notice Create Vesting contract. - * @param _tokenOwner The owner of the tokens. - * @param _amount The amount to be staked. - * @param _cliff The time interval to the first withdraw in seconds. - * @param _duration The total duration in seconds. - * */ + * @notice creates Vesting contract + * @param _tokenOwner the owner of the tokens + * @param _amount the amount to be staked + * @param _cliff the cliff in seconds + * @param _duration the total duration in seconds + * @dev Calls a public createVestingAddr function with vestingCreationType. This is to accomodate the existing logic for LockedSOV + * @dev vestingCreationType 0 = LockedSOV + */ function createVesting( address _tokenOwner, uint256 _amount, uint256 _cliff, uint256 _duration + ) external onlyAuthorized { + createVestingAddr(_tokenOwner, _amount, _cliff, _duration, 3); + } + + /** + * @notice creates Vesting contract + * @param _tokenOwner the owner of the tokens + * @param _amount the amount to be staked + * @param _cliff the cliff in seconds + * @param _duration the total duration in seconds + * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.) + */ + function createVestingAddr( + address _tokenOwner, + uint256 _amount, + uint256 _cliff, + uint256 _duration, + uint256 _vestingCreationType ) public onlyAuthorized { - address vesting = _getOrCreateVesting(_tokenOwner, _cliff, _duration); - emit VestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount); + address vesting = _getOrCreateVesting( + _tokenOwner, + _cliff, + _duration, + uint256(VestingType.Vesting), + _vestingCreationType + ); + + emit VestingCreated( + _tokenOwner, + vesting, + _cliff, + _duration, + _amount, + _vestingCreationType + ); } /** - * @notice Create Team Vesting contract. - * @param _tokenOwner The owner of the tokens. - * @param _amount The amount to be staked. - * @param _cliff The time interval to the first withdraw in seconds. - * @param _duration The total duration in seconds. - * */ + * @notice creates Team Vesting contract + * @param _tokenOwner the owner of the tokens + * @param _amount the amount to be staked + * @param _cliff the cliff in seconds + * @param _duration the total duration in seconds + * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.) + */ function createTeamVesting( address _tokenOwner, uint256 _amount, uint256 _cliff, - uint256 _duration - ) public onlyAuthorized { - address vesting = _getOrCreateTeamVesting(_tokenOwner, _cliff, _duration); - emit TeamVestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount); + uint256 _duration, + uint256 _vestingCreationType + ) external onlyAuthorized { + address vesting = _getOrCreateVesting( + _tokenOwner, + _cliff, + _duration, + uint256(VestingType.TeamVesting), + _vestingCreationType + ); + + emit TeamVestingCreated( + _tokenOwner, + vesting, + _cliff, + _duration, + _amount, + _vestingCreationType + ); } /** - * @notice Stake tokens according to the vesting schedule. - * @param _vesting The address of Vesting contract. - * @param _amount The amount of tokens to stake. - * */ - function stakeTokens(address _vesting, uint256 _amount) public onlyAuthorized { + * @notice stakes tokens according to the vesting schedule + * @param _vesting the address of Vesting contract + * @param _amount the amount of tokens to stake + */ + function stakeTokens(address _vesting, uint256 _amount) external onlyAuthorized { require(_vesting != address(0), "vesting address invalid"); require(_amount > 0, "amount invalid"); @@ -430,79 +247,232 @@ contract VestingRegistry is Ownable { } /** - * @notice Query the vesting contract for an account. - * @param _tokenOwner The owner of the tokens. - * @return The vesting contract address for the given token owner. - * */ + * @notice returns vesting contract address for the given token owner + * @param _tokenOwner the owner of the tokens + * @dev Calls a public getVestingAddr function with cliff and duration. This is to accomodate the existing logic for LockedSOV + * @dev We need to use LockedSOV.changeRegistryCliffAndDuration function very judiciously + * @dev vestingCreationType 0 - LockedSOV + */ function getVesting(address _tokenOwner) public view returns (address) { - return vestingContracts[_tokenOwner][uint256(VestingType.Vesting)]; + return getVestingAddr(_tokenOwner, lockedSOV.cliff(), lockedSOV.duration(), 3); } /** - * @notice Query the team vesting contract for an account. - * @param _tokenOwner The owner of the tokens. - * @return The team vesting contract address for the given token owner. - * */ - function getTeamVesting(address _tokenOwner) public view returns (address) { - return vestingContracts[_tokenOwner][uint256(VestingType.TeamVesting)]; + * @notice public function that returns vesting contract address for the given token owner, cliff, duration + * @dev Important: Please use this instead of getVesting function + */ + function getVestingAddr( + address _tokenOwner, + uint256 _cliff, + uint256 _duration, + uint256 _vestingCreationType + ) public view returns (address) { + uint256 type_ = uint256(VestingType.Vesting); + uint256 uid = uint256( + keccak256( + abi.encodePacked(_tokenOwner, type_, _cliff, _duration, _vestingCreationType) + ) + ); + return vestings[uid].vestingAddress; } /** - * @notice If not exists, deploy a vesting contract through factory. - * @param _tokenOwner The owner of the tokens. - * @param _cliff The time interval to the first withdraw in seconds. - * @param _duration The total duration in seconds. - * @return The vesting contract address for the given token owner - * whether it existed previously or not. - * */ - function _getOrCreateVesting( + * @notice returns team vesting contract address for the given token owner, cliff, duration + */ + function getTeamVesting( address _tokenOwner, uint256 _cliff, - uint256 _duration - ) internal returns (address) { - uint256 type_ = uint256(VestingType.Vesting); - if (vestingContracts[_tokenOwner][type_] == address(0)) { - /// @dev TODO: Owner of OwnerVesting contracts - the same address as tokenOwner. - address vesting = vestingFactory.deployVesting( - SOV, - staking, - _tokenOwner, - _cliff, - _duration, - feeSharingCollector, - _tokenOwner + uint256 _duration, + uint256 _vestingCreationType + ) public view returns (address) { + uint256 type_ = uint256(VestingType.TeamVesting); + uint256 uid = uint256( + keccak256( + abi.encodePacked(_tokenOwner, type_, _cliff, _duration, _vestingCreationType) + ) + ); + return vestings[uid].vestingAddress; + } + + /** + * @dev check if the specific vesting address is team vesting or not + * @dev read the vestingType from vestingCreationAndTypes storage + * + * @param _vestingAddress address of vesting contract + * + * @return true for teamVesting, false for normal vesting + */ + function isTeamVesting(address _vestingAddress) external view returns (bool) { + return (vestingCreationAndTypes[_vestingAddress].isSet && + vestingCreationAndTypes[_vestingAddress].vestingType == + uint32(VestingType.TeamVesting)); + } + + /** + * @dev setter function to register existing vesting contract to vestingCreationAndTypes storage + * @dev need to set the function visilibty to public to support VestingCreationAndTypeDetails struct as parameter + * + * @param _vestingAddresses array of vesting address + * @param _vestingCreationAndTypes array for VestingCreationAndTypeDetails struct + */ + function registerVestingToVestingCreationAndTypes( + address[] memory _vestingAddresses, + VestingCreationAndTypeDetails[] memory _vestingCreationAndTypes + ) public onlyAuthorized { + require(_vestingAddresses.length == _vestingCreationAndTypes.length, "Unmatched length"); + for (uint256 i = 0; i < _vestingCreationAndTypes.length; i++) { + VestingCreationAndTypeDetails + memory _vestingCreationAndType = _vestingCreationAndTypes[i]; + address _vestingAddress = _vestingAddresses[i]; + + vestingCreationAndTypes[_vestingAddress] = _vestingCreationAndType; + + emit VestingCreationAndTypesSet( + _vestingAddress, + vestingCreationAndTypes[_vestingAddress] ); - vestingContracts[_tokenOwner][type_] = vesting; } - return vestingContracts[_tokenOwner][type_]; } /** - * @notice If not exists, deploy a team vesting contract through factory. - * @param _tokenOwner The owner of the tokens. - * @param _cliff The time interval to the first withdraw in seconds. - * @param _duration The total duration in seconds. - * @return The team vesting contract address for the given token owner - * whether it existed previously or not. - * */ - function _getOrCreateTeamVesting( + * @notice Internal function to deploy Vesting/Team Vesting contract + * @param _tokenOwner the owner of the tokens + * @param _cliff the cliff in seconds + * @param _duration the total duration in seconds + * @param _type the type of vesting + * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.) + */ + function _getOrCreateVesting( address _tokenOwner, uint256 _cliff, - uint256 _duration + uint256 _duration, + uint256 _type, + uint256 _vestingCreationType ) internal returns (address) { - uint256 type_ = uint256(VestingType.TeamVesting); - if (vestingContracts[_tokenOwner][type_] == address(0)) { - address vesting = vestingFactory.deployTeamVesting( - SOV, - staking, - _tokenOwner, - _cliff, - _duration, - feeSharingCollector, - vestingOwner + address vesting; + uint256 uid = uint256( + keccak256( + abi.encodePacked(_tokenOwner, _type, _cliff, _duration, _vestingCreationType) + ) + ); + if (vestings[uid].vestingAddress == address(0)) { + if (_type == 1) { + vesting = vestingFactory.deployVesting( + SOV, + staking, + _tokenOwner, + _cliff, + _duration, + feeSharingCollector, + _tokenOwner + ); + } else { + vesting = vestingFactory.deployTeamVesting( + SOV, + staking, + _tokenOwner, + _cliff, + _duration, + feeSharingCollector, + vestingOwner + ); + } + vestings[uid] = Vesting(_type, _vestingCreationType, vesting); + vestingsOf[_tokenOwner].push(uid); + isVesting[vesting] = true; + + vestingCreationAndTypes[vesting] = VestingCreationAndTypeDetails({ + isSet: true, + vestingType: uint32(_type), + vestingCreationType: uint128(_vestingCreationType) + }); + + emit VestingCreationAndTypesSet(vesting, vestingCreationAndTypes[vesting]); + } + return vestings[uid].vestingAddress; + } + + /** + * @notice stores the addresses of Vesting contracts from all three previous versions of Vesting Registry + */ + function _addDeployedVestings(address _tokenOwner, uint256 _vestingCreationType) internal { + uint256 uid; + uint256 i = _vestingCreationType - 1; + + address vestingAddress = vestingRegistries[i].getVesting(_tokenOwner); + if (vestingAddress != address(0)) { + VestingLogic vesting = VestingLogic(vestingAddress); + uid = uint256( + keccak256( + abi.encodePacked( + _tokenOwner, + uint256(VestingType.Vesting), + vesting.cliff(), + vesting.duration(), + _vestingCreationType + ) + ) + ); + vestings[uid] = Vesting( + uint256(VestingType.Vesting), + _vestingCreationType, + vestingAddress ); - vestingContracts[_tokenOwner][type_] = vesting; + vestingsOf[_tokenOwner].push(uid); + isVesting[vestingAddress] = true; } - return vestingContracts[_tokenOwner][type_]; + + address teamVestingAddress = vestingRegistries[i].getTeamVesting(_tokenOwner); + if (teamVestingAddress != address(0)) { + VestingLogic vesting = VestingLogic(teamVestingAddress); + uid = uint256( + keccak256( + abi.encodePacked( + _tokenOwner, + uint256(VestingType.TeamVesting), + vesting.cliff(), + vesting.duration(), + _vestingCreationType + ) + ) + ); + vestings[uid] = Vesting( + uint256(VestingType.TeamVesting), + _vestingCreationType, + teamVestingAddress + ); + vestingsOf[_tokenOwner].push(uid); + isVesting[teamVestingAddress] = true; + } + } + + /** + * @notice returns all vesting details for the given token owner + */ + function getVestingsOf(address _tokenOwner) external view returns (Vesting[] memory) { + uint256[] memory vestingIds = vestingsOf[_tokenOwner]; + uint256 length = vestingIds.length; + Vesting[] memory _vestings = new Vesting[](vestingIds.length); + for (uint256 i = 0; i < length; i++) { + _vestings[i] = vestings[vestingIds[i]]; + } + return _vestings; + } + + /** + * @notice returns cliff and duration for Vesting & TeamVesting contracts + */ + function getVestingDetails( + address _vestingAddress + ) external view returns (uint256 cliff, uint256 duration) { + VestingLogic vesting = VestingLogic(_vestingAddress); + return (vesting.cliff(), vesting.duration()); + } + + /** + * @notice returns if the address is a vesting address + */ + function isVestingAddress(address _vestingAddress) external view returns (bool isVestingAddr) { + return isVesting[_vestingAddress]; } } diff --git a/contracts/governance/Vesting/VestingRegistry2.sol b/contracts/governance/Vesting/VestingRegistry2.sol deleted file mode 100644 index ab0243ac2..000000000 --- a/contracts/governance/Vesting/VestingRegistry2.sol +++ /dev/null @@ -1,438 +0,0 @@ -pragma solidity ^0.5.17; - -import "../../openzeppelin/Ownable.sol"; -import "../../interfaces/IERC20.sol"; -import "../Staking/interfaces/IStaking.sol"; -import "../IFeeSharingCollector.sol"; -import "./IVestingFactory.sol"; -import "./IVesting.sol"; -import "./ITeamVesting.sol"; -import "../../openzeppelin/SafeMath.sol"; - -/** - * @title VestingRegistry 2 contract. - * @notice One time contract needed to distribute tokens to origin sales investors. - * */ -contract VestingRegistry2 is Ownable { - using SafeMath for uint256; - - /* Storage */ - - /// @notice Constant used for computing the vesting dates. - uint256 public constant FOUR_WEEKS = 4 weeks; - - uint256 public constant CSOV_VESTING_CLIFF = FOUR_WEEKS; - uint256 public constant CSOV_VESTING_DURATION = 10 * FOUR_WEEKS; - - IVestingFactory public vestingFactory; - - /// @notice The SOV token contract. - address public SOV; - - /// @notice The CSOV token contracts. - address[] public CSOVtokens; - - uint256 public priceSats; - - /// @notice The staking contract address. - address public staking; - - /// @notice Fee sharing proxy. - address public feeSharingCollector; - - /// @notice The vesting owner (e.g. governance timelock address). - address public vestingOwner; - - /// @dev TODO: Add to the documentation: address can have only one vesting of each type. - /// @dev user => vesting type => vesting contract - mapping(address => mapping(uint256 => address)) public vestingContracts; - - /** - * @dev Struct can be created to save storage slots, but it doesn't make - * sense. We don't have a lot of blacklisted accounts or account with - * locked amount. - * */ - - /// @dev user => flag whether user has already exchange cSOV or got a reimbursement. - mapping(address => bool) public processedList; - - /// @dev user => flag whether user shouldn't be able to exchange or reimburse. - mapping(address => bool) public blacklist; - - /// @dev user => amount of tokens should not be processed. - mapping(address => uint256) public lockedAmount; - - /// @dev user => flag whether user has admin role. - mapping(address => bool) public admins; - - enum VestingType { - TeamVesting, // MultisigVesting - Vesting // TokenHolderVesting - } - - /* Events */ - - event CSOVTokensExchanged(address indexed caller, uint256 amount); - event SOVTransferred(address indexed receiver, uint256 amount); - event VestingCreated( - address indexed tokenOwner, - address vesting, - uint256 cliff, - uint256 duration, - uint256 amount - ); - event TeamVestingCreated( - address indexed tokenOwner, - address vesting, - uint256 cliff, - uint256 duration, - uint256 amount - ); - event TokensStaked(address indexed vesting, uint256 amount); - event AdminAdded(address admin); - event AdminRemoved(address admin); - - /* Functions */ - - /** - * @notice Contract deployment settings. - * @param _vestingFactory The address of vesting factory contract. - * @param _SOV The SOV token address. - * @param _CSOVtokens The array of cSOV tokens. - * @param _priceSats The price of cSOV tokens in satoshis. - * @param _staking The address of staking contract. - * @param _feeSharingCollector The address of fee sharing proxy contract. - * @param _vestingOwner The address of an owner of vesting contract. - * @dev On Sovryn the vesting owner is Exchequer Multisig. - * According to SIP-0007 The Exchequer Multisig is designated to hold - * certain funds in the form of rBTC and SOV, in order to allow for - * flexible deployment of such funds on: - * + facilitating rBTC redemptions for Genesis pre-sale participants. - * + deploying of SOV for the purposes of exchange listings, market - * making, and partnerships with third parties. - * */ - constructor( - address _vestingFactory, - address _SOV, - address[] memory _CSOVtokens, - uint256 _priceSats, - address _staking, - address _feeSharingCollector, - address _vestingOwner - ) public { - require(_SOV != address(0), "SOV address invalid"); - require(_staking != address(0), "staking address invalid"); - require(_feeSharingCollector != address(0), "feeSharingCollector address invalid"); - require(_vestingOwner != address(0), "vestingOwner address invalid"); - - _setVestingFactory(_vestingFactory); - _setCSOVtokens(_CSOVtokens); - - SOV = _SOV; - priceSats = _priceSats; - staking = _staking; - feeSharingCollector = _feeSharingCollector; - vestingOwner = _vestingOwner; - } - - /** - * @dev Throws if called by any account other than the owner or admin. - */ - modifier onlyAuthorized() { - require(isOwner() || admins[msg.sender], "unauthorized"); - _; - } - - /** - * @notice Add account to ACL. - * @param _admin The addresses of the account to grant permissions. - * */ - function addAdmin(address _admin) public onlyOwner { - admins[_admin] = true; - emit AdminAdded(_admin); - } - - /** - * @notice Remove account from ACL. - * @param _admin The addresses of the account to revoke permissions. - * */ - function removeAdmin(address _admin) public onlyOwner { - admins[_admin] = false; - emit AdminRemoved(_admin); - } - - //---PostCSOV-------------------------------------------------------------- - - modifier isNotProcessed() { - require(!processedList[msg.sender], "Address cannot be processed twice"); - _; - } - - modifier isNotBlacklisted() { - require(!blacklist[msg.sender], "Address blacklisted"); - _; - } - - /** - * @notice Get contract balance. - * @return The token balance of the contract. - * */ - function budget() external view returns (uint256) { - uint256 SCBudget = address(this).balance; - return SCBudget; - } - - /** - * @notice Deposit function to receiving value (rBTC). - * */ - function deposit() public payable {} - - /** - * @notice Send all contract balance to an account. - * @param to The account address to send the balance to. - * */ - function withdrawAll(address payable to) public onlyOwner { - to.transfer(address(this).balance); - } - - //-------------------------------------------------------------------------------------------------------------------------------------- - - /** - * @notice Sets vesting factory address. High level endpoint. - * @param _vestingFactory The address of vesting factory contract. - * - * @dev Splitting code on two functions: high level and low level - * is a pattern that makes easy to extend functionality in a readable way, - * without accidentally breaking the actual action being performed. - * For example, checks should be done on high level endpoint, while core - * functionality should be coded on the low level function. - * */ - function setVestingFactory(address _vestingFactory) public onlyOwner { - _setVestingFactory(_vestingFactory); - } - - /** - * @notice Sets vesting factory address. Low level core function. - * @param _vestingFactory The address of vesting factory contract. - * */ - function _setVestingFactory(address _vestingFactory) internal { - require(_vestingFactory != address(0), "vestingFactory address invalid"); - vestingFactory = IVestingFactory(_vestingFactory); - } - - /** - * @notice Sets cSOV tokens array. High level endpoint. - * @param _CSOVtokens The array of cSOV tokens. - * */ - function setCSOVtokens(address[] memory _CSOVtokens) public onlyOwner { - _setCSOVtokens(_CSOVtokens); - } - - /** - * @notice Sets cSOV tokens array by looping through input. Low level function. - * @param _CSOVtokens The array of cSOV tokens. - * */ - function _setCSOVtokens(address[] memory _CSOVtokens) internal { - for (uint256 i = 0; i < _CSOVtokens.length; i++) { - require(_CSOVtokens[i] != address(0), "CSOV address invalid"); - } - CSOVtokens = _CSOVtokens; - } - - /** - * @notice Set blacklist flag (true/false). - * @param _account The address to be blacklisted. - * @param _blacklisted The flag to add/remove to/from a blacklist. - * */ - function setBlacklistFlag(address _account, bool _blacklisted) public onlyOwner { - require(_account != address(0), "account address invalid"); - - blacklist[_account] = _blacklisted; - } - - /** - * @notice Set amount to be subtracted from user token balance. - * @param _account The address with locked amount. - * @param _amount The amount to be locked. - * */ - function setLockedAmount(address _account, uint256 _amount) public onlyOwner { - require(_account != address(0), "account address invalid"); - require(_amount != 0, "amount invalid"); - - lockedAmount[_account] = _amount; - } - - /** - * @notice Transfer SOV tokens to given address. - * - * @dev This is a wrapper for ERC-20 transfer function w/ - * additional checks and triggering an event. - * - * @param _receiver The address of the SOV receiver. - * @param _amount The amount to be transferred. - * */ - function transferSOV(address _receiver, uint256 _amount) public onlyOwner { - require(_receiver != address(0), "receiver address invalid"); - require(_amount != 0, "amount invalid"); - - IERC20(SOV).transfer(_receiver, _amount); - emit SOVTransferred(_receiver, _amount); - } - - /** - * @notice cSOV tokens are moved and staked on Vesting contract. - * @param _amount The amount of tokens to be vested. - * */ - function _createVestingForCSOV(uint256 _amount) internal { - address vesting = _getOrCreateVesting( - msg.sender, - CSOV_VESTING_CLIFF, - CSOV_VESTING_DURATION - ); - - IERC20(SOV).approve(vesting, _amount); - IVesting(vesting).stakeTokens(_amount); - - emit CSOVTokensExchanged(msg.sender, _amount); - } - - /** - * @notice Check a token address is among the cSOV token addresses. - * @param _CSOV The cSOV token address. - * */ - function _validateCSOV(address _CSOV) internal view { - bool isValid = false; - for (uint256 i = 0; i < CSOVtokens.length; i++) { - if (_CSOV == CSOVtokens[i]) { - isValid = true; - break; - } - } - require(isValid, "wrong CSOV address"); - } - - /** - * @notice Create Vesting contract. - * @param _tokenOwner The owner of the tokens. - * @param _amount The amount to be staked. - * @param _cliff The time interval to the first withdraw in seconds. - * @param _duration The total duration in seconds. - * */ - function createVesting( - address _tokenOwner, - uint256 _amount, - uint256 _cliff, - uint256 _duration - ) public onlyAuthorized { - address vesting = _getOrCreateVesting(_tokenOwner, _cliff, _duration); - emit VestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount); - } - - /** - * @notice Create Team Vesting contract. - * @param _tokenOwner The owner of the tokens. - * @param _amount The amount to be staked. - * @param _cliff The time interval to the first withdraw in seconds. - * @param _duration The total duration in seconds. - * */ - function createTeamVesting( - address _tokenOwner, - uint256 _amount, - uint256 _cliff, - uint256 _duration - ) public onlyAuthorized { - address vesting = _getOrCreateTeamVesting(_tokenOwner, _cliff, _duration); - emit TeamVestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount); - } - - /** - * @notice Stake tokens according to the vesting schedule - * @param _vesting the address of Vesting contract - * @param _amount the amount of tokens to stake - * */ - function stakeTokens(address _vesting, uint256 _amount) public onlyAuthorized { - require(_vesting != address(0), "vesting address invalid"); - require(_amount > 0, "amount invalid"); - - IERC20(SOV).approve(_vesting, _amount); - IVesting(_vesting).stakeTokens(_amount); - emit TokensStaked(_vesting, _amount); - } - - /** - * @notice Query the vesting contract for an account. - * @param _tokenOwner The owner of the tokens. - * @return The vesting contract address for the given token owner. - * */ - function getVesting(address _tokenOwner) public view returns (address) { - return vestingContracts[_tokenOwner][uint256(VestingType.Vesting)]; - } - - /** - * @notice Query the team vesting contract for an account. - * @param _tokenOwner The owner of the tokens. - * @return The team vesting contract address for the given token owner. - * */ - function getTeamVesting(address _tokenOwner) public view returns (address) { - return vestingContracts[_tokenOwner][uint256(VestingType.TeamVesting)]; - } - - /** - * @notice If not exists, deploy a vesting contract through factory. - * @param _tokenOwner The owner of the tokens. - * @param _cliff The time interval to the first withdraw in seconds. - * @param _duration The total duration in seconds. - * @return The vesting contract address for the given token owner - * whether it existed previously or not. - * */ - function _getOrCreateVesting( - address _tokenOwner, - uint256 _cliff, - uint256 _duration - ) internal returns (address) { - uint256 type_ = uint256(VestingType.Vesting); - if (vestingContracts[_tokenOwner][type_] == address(0)) { - //TODO Owner of OwnerVesting contracts - the same address as tokenOwner - address vesting = vestingFactory.deployVesting( - SOV, - staking, - _tokenOwner, - _cliff, - _duration, - feeSharingCollector, - _tokenOwner - ); - vestingContracts[_tokenOwner][type_] = vesting; - } - return vestingContracts[_tokenOwner][type_]; - } - - /** - * @notice If not exists, deploy a team vesting contract through factory. - * @param _tokenOwner The owner of the tokens. - * @param _cliff The time interval to the first withdraw in seconds. - * @param _duration The total duration in seconds. - * @return The team vesting contract address for the given token owner - * whether it existed previously or not. - * */ - function _getOrCreateTeamVesting( - address _tokenOwner, - uint256 _cliff, - uint256 _duration - ) internal returns (address) { - uint256 type_ = uint256(VestingType.TeamVesting); - if (vestingContracts[_tokenOwner][type_] == address(0)) { - address vesting = vestingFactory.deployTeamVesting( - SOV, - staking, - _tokenOwner, - _cliff, - _duration, - feeSharingCollector, - vestingOwner - ); - vestingContracts[_tokenOwner][type_] = vesting; - } - return vestingContracts[_tokenOwner][type_]; - } -} diff --git a/contracts/governance/Vesting/VestingRegistry3.sol b/contracts/governance/Vesting/VestingRegistry3.sol deleted file mode 100644 index 76f8c0837..000000000 --- a/contracts/governance/Vesting/VestingRegistry3.sol +++ /dev/null @@ -1,228 +0,0 @@ -pragma solidity ^0.5.17; - -import "../../openzeppelin/Ownable.sol"; -import "../../interfaces/IERC20.sol"; -import "../Staking/interfaces/IStaking.sol"; -import "../IFeeSharingCollector.sol"; -import "./IVestingFactory.sol"; -import "./IVesting.sol"; -import "./ITeamVesting.sol"; -import "../../openzeppelin/SafeMath.sol"; - -contract VestingRegistry3 is Ownable { - using SafeMath for uint256; - - IVestingFactory public vestingFactory; - - ///@notice the SOV token contract - address public SOV; - - ///@notice the staking contract address - address public staking; - //@notice fee sharing proxy - address public feeSharingCollector; - //@notice the vesting owner (e.g. governance timelock address) - address public vestingOwner; - - //TODO add to the documentation: address can have only one vesting of each type - //user => vesting type => vesting contract - mapping(address => mapping(uint256 => address)) public vestingContracts; - - //user => flag whether user has admin role - mapping(address => bool) public admins; - - enum VestingType { - TeamVesting, //MultisigVesting - Vesting //TokenHolderVesting - } - - event SOVTransferred(address indexed receiver, uint256 amount); - event VestingCreated( - address indexed tokenOwner, - address vesting, - uint256 cliff, - uint256 duration, - uint256 amount - ); - event TeamVestingCreated( - address indexed tokenOwner, - address vesting, - uint256 cliff, - uint256 duration, - uint256 amount - ); - event TokensStaked(address indexed vesting, uint256 amount); - event AdminAdded(address admin); - event AdminRemoved(address admin); - - constructor( - address _vestingFactory, - address _SOV, - address _staking, - address _feeSharingCollector, - address _vestingOwner - ) public { - require(_SOV != address(0), "SOV address invalid"); - require(_staking != address(0), "staking address invalid"); - require(_feeSharingCollector != address(0), "feeSharingCollector address invalid"); - require(_vestingOwner != address(0), "vestingOwner address invalid"); - - _setVestingFactory(_vestingFactory); - - SOV = _SOV; - staking = _staking; - feeSharingCollector = _feeSharingCollector; - vestingOwner = _vestingOwner; - } - - /** - * @dev Throws if called by any account other than the owner or admin. - */ - modifier onlyAuthorized() { - require(isOwner() || admins[msg.sender], "unauthorized"); - _; - } - - function addAdmin(address _admin) public onlyOwner { - admins[_admin] = true; - emit AdminAdded(_admin); - } - - function removeAdmin(address _admin) public onlyOwner { - admins[_admin] = false; - emit AdminRemoved(_admin); - } - - /** - * @notice sets vesting factory address - * @param _vestingFactory the address of vesting factory contract - */ - function setVestingFactory(address _vestingFactory) public onlyOwner { - _setVestingFactory(_vestingFactory); - } - - function _setVestingFactory(address _vestingFactory) internal { - require(_vestingFactory != address(0), "vestingFactory address invalid"); - vestingFactory = IVestingFactory(_vestingFactory); - } - - /** - * @notice transfers SOV tokens to given address - * @param _receiver the address of the SOV receiver - * @param _amount the amount to be transferred - */ - function transferSOV(address _receiver, uint256 _amount) public onlyOwner { - require(_receiver != address(0), "receiver address invalid"); - require(_amount != 0, "amount invalid"); - - IERC20(SOV).transfer(_receiver, _amount); - emit SOVTransferred(_receiver, _amount); - } - - /** - * @notice creates Vesting contract - * @param _tokenOwner the owner of the tokens - * @param _amount the amount to be staked - * @param _cliff the cliff in seconds - * @param _duration the total duration in seconds - */ - function createVesting( - address _tokenOwner, - uint256 _amount, - uint256 _cliff, - uint256 _duration - ) public onlyAuthorized { - address vesting = _getOrCreateVesting(_tokenOwner, _cliff, _duration); - emit VestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount); - } - - /** - * @notice creates Team Vesting contract - * @param _tokenOwner the owner of the tokens - * @param _amount the amount to be staked - * @param _cliff the cliff in seconds - * @param _duration the total duration in seconds - */ - function createTeamVesting( - address _tokenOwner, - uint256 _amount, - uint256 _cliff, - uint256 _duration - ) public onlyAuthorized { - address vesting = _getOrCreateTeamVesting(_tokenOwner, _cliff, _duration); - emit TeamVestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount); - } - - /** - * @notice stakes tokens according to the vesting schedule - * @param _vesting the address of Vesting contract - * @param _amount the amount of tokens to stake - */ - function stakeTokens(address _vesting, uint256 _amount) public onlyAuthorized { - require(_vesting != address(0), "vesting address invalid"); - require(_amount > 0, "amount invalid"); - - IERC20(SOV).approve(_vesting, _amount); - IVesting(_vesting).stakeTokens(_amount); - emit TokensStaked(_vesting, _amount); - } - - /** - * @notice returns vesting contract address for the given token owner - * @param _tokenOwner the owner of the tokens - */ - function getVesting(address _tokenOwner) public view returns (address) { - return vestingContracts[_tokenOwner][uint256(VestingType.Vesting)]; - } - - /** - * @notice returns team vesting contract address for the given token owner - * @param _tokenOwner the owner of the tokens - */ - function getTeamVesting(address _tokenOwner) public view returns (address) { - return vestingContracts[_tokenOwner][uint256(VestingType.TeamVesting)]; - } - - function _getOrCreateVesting( - address _tokenOwner, - uint256 _cliff, - uint256 _duration - ) internal returns (address) { - uint256 type_ = uint256(VestingType.Vesting); - if (vestingContracts[_tokenOwner][type_] == address(0)) { - //TODO Owner of OwnerVesting contracts - the same address as tokenOwner - address vesting = vestingFactory.deployVesting( - SOV, - staking, - _tokenOwner, - _cliff, - _duration, - feeSharingCollector, - _tokenOwner - ); - vestingContracts[_tokenOwner][type_] = vesting; - } - return vestingContracts[_tokenOwner][type_]; - } - - function _getOrCreateTeamVesting( - address _tokenOwner, - uint256 _cliff, - uint256 _duration - ) internal returns (address) { - uint256 type_ = uint256(VestingType.TeamVesting); - if (vestingContracts[_tokenOwner][type_] == address(0)) { - address vesting = vestingFactory.deployTeamVesting( - SOV, - staking, - _tokenOwner, - _cliff, - _duration, - feeSharingCollector, - vestingOwner - ); - vestingContracts[_tokenOwner][type_] = vesting; - } - return vestingContracts[_tokenOwner][type_]; - } -} diff --git a/contracts/governance/Vesting/VestingRegistryLogic.sol b/contracts/governance/Vesting/VestingRegistryLogic.sol deleted file mode 100644 index 2c8d8217e..000000000 --- a/contracts/governance/Vesting/VestingRegistryLogic.sol +++ /dev/null @@ -1,474 +0,0 @@ -pragma solidity ^0.5.17; -pragma experimental ABIEncoderV2; - -import "../../interfaces/IERC20.sol"; -import "../IFeeSharingCollector.sol"; -import "./IVesting.sol"; -import "./ITeamVesting.sol"; -import "./VestingRegistryStorage.sol"; - -contract VestingRegistryLogic is VestingRegistryStorage { - event SOVTransferred(address indexed receiver, uint256 amount); - event VestingCreated( - address indexed tokenOwner, - address vesting, - uint256 cliff, - uint256 duration, - uint256 amount, - uint256 vestingCreationType - ); - event TeamVestingCreated( - address indexed tokenOwner, - address vesting, - uint256 cliff, - uint256 duration, - uint256 amount, - uint256 vestingCreationType - ); - event TokensStaked(address indexed vesting, uint256 amount); - event VestingCreationAndTypesSet( - address indexed vesting, - VestingCreationAndTypeDetails vestingCreationAndType - ); - - /** - * @notice Replace constructor with initialize function for Upgradable Contracts - * This function will be called only once by the owner - * */ - function initialize( - address _vestingFactory, - address _SOV, - address _staking, - address _feeSharingCollector, - address _vestingOwner, - address _lockedSOV, - address[] calldata _vestingRegistries - ) external onlyOwner initializer { - require(_SOV != address(0), "SOV address invalid"); - require(_staking != address(0), "staking address invalid"); - require(_feeSharingCollector != address(0), "feeSharingCollector address invalid"); - require(_vestingOwner != address(0), "vestingOwner address invalid"); - require(_lockedSOV != address(0), "LockedSOV address invalid"); - - _setVestingFactory(_vestingFactory); - SOV = _SOV; - staking = _staking; - feeSharingCollector = _feeSharingCollector; - vestingOwner = _vestingOwner; - lockedSOV = LockedSOV(_lockedSOV); - for (uint256 i = 0; i < _vestingRegistries.length; i++) { - require(_vestingRegistries[i] != address(0), "Vesting registry address invalid"); - vestingRegistries.push(IVestingRegistry(_vestingRegistries[i])); - } - } - - /** - * @notice sets vesting factory address - * @param _vestingFactory the address of vesting factory contract - */ - function setVestingFactory(address _vestingFactory) external onlyOwner { - _setVestingFactory(_vestingFactory); - } - - /** - * @notice Internal function that sets vesting factory address - * @param _vestingFactory the address of vesting factory contract - */ - function _setVestingFactory(address _vestingFactory) internal { - require(_vestingFactory != address(0), "vestingFactory address invalid"); - vestingFactory = IVestingFactory(_vestingFactory); - } - - /** - * @notice transfers SOV tokens to given address - * @param _receiver the address of the SOV receiver - * @param _amount the amount to be transferred - */ - function transferSOV(address _receiver, uint256 _amount) external onlyOwner { - require(_receiver != address(0), "receiver address invalid"); - require(_amount != 0, "amount invalid"); - require(IERC20(SOV).transfer(_receiver, _amount), "transfer failed"); - emit SOVTransferred(_receiver, _amount); - } - - /** - * @notice adds vestings that were deployed in previous vesting registries - * @dev migration of data from previous vesting registy contracts - */ - function addDeployedVestings( - address[] calldata _tokenOwners, - uint256[] calldata _vestingCreationTypes - ) external onlyAuthorized { - for (uint256 i = 0; i < _tokenOwners.length; i++) { - require(_tokenOwners[i] != address(0), "token owner cannot be 0 address"); - require(_vestingCreationTypes[i] > 0, "vesting creation type must be greater than 0"); - _addDeployedVestings(_tokenOwners[i], _vestingCreationTypes[i]); - } - } - - /** - * @notice adds four year vestings to vesting registry logic - * @param _tokenOwners array of token owners - * @param _vestingAddresses array of vesting addresses - */ - function addFourYearVestings( - address[] calldata _tokenOwners, - address[] calldata _vestingAddresses - ) external onlyAuthorized { - require(_tokenOwners.length == _vestingAddresses.length, "arrays mismatch"); - uint256 vestingCreationType = 4; - uint256 cliff = 4 weeks; - uint256 duration = 156 weeks; - for (uint256 i = 0; i < _tokenOwners.length; i++) { - require(!isVesting[_vestingAddresses[i]], "vesting exists"); - require(_tokenOwners[i] != address(0), "token owner cannot be 0 address"); - require(_vestingAddresses[i] != address(0), "vesting cannot be 0 address"); - uint256 uid = uint256( - keccak256( - abi.encodePacked( - _tokenOwners[i], - uint256(VestingType.Vesting), - cliff, - duration, - vestingCreationType - ) - ) - ); - vestings[uid] = Vesting( - uint256(VestingType.Vesting), - vestingCreationType, - _vestingAddresses[i] - ); - vestingsOf[_tokenOwners[i]].push(uid); - isVesting[_vestingAddresses[i]] = true; - } - } - - /** - * @notice creates Vesting contract - * @param _tokenOwner the owner of the tokens - * @param _amount the amount to be staked - * @param _cliff the cliff in seconds - * @param _duration the total duration in seconds - * @dev Calls a public createVestingAddr function with vestingCreationType. This is to accomodate the existing logic for LockedSOV - * @dev vestingCreationType 0 = LockedSOV - */ - function createVesting( - address _tokenOwner, - uint256 _amount, - uint256 _cliff, - uint256 _duration - ) external onlyAuthorized { - createVestingAddr(_tokenOwner, _amount, _cliff, _duration, 3); - } - - /** - * @notice creates Vesting contract - * @param _tokenOwner the owner of the tokens - * @param _amount the amount to be staked - * @param _cliff the cliff in seconds - * @param _duration the total duration in seconds - * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.) - */ - function createVestingAddr( - address _tokenOwner, - uint256 _amount, - uint256 _cliff, - uint256 _duration, - uint256 _vestingCreationType - ) public onlyAuthorized { - address vesting = _getOrCreateVesting( - _tokenOwner, - _cliff, - _duration, - uint256(VestingType.Vesting), - _vestingCreationType - ); - - emit VestingCreated( - _tokenOwner, - vesting, - _cliff, - _duration, - _amount, - _vestingCreationType - ); - } - - /** - * @notice creates Team Vesting contract - * @param _tokenOwner the owner of the tokens - * @param _amount the amount to be staked - * @param _cliff the cliff in seconds - * @param _duration the total duration in seconds - * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.) - */ - function createTeamVesting( - address _tokenOwner, - uint256 _amount, - uint256 _cliff, - uint256 _duration, - uint256 _vestingCreationType - ) external onlyAuthorized { - address vesting = _getOrCreateVesting( - _tokenOwner, - _cliff, - _duration, - uint256(VestingType.TeamVesting), - _vestingCreationType - ); - - emit TeamVestingCreated( - _tokenOwner, - vesting, - _cliff, - _duration, - _amount, - _vestingCreationType - ); - } - - /** - * @notice stakes tokens according to the vesting schedule - * @param _vesting the address of Vesting contract - * @param _amount the amount of tokens to stake - */ - function stakeTokens(address _vesting, uint256 _amount) external onlyAuthorized { - require(_vesting != address(0), "vesting address invalid"); - require(_amount > 0, "amount invalid"); - - IERC20(SOV).approve(_vesting, _amount); - IVesting(_vesting).stakeTokens(_amount); - emit TokensStaked(_vesting, _amount); - } - - /** - * @notice returns vesting contract address for the given token owner - * @param _tokenOwner the owner of the tokens - * @dev Calls a public getVestingAddr function with cliff and duration. This is to accomodate the existing logic for LockedSOV - * @dev We need to use LockedSOV.changeRegistryCliffAndDuration function very judiciously - * @dev vestingCreationType 0 - LockedSOV - */ - function getVesting(address _tokenOwner) public view returns (address) { - return getVestingAddr(_tokenOwner, lockedSOV.cliff(), lockedSOV.duration(), 3); - } - - /** - * @notice public function that returns vesting contract address for the given token owner, cliff, duration - * @dev Important: Please use this instead of getVesting function - */ - function getVestingAddr( - address _tokenOwner, - uint256 _cliff, - uint256 _duration, - uint256 _vestingCreationType - ) public view returns (address) { - uint256 type_ = uint256(VestingType.Vesting); - uint256 uid = uint256( - keccak256( - abi.encodePacked(_tokenOwner, type_, _cliff, _duration, _vestingCreationType) - ) - ); - return vestings[uid].vestingAddress; - } - - /** - * @notice returns team vesting contract address for the given token owner, cliff, duration - */ - function getTeamVesting( - address _tokenOwner, - uint256 _cliff, - uint256 _duration, - uint256 _vestingCreationType - ) public view returns (address) { - uint256 type_ = uint256(VestingType.TeamVesting); - uint256 uid = uint256( - keccak256( - abi.encodePacked(_tokenOwner, type_, _cliff, _duration, _vestingCreationType) - ) - ); - return vestings[uid].vestingAddress; - } - - /** - * @dev check if the specific vesting address is team vesting or not - * @dev read the vestingType from vestingCreationAndTypes storage - * - * @param _vestingAddress address of vesting contract - * - * @return true for teamVesting, false for normal vesting - */ - function isTeamVesting(address _vestingAddress) external view returns (bool) { - return (vestingCreationAndTypes[_vestingAddress].isSet && - vestingCreationAndTypes[_vestingAddress].vestingType == - uint32(VestingType.TeamVesting)); - } - - /** - * @dev setter function to register existing vesting contract to vestingCreationAndTypes storage - * @dev need to set the function visilibty to public to support VestingCreationAndTypeDetails struct as parameter - * - * @param _vestingAddresses array of vesting address - * @param _vestingCreationAndTypes array for VestingCreationAndTypeDetails struct - */ - function registerVestingToVestingCreationAndTypes( - address[] memory _vestingAddresses, - VestingCreationAndTypeDetails[] memory _vestingCreationAndTypes - ) public onlyAuthorized { - require(_vestingAddresses.length == _vestingCreationAndTypes.length, "Unmatched length"); - for (uint256 i = 0; i < _vestingCreationAndTypes.length; i++) { - VestingCreationAndTypeDetails - memory _vestingCreationAndType = _vestingCreationAndTypes[i]; - address _vestingAddress = _vestingAddresses[i]; - - vestingCreationAndTypes[_vestingAddress] = _vestingCreationAndType; - - emit VestingCreationAndTypesSet( - _vestingAddress, - vestingCreationAndTypes[_vestingAddress] - ); - } - } - - /** - * @notice Internal function to deploy Vesting/Team Vesting contract - * @param _tokenOwner the owner of the tokens - * @param _cliff the cliff in seconds - * @param _duration the total duration in seconds - * @param _type the type of vesting - * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.) - */ - function _getOrCreateVesting( - address _tokenOwner, - uint256 _cliff, - uint256 _duration, - uint256 _type, - uint256 _vestingCreationType - ) internal returns (address) { - address vesting; - uint256 uid = uint256( - keccak256( - abi.encodePacked(_tokenOwner, _type, _cliff, _duration, _vestingCreationType) - ) - ); - if (vestings[uid].vestingAddress == address(0)) { - if (_type == 1) { - vesting = vestingFactory.deployVesting( - SOV, - staking, - _tokenOwner, - _cliff, - _duration, - feeSharingCollector, - _tokenOwner - ); - } else { - vesting = vestingFactory.deployTeamVesting( - SOV, - staking, - _tokenOwner, - _cliff, - _duration, - feeSharingCollector, - vestingOwner - ); - } - vestings[uid] = Vesting(_type, _vestingCreationType, vesting); - vestingsOf[_tokenOwner].push(uid); - isVesting[vesting] = true; - - vestingCreationAndTypes[vesting] = VestingCreationAndTypeDetails({ - isSet: true, - vestingType: uint32(_type), - vestingCreationType: uint128(_vestingCreationType) - }); - - emit VestingCreationAndTypesSet(vesting, vestingCreationAndTypes[vesting]); - } - return vestings[uid].vestingAddress; - } - - /** - * @notice stores the addresses of Vesting contracts from all three previous versions of Vesting Registry - */ - function _addDeployedVestings(address _tokenOwner, uint256 _vestingCreationType) internal { - uint256 uid; - uint256 i = _vestingCreationType - 1; - - address vestingAddress = vestingRegistries[i].getVesting(_tokenOwner); - if (vestingAddress != address(0)) { - VestingLogic vesting = VestingLogic(vestingAddress); - uid = uint256( - keccak256( - abi.encodePacked( - _tokenOwner, - uint256(VestingType.Vesting), - vesting.cliff(), - vesting.duration(), - _vestingCreationType - ) - ) - ); - vestings[uid] = Vesting( - uint256(VestingType.Vesting), - _vestingCreationType, - vestingAddress - ); - vestingsOf[_tokenOwner].push(uid); - isVesting[vestingAddress] = true; - } - - address teamVestingAddress = vestingRegistries[i].getTeamVesting(_tokenOwner); - if (teamVestingAddress != address(0)) { - VestingLogic vesting = VestingLogic(teamVestingAddress); - uid = uint256( - keccak256( - abi.encodePacked( - _tokenOwner, - uint256(VestingType.TeamVesting), - vesting.cliff(), - vesting.duration(), - _vestingCreationType - ) - ) - ); - vestings[uid] = Vesting( - uint256(VestingType.TeamVesting), - _vestingCreationType, - teamVestingAddress - ); - vestingsOf[_tokenOwner].push(uid); - isVesting[teamVestingAddress] = true; - } - } - - /** - * @notice returns all vesting details for the given token owner - */ - function getVestingsOf(address _tokenOwner) external view returns (Vesting[] memory) { - uint256[] memory vestingIds = vestingsOf[_tokenOwner]; - uint256 length = vestingIds.length; - Vesting[] memory _vestings = new Vesting[](vestingIds.length); - for (uint256 i = 0; i < length; i++) { - _vestings[i] = vestings[vestingIds[i]]; - } - return _vestings; - } - - /** - * @notice returns cliff and duration for Vesting & TeamVesting contracts - */ - function getVestingDetails( - address _vestingAddress - ) external view returns (uint256 cliff, uint256 duration) { - VestingLogic vesting = VestingLogic(_vestingAddress); - return (vesting.cliff(), vesting.duration()); - } - - /** - * @notice returns if the address is a vesting address - */ - function isVestingAddress(address _vestingAddress) external view returns (bool isVestingAddr) { - return isVesting[_vestingAddress]; - } -} diff --git a/contracts/governance/Vesting/VestingRegistryStorage.sol b/contracts/governance/Vesting/VestingRegistryStorage.sol index 8be57bbd6..001514b43 100644 --- a/contracts/governance/Vesting/VestingRegistryStorage.sol +++ b/contracts/governance/Vesting/VestingRegistryStorage.sol @@ -1,7 +1,7 @@ pragma solidity ^0.5.17; import "../../openzeppelin/Initializable.sol"; -import "../../utils/AdminRole.sol"; +import "../../utils/AdminRoleManaged.sol"; import "../../interfaces/IERC20.sol"; import "./IVestingFactory.sol"; import "../../locked/LockedSOV.sol"; @@ -11,12 +11,12 @@ import "./IVestingRegistry.sol"; * @title Vesting Registry Storage Contract. * * @notice This contract is just the storage required for vesting registry. - * It is parent of VestingRegistryProxy and VestingRegistryLogic. + * It is parent of VestingRegistryProxy and VestingRegistry. * * @dev Use Ownable as a parent to align storage structure for Logic and Proxy contracts. * */ -contract VestingRegistryStorage is Initializable, AdminRole { +contract VestingRegistryStorage is Initializable, AdminRoleManaged { ///@notice the vesting factory contract IVestingFactory public vestingFactory; diff --git a/contracts/mockup/VestingRegistryLogicMockUp.sol b/contracts/mockup/VestingRegistryMockUp.sol similarity index 81% rename from contracts/mockup/VestingRegistryLogicMockUp.sol rename to contracts/mockup/VestingRegistryMockUp.sol index 25a61464c..6d08d96a4 100644 --- a/contracts/mockup/VestingRegistryLogicMockUp.sol +++ b/contracts/mockup/VestingRegistryMockUp.sol @@ -1,8 +1,8 @@ pragma solidity ^0.5.17; pragma experimental ABIEncoderV2; -import "../governance/Vesting/VestingRegistryLogic.sol"; +import "../governance/Vesting/VestingRegistry.sol"; -contract VestingRegistryLogicMockup is VestingRegistryLogic { +contract VestingRegistryMockup is VestingRegistry { function isVestingAddress(address _vestingAddress) external view returns (bool isVestingAddr) { return true; } diff --git a/contracts/mockup/modules/IStakingModuleBlockMockup.sol b/contracts/mockup/modules/IStakingModuleBlockMockup.sol index 1c9f6c40e..9ef996db9 100644 --- a/contracts/mockup/modules/IStakingModuleBlockMockup.sol +++ b/contracts/mockup/modules/IStakingModuleBlockMockup.sol @@ -398,7 +398,7 @@ interface IStakingModuleBlockMockup { function numVestingCheckpoints(uint256 date) external view returns (uint32 checkpointsQty); ///@notice vesting registry contract PROXY address - function vestingRegistryLogic() external view returns (address); + function vestingRegistry() external view returns (address); /// @dev user => flag whether user has pauser role. function pausers(address isPauser) external view returns (bool); diff --git a/contracts/utils/AdminManagerRole.sol b/contracts/utils/AdminManagerRole.sol new file mode 100644 index 000000000..50a795a78 --- /dev/null +++ b/contracts/utils/AdminManagerRole.sol @@ -0,0 +1,65 @@ +pragma solidity 0.5.17; + +import "../openzeppelin/Ownable.sol"; + +contract AdminManagerRole is Ownable { + /// @dev user => flag whether user has adminManager role. + bytes32 private constant KEY_ADMIN_MANAGER_ROLE = keccak256("key.admin.manager.role"); + + event AdminManagerChanged( + address indexed sender, + address indexed oldAdminManager, + address indexed newAdminManager + ); + event AdminManagerRemoved(address indexed sender, address indexed removedAdminManager); + + /** + * @dev Throws if called by any account other than the owner or adminManager. + * or on our own overriding sovrynOwnable. + */ + modifier onlyOwnerOrAdminManager() { + require(isOwner() || msg.sender == getAdminManager(), "unauthorized"); + _; + } + + /** + * @notice Set new admin manager. + * @param _newAdminManager The addresses of the account to grant permissions. + * */ + function setAdminManager(address _newAdminManager) public onlyOwner { + require( + _newAdminManager != address(0), + "Use removeAdminManager function to set adminManager to 0" + ); + emit AdminManagerChanged(msg.sender, getAdminManager(), _newAdminManager); + + bytes32 key = KEY_ADMIN_MANAGER_ROLE; + assembly { + sstore(key, _newAdminManager) + } + } + + /** + * @notice Set admin manager to 0 address. + * */ + function removeAdminManager() public onlyOwner { + require(getAdminManager() != address(0), "Admin manager is not set"); + emit AdminManagerRemoved(msg.sender, getAdminManager()); + address _newAdminManager = address(0); + bytes32 key = KEY_ADMIN_MANAGER_ROLE; + assembly { + sstore(key, _newAdminManager) + } + } + + /** + * @notice Return address of the admin manager. + * @return Address of admin manager. + * */ + function getAdminManager() public view returns (address _adminManager) { + bytes32 key = KEY_ADMIN_MANAGER_ROLE; + assembly { + _adminManager := sload(key) + } + } +} diff --git a/contracts/utils/AdminRoleManaged.sol b/contracts/utils/AdminRoleManaged.sol new file mode 100644 index 000000000..be7fca46e --- /dev/null +++ b/contracts/utils/AdminRoleManaged.sol @@ -0,0 +1,39 @@ +pragma solidity 0.5.17; + +import "../openzeppelin/Ownable.sol"; +import "./AdminManagerRole.sol"; + +contract AdminRoleManaged is Ownable, AdminManagerRole { + /// @dev user => flag whether user has admin role. + mapping(address => bool) public admins; + + event AdminAdded(address admin); + event AdminRemoved(address admin); + + /** + * @dev Throws if called by any account other than the owner or admin. + * or on our own overriding sovrynOwnable. + */ + modifier onlyAuthorized() { + require(isOwner() || admins[msg.sender], "unauthorized"); + _; + } + + /** + * @notice Add account to ACL. + * @param _admin The addresses of the account to grant permissions. + * */ + function addAdmin(address _admin) public onlyOwnerOrAdminManager { + admins[_admin] = true; + emit AdminAdded(_admin); + } + + /** + * @notice Remove account from ACL. + * @param _admin The addresses of the account to revoke permissions. + * */ + function removeAdmin(address _admin) public onlyOwnerOrAdminManager { + admins[_admin] = false; + emit AdminRemoved(_admin); + } +} diff --git a/deployment/deploy/1000-deploy-VestingRegistry.js b/deployment/deploy/1000-deploy-VestingRegistry.js index 2f43346c1..6607ece75 100644 --- a/deployment/deploy/1000-deploy-VestingRegistry.js +++ b/deployment/deploy/1000-deploy-VestingRegistry.js @@ -16,7 +16,7 @@ const func = async function (hre) { log(col.bgYellow("Deploying VestingRegistry...")); await deployWithCustomProxy( deployer, - "VestingRegistryLogic", + "VestingRegistry", "VestingRegistryProxy", "VestingRegistry", undefined, diff --git a/deployment/deploy/3000-deploy-VestingRegistry.js b/deployment/deploy/3000-deploy-VestingRegistry.js new file mode 100644 index 000000000..d36b46b04 --- /dev/null +++ b/deployment/deploy/3000-deploy-VestingRegistry.js @@ -0,0 +1,25 @@ +const path = require("path"); +const { getContractNameFromScriptFileName } = require("../helpers/utils"); +const { sendWithMultisig } = require("../helpers/helpers"); +const { deployWithCustomProxy } = require("../helpers/helpers"); +const col = require("cli-color"); +//const deploymentName = getContractNameFromScriptFileName(path.basename(__filename)); +const func = async function (hre) { + const { + deployments: { deploy, get, log }, + getNamedAccounts, + ethers, + } = hre; + const { deployer } = await getNamedAccounts(); + log(col.bgYellow("Deploying VestingRegistry...")); + await deployWithCustomProxy( + deployer, + "VestingRegistry", + "VestingRegistryProxy", + "VestingRegistry", + undefined, + true + ); +}; +func.tags = ["VestingRegistry"]; +module.exports = func; diff --git a/deployment/deployments/rskSovrynTestnet/VestingRegistry.json b/deployment/deployments/rskSovrynTestnet/VestingRegistry.json index 55ae32222..232fcc7e2 100644 --- a/deployment/deployments/rskSovrynTestnet/VestingRegistry.json +++ b/deployment/deployments/rskSovrynTestnet/VestingRegistry.json @@ -14,6 +14,50 @@ "name": "AdminAdded", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "oldAdminManager", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAdminManager", + "type": "address" + } + ], + "name": "AdminManagerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "removedAdminManager", + "type": "address" + } + ], + "name": "AdminManagerRemoved", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -412,6 +456,21 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "getAdminManager", + "outputs": [ + { + "internalType": "address", + "name": "_adminManager", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -774,6 +833,30 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "constant": false, + "inputs": [], + "name": "removeAdminManager", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_newAdminManager", + "type": "address" + } + ], + "name": "setAdminManager", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, { "constant": false, "inputs": [ @@ -999,10 +1082,10 @@ "type": "function" } ], - "numDeployments": 1, - "bytecode": "0x608060405260006100176001600160e01b0361007016565b6000805462010000600160b01b031916620100006001600160a01b038416908102919091178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350610074565b3390565b613077806100836000396000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c80638da5cb5b1161011a578063c680c0b7116100ad578063efb957331161007c578063efb957331461045b578063f2f46b3b1461047b578063f2fde38b14610483578063f421ed7e14610496578063f60826ee146104a957610206565b8063c680c0b714610402578063ca210d8c14610415578063cc49ede714610428578063dfb9366d1461043b57610206565b8063bc84f9ca116100e9578063bc84f9ca146103c1578063bd7b5908146103d4578063c0e09852146103dc578063c36519d1146103ef57610206565b80638da5cb5b1461037d5780638dedf009146103855780638f32d59b146103a6578063b810c648146103ae57610206565b80634cf088d91161019d57806379a83f5a1161016c57806379a83f5a146102f357806380fcb99214610306578063821bee7314610328578063842a49d51461034a578063862e229d1461036a57610206565b80634cf088d9146102bd5780636b7dbb2d146102c557806370480275146102cd578063786931da146102e057610206565b80631f509326116101d95780631f50932614610264578063377220fd14610277578063429b62e51461028a57806342a82b4f146102aa57610206565b806302df04761461020b5780630665a06f1461023457806308dcb360146102495780631785f53c14610251575b600080fd5b61021e61021936600461243f565b6104b1565b60405161022b9190612c3a565b60405180910390f35b61024761024236600461243f565b610512565b005b61021e61056d565b61024761025f366004612311565b61057c565b6102476102723660046124a0565b6105fb565b610247610285366004612311565b61069d565b61029d610298366004612311565b6106cd565b60405161022b9190612d2f565b6102476102b836600461234d565b6106e2565b61021e610951565b61021e610960565b6102476102db366004612311565b61096f565b6102476102ee366004612515565b6109e8565b610247610301366004612405565b610cd7565b610319610314366004612311565b610e83565b60405161022b93929190612d3d565b61033b610336366004612602565b610eb8565b60405161022b93929190612edd565b61035d610358366004612602565b610ee2565b60405161022b9190612d65565b6102476103783660046124a0565b610f09565b61021e610f98565b610398610393366004612311565b610fad565b60405161022b929190612ecf565b61029d6110a1565b6102476103bc366004612515565b6110cb565b6102476103cf366004612585565b6111db565b61021e61133b565b61029d6103ea366004612311565b61134a565b61021e6103fd36600461243f565b61135f565b610247610410366004612405565b611368565b61029d610423366004612311565b6114a8565b61021e610436366004612311565b6114c6565b61044e610449366004612405565b6115e5565b60405161022b9190612ec1565b61046e610469366004612311565b611613565b60405161022b9190612d1e565b61035d61175d565b610247610491366004612311565b61176c565b61029d6104a4366004612311565b611799565b61035d6117e4565b60008060015b9050600086828787876040516020016104d4959493929190612be1565b60408051601f198184030181529181528151602092830120600090815260099092529020600201546001600160a01b0316925050505b949350505050565b61051a6110a1565b8061053457503360009081526001602052604090205460ff165b6105595760405162461bcd60e51b815260040161055090612e23565b60405180910390fd5b6105678484848460036105fb565b50505050565b6005546001600160a01b031681565b6105846110a1565b6105a05760405162461bcd60e51b815260040161055090612e23565b6001600160a01b03811660009081526001602052604090819020805460ff19169055517fa3b62bc36326052d97ea62d63c3d60308ed4c3ea8ac079dd8499f1e9c4f80c0f906105f0908390612c3a565b60405180910390a150565b6106036110a1565b8061061d57503360009081526001602052604090205460ff165b6106395760405162461bcd60e51b815260040161055090612e23565b600061064a86858560015b866117f3565b9050856001600160a01b03167fd6fcfd83804f6b6b63260c7b99eb10060bc1319dbc9177fb6defc7bd614017bf828686898760405161068d959493929190612cd2565b60405180910390a2505050505050565b6106a56110a1565b6106c15760405162461bcd60e51b815260040161055090612e23565b6106ca81611b5b565b50565b60016020526000908152604090205460ff1681565b6106ea6110a1565b6107065760405162461bcd60e51b815260040161055090612e23565b600054610100900460ff168061071f575060005460ff16155b61073b5760405162461bcd60e51b815260040161055090612e13565b600054610100900460ff16158015610766576000805460ff1961ff0019909116610100171660011790555b6001600160a01b03881661078c5760405162461bcd60e51b815260040161055090612ea3565b6001600160a01b0387166107b25760405162461bcd60e51b815260040161055090612e83565b6001600160a01b0386166107d85760405162461bcd60e51b815260040161055090612e33565b6001600160a01b0385166107fe5760405162461bcd60e51b815260040161055090612e03565b6001600160a01b0384166108245760405162461bcd60e51b815260040161055090612e73565b61082d89611b5b565b600580546001600160a01b03199081166001600160a01b038b8116919091179092556006805482168a84161790556007805482168984161790556008805482168884161790556003805490911691861691909117905560005b8281101561093357600084848381811061089c57fe5b90506020020160206108b19190810190612311565b6001600160a01b031614156108d85760405162461bcd60e51b815260040161055090612d73565b60048484838181106108e657fe5b90506020020160206108fb9190810190612311565b815460018082018455600093845260209093200180546001600160a01b0319166001600160a01b039290921691909117905501610886565b508015610946576000805461ff00191690555b505050505050505050565b6006546001600160a01b031681565b6007546001600160a01b031681565b6109776110a1565b6109935760405162461bcd60e51b815260040161055090612e23565b6001600160a01b038116600090815260016020819052604091829020805460ff19169091179055517f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e339906105f0908390612c3a565b6109f06110a1565b80610a0a57503360009081526001602052604090205460ff165b610a265760405162461bcd60e51b815260040161055090612e23565b828114610a455760405162461bcd60e51b815260040161055090612d83565b60046224ea0063059fa60060005b86811015610ccd57600b6000878784818110610a6b57fe5b9050602002016020610a809190810190612311565b6001600160a01b0316815260208101919091526040016000205460ff1615610aba5760405162461bcd60e51b815260040161055090612df3565b6000888883818110610ac857fe5b9050602002016020610add9190810190612311565b6001600160a01b03161415610b045760405162461bcd60e51b815260040161055090612d93565b6000868683818110610b1257fe5b9050602002016020610b279190810190612311565b6001600160a01b03161415610b4e5760405162461bcd60e51b815260040161055090612da3565b6000888883818110610b5c57fe5b9050602002016020610b719190810190612311565b6001858588604051602001610b8a959493929190612be1565b60408051808303601f1901815282825280516020918201206060840183526001845290830188905292508101888885818110610bc257fe5b9050602002016020610bd79190810190612311565b6001600160a01b039081169091526000838152600960209081526040808320855181559185015160018301559390930151600290930180546001600160a01b03191693909216929092179055600a908a8a85818110610c3257fe5b9050602002016020610c479190810190612311565b6001600160a01b031681526020808201929092526040016000908120805460018181018355918352928220909201839055600b90898986818110610c8757fe5b9050602002016020610c9c9190810190612311565b6001600160a01b031681526020810191909152604001600020805460ff191691151591909117905550600101610a53565b5050505050505050565b610cdf6110a1565b80610cf957503360009081526001602052604090205460ff165b610d155760405162461bcd60e51b815260040161055090612e23565b6001600160a01b038216610d3b5760405162461bcd60e51b815260040161055090612de3565b60008111610d5b5760405162461bcd60e51b815260040161055090612dd3565b60055460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390610d8d9085908590600401612cb0565b602060405180830381600087803b158015610da757600080fd5b505af1158015610dbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ddf91908101906125e4565b50604051637547c7a360e01b81526001600160a01b03831690637547c7a390610e0c908490600401612ec1565b600060405180830381600087803b158015610e2657600080fd5b505af1158015610e3a573d6000803e3d6000fd5b50505050816001600160a01b03167fb539ca1e5c8d398ddf1c41c30166f33404941683be4683319b57669a93dad4ef82604051610e779190612ec1565b60405180910390a25050565b600c6020526000908152604090205460ff811690610100810463ffffffff16906501000000000090046001600160801b031683565b6009602052600090815260409020805460018201546002909201549091906001600160a01b031683565b60048181548110610eef57fe5b6000918252602090912001546001600160a01b0316905081565b610f116110a1565b80610f2b57503360009081526001602052604090205460ff165b610f475760405162461bcd60e51b815260040161055090612e23565b6000610f5586858584610644565b9050856001600160a01b03167f3791c6c90c276d011b4b885c0bfba0554342acf50a539baca1b06f070af25ff4828686898760405161068d959493929190612cd2565b6000546201000090046001600160a01b031690565b6000806000839050806001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015610fee57600080fd5b505afa158015611002573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110269190810190612620565b816001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561105f57600080fd5b505afa158015611073573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110979190810190612620565b9250925050915091565b600080546201000090046001600160a01b03166110bc611ba3565b6001600160a01b031614905090565b6110d36110a1565b806110ed57503360009081526001602052604090205460ff165b6111095760405162461bcd60e51b815260040161055090612e23565b60005b838110156111d457600085858381811061112257fe5b90506020020160206111379190810190612311565b6001600160a01b0316141561115e5760405162461bcd60e51b815260040161055090612d93565b600083838381811061116c57fe5b90506020020135116111905760405162461bcd60e51b815260040161055090612e43565b6111cc85858381811061119f57fe5b90506020020160206111b49190810190612311565b8484848181106111c057fe5b90506020020135611ba7565b60010161110c565b5050505050565b6111e36110a1565b806111fd57503360009081526001602052604090205460ff165b6112195760405162461bcd60e51b815260040161055090612e23565b805182511461123a5760405162461bcd60e51b815260040161055090612e63565b60005b81518110156113365761124e6120da565b82828151811061125a57fe5b60200260200101519050600084838151811061127257fe5b6020908102919091018101516001600160a01b0381166000818152600c845260409081902086518154958801518884015160ff199097169115159190911764ffffffff00191661010063ffffffff909216919091021765010000000000600160a81b031916650100000000006001600160801b039096169590950294909417845551919350917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca34916113249190612eb3565b60405180910390a2505060010161123d565b505050565b6008546001600160a01b031681565b600b6020526000908152604090205460ff1681565b600080806104b7565b6113706110a1565b61138c5760405162461bcd60e51b815260040161055090612e23565b6001600160a01b0382166113b25760405162461bcd60e51b815260040161055090612e53565b806113cf5760405162461bcd60e51b815260040161055090612dd3565b60055460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906114019085908590600401612cb0565b602060405180830381600087803b15801561141b57600080fd5b505af115801561142f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061145391908101906125e4565b61146f5760405162461bcd60e51b815260040161055090612e93565b816001600160a01b03167fe1b0ada289bf82fb641c8c1e1c78f820fae44e8f845760725cdeb09167a289ad82604051610e779190612ec1565b6001600160a01b03166000908152600b602052604090205460ff1690565b60006115df82600360009054906101000a90046001600160a01b03166001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561151a57600080fd5b505afa15801561152e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115529190810190612620565b600360009054906101000a90046001600160a01b03166001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156115a057600080fd5b505afa1580156115b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115d89190810190612620565b60036104b1565b92915050565b600a60205281600052604060002081815481106115fe57fe5b90600052602060002001600091509150505481565b606080600a6000846001600160a01b03166001600160a01b0316815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561168557602002820191906000526020600020905b815481526020019060010190808311611671575b50505050509050600081519050606082516040519080825280602002602001820160405280156116cf57816020015b6116bc6120fa565b8152602001906001900390816116b45790505b50905060005b8281101561175457600960008583815181106116ed57fe5b6020908102919091018101518252818101929092526040908101600020815160608101835281548152600182015493810193909352600201546001600160a01b031690820152825183908390811061174157fe5b60209081029190910101526001016116d5565b50949350505050565b6003546001600160a01b031681565b6117746110a1565b6117905760405162461bcd60e51b815260040161055090612e23565b6106ca8161204b565b6001600160a01b0381166000908152600c602052604081205460ff1680156115df5750506001600160a01b03166000908152600c6020526040902054610100900463ffffffff161590565b6002546001600160a01b031681565b60008060008785888887604051602001611811959493929190612be1565b60408051601f198184030181529181528151602092830120600081815260099093529120600201549091506001600160a01b0316611b355784600114156118f557600254600554600654600754604051637d2fbb8f60e11b81526001600160a01b039485169463fa5f771e9461189c9490821693908216928f928f928f929116908490600401612c48565b602060405180830381600087803b1580156118b657600080fd5b505af11580156118ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ee919081019061232f565b91506119eb565b600260009054906101000a90046001600160a01b03166001600160a01b031663546344f0600560009054906101000a90046001600160a01b0316600660009054906101000a90046001600160a01b03168b8b8b600760009054906101000a90046001600160a01b0316600860009054906101000a90046001600160a01b03166040518863ffffffff1660e01b81526004016119969796959493929190612c48565b602060405180830381600087803b1580156119b057600080fd5b505af11580156119c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119e8919081019061232f565b91505b604080516060808201835287825260208083018881526001600160a01b038088168587018181526000898152600986528881209751885593516001808901919091559051600290970180549784166001600160a01b031990981697909717909655908e168252600a83528582208054808701825590835283832001879055808252600b8352858220805460ff1990811687179091558651948501875294845263ffffffff808c168585019081526001600160801b03808d16878a01908152848652600c909652938890209551865491519551909416650100000000000265010000000000600160a81b0319959092166101000264ffffffff00199415159190971617929092169490941791909116178155915190917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca3491611b2c9190612eb3565b60405180910390a25b6000908152600960205260409020600201546001600160a01b0316979650505050505050565b6001600160a01b038116611b815760405162461bcd60e51b815260040161055090612dc3565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b600080600183039050600060048281548110611bbf57fe5b60009182526020909120015460405163cc49ede760e01b81526001600160a01b039091169063cc49ede790611bf8908890600401612c3a565b60206040518083038186803b158015611c1057600080fd5b505afa158015611c24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c48919081019061232f565b90506001600160a01b03811615611df85780856001826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611c9657600080fd5b505afa158015611caa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611cce9190810190612620565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611d0757600080fd5b505afa158015611d1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d3f9190810190612620565b88604051602001611d54959493929190612be1565b60408051601f19818403018152828252805160209182012060608401835260018085528285018a81526001600160a01b0388811687870181815260008681526009885288812099518a5593518986015551600290980180546001600160a01b031916988316989098179097558c168152600a84528481208054808401825590825284822001839055948552600b90925291909220805460ff19169092179091559350505b600060048381548110611e0757fe5b60009182526020909120015460405163c810a3e360e01b81526001600160a01b039091169063c810a3e390611e40908990600401612c3a565b60206040518083038186803b158015611e5857600080fd5b505afa158015611e6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e90919081019061232f565b90506001600160a01b038116156120435780866000826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611ede57600080fd5b505afa158015611ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f169190810190612620565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611f4f57600080fd5b505afa158015611f63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f879190810190612620565b89604051602001611f9c959493929190612be1565b60408051601f19818403018152828252805160209182012060608401835260008085528285018b81526001600160a01b03888116878701818152858552600987528785209851895592516001808a01919091559251600290980180546001600160a01b031916988316989098179097558d168252600a84528482208054808301825590835284832001839055948152600b909252919020805460ff19169092179091559450505b505050505050565b6001600160a01b0381166120715760405162461bcd60e51b815260040161055090612db3565b600080546040516001600160a01b03808516936201000090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b604080516060810182526000808252602082018190529181019190915290565b6040518060600160405280600081526020016000815260200160006001600160a01b031681525090565b80356115df81612ffc565b80516115df81612ffc565b60008083601f84011261214c57600080fd5b50813567ffffffffffffffff81111561216457600080fd5b60208301915083602082028301111561217c57600080fd5b9250929050565b600082601f83011261219457600080fd5b81356121a76121a282612f2c565b612f05565b915081818352602084019350602081019050838560208402820111156121cc57600080fd5b60005b838110156121f857816121e28882612124565b84525060209283019291909101906001016121cf565b5050505092915050565b600082601f83011261221357600080fd5b81356122216121a282612f2c565b9150818183526020840193506020810190508385606084028201111561224657600080fd5b60005b838110156121f8578161225c888261228a565b84525060209092019160609190910190600101612249565b80356115df81613010565b80516115df81613010565b60006060828403121561229c57600080fd5b6122a66060612f05565b905060006122b48484612274565b82525060206122c584848301612306565b60208301525060406122d9848285016122e5565b60408301525092915050565b80356115df81613019565b80356115df81613022565b80516115df81613022565b80356115df8161302b565b60006020828403121561232357600080fd5b600061050a8484612124565b60006020828403121561234157600080fd5b600061050a848461212f565b60008060008060008060008060e0898b03121561236957600080fd5b60006123758b8b612124565b98505060206123868b828c01612124565b97505060406123978b828c01612124565b96505060606123a88b828c01612124565b95505060806123b98b828c01612124565b94505060a06123ca8b828c01612124565b93505060c089013567ffffffffffffffff8111156123e757600080fd5b6123f38b828c0161213a565b92509250509295985092959890939650565b6000806040838503121561241857600080fd5b60006124248585612124565b9250506020612435858286016122f0565b9150509250929050565b6000806000806080858703121561245557600080fd5b60006124618787612124565b9450506020612472878288016122f0565b9350506040612483878288016122f0565b9250506060612494878288016122f0565b91505092959194509250565b600080600080600060a086880312156124b857600080fd5b60006124c48888612124565b95505060206124d5888289016122f0565b94505060406124e6888289016122f0565b93505060606124f7888289016122f0565b9250506080612508888289016122f0565b9150509295509295909350565b6000806000806040858703121561252b57600080fd5b843567ffffffffffffffff81111561254257600080fd5b61254e8782880161213a565b9450945050602085013567ffffffffffffffff81111561256d57600080fd5b6125798782880161213a565b95989497509550505050565b6000806040838503121561259857600080fd5b823567ffffffffffffffff8111156125af57600080fd5b6125bb85828601612183565b925050602083013567ffffffffffffffff8111156125d857600080fd5b61243585828601612202565b6000602082840312156125f657600080fd5b600061050a848461227f565b60006020828403121561261457600080fd5b600061050a84846122f0565b60006020828403121561263257600080fd5b600061050a84846122fb565b600061264a8383612b7e565b505060600190565b61265b81612f7b565b82525050565b61265b61266d82612f7b565b612fde565b600061267d82612f53565b6126878185612f57565b935061269283612f4d565b8060005b838110156126c05781516126aa888261263e565b97506126b583612f4d565b925050600101612696565b509495945050505050565b61265b81612f86565b61265b81612f9a565b60006126ea602083612f57565b7f56657374696e67207265676973747279206164647265737320696e76616c6964815260200192915050565b6000612723600f83612f57565b6e0c2e4e4c2f2e640dad2e6dac2e8c6d608b1b815260200192915050565b600061274e601f83612f57565b7f746f6b656e206f776e65722063616e6e6f742062652030206164647265737300815260200192915050565b6000612787601b83612f57565b7f76657374696e672063616e6e6f74206265203020616464726573730000000000815260200192915050565b60006127c0602683612f57565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b6000612808601e83612f57565b7f76657374696e67466163746f7279206164647265737320696e76616c69640000815260200192915050565b6000612841600e83612f57565b6d185b5bdd5b9d081a5b9d985b1a5960921b815260200192915050565b600061286b601783612f57565b7f76657374696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b60006128a4600e83612f57565b6d76657374696e672065786973747360901b815260200192915050565b60006128ce601c83612f57565b7f76657374696e674f776e6572206164647265737320696e76616c696400000000815260200192915050565b6000612907602e83612f57565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656181526d191e481a5b9a5d1a585b1a5e995960921b602082015260400192915050565b6000612957600c83612f57565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b600061297f602383612f57565b7f66656553686172696e67436f6c6c6563746f72206164647265737320696e76618152621b1a5960ea1b602082015260400192915050565b60006129c4602c83612f57565b7f76657374696e67206372656174696f6e2074797065206d75737420626520677281526b06561746572207468616e20360a41b602082015260400192915050565b6000612a12601883612f57565b7f7265636569766572206164647265737320696e76616c69640000000000000000815260200192915050565b6000612a4b601083612f57565b6f0aadcdac2e8c6d0cac840d8cadccee8d60831b815260200192915050565b6000612a77601983612f57565b7f4c6f636b6564534f56206164647265737320696e76616c696400000000000000815260200192915050565b6000612ab0601783612f57565b7f7374616b696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612ae9600f83612f57565b6e1d1c985b9cd9995c8819985a5b1959608a1b815260200192915050565b6000612b14601383612f57565b7214d3d5881859191c995cdcc81a5b9d985b1a59606a1b815260200192915050565b80546060830190612b4681612fa5565b612b5085826126cb565b50612b5a81612fb8565b612b676020860182612bd8565b50612b7181612fcb565b6111d46040860182612bb5565b80516060830190612b8f8482612bbe565b506020820151612ba26020850182612bbe565b5060408201516105676040850182612652565b61265b81612f66565b61265b81612f97565b61265b612bd382612f97565b612f97565b61265b81612f72565b6000612bed8288612661565b601482019150612bfd8287612bc7565b602082019150612c0d8286612bc7565b602082019150612c1d8285612bc7565b602082019150612c2d8284612bc7565b5060200195945050505050565b602081016115df8284612652565b60e08101612c56828a612652565b612c636020830189612652565b612c706040830188612652565b612c7d6060830187612bbe565b612c8a6080830186612bbe565b612c9760a0830185612652565b612ca460c0830184612652565b98975050505050505050565b60408101612cbe8285612652565b612ccb6020830184612bbe565b9392505050565b60a08101612ce08288612652565b612ced6020830187612bbe565b612cfa6040830186612bbe565b612d076060830185612bbe565b612d146080830184612bbe565b9695505050505050565b60208082528101612ccb8184612672565b602081016115df82846126cb565b60608101612d4b82866126cb565b612d586020830185612bd8565b61050a6040830184612bb5565b602081016115df82846126d4565b602080825281016115df816126dd565b602080825281016115df81612716565b602080825281016115df81612741565b602080825281016115df8161277a565b602080825281016115df816127b3565b602080825281016115df816127fb565b602080825281016115df81612834565b602080825281016115df8161285e565b602080825281016115df81612897565b602080825281016115df816128c1565b602080825281016115df816128fa565b602080825281016115df8161294a565b602080825281016115df81612972565b602080825281016115df816129b7565b602080825281016115df81612a05565b602080825281016115df81612a3e565b602080825281016115df81612a6a565b602080825281016115df81612aa3565b602080825281016115df81612adc565b602080825281016115df81612b07565b606081016115df8284612b36565b602081016115df8284612bbe565b60408101612cbe8285612bbe565b60608101612eeb8286612bbe565b612ef86020830185612bbe565b61050a6040830184612652565b60405181810167ffffffffffffffff81118282101715612f2457600080fd5b604052919050565b600067ffffffffffffffff821115612f4357600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b60ff1690565b6001600160801b031690565b63ffffffff1690565b60006115df82612f8b565b151590565b6001600160a01b031690565b90565b60006115df82612f7b565b60006115df612fb383612f97565b612f60565b60006115df612fc683612ff6565b612f72565b60006115df612fd983612ff0565b612f66565b60006115df8260006115df8260601b90565b60281c90565b60081c90565b61300581612f7b565b81146106ca57600080fd5b61300581612f86565b61300581612f66565b61300581612f97565b61300581612f7256fea365627a7a723158201d7444ad20959aa55436fb5bdba711b0369fe69491b25c92fb66c7287a486a1b6c6578706572696d656e74616cf564736f6c63430005110040", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102065760003560e01c80638da5cb5b1161011a578063c680c0b7116100ad578063efb957331161007c578063efb957331461045b578063f2f46b3b1461047b578063f2fde38b14610483578063f421ed7e14610496578063f60826ee146104a957610206565b8063c680c0b714610402578063ca210d8c14610415578063cc49ede714610428578063dfb9366d1461043b57610206565b8063bc84f9ca116100e9578063bc84f9ca146103c1578063bd7b5908146103d4578063c0e09852146103dc578063c36519d1146103ef57610206565b80638da5cb5b1461037d5780638dedf009146103855780638f32d59b146103a6578063b810c648146103ae57610206565b80634cf088d91161019d57806379a83f5a1161016c57806379a83f5a146102f357806380fcb99214610306578063821bee7314610328578063842a49d51461034a578063862e229d1461036a57610206565b80634cf088d9146102bd5780636b7dbb2d146102c557806370480275146102cd578063786931da146102e057610206565b80631f509326116101d95780631f50932614610264578063377220fd14610277578063429b62e51461028a57806342a82b4f146102aa57610206565b806302df04761461020b5780630665a06f1461023457806308dcb360146102495780631785f53c14610251575b600080fd5b61021e61021936600461243f565b6104b1565b60405161022b9190612c3a565b60405180910390f35b61024761024236600461243f565b610512565b005b61021e61056d565b61024761025f366004612311565b61057c565b6102476102723660046124a0565b6105fb565b610247610285366004612311565b61069d565b61029d610298366004612311565b6106cd565b60405161022b9190612d2f565b6102476102b836600461234d565b6106e2565b61021e610951565b61021e610960565b6102476102db366004612311565b61096f565b6102476102ee366004612515565b6109e8565b610247610301366004612405565b610cd7565b610319610314366004612311565b610e83565b60405161022b93929190612d3d565b61033b610336366004612602565b610eb8565b60405161022b93929190612edd565b61035d610358366004612602565b610ee2565b60405161022b9190612d65565b6102476103783660046124a0565b610f09565b61021e610f98565b610398610393366004612311565b610fad565b60405161022b929190612ecf565b61029d6110a1565b6102476103bc366004612515565b6110cb565b6102476103cf366004612585565b6111db565b61021e61133b565b61029d6103ea366004612311565b61134a565b61021e6103fd36600461243f565b61135f565b610247610410366004612405565b611368565b61029d610423366004612311565b6114a8565b61021e610436366004612311565b6114c6565b61044e610449366004612405565b6115e5565b60405161022b9190612ec1565b61046e610469366004612311565b611613565b60405161022b9190612d1e565b61035d61175d565b610247610491366004612311565b61176c565b61029d6104a4366004612311565b611799565b61035d6117e4565b60008060015b9050600086828787876040516020016104d4959493929190612be1565b60408051601f198184030181529181528151602092830120600090815260099092529020600201546001600160a01b0316925050505b949350505050565b61051a6110a1565b8061053457503360009081526001602052604090205460ff165b6105595760405162461bcd60e51b815260040161055090612e23565b60405180910390fd5b6105678484848460036105fb565b50505050565b6005546001600160a01b031681565b6105846110a1565b6105a05760405162461bcd60e51b815260040161055090612e23565b6001600160a01b03811660009081526001602052604090819020805460ff19169055517fa3b62bc36326052d97ea62d63c3d60308ed4c3ea8ac079dd8499f1e9c4f80c0f906105f0908390612c3a565b60405180910390a150565b6106036110a1565b8061061d57503360009081526001602052604090205460ff165b6106395760405162461bcd60e51b815260040161055090612e23565b600061064a86858560015b866117f3565b9050856001600160a01b03167fd6fcfd83804f6b6b63260c7b99eb10060bc1319dbc9177fb6defc7bd614017bf828686898760405161068d959493929190612cd2565b60405180910390a2505050505050565b6106a56110a1565b6106c15760405162461bcd60e51b815260040161055090612e23565b6106ca81611b5b565b50565b60016020526000908152604090205460ff1681565b6106ea6110a1565b6107065760405162461bcd60e51b815260040161055090612e23565b600054610100900460ff168061071f575060005460ff16155b61073b5760405162461bcd60e51b815260040161055090612e13565b600054610100900460ff16158015610766576000805460ff1961ff0019909116610100171660011790555b6001600160a01b03881661078c5760405162461bcd60e51b815260040161055090612ea3565b6001600160a01b0387166107b25760405162461bcd60e51b815260040161055090612e83565b6001600160a01b0386166107d85760405162461bcd60e51b815260040161055090612e33565b6001600160a01b0385166107fe5760405162461bcd60e51b815260040161055090612e03565b6001600160a01b0384166108245760405162461bcd60e51b815260040161055090612e73565b61082d89611b5b565b600580546001600160a01b03199081166001600160a01b038b8116919091179092556006805482168a84161790556007805482168984161790556008805482168884161790556003805490911691861691909117905560005b8281101561093357600084848381811061089c57fe5b90506020020160206108b19190810190612311565b6001600160a01b031614156108d85760405162461bcd60e51b815260040161055090612d73565b60048484838181106108e657fe5b90506020020160206108fb9190810190612311565b815460018082018455600093845260209093200180546001600160a01b0319166001600160a01b039290921691909117905501610886565b508015610946576000805461ff00191690555b505050505050505050565b6006546001600160a01b031681565b6007546001600160a01b031681565b6109776110a1565b6109935760405162461bcd60e51b815260040161055090612e23565b6001600160a01b038116600090815260016020819052604091829020805460ff19169091179055517f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e339906105f0908390612c3a565b6109f06110a1565b80610a0a57503360009081526001602052604090205460ff165b610a265760405162461bcd60e51b815260040161055090612e23565b828114610a455760405162461bcd60e51b815260040161055090612d83565b60046224ea0063059fa60060005b86811015610ccd57600b6000878784818110610a6b57fe5b9050602002016020610a809190810190612311565b6001600160a01b0316815260208101919091526040016000205460ff1615610aba5760405162461bcd60e51b815260040161055090612df3565b6000888883818110610ac857fe5b9050602002016020610add9190810190612311565b6001600160a01b03161415610b045760405162461bcd60e51b815260040161055090612d93565b6000868683818110610b1257fe5b9050602002016020610b279190810190612311565b6001600160a01b03161415610b4e5760405162461bcd60e51b815260040161055090612da3565b6000888883818110610b5c57fe5b9050602002016020610b719190810190612311565b6001858588604051602001610b8a959493929190612be1565b60408051808303601f1901815282825280516020918201206060840183526001845290830188905292508101888885818110610bc257fe5b9050602002016020610bd79190810190612311565b6001600160a01b039081169091526000838152600960209081526040808320855181559185015160018301559390930151600290930180546001600160a01b03191693909216929092179055600a908a8a85818110610c3257fe5b9050602002016020610c479190810190612311565b6001600160a01b031681526020808201929092526040016000908120805460018181018355918352928220909201839055600b90898986818110610c8757fe5b9050602002016020610c9c9190810190612311565b6001600160a01b031681526020810191909152604001600020805460ff191691151591909117905550600101610a53565b5050505050505050565b610cdf6110a1565b80610cf957503360009081526001602052604090205460ff165b610d155760405162461bcd60e51b815260040161055090612e23565b6001600160a01b038216610d3b5760405162461bcd60e51b815260040161055090612de3565b60008111610d5b5760405162461bcd60e51b815260040161055090612dd3565b60055460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390610d8d9085908590600401612cb0565b602060405180830381600087803b158015610da757600080fd5b505af1158015610dbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ddf91908101906125e4565b50604051637547c7a360e01b81526001600160a01b03831690637547c7a390610e0c908490600401612ec1565b600060405180830381600087803b158015610e2657600080fd5b505af1158015610e3a573d6000803e3d6000fd5b50505050816001600160a01b03167fb539ca1e5c8d398ddf1c41c30166f33404941683be4683319b57669a93dad4ef82604051610e779190612ec1565b60405180910390a25050565b600c6020526000908152604090205460ff811690610100810463ffffffff16906501000000000090046001600160801b031683565b6009602052600090815260409020805460018201546002909201549091906001600160a01b031683565b60048181548110610eef57fe5b6000918252602090912001546001600160a01b0316905081565b610f116110a1565b80610f2b57503360009081526001602052604090205460ff165b610f475760405162461bcd60e51b815260040161055090612e23565b6000610f5586858584610644565b9050856001600160a01b03167f3791c6c90c276d011b4b885c0bfba0554342acf50a539baca1b06f070af25ff4828686898760405161068d959493929190612cd2565b6000546201000090046001600160a01b031690565b6000806000839050806001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015610fee57600080fd5b505afa158015611002573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110269190810190612620565b816001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561105f57600080fd5b505afa158015611073573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110979190810190612620565b9250925050915091565b600080546201000090046001600160a01b03166110bc611ba3565b6001600160a01b031614905090565b6110d36110a1565b806110ed57503360009081526001602052604090205460ff165b6111095760405162461bcd60e51b815260040161055090612e23565b60005b838110156111d457600085858381811061112257fe5b90506020020160206111379190810190612311565b6001600160a01b0316141561115e5760405162461bcd60e51b815260040161055090612d93565b600083838381811061116c57fe5b90506020020135116111905760405162461bcd60e51b815260040161055090612e43565b6111cc85858381811061119f57fe5b90506020020160206111b49190810190612311565b8484848181106111c057fe5b90506020020135611ba7565b60010161110c565b5050505050565b6111e36110a1565b806111fd57503360009081526001602052604090205460ff165b6112195760405162461bcd60e51b815260040161055090612e23565b805182511461123a5760405162461bcd60e51b815260040161055090612e63565b60005b81518110156113365761124e6120da565b82828151811061125a57fe5b60200260200101519050600084838151811061127257fe5b6020908102919091018101516001600160a01b0381166000818152600c845260409081902086518154958801518884015160ff199097169115159190911764ffffffff00191661010063ffffffff909216919091021765010000000000600160a81b031916650100000000006001600160801b039096169590950294909417845551919350917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca34916113249190612eb3565b60405180910390a2505060010161123d565b505050565b6008546001600160a01b031681565b600b6020526000908152604090205460ff1681565b600080806104b7565b6113706110a1565b61138c5760405162461bcd60e51b815260040161055090612e23565b6001600160a01b0382166113b25760405162461bcd60e51b815260040161055090612e53565b806113cf5760405162461bcd60e51b815260040161055090612dd3565b60055460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906114019085908590600401612cb0565b602060405180830381600087803b15801561141b57600080fd5b505af115801561142f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061145391908101906125e4565b61146f5760405162461bcd60e51b815260040161055090612e93565b816001600160a01b03167fe1b0ada289bf82fb641c8c1e1c78f820fae44e8f845760725cdeb09167a289ad82604051610e779190612ec1565b6001600160a01b03166000908152600b602052604090205460ff1690565b60006115df82600360009054906101000a90046001600160a01b03166001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561151a57600080fd5b505afa15801561152e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115529190810190612620565b600360009054906101000a90046001600160a01b03166001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156115a057600080fd5b505afa1580156115b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115d89190810190612620565b60036104b1565b92915050565b600a60205281600052604060002081815481106115fe57fe5b90600052602060002001600091509150505481565b606080600a6000846001600160a01b03166001600160a01b0316815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561168557602002820191906000526020600020905b815481526020019060010190808311611671575b50505050509050600081519050606082516040519080825280602002602001820160405280156116cf57816020015b6116bc6120fa565b8152602001906001900390816116b45790505b50905060005b8281101561175457600960008583815181106116ed57fe5b6020908102919091018101518252818101929092526040908101600020815160608101835281548152600182015493810193909352600201546001600160a01b031690820152825183908390811061174157fe5b60209081029190910101526001016116d5565b50949350505050565b6003546001600160a01b031681565b6117746110a1565b6117905760405162461bcd60e51b815260040161055090612e23565b6106ca8161204b565b6001600160a01b0381166000908152600c602052604081205460ff1680156115df5750506001600160a01b03166000908152600c6020526040902054610100900463ffffffff161590565b6002546001600160a01b031681565b60008060008785888887604051602001611811959493929190612be1565b60408051601f198184030181529181528151602092830120600081815260099093529120600201549091506001600160a01b0316611b355784600114156118f557600254600554600654600754604051637d2fbb8f60e11b81526001600160a01b039485169463fa5f771e9461189c9490821693908216928f928f928f929116908490600401612c48565b602060405180830381600087803b1580156118b657600080fd5b505af11580156118ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ee919081019061232f565b91506119eb565b600260009054906101000a90046001600160a01b03166001600160a01b031663546344f0600560009054906101000a90046001600160a01b0316600660009054906101000a90046001600160a01b03168b8b8b600760009054906101000a90046001600160a01b0316600860009054906101000a90046001600160a01b03166040518863ffffffff1660e01b81526004016119969796959493929190612c48565b602060405180830381600087803b1580156119b057600080fd5b505af11580156119c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119e8919081019061232f565b91505b604080516060808201835287825260208083018881526001600160a01b038088168587018181526000898152600986528881209751885593516001808901919091559051600290970180549784166001600160a01b031990981697909717909655908e168252600a83528582208054808701825590835283832001879055808252600b8352858220805460ff1990811687179091558651948501875294845263ffffffff808c168585019081526001600160801b03808d16878a01908152848652600c909652938890209551865491519551909416650100000000000265010000000000600160a81b0319959092166101000264ffffffff00199415159190971617929092169490941791909116178155915190917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca3491611b2c9190612eb3565b60405180910390a25b6000908152600960205260409020600201546001600160a01b0316979650505050505050565b6001600160a01b038116611b815760405162461bcd60e51b815260040161055090612dc3565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b600080600183039050600060048281548110611bbf57fe5b60009182526020909120015460405163cc49ede760e01b81526001600160a01b039091169063cc49ede790611bf8908890600401612c3a565b60206040518083038186803b158015611c1057600080fd5b505afa158015611c24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c48919081019061232f565b90506001600160a01b03811615611df85780856001826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611c9657600080fd5b505afa158015611caa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611cce9190810190612620565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611d0757600080fd5b505afa158015611d1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d3f9190810190612620565b88604051602001611d54959493929190612be1565b60408051601f19818403018152828252805160209182012060608401835260018085528285018a81526001600160a01b0388811687870181815260008681526009885288812099518a5593518986015551600290980180546001600160a01b031916988316989098179097558c168152600a84528481208054808401825590825284822001839055948552600b90925291909220805460ff19169092179091559350505b600060048381548110611e0757fe5b60009182526020909120015460405163c810a3e360e01b81526001600160a01b039091169063c810a3e390611e40908990600401612c3a565b60206040518083038186803b158015611e5857600080fd5b505afa158015611e6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e90919081019061232f565b90506001600160a01b038116156120435780866000826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611ede57600080fd5b505afa158015611ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f169190810190612620565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611f4f57600080fd5b505afa158015611f63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f879190810190612620565b89604051602001611f9c959493929190612be1565b60408051601f19818403018152828252805160209182012060608401835260008085528285018b81526001600160a01b03888116878701818152858552600987528785209851895592516001808a01919091559251600290980180546001600160a01b031916988316989098179097558d168252600a84528482208054808301825590835284832001839055948152600b909252919020805460ff19169092179091559450505b505050505050565b6001600160a01b0381166120715760405162461bcd60e51b815260040161055090612db3565b600080546040516001600160a01b03808516936201000090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b604080516060810182526000808252602082018190529181019190915290565b6040518060600160405280600081526020016000815260200160006001600160a01b031681525090565b80356115df81612ffc565b80516115df81612ffc565b60008083601f84011261214c57600080fd5b50813567ffffffffffffffff81111561216457600080fd5b60208301915083602082028301111561217c57600080fd5b9250929050565b600082601f83011261219457600080fd5b81356121a76121a282612f2c565b612f05565b915081818352602084019350602081019050838560208402820111156121cc57600080fd5b60005b838110156121f857816121e28882612124565b84525060209283019291909101906001016121cf565b5050505092915050565b600082601f83011261221357600080fd5b81356122216121a282612f2c565b9150818183526020840193506020810190508385606084028201111561224657600080fd5b60005b838110156121f8578161225c888261228a565b84525060209092019160609190910190600101612249565b80356115df81613010565b80516115df81613010565b60006060828403121561229c57600080fd5b6122a66060612f05565b905060006122b48484612274565b82525060206122c584848301612306565b60208301525060406122d9848285016122e5565b60408301525092915050565b80356115df81613019565b80356115df81613022565b80516115df81613022565b80356115df8161302b565b60006020828403121561232357600080fd5b600061050a8484612124565b60006020828403121561234157600080fd5b600061050a848461212f565b60008060008060008060008060e0898b03121561236957600080fd5b60006123758b8b612124565b98505060206123868b828c01612124565b97505060406123978b828c01612124565b96505060606123a88b828c01612124565b95505060806123b98b828c01612124565b94505060a06123ca8b828c01612124565b93505060c089013567ffffffffffffffff8111156123e757600080fd5b6123f38b828c0161213a565b92509250509295985092959890939650565b6000806040838503121561241857600080fd5b60006124248585612124565b9250506020612435858286016122f0565b9150509250929050565b6000806000806080858703121561245557600080fd5b60006124618787612124565b9450506020612472878288016122f0565b9350506040612483878288016122f0565b9250506060612494878288016122f0565b91505092959194509250565b600080600080600060a086880312156124b857600080fd5b60006124c48888612124565b95505060206124d5888289016122f0565b94505060406124e6888289016122f0565b93505060606124f7888289016122f0565b9250506080612508888289016122f0565b9150509295509295909350565b6000806000806040858703121561252b57600080fd5b843567ffffffffffffffff81111561254257600080fd5b61254e8782880161213a565b9450945050602085013567ffffffffffffffff81111561256d57600080fd5b6125798782880161213a565b95989497509550505050565b6000806040838503121561259857600080fd5b823567ffffffffffffffff8111156125af57600080fd5b6125bb85828601612183565b925050602083013567ffffffffffffffff8111156125d857600080fd5b61243585828601612202565b6000602082840312156125f657600080fd5b600061050a848461227f565b60006020828403121561261457600080fd5b600061050a84846122f0565b60006020828403121561263257600080fd5b600061050a84846122fb565b600061264a8383612b7e565b505060600190565b61265b81612f7b565b82525050565b61265b61266d82612f7b565b612fde565b600061267d82612f53565b6126878185612f57565b935061269283612f4d565b8060005b838110156126c05781516126aa888261263e565b97506126b583612f4d565b925050600101612696565b509495945050505050565b61265b81612f86565b61265b81612f9a565b60006126ea602083612f57565b7f56657374696e67207265676973747279206164647265737320696e76616c6964815260200192915050565b6000612723600f83612f57565b6e0c2e4e4c2f2e640dad2e6dac2e8c6d608b1b815260200192915050565b600061274e601f83612f57565b7f746f6b656e206f776e65722063616e6e6f742062652030206164647265737300815260200192915050565b6000612787601b83612f57565b7f76657374696e672063616e6e6f74206265203020616464726573730000000000815260200192915050565b60006127c0602683612f57565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b6000612808601e83612f57565b7f76657374696e67466163746f7279206164647265737320696e76616c69640000815260200192915050565b6000612841600e83612f57565b6d185b5bdd5b9d081a5b9d985b1a5960921b815260200192915050565b600061286b601783612f57565b7f76657374696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b60006128a4600e83612f57565b6d76657374696e672065786973747360901b815260200192915050565b60006128ce601c83612f57565b7f76657374696e674f776e6572206164647265737320696e76616c696400000000815260200192915050565b6000612907602e83612f57565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656181526d191e481a5b9a5d1a585b1a5e995960921b602082015260400192915050565b6000612957600c83612f57565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b600061297f602383612f57565b7f66656553686172696e67436f6c6c6563746f72206164647265737320696e76618152621b1a5960ea1b602082015260400192915050565b60006129c4602c83612f57565b7f76657374696e67206372656174696f6e2074797065206d75737420626520677281526b06561746572207468616e20360a41b602082015260400192915050565b6000612a12601883612f57565b7f7265636569766572206164647265737320696e76616c69640000000000000000815260200192915050565b6000612a4b601083612f57565b6f0aadcdac2e8c6d0cac840d8cadccee8d60831b815260200192915050565b6000612a77601983612f57565b7f4c6f636b6564534f56206164647265737320696e76616c696400000000000000815260200192915050565b6000612ab0601783612f57565b7f7374616b696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612ae9600f83612f57565b6e1d1c985b9cd9995c8819985a5b1959608a1b815260200192915050565b6000612b14601383612f57565b7214d3d5881859191c995cdcc81a5b9d985b1a59606a1b815260200192915050565b80546060830190612b4681612fa5565b612b5085826126cb565b50612b5a81612fb8565b612b676020860182612bd8565b50612b7181612fcb565b6111d46040860182612bb5565b80516060830190612b8f8482612bbe565b506020820151612ba26020850182612bbe565b5060408201516105676040850182612652565b61265b81612f66565b61265b81612f97565b61265b612bd382612f97565b612f97565b61265b81612f72565b6000612bed8288612661565b601482019150612bfd8287612bc7565b602082019150612c0d8286612bc7565b602082019150612c1d8285612bc7565b602082019150612c2d8284612bc7565b5060200195945050505050565b602081016115df8284612652565b60e08101612c56828a612652565b612c636020830189612652565b612c706040830188612652565b612c7d6060830187612bbe565b612c8a6080830186612bbe565b612c9760a0830185612652565b612ca460c0830184612652565b98975050505050505050565b60408101612cbe8285612652565b612ccb6020830184612bbe565b9392505050565b60a08101612ce08288612652565b612ced6020830187612bbe565b612cfa6040830186612bbe565b612d076060830185612bbe565b612d146080830184612bbe565b9695505050505050565b60208082528101612ccb8184612672565b602081016115df82846126cb565b60608101612d4b82866126cb565b612d586020830185612bd8565b61050a6040830184612bb5565b602081016115df82846126d4565b602080825281016115df816126dd565b602080825281016115df81612716565b602080825281016115df81612741565b602080825281016115df8161277a565b602080825281016115df816127b3565b602080825281016115df816127fb565b602080825281016115df81612834565b602080825281016115df8161285e565b602080825281016115df81612897565b602080825281016115df816128c1565b602080825281016115df816128fa565b602080825281016115df8161294a565b602080825281016115df81612972565b602080825281016115df816129b7565b602080825281016115df81612a05565b602080825281016115df81612a3e565b602080825281016115df81612a6a565b602080825281016115df81612aa3565b602080825281016115df81612adc565b602080825281016115df81612b07565b606081016115df8284612b36565b602081016115df8284612bbe565b60408101612cbe8285612bbe565b60608101612eeb8286612bbe565b612ef86020830185612bbe565b61050a6040830184612652565b60405181810167ffffffffffffffff81118282101715612f2457600080fd5b604052919050565b600067ffffffffffffffff821115612f4357600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b60ff1690565b6001600160801b031690565b63ffffffff1690565b60006115df82612f8b565b151590565b6001600160a01b031690565b90565b60006115df82612f7b565b60006115df612fb383612f97565b612f60565b60006115df612fc683612ff6565b612f72565b60006115df612fd983612ff0565b612f66565b60006115df8260006115df8260601b90565b60281c90565b60081c90565b61300581612f7b565b81146106ca57600080fd5b61300581612f86565b61300581612f66565b61300581612f97565b61300581612f7256fea365627a7a723158201d7444ad20959aa55436fb5bdba711b0369fe69491b25c92fb66c7287a486a1b6c6578706572696d656e74616cf564736f6c63430005110040", - "implementation": "0x5C7280543de9Bf0e5C59A99d56ccF47B746C6d55", + "numDeployments": 3, + "bytecode": "0x608060405260006100176001600160e01b0361007016565b6000805462010000600160b01b031916620100006001600160a01b038416908102919091178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350610074565b3390565b613352806100836000396000f3fe608060405234801561001057600080fd5b50600436106102275760003560e01c80638dedf00911610130578063c36519d1116100b8578063efb957331161007c578063efb957331461049f578063f2f46b3b146104bf578063f2fde38b146104c7578063f421ed7e146104da578063f60826ee146104ed57610227565b8063c36519d114610433578063c680c0b714610446578063ca210d8c14610459578063cc49ede71461046c578063dfb9366d1461047f57610227565b8063b810c648116100ff578063b810c648146103df578063bc160758146103f2578063bc84f9ca14610405578063bd7b590814610418578063c0e098521461042057610227565b80638dedf009146103a65780638f32d59b146103c75780639af05157146103cf578063b29d2514146103d757610227565b80636b7dbb2d116101b357806380fcb9921161018257806380fcb99214610327578063821bee7314610349578063842a49d51461036b578063862e229d1461038b5780638da5cb5b1461039e57610227565b80636b7dbb2d146102e657806370480275146102ee578063786931da1461030157806379a83f5a1461031457610227565b80631f509326116101fa5780631f50932614610285578063377220fd14610298578063429b62e5146102ab57806342a82b4f146102cb5780634cf088d9146102de57610227565b806302df04761461022c5780630665a06f1461025557806308dcb3601461026a5780631785f53c14610272575b600080fd5b61023f61023a36600461264e565b6104f5565b60405161024c9190612ef0565b60405180910390f35b61026861026336600461264e565b610556565b005b61023f6105b1565b610268610280366004612520565b6105c0565b6102686102933660046126af565b610662565b6102686102a6366004612520565b610704565b6102be6102b9366004612520565b610734565b60405161024c9190612fe5565b6102686102d936600461255c565b610749565b61023f6109b8565b61023f6109c7565b6102686102fc366004612520565b6109d6565b61026861030f366004612724565b610a72565b610268610322366004612614565b610d61565b61033a610335366004612520565b610f0d565b60405161024c93929190612ff3565b61035c610357366004612811565b610f42565b60405161024c939291906131b3565b61037e610379366004612811565b610f6c565b60405161024c919061301b565b6102686103993660046126af565b610f93565b61023f611022565b6103b96103b4366004612520565b611037565b60405161024c9291906131a5565b6102be61112b565b610268611155565b61023f611212565b6102686103ed366004612724565b611231565b610268610400366004612520565b611341565b610268610413366004612794565b6113ea565b61023f61154a565b6102be61042e366004612520565b611559565b61023f61044136600461264e565b61156e565b610268610454366004612614565b611577565b6102be610467366004612520565b6116b7565b61023f61047a366004612520565b6116d5565b61049261048d366004612614565b6117f4565b60405161024c9190613197565b6104b26104ad366004612520565b611822565b60405161024c9190612fd4565b61037e61196c565b6102686104d5366004612520565b61197b565b6102be6104e8366004612520565b6119a8565b61037e6119f3565b60008060015b905060008682878787604051602001610518959493929190612e8c565b60408051601f198184030181529181528151602092830120600090815260099092529020600201546001600160a01b0316925050505b949350505050565b61055e61112b565b8061057857503360009081526001602052604090205460ff165b61059d5760405162461bcd60e51b8152600401610594906130d9565b60405180910390fd5b6105ab848484846003610662565b50505050565b6005546001600160a01b031681565b6105c861112b565b806105eb57506105d6611212565b6001600160a01b0316336001600160a01b0316145b6106075760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b03811660009081526001602052604090819020805460ff19169055517fa3b62bc36326052d97ea62d63c3d60308ed4c3ea8ac079dd8499f1e9c4f80c0f90610657908390612ef0565b60405180910390a150565b61066a61112b565b8061068457503360009081526001602052604090205460ff165b6106a05760405162461bcd60e51b8152600401610594906130d9565b60006106b186858560015b86611a02565b9050856001600160a01b03167fd6fcfd83804f6b6b63260c7b99eb10060bc1319dbc9177fb6defc7bd614017bf82868689876040516106f4959493929190612f88565b60405180910390a2505050505050565b61070c61112b565b6107285760405162461bcd60e51b8152600401610594906130d9565b61073181611d6a565b50565b60016020526000908152604090205460ff1681565b61075161112b565b61076d5760405162461bcd60e51b8152600401610594906130d9565b600054610100900460ff1680610786575060005460ff16155b6107a25760405162461bcd60e51b8152600401610594906130c9565b600054610100900460ff161580156107cd576000805460ff1961ff0019909116610100171660011790555b6001600160a01b0388166107f35760405162461bcd60e51b815260040161059490613179565b6001600160a01b0387166108195760405162461bcd60e51b815260040161059490613159565b6001600160a01b03861661083f5760405162461bcd60e51b8152600401610594906130e9565b6001600160a01b0385166108655760405162461bcd60e51b8152600401610594906130b9565b6001600160a01b03841661088b5760405162461bcd60e51b815260040161059490613149565b61089489611d6a565b600580546001600160a01b03199081166001600160a01b038b8116919091179092556006805482168a84161790556007805482168984161790556008805482168884161790556003805490911691861691909117905560005b8281101561099a57600084848381811061090357fe5b90506020020160206109189190810190612520565b6001600160a01b0316141561093f5760405162461bcd60e51b815260040161059490613029565b600484848381811061094d57fe5b90506020020160206109629190810190612520565b815460018082018455600093845260209093200180546001600160a01b0319166001600160a01b0392909216919091179055016108ed565b5080156109ad576000805461ff00191690555b505050505050505050565b6006546001600160a01b031681565b6007546001600160a01b031681565b6109de61112b565b80610a0157506109ec611212565b6001600160a01b0316336001600160a01b0316145b610a1d5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b038116600090815260016020819052604091829020805460ff19169091179055517f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e33990610657908390612ef0565b610a7a61112b565b80610a9457503360009081526001602052604090205460ff165b610ab05760405162461bcd60e51b8152600401610594906130d9565b828114610acf5760405162461bcd60e51b815260040161059490613039565b60046224ea0063059fa60060005b86811015610d5757600b6000878784818110610af557fe5b9050602002016020610b0a9190810190612520565b6001600160a01b0316815260208101919091526040016000205460ff1615610b445760405162461bcd60e51b8152600401610594906130a9565b6000888883818110610b5257fe5b9050602002016020610b679190810190612520565b6001600160a01b03161415610b8e5760405162461bcd60e51b815260040161059490613049565b6000868683818110610b9c57fe5b9050602002016020610bb19190810190612520565b6001600160a01b03161415610bd85760405162461bcd60e51b815260040161059490613059565b6000888883818110610be657fe5b9050602002016020610bfb9190810190612520565b6001858588604051602001610c14959493929190612e8c565b60408051808303601f1901815282825280516020918201206060840183526001845290830188905292508101888885818110610c4c57fe5b9050602002016020610c619190810190612520565b6001600160a01b039081169091526000838152600960209081526040808320855181559185015160018301559390930151600290930180546001600160a01b03191693909216929092179055600a908a8a85818110610cbc57fe5b9050602002016020610cd19190810190612520565b6001600160a01b031681526020808201929092526040016000908120805460018181018355918352928220909201839055600b90898986818110610d1157fe5b9050602002016020610d269190810190612520565b6001600160a01b031681526020810191909152604001600020805460ff191691151591909117905550600101610add565b5050505050505050565b610d6961112b565b80610d8357503360009081526001602052604090205460ff165b610d9f5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b038216610dc55760405162461bcd60e51b815260040161059490613099565b60008111610de55760405162461bcd60e51b815260040161059490613089565b60055460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390610e179085908590600401612f66565b602060405180830381600087803b158015610e3157600080fd5b505af1158015610e45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e6991908101906127f3565b50604051637547c7a360e01b81526001600160a01b03831690637547c7a390610e96908490600401613197565b600060405180830381600087803b158015610eb057600080fd5b505af1158015610ec4573d6000803e3d6000fd5b50505050816001600160a01b03167fb539ca1e5c8d398ddf1c41c30166f33404941683be4683319b57669a93dad4ef82604051610f019190613197565b60405180910390a25050565b600c6020526000908152604090205460ff811690610100810463ffffffff16906501000000000090046001600160801b031683565b6009602052600090815260409020805460018201546002909201549091906001600160a01b031683565b60048181548110610f7957fe5b6000918252602090912001546001600160a01b0316905081565b610f9b61112b565b80610fb557503360009081526001602052604090205460ff165b610fd15760405162461bcd60e51b8152600401610594906130d9565b6000610fdf868585846106ab565b9050856001600160a01b03167f3791c6c90c276d011b4b885c0bfba0554342acf50a539baca1b06f070af25ff482868689876040516106f4959493929190612f88565b6000546201000090046001600160a01b031690565b6000806000839050806001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561107857600080fd5b505afa15801561108c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110b0919081019061282f565b816001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156110e957600080fd5b505afa1580156110fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611121919081019061282f565b9250925050915091565b600080546201000090046001600160a01b0316611146611db2565b6001600160a01b031614905090565b61115d61112b565b6111795760405162461bcd60e51b8152600401610594906130d9565b6000611183611212565b6001600160a01b031614156111aa5760405162461bcd60e51b815260040161059490613129565b6111b2611212565b6001600160a01b0316336001600160a01b03167f560ce9bc23860aa4ec0898e6c59b295d3461332304d14f1ac216282407da435f60405160405180910390a3604051600090819061120290612ee5565b6040519081900390209190915550565b60008060405161122190612ee5565b6040519081900390205492915050565b61123961112b565b8061125357503360009081526001602052604090205460ff165b61126f5760405162461bcd60e51b8152600401610594906130d9565b60005b8381101561133a57600085858381811061128857fe5b905060200201602061129d9190810190612520565b6001600160a01b031614156112c45760405162461bcd60e51b815260040161059490613049565b60008383838181106112d257fe5b90506020020135116112f65760405162461bcd60e51b8152600401610594906130f9565b61133285858381811061130557fe5b905060200201602061131a9190810190612520565b84848481811061132657fe5b90506020020135611db6565b600101611272565b5050505050565b61134961112b565b6113655760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b03811661138b5760405162461bcd60e51b815260040161059490613119565b806001600160a01b031661139d611212565b6001600160a01b0316336001600160a01b03167f6bf6a88b1bb767c00d69f519bfabc075817f28425abfc80cfb779ef4e9b0bbe560405160405180910390a4600060405161120290612ee5565b6113f261112b565b8061140c57503360009081526001602052604090205460ff165b6114285760405162461bcd60e51b8152600401610594906130d9565b80518251146114495760405162461bcd60e51b815260040161059490613139565b60005b81518110156115455761145d6122e9565b82828151811061146957fe5b60200260200101519050600084838151811061148157fe5b6020908102919091018101516001600160a01b0381166000818152600c845260409081902086518154958801518884015160ff199097169115159190911764ffffffff00191661010063ffffffff909216919091021765010000000000600160a81b031916650100000000006001600160801b039096169590950294909417845551919350917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca34916115339190613189565b60405180910390a2505060010161144c565b505050565b6008546001600160a01b031681565b600b6020526000908152604090205460ff1681565b600080806104fb565b61157f61112b565b61159b5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b0382166115c15760405162461bcd60e51b815260040161059490613109565b806115de5760405162461bcd60e51b815260040161059490613089565b60055460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906116109085908590600401612f66565b602060405180830381600087803b15801561162a57600080fd5b505af115801561163e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061166291908101906127f3565b61167e5760405162461bcd60e51b815260040161059490613169565b816001600160a01b03167fe1b0ada289bf82fb641c8c1e1c78f820fae44e8f845760725cdeb09167a289ad82604051610f019190613197565b6001600160a01b03166000908152600b602052604090205460ff1690565b60006117ee82600360009054906101000a90046001600160a01b03166001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561172957600080fd5b505afa15801561173d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611761919081019061282f565b600360009054906101000a90046001600160a01b03166001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156117af57600080fd5b505afa1580156117c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117e7919081019061282f565b60036104f5565b92915050565b600a602052816000526040600020818154811061180d57fe5b90600052602060002001600091509150505481565b606080600a6000846001600160a01b03166001600160a01b0316815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561189457602002820191906000526020600020905b815481526020019060010190808311611880575b50505050509050600081519050606082516040519080825280602002602001820160405280156118de57816020015b6118cb612309565b8152602001906001900390816118c35790505b50905060005b8281101561196357600960008583815181106118fc57fe5b6020908102919091018101518252818101929092526040908101600020815160608101835281548152600182015493810193909352600201546001600160a01b031690820152825183908390811061195057fe5b60209081029190910101526001016118e4565b50949350505050565b6003546001600160a01b031681565b61198361112b565b61199f5760405162461bcd60e51b8152600401610594906130d9565b6107318161225a565b6001600160a01b0381166000908152600c602052604081205460ff1680156117ee5750506001600160a01b03166000908152600c6020526040902054610100900463ffffffff161590565b6002546001600160a01b031681565b60008060008785888887604051602001611a20959493929190612e8c565b60408051601f198184030181529181528151602092830120600081815260099093529120600201549091506001600160a01b0316611d44578460011415611b0457600254600554600654600754604051637d2fbb8f60e11b81526001600160a01b039485169463fa5f771e94611aab9490821693908216928f928f928f929116908490600401612efe565b602060405180830381600087803b158015611ac557600080fd5b505af1158015611ad9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611afd919081019061253e565b9150611bfa565b600260009054906101000a90046001600160a01b03166001600160a01b031663546344f0600560009054906101000a90046001600160a01b0316600660009054906101000a90046001600160a01b03168b8b8b600760009054906101000a90046001600160a01b0316600860009054906101000a90046001600160a01b03166040518863ffffffff1660e01b8152600401611ba59796959493929190612efe565b602060405180830381600087803b158015611bbf57600080fd5b505af1158015611bd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611bf7919081019061253e565b91505b604080516060808201835287825260208083018881526001600160a01b038088168587018181526000898152600986528881209751885593516001808901919091559051600290970180549784166001600160a01b031990981697909717909655908e168252600a83528582208054808701825590835283832001879055808252600b8352858220805460ff1990811687179091558651948501875294845263ffffffff808c168585019081526001600160801b03808d16878a01908152848652600c909652938890209551865491519551909416650100000000000265010000000000600160a81b0319959092166101000264ffffffff00199415159190971617929092169490941791909116178155915190917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca3491611d3b9190613189565b60405180910390a25b6000908152600960205260409020600201546001600160a01b0316979650505050505050565b6001600160a01b038116611d905760405162461bcd60e51b815260040161059490613079565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b600080600183039050600060048281548110611dce57fe5b60009182526020909120015460405163cc49ede760e01b81526001600160a01b039091169063cc49ede790611e07908890600401612ef0565b60206040518083038186803b158015611e1f57600080fd5b505afa158015611e33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e57919081019061253e565b90506001600160a01b038116156120075780856001826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611ea557600080fd5b505afa158015611eb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611edd919081019061282f565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611f1657600080fd5b505afa158015611f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f4e919081019061282f565b88604051602001611f63959493929190612e8c565b60408051601f19818403018152828252805160209182012060608401835260018085528285018a81526001600160a01b0388811687870181815260008681526009885288812099518a5593518986015551600290980180546001600160a01b031916988316989098179097558c168152600a84528481208054808401825590825284822001839055948552600b90925291909220805460ff19169092179091559350505b60006004838154811061201657fe5b60009182526020909120015460405163c810a3e360e01b81526001600160a01b039091169063c810a3e39061204f908990600401612ef0565b60206040518083038186803b15801561206757600080fd5b505afa15801561207b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061209f919081019061253e565b90506001600160a01b038116156122525780866000826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b1580156120ed57600080fd5b505afa158015612101573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612125919081019061282f565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561215e57600080fd5b505afa158015612172573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612196919081019061282f565b896040516020016121ab959493929190612e8c565b60408051601f19818403018152828252805160209182012060608401835260008085528285018b81526001600160a01b03888116878701818152858552600987528785209851895592516001808a01919091559251600290980180546001600160a01b031916988316989098179097558d168252600a84528482208054808301825590835284832001839055948152600b909252919020805460ff19169092179091559450505b505050505050565b6001600160a01b0381166122805760405162461bcd60e51b815260040161059490613069565b600080546040516001600160a01b03808516936201000090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b604080516060810182526000808252602082018190529181019190915290565b6040518060600160405280600081526020016000815260200160006001600160a01b031681525090565b80356117ee816132d7565b80516117ee816132d7565b60008083601f84011261235b57600080fd5b50813567ffffffffffffffff81111561237357600080fd5b60208301915083602082028301111561238b57600080fd5b9250929050565b600082601f8301126123a357600080fd5b81356123b66123b182613202565b6131db565b915081818352602084019350602081019050838560208402820111156123db57600080fd5b60005b8381101561240757816123f18882612333565b84525060209283019291909101906001016123de565b5050505092915050565b600082601f83011261242257600080fd5b81356124306123b182613202565b9150818183526020840193506020810190508385606084028201111561245557600080fd5b60005b83811015612407578161246b8882612499565b84525060209092019160609190910190600101612458565b80356117ee816132eb565b80516117ee816132eb565b6000606082840312156124ab57600080fd5b6124b560606131db565b905060006124c38484612483565b82525060206124d484848301612515565b60208301525060406124e8848285016124f4565b60408301525092915050565b80356117ee816132f4565b80356117ee816132fd565b80516117ee816132fd565b80356117ee81613306565b60006020828403121561253257600080fd5b600061054e8484612333565b60006020828403121561255057600080fd5b600061054e848461233e565b60008060008060008060008060e0898b03121561257857600080fd5b60006125848b8b612333565b98505060206125958b828c01612333565b97505060406125a68b828c01612333565b96505060606125b78b828c01612333565b95505060806125c88b828c01612333565b94505060a06125d98b828c01612333565b93505060c089013567ffffffffffffffff8111156125f657600080fd5b6126028b828c01612349565b92509250509295985092959890939650565b6000806040838503121561262757600080fd5b60006126338585612333565b9250506020612644858286016124ff565b9150509250929050565b6000806000806080858703121561266457600080fd5b60006126708787612333565b9450506020612681878288016124ff565b9350506040612692878288016124ff565b92505060606126a3878288016124ff565b91505092959194509250565b600080600080600060a086880312156126c757600080fd5b60006126d38888612333565b95505060206126e4888289016124ff565b94505060406126f5888289016124ff565b9350506060612706888289016124ff565b9250506080612717888289016124ff565b9150509295509295909350565b6000806000806040858703121561273a57600080fd5b843567ffffffffffffffff81111561275157600080fd5b61275d87828801612349565b9450945050602085013567ffffffffffffffff81111561277c57600080fd5b61278887828801612349565b95989497509550505050565b600080604083850312156127a757600080fd5b823567ffffffffffffffff8111156127be57600080fd5b6127ca85828601612392565b925050602083013567ffffffffffffffff8111156127e757600080fd5b61264485828601612411565b60006020828403121561280557600080fd5b600061054e848461248e565b60006020828403121561282357600080fd5b600061054e84846124ff565b60006020828403121561284157600080fd5b600061054e848461250a565b60006128598383612e29565b505060600190565b61286a81613256565b82525050565b61286a61287c82613256565b6132b9565b600061288c82613229565b612896818561322d565b93506128a183613223565b8060005b838110156128cf5781516128b9888261284d565b97506128c483613223565b9250506001016128a5565b509495945050505050565b61286a81613261565b61286a81613275565b60006128f960208361322d565b7f56657374696e67207265676973747279206164647265737320696e76616c6964815260200192915050565b6000612932600f8361322d565b6e0c2e4e4c2f2e640dad2e6dac2e8c6d608b1b815260200192915050565b600061295d601f8361322d565b7f746f6b656e206f776e65722063616e6e6f742062652030206164647265737300815260200192915050565b6000612996601b8361322d565b7f76657374696e672063616e6e6f74206265203020616464726573730000000000815260200192915050565b60006129cf60268361322d565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b6000612a17601e8361322d565b7f76657374696e67466163746f7279206164647265737320696e76616c69640000815260200192915050565b6000612a50600e8361322d565b6d185b5bdd5b9d081a5b9d985b1a5960921b815260200192915050565b6000612a7a60178361322d565b7f76657374696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612ab3600e8361322d565b6d76657374696e672065786973747360901b815260200192915050565b6000612add601c8361322d565b7f76657374696e674f776e6572206164647265737320696e76616c696400000000815260200192915050565b6000612b16602e8361322d565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656181526d191e481a5b9a5d1a585b1a5e995960921b602082015260400192915050565b6000612b66600c8361322d565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000612b8e60238361322d565b7f66656553686172696e67436f6c6c6563746f72206164647265737320696e76618152621b1a5960ea1b602082015260400192915050565b6000612bd3602c8361322d565b7f76657374696e67206372656174696f6e2074797065206d75737420626520677281526b06561746572207468616e20360a41b602082015260400192915050565b6000612c2160188361322d565b7f7265636569766572206164647265737320696e76616c69640000000000000000815260200192915050565b6000612c5a60158361322d565b7434b73b30b634b21030b236b4b71036b0b730b3b2b960591b815260200192915050565b6000612c8b60188361322d565b7f41646d696e206d616e61676572206973206e6f74207365740000000000000000815260200192915050565b6000612cc460108361322d565b6f0aadcdac2e8c6d0cac840d8cadccee8d60831b815260200192915050565b6000612cf060198361322d565b7f4c6f636b6564534f56206164647265737320696e76616c696400000000000000815260200192915050565b6000612d2960178361322d565b7f7374616b696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612d62600f8361322d565b6e1d1c985b9cd9995c8819985a5b1959608a1b815260200192915050565b6000612d8d60138361322d565b7214d3d5881859191c995cdcc81a5b9d985b1a59606a1b815260200192915050565b6000612dbc601683613236565b756b65792e61646d696e2e6d616e616765722e726f6c6560501b815260160192915050565b80546060830190612df181613280565b612dfb85826128da565b50612e0581613293565b612e126020860182612e83565b50612e1c816132a6565b61133a6040860182612e60565b80516060830190612e3a8482612e69565b506020820151612e4d6020850182612e69565b5060408201516105ab6040850182612861565b61286a81613241565b61286a81613272565b61286a612e7e82613272565b613272565b61286a8161324d565b6000612e988288612870565b601482019150612ea88287612e72565b602082019150612eb88286612e72565b602082019150612ec88285612e72565b602082019150612ed88284612e72565b5060200195945050505050565b60006117ee82612daf565b602081016117ee8284612861565b60e08101612f0c828a612861565b612f196020830189612861565b612f266040830188612861565b612f336060830187612e69565b612f406080830186612e69565b612f4d60a0830185612861565b612f5a60c0830184612861565b98975050505050505050565b60408101612f748285612861565b612f816020830184612e69565b9392505050565b60a08101612f968288612861565b612fa36020830187612e69565b612fb06040830186612e69565b612fbd6060830185612e69565b612fca6080830184612e69565b9695505050505050565b60208082528101612f818184612881565b602081016117ee82846128da565b6060810161300182866128da565b61300e6020830185612e83565b61054e6040830184612e60565b602081016117ee82846128e3565b602080825281016117ee816128ec565b602080825281016117ee81612925565b602080825281016117ee81612950565b602080825281016117ee81612989565b602080825281016117ee816129c2565b602080825281016117ee81612a0a565b602080825281016117ee81612a43565b602080825281016117ee81612a6d565b602080825281016117ee81612aa6565b602080825281016117ee81612ad0565b602080825281016117ee81612b09565b602080825281016117ee81612b59565b602080825281016117ee81612b81565b602080825281016117ee81612bc6565b602080825281016117ee81612c14565b602080825281016117ee81612c4d565b602080825281016117ee81612c7e565b602080825281016117ee81612cb7565b602080825281016117ee81612ce3565b602080825281016117ee81612d1c565b602080825281016117ee81612d55565b602080825281016117ee81612d80565b606081016117ee8284612de1565b602081016117ee8284612e69565b60408101612f748285612e69565b606081016131c18286612e69565b6131ce6020830185612e69565b61054e6040830184612861565b60405181810167ffffffffffffffff811182821017156131fa57600080fd5b604052919050565b600067ffffffffffffffff82111561321957600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b919050565b60ff1690565b6001600160801b031690565b63ffffffff1690565b60006117ee82613266565b151590565b6001600160a01b031690565b90565b60006117ee82613256565b60006117ee61328e83613272565b61323b565b60006117ee6132a1836132d1565b61324d565b60006117ee6132b4836132cb565b613241565b60006117ee8260006117ee8260601b90565b60281c90565b60081c90565b6132e081613256565b811461073157600080fd5b6132e081613261565b6132e081613241565b6132e081613272565b6132e08161324d56fea365627a7a72315820dc2ada767f6100aeabb2dd76de36097a63988151b323d89b951fad1348d343756c6578706572696d656e74616cf564736f6c63430005110040", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102275760003560e01c80638dedf00911610130578063c36519d1116100b8578063efb957331161007c578063efb957331461049f578063f2f46b3b146104bf578063f2fde38b146104c7578063f421ed7e146104da578063f60826ee146104ed57610227565b8063c36519d114610433578063c680c0b714610446578063ca210d8c14610459578063cc49ede71461046c578063dfb9366d1461047f57610227565b8063b810c648116100ff578063b810c648146103df578063bc160758146103f2578063bc84f9ca14610405578063bd7b590814610418578063c0e098521461042057610227565b80638dedf009146103a65780638f32d59b146103c75780639af05157146103cf578063b29d2514146103d757610227565b80636b7dbb2d116101b357806380fcb9921161018257806380fcb99214610327578063821bee7314610349578063842a49d51461036b578063862e229d1461038b5780638da5cb5b1461039e57610227565b80636b7dbb2d146102e657806370480275146102ee578063786931da1461030157806379a83f5a1461031457610227565b80631f509326116101fa5780631f50932614610285578063377220fd14610298578063429b62e5146102ab57806342a82b4f146102cb5780634cf088d9146102de57610227565b806302df04761461022c5780630665a06f1461025557806308dcb3601461026a5780631785f53c14610272575b600080fd5b61023f61023a36600461264e565b6104f5565b60405161024c9190612ef0565b60405180910390f35b61026861026336600461264e565b610556565b005b61023f6105b1565b610268610280366004612520565b6105c0565b6102686102933660046126af565b610662565b6102686102a6366004612520565b610704565b6102be6102b9366004612520565b610734565b60405161024c9190612fe5565b6102686102d936600461255c565b610749565b61023f6109b8565b61023f6109c7565b6102686102fc366004612520565b6109d6565b61026861030f366004612724565b610a72565b610268610322366004612614565b610d61565b61033a610335366004612520565b610f0d565b60405161024c93929190612ff3565b61035c610357366004612811565b610f42565b60405161024c939291906131b3565b61037e610379366004612811565b610f6c565b60405161024c919061301b565b6102686103993660046126af565b610f93565b61023f611022565b6103b96103b4366004612520565b611037565b60405161024c9291906131a5565b6102be61112b565b610268611155565b61023f611212565b6102686103ed366004612724565b611231565b610268610400366004612520565b611341565b610268610413366004612794565b6113ea565b61023f61154a565b6102be61042e366004612520565b611559565b61023f61044136600461264e565b61156e565b610268610454366004612614565b611577565b6102be610467366004612520565b6116b7565b61023f61047a366004612520565b6116d5565b61049261048d366004612614565b6117f4565b60405161024c9190613197565b6104b26104ad366004612520565b611822565b60405161024c9190612fd4565b61037e61196c565b6102686104d5366004612520565b61197b565b6102be6104e8366004612520565b6119a8565b61037e6119f3565b60008060015b905060008682878787604051602001610518959493929190612e8c565b60408051601f198184030181529181528151602092830120600090815260099092529020600201546001600160a01b0316925050505b949350505050565b61055e61112b565b8061057857503360009081526001602052604090205460ff165b61059d5760405162461bcd60e51b8152600401610594906130d9565b60405180910390fd5b6105ab848484846003610662565b50505050565b6005546001600160a01b031681565b6105c861112b565b806105eb57506105d6611212565b6001600160a01b0316336001600160a01b0316145b6106075760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b03811660009081526001602052604090819020805460ff19169055517fa3b62bc36326052d97ea62d63c3d60308ed4c3ea8ac079dd8499f1e9c4f80c0f90610657908390612ef0565b60405180910390a150565b61066a61112b565b8061068457503360009081526001602052604090205460ff165b6106a05760405162461bcd60e51b8152600401610594906130d9565b60006106b186858560015b86611a02565b9050856001600160a01b03167fd6fcfd83804f6b6b63260c7b99eb10060bc1319dbc9177fb6defc7bd614017bf82868689876040516106f4959493929190612f88565b60405180910390a2505050505050565b61070c61112b565b6107285760405162461bcd60e51b8152600401610594906130d9565b61073181611d6a565b50565b60016020526000908152604090205460ff1681565b61075161112b565b61076d5760405162461bcd60e51b8152600401610594906130d9565b600054610100900460ff1680610786575060005460ff16155b6107a25760405162461bcd60e51b8152600401610594906130c9565b600054610100900460ff161580156107cd576000805460ff1961ff0019909116610100171660011790555b6001600160a01b0388166107f35760405162461bcd60e51b815260040161059490613179565b6001600160a01b0387166108195760405162461bcd60e51b815260040161059490613159565b6001600160a01b03861661083f5760405162461bcd60e51b8152600401610594906130e9565b6001600160a01b0385166108655760405162461bcd60e51b8152600401610594906130b9565b6001600160a01b03841661088b5760405162461bcd60e51b815260040161059490613149565b61089489611d6a565b600580546001600160a01b03199081166001600160a01b038b8116919091179092556006805482168a84161790556007805482168984161790556008805482168884161790556003805490911691861691909117905560005b8281101561099a57600084848381811061090357fe5b90506020020160206109189190810190612520565b6001600160a01b0316141561093f5760405162461bcd60e51b815260040161059490613029565b600484848381811061094d57fe5b90506020020160206109629190810190612520565b815460018082018455600093845260209093200180546001600160a01b0319166001600160a01b0392909216919091179055016108ed565b5080156109ad576000805461ff00191690555b505050505050505050565b6006546001600160a01b031681565b6007546001600160a01b031681565b6109de61112b565b80610a0157506109ec611212565b6001600160a01b0316336001600160a01b0316145b610a1d5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b038116600090815260016020819052604091829020805460ff19169091179055517f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e33990610657908390612ef0565b610a7a61112b565b80610a9457503360009081526001602052604090205460ff165b610ab05760405162461bcd60e51b8152600401610594906130d9565b828114610acf5760405162461bcd60e51b815260040161059490613039565b60046224ea0063059fa60060005b86811015610d5757600b6000878784818110610af557fe5b9050602002016020610b0a9190810190612520565b6001600160a01b0316815260208101919091526040016000205460ff1615610b445760405162461bcd60e51b8152600401610594906130a9565b6000888883818110610b5257fe5b9050602002016020610b679190810190612520565b6001600160a01b03161415610b8e5760405162461bcd60e51b815260040161059490613049565b6000868683818110610b9c57fe5b9050602002016020610bb19190810190612520565b6001600160a01b03161415610bd85760405162461bcd60e51b815260040161059490613059565b6000888883818110610be657fe5b9050602002016020610bfb9190810190612520565b6001858588604051602001610c14959493929190612e8c565b60408051808303601f1901815282825280516020918201206060840183526001845290830188905292508101888885818110610c4c57fe5b9050602002016020610c619190810190612520565b6001600160a01b039081169091526000838152600960209081526040808320855181559185015160018301559390930151600290930180546001600160a01b03191693909216929092179055600a908a8a85818110610cbc57fe5b9050602002016020610cd19190810190612520565b6001600160a01b031681526020808201929092526040016000908120805460018181018355918352928220909201839055600b90898986818110610d1157fe5b9050602002016020610d269190810190612520565b6001600160a01b031681526020810191909152604001600020805460ff191691151591909117905550600101610add565b5050505050505050565b610d6961112b565b80610d8357503360009081526001602052604090205460ff165b610d9f5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b038216610dc55760405162461bcd60e51b815260040161059490613099565b60008111610de55760405162461bcd60e51b815260040161059490613089565b60055460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390610e179085908590600401612f66565b602060405180830381600087803b158015610e3157600080fd5b505af1158015610e45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e6991908101906127f3565b50604051637547c7a360e01b81526001600160a01b03831690637547c7a390610e96908490600401613197565b600060405180830381600087803b158015610eb057600080fd5b505af1158015610ec4573d6000803e3d6000fd5b50505050816001600160a01b03167fb539ca1e5c8d398ddf1c41c30166f33404941683be4683319b57669a93dad4ef82604051610f019190613197565b60405180910390a25050565b600c6020526000908152604090205460ff811690610100810463ffffffff16906501000000000090046001600160801b031683565b6009602052600090815260409020805460018201546002909201549091906001600160a01b031683565b60048181548110610f7957fe5b6000918252602090912001546001600160a01b0316905081565b610f9b61112b565b80610fb557503360009081526001602052604090205460ff165b610fd15760405162461bcd60e51b8152600401610594906130d9565b6000610fdf868585846106ab565b9050856001600160a01b03167f3791c6c90c276d011b4b885c0bfba0554342acf50a539baca1b06f070af25ff482868689876040516106f4959493929190612f88565b6000546201000090046001600160a01b031690565b6000806000839050806001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561107857600080fd5b505afa15801561108c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110b0919081019061282f565b816001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156110e957600080fd5b505afa1580156110fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611121919081019061282f565b9250925050915091565b600080546201000090046001600160a01b0316611146611db2565b6001600160a01b031614905090565b61115d61112b565b6111795760405162461bcd60e51b8152600401610594906130d9565b6000611183611212565b6001600160a01b031614156111aa5760405162461bcd60e51b815260040161059490613129565b6111b2611212565b6001600160a01b0316336001600160a01b03167f560ce9bc23860aa4ec0898e6c59b295d3461332304d14f1ac216282407da435f60405160405180910390a3604051600090819061120290612ee5565b6040519081900390209190915550565b60008060405161122190612ee5565b6040519081900390205492915050565b61123961112b565b8061125357503360009081526001602052604090205460ff165b61126f5760405162461bcd60e51b8152600401610594906130d9565b60005b8381101561133a57600085858381811061128857fe5b905060200201602061129d9190810190612520565b6001600160a01b031614156112c45760405162461bcd60e51b815260040161059490613049565b60008383838181106112d257fe5b90506020020135116112f65760405162461bcd60e51b8152600401610594906130f9565b61133285858381811061130557fe5b905060200201602061131a9190810190612520565b84848481811061132657fe5b90506020020135611db6565b600101611272565b5050505050565b61134961112b565b6113655760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b03811661138b5760405162461bcd60e51b815260040161059490613119565b806001600160a01b031661139d611212565b6001600160a01b0316336001600160a01b03167f6bf6a88b1bb767c00d69f519bfabc075817f28425abfc80cfb779ef4e9b0bbe560405160405180910390a4600060405161120290612ee5565b6113f261112b565b8061140c57503360009081526001602052604090205460ff165b6114285760405162461bcd60e51b8152600401610594906130d9565b80518251146114495760405162461bcd60e51b815260040161059490613139565b60005b81518110156115455761145d6122e9565b82828151811061146957fe5b60200260200101519050600084838151811061148157fe5b6020908102919091018101516001600160a01b0381166000818152600c845260409081902086518154958801518884015160ff199097169115159190911764ffffffff00191661010063ffffffff909216919091021765010000000000600160a81b031916650100000000006001600160801b039096169590950294909417845551919350917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca34916115339190613189565b60405180910390a2505060010161144c565b505050565b6008546001600160a01b031681565b600b6020526000908152604090205460ff1681565b600080806104fb565b61157f61112b565b61159b5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b0382166115c15760405162461bcd60e51b815260040161059490613109565b806115de5760405162461bcd60e51b815260040161059490613089565b60055460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906116109085908590600401612f66565b602060405180830381600087803b15801561162a57600080fd5b505af115801561163e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061166291908101906127f3565b61167e5760405162461bcd60e51b815260040161059490613169565b816001600160a01b03167fe1b0ada289bf82fb641c8c1e1c78f820fae44e8f845760725cdeb09167a289ad82604051610f019190613197565b6001600160a01b03166000908152600b602052604090205460ff1690565b60006117ee82600360009054906101000a90046001600160a01b03166001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561172957600080fd5b505afa15801561173d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611761919081019061282f565b600360009054906101000a90046001600160a01b03166001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156117af57600080fd5b505afa1580156117c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117e7919081019061282f565b60036104f5565b92915050565b600a602052816000526040600020818154811061180d57fe5b90600052602060002001600091509150505481565b606080600a6000846001600160a01b03166001600160a01b0316815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561189457602002820191906000526020600020905b815481526020019060010190808311611880575b50505050509050600081519050606082516040519080825280602002602001820160405280156118de57816020015b6118cb612309565b8152602001906001900390816118c35790505b50905060005b8281101561196357600960008583815181106118fc57fe5b6020908102919091018101518252818101929092526040908101600020815160608101835281548152600182015493810193909352600201546001600160a01b031690820152825183908390811061195057fe5b60209081029190910101526001016118e4565b50949350505050565b6003546001600160a01b031681565b61198361112b565b61199f5760405162461bcd60e51b8152600401610594906130d9565b6107318161225a565b6001600160a01b0381166000908152600c602052604081205460ff1680156117ee5750506001600160a01b03166000908152600c6020526040902054610100900463ffffffff161590565b6002546001600160a01b031681565b60008060008785888887604051602001611a20959493929190612e8c565b60408051601f198184030181529181528151602092830120600081815260099093529120600201549091506001600160a01b0316611d44578460011415611b0457600254600554600654600754604051637d2fbb8f60e11b81526001600160a01b039485169463fa5f771e94611aab9490821693908216928f928f928f929116908490600401612efe565b602060405180830381600087803b158015611ac557600080fd5b505af1158015611ad9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611afd919081019061253e565b9150611bfa565b600260009054906101000a90046001600160a01b03166001600160a01b031663546344f0600560009054906101000a90046001600160a01b0316600660009054906101000a90046001600160a01b03168b8b8b600760009054906101000a90046001600160a01b0316600860009054906101000a90046001600160a01b03166040518863ffffffff1660e01b8152600401611ba59796959493929190612efe565b602060405180830381600087803b158015611bbf57600080fd5b505af1158015611bd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611bf7919081019061253e565b91505b604080516060808201835287825260208083018881526001600160a01b038088168587018181526000898152600986528881209751885593516001808901919091559051600290970180549784166001600160a01b031990981697909717909655908e168252600a83528582208054808701825590835283832001879055808252600b8352858220805460ff1990811687179091558651948501875294845263ffffffff808c168585019081526001600160801b03808d16878a01908152848652600c909652938890209551865491519551909416650100000000000265010000000000600160a81b0319959092166101000264ffffffff00199415159190971617929092169490941791909116178155915190917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca3491611d3b9190613189565b60405180910390a25b6000908152600960205260409020600201546001600160a01b0316979650505050505050565b6001600160a01b038116611d905760405162461bcd60e51b815260040161059490613079565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b600080600183039050600060048281548110611dce57fe5b60009182526020909120015460405163cc49ede760e01b81526001600160a01b039091169063cc49ede790611e07908890600401612ef0565b60206040518083038186803b158015611e1f57600080fd5b505afa158015611e33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e57919081019061253e565b90506001600160a01b038116156120075780856001826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611ea557600080fd5b505afa158015611eb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611edd919081019061282f565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611f1657600080fd5b505afa158015611f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f4e919081019061282f565b88604051602001611f63959493929190612e8c565b60408051601f19818403018152828252805160209182012060608401835260018085528285018a81526001600160a01b0388811687870181815260008681526009885288812099518a5593518986015551600290980180546001600160a01b031916988316989098179097558c168152600a84528481208054808401825590825284822001839055948552600b90925291909220805460ff19169092179091559350505b60006004838154811061201657fe5b60009182526020909120015460405163c810a3e360e01b81526001600160a01b039091169063c810a3e39061204f908990600401612ef0565b60206040518083038186803b15801561206757600080fd5b505afa15801561207b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061209f919081019061253e565b90506001600160a01b038116156122525780866000826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b1580156120ed57600080fd5b505afa158015612101573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612125919081019061282f565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561215e57600080fd5b505afa158015612172573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612196919081019061282f565b896040516020016121ab959493929190612e8c565b60408051601f19818403018152828252805160209182012060608401835260008085528285018b81526001600160a01b03888116878701818152858552600987528785209851895592516001808a01919091559251600290980180546001600160a01b031916988316989098179097558d168252600a84528482208054808301825590835284832001839055948152600b909252919020805460ff19169092179091559450505b505050505050565b6001600160a01b0381166122805760405162461bcd60e51b815260040161059490613069565b600080546040516001600160a01b03808516936201000090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b604080516060810182526000808252602082018190529181019190915290565b6040518060600160405280600081526020016000815260200160006001600160a01b031681525090565b80356117ee816132d7565b80516117ee816132d7565b60008083601f84011261235b57600080fd5b50813567ffffffffffffffff81111561237357600080fd5b60208301915083602082028301111561238b57600080fd5b9250929050565b600082601f8301126123a357600080fd5b81356123b66123b182613202565b6131db565b915081818352602084019350602081019050838560208402820111156123db57600080fd5b60005b8381101561240757816123f18882612333565b84525060209283019291909101906001016123de565b5050505092915050565b600082601f83011261242257600080fd5b81356124306123b182613202565b9150818183526020840193506020810190508385606084028201111561245557600080fd5b60005b83811015612407578161246b8882612499565b84525060209092019160609190910190600101612458565b80356117ee816132eb565b80516117ee816132eb565b6000606082840312156124ab57600080fd5b6124b560606131db565b905060006124c38484612483565b82525060206124d484848301612515565b60208301525060406124e8848285016124f4565b60408301525092915050565b80356117ee816132f4565b80356117ee816132fd565b80516117ee816132fd565b80356117ee81613306565b60006020828403121561253257600080fd5b600061054e8484612333565b60006020828403121561255057600080fd5b600061054e848461233e565b60008060008060008060008060e0898b03121561257857600080fd5b60006125848b8b612333565b98505060206125958b828c01612333565b97505060406125a68b828c01612333565b96505060606125b78b828c01612333565b95505060806125c88b828c01612333565b94505060a06125d98b828c01612333565b93505060c089013567ffffffffffffffff8111156125f657600080fd5b6126028b828c01612349565b92509250509295985092959890939650565b6000806040838503121561262757600080fd5b60006126338585612333565b9250506020612644858286016124ff565b9150509250929050565b6000806000806080858703121561266457600080fd5b60006126708787612333565b9450506020612681878288016124ff565b9350506040612692878288016124ff565b92505060606126a3878288016124ff565b91505092959194509250565b600080600080600060a086880312156126c757600080fd5b60006126d38888612333565b95505060206126e4888289016124ff565b94505060406126f5888289016124ff565b9350506060612706888289016124ff565b9250506080612717888289016124ff565b9150509295509295909350565b6000806000806040858703121561273a57600080fd5b843567ffffffffffffffff81111561275157600080fd5b61275d87828801612349565b9450945050602085013567ffffffffffffffff81111561277c57600080fd5b61278887828801612349565b95989497509550505050565b600080604083850312156127a757600080fd5b823567ffffffffffffffff8111156127be57600080fd5b6127ca85828601612392565b925050602083013567ffffffffffffffff8111156127e757600080fd5b61264485828601612411565b60006020828403121561280557600080fd5b600061054e848461248e565b60006020828403121561282357600080fd5b600061054e84846124ff565b60006020828403121561284157600080fd5b600061054e848461250a565b60006128598383612e29565b505060600190565b61286a81613256565b82525050565b61286a61287c82613256565b6132b9565b600061288c82613229565b612896818561322d565b93506128a183613223565b8060005b838110156128cf5781516128b9888261284d565b97506128c483613223565b9250506001016128a5565b509495945050505050565b61286a81613261565b61286a81613275565b60006128f960208361322d565b7f56657374696e67207265676973747279206164647265737320696e76616c6964815260200192915050565b6000612932600f8361322d565b6e0c2e4e4c2f2e640dad2e6dac2e8c6d608b1b815260200192915050565b600061295d601f8361322d565b7f746f6b656e206f776e65722063616e6e6f742062652030206164647265737300815260200192915050565b6000612996601b8361322d565b7f76657374696e672063616e6e6f74206265203020616464726573730000000000815260200192915050565b60006129cf60268361322d565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b6000612a17601e8361322d565b7f76657374696e67466163746f7279206164647265737320696e76616c69640000815260200192915050565b6000612a50600e8361322d565b6d185b5bdd5b9d081a5b9d985b1a5960921b815260200192915050565b6000612a7a60178361322d565b7f76657374696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612ab3600e8361322d565b6d76657374696e672065786973747360901b815260200192915050565b6000612add601c8361322d565b7f76657374696e674f776e6572206164647265737320696e76616c696400000000815260200192915050565b6000612b16602e8361322d565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656181526d191e481a5b9a5d1a585b1a5e995960921b602082015260400192915050565b6000612b66600c8361322d565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000612b8e60238361322d565b7f66656553686172696e67436f6c6c6563746f72206164647265737320696e76618152621b1a5960ea1b602082015260400192915050565b6000612bd3602c8361322d565b7f76657374696e67206372656174696f6e2074797065206d75737420626520677281526b06561746572207468616e20360a41b602082015260400192915050565b6000612c2160188361322d565b7f7265636569766572206164647265737320696e76616c69640000000000000000815260200192915050565b6000612c5a60158361322d565b7434b73b30b634b21030b236b4b71036b0b730b3b2b960591b815260200192915050565b6000612c8b60188361322d565b7f41646d696e206d616e61676572206973206e6f74207365740000000000000000815260200192915050565b6000612cc460108361322d565b6f0aadcdac2e8c6d0cac840d8cadccee8d60831b815260200192915050565b6000612cf060198361322d565b7f4c6f636b6564534f56206164647265737320696e76616c696400000000000000815260200192915050565b6000612d2960178361322d565b7f7374616b696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612d62600f8361322d565b6e1d1c985b9cd9995c8819985a5b1959608a1b815260200192915050565b6000612d8d60138361322d565b7214d3d5881859191c995cdcc81a5b9d985b1a59606a1b815260200192915050565b6000612dbc601683613236565b756b65792e61646d696e2e6d616e616765722e726f6c6560501b815260160192915050565b80546060830190612df181613280565b612dfb85826128da565b50612e0581613293565b612e126020860182612e83565b50612e1c816132a6565b61133a6040860182612e60565b80516060830190612e3a8482612e69565b506020820151612e4d6020850182612e69565b5060408201516105ab6040850182612861565b61286a81613241565b61286a81613272565b61286a612e7e82613272565b613272565b61286a8161324d565b6000612e988288612870565b601482019150612ea88287612e72565b602082019150612eb88286612e72565b602082019150612ec88285612e72565b602082019150612ed88284612e72565b5060200195945050505050565b60006117ee82612daf565b602081016117ee8284612861565b60e08101612f0c828a612861565b612f196020830189612861565b612f266040830188612861565b612f336060830187612e69565b612f406080830186612e69565b612f4d60a0830185612861565b612f5a60c0830184612861565b98975050505050505050565b60408101612f748285612861565b612f816020830184612e69565b9392505050565b60a08101612f968288612861565b612fa36020830187612e69565b612fb06040830186612e69565b612fbd6060830185612e69565b612fca6080830184612e69565b9695505050505050565b60208082528101612f818184612881565b602081016117ee82846128da565b6060810161300182866128da565b61300e6020830185612e83565b61054e6040830184612e60565b602081016117ee82846128e3565b602080825281016117ee816128ec565b602080825281016117ee81612925565b602080825281016117ee81612950565b602080825281016117ee81612989565b602080825281016117ee816129c2565b602080825281016117ee81612a0a565b602080825281016117ee81612a43565b602080825281016117ee81612a6d565b602080825281016117ee81612aa6565b602080825281016117ee81612ad0565b602080825281016117ee81612b09565b602080825281016117ee81612b59565b602080825281016117ee81612b81565b602080825281016117ee81612bc6565b602080825281016117ee81612c14565b602080825281016117ee81612c4d565b602080825281016117ee81612c7e565b602080825281016117ee81612cb7565b602080825281016117ee81612ce3565b602080825281016117ee81612d1c565b602080825281016117ee81612d55565b602080825281016117ee81612d80565b606081016117ee8284612de1565b602081016117ee8284612e69565b60408101612f748285612e69565b606081016131c18286612e69565b6131ce6020830185612e69565b61054e6040830184612861565b60405181810167ffffffffffffffff811182821017156131fa57600080fd5b604052919050565b600067ffffffffffffffff82111561321957600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b919050565b60ff1690565b6001600160801b031690565b63ffffffff1690565b60006117ee82613266565b151590565b6001600160a01b031690565b90565b60006117ee82613256565b60006117ee61328e83613272565b61323b565b60006117ee6132a1836132d1565b61324d565b60006117ee6132b4836132cb565b613241565b60006117ee8260006117ee8260601b90565b60281c90565b60081c90565b6132e081613256565b811461073157600080fd5b6132e081613261565b6132e081613241565b6132e081613272565b6132e08161324d56fea365627a7a72315820dc2ada767f6100aeabb2dd76de36097a63988151b323d89b951fad1348d343756c6578706572696d656e74616cf564736f6c63430005110040", + "implementation": "0x7acCf9Fcb7C0e89CA1dEe1918F282401f50e3d22", "devdoc": { "methods": { "addAdmin(address)": { @@ -1046,6 +1129,9 @@ "_vestingCreationType": "the type of vesting created(e.g. Origin, Bug Bounty etc.)" } }, + "getAdminManager()": { + "return": "Address of admin manager." + }, "getVesting(address)": { "details": "Calls a public getVestingAddr function with cliff and duration. This is to accomodate the existing logic for LockedSOVWe need to use LockedSOV.changeRegistryCliffAndDuration function very judiciouslyvestingCreationType 0 - LockedSOV", "params": { @@ -1080,6 +1166,11 @@ "_admin": "The addresses of the account to revoke permissions." } }, + "setAdminManager(address)": { + "params": { + "_newAdminManager": "The addresses of the account to grant permissions." + } + }, "setVestingFactory(address)": { "params": { "_vestingFactory": "the address of vesting factory contract" @@ -1122,6 +1213,9 @@ "createVestingAddr(address,uint256,uint256,uint256,uint256)": { "notice": "creates Vesting contract" }, + "getAdminManager()": { + "notice": "Return address of the admin manager." + }, "getTeamVesting(address,uint256,uint256,uint256)": { "notice": "returns team vesting contract address for the given token owner, cliff, duration" }, @@ -1146,6 +1240,12 @@ "removeAdmin(address)": { "notice": "Remove account from ACL." }, + "removeAdminManager()": { + "notice": "Set admin manager to 0 address." + }, + "setAdminManager(address)": { + "notice": "Set new admin manager." + }, "setVestingFactory(address)": { "notice": "sets vesting factory address" }, @@ -1160,7 +1260,7 @@ "storageLayout": { "storage": [ { - "astId": 52747, + "astId": 54561, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "_initialized", "offset": 0, @@ -1168,7 +1268,7 @@ "type": "t_bool" }, { - "astId": 52749, + "astId": 54563, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "_initializing", "offset": 1, @@ -1176,7 +1276,7 @@ "type": "t_bool" }, { - "astId": 52792, + "astId": 54606, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "_owner", "offset": 2, @@ -1184,7 +1284,7 @@ "type": "t_address" }, { - "astId": 59869, + "astId": 62325, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "admins", "offset": 0, @@ -1192,31 +1292,31 @@ "type": "t_mapping(t_address,t_bool)" }, { - "astId": 30484, + "astId": 31932, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingFactory", "offset": 0, "slot": "2", - "type": "t_contract(IVestingFactory)24652" + "type": "t_contract(IVestingFactory)26100" }, { - "astId": 30486, + "astId": 31934, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "lockedSOV", "offset": 0, "slot": "3", - "type": "t_contract(ILockedSOV)33603" + "type": "t_contract(ILockedSOV)35121" }, { - "astId": 30489, + "astId": 31937, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingRegistries", "offset": 0, "slot": "4", - "type": "t_array(t_contract(IVestingRegistry)24688)dyn_storage" + "type": "t_array(t_contract(IVestingRegistry)26136)dyn_storage" }, { - "astId": 30491, + "astId": 31939, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "SOV", "offset": 0, @@ -1224,7 +1324,7 @@ "type": "t_address" }, { - "astId": 30493, + "astId": 31941, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "staking", "offset": 0, @@ -1232,7 +1332,7 @@ "type": "t_address" }, { - "astId": 30495, + "astId": 31943, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "feeSharingCollector", "offset": 0, @@ -1240,7 +1340,7 @@ "type": "t_address" }, { - "astId": 30497, + "astId": 31945, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingOwner", "offset": 0, @@ -1248,15 +1348,15 @@ "type": "t_address" }, { - "astId": 30511, + "astId": 31959, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestings", "offset": 0, "slot": "9", - "type": "t_mapping(t_uint256,t_struct(Vesting)30507_storage)" + "type": "t_mapping(t_uint256,t_struct(Vesting)31955_storage)" }, { - "astId": 30516, + "astId": 31964, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingsOf", "offset": 0, @@ -1264,7 +1364,7 @@ "type": "t_mapping(t_address,t_array(t_uint256)dyn_storage)" }, { - "astId": 30520, + "astId": 31968, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "isVesting", "offset": 0, @@ -1272,12 +1372,12 @@ "type": "t_mapping(t_address,t_bool)" }, { - "astId": 30531, + "astId": 31979, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingCreationAndTypes", "offset": 0, "slot": "12", - "type": "t_mapping(t_address,t_struct(VestingCreationAndTypeDetails)30527_storage)" + "type": "t_mapping(t_address,t_struct(VestingCreationAndTypeDetails)31975_storage)" } ], "types": { @@ -1286,8 +1386,8 @@ "label": "address", "numberOfBytes": "20" }, - "t_array(t_contract(IVestingRegistry)24688)dyn_storage": { - "base": "t_contract(IVestingRegistry)24688", + "t_array(t_contract(IVestingRegistry)26136)dyn_storage": { + "base": "t_contract(IVestingRegistry)26136", "encoding": "dynamic_array", "label": "contract IVestingRegistry[]", "numberOfBytes": "32" @@ -1303,17 +1403,17 @@ "label": "bool", "numberOfBytes": "1" }, - "t_contract(ILockedSOV)33603": { + "t_contract(ILockedSOV)35121": { "encoding": "inplace", "label": "contract ILockedSOV", "numberOfBytes": "20" }, - "t_contract(IVestingFactory)24652": { + "t_contract(IVestingFactory)26100": { "encoding": "inplace", "label": "contract IVestingFactory", "numberOfBytes": "20" }, - "t_contract(IVestingRegistry)24688": { + "t_contract(IVestingRegistry)26136": { "encoding": "inplace", "label": "contract IVestingRegistry", "numberOfBytes": "20" @@ -1332,26 +1432,26 @@ "numberOfBytes": "32", "value": "t_bool" }, - "t_mapping(t_address,t_struct(VestingCreationAndTypeDetails)30527_storage)": { + "t_mapping(t_address,t_struct(VestingCreationAndTypeDetails)31975_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct VestingRegistryStorage.VestingCreationAndTypeDetails)", "numberOfBytes": "32", - "value": "t_struct(VestingCreationAndTypeDetails)30527_storage" + "value": "t_struct(VestingCreationAndTypeDetails)31975_storage" }, - "t_mapping(t_uint256,t_struct(Vesting)30507_storage)": { + "t_mapping(t_uint256,t_struct(Vesting)31955_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct VestingRegistryStorage.Vesting)", "numberOfBytes": "32", - "value": "t_struct(Vesting)30507_storage" + "value": "t_struct(Vesting)31955_storage" }, - "t_struct(Vesting)30507_storage": { + "t_struct(Vesting)31955_storage": { "encoding": "inplace", "label": "struct VestingRegistryStorage.Vesting", "members": [ { - "astId": 30502, + "astId": 31950, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingType", "offset": 0, @@ -1359,7 +1459,7 @@ "type": "t_uint256" }, { - "astId": 30504, + "astId": 31952, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingCreationType", "offset": 0, @@ -1367,7 +1467,7 @@ "type": "t_uint256" }, { - "astId": 30506, + "astId": 31954, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingAddress", "offset": 0, @@ -1377,12 +1477,12 @@ ], "numberOfBytes": "96" }, - "t_struct(VestingCreationAndTypeDetails)30527_storage": { + "t_struct(VestingCreationAndTypeDetails)31975_storage": { "encoding": "inplace", "label": "struct VestingRegistryStorage.VestingCreationAndTypeDetails", "members": [ { - "astId": 30522, + "astId": 31970, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "isSet", "offset": 0, @@ -1390,7 +1490,7 @@ "type": "t_bool" }, { - "astId": 30524, + "astId": 31972, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingType", "offset": 1, @@ -1398,7 +1498,7 @@ "type": "t_uint32" }, { - "astId": 30526, + "astId": 31974, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingCreationType", "offset": 5, diff --git a/deployment/deployments/rskSovrynTestnet/VestingRegistry_Implementation.json b/deployment/deployments/rskSovrynTestnet/VestingRegistry_Implementation.json index 6e0c68a5a..4abb02ce7 100644 --- a/deployment/deployments/rskSovrynTestnet/VestingRegistry_Implementation.json +++ b/deployment/deployments/rskSovrynTestnet/VestingRegistry_Implementation.json @@ -1,5 +1,5 @@ { - "address": "0x5C7280543de9Bf0e5C59A99d56ccF47B746C6d55", + "address": "0x7acCf9Fcb7C0e89CA1dEe1918F282401f50e3d22", "abi": [ { "anonymous": false, @@ -14,6 +14,50 @@ "name": "AdminAdded", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "oldAdminManager", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAdminManager", + "type": "address" + } + ], + "name": "AdminManagerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "removedAdminManager", + "type": "address" + } + ], + "name": "AdminManagerRemoved", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -412,6 +456,21 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "getAdminManager", + "outputs": [ + { + "internalType": "address", + "name": "_adminManager", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -774,6 +833,30 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "constant": false, + "inputs": [], + "name": "removeAdminManager", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_newAdminManager", + "type": "address" + } + ], + "name": "setAdminManager", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, { "constant": false, "inputs": [ @@ -999,43 +1082,43 @@ "type": "function" } ], - "transactionHash": "0xfee323a7a76f4daa010a7bbf9a9d44dcfce26527f4e73e4bdeb8e9c7bc83fa4f", + "transactionHash": "0x7c49f45d7db43aea11122d3ccc954c7630a63ea07244e51ba70c4d56535c3aed", "receipt": { "to": null, - "from": "0x8C9143221F2b72Fcef391893c3a02Cf0fE84f50b", - "contractAddress": "0x5C7280543de9Bf0e5C59A99d56ccF47B746C6d55", + "from": "0x13Be55487D37FE3C66EE7305e1e9C1ac85de75Ae", + "contractAddress": "0x7acCf9Fcb7C0e89CA1dEe1918F282401f50e3d22", "transactionIndex": 0, - "gasUsed": "3388527", - "logsBloom": "0x00000000000000000000000000000000000000001000000000800000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000000800000000002000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000400020000000000000000000000000000000000000000000200000000000000000000000", - "blockHash": "0x3e00c2af1f19c45cb7bf928435ce9d8eac26919b5d6fd351380abc33bfc43012", - "transactionHash": "0xfee323a7a76f4daa010a7bbf9a9d44dcfce26527f4e73e4bdeb8e9c7bc83fa4f", + "gasUsed": "3583456", + "logsBloom": "0x00000000000020000000000000020000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000020000000000000000001800000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000020000000000000000000000000000000000000000040000000000000000000000000", + "blockHash": "0x410d2ff00fb38dd30b91d0e50aa7936e344692e36a64b642e5986fb288b917e8", + "transactionHash": "0x7c49f45d7db43aea11122d3ccc954c7630a63ea07244e51ba70c4d56535c3aed", "logs": [ { "transactionIndex": 0, - "blockNumber": 3779337, - "transactionHash": "0xfee323a7a76f4daa010a7bbf9a9d44dcfce26527f4e73e4bdeb8e9c7bc83fa4f", - "address": "0x5C7280543de9Bf0e5C59A99d56ccF47B746C6d55", + "blockNumber": 4814405, + "transactionHash": "0x7c49f45d7db43aea11122d3ccc954c7630a63ea07244e51ba70c4d56535c3aed", + "address": "0x7acCf9Fcb7C0e89CA1dEe1918F282401f50e3d22", "topics": [ "0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0", "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000008c9143221f2b72fcef391893c3a02cf0fe84f50b" + "0x00000000000000000000000013be55487d37fe3c66ee7305e1e9c1ac85de75ae" ], "data": "0x", "logIndex": 0, - "blockHash": "0x3e00c2af1f19c45cb7bf928435ce9d8eac26919b5d6fd351380abc33bfc43012" + "blockHash": "0x410d2ff00fb38dd30b91d0e50aa7936e344692e36a64b642e5986fb288b917e8" } ], - "blockNumber": 3779337, - "cumulativeGasUsed": "3388527", + "blockNumber": 4814405, + "cumulativeGasUsed": "3583456", "status": 1, "byzantium": true }, "args": [], - "numDeployments": 1, - "solcInputHash": "cce46e19f0546ba1f0dc01e10dfdbc8a", - "metadata": "{\"compiler\":{\"version\":\"0.5.17+commit.d19bba13\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"AdminAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"AdminRemoved\",\"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\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"SOVTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"tokenOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"vesting\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"cliff\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"duration\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"TeamVestingCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"vesting\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"TokensStaked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"tokenOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"vesting\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"cliff\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"duration\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"VestingCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"vesting\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isSet\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"vestingType\",\"type\":\"uint32\"},{\"internalType\":\"uint128\",\"name\":\"vestingCreationType\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"struct VestingRegistryStorage.VestingCreationAndTypeDetails\",\"name\":\"vestingCreationAndType\",\"type\":\"tuple\"}],\"name\":\"VestingCreationAndTypesSet\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"SOV\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_admin\",\"type\":\"address\"}],\"name\":\"addAdmin\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokenOwners\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_vestingCreationTypes\",\"type\":\"uint256[]\"}],\"name\":\"addDeployedVestings\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokenOwners\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_vestingAddresses\",\"type\":\"address[]\"}],\"name\":\"addFourYearVestings\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"admins\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_duration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"createTeamVesting\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_duration\",\"type\":\"uint256\"}],\"name\":\"createVesting\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_duration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"createVestingAddr\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"feeSharingCollector\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_duration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"getTeamVesting\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"}],\"name\":\"getVesting\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_duration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"getVestingAddr\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vestingAddress\",\"type\":\"address\"}],\"name\":\"getVestingDetails\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"duration\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"}],\"name\":\"getVestingsOf\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"vestingType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"vestingCreationType\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"vestingAddress\",\"type\":\"address\"}],\"internalType\":\"struct VestingRegistryStorage.Vesting[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vestingFactory\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_SOV\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_staking\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_feeSharingCollector\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_vestingOwner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_lockedSOV\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"_vestingRegistries\",\"type\":\"address[]\"}],\"name\":\"initialize\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"isOwner\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vestingAddress\",\"type\":\"address\"}],\"name\":\"isTeamVesting\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isVesting\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vestingAddress\",\"type\":\"address\"}],\"name\":\"isVestingAddress\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isVestingAddr\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"lockedSOV\",\"outputs\":[{\"internalType\":\"contract ILockedSOV\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_vestingAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isSet\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"vestingType\",\"type\":\"uint32\"},{\"internalType\":\"uint128\",\"name\":\"vestingCreationType\",\"type\":\"uint128\"}],\"internalType\":\"struct VestingRegistryStorage.VestingCreationAndTypeDetails[]\",\"name\":\"_vestingCreationAndTypes\",\"type\":\"tuple[]\"}],\"name\":\"registerVestingToVestingCreationAndTypes\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_admin\",\"type\":\"address\"}],\"name\":\"removeAdmin\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vestingFactory\",\"type\":\"address\"}],\"name\":\"setVestingFactory\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vesting\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"stakeTokens\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"staking\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"transferSOV\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"vestingCreationAndTypes\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isSet\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"vestingType\",\"type\":\"uint32\"},{\"internalType\":\"uint128\",\"name\":\"vestingCreationType\",\"type\":\"uint128\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vestingFactory\",\"outputs\":[{\"internalType\":\"contract IVestingFactory\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vestingOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"vestingRegistries\",\"outputs\":[{\"internalType\":\"contract IVestingRegistry\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"vestings\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"vestingType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"vestingCreationType\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"vestingAddress\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"vestingsOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"methods\":{\"addAdmin(address)\":{\"params\":{\"_admin\":\"The addresses of the account to grant permissions.\"}},\"addDeployedVestings(address[],uint256[])\":{\"details\":\"migration of data from previous vesting registy contracts\"},\"addFourYearVestings(address[],address[])\":{\"params\":{\"_tokenOwners\":\"array of token owners\",\"_vestingAddresses\":\"array of vesting addresses\"}},\"createTeamVesting(address,uint256,uint256,uint256,uint256)\":{\"params\":{\"_amount\":\"the amount to be staked\",\"_cliff\":\"the cliff in seconds\",\"_duration\":\"the total duration in seconds\",\"_tokenOwner\":\"the owner of the tokens\",\"_vestingCreationType\":\"the type of vesting created(e.g. Origin, Bug Bounty etc.)\"}},\"createVesting(address,uint256,uint256,uint256)\":{\"details\":\"Calls a public createVestingAddr function with vestingCreationType. This is to accomodate the existing logic for LockedSOVvestingCreationType 0 = LockedSOV\",\"params\":{\"_amount\":\"the amount to be staked\",\"_cliff\":\"the cliff in seconds\",\"_duration\":\"the total duration in seconds\",\"_tokenOwner\":\"the owner of the tokens\"}},\"createVestingAddr(address,uint256,uint256,uint256,uint256)\":{\"params\":{\"_amount\":\"the amount to be staked\",\"_cliff\":\"the cliff in seconds\",\"_duration\":\"the total duration in seconds\",\"_tokenOwner\":\"the owner of the tokens\",\"_vestingCreationType\":\"the type of vesting created(e.g. Origin, Bug Bounty etc.)\"}},\"getVesting(address)\":{\"details\":\"Calls a public getVestingAddr function with cliff and duration. This is to accomodate the existing logic for LockedSOVWe need to use LockedSOV.changeRegistryCliffAndDuration function very judiciouslyvestingCreationType 0 - LockedSOV\",\"params\":{\"_tokenOwner\":\"the owner of the tokens\"}},\"getVestingAddr(address,uint256,uint256,uint256)\":{\"details\":\"Important: Please use this instead of getVesting function\"},\"isOwner()\":{\"details\":\"Returns true if the caller is the current owner.\"},\"isTeamVesting(address)\":{\"details\":\"check if the specific vesting address is team vesting or notread the vestingType from vestingCreationAndTypes storage\",\"params\":{\"_vestingAddress\":\"address of vesting contract\"},\"return\":\"true for teamVesting, false for normal vesting\"},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"registerVestingToVestingCreationAndTypes(address[],(bool,uint32,uint128)[])\":{\"details\":\"setter function to register existing vesting contract to vestingCreationAndTypes storageneed to set the function visilibty to public to support VestingCreationAndTypeDetails struct as parameter\",\"params\":{\"_vestingAddresses\":\"array of vesting address\",\"_vestingCreationAndTypes\":\"array for VestingCreationAndTypeDetails struct\"}},\"removeAdmin(address)\":{\"params\":{\"_admin\":\"The addresses of the account to revoke permissions.\"}},\"setVestingFactory(address)\":{\"params\":{\"_vestingFactory\":\"the address of vesting factory contract\"}},\"stakeTokens(address,uint256)\":{\"params\":{\"_amount\":\"the amount of tokens to stake\",\"_vesting\":\"the address of Vesting contract\"}},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"},\"transferSOV(address,uint256)\":{\"params\":{\"_amount\":\"the amount to be transferred\",\"_receiver\":\"the address of the SOV receiver\"}}}},\"userdoc\":{\"methods\":{\"addAdmin(address)\":{\"notice\":\"Add account to ACL.\"},\"addDeployedVestings(address[],uint256[])\":{\"notice\":\"adds vestings that were deployed in previous vesting registries\"},\"addFourYearVestings(address[],address[])\":{\"notice\":\"adds four year vestings to vesting registry logic\"},\"createTeamVesting(address,uint256,uint256,uint256,uint256)\":{\"notice\":\"creates Team Vesting contract\"},\"createVesting(address,uint256,uint256,uint256)\":{\"notice\":\"creates Vesting contract\"},\"createVestingAddr(address,uint256,uint256,uint256,uint256)\":{\"notice\":\"creates Vesting contract\"},\"getTeamVesting(address,uint256,uint256,uint256)\":{\"notice\":\"returns team vesting contract address for the given token owner, cliff, duration\"},\"getVesting(address)\":{\"notice\":\"returns vesting contract address for the given token owner\"},\"getVestingAddr(address,uint256,uint256,uint256)\":{\"notice\":\"public function that returns vesting contract address for the given token owner, cliff, duration\"},\"getVestingDetails(address)\":{\"notice\":\"returns cliff and duration for Vesting & TeamVesting contracts\"},\"getVestingsOf(address)\":{\"notice\":\"returns all vesting details for the given token owner\"},\"initialize(address,address,address,address,address,address,address[])\":{\"notice\":\"Replace constructor with initialize function for Upgradable Contracts This function will be called only once by the owner\"},\"isVestingAddress(address)\":{\"notice\":\"returns if the address is a vesting address\"},\"removeAdmin(address)\":{\"notice\":\"Remove account from ACL.\"},\"setVestingFactory(address)\":{\"notice\":\"sets vesting factory address\"},\"stakeTokens(address,uint256)\":{\"notice\":\"stakes tokens according to the vesting schedule\"},\"transferSOV(address,uint256)\":{\"notice\":\"transfers SOV tokens to given address\"}}}},\"settings\":{\"compilationTarget\":{\"contracts/governance/Vesting/VestingRegistryLogic.sol\":\"VestingRegistryLogic\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/governance/ApprovalReceiver.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\nimport \\\"./ErrorDecoder.sol\\\";\\nimport \\\"../token/IApproveAndCall.sol\\\";\\n\\n/**\\n * @title Base contract for receiving approval from SOV token.\\n */\\ncontract ApprovalReceiver is ErrorDecoder, IApproveAndCall {\\n modifier onlyThisContract() {\\n // Accepts calls only from receiveApproval function.\\n require(msg.sender == address(this), \\\"unauthorized\\\");\\n _;\\n }\\n\\n /**\\n * @notice Receives approval from SOV token.\\n * @param _data The data will be used for low level call.\\n */\\n function receiveApproval(\\n address _sender,\\n uint256 _amount,\\n address _token,\\n bytes calldata _data\\n ) external {\\n // Accepts calls only from SOV token.\\n require(msg.sender == _getToken(), \\\"unauthorized\\\");\\n require(msg.sender == _token, \\\"unauthorized\\\");\\n\\n // Only allowed methods.\\n bool isAllowed = false;\\n bytes4[] memory selectors = _getSelectors();\\n bytes4 sig = _getSig(_data);\\n for (uint256 i = 0; i < selectors.length; i++) {\\n if (sig == selectors[i]) {\\n isAllowed = true;\\n break;\\n }\\n }\\n require(isAllowed, \\\"method is not allowed\\\");\\n\\n // Check sender and amount.\\n address sender;\\n uint256 amount;\\n (, sender, amount) = abi.decode(\\n abi.encodePacked(bytes28(0), _data),\\n (bytes32, address, uint256)\\n );\\n require(sender == _sender, \\\"sender mismatch\\\");\\n require(amount == _amount, \\\"amount mismatch\\\");\\n\\n _call(_data);\\n }\\n\\n /**\\n * @notice Returns token address, only this address can be a sender for receiveApproval.\\n * @dev Should be overridden in child contracts, otherwise error will be thrown.\\n * @return By default, 0x. When overriden, the token address making the call.\\n */\\n function _getToken() internal view returns (address) {\\n return address(0);\\n }\\n\\n /**\\n * @notice Returns list of function selectors allowed to be invoked.\\n * @dev Should be overridden in child contracts, otherwise error will be thrown.\\n * @return By default, empty array. When overriden, allowed selectors.\\n */\\n function _getSelectors() internal pure returns (bytes4[] memory) {\\n return new bytes4[](0);\\n }\\n\\n /**\\n * @notice Makes call and reverts w/ enhanced error message.\\n * @param _data Error message as bytes.\\n */\\n function _call(bytes memory _data) internal {\\n (bool success, bytes memory returnData) = address(this).call(_data);\\n if (!success) {\\n if (returnData.length <= ERROR_MESSAGE_SHIFT) {\\n revert(\\\"receiveApproval: Transaction execution reverted.\\\");\\n } else {\\n revert(_addErrorMessage(\\\"receiveApproval: \\\", string(returnData)));\\n }\\n }\\n }\\n\\n /**\\n * @notice Extracts the called function selector, a hash of the signature.\\n * @dev The first four bytes of the call data for a function call specifies\\n * the function to be called. It is the first (left, high-order in big-endian)\\n * four bytes of the Keccak-256 (SHA-3) hash of the signature of the function.\\n * Solidity doesn't yet support a casting of byte[4] to bytes4.\\n * Example:\\n * msg.data:\\n * 0xcdcd77c000000000000000000000000000000000000000000000000000000000000\\n * 000450000000000000000000000000000000000000000000000000000000000000001\\n * selector (or method ID): 0xcdcd77c0\\n * signature: baz(uint32,bool)\\n * @param _data The msg.data from the low level call.\\n * @return sig First 4 bytes of msg.data i.e. the selector, hash of the signature.\\n */\\n function _getSig(bytes memory _data) internal pure returns (bytes4 sig) {\\n assembly {\\n sig := mload(add(_data, 32))\\n }\\n }\\n}\\n\",\"keccak256\":\"0xfec344456774fa83b0885dd71825ccb6780be8db63c394f3ca09107977c65429\"},\"contracts/governance/ErrorDecoder.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Base contract to properly handle returned data on failed calls\\n * @dev On EVM if the return data length of a call is less than 68,\\n * then the transaction fails silently without a revert message!\\n *\\n * As described in the Solidity documentation\\n * https://solidity.readthedocs.io/en/v0.5.17/control-structures.html#revert\\n * the revert reason is an ABI-encoded string consisting of:\\n * 0x08c379a0 // Function selector (method id) for \\\"Error(string)\\\" signature\\n * 0x0000000000000000000000000000000000000000000000000000000000000020 // Data offset\\n * 0x000000000000000000000000000000000000000000000000000000000000001a // String length\\n * 0x4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 // String data\\n *\\n * Another example, debug data from test:\\n * 0x08c379a0\\n * 0000000000000000000000000000000000000000000000000000000000000020\\n * 0000000000000000000000000000000000000000000000000000000000000034\\n * 54696d656c6f636b3a3a73657444656c61793a2044656c6179206d7573742065\\n * 7863656564206d696e696d756d2064656c61792e000000000000000000000000\\n *\\n * Parsed into:\\n * Data offset: 20\\n * Length: 34\\n * Error message:\\n * 54696d656c6f636b3a3a73657444656c61793a2044656c6179206d7573742065\\n * 7863656564206d696e696d756d2064656c61792e000000000000000000000000\\n */\\ncontract ErrorDecoder {\\n uint256 constant ERROR_MESSAGE_SHIFT = 68; // EVM silent revert error string length\\n\\n /**\\n * @notice Concats two error strings taking into account ERROR_MESSAGE_SHIFT.\\n * @param str1 First string, usually a hardcoded context written by dev.\\n * @param str2 Second string, usually the error message from the reverted call.\\n * @return The concatenated error string\\n */\\n function _addErrorMessage(string memory str1, string memory str2)\\n internal\\n pure\\n returns (string memory)\\n {\\n bytes memory bytesStr1 = bytes(str1);\\n bytes memory bytesStr2 = bytes(str2);\\n string memory str12 =\\n new string(bytesStr1.length + bytesStr2.length - ERROR_MESSAGE_SHIFT);\\n bytes memory bytesStr12 = bytes(str12);\\n uint256 j = 0;\\n for (uint256 i = 0; i < bytesStr1.length; i++) {\\n bytesStr12[j++] = bytesStr1[i];\\n }\\n for (uint256 i = ERROR_MESSAGE_SHIFT; i < bytesStr2.length; i++) {\\n bytesStr12[j++] = bytesStr2[i];\\n }\\n return string(bytesStr12);\\n }\\n}\\n\",\"keccak256\":\"0xa0fa7986924aab574ca9e7c265f8c7bf00671ba1d86dbad143df7c14455f1c6a\"},\"contracts/governance/IFeeSharingCollector.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for contract governance/FeeSharingCollector/FeeSharingCollector.sol\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n * */\\ninterface IFeeSharingCollector {\\n function withdrawFees(address[] calldata _token) external;\\n\\n function transferTokens(address _token, uint96 _amount) external;\\n\\n function withdraw(\\n address _loanPoolToken,\\n uint32 _maxCheckpoints,\\n address _receiver\\n ) external;\\n}\\n\",\"keccak256\":\"0x7794cb434d9395ea983dcf8ded48db5b68897a338429320f60172c4caa47fb40\"},\"contracts/governance/Staking/interfaces/IStaking.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\npragma experimental ABIEncoderV2;\\n\\n/**\\n * @title Interface for Staking modules governance/Staking/modules\\n */\\n\\ninterface IStaking {\\n /*************************** StakingAdminModule ***************************/\\n\\n /**\\n * @notice Add account to Admins ACL.\\n * @param _admin The addresses of the account to grant permissions.\\n * */\\n function addAdmin(address _admin) external;\\n\\n /**\\n * @notice Remove account from Admins ACL.\\n * @param _admin The addresses of the account to revoke permissions.\\n * */\\n function removeAdmin(address _admin) external;\\n\\n /**\\n * @notice Add account to pausers ACL.\\n * @param _pauser The address to grant pauser permissions.\\n * */\\n function addPauser(address _pauser) external;\\n\\n /**\\n * @notice Remove account from pausers ACL.\\n * @param _pauser The address to grant pauser permissions.\\n * */\\n function removePauser(address _pauser) external;\\n\\n /**\\n * @notice Pause/unpause contract\\n * @param _pause true when pausing, false when unpausing\\n * */\\n function pauseUnpause(bool _pause) external;\\n\\n /**\\n * @notice Freeze contract - disable all functions\\n * @param _freeze true when freezing, false when unfreezing\\n * @dev When freezing, pause is always applied too. When unfreezing, the contract is left in paused stated.\\n * */\\n function freezeUnfreeze(bool _freeze) external;\\n\\n /**\\n * @notice Allows the owner to set a fee sharing proxy contract.\\n * We need it for unstaking with slashing.\\n * @param _feeSharing The address of FeeSharingCollectorProxy contract.\\n * */\\n function setFeeSharing(address _feeSharing) external;\\n\\n /**\\n * @notice Allow the owner to set weight scaling.\\n * We need it for unstaking with slashing.\\n * @param _weightScaling The weight scaling.\\n * */\\n function setWeightScaling(uint96 _weightScaling) external;\\n\\n /**\\n * @notice Allow the owner to set a new staking contract.\\n * As a consequence it allows the stakers to migrate their positions\\n * to the new contract.\\n * @dev Doesn't have any influence as long as migrateToNewStakingContract\\n * is not implemented.\\n * @param _newStakingContract The address of the new staking contract.\\n * */\\n function setNewStakingContract(address _newStakingContract) external;\\n\\n /**\\n * @notice Allow a staker to migrate his positions to the new staking contract.\\n * @dev Staking contract needs to be set before by the owner.\\n * Currently not implemented, just needed for the interface.\\n * In case it's needed at some point in the future,\\n * the implementation needs to be changed first.\\n * */\\n function migrateToNewStakingContract() external; // dummy - not implemented as of now\\n\\n /*************************** StakingGovernanceModule ***************************/\\n\\n /**\\n * @notice Compute the total voting power at a given time.\\n * @param blockNumber The block number, needed for checkpointing.\\n * @param time The timestamp for which to calculate the total voting power.\\n * @return The total voting power at the given time.\\n * */\\n function getPriorTotalVotingPower(uint32 blockNumber, uint256 time)\\n external\\n view\\n returns (uint96);\\n\\n /**\\n * @notice Get the current votes balance for a user account.\\n * @param account The address to get votes balance.\\n * @dev This is a wrapper to simplify arguments. The actual computation is\\n * performed on WeightedStaking parent contract.\\n * @return The number of current votes for a user account.\\n * */\\n function getCurrentVotes(address account) external view returns (uint96);\\n\\n /**\\n * @notice Determine the prior number of votes for a delegatee as of a block number.\\n * Iterate through checkpoints adding up voting power.\\n * @dev Block number must be a finalized block or else this function will revert\\n * to prevent misinformation.\\n * Used for Voting, not for fee sharing.\\n * @param account The address of the account to check.\\n * @param blockNumber The block number to get the vote balance at.\\n * @param date The staking date to compute the power for.\\n * @return The number of votes the delegatee had as of the given block.\\n * */\\n function getPriorVotes(\\n address account,\\n uint256 blockNumber,\\n uint256 date\\n ) external view returns (uint96);\\n\\n /**\\n * @notice Determine the prior number of stake for an account as of a block number.\\n * @dev Block number must be a finalized block or else this function will\\n * revert to prevent misinformation.\\n * @param account The address of the account to check.\\n * @param date The staking date to compute the power for.\\n * @param blockNumber The block number to get the vote balance at.\\n * @return The number of votes the account had as of the given block.\\n * */\\n function getPriorStakeByDateForDelegatee(\\n address account,\\n uint256 date,\\n uint256 blockNumber\\n ) external view returns (uint96);\\n\\n /**\\n * @notice Determine the prior number of stake for an unlocking date as of a block number.\\n * @dev Block number must be a finalized block or else this function will\\n * revert to prevent misinformation.\\n * TODO: WeightedStaking::getPriorTotalStakesForDate should probably better\\n * be internal instead of a public function.\\n * @param date The date to check the stakes for.\\n * @param blockNumber The block number to get the vote balance at.\\n * @return The number of votes the account had as of the given block.\\n * */\\n function getPriorTotalStakesForDate(uint256 date, uint256 blockNumber)\\n external\\n view\\n returns (uint96);\\n\\n /**\\n * @notice Delegate votes from `msg.sender` which are locked until lockDate to `delegatee`.\\n * @param delegatee The address to delegate votes to.\\n * @param lockDate the date if the position to delegate.\\n * */\\n function delegate(address delegatee, uint256 lockDate) external;\\n\\n /**\\n * @notice Delegates votes from signatory to a delegatee account.\\n * Voting with EIP-712 Signatures.\\n *\\n * Voting power can be delegated to any address, and then can be used to\\n * vote on proposals. A key benefit to users of by-signature functionality\\n * is that they can create a signed vote transaction for free, and have a\\n * trusted third-party spend rBTC(or ETH) on gas fees and write it to the\\n * blockchain for them.\\n *\\n * The third party in this scenario, submitting the SOV-holder\\u2019s signed\\n * transaction holds a voting power that is for only a single proposal.\\n * The signatory still holds the power to vote on their own behalf in\\n * the proposal if the third party has not yet published the signed\\n * transaction that was given to them.\\n *\\n * @dev The signature needs to be broken up into 3 parameters, known as\\n * v, r and s:\\n * const r = '0x' + sig.substring(2).substring(0, 64);\\n * const s = '0x' + sig.substring(2).substring(64, 128);\\n * const v = '0x' + sig.substring(2).substring(128, 130);\\n *\\n * @param delegatee The address to delegate votes to.\\n * @param lockDate The date until which the position is locked.\\n * @param nonce The contract state required to match the signature.\\n * @param expiry The time at which to expire the signature.\\n * @param v The recovery byte of the signature.\\n * @param r Half of the ECDSA signature pair.\\n * @param s Half of the ECDSA signature pair.\\n * */\\n function delegateBySig(\\n address delegatee,\\n uint256 lockDate,\\n uint256 nonce,\\n uint256 expiry,\\n uint8 v,\\n bytes32 r,\\n bytes32 s\\n ) external;\\n\\n /*************************** StakingStakeModule ***************************/\\n\\n /**\\n * @notice Stake the given amount for the given duration of time.\\n * @param amount The number of tokens to stake.\\n * @param until Timestamp indicating the date until which to stake.\\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\\n * @param delegatee The address of the delegatee or 0x0 if there is none.\\n * */\\n function stake(\\n uint96 amount,\\n uint256 until,\\n address stakeFor,\\n address delegatee\\n ) external;\\n\\n /**\\n * @notice Stake the given amount for the given duration of time.\\n * @dev This function will be invoked from receiveApproval\\n * @dev SOV.approveAndCall -> this.receiveApproval -> this.stakeWithApproval\\n * @param sender The sender of SOV.approveAndCall\\n * @param amount The number of tokens to stake.\\n * @param until Timestamp indicating the date until which to stake.\\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\\n * @param delegatee The address of the delegatee or 0x0 if there is none.\\n * */\\n function stakeWithApproval(\\n address sender,\\n uint96 amount,\\n uint256 until,\\n address stakeFor,\\n address delegatee\\n ) external;\\n\\n /**\\n * @notice Receives approval from SOV token.\\n * @param _data The data will be used for low level call.\\n */\\n function receiveApproval(\\n address _sender,\\n uint256 _amount,\\n address _token,\\n bytes calldata _data\\n ) external;\\n\\n /**\\n * @notice Extend the staking duration until the specified date.\\n * @param previousLock The old unlocking timestamp.\\n * @param until The new unlocking timestamp in seconds.\\n * */\\n function extendStakingDuration(uint256 previousLock, uint256 until) external;\\n\\n /**\\n * @dev DO NOT USE this misspelled function. Use stakeBySchedule function instead.\\n * This function cannot be deprecated while we have non-upgradeable vesting contracts.\\n * */\\n function stakesBySchedule(\\n uint256 amount,\\n uint256 cliff,\\n uint256 duration,\\n uint256 intervalLength,\\n address stakeFor,\\n address delegatee\\n ) external;\\n\\n /**\\n * @notice Stake tokens according to the vesting schedule.\\n * @param amount The amount of tokens to stake.\\n * @param cliff The time interval to the first withdraw.\\n * @param duration The staking duration.\\n * @param intervalLength The length of each staking interval when cliff passed.\\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\\n * @param delegatee The address of the delegatee or 0x0 if there is none.\\n * */\\n function stakeBySchedule(\\n uint256 amount,\\n uint256 cliff,\\n uint256 duration,\\n uint256 intervalLength,\\n address stakeFor,\\n address delegatee\\n ) external;\\n\\n /**\\n * @notice Get the number of staked tokens held by the user account.\\n * @dev Iterate checkpoints adding up stakes.\\n * @param account The address of the account to get the balance of.\\n * @return The number of tokens held.\\n * */\\n function balanceOf(address account) external view returns (uint96 balance);\\n\\n /**\\n * @notice Get the current number of tokens staked for a day.\\n * @param lockedTS The timestamp to get the staked tokens for.\\n * */\\n function getCurrentStakedUntil(uint256 lockedTS) external view returns (uint96);\\n\\n /**\\n * @notice Get list of stakes for a user account.\\n * @param account The address to get stakes.\\n * @return The arrays of dates and stakes.\\n * */\\n function getStakes(address account)\\n external\\n view\\n returns (uint256[] memory dates, uint96[] memory stakes);\\n\\n /**\\n * @notice Unstaking is possible every 2 weeks only. This means, to\\n * calculate the key value for the staking checkpoints, we need to\\n * map the intended timestamp to the closest available date.\\n * @param timestamp The unlocking timestamp.\\n * @return The actual unlocking date (might be up to 2 weeks shorter than intended).\\n * */\\n function timestampToLockDate(uint256 timestamp) external view returns (uint256);\\n\\n /*************************** StakingStorageModule ***************************/\\n\\n /// @notice The maximum duration to stake tokens\\n /// @return MAX_DURATION to stake tokens\\n function getStorageMaxDurationToStakeTokens() external pure returns (uint256);\\n\\n /// @notice The maximum possible voting weight before adding +1 (actually 10, but need 9 for computation).\\n /// @return uint256(MAX_VOTING_WEIGHT);\\n function getStorageMaxVotingWeight() external pure returns (uint256);\\n\\n /// @notice weight is multiplied with this factor (for allowing decimals, like 1.2x).\\n /// @dev MAX_VOTING_WEIGHT * WEIGHT_FACTOR needs to be < 792, because there are 100,000,000 SOV with 18 decimals\\n /// @return uint256(WEIGHT_FACTOR);\\n function getStorageWeightFactor() external pure returns (uint256);\\n\\n /// @return uint256(DEFAULT_WEIGHT_SCALING);\\n function getStorageDefaultWeightScaling() external pure returns (uint256);\\n\\n /// @notice return (uint256(MIN_WEIGHT_SCALING), uint256(MAX_WEIGHT_SCALING))\\n function getStorageRangeForWeightScaling()\\n external\\n pure\\n returns (uint256 minWeightScaling, uint256 maxWeightScaling);\\n\\n /// @notice The EIP-712 typehash for the contract's domain.\\n /// @return uint256(DOMAIN_TYPEHASH);\\n function getStorageDomainTypehash() external pure returns (uint256);\\n\\n /// @notice The EIP-712 typehash for the delegation struct used by the contract.\\n /// @return uint256(DELEGATION_TYPEHASH);\\n function getStorageDelegationTypehash() external pure returns (uint256);\\n\\n /// @return name;\\n function getStorageName() external view returns (string memory);\\n\\n /// AUTOGENERATED FUNCTIONS FROM THE STAKING STORAGE PUBLIC VARIABLES ///\\n\\n /// @notice The timestamp of contract creation. Base for the staking period calculation.\\n function kickoffTS() external view returns (uint256);\\n\\n /// @notice The token to be staked\\n function SOVToken() external view returns (address);\\n\\n /// @notice Stakers delegated voting power\\n /// @param staker - the delegating address\\n /// @param until - delegated voting\\n /// @return _delegate - voting power delegated to address\\n function delegates(address staker, uint256 until) external view returns (address _delegate);\\n\\n /// @notice If this flag is set to true, all tokens are unlocked immediately\\n /// see function unlockAllTokens() for details\\n function allUnlocked() external view returns (bool);\\n\\n /// @notice Used for stake migrations to a new staking contract with a different storage structure\\n function newStakingContract() external view returns (address);\\n\\n /// CHECKPOINTS\\n struct Checkpoint {\\n uint32 fromBlock;\\n uint96 stake;\\n }\\n\\n /// @notice A record of tokens to be unstaked at a given time in total.\\n /// For total voting power computation. Voting weights get adjusted bi-weekly.\\n /// @dev totalStakingCheckpoints[date][index] is a checkpoint\\n function totalStakingCheckpoints(uint256 date, uint32 index)\\n external\\n view\\n returns (Checkpoint memory);\\n\\n /// @notice The number of total staking checkpoints for each date.\\n /// @dev numTotalStakingCheckpoints[date] is a number.\\n function numTotalStakingCheckpoints(uint256 date)\\n external\\n view\\n returns (bytes32 checkpointsQty);\\n\\n /// @notice A record of tokens to be unstaked at a given time which were delegated to a certain address.\\n /// For delegatee voting power computation. Voting weights get adjusted bi-weekly.\\n /// @dev delegateStakingCheckpoints[delegatee][date][index] is a checkpoint.\\n function delegateStakingCheckpoints(\\n address delagatee,\\n uint256 date,\\n uint32 index\\n ) external view returns (Checkpoint memory);\\n\\n /// @notice The number of total staking checkpoints for each date per delegate.\\n /// @dev numDelegateStakingCheckpoints[delegatee][date] is a number.\\n function numDelegateStakingCheckpoints(address delegatee, uint256 date)\\n external\\n view\\n returns (bytes32 checkpointsQty);\\n\\n /// @notice A record of tokens to be unstaked at a given time which per user address (address -> lockDate -> stake checkpoint)\\n /// @dev userStakingCheckpoints[user][date][index] is a checkpoint.\\n function userStakingCheckpoints(\\n address user,\\n uint256 date,\\n uint32 index\\n ) external view returns (Checkpoint memory);\\n\\n /// @notice The number of total staking checkpoints for each date per user.\\n /// @dev numUserStakingCheckpoints[user][date] is a number\\n function numUserStakingCheckpoints(address user, uint256 date)\\n external\\n view\\n returns (uint32 checkpointsQty);\\n\\n /// @notice A record of states for signing / validating signatures\\n /// @dev nonces[user] is a number.\\n function nonces(address user) external view returns (uint256 nonce);\\n\\n /// SLASHING ///\\n\\n /// @notice the address of FeeSharingCollectorProxy contract, we need it for unstaking with slashing.\\n function feeSharing() external view returns (address);\\n\\n /// @notice used for weight scaling when unstaking with slashing.\\n /// @return uint96 DEFAULT_WEIGHT_SCALING\\n function weightScaling() external view returns (uint96);\\n\\n /// @notice List of vesting contracts, tokens for these contracts won't be slashed if unstaked by governance.\\n /// @dev vestingWhitelist[contract] is true/false.\\n function vestingWhitelist(address isWhitelisted) external view returns (bool);\\n\\n /// @dev user => flag whether user has admin role.\\n /// @dev multisig should be an admin, admin can invoke only governanceWithdrawVesting function,\\n /// \\tthis function works only with Team Vesting contracts\\n function admins(address isAdmin) external view returns (bool);\\n\\n /// @dev vesting contract code hash => flag whether it's registered code hash\\n function vestingCodeHashes(bytes32 vestingLogicCodeHash) external view returns (bool);\\n\\n /// @notice A record of tokens to be unstaked from vesting contract at a given time (lockDate -> vest checkpoint)\\n /// @dev vestingCheckpoints[date][index] is a checkpoint.\\n function vestingCheckpoints(uint256 date, uint32 index)\\n external\\n view\\n returns (Checkpoint memory);\\n\\n /// @notice The number of total vesting checkpoints for each date.\\n /// @dev numVestingCheckpoints[date] is a number.\\n function numVestingCheckpoints(uint256 date) external view returns (uint32 checkpointsQty);\\n\\n ///@notice vesting registry contract PROXY address\\n function vestingRegistryLogic() external view returns (address);\\n\\n /// @dev user => flag whether user has pauser role.\\n function pausers(address isPauser) external view returns (bool);\\n\\n /// @dev Staking contract is paused\\n function paused() external view returns (bool);\\n\\n /// @dev Staking contract is frozen\\n function frozen() external view returns (bool);\\n\\n /*************************** StakingVestingModule ***************************/\\n\\n /**\\n * @notice Return flag whether the given address is a registered vesting contract.\\n * @param stakerAddress the address to check\\n */\\n function isVestingContract(address stakerAddress) external view returns (bool);\\n\\n /**\\n * @notice Remove vesting contract's code hash to a map of code hashes.\\n * @param vesting The address of Vesting contract.\\n * @dev We need it to use isVestingContract() function instead of isContract()\\n */\\n function removeContractCodeHash(address vesting) external;\\n\\n /**\\n * @notice Add vesting contract's code hash to a map of code hashes.\\n * @param vesting The address of Vesting contract.\\n * @dev We need it to use isVestingContract() function instead of isContract()\\n */\\n function addContractCodeHash(address vesting) external;\\n\\n /**\\n * @notice Determine the prior number of vested stake for an account until a\\n * certain lock date as of a block number.\\n * @dev Block number must be a finalized block or else this function\\n * will revert to prevent misinformation.\\n * @param date The lock date.\\n * @param blockNumber The block number to get the vote balance at.\\n * @return The number of votes the account had as of the given block.\\n * */\\n function getPriorVestingStakeByDate(uint256 date, uint256 blockNumber)\\n external\\n view\\n returns (uint96);\\n\\n /**\\n * @notice Compute the voting power for a specific date.\\n * Power = stake * weight\\n * @param date The staking date to compute the power for. Adjusted to the next valid lock date, if necessary.\\n * @param startDate The date for which we need to know the power of the stake.\\n * @param blockNumber The block number, needed for checkpointing.\\n * @return The stacking power.\\n * */\\n function weightedVestingStakeByDate(\\n uint256 date,\\n uint256 startDate,\\n uint256 blockNumber\\n ) external view returns (uint96 power);\\n\\n /**\\n * @notice Determine the prior weighted vested amount for an account as of a block number.\\n * Iterate through checkpoints adding up voting power.\\n * @dev Block number must be a finalized block or else this function will\\n * revert to prevent misinformation.\\n * Used for fee sharing, not voting.\\n * TODO: WeightedStaking::getPriorVestingWeightedStake is using the variable name \\\"votes\\\"\\n * to add up token stake, and that could be misleading.\\n *\\n * @param blockNumber The block number to get the vote balance at.\\n * @param date The staking date to compute the power for.\\n * @return The weighted stake the account had as of the given block.\\n * */\\n function getPriorVestingWeightedStake(uint256 blockNumber, uint256 date)\\n external\\n view\\n returns (uint96 votes);\\n\\n /**\\n * @notice Determine the prior number of stake for an account until a\\n * certain lock date as of a block number.\\n * @dev Block number must be a finalized block or else this function\\n * will revert to prevent misinformation.\\n * @param account The address of the account to check.\\n * @param date The lock date.\\n * @param blockNumber The block number to get the vote balance at.\\n * @return The number of votes the account had as of the given block.\\n * */\\n function getPriorUserStakeByDate(\\n address account,\\n uint256 date,\\n uint256 blockNumber\\n ) external view returns (uint96);\\n\\n /**\\n * @notice Sets the users' vesting stakes for a giving lock dates and writes checkpoints.\\n * @param lockedDates The arrays of lock dates.\\n * @param values The array of values to add to the staked balance.\\n */\\n function setVestingStakes(uint256[] calldata lockedDates, uint96[] calldata values) external;\\n\\n /**\\n * @notice sets vesting registry\\n * @param _vestingRegistryProxy the address of vesting registry proxy contract\\n * @dev _vestingRegistryProxy can be set to 0 as this function can be reused by\\n * various other functionalities without the necessity of linking it with Vesting Registry\\n */\\n function setVestingRegistry(address _vestingRegistryProxy) external;\\n\\n /*************************** StakingWithdrawModule ***************************/\\n\\n /**\\n * @notice Withdraw the given amount of tokens if they are unlocked.\\n * @param amount The number of tokens to withdraw.\\n * @param until The date until which the tokens were staked.\\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\\n * */\\n function withdraw(\\n uint96 amount,\\n uint256 until,\\n address receiver\\n ) external;\\n\\n /**\\n * @notice Withdraw the given amount of tokens.\\n * @param amount The number of tokens to withdraw.\\n * @param until The date until which the tokens were staked.\\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\\n * @dev Can be invoked only by whitelisted contract passed to governanceWithdrawVesting\\n * @dev **WARNING** This function should not be no longer used by Sovryn Protocol.\\n * Sovryn protocol will use the cancelTeamVesting function for the withdrawal moving forward.\\n * */\\n function governanceWithdraw(\\n uint96 amount,\\n uint256 until,\\n address receiver\\n ) external;\\n\\n /**\\n * @notice Withdraw tokens for vesting contract.\\n * @param vesting The address of Vesting contract.\\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\\n * @dev Can be invoked only by whitelisted contract passed to governanceWithdrawVesting.\\n * */\\n function governanceWithdrawVesting(address vesting, address receiver) external;\\n\\n /**\\n * @notice Get available and punished amount for withdrawing.\\n * @param amount The number of tokens to withdraw.\\n * @param until The date until which the tokens were staked.\\n * */\\n function getWithdrawAmounts(uint96 amount, uint256 until)\\n external\\n view\\n returns (uint96, uint96);\\n\\n /**\\n * @notice Allow the owner to unlock all tokens in case the staking contract\\n * is going to be replaced\\n * Note: Not reversible on purpose. once unlocked, everything is unlocked.\\n * The owner should not be able to just quickly unlock to withdraw his own\\n * tokens and lock again.\\n * @dev Last resort.\\n * */\\n function unlockAllTokens() external;\\n\\n /*************************** WeightedStakingModule ***************************/\\n\\n /**\\n * @notice Determine the prior weighted stake for an account as of a block number.\\n * Iterate through checkpoints adding up voting power.\\n * @dev Block number must be a finalized block or else this function will\\n * revert to prevent misinformation.\\n * Used for fee sharing, not voting.\\n *\\n * @param account The address of the account to check.\\n * @param blockNumber The block number to get the vote balance at.\\n * @param date The date/timestamp of the unstaking time.\\n * @return The weighted stake the account had as of the given block.\\n * */\\n function getPriorWeightedStake(\\n address account,\\n uint256 blockNumber,\\n uint256 date\\n ) external view returns (uint96 priorWeightedStake);\\n\\n /**\\n * @notice Compute the voting power for a specific date.\\n * Power = stake * weight\\n * TODO: WeightedStaking::weightedStakeByDate should probably better\\n * be internal instead of a public function.\\n * @param account The user address.\\n * @param date The staking date to compute the power for.\\n * @param startDate The date for which we need to know the power of the stake.\\n * @param blockNumber The block number, needed for checkpointing.\\n * @return The stacking power.\\n * */\\n function weightedStakeByDate(\\n address account,\\n uint256 date,\\n uint256 startDate,\\n uint256 blockNumber\\n ) external view returns (uint96 power);\\n\\n /**\\n * @notice Compute the weight for a specific date.\\n * @param date The unlocking date.\\n * @param startDate We compute the weight for the tokens staked until 'date' on 'startDate'.\\n * @return The weighted stake the account had as of the given block.\\n * */\\n function computeWeightByDate(uint256 date, uint256 startDate)\\n external\\n pure\\n returns (uint96 weight);\\n\\n /**\\n * @notice Returns public constant MAX_DURATION\\n * preserved for backwards compatibility\\n * Use getStorageMaxDurationToStakeTokens()\\n * @return uint96 MAX_DURATION for staking\\n **/\\n function MAX_DURATION() external view returns (uint256);\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() external view returns (address);\\n\\n /**\\n * @dev Returns true if the caller is the current owner.\\n */\\n function isOwner() external view returns (bool);\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) external;\\n\\n /**\\n * @notice Governance withdraw vesting directly through staking contract.\\n * This direct withdraw vesting solves the out of gas issue when there are too many iterations when withdrawing.\\n * This function only allows cancelling vesting contract of the TeamVesting type.\\n *\\n * @param vesting The vesting address.\\n * @param receiver The receiving address.\\n * @param startFrom The start value for the iterations.\\n */\\n function cancelTeamVesting(\\n address vesting,\\n address receiver,\\n uint256 startFrom\\n ) external;\\n\\n /**\\n * @notice Max iteration for direct withdrawal from staking to prevent out of gas issue.\\n *\\n * @return max iteration value.\\n */\\n function getMaxVestingWithdrawIterations() external view returns (uint256);\\n\\n /**\\n * @dev set max withdraw iterations.\\n *\\n * @param maxIterations new max iterations value.\\n */\\n function setMaxVestingWithdrawIterations(uint256 maxIterations) external;\\n}\\n\",\"keccak256\":\"0x5d400d10ac5d54f0a1103cf34a1048e585f5c6f20be3f5192b5bc7b277e5bfb0\"},\"contracts/governance/Vesting/ITeamVesting.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for TeamVesting contract.\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n * This interface is used by Staking contract to call governanceWithdrawTokens\\n * function having the vesting contract instance address.\\n */\\ninterface ITeamVesting {\\n function startDate() external view returns (uint256);\\n\\n function cliff() external view returns (uint256);\\n\\n function endDate() external view returns (uint256);\\n\\n function duration() external view returns (uint256);\\n\\n function tokenOwner() external view returns (address);\\n\\n function governanceWithdrawTokens(address receiver) external;\\n}\\n\",\"keccak256\":\"0xeb926db5c0681a7bdbc70c3d25df2551a6124e191654b3b8a0f810b0d03ec66f\"},\"contracts/governance/Vesting/IVesting.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for Vesting contract.\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n * This interface is used by VestingLogic contract to implement stakeTokens function\\n * and on VestingRegistry contract to call IVesting(vesting).stakeTokens function\\n * at a vesting instance.\\n */\\ninterface IVesting {\\n function duration() external returns (uint256);\\n\\n function endDate() external returns (uint256);\\n\\n function stakeTokens(uint256 amount) external;\\n}\\n\",\"keccak256\":\"0x373d8b2669d69d57ed11f1caf345e3a818f5c60019b3fff91acb01d323c0600c\"},\"contracts/governance/Vesting/IVestingFactory.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for Vesting Factory contract.\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n * This interface is used by VestingFactory contract to override empty\\n * implemention of deployVesting and deployTeamVesting functions\\n * and on VestingRegistry contract to use an instance of VestingFactory.\\n */\\ninterface IVestingFactory {\\n function deployVesting(\\n address _SOV,\\n address _staking,\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration,\\n address _feeSharing,\\n address _owner\\n ) external returns (address);\\n\\n function deployTeamVesting(\\n address _SOV,\\n address _staking,\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration,\\n address _feeSharing,\\n address _owner\\n ) external returns (address);\\n}\\n\",\"keccak256\":\"0xc77e276b71ec23ca6d4eead9a842bc01cc37bcfe55a88528190f1f6106773175\"},\"contracts/governance/Vesting/IVestingRegistry.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for upgradable Vesting Registry contract.\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n */\\ninterface IVestingRegistry {\\n function getVesting(address _tokenOwner) external view returns (address);\\n\\n function getTeamVesting(address _tokenOwner) external view returns (address);\\n\\n function setVestingRegistry(address _vestingRegistryProxy) external;\\n\\n function isVestingAddress(address _vestingAddress) external view returns (bool);\\n\\n function isTeamVesting(address _vestingAddress) external view returns (bool);\\n}\\n\",\"keccak256\":\"0x08bf5badf1813b59f8b06d3bb9280f4b35d3d07947c728dad79e43fcc1d4130e\"},\"contracts/governance/Vesting/VestingLogic.sol\":{\"content\":\"pragma solidity ^0.5.17;\\npragma experimental ABIEncoderV2;\\n\\nimport \\\"../../openzeppelin/Ownable.sol\\\";\\nimport \\\"../../interfaces/IERC20.sol\\\";\\nimport \\\"../Staking/interfaces/IStaking.sol\\\";\\nimport \\\"../IFeeSharingCollector.sol\\\";\\nimport \\\"./IVesting.sol\\\";\\nimport \\\"../ApprovalReceiver.sol\\\";\\nimport \\\"./VestingStorage.sol\\\";\\n\\n/**\\n * @title Vesting Logic contract.\\n * @notice Staking, delegating and withdrawal functionality.\\n * @dev Deployed by a VestingFactory contract.\\n * */\\ncontract VestingLogic is IVesting, VestingStorage, ApprovalReceiver {\\n /* Events */\\n\\n event TokensStaked(address indexed caller, uint256 amount);\\n event VotesDelegated(address indexed caller, address delegatee);\\n event TokensWithdrawn(address indexed caller, address receiver);\\n event DividendsCollected(\\n address indexed caller,\\n address loanPoolToken,\\n address receiver,\\n uint32 maxCheckpoints\\n );\\n event MigratedToNewStakingContract(address indexed caller, address newStakingContract);\\n\\n /* Modifiers */\\n\\n /**\\n * @dev Throws if called by any account other than the token owner or the contract owner.\\n */\\n modifier onlyOwners() {\\n require(msg.sender == tokenOwner || isOwner(), \\\"unauthorized\\\");\\n _;\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the token owner.\\n */\\n modifier onlyTokenOwner() {\\n require(msg.sender == tokenOwner, \\\"unauthorized\\\");\\n _;\\n }\\n\\n /* Functions */\\n\\n /**\\n * @notice Stakes tokens according to the vesting schedule.\\n * @param _amount The amount of tokens to stake.\\n * */\\n function stakeTokens(uint256 _amount) public {\\n _stakeTokens(msg.sender, _amount);\\n }\\n\\n /**\\n * @notice Stakes tokens according to the vesting schedule.\\n * @dev This function will be invoked from receiveApproval.\\n * @dev SOV.approveAndCall -> this.receiveApproval -> this.stakeTokensWithApproval\\n * @param _sender The sender of SOV.approveAndCall\\n * @param _amount The amount of tokens to stake.\\n * */\\n function stakeTokensWithApproval(address _sender, uint256 _amount) public onlyThisContract {\\n _stakeTokens(_sender, _amount);\\n }\\n\\n /**\\n * @notice Stakes tokens according to the vesting schedule. Low level function.\\n * @dev Once here the allowance of tokens is taken for granted.\\n * @param _sender The sender of tokens to stake.\\n * @param _amount The amount of tokens to stake.\\n * */\\n function _stakeTokens(address _sender, uint256 _amount) internal {\\n /// @dev Maybe better to allow staking unil the cliff was reached.\\n if (startDate == 0) {\\n startDate = staking.timestampToLockDate(block.timestamp);\\n }\\n endDate = staking.timestampToLockDate(block.timestamp + duration);\\n\\n /// @dev Transfer the tokens to this contract.\\n bool success = SOV.transferFrom(_sender, address(this), _amount);\\n require(success);\\n\\n /// @dev Allow the staking contract to access them.\\n SOV.approve(address(staking), _amount);\\n\\n staking.stakeBySchedule(_amount, cliff, duration, FOUR_WEEKS, address(this), tokenOwner);\\n\\n emit TokensStaked(_sender, _amount);\\n }\\n\\n /**\\n * @notice Delegate votes from `msg.sender` which are locked until lockDate\\n * to `delegatee`.\\n * @param _delegatee The address to delegate votes to.\\n * */\\n function delegate(address _delegatee) public onlyTokenOwner {\\n require(_delegatee != address(0), \\\"delegatee address invalid\\\");\\n\\n /// @dev Withdraw for each unlocked position.\\n /// @dev Don't change FOUR_WEEKS to TWO_WEEKS, a lot of vestings already deployed with FOUR_WEEKS\\n ///\\t\\tworkaround found, but it doesn't work with TWO_WEEKS\\n for (uint256 i = startDate + cliff; i <= endDate; i += FOUR_WEEKS) {\\n staking.delegate(_delegatee, i);\\n }\\n emit VotesDelegated(msg.sender, _delegatee);\\n }\\n\\n /**\\n * @notice Withdraws all tokens from the staking contract and\\n * forwards them to an address specified by the token owner.\\n * @param receiver The receiving address.\\n * @dev Can be called only by owner.\\n * @dev **WARNING** This function should not be no longer used by Sovryn Protocol.\\n * Sovryn protocol will use the cancelTeamVesting function for the withdrawal moving forward.\\n * */\\n function governanceWithdrawTokens(address receiver) public {\\n require(msg.sender == address(staking), \\\"unauthorized\\\");\\n\\n _withdrawTokens(receiver, true);\\n }\\n\\n /**\\n * @notice Withdraws unlocked tokens from the staking contract and\\n * forwards them to an address specified by the token owner.\\n * @param receiver The receiving address.\\n * */\\n function withdrawTokens(address receiver) public onlyOwners {\\n _withdrawTokens(receiver, false);\\n }\\n\\n /**\\n * @notice Withdraws tokens from the staking contract and forwards them\\n * to an address specified by the token owner. Low level function.\\n * @dev Once here the caller permission is taken for granted.\\n * @param receiver The receiving address.\\n * @param isGovernance Whether all tokens (true)\\n * or just unlocked tokens (false).\\n * */\\n function _withdrawTokens(address receiver, bool isGovernance) internal {\\n require(receiver != address(0), \\\"receiver address invalid\\\");\\n\\n uint96 stake;\\n\\n /// @dev Usually we just need to iterate over the possible dates until now.\\n uint256 end;\\n\\n /// @dev In the unlikely case that all tokens have been unlocked early,\\n /// allow to withdraw all of them.\\n if (staking.allUnlocked() || isGovernance) {\\n end = endDate;\\n } else {\\n end = block.timestamp;\\n }\\n\\n /// @dev Withdraw for each unlocked position.\\n /// @dev Don't change FOUR_WEEKS to TWO_WEEKS, a lot of vestings already deployed with FOUR_WEEKS\\n ///\\t\\tworkaround found, but it doesn't work with TWO_WEEKS\\n for (uint256 i = startDate + cliff; i <= end; i += FOUR_WEEKS) {\\n /// @dev Read amount to withdraw.\\n stake = staking.getPriorUserStakeByDate(address(this), i, block.number - 1);\\n\\n /// @dev Withdraw if > 0\\n if (stake > 0) {\\n if (isGovernance) {\\n staking.governanceWithdraw(stake, i, receiver);\\n } else {\\n staking.withdraw(stake, i, receiver);\\n }\\n }\\n }\\n\\n emit TokensWithdrawn(msg.sender, receiver);\\n }\\n\\n /**\\n * @notice Collect dividends from fee sharing proxy.\\n * @param _loanPoolToken The loan pool token address.\\n * @param _maxCheckpoints Maximum number of checkpoints to be processed.\\n * @param _receiver The receiver of tokens or msg.sender\\n * */\\n function collectDividends(\\n address _loanPoolToken,\\n uint32 _maxCheckpoints,\\n address _receiver\\n ) public onlyOwners {\\n require(_receiver != address(0), \\\"receiver address invalid\\\");\\n\\n /// @dev Invokes the fee sharing proxy.\\n feeSharingCollector.withdraw(_loanPoolToken, _maxCheckpoints, _receiver);\\n\\n emit DividendsCollected(msg.sender, _loanPoolToken, _receiver, _maxCheckpoints);\\n }\\n\\n /**\\n * @notice Allows the owners to migrate the positions\\n * to a new staking contract.\\n * */\\n function migrateToNewStakingContract() public onlyOwners {\\n staking.migrateToNewStakingContract();\\n staking = IStaking(staking.newStakingContract());\\n emit MigratedToNewStakingContract(msg.sender, address(staking));\\n }\\n\\n /**\\n * @notice Overrides default ApprovalReceiver._getToken function to\\n * register SOV token on this contract.\\n * @return The address of SOV token.\\n * */\\n function _getToken() internal view returns (address) {\\n return address(SOV);\\n }\\n\\n /**\\n * @notice Overrides default ApprovalReceiver._getSelectors function to\\n * register stakeTokensWithApproval selector on this contract.\\n * @return The array of registered selectors on this contract.\\n * */\\n function _getSelectors() internal pure returns (bytes4[] memory) {\\n bytes4[] memory selectors = new bytes4[](1);\\n selectors[0] = this.stakeTokensWithApproval.selector;\\n return selectors;\\n }\\n}\\n\",\"keccak256\":\"0x44622e8c7eef783fc97fb37c59262d448b05c72f02076428cbff18177ed3b3ac\"},\"contracts/governance/Vesting/VestingRegistry.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\nimport \\\"../../openzeppelin/Ownable.sol\\\";\\nimport \\\"../../interfaces/IERC20.sol\\\";\\nimport \\\"../Staking/interfaces/IStaking.sol\\\";\\nimport \\\"../IFeeSharingCollector.sol\\\";\\nimport \\\"./IVestingFactory.sol\\\";\\nimport \\\"./IVesting.sol\\\";\\nimport \\\"./ITeamVesting.sol\\\";\\nimport \\\"../../openzeppelin/SafeMath.sol\\\";\\n\\n/**\\n * @title Vesting Registry contract.\\n *\\n * @notice On January 25, 2020, Sovryn launched the Genesis Reservation system.\\n * Sovryn community members who controlled a special NFT were granted access to\\n * stake BTC or rBTC for cSOV tokens at a rate of 2500 satoshis per cSOV. Per\\n * SIP-0003, up to 2,000,000 cSOV were made available in the Genesis event,\\n * which will be redeemable on a 1:1 basis for cSOV, subject to approval by\\n * existing SOV holders.\\n *\\n * On 15 Feb 2021 Sovryn is taking another step in its journey to decentralized\\n * financial sovereignty with the vote on SIP 0005. This proposal will enable\\n * participants of the Genesis Reservation system to redeem their reserved cSOV\\n * tokens for SOV. They will also have the choice to redeem cSOV for rBTC if\\n * they decide to exit the system.\\n *\\n * This contract deals with the vesting and redemption of cSOV tokens.\\n * */\\ncontract VestingRegistry is Ownable {\\n using SafeMath for uint256;\\n\\n /* Storage */\\n\\n /// @notice Constant used for computing the vesting dates.\\n uint256 public constant FOUR_WEEKS = 4 weeks;\\n\\n uint256 public constant CSOV_VESTING_CLIFF = FOUR_WEEKS;\\n uint256 public constant CSOV_VESTING_DURATION = 10 * FOUR_WEEKS;\\n\\n IVestingFactory public vestingFactory;\\n\\n /// @notice The SOV token contract.\\n address public SOV;\\n\\n /// @notice The cSOV token contracts.\\n address[] public CSOVtokens;\\n\\n uint256 public priceSats;\\n\\n /// @notice The staking contract address.\\n address public staking;\\n\\n /// @notice Fee sharing proxy.\\n address public feeSharingCollector;\\n\\n /// @notice The vesting owner (e.g. governance timelock address).\\n address public vestingOwner;\\n\\n /// @dev TODO: Add to the documentation: address can have only one vesting of each type.\\n /// @dev user => vesting type => vesting contract.\\n mapping(address => mapping(uint256 => address)) public vestingContracts;\\n\\n /**\\n * @dev Struct can be created to save storage slots, but it doesn't make\\n * sense. We don't have a lot of blacklisted accounts or account with\\n * locked amount.\\n * */\\n\\n /// @dev user => flag whether user has already exchange cSOV or got a reimbursement.\\n mapping(address => bool) public processedList;\\n\\n /// @dev user => flag whether user shouldn't be able to exchange or reimburse.\\n mapping(address => bool) public blacklist;\\n\\n /// @dev user => amount of tokens should not be processed.\\n mapping(address => uint256) public lockedAmount;\\n\\n /// @dev user => flag whether user has admin role.\\n mapping(address => bool) public admins;\\n\\n enum VestingType {\\n TeamVesting, // MultisigVesting\\n Vesting // TokenHolderVesting\\n }\\n\\n /* Events */\\n\\n event CSOVReImburse(address from, uint256 CSOVamount, uint256 reImburseAmount);\\n event CSOVTokensExchanged(address indexed caller, uint256 amount);\\n event SOVTransferred(address indexed receiver, uint256 amount);\\n event VestingCreated(\\n address indexed tokenOwner,\\n address vesting,\\n uint256 cliff,\\n uint256 duration,\\n uint256 amount\\n );\\n event TeamVestingCreated(\\n address indexed tokenOwner,\\n address vesting,\\n uint256 cliff,\\n uint256 duration,\\n uint256 amount\\n );\\n event TokensStaked(address indexed vesting, uint256 amount);\\n event AdminAdded(address admin);\\n event AdminRemoved(address admin);\\n\\n /* Functions */\\n\\n /**\\n * @notice Contract deployment settings.\\n * @param _vestingFactory The address of vesting factory contract.\\n * @param _SOV The SOV token address.\\n * @param _CSOVtokens The array of cSOV tokens.\\n * @param _priceSats The price of cSOV tokens in satoshis.\\n * @param _staking The address of staking contract.\\n * @param _feeSharingCollector The address of fee sharing collector proxy contract.\\n * @param _vestingOwner The address of an owner of vesting contract.\\n * @dev On Sovryn the vesting owner is Exchequer Multisig.\\n * According to SIP-0007 The Exchequer Multisig is designated to hold\\n * certain funds in the form of rBTC and SOV, in order to allow for\\n * flexible deployment of such funds on:\\n * + facilitating rBTC redemptions for Genesis pre-sale participants.\\n * + deploying of SOV for the purposes of exchange listings, market\\n * making, and partnerships with third parties.\\n * */\\n constructor(\\n address _vestingFactory,\\n address _SOV,\\n address[] memory _CSOVtokens,\\n uint256 _priceSats,\\n address _staking,\\n address _feeSharingCollector,\\n address _vestingOwner\\n ) public {\\n require(_SOV != address(0), \\\"SOV address invalid\\\");\\n require(_staking != address(0), \\\"staking address invalid\\\");\\n require(_feeSharingCollector != address(0), \\\"feeSharingCollector address invalid\\\");\\n require(_vestingOwner != address(0), \\\"vestingOwner address invalid\\\");\\n\\n _setVestingFactory(_vestingFactory);\\n _setCSOVtokens(_CSOVtokens);\\n\\n SOV = _SOV;\\n priceSats = _priceSats;\\n staking = _staking;\\n feeSharingCollector = _feeSharingCollector;\\n vestingOwner = _vestingOwner;\\n }\\n\\n //---ACL------------------------------------------------------------------\\n\\n /**\\n * @dev Throws if called by any account other than the owner or admin.\\n * TODO: This ACL logic should be available on OpenZeppeling Ownable.sol\\n * or on our own overriding sovrynOwnable. This same logic is repeated\\n * on OriginInvestorsClaim.sol, TokenSender.sol and VestingRegistry2.sol\\n */\\n modifier onlyAuthorized() {\\n require(isOwner() || admins[msg.sender], \\\"unauthorized\\\");\\n _;\\n }\\n\\n /**\\n * @notice Add account to ACL.\\n * @param _admin The addresses of the account to grant permissions.\\n * */\\n function addAdmin(address _admin) public onlyOwner {\\n admins[_admin] = true;\\n emit AdminAdded(_admin);\\n }\\n\\n /**\\n * @notice Remove account from ACL.\\n * @param _admin The addresses of the account to revoke permissions.\\n * */\\n function removeAdmin(address _admin) public onlyOwner {\\n admins[_admin] = false;\\n emit AdminRemoved(_admin);\\n }\\n\\n //---PostCSOV--------------------------------------------------------------\\n\\n modifier isNotProcessed() {\\n require(!processedList[msg.sender], \\\"Address cannot be processed twice\\\");\\n _;\\n }\\n\\n modifier isNotBlacklisted() {\\n require(!blacklist[msg.sender], \\\"Address blacklisted\\\");\\n _;\\n }\\n\\n /**\\n * @notice cSOV payout to sender with rBTC currency.\\n * 1.- Check holder cSOV balance by adding up every cSOV token balance.\\n * 2.- ReImburse rBTC if funds available.\\n * 3.- And store holder address in processedList.\\n */\\n function reImburse() public isNotProcessed isNotBlacklisted {\\n uint256 CSOVAmountWei = 0;\\n for (uint256 i = 0; i < CSOVtokens.length; i++) {\\n address CSOV = CSOVtokens[i];\\n uint256 balance = IERC20(CSOV).balanceOf(msg.sender);\\n CSOVAmountWei = CSOVAmountWei.add(balance);\\n }\\n\\n require(CSOVAmountWei > lockedAmount[msg.sender], \\\"holder has no CSOV\\\");\\n CSOVAmountWei -= lockedAmount[msg.sender];\\n processedList[msg.sender] = true;\\n\\n /**\\n * @dev Found and fixed the SIP-0007 bug on VestingRegistry::reImburse formula.\\n * More details at Documenting Code issues at point 11 in\\n * https://docs.google.com/document/d/10idTD1K6JvoBmtPKGuJ2Ub_mMh6qTLLlTP693GQKMyU/\\n * Previous buggy code: uint256 reImburseAmount = (CSOVAmountWei.mul(priceSats)).div(10**10);\\n * */\\n uint256 reImburseAmount = (CSOVAmountWei.mul(priceSats)).div(10**8);\\n require(address(this).balance >= reImburseAmount, \\\"Not enough funds to reimburse\\\");\\n msg.sender.transfer(reImburseAmount);\\n\\n emit CSOVReImburse(msg.sender, CSOVAmountWei, reImburseAmount);\\n }\\n\\n /**\\n * @notice Get contract balance.\\n * @return The token balance of the contract.\\n * */\\n function budget() external view returns (uint256) {\\n uint256 SCBudget = address(this).balance;\\n return SCBudget;\\n }\\n\\n /**\\n * @notice Deposit function to receiving value (rBTC).\\n * */\\n function deposit() public payable {}\\n\\n /**\\n * @notice Send all contract balance to an account.\\n * @param to The account address to send the balance to.\\n * */\\n function withdrawAll(address payable to) public onlyOwner {\\n to.transfer(address(this).balance);\\n }\\n\\n //--------------------------------------------------------------------------------------------------------------------------------------\\n\\n /**\\n * @notice Sets vesting factory address. High level endpoint.\\n * @param _vestingFactory The address of vesting factory contract.\\n *\\n * @dev Splitting code on two functions: high level and low level\\n * is a pattern that makes easy to extend functionality in a readable way,\\n * without accidentally breaking the actual action being performed.\\n * For example, checks should be done on high level endpoint, while core\\n * functionality should be coded on the low level function.\\n * */\\n function setVestingFactory(address _vestingFactory) public onlyOwner {\\n _setVestingFactory(_vestingFactory);\\n }\\n\\n /**\\n * @notice Sets vesting factory address. Low level core function.\\n * @param _vestingFactory The address of vesting factory contract.\\n * */\\n function _setVestingFactory(address _vestingFactory) internal {\\n require(_vestingFactory != address(0), \\\"vestingFactory address invalid\\\");\\n vestingFactory = IVestingFactory(_vestingFactory);\\n }\\n\\n /**\\n * @notice Sets cSOV tokens array. High level endpoint.\\n * @param _CSOVtokens The array of cSOV tokens.\\n * */\\n function setCSOVtokens(address[] memory _CSOVtokens) public onlyOwner {\\n _setCSOVtokens(_CSOVtokens);\\n }\\n\\n /**\\n * @notice Sets cSOV tokens array by looping through input. Low level function.\\n * @param _CSOVtokens The array of cSOV tokens.\\n * */\\n function _setCSOVtokens(address[] memory _CSOVtokens) internal {\\n for (uint256 i = 0; i < _CSOVtokens.length; i++) {\\n require(_CSOVtokens[i] != address(0), \\\"CSOV address invalid\\\");\\n }\\n CSOVtokens = _CSOVtokens;\\n }\\n\\n /**\\n * @notice Set blacklist flag (true/false).\\n * @param _account The address to be blacklisted.\\n * @param _blacklisted The flag to add/remove to/from a blacklist.\\n * */\\n function setBlacklistFlag(address _account, bool _blacklisted) public onlyOwner {\\n require(_account != address(0), \\\"account address invalid\\\");\\n\\n blacklist[_account] = _blacklisted;\\n }\\n\\n /**\\n * @notice Set amount to be subtracted from user token balance.\\n * @param _account The address with locked amount.\\n * @param _amount The amount to be locked.\\n * */\\n function setLockedAmount(address _account, uint256 _amount) public onlyOwner {\\n require(_account != address(0), \\\"account address invalid\\\");\\n require(_amount != 0, \\\"amount invalid\\\");\\n\\n lockedAmount[_account] = _amount;\\n }\\n\\n /**\\n * @notice Transfer SOV tokens to given address.\\n *\\n * @dev This is a wrapper for ERC-20 transfer function w/\\n * additional checks and triggering an event.\\n *\\n * @param _receiver The address of the SOV receiver.\\n * @param _amount The amount to be transferred.\\n * */\\n function transferSOV(address _receiver, uint256 _amount) public onlyOwner {\\n require(_receiver != address(0), \\\"receiver address invalid\\\");\\n require(_amount != 0, \\\"amount invalid\\\");\\n\\n IERC20(SOV).transfer(_receiver, _amount);\\n emit SOVTransferred(_receiver, _amount);\\n }\\n\\n /**\\n * @notice Exchange cSOV to SOV with 1:1 rate\\n */\\n function exchangeAllCSOV() public isNotProcessed isNotBlacklisted {\\n processedList[msg.sender] = true;\\n\\n uint256 amount = 0;\\n for (uint256 i = 0; i < CSOVtokens.length; i++) {\\n address CSOV = CSOVtokens[i];\\n uint256 balance = IERC20(CSOV).balanceOf(msg.sender);\\n amount += balance;\\n }\\n\\n require(amount > lockedAmount[msg.sender], \\\"amount invalid\\\");\\n amount -= lockedAmount[msg.sender];\\n\\n _createVestingForCSOV(amount);\\n }\\n\\n /**\\n * @notice cSOV tokens are moved and staked on Vesting contract.\\n * @param _amount The amount of tokens to be vested.\\n * */\\n function _createVestingForCSOV(uint256 _amount) internal {\\n address vesting =\\n _getOrCreateVesting(msg.sender, CSOV_VESTING_CLIFF, CSOV_VESTING_DURATION);\\n\\n IERC20(SOV).approve(vesting, _amount);\\n IVesting(vesting).stakeTokens(_amount);\\n\\n emit CSOVTokensExchanged(msg.sender, _amount);\\n }\\n\\n /**\\n * @notice Check a token address is among the cSOV token addresses.\\n * @param _CSOV The cSOV token address.\\n * */\\n function _validateCSOV(address _CSOV) internal view {\\n bool isValid = false;\\n for (uint256 i = 0; i < CSOVtokens.length; i++) {\\n if (_CSOV == CSOVtokens[i]) {\\n isValid = true;\\n break;\\n }\\n }\\n require(isValid, \\\"wrong CSOV address\\\");\\n }\\n\\n /**\\n * @notice Create Vesting contract.\\n * @param _tokenOwner The owner of the tokens.\\n * @param _amount The amount to be staked.\\n * @param _cliff The time interval to the first withdraw in seconds.\\n * @param _duration The total duration in seconds.\\n * */\\n function createVesting(\\n address _tokenOwner,\\n uint256 _amount,\\n uint256 _cliff,\\n uint256 _duration\\n ) public onlyAuthorized {\\n address vesting = _getOrCreateVesting(_tokenOwner, _cliff, _duration);\\n emit VestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount);\\n }\\n\\n /**\\n * @notice Create Team Vesting contract.\\n * @param _tokenOwner The owner of the tokens.\\n * @param _amount The amount to be staked.\\n * @param _cliff The time interval to the first withdraw in seconds.\\n * @param _duration The total duration in seconds.\\n * */\\n function createTeamVesting(\\n address _tokenOwner,\\n uint256 _amount,\\n uint256 _cliff,\\n uint256 _duration\\n ) public onlyAuthorized {\\n address vesting = _getOrCreateTeamVesting(_tokenOwner, _cliff, _duration);\\n emit TeamVestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount);\\n }\\n\\n /**\\n * @notice Stake tokens according to the vesting schedule.\\n * @param _vesting The address of Vesting contract.\\n * @param _amount The amount of tokens to stake.\\n * */\\n function stakeTokens(address _vesting, uint256 _amount) public onlyAuthorized {\\n require(_vesting != address(0), \\\"vesting address invalid\\\");\\n require(_amount > 0, \\\"amount invalid\\\");\\n\\n IERC20(SOV).approve(_vesting, _amount);\\n IVesting(_vesting).stakeTokens(_amount);\\n emit TokensStaked(_vesting, _amount);\\n }\\n\\n /**\\n * @notice Query the vesting contract for an account.\\n * @param _tokenOwner The owner of the tokens.\\n * @return The vesting contract address for the given token owner.\\n * */\\n function getVesting(address _tokenOwner) public view returns (address) {\\n return vestingContracts[_tokenOwner][uint256(VestingType.Vesting)];\\n }\\n\\n /**\\n * @notice Query the team vesting contract for an account.\\n * @param _tokenOwner The owner of the tokens.\\n * @return The team vesting contract address for the given token owner.\\n * */\\n function getTeamVesting(address _tokenOwner) public view returns (address) {\\n return vestingContracts[_tokenOwner][uint256(VestingType.TeamVesting)];\\n }\\n\\n /**\\n * @notice If not exists, deploy a vesting contract through factory.\\n * @param _tokenOwner The owner of the tokens.\\n * @param _cliff The time interval to the first withdraw in seconds.\\n * @param _duration The total duration in seconds.\\n * @return The vesting contract address for the given token owner\\n * whether it existed previously or not.\\n * */\\n function _getOrCreateVesting(\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration\\n ) internal returns (address) {\\n uint256 type_ = uint256(VestingType.Vesting);\\n if (vestingContracts[_tokenOwner][type_] == address(0)) {\\n /// @dev TODO: Owner of OwnerVesting contracts - the same address as tokenOwner.\\n address vesting =\\n vestingFactory.deployVesting(\\n SOV,\\n staking,\\n _tokenOwner,\\n _cliff,\\n _duration,\\n feeSharingCollector,\\n _tokenOwner\\n );\\n vestingContracts[_tokenOwner][type_] = vesting;\\n }\\n return vestingContracts[_tokenOwner][type_];\\n }\\n\\n /**\\n * @notice If not exists, deploy a team vesting contract through factory.\\n * @param _tokenOwner The owner of the tokens.\\n * @param _cliff The time interval to the first withdraw in seconds.\\n * @param _duration The total duration in seconds.\\n * @return The team vesting contract address for the given token owner\\n * whether it existed previously or not.\\n * */\\n function _getOrCreateTeamVesting(\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration\\n ) internal returns (address) {\\n uint256 type_ = uint256(VestingType.TeamVesting);\\n if (vestingContracts[_tokenOwner][type_] == address(0)) {\\n address vesting =\\n vestingFactory.deployTeamVesting(\\n SOV,\\n staking,\\n _tokenOwner,\\n _cliff,\\n _duration,\\n feeSharingCollector,\\n vestingOwner\\n );\\n vestingContracts[_tokenOwner][type_] = vesting;\\n }\\n return vestingContracts[_tokenOwner][type_];\\n }\\n}\\n\",\"keccak256\":\"0xd691de2f5a675394f19be5264b659a646b4bc45752a59055484aa80f25c08a57\"},\"contracts/governance/Vesting/VestingRegistryLogic.sol\":{\"content\":\"pragma solidity ^0.5.17;\\npragma experimental ABIEncoderV2;\\n\\nimport \\\"../../interfaces/IERC20.sol\\\";\\nimport \\\"../IFeeSharingCollector.sol\\\";\\nimport \\\"./IVesting.sol\\\";\\nimport \\\"./ITeamVesting.sol\\\";\\nimport \\\"./VestingRegistryStorage.sol\\\";\\n\\ncontract VestingRegistryLogic is VestingRegistryStorage {\\n event SOVTransferred(address indexed receiver, uint256 amount);\\n event VestingCreated(\\n address indexed tokenOwner,\\n address vesting,\\n uint256 cliff,\\n uint256 duration,\\n uint256 amount,\\n uint256 vestingCreationType\\n );\\n event TeamVestingCreated(\\n address indexed tokenOwner,\\n address vesting,\\n uint256 cliff,\\n uint256 duration,\\n uint256 amount,\\n uint256 vestingCreationType\\n );\\n event TokensStaked(address indexed vesting, uint256 amount);\\n event VestingCreationAndTypesSet(\\n address indexed vesting,\\n VestingCreationAndTypeDetails vestingCreationAndType\\n );\\n\\n /**\\n * @notice Replace constructor with initialize function for Upgradable Contracts\\n * This function will be called only once by the owner\\n * */\\n function initialize(\\n address _vestingFactory,\\n address _SOV,\\n address _staking,\\n address _feeSharingCollector,\\n address _vestingOwner,\\n address _lockedSOV,\\n address[] calldata _vestingRegistries\\n ) external onlyOwner initializer {\\n require(_SOV != address(0), \\\"SOV address invalid\\\");\\n require(_staking != address(0), \\\"staking address invalid\\\");\\n require(_feeSharingCollector != address(0), \\\"feeSharingCollector address invalid\\\");\\n require(_vestingOwner != address(0), \\\"vestingOwner address invalid\\\");\\n require(_lockedSOV != address(0), \\\"LockedSOV address invalid\\\");\\n\\n _setVestingFactory(_vestingFactory);\\n SOV = _SOV;\\n staking = _staking;\\n feeSharingCollector = _feeSharingCollector;\\n vestingOwner = _vestingOwner;\\n lockedSOV = LockedSOV(_lockedSOV);\\n for (uint256 i = 0; i < _vestingRegistries.length; i++) {\\n require(_vestingRegistries[i] != address(0), \\\"Vesting registry address invalid\\\");\\n vestingRegistries.push(IVestingRegistry(_vestingRegistries[i]));\\n }\\n }\\n\\n /**\\n * @notice sets vesting factory address\\n * @param _vestingFactory the address of vesting factory contract\\n */\\n function setVestingFactory(address _vestingFactory) external onlyOwner {\\n _setVestingFactory(_vestingFactory);\\n }\\n\\n /**\\n * @notice Internal function that sets vesting factory address\\n * @param _vestingFactory the address of vesting factory contract\\n */\\n function _setVestingFactory(address _vestingFactory) internal {\\n require(_vestingFactory != address(0), \\\"vestingFactory address invalid\\\");\\n vestingFactory = IVestingFactory(_vestingFactory);\\n }\\n\\n /**\\n * @notice transfers SOV tokens to given address\\n * @param _receiver the address of the SOV receiver\\n * @param _amount the amount to be transferred\\n */\\n function transferSOV(address _receiver, uint256 _amount) external onlyOwner {\\n require(_receiver != address(0), \\\"receiver address invalid\\\");\\n require(_amount != 0, \\\"amount invalid\\\");\\n require(IERC20(SOV).transfer(_receiver, _amount), \\\"transfer failed\\\");\\n emit SOVTransferred(_receiver, _amount);\\n }\\n\\n /**\\n * @notice adds vestings that were deployed in previous vesting registries\\n * @dev migration of data from previous vesting registy contracts\\n */\\n function addDeployedVestings(\\n address[] calldata _tokenOwners,\\n uint256[] calldata _vestingCreationTypes\\n ) external onlyAuthorized {\\n for (uint256 i = 0; i < _tokenOwners.length; i++) {\\n require(_tokenOwners[i] != address(0), \\\"token owner cannot be 0 address\\\");\\n require(_vestingCreationTypes[i] > 0, \\\"vesting creation type must be greater than 0\\\");\\n _addDeployedVestings(_tokenOwners[i], _vestingCreationTypes[i]);\\n }\\n }\\n\\n /**\\n * @notice adds four year vestings to vesting registry logic\\n * @param _tokenOwners array of token owners\\n * @param _vestingAddresses array of vesting addresses\\n */\\n function addFourYearVestings(\\n address[] calldata _tokenOwners,\\n address[] calldata _vestingAddresses\\n ) external onlyAuthorized {\\n require(_tokenOwners.length == _vestingAddresses.length, \\\"arrays mismatch\\\");\\n uint256 vestingCreationType = 4;\\n uint256 cliff = 4 weeks;\\n uint256 duration = 156 weeks;\\n for (uint256 i = 0; i < _tokenOwners.length; i++) {\\n require(!isVesting[_vestingAddresses[i]], \\\"vesting exists\\\");\\n require(_tokenOwners[i] != address(0), \\\"token owner cannot be 0 address\\\");\\n require(_vestingAddresses[i] != address(0), \\\"vesting cannot be 0 address\\\");\\n uint256 uid =\\n uint256(\\n keccak256(\\n abi.encodePacked(\\n _tokenOwners[i],\\n uint256(VestingType.Vesting),\\n cliff,\\n duration,\\n vestingCreationType\\n )\\n )\\n );\\n vestings[uid] = Vesting(\\n uint256(VestingType.Vesting),\\n vestingCreationType,\\n _vestingAddresses[i]\\n );\\n vestingsOf[_tokenOwners[i]].push(uid);\\n isVesting[_vestingAddresses[i]] = true;\\n }\\n }\\n\\n /**\\n * @notice creates Vesting contract\\n * @param _tokenOwner the owner of the tokens\\n * @param _amount the amount to be staked\\n * @param _cliff the cliff in seconds\\n * @param _duration the total duration in seconds\\n * @dev Calls a public createVestingAddr function with vestingCreationType. This is to accomodate the existing logic for LockedSOV\\n * @dev vestingCreationType 0 = LockedSOV\\n */\\n function createVesting(\\n address _tokenOwner,\\n uint256 _amount,\\n uint256 _cliff,\\n uint256 _duration\\n ) external onlyAuthorized {\\n createVestingAddr(_tokenOwner, _amount, _cliff, _duration, 3);\\n }\\n\\n /**\\n * @notice creates Vesting contract\\n * @param _tokenOwner the owner of the tokens\\n * @param _amount the amount to be staked\\n * @param _cliff the cliff in seconds\\n * @param _duration the total duration in seconds\\n * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.)\\n */\\n function createVestingAddr(\\n address _tokenOwner,\\n uint256 _amount,\\n uint256 _cliff,\\n uint256 _duration,\\n uint256 _vestingCreationType\\n ) public onlyAuthorized {\\n address vesting =\\n _getOrCreateVesting(\\n _tokenOwner,\\n _cliff,\\n _duration,\\n uint256(VestingType.Vesting),\\n _vestingCreationType\\n );\\n\\n emit VestingCreated(\\n _tokenOwner,\\n vesting,\\n _cliff,\\n _duration,\\n _amount,\\n _vestingCreationType\\n );\\n }\\n\\n /**\\n * @notice creates Team Vesting contract\\n * @param _tokenOwner the owner of the tokens\\n * @param _amount the amount to be staked\\n * @param _cliff the cliff in seconds\\n * @param _duration the total duration in seconds\\n * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.)\\n */\\n function createTeamVesting(\\n address _tokenOwner,\\n uint256 _amount,\\n uint256 _cliff,\\n uint256 _duration,\\n uint256 _vestingCreationType\\n ) external onlyAuthorized {\\n address vesting =\\n _getOrCreateVesting(\\n _tokenOwner,\\n _cliff,\\n _duration,\\n uint256(VestingType.TeamVesting),\\n _vestingCreationType\\n );\\n\\n emit TeamVestingCreated(\\n _tokenOwner,\\n vesting,\\n _cliff,\\n _duration,\\n _amount,\\n _vestingCreationType\\n );\\n }\\n\\n /**\\n * @notice stakes tokens according to the vesting schedule\\n * @param _vesting the address of Vesting contract\\n * @param _amount the amount of tokens to stake\\n */\\n function stakeTokens(address _vesting, uint256 _amount) external onlyAuthorized {\\n require(_vesting != address(0), \\\"vesting address invalid\\\");\\n require(_amount > 0, \\\"amount invalid\\\");\\n\\n IERC20(SOV).approve(_vesting, _amount);\\n IVesting(_vesting).stakeTokens(_amount);\\n emit TokensStaked(_vesting, _amount);\\n }\\n\\n /**\\n * @notice returns vesting contract address for the given token owner\\n * @param _tokenOwner the owner of the tokens\\n * @dev Calls a public getVestingAddr function with cliff and duration. This is to accomodate the existing logic for LockedSOV\\n * @dev We need to use LockedSOV.changeRegistryCliffAndDuration function very judiciously\\n * @dev vestingCreationType 0 - LockedSOV\\n */\\n function getVesting(address _tokenOwner) public view returns (address) {\\n return getVestingAddr(_tokenOwner, lockedSOV.cliff(), lockedSOV.duration(), 3);\\n }\\n\\n /**\\n * @notice public function that returns vesting contract address for the given token owner, cliff, duration\\n * @dev Important: Please use this instead of getVesting function\\n */\\n function getVestingAddr(\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration,\\n uint256 _vestingCreationType\\n ) public view returns (address) {\\n uint256 type_ = uint256(VestingType.Vesting);\\n uint256 uid =\\n uint256(\\n keccak256(\\n abi.encodePacked(_tokenOwner, type_, _cliff, _duration, _vestingCreationType)\\n )\\n );\\n return vestings[uid].vestingAddress;\\n }\\n\\n /**\\n * @notice returns team vesting contract address for the given token owner, cliff, duration\\n */\\n function getTeamVesting(\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration,\\n uint256 _vestingCreationType\\n ) public view returns (address) {\\n uint256 type_ = uint256(VestingType.TeamVesting);\\n uint256 uid =\\n uint256(\\n keccak256(\\n abi.encodePacked(_tokenOwner, type_, _cliff, _duration, _vestingCreationType)\\n )\\n );\\n return vestings[uid].vestingAddress;\\n }\\n\\n /**\\n * @dev check if the specific vesting address is team vesting or not\\n * @dev read the vestingType from vestingCreationAndTypes storage\\n *\\n * @param _vestingAddress address of vesting contract\\n *\\n * @return true for teamVesting, false for normal vesting\\n */\\n function isTeamVesting(address _vestingAddress) external view returns (bool) {\\n return (vestingCreationAndTypes[_vestingAddress].isSet &&\\n vestingCreationAndTypes[_vestingAddress].vestingType ==\\n uint32(VestingType.TeamVesting));\\n }\\n\\n /**\\n * @dev setter function to register existing vesting contract to vestingCreationAndTypes storage\\n * @dev need to set the function visilibty to public to support VestingCreationAndTypeDetails struct as parameter\\n *\\n * @param _vestingAddresses array of vesting address\\n * @param _vestingCreationAndTypes array for VestingCreationAndTypeDetails struct\\n */\\n function registerVestingToVestingCreationAndTypes(\\n address[] memory _vestingAddresses,\\n VestingCreationAndTypeDetails[] memory _vestingCreationAndTypes\\n ) public onlyAuthorized {\\n require(_vestingAddresses.length == _vestingCreationAndTypes.length, \\\"Unmatched length\\\");\\n for (uint256 i = 0; i < _vestingCreationAndTypes.length; i++) {\\n VestingCreationAndTypeDetails memory _vestingCreationAndType =\\n _vestingCreationAndTypes[i];\\n address _vestingAddress = _vestingAddresses[i];\\n\\n vestingCreationAndTypes[_vestingAddress] = _vestingCreationAndType;\\n\\n emit VestingCreationAndTypesSet(\\n _vestingAddress,\\n vestingCreationAndTypes[_vestingAddress]\\n );\\n }\\n }\\n\\n /**\\n * @notice Internal function to deploy Vesting/Team Vesting contract\\n * @param _tokenOwner the owner of the tokens\\n * @param _cliff the cliff in seconds\\n * @param _duration the total duration in seconds\\n * @param _type the type of vesting\\n * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.)\\n */\\n function _getOrCreateVesting(\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration,\\n uint256 _type,\\n uint256 _vestingCreationType\\n ) internal returns (address) {\\n address vesting;\\n uint256 uid =\\n uint256(\\n keccak256(\\n abi.encodePacked(_tokenOwner, _type, _cliff, _duration, _vestingCreationType)\\n )\\n );\\n if (vestings[uid].vestingAddress == address(0)) {\\n if (_type == 1) {\\n vesting = vestingFactory.deployVesting(\\n SOV,\\n staking,\\n _tokenOwner,\\n _cliff,\\n _duration,\\n feeSharingCollector,\\n _tokenOwner\\n );\\n } else {\\n vesting = vestingFactory.deployTeamVesting(\\n SOV,\\n staking,\\n _tokenOwner,\\n _cliff,\\n _duration,\\n feeSharingCollector,\\n vestingOwner\\n );\\n }\\n vestings[uid] = Vesting(_type, _vestingCreationType, vesting);\\n vestingsOf[_tokenOwner].push(uid);\\n isVesting[vesting] = true;\\n\\n vestingCreationAndTypes[vesting] = VestingCreationAndTypeDetails({\\n isSet: true,\\n vestingType: uint32(_type),\\n vestingCreationType: uint128(_vestingCreationType)\\n });\\n\\n emit VestingCreationAndTypesSet(vesting, vestingCreationAndTypes[vesting]);\\n }\\n return vestings[uid].vestingAddress;\\n }\\n\\n /**\\n * @notice stores the addresses of Vesting contracts from all three previous versions of Vesting Registry\\n */\\n function _addDeployedVestings(address _tokenOwner, uint256 _vestingCreationType) internal {\\n uint256 uid;\\n uint256 i = _vestingCreationType - 1;\\n\\n address vestingAddress = vestingRegistries[i].getVesting(_tokenOwner);\\n if (vestingAddress != address(0)) {\\n VestingLogic vesting = VestingLogic(vestingAddress);\\n uid = uint256(\\n keccak256(\\n abi.encodePacked(\\n _tokenOwner,\\n uint256(VestingType.Vesting),\\n vesting.cliff(),\\n vesting.duration(),\\n _vestingCreationType\\n )\\n )\\n );\\n vestings[uid] = Vesting(\\n uint256(VestingType.Vesting),\\n _vestingCreationType,\\n vestingAddress\\n );\\n vestingsOf[_tokenOwner].push(uid);\\n isVesting[vestingAddress] = true;\\n }\\n\\n address teamVestingAddress = vestingRegistries[i].getTeamVesting(_tokenOwner);\\n if (teamVestingAddress != address(0)) {\\n VestingLogic vesting = VestingLogic(teamVestingAddress);\\n uid = uint256(\\n keccak256(\\n abi.encodePacked(\\n _tokenOwner,\\n uint256(VestingType.TeamVesting),\\n vesting.cliff(),\\n vesting.duration(),\\n _vestingCreationType\\n )\\n )\\n );\\n vestings[uid] = Vesting(\\n uint256(VestingType.TeamVesting),\\n _vestingCreationType,\\n teamVestingAddress\\n );\\n vestingsOf[_tokenOwner].push(uid);\\n isVesting[teamVestingAddress] = true;\\n }\\n }\\n\\n /**\\n * @notice returns all vesting details for the given token owner\\n */\\n function getVestingsOf(address _tokenOwner) external view returns (Vesting[] memory) {\\n uint256[] memory vestingIds = vestingsOf[_tokenOwner];\\n uint256 length = vestingIds.length;\\n Vesting[] memory _vestings = new Vesting[](vestingIds.length);\\n for (uint256 i = 0; i < length; i++) {\\n _vestings[i] = vestings[vestingIds[i]];\\n }\\n return _vestings;\\n }\\n\\n /**\\n * @notice returns cliff and duration for Vesting & TeamVesting contracts\\n */\\n function getVestingDetails(address _vestingAddress)\\n external\\n view\\n returns (uint256 cliff, uint256 duration)\\n {\\n VestingLogic vesting = VestingLogic(_vestingAddress);\\n return (vesting.cliff(), vesting.duration());\\n }\\n\\n /**\\n * @notice returns if the address is a vesting address\\n */\\n function isVestingAddress(address _vestingAddress) external view returns (bool isVestingAddr) {\\n return isVesting[_vestingAddress];\\n }\\n}\\n\",\"keccak256\":\"0xe5c27d62f4a228d602d1e9142d76540e701d34640b8908a8b1ce19caabb5be85\"},\"contracts/governance/Vesting/VestingRegistryStorage.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\nimport \\\"../../openzeppelin/Initializable.sol\\\";\\nimport \\\"../../utils/AdminRole.sol\\\";\\nimport \\\"../../interfaces/IERC20.sol\\\";\\nimport \\\"./IVestingFactory.sol\\\";\\nimport \\\"../../locked/LockedSOV.sol\\\";\\nimport \\\"./IVestingRegistry.sol\\\";\\n\\n/**\\n * @title Vesting Registry Storage Contract.\\n *\\n * @notice This contract is just the storage required for vesting registry.\\n * It is parent of VestingRegistryProxy and VestingRegistryLogic.\\n *\\n * @dev Use Ownable as a parent to align storage structure for Logic and Proxy contracts.\\n * */\\n\\ncontract VestingRegistryStorage is Initializable, AdminRole {\\n ///@notice the vesting factory contract\\n IVestingFactory public vestingFactory;\\n\\n ///@notice the Locked SOV contract\\n ILockedSOV public lockedSOV;\\n\\n ///@notice the list of vesting registries\\n IVestingRegistry[] public vestingRegistries;\\n\\n ///@notice the SOV token contract\\n address public SOV;\\n\\n ///@notice the staking contract address\\n address public staking;\\n\\n ///@notice fee sharing proxy\\n address public feeSharingCollector;\\n\\n ///@notice the vesting owner (e.g. governance timelock address)\\n address public vestingOwner;\\n\\n enum VestingType {\\n TeamVesting, //MultisigVesting\\n Vesting //TokenHolderVesting\\n }\\n\\n ///@notice Vesting details\\n struct Vesting {\\n uint256 vestingType;\\n uint256 vestingCreationType;\\n address vestingAddress;\\n }\\n\\n ///@notice A record of vesting details for a unique id\\n ///@dev vestings[uid] returns vesting data\\n mapping(uint256 => Vesting) public vestings;\\n\\n ///@notice A record of all unique ids for a particular token owner\\n ///@dev vestingsOf[tokenOwner] returns array of unique ids\\n mapping(address => uint256[]) public vestingsOf;\\n\\n ///@notice A record of all vesting addresses\\n ///@dev isVesting[address] returns if the address is a vesting address\\n mapping(address => bool) public isVesting;\\n\\n /// @notice Store vesting creation type & vesting type information\\n /// @dev it is packed into 1 single storage slot for cheaper gas usage\\n struct VestingCreationAndTypeDetails {\\n bool isSet;\\n uint32 vestingType;\\n uint128 vestingCreationType;\\n }\\n\\n ///@notice A record of all vesting addresses with the detail\\n ///@dev vestingDetail[vestingAddress] returns Vesting struct data\\n ///@dev can be used to easily check the vesting type / creation type based on the vesting address itself\\n mapping(address => VestingCreationAndTypeDetails) public vestingCreationAndTypes;\\n}\\n\",\"keccak256\":\"0x4f657748626f68c2a6fb82f10a54f7a5e9d2971e92de318c66c9ecb6acf0c1ed\"},\"contracts/governance/Vesting/VestingStorage.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\nimport \\\"../../openzeppelin/Ownable.sol\\\";\\nimport \\\"../../interfaces/IERC20.sol\\\";\\nimport \\\"../Staking/interfaces/IStaking.sol\\\";\\nimport \\\"../IFeeSharingCollector.sol\\\";\\n\\n/**\\n * @title Vesting Storage Contract.\\n *\\n * @notice This contract is just the storage required for vesting.\\n * It is parent of VestingLogic and TeamVesting.\\n *\\n * @dev Use Ownable as a parent to align storage structure for Logic and Proxy contracts.\\n * */\\ncontract VestingStorage is Ownable {\\n /// @notice The SOV token contract.\\n IERC20 public SOV;\\n\\n /// @notice The staking contract address.\\n IStaking public staking;\\n\\n /// @notice The owner of the vested tokens.\\n address public tokenOwner;\\n\\n /// @notice Fee sharing Proxy.\\n IFeeSharingCollector public feeSharingCollector;\\n\\n /// @notice The cliff. After this time period the tokens begin to unlock.\\n uint256 public cliff;\\n\\n /// @notice The duration. After this period all tokens will have been unlocked.\\n uint256 public duration;\\n\\n /// @notice The start date of the vesting.\\n uint256 public startDate;\\n\\n /// @notice The end date of the vesting.\\n uint256 public endDate;\\n\\n /// @notice Constant used for computing the vesting dates.\\n uint256 constant FOUR_WEEKS = 4 weeks;\\n}\\n\",\"keccak256\":\"0xf70f579d357d8f0aa0839824c1a1d66713c3cd42a58118d2893a35b52baaa140\"},\"contracts/interfaces/IERC20.sol\":{\"content\":\"/**\\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\\n * Licensed under the Apache License, Version 2.0.\\n */\\n\\npragma solidity >=0.5.0 <0.6.0;\\n\\ncontract IERC20 {\\n string public name;\\n uint8 public decimals;\\n string public symbol;\\n\\n function totalSupply() public view returns (uint256);\\n\\n function balanceOf(address _who) public view returns (uint256);\\n\\n function allowance(address _owner, address _spender) public view returns (uint256);\\n\\n function approve(address _spender, uint256 _value) public returns (bool);\\n\\n function transfer(address _to, uint256 _value) public returns (bool);\\n\\n function transferFrom(\\n address _from,\\n address _to,\\n uint256 _value\\n ) public returns (bool);\\n\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0xea0bb1bf7d022130bb42a4efdfd875ef76ac5ac730a94ddd7b8c9949d4253855\"},\"contracts/locked/ILockedSOV.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title The Locked SOV Interface.\\n * @author Franklin Richards - powerhousefrank@protonmail.com\\n * @notice This interface is an incomplete yet useful for future migration of LockedSOV Contract.\\n * @dev Only use it if you know what you are doing.\\n */\\ninterface ILockedSOV {\\n /**\\n * @notice Adds SOV to the user balance (Locked and Unlocked Balance based on `_basisPoint`).\\n * @param _userAddress The user whose locked balance has to be updated with `_sovAmount`.\\n * @param _sovAmount The amount of SOV to be added to the locked and/or unlocked balance.\\n * @param _basisPoint The % (in Basis Point)which determines how much will be unlocked immediately.\\n */\\n function deposit(\\n address _userAddress,\\n uint256 _sovAmount,\\n uint256 _basisPoint\\n ) external;\\n\\n /**\\n * @notice Adds SOV to the locked balance of a user.\\n * @param _userAddress The user whose locked balance has to be updated with _sovAmount.\\n * @param _sovAmount The amount of SOV to be added to the locked balance.\\n */\\n function depositSOV(address _userAddress, uint256 _sovAmount) external;\\n\\n /**\\n * @notice Withdraws unlocked tokens and Stakes Locked tokens for a user who already have a vesting created.\\n * @param _userAddress The address of user tokens will be withdrawn.\\n */\\n function withdrawAndStakeTokensFrom(address _userAddress) external;\\n\\n function cliff() external view returns (uint256);\\n\\n function duration() external view returns (uint256);\\n\\n function getLockedBalance(address _addr) external view returns (uint256 _balance);\\n\\n function getUnlockedBalance(address _addr) external view returns (uint256 _balance);\\n}\\n\",\"keccak256\":\"0x2ecf9d6c3704b4210b86d15a35650b34a1c44d0fb57795668fd2b2b4eab6104f\"},\"contracts/locked/LockedSOV.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\nimport \\\"../openzeppelin/SafeMath.sol\\\";\\nimport \\\"../interfaces/IERC20.sol\\\";\\nimport \\\"../governance/Vesting/VestingRegistry.sol\\\";\\nimport \\\"../governance/Vesting/VestingLogic.sol\\\";\\nimport \\\"./ILockedSOV.sol\\\";\\n\\n/**\\n * @title The Locked SOV Contract.\\n * @author Franklin Richards - powerhousefrank@protonmail.com\\n * @notice This contract is used to receive reward from other contracts, Create Vesting and Stake Tokens.\\n */\\ncontract LockedSOV is ILockedSOV {\\n using SafeMath for uint256;\\n\\n uint256 public constant MAX_BASIS_POINT = 10000;\\n uint256 public constant MAX_DURATION = 37;\\n\\n /* Storage */\\n\\n /// @notice True if the migration to a new Locked SOV Contract has started.\\n bool public migration;\\n\\n /// @notice The cliff is the time period after which the tokens begin to unlock.\\n uint256 public cliff;\\n /// @notice The duration is the time period after all tokens will have been unlocked.\\n uint256 public duration;\\n\\n /// @notice The SOV token contract.\\n IERC20 public SOV;\\n /// @notice The Vesting registry contract.\\n VestingRegistry public vestingRegistry;\\n /// @notice The New (Future) Locked SOV.\\n ILockedSOV public newLockedSOV;\\n\\n /// @notice The locked user balances.\\n mapping(address => uint256) private lockedBalances;\\n /// @notice The unlocked user balances.\\n mapping(address => uint256) private unlockedBalances;\\n /// @notice The contracts/wallets with admin power.\\n mapping(address => bool) private isAdmin;\\n\\n /* Events */\\n\\n /// @notice Emitted when a new Admin is added to the admin list.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _newAdmin The address of the new admin.\\n event AdminAdded(address indexed _initiator, address indexed _newAdmin);\\n\\n /// @notice Emitted when an admin is removed from the admin list.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _removedAdmin The address of the removed admin.\\n event AdminRemoved(address indexed _initiator, address indexed _removedAdmin);\\n\\n /// @notice Emitted when Vesting Registry, Duration and/or Cliff is updated.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _vestingRegistry The Vesting Registry Contract.\\n /// @param _cliff The time period after which the tokens begin to unlock.\\n /// @param _duration The time period after all tokens will have been unlocked.\\n event RegistryCliffAndDurationUpdated(\\n address indexed _initiator,\\n address indexed _vestingRegistry,\\n uint256 _cliff,\\n uint256 _duration\\n );\\n\\n /// @notice Emitted when a new deposit is made.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _userAddress The user to whose un/locked balance a new deposit was made.\\n /// @param _sovAmount The amount of SOV to be added to the un/locked balance.\\n /// @param _basisPoint The % (in Basis Point) which determines how much will be unlocked immediately.\\n event Deposited(\\n address indexed _initiator,\\n address indexed _userAddress,\\n uint256 _sovAmount,\\n uint256 _basisPoint\\n );\\n\\n /// @notice Emitted when a user withdraws the fund.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _userAddress The user whose unlocked balance has to be withdrawn.\\n /// @param _sovAmount The amount of SOV withdrawn from the unlocked balance.\\n event Withdrawn(address indexed _initiator, address indexed _userAddress, uint256 _sovAmount);\\n\\n /// @notice Emitted when a user creates a vesting for himself.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _userAddress The user whose unlocked balance has to be withdrawn.\\n /// @param _vesting The Vesting Contract.\\n event VestingCreated(\\n address indexed _initiator,\\n address indexed _userAddress,\\n address indexed _vesting\\n );\\n\\n /// @notice Emitted when a user stakes tokens.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _vesting The Vesting Contract.\\n /// @param _amount The amount of locked tokens staked by the user.\\n event TokenStaked(address indexed _initiator, address indexed _vesting, uint256 _amount);\\n\\n /// @notice Emitted when an admin initiates a migration to new Locked SOV Contract.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _newLockedSOV The address of the new Locked SOV Contract.\\n event MigrationStarted(address indexed _initiator, address indexed _newLockedSOV);\\n\\n /// @notice Emitted when a user initiates the transfer to a new Locked SOV Contract.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _amount The amount of locked tokens to transfer from this contract to the new one.\\n event UserTransfered(address indexed _initiator, uint256 _amount);\\n\\n /* Modifiers */\\n\\n modifier onlyAdmin {\\n require(isAdmin[msg.sender], \\\"Only admin can call this.\\\");\\n _;\\n }\\n\\n modifier migrationAllowed {\\n require(migration, \\\"Migration has not yet started.\\\");\\n _;\\n }\\n\\n /* Constructor */\\n\\n /**\\n * @notice Setup the required parameters.\\n * @param _SOV The SOV Token Address.\\n * @param _vestingRegistry The Vesting Registry Address.\\n * @param _cliff The time period after which the tokens begin to unlock.\\n * @param _duration The time period after all tokens will have been unlocked.\\n * @param _admins The list of Admins to be added.\\n */\\n constructor(\\n address _SOV,\\n address _vestingRegistry,\\n uint256 _cliff,\\n uint256 _duration,\\n address[] memory _admins\\n ) public {\\n require(_SOV != address(0), \\\"Invalid SOV Address.\\\");\\n require(_vestingRegistry != address(0), \\\"Vesting registry address is invalid.\\\");\\n require(_duration < MAX_DURATION, \\\"Duration is too long.\\\");\\n\\n SOV = IERC20(_SOV);\\n vestingRegistry = VestingRegistry(_vestingRegistry);\\n cliff = _cliff * 4 weeks;\\n duration = _duration * 4 weeks;\\n\\n for (uint256 index = 0; index < _admins.length; index++) {\\n isAdmin[_admins[index]] = true;\\n }\\n }\\n\\n /* Public or External Functions */\\n\\n /**\\n * @notice The function to add a new admin.\\n * @param _newAdmin The address of the new admin.\\n * @dev Only callable by an Admin.\\n */\\n function addAdmin(address _newAdmin) public onlyAdmin {\\n require(_newAdmin != address(0), \\\"Invalid Address.\\\");\\n require(!isAdmin[_newAdmin], \\\"Address is already admin.\\\");\\n isAdmin[_newAdmin] = true;\\n\\n emit AdminAdded(msg.sender, _newAdmin);\\n }\\n\\n /**\\n * @notice The function to remove an admin.\\n * @param _adminToRemove The address of the admin which should be removed.\\n * @dev Only callable by an Admin.\\n */\\n function removeAdmin(address _adminToRemove) public onlyAdmin {\\n require(isAdmin[_adminToRemove], \\\"Address is not an admin.\\\");\\n isAdmin[_adminToRemove] = false;\\n\\n emit AdminRemoved(msg.sender, _adminToRemove);\\n }\\n\\n /**\\n * @notice The function to update the Vesting Registry, Duration and Cliff.\\n * @param _vestingRegistry The Vesting Registry Address.\\n * @param _cliff The time period after which the tokens begin to unlock.\\n * @param _duration The time period after all tokens will have been unlocked.\\n * @dev IMPORTANT 1: You have to change Vesting Registry if you want to change Duration and/or Cliff.\\n * IMPORTANT 2: `_cliff` and `_duration` is multiplied by 4 weeks in this function.\\n */\\n function changeRegistryCliffAndDuration(\\n address _vestingRegistry,\\n uint256 _cliff,\\n uint256 _duration\\n ) external onlyAdmin {\\n require(\\n address(vestingRegistry) != _vestingRegistry,\\n \\\"Vesting Registry has to be different for changing duration and cliff.\\\"\\n );\\n /// If duration is also zero, then it is similar to Unlocked SOV.\\n require(_duration != 0, \\\"Duration cannot be zero.\\\");\\n require(_duration < MAX_DURATION, \\\"Duration is too long.\\\");\\n\\n vestingRegistry = VestingRegistry(_vestingRegistry);\\n\\n cliff = _cliff * 4 weeks;\\n duration = _duration * 4 weeks;\\n\\n emit RegistryCliffAndDurationUpdated(msg.sender, _vestingRegistry, _cliff, _duration);\\n }\\n\\n /**\\n * @notice Adds SOV to the user balance (Locked and Unlocked Balance based on `_basisPoint`).\\n * @param _userAddress The user whose locked balance has to be updated with `_sovAmount`.\\n * @param _sovAmount The amount of SOV to be added to the locked and/or unlocked balance.\\n * @param _basisPoint The % (in Basis Point)which determines how much will be unlocked immediately.\\n */\\n function deposit(\\n address _userAddress,\\n uint256 _sovAmount,\\n uint256 _basisPoint\\n ) external {\\n _deposit(_userAddress, _sovAmount, _basisPoint);\\n }\\n\\n /**\\n * @notice Adds SOV to the locked balance of a user.\\n * @param _userAddress The user whose locked balance has to be updated with _sovAmount.\\n * @param _sovAmount The amount of SOV to be added to the locked balance.\\n * @dev This is here because there are dependency with other contracts.\\n */\\n function depositSOV(address _userAddress, uint256 _sovAmount) external {\\n _deposit(_userAddress, _sovAmount, 0);\\n }\\n\\n function _deposit(\\n address _userAddress,\\n uint256 _sovAmount,\\n uint256 _basisPoint\\n ) private {\\n // MAX_BASIS_POINT is not included because if 100% is unlocked, then LockedSOV is not required to be used.\\n require(_basisPoint < MAX_BASIS_POINT, \\\"Basis Point has to be less than 10000.\\\");\\n bool txStatus = SOV.transferFrom(msg.sender, address(this), _sovAmount);\\n require(txStatus, \\\"Token transfer was not successful. Check receiver address.\\\");\\n\\n uint256 unlockedBal = _sovAmount.mul(_basisPoint).div(MAX_BASIS_POINT);\\n\\n unlockedBalances[_userAddress] = unlockedBalances[_userAddress].add(unlockedBal);\\n lockedBalances[_userAddress] = lockedBalances[_userAddress].add(_sovAmount).sub(\\n unlockedBal\\n );\\n\\n emit Deposited(msg.sender, _userAddress, _sovAmount, _basisPoint);\\n }\\n\\n /**\\n * @notice A function to withdraw the unlocked balance.\\n * @param _receiverAddress If specified, the unlocked balance will go to this address, else to msg.sender.\\n */\\n function withdraw(address _receiverAddress) public {\\n _withdraw(msg.sender, _receiverAddress);\\n }\\n\\n function _withdraw(address _sender, address _receiverAddress) private {\\n address userAddr = _receiverAddress;\\n if (_receiverAddress == address(0)) {\\n userAddr = _sender;\\n }\\n\\n uint256 amount = unlockedBalances[_sender];\\n unlockedBalances[_sender] = 0;\\n\\n bool txStatus = SOV.transfer(userAddr, amount);\\n require(txStatus, \\\"Token transfer was not successful. Check receiver address.\\\");\\n\\n emit Withdrawn(_sender, userAddr, amount);\\n }\\n\\n /**\\n * @notice Creates vesting if not already created and Stakes tokens for a user.\\n * @dev Only use this function if the `duration` is small.\\n */\\n function createVestingAndStake() public {\\n _createVestingAndStake(msg.sender);\\n }\\n\\n function _createVestingAndStake(address _sender) private {\\n address vestingAddr = _getVesting(_sender);\\n\\n if (vestingAddr == address(0)) {\\n vestingAddr = _createVesting(_sender);\\n }\\n\\n _stakeTokens(_sender, vestingAddr);\\n }\\n\\n /**\\n * @notice Creates vesting contract (if it hasn't been created yet) for the calling user.\\n * @return _vestingAddress The New Vesting Contract Created.\\n */\\n function createVesting() public returns (address _vestingAddress) {\\n _vestingAddress = _createVesting(msg.sender);\\n }\\n\\n /**\\n * @notice Stakes tokens for a user who already have a vesting created.\\n * @dev The user should already have a vesting created, else this function will throw error.\\n */\\n function stakeTokens() public {\\n VestingLogic vesting = VestingLogic(_getVesting(msg.sender));\\n\\n require(\\n cliff == vesting.cliff() && duration == vesting.duration(),\\n \\\"Wrong Vesting Schedule.\\\"\\n );\\n\\n _stakeTokens(msg.sender, address(vesting));\\n }\\n\\n /**\\n * @notice Withdraws unlocked tokens and Stakes Locked tokens for a user who already have a vesting created.\\n * @param _receiverAddress If specified, the unlocked balance will go to this address, else to msg.sender.\\n */\\n function withdrawAndStakeTokens(address _receiverAddress) external {\\n _withdraw(msg.sender, _receiverAddress);\\n _createVestingAndStake(msg.sender);\\n }\\n\\n /**\\n * @notice Withdraws unlocked tokens and Stakes Locked tokens for a user who already have a vesting created.\\n * @param _userAddress The address of user tokens will be withdrawn.\\n */\\n function withdrawAndStakeTokensFrom(address _userAddress) external {\\n _withdraw(_userAddress, _userAddress);\\n _createVestingAndStake(_userAddress);\\n }\\n\\n /**\\n * @notice Function to start the process of migration to new contract.\\n * @param _newLockedSOV The new locked sov contract address.\\n */\\n function startMigration(address _newLockedSOV) external onlyAdmin {\\n require(_newLockedSOV != address(0), \\\"New Locked SOV Address is Invalid.\\\");\\n newLockedSOV = ILockedSOV(_newLockedSOV);\\n SOV.approve(_newLockedSOV, SOV.balanceOf(address(this)));\\n migration = true;\\n\\n emit MigrationStarted(msg.sender, _newLockedSOV);\\n }\\n\\n /**\\n * @notice Function to transfer the locked balance from this contract to new LockedSOV Contract.\\n * @dev Address is not specified to discourage selling lockedSOV to other address.\\n */\\n function transfer() external migrationAllowed {\\n uint256 amount = lockedBalances[msg.sender];\\n lockedBalances[msg.sender] = 0;\\n\\n newLockedSOV.depositSOV(msg.sender, amount);\\n\\n emit UserTransfered(msg.sender, amount);\\n }\\n\\n /* Internal Functions */\\n\\n /**\\n * @notice Creates a Vesting Contract for a user.\\n * @param _tokenOwner The owner of the vesting contract.\\n * @return _vestingAddress The Vesting Contract Address.\\n * @dev Does not do anything if Vesting Contract was already created.\\n */\\n function _createVesting(address _tokenOwner) internal returns (address _vestingAddress) {\\n /// Here zero is given in place of amount, as amount is not really used in `vestingRegistry.createVesting()`.\\n vestingRegistry.createVesting(_tokenOwner, 0, cliff, duration);\\n _vestingAddress = _getVesting(_tokenOwner);\\n emit VestingCreated(msg.sender, _tokenOwner, _vestingAddress);\\n }\\n\\n /**\\n * @notice Returns the Vesting Contract Address.\\n * @param _tokenOwner The owner of the vesting contract.\\n * @return _vestingAddress The Vesting Contract Address.\\n */\\n function _getVesting(address _tokenOwner) internal view returns (address _vestingAddress) {\\n return vestingRegistry.getVesting(_tokenOwner);\\n }\\n\\n /**\\n * @notice Stakes the tokens in a particular vesting contract.\\n * @param _vesting The Vesting Contract Address.\\n */\\n function _stakeTokens(address _sender, address _vesting) internal {\\n uint256 amount = lockedBalances[_sender];\\n lockedBalances[_sender] = 0;\\n\\n require(SOV.approve(_vesting, amount), \\\"Approve failed.\\\");\\n VestingLogic(_vesting).stakeTokens(amount);\\n\\n emit TokenStaked(_sender, _vesting, amount);\\n }\\n\\n /* Getter or Read Functions */\\n\\n /**\\n * @notice The function to get the locked balance of a user.\\n * @param _addr The address of the user to check the locked balance.\\n * @return _balance The locked balance of the address `_addr`.\\n */\\n function getLockedBalance(address _addr) external view returns (uint256 _balance) {\\n return lockedBalances[_addr];\\n }\\n\\n /**\\n * @notice The function to get the unlocked balance of a user.\\n * @param _addr The address of the user to check the unlocked balance.\\n * @return _balance The unlocked balance of the address `_addr`.\\n */\\n function getUnlockedBalance(address _addr) external view returns (uint256 _balance) {\\n return unlockedBalances[_addr];\\n }\\n\\n /**\\n * @notice The function to check is an address is admin or not.\\n * @param _addr The address of the user to check the admin status.\\n * @return _status True if admin, False otherwise.\\n */\\n function adminStatus(address _addr) external view returns (bool _status) {\\n return isAdmin[_addr];\\n }\\n}\\n\",\"keccak256\":\"0xadc3b976d89a409f04644071a9f40c57d5e96a0b13d364f1aa4324e648607999\"},\"contracts/openzeppelin/Context.sol\":{\"content\":\"pragma solidity >=0.5.0 <0.6.0;\\n\\n/*\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with GSN meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\ncontract Context {\\n // Empty internal constructor, to prevent people from mistakenly deploying\\n // an instance of this contract, which should be used via inheritance.\\n constructor() internal {}\\n\\n // solhint-disable-previous-line no-empty-blocks\\n\\n function _msgSender() internal view returns (address payable) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view returns (bytes memory) {\\n this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691\\n return msg.data;\\n }\\n}\\n\",\"keccak256\":\"0x7860cb1591dbd66bb497c60c46866d9fcdb56c73306ed86b25801000af1c7b2b\"},\"contracts/openzeppelin/Initializable.sol\":{\"content\":\"pragma solidity >=0.5.0 <0.6.0;\\n\\n/**\\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\\n * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an\\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\\n *\\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\\n *\\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\\n */\\ncontract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n */\\n bool private _initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private _initializing;\\n\\n /**\\n * @dev Modifier to protect an initializer function from being invoked twice.\\n */\\n modifier initializer() {\\n require(_initializing || !_initialized, \\\"Initializable: contract is already initialized\\\");\\n\\n bool isTopLevelCall = !_initializing;\\n if (isTopLevelCall) {\\n _initializing = true;\\n _initialized = true;\\n }\\n\\n _;\\n\\n if (isTopLevelCall) {\\n _initializing = false;\\n }\\n }\\n}\\n\",\"keccak256\":\"0xa5064e2730ae1037f4efc83654676174484b827890d30ee618048004a04d6b51\"},\"contracts/openzeppelin/Ownable.sol\":{\"content\":\"pragma solidity >=0.5.0 <0.6.0;\\n\\nimport \\\"./Context.sol\\\";\\n\\n/**\\n * @dev Contract module which provides a basic access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * This module is used through inheritance. It will make available the modifier\\n * `onlyOwner`, which can be applied to your functions to restrict their use to\\n * the owner.\\n */\\ncontract Ownable is Context {\\n address private _owner;\\n\\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\\n\\n /**\\n * @dev Initializes the contract setting the deployer as the initial owner.\\n */\\n constructor() internal {\\n address msgSender = _msgSender();\\n _owner = msgSender;\\n emit OwnershipTransferred(address(0), msgSender);\\n }\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() public view returns (address) {\\n return _owner;\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the owner.\\n */\\n modifier onlyOwner() {\\n require(isOwner(), \\\"unauthorized\\\");\\n _;\\n }\\n\\n /**\\n * @dev Returns true if the caller is the current owner.\\n */\\n function isOwner() public view returns (bool) {\\n return _msgSender() == _owner;\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public onlyOwner {\\n _transferOwnership(newOwner);\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n */\\n function _transferOwnership(address newOwner) internal {\\n require(newOwner != address(0), \\\"Ownable: new owner is the zero address\\\");\\n emit OwnershipTransferred(_owner, newOwner);\\n _owner = newOwner;\\n }\\n}\\n\",\"keccak256\":\"0x94496c375b3e82d87d7f01ce1577f008fab374312cf93012a0eca716e6aadb3a\"},\"contracts/openzeppelin/SafeMath.sol\":{\"content\":\"pragma solidity >=0.5.0 <0.6.0;\\n\\n/**\\n * @dev Wrappers over Solidity's arithmetic operations with added overflow\\n * checks.\\n *\\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\\n * in bugs, because programmers usually assume that an overflow raises an\\n * error, which is the standard behavior in high level programming languages.\\n * `SafeMath` restores this intuition by reverting the transaction when an\\n * operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n */\\nlibrary SafeMath {\\n /**\\n * @dev Returns the addition of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `+` operator.\\n *\\n * Requirements:\\n * - Addition cannot overflow.\\n */\\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\\n uint256 c = a + b;\\n require(c >= a, \\\"SafeMath: addition overflow\\\");\\n\\n return c;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting on\\n * overflow (when the result is negative).\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n * - Subtraction cannot overflow.\\n */\\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\\n return sub(a, b, \\\"SafeMath: subtraction overflow\\\");\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\\n * overflow (when the result is negative).\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n * - Subtraction cannot overflow.\\n *\\n * _Available since v2.4.0._\\n */\\n function sub(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n require(b <= a, errorMessage);\\n uint256 c = a - b;\\n\\n return c;\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `*` operator.\\n *\\n * Requirements:\\n * - Multiplication cannot overflow.\\n */\\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\\n // benefit is lost if 'b' is also tested.\\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\\n if (a == 0) {\\n return 0;\\n }\\n\\n uint256 c = a * b;\\n require(c / a == b, \\\"SafeMath: multiplication overflow\\\");\\n\\n return c;\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers. Reverts on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator. Note: this function uses a\\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\\n * uses an invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n * - The divisor cannot be zero.\\n */\\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\\n return div(a, b, \\\"SafeMath: division by zero\\\");\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers. Reverts with custom message on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator. Note: this function uses a\\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\\n * uses an invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n * - The divisor cannot be zero.\\n *\\n * _Available since v2.4.0._\\n */\\n function div(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n // Solidity only automatically asserts when dividing by 0\\n require(b != 0, errorMessage);\\n uint256 c = a / b;\\n // assert(a == b * c + a % b); // There is no case in which this doesn't hold\\n\\n return c;\\n }\\n\\n /**\\n * @dev Integer division of two numbers, rounding up and truncating the quotient\\n */\\n function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {\\n return divCeil(a, b, \\\"SafeMath: division by zero\\\");\\n }\\n\\n /**\\n * @dev Integer division of two numbers, rounding up and truncating the quotient\\n */\\n function divCeil(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n // Solidity only automatically asserts when dividing by 0\\n require(b != 0, errorMessage);\\n\\n if (a == 0) {\\n return 0;\\n }\\n uint256 c = ((a - 1) / b) + 1;\\n\\n return c;\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * Reverts when dividing by zero.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n * - The divisor cannot be zero.\\n */\\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\\n return mod(a, b, \\\"SafeMath: modulo by zero\\\");\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * Reverts with custom message when dividing by zero.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n * - The divisor cannot be zero.\\n *\\n * _Available since v2.4.0._\\n */\\n function mod(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n require(b != 0, errorMessage);\\n return a % b;\\n }\\n\\n function min256(uint256 _a, uint256 _b) internal pure returns (uint256) {\\n return _a < _b ? _a : _b;\\n }\\n}\\n\",\"keccak256\":\"0xbff8d6273e1a6870d1a142c0c23acd63a4dd47760f250390f49ee56333bcb6e8\"},\"contracts/token/IApproveAndCall.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for contract governance/ApprovalReceiver.sol\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n */\\ninterface IApproveAndCall {\\n /**\\n * @notice Receives approval from SOV token.\\n * @param _sender The sender of SOV.approveAndCall function.\\n * @param _amount The amount was approved.\\n * @param _token The address of token.\\n * @param _data The data will be used for low level call.\\n * */\\n function receiveApproval(\\n address _sender,\\n uint256 _amount,\\n address _token,\\n bytes calldata _data\\n ) external;\\n}\\n\",\"keccak256\":\"0x0ca93f8436a4d81d80de5ea9214139b490d96f708f09c975a0869ce9abc61635\"},\"contracts/utils/AdminRole.sol\":{\"content\":\"pragma solidity 0.5.17;\\n\\nimport \\\"../openzeppelin/Ownable.sol\\\";\\n\\ncontract AdminRole is Ownable {\\n /// @dev user => flag whether user has admin role.\\n mapping(address => bool) public admins;\\n\\n event AdminAdded(address admin);\\n event AdminRemoved(address admin);\\n\\n /**\\n * @dev Throws if called by any account other than the owner or admin.\\n * or on our own overriding sovrynOwnable.\\n */\\n modifier onlyAuthorized() {\\n require(isOwner() || admins[msg.sender], \\\"unauthorized\\\");\\n _;\\n }\\n\\n /**\\n * @notice Add account to ACL.\\n * @param _admin The addresses of the account to grant permissions.\\n * */\\n function addAdmin(address _admin) public onlyOwner {\\n admins[_admin] = true;\\n emit AdminAdded(_admin);\\n }\\n\\n /**\\n * @notice Remove account from ACL.\\n * @param _admin The addresses of the account to revoke permissions.\\n * */\\n function removeAdmin(address _admin) public onlyOwner {\\n admins[_admin] = false;\\n emit AdminRemoved(_admin);\\n }\\n}\\n\",\"keccak256\":\"0x7333f56bc6887f53b12ec51d9e6d7f2aae23063000b101e6bb7c3ae684cca51d\"}},\"version\":1}", - "bytecode": "0x608060405260006100176001600160e01b0361007016565b6000805462010000600160b01b031916620100006001600160a01b038416908102919091178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350610074565b3390565b613077806100836000396000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c80638da5cb5b1161011a578063c680c0b7116100ad578063efb957331161007c578063efb957331461045b578063f2f46b3b1461047b578063f2fde38b14610483578063f421ed7e14610496578063f60826ee146104a957610206565b8063c680c0b714610402578063ca210d8c14610415578063cc49ede714610428578063dfb9366d1461043b57610206565b8063bc84f9ca116100e9578063bc84f9ca146103c1578063bd7b5908146103d4578063c0e09852146103dc578063c36519d1146103ef57610206565b80638da5cb5b1461037d5780638dedf009146103855780638f32d59b146103a6578063b810c648146103ae57610206565b80634cf088d91161019d57806379a83f5a1161016c57806379a83f5a146102f357806380fcb99214610306578063821bee7314610328578063842a49d51461034a578063862e229d1461036a57610206565b80634cf088d9146102bd5780636b7dbb2d146102c557806370480275146102cd578063786931da146102e057610206565b80631f509326116101d95780631f50932614610264578063377220fd14610277578063429b62e51461028a57806342a82b4f146102aa57610206565b806302df04761461020b5780630665a06f1461023457806308dcb360146102495780631785f53c14610251575b600080fd5b61021e61021936600461243f565b6104b1565b60405161022b9190612c3a565b60405180910390f35b61024761024236600461243f565b610512565b005b61021e61056d565b61024761025f366004612311565b61057c565b6102476102723660046124a0565b6105fb565b610247610285366004612311565b61069d565b61029d610298366004612311565b6106cd565b60405161022b9190612d2f565b6102476102b836600461234d565b6106e2565b61021e610951565b61021e610960565b6102476102db366004612311565b61096f565b6102476102ee366004612515565b6109e8565b610247610301366004612405565b610cd7565b610319610314366004612311565b610e83565b60405161022b93929190612d3d565b61033b610336366004612602565b610eb8565b60405161022b93929190612edd565b61035d610358366004612602565b610ee2565b60405161022b9190612d65565b6102476103783660046124a0565b610f09565b61021e610f98565b610398610393366004612311565b610fad565b60405161022b929190612ecf565b61029d6110a1565b6102476103bc366004612515565b6110cb565b6102476103cf366004612585565b6111db565b61021e61133b565b61029d6103ea366004612311565b61134a565b61021e6103fd36600461243f565b61135f565b610247610410366004612405565b611368565b61029d610423366004612311565b6114a8565b61021e610436366004612311565b6114c6565b61044e610449366004612405565b6115e5565b60405161022b9190612ec1565b61046e610469366004612311565b611613565b60405161022b9190612d1e565b61035d61175d565b610247610491366004612311565b61176c565b61029d6104a4366004612311565b611799565b61035d6117e4565b60008060015b9050600086828787876040516020016104d4959493929190612be1565b60408051601f198184030181529181528151602092830120600090815260099092529020600201546001600160a01b0316925050505b949350505050565b61051a6110a1565b8061053457503360009081526001602052604090205460ff165b6105595760405162461bcd60e51b815260040161055090612e23565b60405180910390fd5b6105678484848460036105fb565b50505050565b6005546001600160a01b031681565b6105846110a1565b6105a05760405162461bcd60e51b815260040161055090612e23565b6001600160a01b03811660009081526001602052604090819020805460ff19169055517fa3b62bc36326052d97ea62d63c3d60308ed4c3ea8ac079dd8499f1e9c4f80c0f906105f0908390612c3a565b60405180910390a150565b6106036110a1565b8061061d57503360009081526001602052604090205460ff165b6106395760405162461bcd60e51b815260040161055090612e23565b600061064a86858560015b866117f3565b9050856001600160a01b03167fd6fcfd83804f6b6b63260c7b99eb10060bc1319dbc9177fb6defc7bd614017bf828686898760405161068d959493929190612cd2565b60405180910390a2505050505050565b6106a56110a1565b6106c15760405162461bcd60e51b815260040161055090612e23565b6106ca81611b5b565b50565b60016020526000908152604090205460ff1681565b6106ea6110a1565b6107065760405162461bcd60e51b815260040161055090612e23565b600054610100900460ff168061071f575060005460ff16155b61073b5760405162461bcd60e51b815260040161055090612e13565b600054610100900460ff16158015610766576000805460ff1961ff0019909116610100171660011790555b6001600160a01b03881661078c5760405162461bcd60e51b815260040161055090612ea3565b6001600160a01b0387166107b25760405162461bcd60e51b815260040161055090612e83565b6001600160a01b0386166107d85760405162461bcd60e51b815260040161055090612e33565b6001600160a01b0385166107fe5760405162461bcd60e51b815260040161055090612e03565b6001600160a01b0384166108245760405162461bcd60e51b815260040161055090612e73565b61082d89611b5b565b600580546001600160a01b03199081166001600160a01b038b8116919091179092556006805482168a84161790556007805482168984161790556008805482168884161790556003805490911691861691909117905560005b8281101561093357600084848381811061089c57fe5b90506020020160206108b19190810190612311565b6001600160a01b031614156108d85760405162461bcd60e51b815260040161055090612d73565b60048484838181106108e657fe5b90506020020160206108fb9190810190612311565b815460018082018455600093845260209093200180546001600160a01b0319166001600160a01b039290921691909117905501610886565b508015610946576000805461ff00191690555b505050505050505050565b6006546001600160a01b031681565b6007546001600160a01b031681565b6109776110a1565b6109935760405162461bcd60e51b815260040161055090612e23565b6001600160a01b038116600090815260016020819052604091829020805460ff19169091179055517f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e339906105f0908390612c3a565b6109f06110a1565b80610a0a57503360009081526001602052604090205460ff165b610a265760405162461bcd60e51b815260040161055090612e23565b828114610a455760405162461bcd60e51b815260040161055090612d83565b60046224ea0063059fa60060005b86811015610ccd57600b6000878784818110610a6b57fe5b9050602002016020610a809190810190612311565b6001600160a01b0316815260208101919091526040016000205460ff1615610aba5760405162461bcd60e51b815260040161055090612df3565b6000888883818110610ac857fe5b9050602002016020610add9190810190612311565b6001600160a01b03161415610b045760405162461bcd60e51b815260040161055090612d93565b6000868683818110610b1257fe5b9050602002016020610b279190810190612311565b6001600160a01b03161415610b4e5760405162461bcd60e51b815260040161055090612da3565b6000888883818110610b5c57fe5b9050602002016020610b719190810190612311565b6001858588604051602001610b8a959493929190612be1565b60408051808303601f1901815282825280516020918201206060840183526001845290830188905292508101888885818110610bc257fe5b9050602002016020610bd79190810190612311565b6001600160a01b039081169091526000838152600960209081526040808320855181559185015160018301559390930151600290930180546001600160a01b03191693909216929092179055600a908a8a85818110610c3257fe5b9050602002016020610c479190810190612311565b6001600160a01b031681526020808201929092526040016000908120805460018181018355918352928220909201839055600b90898986818110610c8757fe5b9050602002016020610c9c9190810190612311565b6001600160a01b031681526020810191909152604001600020805460ff191691151591909117905550600101610a53565b5050505050505050565b610cdf6110a1565b80610cf957503360009081526001602052604090205460ff165b610d155760405162461bcd60e51b815260040161055090612e23565b6001600160a01b038216610d3b5760405162461bcd60e51b815260040161055090612de3565b60008111610d5b5760405162461bcd60e51b815260040161055090612dd3565b60055460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390610d8d9085908590600401612cb0565b602060405180830381600087803b158015610da757600080fd5b505af1158015610dbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ddf91908101906125e4565b50604051637547c7a360e01b81526001600160a01b03831690637547c7a390610e0c908490600401612ec1565b600060405180830381600087803b158015610e2657600080fd5b505af1158015610e3a573d6000803e3d6000fd5b50505050816001600160a01b03167fb539ca1e5c8d398ddf1c41c30166f33404941683be4683319b57669a93dad4ef82604051610e779190612ec1565b60405180910390a25050565b600c6020526000908152604090205460ff811690610100810463ffffffff16906501000000000090046001600160801b031683565b6009602052600090815260409020805460018201546002909201549091906001600160a01b031683565b60048181548110610eef57fe5b6000918252602090912001546001600160a01b0316905081565b610f116110a1565b80610f2b57503360009081526001602052604090205460ff165b610f475760405162461bcd60e51b815260040161055090612e23565b6000610f5586858584610644565b9050856001600160a01b03167f3791c6c90c276d011b4b885c0bfba0554342acf50a539baca1b06f070af25ff4828686898760405161068d959493929190612cd2565b6000546201000090046001600160a01b031690565b6000806000839050806001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015610fee57600080fd5b505afa158015611002573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110269190810190612620565b816001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561105f57600080fd5b505afa158015611073573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110979190810190612620565b9250925050915091565b600080546201000090046001600160a01b03166110bc611ba3565b6001600160a01b031614905090565b6110d36110a1565b806110ed57503360009081526001602052604090205460ff165b6111095760405162461bcd60e51b815260040161055090612e23565b60005b838110156111d457600085858381811061112257fe5b90506020020160206111379190810190612311565b6001600160a01b0316141561115e5760405162461bcd60e51b815260040161055090612d93565b600083838381811061116c57fe5b90506020020135116111905760405162461bcd60e51b815260040161055090612e43565b6111cc85858381811061119f57fe5b90506020020160206111b49190810190612311565b8484848181106111c057fe5b90506020020135611ba7565b60010161110c565b5050505050565b6111e36110a1565b806111fd57503360009081526001602052604090205460ff165b6112195760405162461bcd60e51b815260040161055090612e23565b805182511461123a5760405162461bcd60e51b815260040161055090612e63565b60005b81518110156113365761124e6120da565b82828151811061125a57fe5b60200260200101519050600084838151811061127257fe5b6020908102919091018101516001600160a01b0381166000818152600c845260409081902086518154958801518884015160ff199097169115159190911764ffffffff00191661010063ffffffff909216919091021765010000000000600160a81b031916650100000000006001600160801b039096169590950294909417845551919350917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca34916113249190612eb3565b60405180910390a2505060010161123d565b505050565b6008546001600160a01b031681565b600b6020526000908152604090205460ff1681565b600080806104b7565b6113706110a1565b61138c5760405162461bcd60e51b815260040161055090612e23565b6001600160a01b0382166113b25760405162461bcd60e51b815260040161055090612e53565b806113cf5760405162461bcd60e51b815260040161055090612dd3565b60055460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906114019085908590600401612cb0565b602060405180830381600087803b15801561141b57600080fd5b505af115801561142f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061145391908101906125e4565b61146f5760405162461bcd60e51b815260040161055090612e93565b816001600160a01b03167fe1b0ada289bf82fb641c8c1e1c78f820fae44e8f845760725cdeb09167a289ad82604051610e779190612ec1565b6001600160a01b03166000908152600b602052604090205460ff1690565b60006115df82600360009054906101000a90046001600160a01b03166001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561151a57600080fd5b505afa15801561152e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115529190810190612620565b600360009054906101000a90046001600160a01b03166001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156115a057600080fd5b505afa1580156115b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115d89190810190612620565b60036104b1565b92915050565b600a60205281600052604060002081815481106115fe57fe5b90600052602060002001600091509150505481565b606080600a6000846001600160a01b03166001600160a01b0316815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561168557602002820191906000526020600020905b815481526020019060010190808311611671575b50505050509050600081519050606082516040519080825280602002602001820160405280156116cf57816020015b6116bc6120fa565b8152602001906001900390816116b45790505b50905060005b8281101561175457600960008583815181106116ed57fe5b6020908102919091018101518252818101929092526040908101600020815160608101835281548152600182015493810193909352600201546001600160a01b031690820152825183908390811061174157fe5b60209081029190910101526001016116d5565b50949350505050565b6003546001600160a01b031681565b6117746110a1565b6117905760405162461bcd60e51b815260040161055090612e23565b6106ca8161204b565b6001600160a01b0381166000908152600c602052604081205460ff1680156115df5750506001600160a01b03166000908152600c6020526040902054610100900463ffffffff161590565b6002546001600160a01b031681565b60008060008785888887604051602001611811959493929190612be1565b60408051601f198184030181529181528151602092830120600081815260099093529120600201549091506001600160a01b0316611b355784600114156118f557600254600554600654600754604051637d2fbb8f60e11b81526001600160a01b039485169463fa5f771e9461189c9490821693908216928f928f928f929116908490600401612c48565b602060405180830381600087803b1580156118b657600080fd5b505af11580156118ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ee919081019061232f565b91506119eb565b600260009054906101000a90046001600160a01b03166001600160a01b031663546344f0600560009054906101000a90046001600160a01b0316600660009054906101000a90046001600160a01b03168b8b8b600760009054906101000a90046001600160a01b0316600860009054906101000a90046001600160a01b03166040518863ffffffff1660e01b81526004016119969796959493929190612c48565b602060405180830381600087803b1580156119b057600080fd5b505af11580156119c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119e8919081019061232f565b91505b604080516060808201835287825260208083018881526001600160a01b038088168587018181526000898152600986528881209751885593516001808901919091559051600290970180549784166001600160a01b031990981697909717909655908e168252600a83528582208054808701825590835283832001879055808252600b8352858220805460ff1990811687179091558651948501875294845263ffffffff808c168585019081526001600160801b03808d16878a01908152848652600c909652938890209551865491519551909416650100000000000265010000000000600160a81b0319959092166101000264ffffffff00199415159190971617929092169490941791909116178155915190917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca3491611b2c9190612eb3565b60405180910390a25b6000908152600960205260409020600201546001600160a01b0316979650505050505050565b6001600160a01b038116611b815760405162461bcd60e51b815260040161055090612dc3565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b600080600183039050600060048281548110611bbf57fe5b60009182526020909120015460405163cc49ede760e01b81526001600160a01b039091169063cc49ede790611bf8908890600401612c3a565b60206040518083038186803b158015611c1057600080fd5b505afa158015611c24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c48919081019061232f565b90506001600160a01b03811615611df85780856001826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611c9657600080fd5b505afa158015611caa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611cce9190810190612620565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611d0757600080fd5b505afa158015611d1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d3f9190810190612620565b88604051602001611d54959493929190612be1565b60408051601f19818403018152828252805160209182012060608401835260018085528285018a81526001600160a01b0388811687870181815260008681526009885288812099518a5593518986015551600290980180546001600160a01b031916988316989098179097558c168152600a84528481208054808401825590825284822001839055948552600b90925291909220805460ff19169092179091559350505b600060048381548110611e0757fe5b60009182526020909120015460405163c810a3e360e01b81526001600160a01b039091169063c810a3e390611e40908990600401612c3a565b60206040518083038186803b158015611e5857600080fd5b505afa158015611e6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e90919081019061232f565b90506001600160a01b038116156120435780866000826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611ede57600080fd5b505afa158015611ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f169190810190612620565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611f4f57600080fd5b505afa158015611f63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f879190810190612620565b89604051602001611f9c959493929190612be1565b60408051601f19818403018152828252805160209182012060608401835260008085528285018b81526001600160a01b03888116878701818152858552600987528785209851895592516001808a01919091559251600290980180546001600160a01b031916988316989098179097558d168252600a84528482208054808301825590835284832001839055948152600b909252919020805460ff19169092179091559450505b505050505050565b6001600160a01b0381166120715760405162461bcd60e51b815260040161055090612db3565b600080546040516001600160a01b03808516936201000090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b604080516060810182526000808252602082018190529181019190915290565b6040518060600160405280600081526020016000815260200160006001600160a01b031681525090565b80356115df81612ffc565b80516115df81612ffc565b60008083601f84011261214c57600080fd5b50813567ffffffffffffffff81111561216457600080fd5b60208301915083602082028301111561217c57600080fd5b9250929050565b600082601f83011261219457600080fd5b81356121a76121a282612f2c565b612f05565b915081818352602084019350602081019050838560208402820111156121cc57600080fd5b60005b838110156121f857816121e28882612124565b84525060209283019291909101906001016121cf565b5050505092915050565b600082601f83011261221357600080fd5b81356122216121a282612f2c565b9150818183526020840193506020810190508385606084028201111561224657600080fd5b60005b838110156121f8578161225c888261228a565b84525060209092019160609190910190600101612249565b80356115df81613010565b80516115df81613010565b60006060828403121561229c57600080fd5b6122a66060612f05565b905060006122b48484612274565b82525060206122c584848301612306565b60208301525060406122d9848285016122e5565b60408301525092915050565b80356115df81613019565b80356115df81613022565b80516115df81613022565b80356115df8161302b565b60006020828403121561232357600080fd5b600061050a8484612124565b60006020828403121561234157600080fd5b600061050a848461212f565b60008060008060008060008060e0898b03121561236957600080fd5b60006123758b8b612124565b98505060206123868b828c01612124565b97505060406123978b828c01612124565b96505060606123a88b828c01612124565b95505060806123b98b828c01612124565b94505060a06123ca8b828c01612124565b93505060c089013567ffffffffffffffff8111156123e757600080fd5b6123f38b828c0161213a565b92509250509295985092959890939650565b6000806040838503121561241857600080fd5b60006124248585612124565b9250506020612435858286016122f0565b9150509250929050565b6000806000806080858703121561245557600080fd5b60006124618787612124565b9450506020612472878288016122f0565b9350506040612483878288016122f0565b9250506060612494878288016122f0565b91505092959194509250565b600080600080600060a086880312156124b857600080fd5b60006124c48888612124565b95505060206124d5888289016122f0565b94505060406124e6888289016122f0565b93505060606124f7888289016122f0565b9250506080612508888289016122f0565b9150509295509295909350565b6000806000806040858703121561252b57600080fd5b843567ffffffffffffffff81111561254257600080fd5b61254e8782880161213a565b9450945050602085013567ffffffffffffffff81111561256d57600080fd5b6125798782880161213a565b95989497509550505050565b6000806040838503121561259857600080fd5b823567ffffffffffffffff8111156125af57600080fd5b6125bb85828601612183565b925050602083013567ffffffffffffffff8111156125d857600080fd5b61243585828601612202565b6000602082840312156125f657600080fd5b600061050a848461227f565b60006020828403121561261457600080fd5b600061050a84846122f0565b60006020828403121561263257600080fd5b600061050a84846122fb565b600061264a8383612b7e565b505060600190565b61265b81612f7b565b82525050565b61265b61266d82612f7b565b612fde565b600061267d82612f53565b6126878185612f57565b935061269283612f4d565b8060005b838110156126c05781516126aa888261263e565b97506126b583612f4d565b925050600101612696565b509495945050505050565b61265b81612f86565b61265b81612f9a565b60006126ea602083612f57565b7f56657374696e67207265676973747279206164647265737320696e76616c6964815260200192915050565b6000612723600f83612f57565b6e0c2e4e4c2f2e640dad2e6dac2e8c6d608b1b815260200192915050565b600061274e601f83612f57565b7f746f6b656e206f776e65722063616e6e6f742062652030206164647265737300815260200192915050565b6000612787601b83612f57565b7f76657374696e672063616e6e6f74206265203020616464726573730000000000815260200192915050565b60006127c0602683612f57565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b6000612808601e83612f57565b7f76657374696e67466163746f7279206164647265737320696e76616c69640000815260200192915050565b6000612841600e83612f57565b6d185b5bdd5b9d081a5b9d985b1a5960921b815260200192915050565b600061286b601783612f57565b7f76657374696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b60006128a4600e83612f57565b6d76657374696e672065786973747360901b815260200192915050565b60006128ce601c83612f57565b7f76657374696e674f776e6572206164647265737320696e76616c696400000000815260200192915050565b6000612907602e83612f57565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656181526d191e481a5b9a5d1a585b1a5e995960921b602082015260400192915050565b6000612957600c83612f57565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b600061297f602383612f57565b7f66656553686172696e67436f6c6c6563746f72206164647265737320696e76618152621b1a5960ea1b602082015260400192915050565b60006129c4602c83612f57565b7f76657374696e67206372656174696f6e2074797065206d75737420626520677281526b06561746572207468616e20360a41b602082015260400192915050565b6000612a12601883612f57565b7f7265636569766572206164647265737320696e76616c69640000000000000000815260200192915050565b6000612a4b601083612f57565b6f0aadcdac2e8c6d0cac840d8cadccee8d60831b815260200192915050565b6000612a77601983612f57565b7f4c6f636b6564534f56206164647265737320696e76616c696400000000000000815260200192915050565b6000612ab0601783612f57565b7f7374616b696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612ae9600f83612f57565b6e1d1c985b9cd9995c8819985a5b1959608a1b815260200192915050565b6000612b14601383612f57565b7214d3d5881859191c995cdcc81a5b9d985b1a59606a1b815260200192915050565b80546060830190612b4681612fa5565b612b5085826126cb565b50612b5a81612fb8565b612b676020860182612bd8565b50612b7181612fcb565b6111d46040860182612bb5565b80516060830190612b8f8482612bbe565b506020820151612ba26020850182612bbe565b5060408201516105676040850182612652565b61265b81612f66565b61265b81612f97565b61265b612bd382612f97565b612f97565b61265b81612f72565b6000612bed8288612661565b601482019150612bfd8287612bc7565b602082019150612c0d8286612bc7565b602082019150612c1d8285612bc7565b602082019150612c2d8284612bc7565b5060200195945050505050565b602081016115df8284612652565b60e08101612c56828a612652565b612c636020830189612652565b612c706040830188612652565b612c7d6060830187612bbe565b612c8a6080830186612bbe565b612c9760a0830185612652565b612ca460c0830184612652565b98975050505050505050565b60408101612cbe8285612652565b612ccb6020830184612bbe565b9392505050565b60a08101612ce08288612652565b612ced6020830187612bbe565b612cfa6040830186612bbe565b612d076060830185612bbe565b612d146080830184612bbe565b9695505050505050565b60208082528101612ccb8184612672565b602081016115df82846126cb565b60608101612d4b82866126cb565b612d586020830185612bd8565b61050a6040830184612bb5565b602081016115df82846126d4565b602080825281016115df816126dd565b602080825281016115df81612716565b602080825281016115df81612741565b602080825281016115df8161277a565b602080825281016115df816127b3565b602080825281016115df816127fb565b602080825281016115df81612834565b602080825281016115df8161285e565b602080825281016115df81612897565b602080825281016115df816128c1565b602080825281016115df816128fa565b602080825281016115df8161294a565b602080825281016115df81612972565b602080825281016115df816129b7565b602080825281016115df81612a05565b602080825281016115df81612a3e565b602080825281016115df81612a6a565b602080825281016115df81612aa3565b602080825281016115df81612adc565b602080825281016115df81612b07565b606081016115df8284612b36565b602081016115df8284612bbe565b60408101612cbe8285612bbe565b60608101612eeb8286612bbe565b612ef86020830185612bbe565b61050a6040830184612652565b60405181810167ffffffffffffffff81118282101715612f2457600080fd5b604052919050565b600067ffffffffffffffff821115612f4357600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b60ff1690565b6001600160801b031690565b63ffffffff1690565b60006115df82612f8b565b151590565b6001600160a01b031690565b90565b60006115df82612f7b565b60006115df612fb383612f97565b612f60565b60006115df612fc683612ff6565b612f72565b60006115df612fd983612ff0565b612f66565b60006115df8260006115df8260601b90565b60281c90565b60081c90565b61300581612f7b565b81146106ca57600080fd5b61300581612f86565b61300581612f66565b61300581612f97565b61300581612f7256fea365627a7a723158201d7444ad20959aa55436fb5bdba711b0369fe69491b25c92fb66c7287a486a1b6c6578706572696d656e74616cf564736f6c63430005110040", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102065760003560e01c80638da5cb5b1161011a578063c680c0b7116100ad578063efb957331161007c578063efb957331461045b578063f2f46b3b1461047b578063f2fde38b14610483578063f421ed7e14610496578063f60826ee146104a957610206565b8063c680c0b714610402578063ca210d8c14610415578063cc49ede714610428578063dfb9366d1461043b57610206565b8063bc84f9ca116100e9578063bc84f9ca146103c1578063bd7b5908146103d4578063c0e09852146103dc578063c36519d1146103ef57610206565b80638da5cb5b1461037d5780638dedf009146103855780638f32d59b146103a6578063b810c648146103ae57610206565b80634cf088d91161019d57806379a83f5a1161016c57806379a83f5a146102f357806380fcb99214610306578063821bee7314610328578063842a49d51461034a578063862e229d1461036a57610206565b80634cf088d9146102bd5780636b7dbb2d146102c557806370480275146102cd578063786931da146102e057610206565b80631f509326116101d95780631f50932614610264578063377220fd14610277578063429b62e51461028a57806342a82b4f146102aa57610206565b806302df04761461020b5780630665a06f1461023457806308dcb360146102495780631785f53c14610251575b600080fd5b61021e61021936600461243f565b6104b1565b60405161022b9190612c3a565b60405180910390f35b61024761024236600461243f565b610512565b005b61021e61056d565b61024761025f366004612311565b61057c565b6102476102723660046124a0565b6105fb565b610247610285366004612311565b61069d565b61029d610298366004612311565b6106cd565b60405161022b9190612d2f565b6102476102b836600461234d565b6106e2565b61021e610951565b61021e610960565b6102476102db366004612311565b61096f565b6102476102ee366004612515565b6109e8565b610247610301366004612405565b610cd7565b610319610314366004612311565b610e83565b60405161022b93929190612d3d565b61033b610336366004612602565b610eb8565b60405161022b93929190612edd565b61035d610358366004612602565b610ee2565b60405161022b9190612d65565b6102476103783660046124a0565b610f09565b61021e610f98565b610398610393366004612311565b610fad565b60405161022b929190612ecf565b61029d6110a1565b6102476103bc366004612515565b6110cb565b6102476103cf366004612585565b6111db565b61021e61133b565b61029d6103ea366004612311565b61134a565b61021e6103fd36600461243f565b61135f565b610247610410366004612405565b611368565b61029d610423366004612311565b6114a8565b61021e610436366004612311565b6114c6565b61044e610449366004612405565b6115e5565b60405161022b9190612ec1565b61046e610469366004612311565b611613565b60405161022b9190612d1e565b61035d61175d565b610247610491366004612311565b61176c565b61029d6104a4366004612311565b611799565b61035d6117e4565b60008060015b9050600086828787876040516020016104d4959493929190612be1565b60408051601f198184030181529181528151602092830120600090815260099092529020600201546001600160a01b0316925050505b949350505050565b61051a6110a1565b8061053457503360009081526001602052604090205460ff165b6105595760405162461bcd60e51b815260040161055090612e23565b60405180910390fd5b6105678484848460036105fb565b50505050565b6005546001600160a01b031681565b6105846110a1565b6105a05760405162461bcd60e51b815260040161055090612e23565b6001600160a01b03811660009081526001602052604090819020805460ff19169055517fa3b62bc36326052d97ea62d63c3d60308ed4c3ea8ac079dd8499f1e9c4f80c0f906105f0908390612c3a565b60405180910390a150565b6106036110a1565b8061061d57503360009081526001602052604090205460ff165b6106395760405162461bcd60e51b815260040161055090612e23565b600061064a86858560015b866117f3565b9050856001600160a01b03167fd6fcfd83804f6b6b63260c7b99eb10060bc1319dbc9177fb6defc7bd614017bf828686898760405161068d959493929190612cd2565b60405180910390a2505050505050565b6106a56110a1565b6106c15760405162461bcd60e51b815260040161055090612e23565b6106ca81611b5b565b50565b60016020526000908152604090205460ff1681565b6106ea6110a1565b6107065760405162461bcd60e51b815260040161055090612e23565b600054610100900460ff168061071f575060005460ff16155b61073b5760405162461bcd60e51b815260040161055090612e13565b600054610100900460ff16158015610766576000805460ff1961ff0019909116610100171660011790555b6001600160a01b03881661078c5760405162461bcd60e51b815260040161055090612ea3565b6001600160a01b0387166107b25760405162461bcd60e51b815260040161055090612e83565b6001600160a01b0386166107d85760405162461bcd60e51b815260040161055090612e33565b6001600160a01b0385166107fe5760405162461bcd60e51b815260040161055090612e03565b6001600160a01b0384166108245760405162461bcd60e51b815260040161055090612e73565b61082d89611b5b565b600580546001600160a01b03199081166001600160a01b038b8116919091179092556006805482168a84161790556007805482168984161790556008805482168884161790556003805490911691861691909117905560005b8281101561093357600084848381811061089c57fe5b90506020020160206108b19190810190612311565b6001600160a01b031614156108d85760405162461bcd60e51b815260040161055090612d73565b60048484838181106108e657fe5b90506020020160206108fb9190810190612311565b815460018082018455600093845260209093200180546001600160a01b0319166001600160a01b039290921691909117905501610886565b508015610946576000805461ff00191690555b505050505050505050565b6006546001600160a01b031681565b6007546001600160a01b031681565b6109776110a1565b6109935760405162461bcd60e51b815260040161055090612e23565b6001600160a01b038116600090815260016020819052604091829020805460ff19169091179055517f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e339906105f0908390612c3a565b6109f06110a1565b80610a0a57503360009081526001602052604090205460ff165b610a265760405162461bcd60e51b815260040161055090612e23565b828114610a455760405162461bcd60e51b815260040161055090612d83565b60046224ea0063059fa60060005b86811015610ccd57600b6000878784818110610a6b57fe5b9050602002016020610a809190810190612311565b6001600160a01b0316815260208101919091526040016000205460ff1615610aba5760405162461bcd60e51b815260040161055090612df3565b6000888883818110610ac857fe5b9050602002016020610add9190810190612311565b6001600160a01b03161415610b045760405162461bcd60e51b815260040161055090612d93565b6000868683818110610b1257fe5b9050602002016020610b279190810190612311565b6001600160a01b03161415610b4e5760405162461bcd60e51b815260040161055090612da3565b6000888883818110610b5c57fe5b9050602002016020610b719190810190612311565b6001858588604051602001610b8a959493929190612be1565b60408051808303601f1901815282825280516020918201206060840183526001845290830188905292508101888885818110610bc257fe5b9050602002016020610bd79190810190612311565b6001600160a01b039081169091526000838152600960209081526040808320855181559185015160018301559390930151600290930180546001600160a01b03191693909216929092179055600a908a8a85818110610c3257fe5b9050602002016020610c479190810190612311565b6001600160a01b031681526020808201929092526040016000908120805460018181018355918352928220909201839055600b90898986818110610c8757fe5b9050602002016020610c9c9190810190612311565b6001600160a01b031681526020810191909152604001600020805460ff191691151591909117905550600101610a53565b5050505050505050565b610cdf6110a1565b80610cf957503360009081526001602052604090205460ff165b610d155760405162461bcd60e51b815260040161055090612e23565b6001600160a01b038216610d3b5760405162461bcd60e51b815260040161055090612de3565b60008111610d5b5760405162461bcd60e51b815260040161055090612dd3565b60055460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390610d8d9085908590600401612cb0565b602060405180830381600087803b158015610da757600080fd5b505af1158015610dbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ddf91908101906125e4565b50604051637547c7a360e01b81526001600160a01b03831690637547c7a390610e0c908490600401612ec1565b600060405180830381600087803b158015610e2657600080fd5b505af1158015610e3a573d6000803e3d6000fd5b50505050816001600160a01b03167fb539ca1e5c8d398ddf1c41c30166f33404941683be4683319b57669a93dad4ef82604051610e779190612ec1565b60405180910390a25050565b600c6020526000908152604090205460ff811690610100810463ffffffff16906501000000000090046001600160801b031683565b6009602052600090815260409020805460018201546002909201549091906001600160a01b031683565b60048181548110610eef57fe5b6000918252602090912001546001600160a01b0316905081565b610f116110a1565b80610f2b57503360009081526001602052604090205460ff165b610f475760405162461bcd60e51b815260040161055090612e23565b6000610f5586858584610644565b9050856001600160a01b03167f3791c6c90c276d011b4b885c0bfba0554342acf50a539baca1b06f070af25ff4828686898760405161068d959493929190612cd2565b6000546201000090046001600160a01b031690565b6000806000839050806001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015610fee57600080fd5b505afa158015611002573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110269190810190612620565b816001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561105f57600080fd5b505afa158015611073573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110979190810190612620565b9250925050915091565b600080546201000090046001600160a01b03166110bc611ba3565b6001600160a01b031614905090565b6110d36110a1565b806110ed57503360009081526001602052604090205460ff165b6111095760405162461bcd60e51b815260040161055090612e23565b60005b838110156111d457600085858381811061112257fe5b90506020020160206111379190810190612311565b6001600160a01b0316141561115e5760405162461bcd60e51b815260040161055090612d93565b600083838381811061116c57fe5b90506020020135116111905760405162461bcd60e51b815260040161055090612e43565b6111cc85858381811061119f57fe5b90506020020160206111b49190810190612311565b8484848181106111c057fe5b90506020020135611ba7565b60010161110c565b5050505050565b6111e36110a1565b806111fd57503360009081526001602052604090205460ff165b6112195760405162461bcd60e51b815260040161055090612e23565b805182511461123a5760405162461bcd60e51b815260040161055090612e63565b60005b81518110156113365761124e6120da565b82828151811061125a57fe5b60200260200101519050600084838151811061127257fe5b6020908102919091018101516001600160a01b0381166000818152600c845260409081902086518154958801518884015160ff199097169115159190911764ffffffff00191661010063ffffffff909216919091021765010000000000600160a81b031916650100000000006001600160801b039096169590950294909417845551919350917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca34916113249190612eb3565b60405180910390a2505060010161123d565b505050565b6008546001600160a01b031681565b600b6020526000908152604090205460ff1681565b600080806104b7565b6113706110a1565b61138c5760405162461bcd60e51b815260040161055090612e23565b6001600160a01b0382166113b25760405162461bcd60e51b815260040161055090612e53565b806113cf5760405162461bcd60e51b815260040161055090612dd3565b60055460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906114019085908590600401612cb0565b602060405180830381600087803b15801561141b57600080fd5b505af115801561142f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061145391908101906125e4565b61146f5760405162461bcd60e51b815260040161055090612e93565b816001600160a01b03167fe1b0ada289bf82fb641c8c1e1c78f820fae44e8f845760725cdeb09167a289ad82604051610e779190612ec1565b6001600160a01b03166000908152600b602052604090205460ff1690565b60006115df82600360009054906101000a90046001600160a01b03166001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561151a57600080fd5b505afa15801561152e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115529190810190612620565b600360009054906101000a90046001600160a01b03166001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156115a057600080fd5b505afa1580156115b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115d89190810190612620565b60036104b1565b92915050565b600a60205281600052604060002081815481106115fe57fe5b90600052602060002001600091509150505481565b606080600a6000846001600160a01b03166001600160a01b0316815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561168557602002820191906000526020600020905b815481526020019060010190808311611671575b50505050509050600081519050606082516040519080825280602002602001820160405280156116cf57816020015b6116bc6120fa565b8152602001906001900390816116b45790505b50905060005b8281101561175457600960008583815181106116ed57fe5b6020908102919091018101518252818101929092526040908101600020815160608101835281548152600182015493810193909352600201546001600160a01b031690820152825183908390811061174157fe5b60209081029190910101526001016116d5565b50949350505050565b6003546001600160a01b031681565b6117746110a1565b6117905760405162461bcd60e51b815260040161055090612e23565b6106ca8161204b565b6001600160a01b0381166000908152600c602052604081205460ff1680156115df5750506001600160a01b03166000908152600c6020526040902054610100900463ffffffff161590565b6002546001600160a01b031681565b60008060008785888887604051602001611811959493929190612be1565b60408051601f198184030181529181528151602092830120600081815260099093529120600201549091506001600160a01b0316611b355784600114156118f557600254600554600654600754604051637d2fbb8f60e11b81526001600160a01b039485169463fa5f771e9461189c9490821693908216928f928f928f929116908490600401612c48565b602060405180830381600087803b1580156118b657600080fd5b505af11580156118ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ee919081019061232f565b91506119eb565b600260009054906101000a90046001600160a01b03166001600160a01b031663546344f0600560009054906101000a90046001600160a01b0316600660009054906101000a90046001600160a01b03168b8b8b600760009054906101000a90046001600160a01b0316600860009054906101000a90046001600160a01b03166040518863ffffffff1660e01b81526004016119969796959493929190612c48565b602060405180830381600087803b1580156119b057600080fd5b505af11580156119c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119e8919081019061232f565b91505b604080516060808201835287825260208083018881526001600160a01b038088168587018181526000898152600986528881209751885593516001808901919091559051600290970180549784166001600160a01b031990981697909717909655908e168252600a83528582208054808701825590835283832001879055808252600b8352858220805460ff1990811687179091558651948501875294845263ffffffff808c168585019081526001600160801b03808d16878a01908152848652600c909652938890209551865491519551909416650100000000000265010000000000600160a81b0319959092166101000264ffffffff00199415159190971617929092169490941791909116178155915190917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca3491611b2c9190612eb3565b60405180910390a25b6000908152600960205260409020600201546001600160a01b0316979650505050505050565b6001600160a01b038116611b815760405162461bcd60e51b815260040161055090612dc3565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b600080600183039050600060048281548110611bbf57fe5b60009182526020909120015460405163cc49ede760e01b81526001600160a01b039091169063cc49ede790611bf8908890600401612c3a565b60206040518083038186803b158015611c1057600080fd5b505afa158015611c24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c48919081019061232f565b90506001600160a01b03811615611df85780856001826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611c9657600080fd5b505afa158015611caa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611cce9190810190612620565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611d0757600080fd5b505afa158015611d1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d3f9190810190612620565b88604051602001611d54959493929190612be1565b60408051601f19818403018152828252805160209182012060608401835260018085528285018a81526001600160a01b0388811687870181815260008681526009885288812099518a5593518986015551600290980180546001600160a01b031916988316989098179097558c168152600a84528481208054808401825590825284822001839055948552600b90925291909220805460ff19169092179091559350505b600060048381548110611e0757fe5b60009182526020909120015460405163c810a3e360e01b81526001600160a01b039091169063c810a3e390611e40908990600401612c3a565b60206040518083038186803b158015611e5857600080fd5b505afa158015611e6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e90919081019061232f565b90506001600160a01b038116156120435780866000826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611ede57600080fd5b505afa158015611ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f169190810190612620565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611f4f57600080fd5b505afa158015611f63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f879190810190612620565b89604051602001611f9c959493929190612be1565b60408051601f19818403018152828252805160209182012060608401835260008085528285018b81526001600160a01b03888116878701818152858552600987528785209851895592516001808a01919091559251600290980180546001600160a01b031916988316989098179097558d168252600a84528482208054808301825590835284832001839055948152600b909252919020805460ff19169092179091559450505b505050505050565b6001600160a01b0381166120715760405162461bcd60e51b815260040161055090612db3565b600080546040516001600160a01b03808516936201000090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b604080516060810182526000808252602082018190529181019190915290565b6040518060600160405280600081526020016000815260200160006001600160a01b031681525090565b80356115df81612ffc565b80516115df81612ffc565b60008083601f84011261214c57600080fd5b50813567ffffffffffffffff81111561216457600080fd5b60208301915083602082028301111561217c57600080fd5b9250929050565b600082601f83011261219457600080fd5b81356121a76121a282612f2c565b612f05565b915081818352602084019350602081019050838560208402820111156121cc57600080fd5b60005b838110156121f857816121e28882612124565b84525060209283019291909101906001016121cf565b5050505092915050565b600082601f83011261221357600080fd5b81356122216121a282612f2c565b9150818183526020840193506020810190508385606084028201111561224657600080fd5b60005b838110156121f8578161225c888261228a565b84525060209092019160609190910190600101612249565b80356115df81613010565b80516115df81613010565b60006060828403121561229c57600080fd5b6122a66060612f05565b905060006122b48484612274565b82525060206122c584848301612306565b60208301525060406122d9848285016122e5565b60408301525092915050565b80356115df81613019565b80356115df81613022565b80516115df81613022565b80356115df8161302b565b60006020828403121561232357600080fd5b600061050a8484612124565b60006020828403121561234157600080fd5b600061050a848461212f565b60008060008060008060008060e0898b03121561236957600080fd5b60006123758b8b612124565b98505060206123868b828c01612124565b97505060406123978b828c01612124565b96505060606123a88b828c01612124565b95505060806123b98b828c01612124565b94505060a06123ca8b828c01612124565b93505060c089013567ffffffffffffffff8111156123e757600080fd5b6123f38b828c0161213a565b92509250509295985092959890939650565b6000806040838503121561241857600080fd5b60006124248585612124565b9250506020612435858286016122f0565b9150509250929050565b6000806000806080858703121561245557600080fd5b60006124618787612124565b9450506020612472878288016122f0565b9350506040612483878288016122f0565b9250506060612494878288016122f0565b91505092959194509250565b600080600080600060a086880312156124b857600080fd5b60006124c48888612124565b95505060206124d5888289016122f0565b94505060406124e6888289016122f0565b93505060606124f7888289016122f0565b9250506080612508888289016122f0565b9150509295509295909350565b6000806000806040858703121561252b57600080fd5b843567ffffffffffffffff81111561254257600080fd5b61254e8782880161213a565b9450945050602085013567ffffffffffffffff81111561256d57600080fd5b6125798782880161213a565b95989497509550505050565b6000806040838503121561259857600080fd5b823567ffffffffffffffff8111156125af57600080fd5b6125bb85828601612183565b925050602083013567ffffffffffffffff8111156125d857600080fd5b61243585828601612202565b6000602082840312156125f657600080fd5b600061050a848461227f565b60006020828403121561261457600080fd5b600061050a84846122f0565b60006020828403121561263257600080fd5b600061050a84846122fb565b600061264a8383612b7e565b505060600190565b61265b81612f7b565b82525050565b61265b61266d82612f7b565b612fde565b600061267d82612f53565b6126878185612f57565b935061269283612f4d565b8060005b838110156126c05781516126aa888261263e565b97506126b583612f4d565b925050600101612696565b509495945050505050565b61265b81612f86565b61265b81612f9a565b60006126ea602083612f57565b7f56657374696e67207265676973747279206164647265737320696e76616c6964815260200192915050565b6000612723600f83612f57565b6e0c2e4e4c2f2e640dad2e6dac2e8c6d608b1b815260200192915050565b600061274e601f83612f57565b7f746f6b656e206f776e65722063616e6e6f742062652030206164647265737300815260200192915050565b6000612787601b83612f57565b7f76657374696e672063616e6e6f74206265203020616464726573730000000000815260200192915050565b60006127c0602683612f57565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b6000612808601e83612f57565b7f76657374696e67466163746f7279206164647265737320696e76616c69640000815260200192915050565b6000612841600e83612f57565b6d185b5bdd5b9d081a5b9d985b1a5960921b815260200192915050565b600061286b601783612f57565b7f76657374696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b60006128a4600e83612f57565b6d76657374696e672065786973747360901b815260200192915050565b60006128ce601c83612f57565b7f76657374696e674f776e6572206164647265737320696e76616c696400000000815260200192915050565b6000612907602e83612f57565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656181526d191e481a5b9a5d1a585b1a5e995960921b602082015260400192915050565b6000612957600c83612f57565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b600061297f602383612f57565b7f66656553686172696e67436f6c6c6563746f72206164647265737320696e76618152621b1a5960ea1b602082015260400192915050565b60006129c4602c83612f57565b7f76657374696e67206372656174696f6e2074797065206d75737420626520677281526b06561746572207468616e20360a41b602082015260400192915050565b6000612a12601883612f57565b7f7265636569766572206164647265737320696e76616c69640000000000000000815260200192915050565b6000612a4b601083612f57565b6f0aadcdac2e8c6d0cac840d8cadccee8d60831b815260200192915050565b6000612a77601983612f57565b7f4c6f636b6564534f56206164647265737320696e76616c696400000000000000815260200192915050565b6000612ab0601783612f57565b7f7374616b696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612ae9600f83612f57565b6e1d1c985b9cd9995c8819985a5b1959608a1b815260200192915050565b6000612b14601383612f57565b7214d3d5881859191c995cdcc81a5b9d985b1a59606a1b815260200192915050565b80546060830190612b4681612fa5565b612b5085826126cb565b50612b5a81612fb8565b612b676020860182612bd8565b50612b7181612fcb565b6111d46040860182612bb5565b80516060830190612b8f8482612bbe565b506020820151612ba26020850182612bbe565b5060408201516105676040850182612652565b61265b81612f66565b61265b81612f97565b61265b612bd382612f97565b612f97565b61265b81612f72565b6000612bed8288612661565b601482019150612bfd8287612bc7565b602082019150612c0d8286612bc7565b602082019150612c1d8285612bc7565b602082019150612c2d8284612bc7565b5060200195945050505050565b602081016115df8284612652565b60e08101612c56828a612652565b612c636020830189612652565b612c706040830188612652565b612c7d6060830187612bbe565b612c8a6080830186612bbe565b612c9760a0830185612652565b612ca460c0830184612652565b98975050505050505050565b60408101612cbe8285612652565b612ccb6020830184612bbe565b9392505050565b60a08101612ce08288612652565b612ced6020830187612bbe565b612cfa6040830186612bbe565b612d076060830185612bbe565b612d146080830184612bbe565b9695505050505050565b60208082528101612ccb8184612672565b602081016115df82846126cb565b60608101612d4b82866126cb565b612d586020830185612bd8565b61050a6040830184612bb5565b602081016115df82846126d4565b602080825281016115df816126dd565b602080825281016115df81612716565b602080825281016115df81612741565b602080825281016115df8161277a565b602080825281016115df816127b3565b602080825281016115df816127fb565b602080825281016115df81612834565b602080825281016115df8161285e565b602080825281016115df81612897565b602080825281016115df816128c1565b602080825281016115df816128fa565b602080825281016115df8161294a565b602080825281016115df81612972565b602080825281016115df816129b7565b602080825281016115df81612a05565b602080825281016115df81612a3e565b602080825281016115df81612a6a565b602080825281016115df81612aa3565b602080825281016115df81612adc565b602080825281016115df81612b07565b606081016115df8284612b36565b602081016115df8284612bbe565b60408101612cbe8285612bbe565b60608101612eeb8286612bbe565b612ef86020830185612bbe565b61050a6040830184612652565b60405181810167ffffffffffffffff81118282101715612f2457600080fd5b604052919050565b600067ffffffffffffffff821115612f4357600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b60ff1690565b6001600160801b031690565b63ffffffff1690565b60006115df82612f8b565b151590565b6001600160a01b031690565b90565b60006115df82612f7b565b60006115df612fb383612f97565b612f60565b60006115df612fc683612ff6565b612f72565b60006115df612fd983612ff0565b612f66565b60006115df8260006115df8260601b90565b60281c90565b60081c90565b61300581612f7b565b81146106ca57600080fd5b61300581612f86565b61300581612f66565b61300581612f97565b61300581612f7256fea365627a7a723158201d7444ad20959aa55436fb5bdba711b0369fe69491b25c92fb66c7287a486a1b6c6578706572696d656e74616cf564736f6c63430005110040", + "numDeployments": 2, + "solcInputHash": "757b9c0328cdd77755520b9ff358f21a", + "metadata": "{\"compiler\":{\"version\":\"0.5.17+commit.d19bba13\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"AdminAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldAdminManager\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAdminManager\",\"type\":\"address\"}],\"name\":\"AdminManagerChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"removedAdminManager\",\"type\":\"address\"}],\"name\":\"AdminManagerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"admin\",\"type\":\"address\"}],\"name\":\"AdminRemoved\",\"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\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"SOVTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"tokenOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"vesting\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"cliff\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"duration\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"TeamVestingCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"vesting\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"TokensStaked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"tokenOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"vesting\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"cliff\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"duration\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"VestingCreated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"vesting\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isSet\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"vestingType\",\"type\":\"uint32\"},{\"internalType\":\"uint128\",\"name\":\"vestingCreationType\",\"type\":\"uint128\"}],\"indexed\":false,\"internalType\":\"struct VestingRegistryStorage.VestingCreationAndTypeDetails\",\"name\":\"vestingCreationAndType\",\"type\":\"tuple\"}],\"name\":\"VestingCreationAndTypesSet\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[],\"name\":\"SOV\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_admin\",\"type\":\"address\"}],\"name\":\"addAdmin\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokenOwners\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"_vestingCreationTypes\",\"type\":\"uint256[]\"}],\"name\":\"addDeployedVestings\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_tokenOwners\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_vestingAddresses\",\"type\":\"address[]\"}],\"name\":\"addFourYearVestings\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"admins\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_duration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"createTeamVesting\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_duration\",\"type\":\"uint256\"}],\"name\":\"createVesting\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_duration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"createVestingAddr\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"feeSharingCollector\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getAdminManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"_adminManager\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_duration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"getTeamVesting\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"}],\"name\":\"getVesting\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_duration\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_vestingCreationType\",\"type\":\"uint256\"}],\"name\":\"getVestingAddr\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vestingAddress\",\"type\":\"address\"}],\"name\":\"getVestingDetails\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"cliff\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"duration\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_tokenOwner\",\"type\":\"address\"}],\"name\":\"getVestingsOf\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"vestingType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"vestingCreationType\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"vestingAddress\",\"type\":\"address\"}],\"internalType\":\"struct VestingRegistryStorage.Vesting[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vestingFactory\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_SOV\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_staking\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_feeSharingCollector\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_vestingOwner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_lockedSOV\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"_vestingRegistries\",\"type\":\"address[]\"}],\"name\":\"initialize\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"isOwner\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vestingAddress\",\"type\":\"address\"}],\"name\":\"isTeamVesting\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isVesting\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vestingAddress\",\"type\":\"address\"}],\"name\":\"isVestingAddress\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isVestingAddr\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"lockedSOV\",\"outputs\":[{\"internalType\":\"contract ILockedSOV\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_vestingAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"isSet\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"vestingType\",\"type\":\"uint32\"},{\"internalType\":\"uint128\",\"name\":\"vestingCreationType\",\"type\":\"uint128\"}],\"internalType\":\"struct VestingRegistryStorage.VestingCreationAndTypeDetails[]\",\"name\":\"_vestingCreationAndTypes\",\"type\":\"tuple[]\"}],\"name\":\"registerVestingToVestingCreationAndTypes\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_admin\",\"type\":\"address\"}],\"name\":\"removeAdmin\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"removeAdminManager\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_newAdminManager\",\"type\":\"address\"}],\"name\":\"setAdminManager\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vestingFactory\",\"type\":\"address\"}],\"name\":\"setVestingFactory\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_vesting\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"stakeTokens\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"staking\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"internalType\":\"address\",\"name\":\"_receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"transferSOV\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"vestingCreationAndTypes\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isSet\",\"type\":\"bool\"},{\"internalType\":\"uint32\",\"name\":\"vestingType\",\"type\":\"uint32\"},{\"internalType\":\"uint128\",\"name\":\"vestingCreationType\",\"type\":\"uint128\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vestingFactory\",\"outputs\":[{\"internalType\":\"contract IVestingFactory\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"vestingOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"vestingRegistries\",\"outputs\":[{\"internalType\":\"contract IVestingRegistry\",\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"vestings\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"vestingType\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"vestingCreationType\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"vestingAddress\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"vestingsOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"methods\":{\"addAdmin(address)\":{\"params\":{\"_admin\":\"The addresses of the account to grant permissions.\"}},\"addDeployedVestings(address[],uint256[])\":{\"details\":\"migration of data from previous vesting registy contracts\"},\"addFourYearVestings(address[],address[])\":{\"params\":{\"_tokenOwners\":\"array of token owners\",\"_vestingAddresses\":\"array of vesting addresses\"}},\"createTeamVesting(address,uint256,uint256,uint256,uint256)\":{\"params\":{\"_amount\":\"the amount to be staked\",\"_cliff\":\"the cliff in seconds\",\"_duration\":\"the total duration in seconds\",\"_tokenOwner\":\"the owner of the tokens\",\"_vestingCreationType\":\"the type of vesting created(e.g. Origin, Bug Bounty etc.)\"}},\"createVesting(address,uint256,uint256,uint256)\":{\"details\":\"Calls a public createVestingAddr function with vestingCreationType. This is to accomodate the existing logic for LockedSOVvestingCreationType 0 = LockedSOV\",\"params\":{\"_amount\":\"the amount to be staked\",\"_cliff\":\"the cliff in seconds\",\"_duration\":\"the total duration in seconds\",\"_tokenOwner\":\"the owner of the tokens\"}},\"createVestingAddr(address,uint256,uint256,uint256,uint256)\":{\"params\":{\"_amount\":\"the amount to be staked\",\"_cliff\":\"the cliff in seconds\",\"_duration\":\"the total duration in seconds\",\"_tokenOwner\":\"the owner of the tokens\",\"_vestingCreationType\":\"the type of vesting created(e.g. Origin, Bug Bounty etc.)\"}},\"getAdminManager()\":{\"return\":\"Address of admin manager.\"},\"getVesting(address)\":{\"details\":\"Calls a public getVestingAddr function with cliff and duration. This is to accomodate the existing logic for LockedSOVWe need to use LockedSOV.changeRegistryCliffAndDuration function very judiciouslyvestingCreationType 0 - LockedSOV\",\"params\":{\"_tokenOwner\":\"the owner of the tokens\"}},\"getVestingAddr(address,uint256,uint256,uint256)\":{\"details\":\"Important: Please use this instead of getVesting function\"},\"isOwner()\":{\"details\":\"Returns true if the caller is the current owner.\"},\"isTeamVesting(address)\":{\"details\":\"check if the specific vesting address is team vesting or notread the vestingType from vestingCreationAndTypes storage\",\"params\":{\"_vestingAddress\":\"address of vesting contract\"},\"return\":\"true for teamVesting, false for normal vesting\"},\"owner()\":{\"details\":\"Returns the address of the current owner.\"},\"registerVestingToVestingCreationAndTypes(address[],(bool,uint32,uint128)[])\":{\"details\":\"setter function to register existing vesting contract to vestingCreationAndTypes storageneed to set the function visilibty to public to support VestingCreationAndTypeDetails struct as parameter\",\"params\":{\"_vestingAddresses\":\"array of vesting address\",\"_vestingCreationAndTypes\":\"array for VestingCreationAndTypeDetails struct\"}},\"removeAdmin(address)\":{\"params\":{\"_admin\":\"The addresses of the account to revoke permissions.\"}},\"setAdminManager(address)\":{\"params\":{\"_newAdminManager\":\"The addresses of the account to grant permissions.\"}},\"setVestingFactory(address)\":{\"params\":{\"_vestingFactory\":\"the address of vesting factory contract\"}},\"stakeTokens(address,uint256)\":{\"params\":{\"_amount\":\"the amount of tokens to stake\",\"_vesting\":\"the address of Vesting contract\"}},\"transferOwnership(address)\":{\"details\":\"Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner.\"},\"transferSOV(address,uint256)\":{\"params\":{\"_amount\":\"the amount to be transferred\",\"_receiver\":\"the address of the SOV receiver\"}}}},\"userdoc\":{\"methods\":{\"addAdmin(address)\":{\"notice\":\"Add account to ACL.\"},\"addDeployedVestings(address[],uint256[])\":{\"notice\":\"adds vestings that were deployed in previous vesting registries\"},\"addFourYearVestings(address[],address[])\":{\"notice\":\"adds four year vestings to vesting registry logic\"},\"createTeamVesting(address,uint256,uint256,uint256,uint256)\":{\"notice\":\"creates Team Vesting contract\"},\"createVesting(address,uint256,uint256,uint256)\":{\"notice\":\"creates Vesting contract\"},\"createVestingAddr(address,uint256,uint256,uint256,uint256)\":{\"notice\":\"creates Vesting contract\"},\"getAdminManager()\":{\"notice\":\"Return address of the admin manager.\"},\"getTeamVesting(address,uint256,uint256,uint256)\":{\"notice\":\"returns team vesting contract address for the given token owner, cliff, duration\"},\"getVesting(address)\":{\"notice\":\"returns vesting contract address for the given token owner\"},\"getVestingAddr(address,uint256,uint256,uint256)\":{\"notice\":\"public function that returns vesting contract address for the given token owner, cliff, duration\"},\"getVestingDetails(address)\":{\"notice\":\"returns cliff and duration for Vesting & TeamVesting contracts\"},\"getVestingsOf(address)\":{\"notice\":\"returns all vesting details for the given token owner\"},\"initialize(address,address,address,address,address,address,address[])\":{\"notice\":\"Replace constructor with initialize function for Upgradable Contracts This function will be called only once by the owner\"},\"isVestingAddress(address)\":{\"notice\":\"returns if the address is a vesting address\"},\"removeAdmin(address)\":{\"notice\":\"Remove account from ACL.\"},\"removeAdminManager()\":{\"notice\":\"Set admin manager to 0 address.\"},\"setAdminManager(address)\":{\"notice\":\"Set new admin manager.\"},\"setVestingFactory(address)\":{\"notice\":\"sets vesting factory address\"},\"stakeTokens(address,uint256)\":{\"notice\":\"stakes tokens according to the vesting schedule\"},\"transferSOV(address,uint256)\":{\"notice\":\"transfers SOV tokens to given address\"}}}},\"settings\":{\"compilationTarget\":{\"contracts/governance/Vesting/VestingRegistryLogic.sol\":\"VestingRegistryLogic\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/governance/ApprovalReceiver.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\nimport \\\"./ErrorDecoder.sol\\\";\\nimport \\\"../token/IApproveAndCall.sol\\\";\\n\\n/**\\n * @title Base contract for receiving approval from SOV token.\\n */\\ncontract ApprovalReceiver is ErrorDecoder, IApproveAndCall {\\n modifier onlyThisContract() {\\n // Accepts calls only from receiveApproval function.\\n require(msg.sender == address(this), \\\"unauthorized\\\");\\n _;\\n }\\n\\n /**\\n * @notice Receives approval from SOV token.\\n * @param _data The data will be used for low level call.\\n */\\n function receiveApproval(\\n address _sender,\\n uint256 _amount,\\n address _token,\\n bytes calldata _data\\n ) external {\\n // Accepts calls only from SOV token.\\n require(msg.sender == _getToken(), \\\"unauthorized\\\");\\n require(msg.sender == _token, \\\"unauthorized\\\");\\n\\n // Only allowed methods.\\n bool isAllowed = false;\\n bytes4[] memory selectors = _getSelectors();\\n bytes4 sig = _getSig(_data);\\n for (uint256 i = 0; i < selectors.length; i++) {\\n if (sig == selectors[i]) {\\n isAllowed = true;\\n break;\\n }\\n }\\n require(isAllowed, \\\"method is not allowed\\\");\\n\\n // Check sender and amount.\\n address sender;\\n uint256 amount;\\n (, sender, amount) = abi.decode(\\n abi.encodePacked(bytes28(0), _data),\\n (bytes32, address, uint256)\\n );\\n require(sender == _sender, \\\"sender mismatch\\\");\\n require(amount == _amount, \\\"amount mismatch\\\");\\n\\n _call(_data);\\n }\\n\\n /**\\n * @notice Returns token address, only this address can be a sender for receiveApproval.\\n * @dev Should be overridden in child contracts, otherwise error will be thrown.\\n * @return By default, 0x. When overriden, the token address making the call.\\n */\\n function _getToken() internal view returns (address) {\\n return address(0);\\n }\\n\\n /**\\n * @notice Returns list of function selectors allowed to be invoked.\\n * @dev Should be overridden in child contracts, otherwise error will be thrown.\\n * @return By default, empty array. When overriden, allowed selectors.\\n */\\n function _getSelectors() internal pure returns (bytes4[] memory) {\\n return new bytes4[](0);\\n }\\n\\n /**\\n * @notice Makes call and reverts w/ enhanced error message.\\n * @param _data Error message as bytes.\\n */\\n function _call(bytes memory _data) internal {\\n (bool success, bytes memory returnData) = address(this).call(_data);\\n if (!success) {\\n if (returnData.length <= ERROR_MESSAGE_SHIFT) {\\n revert(\\\"receiveApproval: Transaction execution reverted.\\\");\\n } else {\\n revert(_addErrorMessage(\\\"receiveApproval: \\\", string(returnData)));\\n }\\n }\\n }\\n\\n /**\\n * @notice Extracts the called function selector, a hash of the signature.\\n * @dev The first four bytes of the call data for a function call specifies\\n * the function to be called. It is the first (left, high-order in big-endian)\\n * four bytes of the Keccak-256 (SHA-3) hash of the signature of the function.\\n * Solidity doesn't yet support a casting of byte[4] to bytes4.\\n * Example:\\n * msg.data:\\n * 0xcdcd77c000000000000000000000000000000000000000000000000000000000000\\n * 000450000000000000000000000000000000000000000000000000000000000000001\\n * selector (or method ID): 0xcdcd77c0\\n * signature: baz(uint32,bool)\\n * @param _data The msg.data from the low level call.\\n * @return sig First 4 bytes of msg.data i.e. the selector, hash of the signature.\\n */\\n function _getSig(bytes memory _data) internal pure returns (bytes4 sig) {\\n assembly {\\n sig := mload(add(_data, 32))\\n }\\n }\\n}\\n\",\"keccak256\":\"0xfec344456774fa83b0885dd71825ccb6780be8db63c394f3ca09107977c65429\"},\"contracts/governance/ErrorDecoder.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Base contract to properly handle returned data on failed calls\\n * @dev On EVM if the return data length of a call is less than 68,\\n * then the transaction fails silently without a revert message!\\n *\\n * As described in the Solidity documentation\\n * https://solidity.readthedocs.io/en/v0.5.17/control-structures.html#revert\\n * the revert reason is an ABI-encoded string consisting of:\\n * 0x08c379a0 // Function selector (method id) for \\\"Error(string)\\\" signature\\n * 0x0000000000000000000000000000000000000000000000000000000000000020 // Data offset\\n * 0x000000000000000000000000000000000000000000000000000000000000001a // String length\\n * 0x4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 // String data\\n *\\n * Another example, debug data from test:\\n * 0x08c379a0\\n * 0000000000000000000000000000000000000000000000000000000000000020\\n * 0000000000000000000000000000000000000000000000000000000000000034\\n * 54696d656c6f636b3a3a73657444656c61793a2044656c6179206d7573742065\\n * 7863656564206d696e696d756d2064656c61792e000000000000000000000000\\n *\\n * Parsed into:\\n * Data offset: 20\\n * Length: 34\\n * Error message:\\n * 54696d656c6f636b3a3a73657444656c61793a2044656c6179206d7573742065\\n * 7863656564206d696e696d756d2064656c61792e000000000000000000000000\\n */\\ncontract ErrorDecoder {\\n uint256 constant ERROR_MESSAGE_SHIFT = 68; // EVM silent revert error string length\\n\\n /**\\n * @notice Concats two error strings taking into account ERROR_MESSAGE_SHIFT.\\n * @param str1 First string, usually a hardcoded context written by dev.\\n * @param str2 Second string, usually the error message from the reverted call.\\n * @return The concatenated error string\\n */\\n function _addErrorMessage(string memory str1, string memory str2)\\n internal\\n pure\\n returns (string memory)\\n {\\n bytes memory bytesStr1 = bytes(str1);\\n bytes memory bytesStr2 = bytes(str2);\\n string memory str12 =\\n new string(bytesStr1.length + bytesStr2.length - ERROR_MESSAGE_SHIFT);\\n bytes memory bytesStr12 = bytes(str12);\\n uint256 j = 0;\\n for (uint256 i = 0; i < bytesStr1.length; i++) {\\n bytesStr12[j++] = bytesStr1[i];\\n }\\n for (uint256 i = ERROR_MESSAGE_SHIFT; i < bytesStr2.length; i++) {\\n bytesStr12[j++] = bytesStr2[i];\\n }\\n return string(bytesStr12);\\n }\\n}\\n\",\"keccak256\":\"0xa0fa7986924aab574ca9e7c265f8c7bf00671ba1d86dbad143df7c14455f1c6a\"},\"contracts/governance/IFeeSharingCollector.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for contract governance/FeeSharingCollector/FeeSharingCollector.sol\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n * */\\ninterface IFeeSharingCollector {\\n function withdrawFees(address[] calldata _token) external;\\n\\n function transferTokens(address _token, uint96 _amount) external;\\n\\n function withdraw(\\n address _loanPoolToken,\\n uint32 _maxCheckpoints,\\n address _receiver\\n ) external;\\n}\\n\",\"keccak256\":\"0x7794cb434d9395ea983dcf8ded48db5b68897a338429320f60172c4caa47fb40\"},\"contracts/governance/Staking/interfaces/IStaking.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\npragma experimental ABIEncoderV2;\\n\\n/**\\n * @title Interface for Staking modules governance/Staking/modules\\n */\\n\\ninterface IStaking {\\n /*************************** StakingAdminModule ***************************/\\n\\n /**\\n * @notice Add account to Admins ACL.\\n * @param _admin The addresses of the account to grant permissions.\\n * */\\n function addAdmin(address _admin) external;\\n\\n /**\\n * @notice Remove account from Admins ACL.\\n * @param _admin The addresses of the account to revoke permissions.\\n * */\\n function removeAdmin(address _admin) external;\\n\\n /**\\n * @notice Add account to pausers ACL.\\n * @param _pauser The address to grant pauser permissions.\\n * */\\n function addPauser(address _pauser) external;\\n\\n /**\\n * @notice Remove account from pausers ACL.\\n * @param _pauser The address to grant pauser permissions.\\n * */\\n function removePauser(address _pauser) external;\\n\\n /**\\n * @notice Pause/unpause contract\\n * @param _pause true when pausing, false when unpausing\\n * */\\n function pauseUnpause(bool _pause) external;\\n\\n /**\\n * @notice Freeze contract - disable all functions\\n * @param _freeze true when freezing, false when unfreezing\\n * @dev When freezing, pause is always applied too. When unfreezing, the contract is left in paused stated.\\n * */\\n function freezeUnfreeze(bool _freeze) external;\\n\\n /**\\n * @notice Allows the owner to set a fee sharing proxy contract.\\n * We need it for unstaking with slashing.\\n * @param _feeSharing The address of FeeSharingCollectorProxy contract.\\n * */\\n function setFeeSharing(address _feeSharing) external;\\n\\n /**\\n * @notice Allow the owner to set weight scaling.\\n * We need it for unstaking with slashing.\\n * @param _weightScaling The weight scaling.\\n * */\\n function setWeightScaling(uint96 _weightScaling) external;\\n\\n /**\\n * @notice Allow the owner to set a new staking contract.\\n * As a consequence it allows the stakers to migrate their positions\\n * to the new contract.\\n * @dev Doesn't have any influence as long as migrateToNewStakingContract\\n * is not implemented.\\n * @param _newStakingContract The address of the new staking contract.\\n * */\\n function setNewStakingContract(address _newStakingContract) external;\\n\\n /**\\n * @notice Allow a staker to migrate his positions to the new staking contract.\\n * @dev Staking contract needs to be set before by the owner.\\n * Currently not implemented, just needed for the interface.\\n * In case it's needed at some point in the future,\\n * the implementation needs to be changed first.\\n * */\\n function migrateToNewStakingContract() external; // dummy - not implemented as of now\\n\\n /*************************** StakingGovernanceModule ***************************/\\n\\n /**\\n * @notice Compute the total voting power at a given time.\\n * @param blockNumber The block number, needed for checkpointing.\\n * @param time The timestamp for which to calculate the total voting power.\\n * @return The total voting power at the given time.\\n * */\\n function getPriorTotalVotingPower(uint32 blockNumber, uint256 time)\\n external\\n view\\n returns (uint96);\\n\\n /**\\n * @notice Get the current votes balance for a user account.\\n * @param account The address to get votes balance.\\n * @dev This is a wrapper to simplify arguments. The actual computation is\\n * performed on WeightedStaking parent contract.\\n * @return The number of current votes for a user account.\\n * */\\n function getCurrentVotes(address account) external view returns (uint96);\\n\\n /**\\n * @notice Determine the prior number of votes for a delegatee as of a block number.\\n * Iterate through checkpoints adding up voting power.\\n * @dev Block number must be a finalized block or else this function will revert\\n * to prevent misinformation.\\n * Used for Voting, not for fee sharing.\\n * @param account The address of the account to check.\\n * @param blockNumber The block number to get the vote balance at.\\n * @param date The staking date to compute the power for.\\n * @return The number of votes the delegatee had as of the given block.\\n * */\\n function getPriorVotes(\\n address account,\\n uint256 blockNumber,\\n uint256 date\\n ) external view returns (uint96);\\n\\n /**\\n * @notice Determine the prior number of stake for an account as of a block number.\\n * @dev Block number must be a finalized block or else this function will\\n * revert to prevent misinformation.\\n * @param account The address of the account to check.\\n * @param date The staking date to compute the power for.\\n * @param blockNumber The block number to get the vote balance at.\\n * @return The number of votes the account had as of the given block.\\n * */\\n function getPriorStakeByDateForDelegatee(\\n address account,\\n uint256 date,\\n uint256 blockNumber\\n ) external view returns (uint96);\\n\\n /**\\n * @notice Determine the prior number of stake for an unlocking date as of a block number.\\n * @dev Block number must be a finalized block or else this function will\\n * revert to prevent misinformation.\\n * TODO: WeightedStaking::getPriorTotalStakesForDate should probably better\\n * be internal instead of a public function.\\n * @param date The date to check the stakes for.\\n * @param blockNumber The block number to get the vote balance at.\\n * @return The number of votes the account had as of the given block.\\n * */\\n function getPriorTotalStakesForDate(uint256 date, uint256 blockNumber)\\n external\\n view\\n returns (uint96);\\n\\n /**\\n * @notice Delegate votes from `msg.sender` which are locked until lockDate to `delegatee`.\\n * @param delegatee The address to delegate votes to.\\n * @param lockDate the date if the position to delegate.\\n * */\\n function delegate(address delegatee, uint256 lockDate) external;\\n\\n /*************************** StakingStakeModule ***************************/\\n\\n event TokensStaked(\\n address indexed staker,\\n uint256 amount,\\n uint256 lockedUntil,\\n uint256 totalStaked\\n );\\n\\n /**\\n * @notice Stake the given amount for the given duration of time.\\n * @param amount The number of tokens to stake.\\n * @param until Timestamp indicating the date until which to stake.\\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\\n * @param delegatee The address of the delegatee or 0x0 if there is none.\\n * */\\n function stake(\\n uint96 amount,\\n uint256 until,\\n address stakeFor,\\n address delegatee\\n ) external;\\n\\n /**\\n * @notice Stake the given amount for the given duration of time.\\n * @dev This function will be invoked from receiveApproval\\n * @dev SOV.approveAndCall -> this.receiveApproval -> this.stakeWithApproval\\n * @param sender The sender of SOV.approveAndCall\\n * @param amount The number of tokens to stake.\\n * @param until Timestamp indicating the date until which to stake.\\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\\n * @param delegatee The address of the delegatee or 0x0 if there is none.\\n * */\\n function stakeWithApproval(\\n address sender,\\n uint96 amount,\\n uint256 until,\\n address stakeFor,\\n address delegatee\\n ) external;\\n\\n /**\\n * @notice Receives approval from SOV token.\\n * @param _data The data will be used for low level call.\\n */\\n function receiveApproval(\\n address _sender,\\n uint256 _amount,\\n address _token,\\n bytes calldata _data\\n ) external;\\n\\n /**\\n * @notice Extend the staking duration until the specified date.\\n * @param previousLock The old unlocking timestamp.\\n * @param until The new unlocking timestamp in seconds.\\n * */\\n function extendStakingDuration(uint256 previousLock, uint256 until) external;\\n\\n /**\\n * @dev DO NOT USE this misspelled function. Use stakeBySchedule function instead.\\n * This function cannot be deprecated while we have non-upgradeable vesting contracts.\\n * */\\n function stakesBySchedule(\\n uint256 amount,\\n uint256 cliff,\\n uint256 duration,\\n uint256 intervalLength,\\n address stakeFor,\\n address delegatee\\n ) external;\\n\\n /**\\n * @notice Stake tokens according to the vesting schedule.\\n * @param amount The amount of tokens to stake.\\n * @param cliff The time interval to the first withdraw.\\n * @param duration The staking duration.\\n * @param intervalLength The length of each staking interval when cliff passed.\\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\\n * @param delegatee The address of the delegatee or 0x0 if there is none.\\n * */\\n function stakeBySchedule(\\n uint256 amount,\\n uint256 cliff,\\n uint256 duration,\\n uint256 intervalLength,\\n address stakeFor,\\n address delegatee\\n ) external;\\n\\n /**\\n * @notice Get the number of staked tokens held by the user account.\\n * @dev Iterate checkpoints adding up stakes.\\n * @param account The address of the account to get the balance of.\\n * @return The number of tokens held.\\n * */\\n function balanceOf(address account) external view returns (uint96 balance);\\n\\n /**\\n * @notice Get the current number of tokens staked for a day.\\n * @param lockedTS The timestamp to get the staked tokens for.\\n * */\\n function getCurrentStakedUntil(uint256 lockedTS) external view returns (uint96);\\n\\n /**\\n * @notice Get list of stakes for a user account.\\n * @param account The address to get stakes.\\n * @return The arrays of dates and stakes.\\n * */\\n function getStakes(address account)\\n external\\n view\\n returns (uint256[] memory dates, uint96[] memory stakes);\\n\\n /**\\n * @notice Unstaking is possible every 2 weeks only. This means, to\\n * calculate the key value for the staking checkpoints, we need to\\n * map the intended timestamp to the closest available date.\\n * @param timestamp The unlocking timestamp.\\n * @return The actual unlocking date (might be up to 2 weeks shorter than intended).\\n * */\\n function timestampToLockDate(uint256 timestamp) external view returns (uint256);\\n\\n /*************************** StakingStorageModule ***************************/\\n\\n /// @notice The maximum duration to stake tokens\\n /// @return MAX_DURATION to stake tokens\\n function getStorageMaxDurationToStakeTokens() external pure returns (uint256);\\n\\n /// @notice The maximum possible voting weight before adding +1 (actually 10, but need 9 for computation).\\n /// @return uint256(MAX_VOTING_WEIGHT);\\n function getStorageMaxVotingWeight() external pure returns (uint256);\\n\\n /// @notice weight is multiplied with this factor (for allowing decimals, like 1.2x).\\n /// @dev MAX_VOTING_WEIGHT * WEIGHT_FACTOR needs to be < 792, because there are 100,000,000 SOV with 18 decimals\\n /// @return uint256(WEIGHT_FACTOR);\\n function getStorageWeightFactor() external pure returns (uint256);\\n\\n /// @return uint256(DEFAULT_WEIGHT_SCALING);\\n function getStorageDefaultWeightScaling() external pure returns (uint256);\\n\\n /// @notice return (uint256(MIN_WEIGHT_SCALING), uint256(MAX_WEIGHT_SCALING))\\n function getStorageRangeForWeightScaling()\\n external\\n pure\\n returns (uint256 minWeightScaling, uint256 maxWeightScaling);\\n\\n /// @notice The EIP-712 typehash for the contract's domain.\\n /// @return uint256(DOMAIN_TYPEHASH);\\n function getStorageDomainTypehash() external pure returns (uint256);\\n\\n /// @notice The EIP-712 typehash for the delegation struct used by the contract.\\n /// @return uint256(DELEGATION_TYPEHASH);\\n function getStorageDelegationTypehash() external pure returns (uint256);\\n\\n /// @return name;\\n function getStorageName() external view returns (string memory);\\n\\n /// AUTOGENERATED FUNCTIONS FROM THE STAKING STORAGE PUBLIC VARIABLES ///\\n\\n /// @notice The timestamp of contract creation. Base for the staking period calculation.\\n function kickoffTS() external view returns (uint256);\\n\\n /// @notice The token to be staked\\n function SOVToken() external view returns (address);\\n\\n /// @notice Stakers delegated voting power\\n /// @param staker - the delegating address\\n /// @param until - delegated voting\\n /// @return _delegate - voting power delegated to address\\n function delegates(address staker, uint256 until) external view returns (address _delegate);\\n\\n /// @notice If this flag is set to true, all tokens are unlocked immediately\\n /// see function unlockAllTokens() for details\\n function allUnlocked() external view returns (bool);\\n\\n /// @notice Used for stake migrations to a new staking contract with a different storage structure\\n function newStakingContract() external view returns (address);\\n\\n /// CHECKPOINTS\\n struct Checkpoint {\\n uint32 fromBlock;\\n uint96 stake;\\n }\\n\\n /// @notice A record of tokens to be unstaked at a given time in total.\\n /// For total voting power computation. Voting weights get adjusted bi-weekly.\\n /// @dev totalStakingCheckpoints[date][index] is a checkpoint\\n function totalStakingCheckpoints(uint256 date, uint32 index)\\n external\\n view\\n returns (Checkpoint memory);\\n\\n /// @notice The number of total staking checkpoints for each date.\\n /// @dev numTotalStakingCheckpoints[date] is a number.\\n function numTotalStakingCheckpoints(uint256 date)\\n external\\n view\\n returns (uint32 checkpointsQty);\\n\\n /// @notice A record of tokens to be unstaked at a given time which were delegated to a certain address.\\n /// For delegatee voting power computation. Voting weights get adjusted bi-weekly.\\n /// @dev delegateStakingCheckpoints[delegatee][date][index] is a checkpoint.\\n function delegateStakingCheckpoints(\\n address delagatee,\\n uint256 date,\\n uint32 index\\n ) external view returns (Checkpoint memory);\\n\\n /// @notice The number of total staking checkpoints for each date per delegate.\\n /// @dev numDelegateStakingCheckpoints[delegatee][date] is a number.\\n function numDelegateStakingCheckpoints(address delegatee, uint256 date)\\n external\\n view\\n returns (uint32 checkpointsQty);\\n\\n /// @notice A record of tokens to be unstaked at a given time which per user address (address -> lockDate -> stake checkpoint)\\n /// @dev userStakingCheckpoints[user][date][index] is a checkpoint.\\n function userStakingCheckpoints(\\n address user,\\n uint256 date,\\n uint32 index\\n ) external view returns (Checkpoint memory);\\n\\n /// @notice The number of total staking checkpoints for each date per user.\\n /// @dev numUserStakingCheckpoints[user][date] is a number\\n function numUserStakingCheckpoints(address user, uint256 date)\\n external\\n view\\n returns (uint32 checkpointsQty);\\n\\n /// @notice A record of states for signing / validating signatures\\n /// @dev nonces[user] is a number.\\n function nonces(address user) external view returns (uint256 nonce);\\n\\n /// SLASHING ///\\n\\n /// @notice the address of FeeSharingCollectorProxy contract, we need it for unstaking with slashing.\\n function feeSharing() external view returns (address);\\n\\n /// @notice used for weight scaling when unstaking with slashing.\\n /// @return uint96 DEFAULT_WEIGHT_SCALING\\n function weightScaling() external view returns (uint96);\\n\\n /// @notice List of vesting contracts, tokens for these contracts won't be slashed if unstaked by governance.\\n /// @dev vestingWhitelist[contract] is true/false.\\n function vestingWhitelist(address isWhitelisted) external view returns (bool);\\n\\n /// @dev user => flag whether user has admin role.\\n /// @dev multisig should be an admin, admin can invoke only governanceWithdrawVesting function,\\n /// \\tthis function works only with Team Vesting contracts\\n function admins(address isAdmin) external view returns (bool);\\n\\n /// @dev vesting contract code hash => flag whether it's registered code hash\\n function vestingCodeHashes(bytes32 vestingLogicCodeHash) external view returns (bool);\\n\\n /// @notice A record of tokens to be unstaked from vesting contract at a given time (lockDate -> vest checkpoint)\\n /// @dev vestingCheckpoints[date][index] is a checkpoint.\\n function vestingCheckpoints(uint256 date, uint32 index)\\n external\\n view\\n returns (Checkpoint memory);\\n\\n /// @notice The number of total vesting checkpoints for each date.\\n /// @dev numVestingCheckpoints[date] is a number.\\n function numVestingCheckpoints(uint256 date) external view returns (uint32 checkpointsQty);\\n\\n ///@notice vesting registry contract PROXY address\\n function vestingRegistryLogic() external view returns (address);\\n\\n /// @dev user => flag whether user has pauser role.\\n function pausers(address isPauser) external view returns (bool);\\n\\n /// @dev Staking contract is paused\\n function paused() external view returns (bool);\\n\\n /// @dev Staking contract is frozen\\n function frozen() external view returns (bool);\\n\\n /*************************** StakingVestingModule ***************************/\\n\\n event VestingStakeSet(uint256 lockedTS, uint96 value);\\n\\n /**\\n * @notice Return flag whether the given address is a registered vesting contract.\\n * @param stakerAddress the address to check\\n */\\n function isVestingContract(address stakerAddress) external view returns (bool);\\n\\n /**\\n * @notice Remove vesting contract's code hash to a map of code hashes.\\n * @param vesting The address of Vesting contract.\\n * @dev We need it to use isVestingContract() function instead of isContract()\\n */\\n function removeContractCodeHash(address vesting) external;\\n\\n /**\\n * @notice Add vesting contract's code hash to a map of code hashes.\\n * @param vesting The address of Vesting contract.\\n * @dev We need it to use isVestingContract() function instead of isContract()\\n */\\n function addContractCodeHash(address vesting) external;\\n\\n /**\\n * @notice Determine the prior number of vested stake for an account until a\\n * certain lock date as of a block number.\\n * @dev Block number must be a finalized block or else this function\\n * will revert to prevent misinformation.\\n * @param date The lock date.\\n * @param blockNumber The block number to get the vote balance at.\\n * @return The number of votes the account had as of the given block.\\n * */\\n function getPriorVestingStakeByDate(uint256 date, uint256 blockNumber)\\n external\\n view\\n returns (uint96);\\n\\n /**\\n * @notice Compute the voting power for a specific date.\\n * Power = stake * weight\\n * @param date The staking date to compute the power for. Adjusted to the next valid lock date, if necessary.\\n * @param startDate The date for which we need to know the power of the stake.\\n * @param blockNumber The block number, needed for checkpointing.\\n * @return The stacking power.\\n * */\\n function weightedVestingStakeByDate(\\n uint256 date,\\n uint256 startDate,\\n uint256 blockNumber\\n ) external view returns (uint96 power);\\n\\n /**\\n * @notice Determine the prior weighted vested amount for an account as of a block number.\\n * Iterate through checkpoints adding up voting power.\\n * @dev Block number must be a finalized block or else this function will\\n * revert to prevent misinformation.\\n * Used for fee sharing, not voting.\\n * TODO: WeightedStaking::getPriorVestingWeightedStake is using the variable name \\\"votes\\\"\\n * to add up token stake, and that could be misleading.\\n *\\n * @param blockNumber The block number to get the vote balance at.\\n * @param date The staking date to compute the power for.\\n * @return The weighted stake the account had as of the given block.\\n * */\\n function getPriorVestingWeightedStake(uint256 blockNumber, uint256 date)\\n external\\n view\\n returns (uint96 votes);\\n\\n /**\\n * @notice Determine the prior number of stake for an account until a\\n * certain lock date as of a block number.\\n * @dev Block number must be a finalized block or else this function\\n * will revert to prevent misinformation.\\n * @param account The address of the account to check.\\n * @param date The lock date.\\n * @param blockNumber The block number to get the vote balance at.\\n * @return The number of votes the account had as of the given block.\\n * */\\n function getPriorUserStakeByDate(\\n address account,\\n uint256 date,\\n uint256 blockNumber\\n ) external view returns (uint96);\\n\\n /**\\n * @notice Sets the users' vesting stakes for a giving lock dates and writes checkpoints.\\n * @param lockedDates The arrays of lock dates.\\n * @param values The array of values to add to the staked balance.\\n */\\n function setVestingStakes(uint256[] calldata lockedDates, uint96[] calldata values) external;\\n\\n /**\\n * @notice sets vesting registry\\n * @param _vestingRegistryProxy the address of vesting registry proxy contract\\n * @dev _vestingRegistryProxy can be set to 0 as this function can be reused by\\n * various other functionalities without the necessity of linking it with Vesting Registry\\n */\\n function setVestingRegistry(address _vestingRegistryProxy) external;\\n\\n /*************************** StakingWithdrawModule ***************************/\\n\\n /**\\n * @notice Withdraw the given amount of tokens if they are unlocked.\\n * @param amount The number of tokens to withdraw.\\n * @param until The date until which the tokens were staked.\\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\\n * */\\n function withdraw(\\n uint96 amount,\\n uint256 until,\\n address receiver\\n ) external;\\n\\n /**\\n * @notice Withdraw the given amount of tokens.\\n * @param amount The number of tokens to withdraw.\\n * @param until The date until which the tokens were staked.\\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\\n * @dev Can be invoked only by whitelisted contract passed to governanceWithdrawVesting\\n * @dev **WARNING** This function should not be no longer used by Sovryn Protocol.\\n * Sovryn protocol will use the cancelTeamVesting function for the withdrawal moving forward.\\n * */\\n function governanceWithdraw(\\n uint96 amount,\\n uint256 until,\\n address receiver\\n ) external;\\n\\n /**\\n * @notice Withdraw tokens for vesting contract.\\n * @param vesting The address of Vesting contract.\\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\\n * @dev Can be invoked only by whitelisted contract passed to governanceWithdrawVesting.\\n * */\\n function governanceWithdrawVesting(address vesting, address receiver) external;\\n\\n /**\\n * @notice Get available and punished amount for withdrawing.\\n * @param amount The number of tokens to withdraw.\\n * @param until The date until which the tokens were staked.\\n * */\\n function getWithdrawAmounts(uint96 amount, uint256 until)\\n external\\n view\\n returns (uint96, uint96);\\n\\n /**\\n * @notice Allow the owner to unlock all tokens in case the staking contract\\n * is going to be replaced\\n * Note: Not reversible on purpose. once unlocked, everything is unlocked.\\n * The owner should not be able to just quickly unlock to withdraw his own\\n * tokens and lock again.\\n * @dev Last resort.\\n * */\\n function unlockAllTokens() external;\\n\\n /*************************** WeightedStakingModule ***************************/\\n\\n /**\\n * @notice Determine the prior weighted stake for an account as of a block number.\\n * Iterate through checkpoints adding up voting power.\\n * @dev Block number must be a finalized block or else this function will\\n * revert to prevent misinformation.\\n * Used for fee sharing, not voting.\\n *\\n * @param account The address of the account to check.\\n * @param blockNumber The block number to get the vote balance at.\\n * @param date The date/timestamp of the unstaking time.\\n * @return The weighted stake the account had as of the given block.\\n * */\\n function getPriorWeightedStake(\\n address account,\\n uint256 blockNumber,\\n uint256 date\\n ) external view returns (uint96 priorWeightedStake);\\n\\n /**\\n * @notice Compute the voting power for a specific date.\\n * Power = stake * weight\\n * TODO: WeightedStaking::weightedStakeByDate should probably better\\n * be internal instead of a public function.\\n * @param account The user address.\\n * @param date The staking date to compute the power for.\\n * @param startDate The date for which we need to know the power of the stake.\\n * @param blockNumber The block number, needed for checkpointing.\\n * @return The stacking power.\\n * */\\n function weightedStakeByDate(\\n address account,\\n uint256 date,\\n uint256 startDate,\\n uint256 blockNumber\\n ) external view returns (uint96 power);\\n\\n /**\\n * @notice Compute the weight for a specific date.\\n * @param date The unlocking date.\\n * @param startDate We compute the weight for the tokens staked until 'date' on 'startDate'.\\n * @return The weighted stake the account had as of the given block.\\n * */\\n function computeWeightByDate(uint256 date, uint256 startDate)\\n external\\n pure\\n returns (uint96 weight);\\n\\n /**\\n * @notice Returns public constant MAX_DURATION\\n * preserved for backwards compatibility\\n * Use getStorageMaxDurationToStakeTokens()\\n * @return uint96 MAX_DURATION for staking\\n **/\\n function MAX_DURATION() external view returns (uint256);\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() external view returns (address);\\n\\n /**\\n * @dev Returns true if the caller is the current owner.\\n */\\n function isOwner() external view returns (bool);\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) external;\\n\\n /**\\n * @notice Governance withdraw vesting directly through staking contract.\\n * This direct withdraw vesting solves the out of gas issue when there are too many iterations when withdrawing.\\n * This function only allows cancelling vesting contract of the TeamVesting type.\\n *\\n * @param vesting The vesting address.\\n * @param receiver The receiving address.\\n * @param startFrom The start value for the iterations.\\n */\\n function cancelTeamVesting(\\n address vesting,\\n address receiver,\\n uint256 startFrom\\n ) external;\\n\\n /**\\n * @notice Max iteration for direct withdrawal from staking to prevent out of gas issue.\\n *\\n * @return max iteration value.\\n */\\n function getMaxVestingWithdrawIterations() external view returns (uint256);\\n\\n /**\\n * @dev set max withdraw iterations.\\n *\\n * @param maxIterations new max iterations value.\\n */\\n function setMaxVestingWithdrawIterations(uint256 maxIterations) external;\\n}\\n\",\"keccak256\":\"0x720bd2cc1042cb4abc2bd3a6839131638eafd3d224571ad9ac21cae36625ec2e\"},\"contracts/governance/Vesting/ITeamVesting.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for TeamVesting contract.\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n * This interface is used by Staking contract to call governanceWithdrawTokens\\n * function having the vesting contract instance address.\\n */\\ninterface ITeamVesting {\\n function startDate() external view returns (uint256);\\n\\n function cliff() external view returns (uint256);\\n\\n function endDate() external view returns (uint256);\\n\\n function duration() external view returns (uint256);\\n\\n function tokenOwner() external view returns (address);\\n\\n function governanceWithdrawTokens(address receiver) external;\\n}\\n\",\"keccak256\":\"0xeb926db5c0681a7bdbc70c3d25df2551a6124e191654b3b8a0f810b0d03ec66f\"},\"contracts/governance/Vesting/IVesting.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for Vesting contract.\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n * This interface is used by VestingLogic contract to implement stakeTokens function\\n * and on VestingRegistry contract to call IVesting(vesting).stakeTokens function\\n * at a vesting instance.\\n */\\ninterface IVesting {\\n function duration() external returns (uint256);\\n\\n function endDate() external returns (uint256);\\n\\n function stakeTokens(uint256 amount) external;\\n\\n function tokenOwner() external view returns (address);\\n}\\n\",\"keccak256\":\"0x3482a1e27402655f85f5ff2cb06e0876e9bb94e1a63446a09e33babd60274b4b\"},\"contracts/governance/Vesting/IVestingFactory.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for Vesting Factory contract.\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n * This interface is used by VestingFactory contract to override empty\\n * implemention of deployVesting and deployTeamVesting functions\\n * and on VestingRegistry contract to use an instance of VestingFactory.\\n */\\ninterface IVestingFactory {\\n function deployVesting(\\n address _SOV,\\n address _staking,\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration,\\n address _feeSharing,\\n address _owner\\n ) external returns (address);\\n\\n function deployTeamVesting(\\n address _SOV,\\n address _staking,\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration,\\n address _feeSharing,\\n address _owner\\n ) external returns (address);\\n}\\n\",\"keccak256\":\"0xc77e276b71ec23ca6d4eead9a842bc01cc37bcfe55a88528190f1f6106773175\"},\"contracts/governance/Vesting/IVestingRegistry.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for upgradable Vesting Registry contract.\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n */\\ninterface IVestingRegistry {\\n function getVesting(address _tokenOwner) external view returns (address);\\n\\n function getTeamVesting(address _tokenOwner) external view returns (address);\\n\\n function setVestingRegistry(address _vestingRegistryProxy) external;\\n\\n function isVestingAddress(address _vestingAddress) external view returns (bool);\\n\\n function isTeamVesting(address _vestingAddress) external view returns (bool);\\n}\\n\",\"keccak256\":\"0x08bf5badf1813b59f8b06d3bb9280f4b35d3d07947c728dad79e43fcc1d4130e\"},\"contracts/governance/Vesting/VestingLogic.sol\":{\"content\":\"pragma solidity ^0.5.17;\\npragma experimental ABIEncoderV2;\\n\\nimport \\\"../../openzeppelin/Ownable.sol\\\";\\nimport \\\"../../interfaces/IERC20.sol\\\";\\nimport \\\"../Staking/interfaces/IStaking.sol\\\";\\nimport \\\"../IFeeSharingCollector.sol\\\";\\nimport \\\"./IVesting.sol\\\";\\nimport \\\"../ApprovalReceiver.sol\\\";\\nimport \\\"./VestingStorage.sol\\\";\\n\\n/**\\n * @title Vesting Logic contract.\\n * @notice Staking, delegating and withdrawal functionality.\\n * @dev Deployed by a VestingFactory contract.\\n * */\\ncontract VestingLogic is IVesting, VestingStorage, ApprovalReceiver {\\n /* Events */\\n\\n event TokensStaked(address indexed caller, uint256 amount);\\n event VotesDelegated(address indexed caller, address delegatee);\\n event TokensWithdrawn(address indexed caller, address receiver);\\n event DividendsCollected(\\n address indexed caller,\\n address loanPoolToken,\\n address receiver,\\n uint32 maxCheckpoints\\n );\\n event MigratedToNewStakingContract(address indexed caller, address newStakingContract);\\n\\n /* Modifiers */\\n\\n /**\\n * @dev Throws if called by any account other than the token owner or the contract owner.\\n */\\n modifier onlyOwners() {\\n require(msg.sender == tokenOwner || isOwner(), \\\"unauthorized\\\");\\n _;\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the token owner.\\n */\\n modifier onlyTokenOwner() {\\n require(msg.sender == tokenOwner, \\\"unauthorized\\\");\\n _;\\n }\\n\\n /* Functions */\\n\\n /**\\n * @notice Stakes tokens according to the vesting schedule.\\n * @param _amount The amount of tokens to stake.\\n * */\\n function stakeTokens(uint256 _amount) public {\\n _stakeTokens(msg.sender, _amount);\\n }\\n\\n /**\\n * @notice Stakes tokens according to the vesting schedule.\\n * @dev This function will be invoked from receiveApproval.\\n * @dev SOV.approveAndCall -> this.receiveApproval -> this.stakeTokensWithApproval\\n * @param _sender The sender of SOV.approveAndCall\\n * @param _amount The amount of tokens to stake.\\n * */\\n function stakeTokensWithApproval(address _sender, uint256 _amount) public onlyThisContract {\\n _stakeTokens(_sender, _amount);\\n }\\n\\n /**\\n * @notice Stakes tokens according to the vesting schedule. Low level function.\\n * @dev Once here the allowance of tokens is taken for granted.\\n * @param _sender The sender of tokens to stake.\\n * @param _amount The amount of tokens to stake.\\n * */\\n function _stakeTokens(address _sender, uint256 _amount) internal {\\n /// @dev Maybe better to allow staking unil the cliff was reached.\\n if (startDate == 0) {\\n startDate = staking.timestampToLockDate(block.timestamp);\\n }\\n endDate = staking.timestampToLockDate(block.timestamp + duration);\\n\\n /// @dev Transfer the tokens to this contract.\\n bool success = SOV.transferFrom(_sender, address(this), _amount);\\n require(success);\\n\\n /// @dev Allow the staking contract to access them.\\n SOV.approve(address(staking), _amount);\\n\\n staking.stakeBySchedule(_amount, cliff, duration, FOUR_WEEKS, address(this), tokenOwner);\\n\\n emit TokensStaked(_sender, _amount);\\n }\\n\\n /**\\n * @notice Delegate votes from `msg.sender` which are locked until lockDate\\n * to `delegatee`.\\n * @param _delegatee The address to delegate votes to.\\n * */\\n function delegate(address _delegatee) public onlyTokenOwner {\\n require(_delegatee != address(0), \\\"delegatee address invalid\\\");\\n\\n /// @dev Withdraw for each unlocked position.\\n /// @dev Don't change FOUR_WEEKS to TWO_WEEKS, a lot of vestings already deployed with FOUR_WEEKS\\n ///\\t\\tworkaround found, but it doesn't work with TWO_WEEKS\\n for (uint256 i = startDate + cliff; i <= endDate; i += FOUR_WEEKS) {\\n staking.delegate(_delegatee, i);\\n }\\n emit VotesDelegated(msg.sender, _delegatee);\\n }\\n\\n /**\\n * @notice Withdraws all tokens from the staking contract and\\n * forwards them to an address specified by the token owner.\\n * @param receiver The receiving address.\\n * @dev Can be called only by owner.\\n * @dev **WARNING** This function should not be no longer used by Sovryn Protocol.\\n * Sovryn protocol will use the cancelTeamVesting function for the withdrawal moving forward.\\n * */\\n function governanceWithdrawTokens(address receiver) public {\\n require(msg.sender == address(staking), \\\"unauthorized\\\");\\n\\n _withdrawTokens(receiver, true);\\n }\\n\\n /**\\n * @notice Withdraws unlocked tokens from the staking contract and\\n * forwards them to an address specified by the token owner.\\n * @param receiver The receiving address.\\n * */\\n function withdrawTokens(address receiver) public onlyOwners {\\n _withdrawTokens(receiver, false);\\n }\\n\\n /**\\n * @notice Withdraws tokens from the staking contract and forwards them\\n * to an address specified by the token owner. Low level function.\\n * @dev Once here the caller permission is taken for granted.\\n * @param receiver The receiving address.\\n * @param isGovernance Whether all tokens (true)\\n * or just unlocked tokens (false).\\n * */\\n function _withdrawTokens(address receiver, bool isGovernance) internal {\\n require(receiver != address(0), \\\"receiver address invalid\\\");\\n\\n uint96 stake;\\n\\n /// @dev Usually we just need to iterate over the possible dates until now.\\n uint256 end;\\n\\n /// @dev In the unlikely case that all tokens have been unlocked early,\\n /// allow to withdraw all of them.\\n if (staking.allUnlocked() || isGovernance) {\\n end = endDate;\\n } else {\\n end = block.timestamp;\\n }\\n\\n /// @dev Withdraw for each unlocked position.\\n /// @dev Don't change FOUR_WEEKS to TWO_WEEKS, a lot of vestings already deployed with FOUR_WEEKS\\n ///\\t\\tworkaround found, but it doesn't work with TWO_WEEKS\\n for (uint256 i = startDate + cliff; i <= end; i += FOUR_WEEKS) {\\n /// @dev Read amount to withdraw.\\n stake = staking.getPriorUserStakeByDate(address(this), i, block.number - 1);\\n\\n /// @dev Withdraw if > 0\\n if (stake > 0) {\\n if (isGovernance) {\\n staking.governanceWithdraw(stake, i, receiver);\\n } else {\\n staking.withdraw(stake, i, receiver);\\n }\\n }\\n }\\n\\n emit TokensWithdrawn(msg.sender, receiver);\\n }\\n\\n /**\\n * @notice Collect dividends from fee sharing proxy.\\n * @param _loanPoolToken The loan pool token address.\\n * @param _maxCheckpoints Maximum number of checkpoints to be processed.\\n * @param _receiver The receiver of tokens or msg.sender\\n * */\\n function collectDividends(\\n address _loanPoolToken,\\n uint32 _maxCheckpoints,\\n address _receiver\\n ) public onlyOwners {\\n require(_receiver != address(0), \\\"receiver address invalid\\\");\\n\\n /// @dev Invokes the fee sharing proxy.\\n feeSharingCollector.withdraw(_loanPoolToken, _maxCheckpoints, _receiver);\\n\\n emit DividendsCollected(msg.sender, _loanPoolToken, _receiver, _maxCheckpoints);\\n }\\n\\n /**\\n * @notice Allows the owners to migrate the positions\\n * to a new staking contract.\\n * */\\n function migrateToNewStakingContract() public onlyOwners {\\n staking.migrateToNewStakingContract();\\n staking = IStaking(staking.newStakingContract());\\n emit MigratedToNewStakingContract(msg.sender, address(staking));\\n }\\n\\n /**\\n * @notice Overrides default ApprovalReceiver._getToken function to\\n * register SOV token on this contract.\\n * @return The address of SOV token.\\n * */\\n function _getToken() internal view returns (address) {\\n return address(SOV);\\n }\\n\\n /**\\n * @notice Overrides default ApprovalReceiver._getSelectors function to\\n * register stakeTokensWithApproval selector on this contract.\\n * @return The array of registered selectors on this contract.\\n * */\\n function _getSelectors() internal pure returns (bytes4[] memory) {\\n bytes4[] memory selectors = new bytes4[](1);\\n selectors[0] = this.stakeTokensWithApproval.selector;\\n return selectors;\\n }\\n}\\n\",\"keccak256\":\"0x44622e8c7eef783fc97fb37c59262d448b05c72f02076428cbff18177ed3b3ac\"},\"contracts/governance/Vesting/VestingRegistry.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\nimport \\\"../../openzeppelin/Ownable.sol\\\";\\nimport \\\"../../interfaces/IERC20.sol\\\";\\nimport \\\"../Staking/interfaces/IStaking.sol\\\";\\nimport \\\"../IFeeSharingCollector.sol\\\";\\nimport \\\"./IVestingFactory.sol\\\";\\nimport \\\"./IVesting.sol\\\";\\nimport \\\"./ITeamVesting.sol\\\";\\nimport \\\"../../openzeppelin/SafeMath.sol\\\";\\n\\n/**\\n * @title Vesting Registry contract.\\n *\\n * @notice On January 25, 2020, Sovryn launched the Genesis Reservation system.\\n * Sovryn community members who controlled a special NFT were granted access to\\n * stake BTC or rBTC for cSOV tokens at a rate of 2500 satoshis per cSOV. Per\\n * SIP-0003, up to 2,000,000 cSOV were made available in the Genesis event,\\n * which will be redeemable on a 1:1 basis for cSOV, subject to approval by\\n * existing SOV holders.\\n *\\n * On 15 Feb 2021 Sovryn is taking another step in its journey to decentralized\\n * financial sovereignty with the vote on SIP 0005. This proposal will enable\\n * participants of the Genesis Reservation system to redeem their reserved cSOV\\n * tokens for SOV. They will also have the choice to redeem cSOV for rBTC if\\n * they decide to exit the system.\\n *\\n * This contract deals with the vesting and redemption of cSOV tokens.\\n * */\\ncontract VestingRegistry is Ownable {\\n using SafeMath for uint256;\\n\\n /* Storage */\\n\\n /// @notice Constant used for computing the vesting dates.\\n uint256 public constant FOUR_WEEKS = 4 weeks;\\n\\n uint256 public constant CSOV_VESTING_CLIFF = FOUR_WEEKS;\\n uint256 public constant CSOV_VESTING_DURATION = 10 * FOUR_WEEKS;\\n\\n IVestingFactory public vestingFactory;\\n\\n /// @notice The SOV token contract.\\n address public SOV;\\n\\n /// @notice The cSOV token contracts.\\n address[] public CSOVtokens;\\n\\n uint256 public priceSats;\\n\\n /// @notice The staking contract address.\\n address public staking;\\n\\n /// @notice Fee sharing proxy.\\n address public feeSharingCollector;\\n\\n /// @notice The vesting owner (e.g. governance timelock address).\\n address public vestingOwner;\\n\\n /// @dev TODO: Add to the documentation: address can have only one vesting of each type.\\n /// @dev user => vesting type => vesting contract.\\n mapping(address => mapping(uint256 => address)) public vestingContracts;\\n\\n /**\\n * @dev Struct can be created to save storage slots, but it doesn't make\\n * sense. We don't have a lot of blacklisted accounts or account with\\n * locked amount.\\n * */\\n\\n /// @dev user => flag whether user has already exchange cSOV or got a reimbursement.\\n mapping(address => bool) public processedList;\\n\\n /// @dev user => flag whether user shouldn't be able to exchange or reimburse.\\n mapping(address => bool) public blacklist;\\n\\n /// @dev user => amount of tokens should not be processed.\\n mapping(address => uint256) public lockedAmount;\\n\\n /// @dev user => flag whether user has admin role.\\n mapping(address => bool) public admins;\\n\\n enum VestingType {\\n TeamVesting, // MultisigVesting\\n Vesting // TokenHolderVesting\\n }\\n\\n /* Events */\\n\\n event CSOVReImburse(address from, uint256 CSOVamount, uint256 reImburseAmount);\\n event CSOVTokensExchanged(address indexed caller, uint256 amount);\\n event SOVTransferred(address indexed receiver, uint256 amount);\\n event VestingCreated(\\n address indexed tokenOwner,\\n address vesting,\\n uint256 cliff,\\n uint256 duration,\\n uint256 amount\\n );\\n event TeamVestingCreated(\\n address indexed tokenOwner,\\n address vesting,\\n uint256 cliff,\\n uint256 duration,\\n uint256 amount\\n );\\n event TokensStaked(address indexed vesting, uint256 amount);\\n event AdminAdded(address admin);\\n event AdminRemoved(address admin);\\n\\n /* Functions */\\n\\n /**\\n * @notice Contract deployment settings.\\n * @param _vestingFactory The address of vesting factory contract.\\n * @param _SOV The SOV token address.\\n * @param _CSOVtokens The array of cSOV tokens.\\n * @param _priceSats The price of cSOV tokens in satoshis.\\n * @param _staking The address of staking contract.\\n * @param _feeSharingCollector The address of fee sharing collector proxy contract.\\n * @param _vestingOwner The address of an owner of vesting contract.\\n * @dev On Sovryn the vesting owner is Exchequer Multisig.\\n * According to SIP-0007 The Exchequer Multisig is designated to hold\\n * certain funds in the form of rBTC and SOV, in order to allow for\\n * flexible deployment of such funds on:\\n * + facilitating rBTC redemptions for Genesis pre-sale participants.\\n * + deploying of SOV for the purposes of exchange listings, market\\n * making, and partnerships with third parties.\\n * */\\n constructor(\\n address _vestingFactory,\\n address _SOV,\\n address[] memory _CSOVtokens,\\n uint256 _priceSats,\\n address _staking,\\n address _feeSharingCollector,\\n address _vestingOwner\\n ) public {\\n require(_SOV != address(0), \\\"SOV address invalid\\\");\\n require(_staking != address(0), \\\"staking address invalid\\\");\\n require(_feeSharingCollector != address(0), \\\"feeSharingCollector address invalid\\\");\\n require(_vestingOwner != address(0), \\\"vestingOwner address invalid\\\");\\n\\n _setVestingFactory(_vestingFactory);\\n _setCSOVtokens(_CSOVtokens);\\n\\n SOV = _SOV;\\n priceSats = _priceSats;\\n staking = _staking;\\n feeSharingCollector = _feeSharingCollector;\\n vestingOwner = _vestingOwner;\\n }\\n\\n //---ACL------------------------------------------------------------------\\n\\n /**\\n * @dev Throws if called by any account other than the owner or admin.\\n * TODO: This ACL logic should be available on OpenZeppeling Ownable.sol\\n * or on our own overriding sovrynOwnable. This same logic is repeated\\n * on OriginInvestorsClaim.sol, TokenSender.sol and VestingRegistry2.sol\\n */\\n modifier onlyAuthorized() {\\n require(isOwner() || admins[msg.sender], \\\"unauthorized\\\");\\n _;\\n }\\n\\n /**\\n * @notice Add account to ACL.\\n * @param _admin The addresses of the account to grant permissions.\\n * */\\n function addAdmin(address _admin) public onlyOwner {\\n admins[_admin] = true;\\n emit AdminAdded(_admin);\\n }\\n\\n /**\\n * @notice Remove account from ACL.\\n * @param _admin The addresses of the account to revoke permissions.\\n * */\\n function removeAdmin(address _admin) public onlyOwner {\\n admins[_admin] = false;\\n emit AdminRemoved(_admin);\\n }\\n\\n //---PostCSOV--------------------------------------------------------------\\n\\n modifier isNotProcessed() {\\n require(!processedList[msg.sender], \\\"Address cannot be processed twice\\\");\\n _;\\n }\\n\\n modifier isNotBlacklisted() {\\n require(!blacklist[msg.sender], \\\"Address blacklisted\\\");\\n _;\\n }\\n\\n /**\\n * @notice cSOV payout to sender with rBTC currency.\\n * 1.- Check holder cSOV balance by adding up every cSOV token balance.\\n * 2.- ReImburse rBTC if funds available.\\n * 3.- And store holder address in processedList.\\n */\\n function reImburse() public isNotProcessed isNotBlacklisted {\\n uint256 CSOVAmountWei = 0;\\n for (uint256 i = 0; i < CSOVtokens.length; i++) {\\n address CSOV = CSOVtokens[i];\\n uint256 balance = IERC20(CSOV).balanceOf(msg.sender);\\n CSOVAmountWei = CSOVAmountWei.add(balance);\\n }\\n\\n require(CSOVAmountWei > lockedAmount[msg.sender], \\\"holder has no CSOV\\\");\\n CSOVAmountWei -= lockedAmount[msg.sender];\\n processedList[msg.sender] = true;\\n\\n /**\\n * @dev Found and fixed the SIP-0007 bug on VestingRegistry::reImburse formula.\\n * More details at Documenting Code issues at point 11 in\\n * https://docs.google.com/document/d/10idTD1K6JvoBmtPKGuJ2Ub_mMh6qTLLlTP693GQKMyU/\\n * Previous buggy code: uint256 reImburseAmount = (CSOVAmountWei.mul(priceSats)).div(10**10);\\n * */\\n uint256 reImburseAmount = (CSOVAmountWei.mul(priceSats)).div(10**8);\\n require(address(this).balance >= reImburseAmount, \\\"Not enough funds to reimburse\\\");\\n msg.sender.transfer(reImburseAmount);\\n\\n emit CSOVReImburse(msg.sender, CSOVAmountWei, reImburseAmount);\\n }\\n\\n /**\\n * @notice Get contract balance.\\n * @return The token balance of the contract.\\n * */\\n function budget() external view returns (uint256) {\\n uint256 SCBudget = address(this).balance;\\n return SCBudget;\\n }\\n\\n /**\\n * @notice Deposit function to receiving value (rBTC).\\n * */\\n function deposit() public payable {}\\n\\n /**\\n * @notice Send all contract balance to an account.\\n * @param to The account address to send the balance to.\\n * */\\n function withdrawAll(address payable to) public onlyOwner {\\n to.transfer(address(this).balance);\\n }\\n\\n //--------------------------------------------------------------------------------------------------------------------------------------\\n\\n /**\\n * @notice Sets vesting factory address. High level endpoint.\\n * @param _vestingFactory The address of vesting factory contract.\\n *\\n * @dev Splitting code on two functions: high level and low level\\n * is a pattern that makes easy to extend functionality in a readable way,\\n * without accidentally breaking the actual action being performed.\\n * For example, checks should be done on high level endpoint, while core\\n * functionality should be coded on the low level function.\\n * */\\n function setVestingFactory(address _vestingFactory) public onlyOwner {\\n _setVestingFactory(_vestingFactory);\\n }\\n\\n /**\\n * @notice Sets vesting factory address. Low level core function.\\n * @param _vestingFactory The address of vesting factory contract.\\n * */\\n function _setVestingFactory(address _vestingFactory) internal {\\n require(_vestingFactory != address(0), \\\"vestingFactory address invalid\\\");\\n vestingFactory = IVestingFactory(_vestingFactory);\\n }\\n\\n /**\\n * @notice Sets cSOV tokens array. High level endpoint.\\n * @param _CSOVtokens The array of cSOV tokens.\\n * */\\n function setCSOVtokens(address[] memory _CSOVtokens) public onlyOwner {\\n _setCSOVtokens(_CSOVtokens);\\n }\\n\\n /**\\n * @notice Sets cSOV tokens array by looping through input. Low level function.\\n * @param _CSOVtokens The array of cSOV tokens.\\n * */\\n function _setCSOVtokens(address[] memory _CSOVtokens) internal {\\n for (uint256 i = 0; i < _CSOVtokens.length; i++) {\\n require(_CSOVtokens[i] != address(0), \\\"CSOV address invalid\\\");\\n }\\n CSOVtokens = _CSOVtokens;\\n }\\n\\n /**\\n * @notice Set blacklist flag (true/false).\\n * @param _account The address to be blacklisted.\\n * @param _blacklisted The flag to add/remove to/from a blacklist.\\n * */\\n function setBlacklistFlag(address _account, bool _blacklisted) public onlyOwner {\\n require(_account != address(0), \\\"account address invalid\\\");\\n\\n blacklist[_account] = _blacklisted;\\n }\\n\\n /**\\n * @notice Set amount to be subtracted from user token balance.\\n * @param _account The address with locked amount.\\n * @param _amount The amount to be locked.\\n * */\\n function setLockedAmount(address _account, uint256 _amount) public onlyOwner {\\n require(_account != address(0), \\\"account address invalid\\\");\\n require(_amount != 0, \\\"amount invalid\\\");\\n\\n lockedAmount[_account] = _amount;\\n }\\n\\n /**\\n * @notice Transfer SOV tokens to given address.\\n *\\n * @dev This is a wrapper for ERC-20 transfer function w/\\n * additional checks and triggering an event.\\n *\\n * @param _receiver The address of the SOV receiver.\\n * @param _amount The amount to be transferred.\\n * */\\n function transferSOV(address _receiver, uint256 _amount) public onlyOwner {\\n require(_receiver != address(0), \\\"receiver address invalid\\\");\\n require(_amount != 0, \\\"amount invalid\\\");\\n\\n IERC20(SOV).transfer(_receiver, _amount);\\n emit SOVTransferred(_receiver, _amount);\\n }\\n\\n /**\\n * @notice Exchange cSOV to SOV with 1:1 rate\\n */\\n function exchangeAllCSOV() public isNotProcessed isNotBlacklisted {\\n processedList[msg.sender] = true;\\n\\n uint256 amount = 0;\\n for (uint256 i = 0; i < CSOVtokens.length; i++) {\\n address CSOV = CSOVtokens[i];\\n uint256 balance = IERC20(CSOV).balanceOf(msg.sender);\\n amount += balance;\\n }\\n\\n require(amount > lockedAmount[msg.sender], \\\"amount invalid\\\");\\n amount -= lockedAmount[msg.sender];\\n\\n _createVestingForCSOV(amount);\\n }\\n\\n /**\\n * @notice cSOV tokens are moved and staked on Vesting contract.\\n * @param _amount The amount of tokens to be vested.\\n * */\\n function _createVestingForCSOV(uint256 _amount) internal {\\n address vesting =\\n _getOrCreateVesting(msg.sender, CSOV_VESTING_CLIFF, CSOV_VESTING_DURATION);\\n\\n IERC20(SOV).approve(vesting, _amount);\\n IVesting(vesting).stakeTokens(_amount);\\n\\n emit CSOVTokensExchanged(msg.sender, _amount);\\n }\\n\\n /**\\n * @notice Check a token address is among the cSOV token addresses.\\n * @param _CSOV The cSOV token address.\\n * */\\n function _validateCSOV(address _CSOV) internal view {\\n bool isValid = false;\\n for (uint256 i = 0; i < CSOVtokens.length; i++) {\\n if (_CSOV == CSOVtokens[i]) {\\n isValid = true;\\n break;\\n }\\n }\\n require(isValid, \\\"wrong CSOV address\\\");\\n }\\n\\n /**\\n * @notice Create Vesting contract.\\n * @param _tokenOwner The owner of the tokens.\\n * @param _amount The amount to be staked.\\n * @param _cliff The time interval to the first withdraw in seconds.\\n * @param _duration The total duration in seconds.\\n * */\\n function createVesting(\\n address _tokenOwner,\\n uint256 _amount,\\n uint256 _cliff,\\n uint256 _duration\\n ) public onlyAuthorized {\\n address vesting = _getOrCreateVesting(_tokenOwner, _cliff, _duration);\\n emit VestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount);\\n }\\n\\n /**\\n * @notice Create Team Vesting contract.\\n * @param _tokenOwner The owner of the tokens.\\n * @param _amount The amount to be staked.\\n * @param _cliff The time interval to the first withdraw in seconds.\\n * @param _duration The total duration in seconds.\\n * */\\n function createTeamVesting(\\n address _tokenOwner,\\n uint256 _amount,\\n uint256 _cliff,\\n uint256 _duration\\n ) public onlyAuthorized {\\n address vesting = _getOrCreateTeamVesting(_tokenOwner, _cliff, _duration);\\n emit TeamVestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount);\\n }\\n\\n /**\\n * @notice Stake tokens according to the vesting schedule.\\n * @param _vesting The address of Vesting contract.\\n * @param _amount The amount of tokens to stake.\\n * */\\n function stakeTokens(address _vesting, uint256 _amount) public onlyAuthorized {\\n require(_vesting != address(0), \\\"vesting address invalid\\\");\\n require(_amount > 0, \\\"amount invalid\\\");\\n\\n IERC20(SOV).approve(_vesting, _amount);\\n IVesting(_vesting).stakeTokens(_amount);\\n emit TokensStaked(_vesting, _amount);\\n }\\n\\n /**\\n * @notice Query the vesting contract for an account.\\n * @param _tokenOwner The owner of the tokens.\\n * @return The vesting contract address for the given token owner.\\n * */\\n function getVesting(address _tokenOwner) public view returns (address) {\\n return vestingContracts[_tokenOwner][uint256(VestingType.Vesting)];\\n }\\n\\n /**\\n * @notice Query the team vesting contract for an account.\\n * @param _tokenOwner The owner of the tokens.\\n * @return The team vesting contract address for the given token owner.\\n * */\\n function getTeamVesting(address _tokenOwner) public view returns (address) {\\n return vestingContracts[_tokenOwner][uint256(VestingType.TeamVesting)];\\n }\\n\\n /**\\n * @notice If not exists, deploy a vesting contract through factory.\\n * @param _tokenOwner The owner of the tokens.\\n * @param _cliff The time interval to the first withdraw in seconds.\\n * @param _duration The total duration in seconds.\\n * @return The vesting contract address for the given token owner\\n * whether it existed previously or not.\\n * */\\n function _getOrCreateVesting(\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration\\n ) internal returns (address) {\\n uint256 type_ = uint256(VestingType.Vesting);\\n if (vestingContracts[_tokenOwner][type_] == address(0)) {\\n /// @dev TODO: Owner of OwnerVesting contracts - the same address as tokenOwner.\\n address vesting =\\n vestingFactory.deployVesting(\\n SOV,\\n staking,\\n _tokenOwner,\\n _cliff,\\n _duration,\\n feeSharingCollector,\\n _tokenOwner\\n );\\n vestingContracts[_tokenOwner][type_] = vesting;\\n }\\n return vestingContracts[_tokenOwner][type_];\\n }\\n\\n /**\\n * @notice If not exists, deploy a team vesting contract through factory.\\n * @param _tokenOwner The owner of the tokens.\\n * @param _cliff The time interval to the first withdraw in seconds.\\n * @param _duration The total duration in seconds.\\n * @return The team vesting contract address for the given token owner\\n * whether it existed previously or not.\\n * */\\n function _getOrCreateTeamVesting(\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration\\n ) internal returns (address) {\\n uint256 type_ = uint256(VestingType.TeamVesting);\\n if (vestingContracts[_tokenOwner][type_] == address(0)) {\\n address vesting =\\n vestingFactory.deployTeamVesting(\\n SOV,\\n staking,\\n _tokenOwner,\\n _cliff,\\n _duration,\\n feeSharingCollector,\\n vestingOwner\\n );\\n vestingContracts[_tokenOwner][type_] = vesting;\\n }\\n return vestingContracts[_tokenOwner][type_];\\n }\\n}\\n\",\"keccak256\":\"0xd691de2f5a675394f19be5264b659a646b4bc45752a59055484aa80f25c08a57\"},\"contracts/governance/Vesting/VestingRegistryLogic.sol\":{\"content\":\"pragma solidity ^0.5.17;\\npragma experimental ABIEncoderV2;\\n\\nimport \\\"../../interfaces/IERC20.sol\\\";\\nimport \\\"../IFeeSharingCollector.sol\\\";\\nimport \\\"./IVesting.sol\\\";\\nimport \\\"./ITeamVesting.sol\\\";\\nimport \\\"./VestingRegistryStorage.sol\\\";\\n\\ncontract VestingRegistryLogic is VestingRegistryStorage {\\n event SOVTransferred(address indexed receiver, uint256 amount);\\n event VestingCreated(\\n address indexed tokenOwner,\\n address vesting,\\n uint256 cliff,\\n uint256 duration,\\n uint256 amount,\\n uint256 vestingCreationType\\n );\\n event TeamVestingCreated(\\n address indexed tokenOwner,\\n address vesting,\\n uint256 cliff,\\n uint256 duration,\\n uint256 amount,\\n uint256 vestingCreationType\\n );\\n event TokensStaked(address indexed vesting, uint256 amount);\\n event VestingCreationAndTypesSet(\\n address indexed vesting,\\n VestingCreationAndTypeDetails vestingCreationAndType\\n );\\n\\n /**\\n * @notice Replace constructor with initialize function for Upgradable Contracts\\n * This function will be called only once by the owner\\n * */\\n function initialize(\\n address _vestingFactory,\\n address _SOV,\\n address _staking,\\n address _feeSharingCollector,\\n address _vestingOwner,\\n address _lockedSOV,\\n address[] calldata _vestingRegistries\\n ) external onlyOwner initializer {\\n require(_SOV != address(0), \\\"SOV address invalid\\\");\\n require(_staking != address(0), \\\"staking address invalid\\\");\\n require(_feeSharingCollector != address(0), \\\"feeSharingCollector address invalid\\\");\\n require(_vestingOwner != address(0), \\\"vestingOwner address invalid\\\");\\n require(_lockedSOV != address(0), \\\"LockedSOV address invalid\\\");\\n\\n _setVestingFactory(_vestingFactory);\\n SOV = _SOV;\\n staking = _staking;\\n feeSharingCollector = _feeSharingCollector;\\n vestingOwner = _vestingOwner;\\n lockedSOV = LockedSOV(_lockedSOV);\\n for (uint256 i = 0; i < _vestingRegistries.length; i++) {\\n require(_vestingRegistries[i] != address(0), \\\"Vesting registry address invalid\\\");\\n vestingRegistries.push(IVestingRegistry(_vestingRegistries[i]));\\n }\\n }\\n\\n /**\\n * @notice sets vesting factory address\\n * @param _vestingFactory the address of vesting factory contract\\n */\\n function setVestingFactory(address _vestingFactory) external onlyOwner {\\n _setVestingFactory(_vestingFactory);\\n }\\n\\n /**\\n * @notice Internal function that sets vesting factory address\\n * @param _vestingFactory the address of vesting factory contract\\n */\\n function _setVestingFactory(address _vestingFactory) internal {\\n require(_vestingFactory != address(0), \\\"vestingFactory address invalid\\\");\\n vestingFactory = IVestingFactory(_vestingFactory);\\n }\\n\\n /**\\n * @notice transfers SOV tokens to given address\\n * @param _receiver the address of the SOV receiver\\n * @param _amount the amount to be transferred\\n */\\n function transferSOV(address _receiver, uint256 _amount) external onlyOwner {\\n require(_receiver != address(0), \\\"receiver address invalid\\\");\\n require(_amount != 0, \\\"amount invalid\\\");\\n require(IERC20(SOV).transfer(_receiver, _amount), \\\"transfer failed\\\");\\n emit SOVTransferred(_receiver, _amount);\\n }\\n\\n /**\\n * @notice adds vestings that were deployed in previous vesting registries\\n * @dev migration of data from previous vesting registy contracts\\n */\\n function addDeployedVestings(\\n address[] calldata _tokenOwners,\\n uint256[] calldata _vestingCreationTypes\\n ) external onlyAuthorized {\\n for (uint256 i = 0; i < _tokenOwners.length; i++) {\\n require(_tokenOwners[i] != address(0), \\\"token owner cannot be 0 address\\\");\\n require(_vestingCreationTypes[i] > 0, \\\"vesting creation type must be greater than 0\\\");\\n _addDeployedVestings(_tokenOwners[i], _vestingCreationTypes[i]);\\n }\\n }\\n\\n /**\\n * @notice adds four year vestings to vesting registry logic\\n * @param _tokenOwners array of token owners\\n * @param _vestingAddresses array of vesting addresses\\n */\\n function addFourYearVestings(\\n address[] calldata _tokenOwners,\\n address[] calldata _vestingAddresses\\n ) external onlyAuthorized {\\n require(_tokenOwners.length == _vestingAddresses.length, \\\"arrays mismatch\\\");\\n uint256 vestingCreationType = 4;\\n uint256 cliff = 4 weeks;\\n uint256 duration = 156 weeks;\\n for (uint256 i = 0; i < _tokenOwners.length; i++) {\\n require(!isVesting[_vestingAddresses[i]], \\\"vesting exists\\\");\\n require(_tokenOwners[i] != address(0), \\\"token owner cannot be 0 address\\\");\\n require(_vestingAddresses[i] != address(0), \\\"vesting cannot be 0 address\\\");\\n uint256 uid =\\n uint256(\\n keccak256(\\n abi.encodePacked(\\n _tokenOwners[i],\\n uint256(VestingType.Vesting),\\n cliff,\\n duration,\\n vestingCreationType\\n )\\n )\\n );\\n vestings[uid] = Vesting(\\n uint256(VestingType.Vesting),\\n vestingCreationType,\\n _vestingAddresses[i]\\n );\\n vestingsOf[_tokenOwners[i]].push(uid);\\n isVesting[_vestingAddresses[i]] = true;\\n }\\n }\\n\\n /**\\n * @notice creates Vesting contract\\n * @param _tokenOwner the owner of the tokens\\n * @param _amount the amount to be staked\\n * @param _cliff the cliff in seconds\\n * @param _duration the total duration in seconds\\n * @dev Calls a public createVestingAddr function with vestingCreationType. This is to accomodate the existing logic for LockedSOV\\n * @dev vestingCreationType 0 = LockedSOV\\n */\\n function createVesting(\\n address _tokenOwner,\\n uint256 _amount,\\n uint256 _cliff,\\n uint256 _duration\\n ) external onlyAuthorized {\\n createVestingAddr(_tokenOwner, _amount, _cliff, _duration, 3);\\n }\\n\\n /**\\n * @notice creates Vesting contract\\n * @param _tokenOwner the owner of the tokens\\n * @param _amount the amount to be staked\\n * @param _cliff the cliff in seconds\\n * @param _duration the total duration in seconds\\n * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.)\\n */\\n function createVestingAddr(\\n address _tokenOwner,\\n uint256 _amount,\\n uint256 _cliff,\\n uint256 _duration,\\n uint256 _vestingCreationType\\n ) public onlyAuthorized {\\n address vesting =\\n _getOrCreateVesting(\\n _tokenOwner,\\n _cliff,\\n _duration,\\n uint256(VestingType.Vesting),\\n _vestingCreationType\\n );\\n\\n emit VestingCreated(\\n _tokenOwner,\\n vesting,\\n _cliff,\\n _duration,\\n _amount,\\n _vestingCreationType\\n );\\n }\\n\\n /**\\n * @notice creates Team Vesting contract\\n * @param _tokenOwner the owner of the tokens\\n * @param _amount the amount to be staked\\n * @param _cliff the cliff in seconds\\n * @param _duration the total duration in seconds\\n * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.)\\n */\\n function createTeamVesting(\\n address _tokenOwner,\\n uint256 _amount,\\n uint256 _cliff,\\n uint256 _duration,\\n uint256 _vestingCreationType\\n ) external onlyAuthorized {\\n address vesting =\\n _getOrCreateVesting(\\n _tokenOwner,\\n _cliff,\\n _duration,\\n uint256(VestingType.TeamVesting),\\n _vestingCreationType\\n );\\n\\n emit TeamVestingCreated(\\n _tokenOwner,\\n vesting,\\n _cliff,\\n _duration,\\n _amount,\\n _vestingCreationType\\n );\\n }\\n\\n /**\\n * @notice stakes tokens according to the vesting schedule\\n * @param _vesting the address of Vesting contract\\n * @param _amount the amount of tokens to stake\\n */\\n function stakeTokens(address _vesting, uint256 _amount) external onlyAuthorized {\\n require(_vesting != address(0), \\\"vesting address invalid\\\");\\n require(_amount > 0, \\\"amount invalid\\\");\\n\\n IERC20(SOV).approve(_vesting, _amount);\\n IVesting(_vesting).stakeTokens(_amount);\\n emit TokensStaked(_vesting, _amount);\\n }\\n\\n /**\\n * @notice returns vesting contract address for the given token owner\\n * @param _tokenOwner the owner of the tokens\\n * @dev Calls a public getVestingAddr function with cliff and duration. This is to accomodate the existing logic for LockedSOV\\n * @dev We need to use LockedSOV.changeRegistryCliffAndDuration function very judiciously\\n * @dev vestingCreationType 0 - LockedSOV\\n */\\n function getVesting(address _tokenOwner) public view returns (address) {\\n return getVestingAddr(_tokenOwner, lockedSOV.cliff(), lockedSOV.duration(), 3);\\n }\\n\\n /**\\n * @notice public function that returns vesting contract address for the given token owner, cliff, duration\\n * @dev Important: Please use this instead of getVesting function\\n */\\n function getVestingAddr(\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration,\\n uint256 _vestingCreationType\\n ) public view returns (address) {\\n uint256 type_ = uint256(VestingType.Vesting);\\n uint256 uid =\\n uint256(\\n keccak256(\\n abi.encodePacked(_tokenOwner, type_, _cliff, _duration, _vestingCreationType)\\n )\\n );\\n return vestings[uid].vestingAddress;\\n }\\n\\n /**\\n * @notice returns team vesting contract address for the given token owner, cliff, duration\\n */\\n function getTeamVesting(\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration,\\n uint256 _vestingCreationType\\n ) public view returns (address) {\\n uint256 type_ = uint256(VestingType.TeamVesting);\\n uint256 uid =\\n uint256(\\n keccak256(\\n abi.encodePacked(_tokenOwner, type_, _cliff, _duration, _vestingCreationType)\\n )\\n );\\n return vestings[uid].vestingAddress;\\n }\\n\\n /**\\n * @dev check if the specific vesting address is team vesting or not\\n * @dev read the vestingType from vestingCreationAndTypes storage\\n *\\n * @param _vestingAddress address of vesting contract\\n *\\n * @return true for teamVesting, false for normal vesting\\n */\\n function isTeamVesting(address _vestingAddress) external view returns (bool) {\\n return (vestingCreationAndTypes[_vestingAddress].isSet &&\\n vestingCreationAndTypes[_vestingAddress].vestingType ==\\n uint32(VestingType.TeamVesting));\\n }\\n\\n /**\\n * @dev setter function to register existing vesting contract to vestingCreationAndTypes storage\\n * @dev need to set the function visilibty to public to support VestingCreationAndTypeDetails struct as parameter\\n *\\n * @param _vestingAddresses array of vesting address\\n * @param _vestingCreationAndTypes array for VestingCreationAndTypeDetails struct\\n */\\n function registerVestingToVestingCreationAndTypes(\\n address[] memory _vestingAddresses,\\n VestingCreationAndTypeDetails[] memory _vestingCreationAndTypes\\n ) public onlyAuthorized {\\n require(_vestingAddresses.length == _vestingCreationAndTypes.length, \\\"Unmatched length\\\");\\n for (uint256 i = 0; i < _vestingCreationAndTypes.length; i++) {\\n VestingCreationAndTypeDetails memory _vestingCreationAndType =\\n _vestingCreationAndTypes[i];\\n address _vestingAddress = _vestingAddresses[i];\\n\\n vestingCreationAndTypes[_vestingAddress] = _vestingCreationAndType;\\n\\n emit VestingCreationAndTypesSet(\\n _vestingAddress,\\n vestingCreationAndTypes[_vestingAddress]\\n );\\n }\\n }\\n\\n /**\\n * @notice Internal function to deploy Vesting/Team Vesting contract\\n * @param _tokenOwner the owner of the tokens\\n * @param _cliff the cliff in seconds\\n * @param _duration the total duration in seconds\\n * @param _type the type of vesting\\n * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.)\\n */\\n function _getOrCreateVesting(\\n address _tokenOwner,\\n uint256 _cliff,\\n uint256 _duration,\\n uint256 _type,\\n uint256 _vestingCreationType\\n ) internal returns (address) {\\n address vesting;\\n uint256 uid =\\n uint256(\\n keccak256(\\n abi.encodePacked(_tokenOwner, _type, _cliff, _duration, _vestingCreationType)\\n )\\n );\\n if (vestings[uid].vestingAddress == address(0)) {\\n if (_type == 1) {\\n vesting = vestingFactory.deployVesting(\\n SOV,\\n staking,\\n _tokenOwner,\\n _cliff,\\n _duration,\\n feeSharingCollector,\\n _tokenOwner\\n );\\n } else {\\n vesting = vestingFactory.deployTeamVesting(\\n SOV,\\n staking,\\n _tokenOwner,\\n _cliff,\\n _duration,\\n feeSharingCollector,\\n vestingOwner\\n );\\n }\\n vestings[uid] = Vesting(_type, _vestingCreationType, vesting);\\n vestingsOf[_tokenOwner].push(uid);\\n isVesting[vesting] = true;\\n\\n vestingCreationAndTypes[vesting] = VestingCreationAndTypeDetails({\\n isSet: true,\\n vestingType: uint32(_type),\\n vestingCreationType: uint128(_vestingCreationType)\\n });\\n\\n emit VestingCreationAndTypesSet(vesting, vestingCreationAndTypes[vesting]);\\n }\\n return vestings[uid].vestingAddress;\\n }\\n\\n /**\\n * @notice stores the addresses of Vesting contracts from all three previous versions of Vesting Registry\\n */\\n function _addDeployedVestings(address _tokenOwner, uint256 _vestingCreationType) internal {\\n uint256 uid;\\n uint256 i = _vestingCreationType - 1;\\n\\n address vestingAddress = vestingRegistries[i].getVesting(_tokenOwner);\\n if (vestingAddress != address(0)) {\\n VestingLogic vesting = VestingLogic(vestingAddress);\\n uid = uint256(\\n keccak256(\\n abi.encodePacked(\\n _tokenOwner,\\n uint256(VestingType.Vesting),\\n vesting.cliff(),\\n vesting.duration(),\\n _vestingCreationType\\n )\\n )\\n );\\n vestings[uid] = Vesting(\\n uint256(VestingType.Vesting),\\n _vestingCreationType,\\n vestingAddress\\n );\\n vestingsOf[_tokenOwner].push(uid);\\n isVesting[vestingAddress] = true;\\n }\\n\\n address teamVestingAddress = vestingRegistries[i].getTeamVesting(_tokenOwner);\\n if (teamVestingAddress != address(0)) {\\n VestingLogic vesting = VestingLogic(teamVestingAddress);\\n uid = uint256(\\n keccak256(\\n abi.encodePacked(\\n _tokenOwner,\\n uint256(VestingType.TeamVesting),\\n vesting.cliff(),\\n vesting.duration(),\\n _vestingCreationType\\n )\\n )\\n );\\n vestings[uid] = Vesting(\\n uint256(VestingType.TeamVesting),\\n _vestingCreationType,\\n teamVestingAddress\\n );\\n vestingsOf[_tokenOwner].push(uid);\\n isVesting[teamVestingAddress] = true;\\n }\\n }\\n\\n /**\\n * @notice returns all vesting details for the given token owner\\n */\\n function getVestingsOf(address _tokenOwner) external view returns (Vesting[] memory) {\\n uint256[] memory vestingIds = vestingsOf[_tokenOwner];\\n uint256 length = vestingIds.length;\\n Vesting[] memory _vestings = new Vesting[](vestingIds.length);\\n for (uint256 i = 0; i < length; i++) {\\n _vestings[i] = vestings[vestingIds[i]];\\n }\\n return _vestings;\\n }\\n\\n /**\\n * @notice returns cliff and duration for Vesting & TeamVesting contracts\\n */\\n function getVestingDetails(address _vestingAddress)\\n external\\n view\\n returns (uint256 cliff, uint256 duration)\\n {\\n VestingLogic vesting = VestingLogic(_vestingAddress);\\n return (vesting.cliff(), vesting.duration());\\n }\\n\\n /**\\n * @notice returns if the address is a vesting address\\n */\\n function isVestingAddress(address _vestingAddress) external view returns (bool isVestingAddr) {\\n return isVesting[_vestingAddress];\\n }\\n}\\n\",\"keccak256\":\"0xe5c27d62f4a228d602d1e9142d76540e701d34640b8908a8b1ce19caabb5be85\"},\"contracts/governance/Vesting/VestingRegistryStorage.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\nimport \\\"../../openzeppelin/Initializable.sol\\\";\\nimport \\\"../../utils/AdminRoleManaged.sol\\\";\\nimport \\\"../../interfaces/IERC20.sol\\\";\\nimport \\\"./IVestingFactory.sol\\\";\\nimport \\\"../../locked/LockedSOV.sol\\\";\\nimport \\\"./IVestingRegistry.sol\\\";\\n\\n/**\\n * @title Vesting Registry Storage Contract.\\n *\\n * @notice This contract is just the storage required for vesting registry.\\n * It is parent of VestingRegistryProxy and VestingRegistryLogic.\\n *\\n * @dev Use Ownable as a parent to align storage structure for Logic and Proxy contracts.\\n * */\\n\\ncontract VestingRegistryStorage is Initializable, AdminRoleManaged {\\n ///@notice the vesting factory contract\\n IVestingFactory public vestingFactory;\\n\\n ///@notice the Locked SOV contract\\n ///@dev NOTES: No need to update lockedSOV in this contract, since it might break the vestingRegistry if the new lockedSOV does not have the same value of cliff & duration.\\n ILockedSOV public lockedSOV;\\n\\n ///@notice the list of vesting registries\\n IVestingRegistry[] public vestingRegistries;\\n\\n ///@notice the SOV token contract\\n address public SOV;\\n\\n ///@notice the staking contract address\\n address public staking;\\n\\n ///@notice fee sharing proxy\\n address public feeSharingCollector;\\n\\n ///@notice the vesting owner (e.g. governance timelock address)\\n address public vestingOwner;\\n\\n enum VestingType {\\n TeamVesting, //MultisigVesting\\n Vesting //TokenHolderVesting\\n }\\n\\n ///@notice Vesting details\\n struct Vesting {\\n uint256 vestingType;\\n uint256 vestingCreationType;\\n address vestingAddress;\\n }\\n\\n ///@notice A record of vesting details for a unique id\\n ///@dev vestings[uid] returns vesting data\\n mapping(uint256 => Vesting) public vestings;\\n\\n ///@notice A record of all unique ids for a particular token owner\\n ///@dev vestingsOf[tokenOwner] returns array of unique ids\\n mapping(address => uint256[]) public vestingsOf;\\n\\n ///@notice A record of all vesting addresses\\n ///@dev isVesting[address] returns if the address is a vesting address\\n mapping(address => bool) public isVesting;\\n\\n /// @notice Store vesting creation type & vesting type information\\n /// @dev it is packed into 1 single storage slot for cheaper gas usage\\n struct VestingCreationAndTypeDetails {\\n bool isSet;\\n uint32 vestingType;\\n uint128 vestingCreationType;\\n }\\n\\n ///@notice A record of all vesting addresses with the detail\\n ///@dev vestingDetail[vestingAddress] returns Vesting struct data\\n ///@dev can be used to easily check the vesting type / creation type based on the vesting address itself\\n mapping(address => VestingCreationAndTypeDetails) public vestingCreationAndTypes;\\n}\\n\",\"keccak256\":\"0x5499b056e9229b7608b37e93ec8a96b590f00025d00973de9ba2665fd36caca5\"},\"contracts/governance/Vesting/VestingStorage.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\nimport \\\"../../openzeppelin/Ownable.sol\\\";\\nimport \\\"../../interfaces/IERC20.sol\\\";\\nimport \\\"../Staking/interfaces/IStaking.sol\\\";\\nimport \\\"../IFeeSharingCollector.sol\\\";\\n\\n/**\\n * @title Vesting Storage Contract.\\n *\\n * @notice This contract is just the storage required for vesting.\\n * It is parent of VestingLogic and TeamVesting.\\n *\\n * @dev Use Ownable as a parent to align storage structure for Logic and Proxy contracts.\\n * */\\ncontract VestingStorage is Ownable {\\n /// @notice The SOV token contract.\\n IERC20 public SOV;\\n\\n /// @notice The staking contract address.\\n IStaking public staking;\\n\\n /// @notice The owner of the vested tokens.\\n address public tokenOwner;\\n\\n /// @notice Fee sharing Proxy.\\n IFeeSharingCollector public feeSharingCollector;\\n\\n /// @notice The cliff. After this time period the tokens begin to unlock.\\n uint256 public cliff;\\n\\n /// @notice The duration. After this period all tokens will have been unlocked.\\n uint256 public duration;\\n\\n /// @notice The start date of the vesting.\\n uint256 public startDate;\\n\\n /// @notice The end date of the vesting.\\n uint256 public endDate;\\n\\n /// @notice Constant used for computing the vesting dates.\\n uint256 constant FOUR_WEEKS = 4 weeks;\\n}\\n\",\"keccak256\":\"0xf70f579d357d8f0aa0839824c1a1d66713c3cd42a58118d2893a35b52baaa140\"},\"contracts/interfaces/IERC20.sol\":{\"content\":\"/**\\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\\n * Licensed under the Apache License, Version 2.0.\\n */\\n\\npragma solidity >=0.5.0 <0.6.0;\\n\\ncontract IERC20 {\\n string public name;\\n uint8 public decimals;\\n string public symbol;\\n\\n function totalSupply() external view returns (uint256);\\n\\n function balanceOf(address _who) external view returns (uint256);\\n\\n function allowance(address _owner, address _spender) external view returns (uint256);\\n\\n function approve(address _spender, uint256 _value) external returns (bool);\\n\\n function transfer(address _to, uint256 _value) external returns (bool);\\n\\n function transferFrom(\\n address _from,\\n address _to,\\n uint256 _value\\n ) external returns (bool);\\n\\n event Transfer(address indexed from, address indexed to, uint256 value);\\n event Approval(address indexed owner, address indexed spender, uint256 value);\\n}\\n\",\"keccak256\":\"0xbc0c9bb48f19651930ec9aff366b2e11a1abf89c846e4b2d52d8102b15ce6721\"},\"contracts/locked/ILockedSOV.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title The Locked SOV Interface.\\n * @author Franklin Richards - powerhousefrank@protonmail.com\\n * @notice This interface is an incomplete yet useful for future migration of LockedSOV Contract.\\n * @dev Only use it if you know what you are doing.\\n */\\ninterface ILockedSOV {\\n /**\\n * @notice Adds SOV to the user balance (Locked and Unlocked Balance based on `_basisPoint`).\\n * @param _userAddress The user whose locked balance has to be updated with `_sovAmount`.\\n * @param _sovAmount The amount of SOV to be added to the locked and/or unlocked balance.\\n * @param _basisPoint The % (in Basis Point)which determines how much will be unlocked immediately.\\n */\\n function deposit(\\n address _userAddress,\\n uint256 _sovAmount,\\n uint256 _basisPoint\\n ) external;\\n\\n /**\\n * @notice Adds SOV to the locked balance of a user.\\n * @param _userAddress The user whose locked balance has to be updated with _sovAmount.\\n * @param _sovAmount The amount of SOV to be added to the locked balance.\\n */\\n function depositSOV(address _userAddress, uint256 _sovAmount) external;\\n\\n /**\\n * @notice Withdraws unlocked tokens and Stakes Locked tokens for a user who already have a vesting created.\\n * @param _userAddress The address of user tokens will be withdrawn.\\n */\\n function withdrawAndStakeTokensFrom(address _userAddress) external;\\n\\n function cliff() external view returns (uint256);\\n\\n function duration() external view returns (uint256);\\n\\n function getLockedBalance(address _addr) external view returns (uint256 _balance);\\n\\n function getUnlockedBalance(address _addr) external view returns (uint256 _balance);\\n}\\n\",\"keccak256\":\"0x2ecf9d6c3704b4210b86d15a35650b34a1c44d0fb57795668fd2b2b4eab6104f\"},\"contracts/locked/LockedSOV.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\nimport \\\"../openzeppelin/SafeMath.sol\\\";\\nimport \\\"../interfaces/IERC20.sol\\\";\\nimport \\\"../governance/Vesting/VestingRegistry.sol\\\";\\nimport \\\"../governance/Vesting/VestingLogic.sol\\\";\\nimport \\\"./ILockedSOV.sol\\\";\\n\\n/**\\n * @title The Locked SOV Contract.\\n * @author Franklin Richards - powerhousefrank@protonmail.com\\n * @notice This contract is used to receive reward from other contracts, Create Vesting and Stake Tokens.\\n */\\ncontract LockedSOV is ILockedSOV {\\n using SafeMath for uint256;\\n\\n uint256 public constant MAX_BASIS_POINT = 10000;\\n uint256 public constant MAX_DURATION = 37;\\n\\n /* Storage */\\n\\n /// @notice True if the migration to a new Locked SOV Contract has started.\\n bool public migration;\\n\\n /// @notice The cliff is the time period after which the tokens begin to unlock.\\n uint256 public cliff;\\n /// @notice The duration is the time period after all tokens will have been unlocked.\\n uint256 public duration;\\n\\n /// @notice The SOV token contract.\\n IERC20 public SOV;\\n /// @notice The Vesting registry contract.\\n VestingRegistry public vestingRegistry;\\n /// @notice The New (Future) Locked SOV.\\n ILockedSOV public newLockedSOV;\\n\\n /// @notice The locked user balances.\\n mapping(address => uint256) private lockedBalances;\\n /// @notice The unlocked user balances.\\n mapping(address => uint256) private unlockedBalances;\\n /// @notice The contracts/wallets with admin power.\\n mapping(address => bool) private isAdmin;\\n\\n /* Events */\\n\\n /// @notice Emitted when a new Admin is added to the admin list.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _newAdmin The address of the new admin.\\n event AdminAdded(address indexed _initiator, address indexed _newAdmin);\\n\\n /// @notice Emitted when an admin is removed from the admin list.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _removedAdmin The address of the removed admin.\\n event AdminRemoved(address indexed _initiator, address indexed _removedAdmin);\\n\\n /// @notice Emitted when Vesting Registry, Duration and/or Cliff is updated.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _vestingRegistry The Vesting Registry Contract.\\n /// @param _cliff The time period after which the tokens begin to unlock.\\n /// @param _duration The time period after all tokens will have been unlocked.\\n event RegistryCliffAndDurationUpdated(\\n address indexed _initiator,\\n address indexed _vestingRegistry,\\n uint256 _cliff,\\n uint256 _duration\\n );\\n\\n /// @notice Emitted when a new deposit is made.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _userAddress The user to whose un/locked balance a new deposit was made.\\n /// @param _sovAmount The amount of SOV to be added to the un/locked balance.\\n /// @param _basisPoint The % (in Basis Point) which determines how much will be unlocked immediately.\\n event Deposited(\\n address indexed _initiator,\\n address indexed _userAddress,\\n uint256 _sovAmount,\\n uint256 _basisPoint\\n );\\n\\n /// @notice Emitted when a user withdraws the fund.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _userAddress The user whose unlocked balance has to be withdrawn.\\n /// @param _sovAmount The amount of SOV withdrawn from the unlocked balance.\\n event Withdrawn(address indexed _initiator, address indexed _userAddress, uint256 _sovAmount);\\n\\n /// @notice Emitted when a user creates a vesting for himself.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _userAddress The user whose unlocked balance has to be withdrawn.\\n /// @param _vesting The Vesting Contract.\\n event VestingCreated(\\n address indexed _initiator,\\n address indexed _userAddress,\\n address indexed _vesting\\n );\\n\\n /// @notice Emitted when a user stakes tokens.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _vesting The Vesting Contract.\\n /// @param _amount The amount of locked tokens staked by the user.\\n event TokenStaked(address indexed _initiator, address indexed _vesting, uint256 _amount);\\n\\n /// @notice Emitted when an admin initiates a migration to new Locked SOV Contract.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _newLockedSOV The address of the new Locked SOV Contract.\\n event MigrationStarted(address indexed _initiator, address indexed _newLockedSOV);\\n\\n /// @notice Emitted when a user initiates the transfer to a new Locked SOV Contract.\\n /// @param _initiator The address which initiated this event to be emitted.\\n /// @param _amount The amount of locked tokens to transfer from this contract to the new one.\\n event UserTransfered(address indexed _initiator, uint256 _amount);\\n\\n /* Modifiers */\\n\\n modifier onlyAdmin {\\n require(isAdmin[msg.sender], \\\"Only admin can call this.\\\");\\n _;\\n }\\n\\n modifier migrationAllowed {\\n require(migration, \\\"Migration has not yet started.\\\");\\n _;\\n }\\n\\n /* Constructor */\\n\\n /**\\n * @notice Setup the required parameters.\\n * @param _SOV The SOV Token Address.\\n * @param _vestingRegistry The Vesting Registry Address.\\n * @param _cliff The time period after which the tokens begin to unlock.\\n * @param _duration The time period after all tokens will have been unlocked.\\n * @param _admins The list of Admins to be added.\\n */\\n constructor(\\n address _SOV,\\n address _vestingRegistry,\\n uint256 _cliff,\\n uint256 _duration,\\n address[] memory _admins\\n ) public {\\n require(_SOV != address(0), \\\"Invalid SOV Address.\\\");\\n require(_vestingRegistry != address(0), \\\"Vesting registry address is invalid.\\\");\\n require(_duration < MAX_DURATION, \\\"Duration is too long.\\\");\\n\\n SOV = IERC20(_SOV);\\n vestingRegistry = VestingRegistry(_vestingRegistry);\\n cliff = _cliff * 4 weeks;\\n duration = _duration * 4 weeks;\\n\\n for (uint256 index = 0; index < _admins.length; index++) {\\n isAdmin[_admins[index]] = true;\\n }\\n }\\n\\n /* Public or External Functions */\\n\\n /**\\n * @notice The function to add a new admin.\\n * @param _newAdmin The address of the new admin.\\n * @dev Only callable by an Admin.\\n */\\n function addAdmin(address _newAdmin) public onlyAdmin {\\n require(_newAdmin != address(0), \\\"Invalid Address.\\\");\\n require(!isAdmin[_newAdmin], \\\"Address is already admin.\\\");\\n isAdmin[_newAdmin] = true;\\n\\n emit AdminAdded(msg.sender, _newAdmin);\\n }\\n\\n /**\\n * @notice The function to remove an admin.\\n * @param _adminToRemove The address of the admin which should be removed.\\n * @dev Only callable by an Admin.\\n */\\n function removeAdmin(address _adminToRemove) public onlyAdmin {\\n require(isAdmin[_adminToRemove], \\\"Address is not an admin.\\\");\\n isAdmin[_adminToRemove] = false;\\n\\n emit AdminRemoved(msg.sender, _adminToRemove);\\n }\\n\\n /**\\n * @notice The function to update the Vesting Registry, Duration and Cliff.\\n * @param _vestingRegistry The Vesting Registry Address.\\n * @param _cliff The time period after which the tokens begin to unlock.\\n * @param _duration The time period after all tokens will have been unlocked.\\n * @dev IMPORTANT 1: You have to change Vesting Registry if you want to change Duration and/or Cliff.\\n * IMPORTANT 2: `_cliff` and `_duration` is multiplied by 4 weeks in this function.\\n */\\n function changeRegistryCliffAndDuration(\\n address _vestingRegistry,\\n uint256 _cliff,\\n uint256 _duration\\n ) external onlyAdmin {\\n require(\\n address(vestingRegistry) != _vestingRegistry,\\n \\\"Vesting Registry has to be different for changing duration and cliff.\\\"\\n );\\n /// If duration is also zero, then it is similar to Unlocked SOV.\\n require(_duration != 0, \\\"Duration cannot be zero.\\\");\\n require(_duration < MAX_DURATION, \\\"Duration is too long.\\\");\\n\\n vestingRegistry = VestingRegistry(_vestingRegistry);\\n\\n cliff = _cliff * 4 weeks;\\n duration = _duration * 4 weeks;\\n\\n emit RegistryCliffAndDurationUpdated(msg.sender, _vestingRegistry, _cliff, _duration);\\n }\\n\\n /**\\n * @notice Adds SOV to the user balance (Locked and Unlocked Balance based on `_basisPoint`).\\n * @param _userAddress The user whose locked balance has to be updated with `_sovAmount`.\\n * @param _sovAmount The amount of SOV to be added to the locked and/or unlocked balance.\\n * @param _basisPoint The % (in Basis Point)which determines how much will be unlocked immediately.\\n */\\n function deposit(\\n address _userAddress,\\n uint256 _sovAmount,\\n uint256 _basisPoint\\n ) external {\\n _deposit(_userAddress, _sovAmount, _basisPoint);\\n }\\n\\n /**\\n * @notice Adds SOV to the locked balance of a user.\\n * @param _userAddress The user whose locked balance has to be updated with _sovAmount.\\n * @param _sovAmount The amount of SOV to be added to the locked balance.\\n * @dev This is here because there are dependency with other contracts.\\n */\\n function depositSOV(address _userAddress, uint256 _sovAmount) external {\\n _deposit(_userAddress, _sovAmount, 0);\\n }\\n\\n function _deposit(\\n address _userAddress,\\n uint256 _sovAmount,\\n uint256 _basisPoint\\n ) private {\\n // MAX_BASIS_POINT is not included because if 100% is unlocked, then LockedSOV is not required to be used.\\n require(_basisPoint < MAX_BASIS_POINT, \\\"Basis Point has to be less than 10000.\\\");\\n bool txStatus = SOV.transferFrom(msg.sender, address(this), _sovAmount);\\n require(txStatus, \\\"Token transfer was not successful. Check receiver address.\\\");\\n\\n uint256 unlockedBal = _sovAmount.mul(_basisPoint).div(MAX_BASIS_POINT);\\n\\n unlockedBalances[_userAddress] = unlockedBalances[_userAddress].add(unlockedBal);\\n lockedBalances[_userAddress] = lockedBalances[_userAddress].add(_sovAmount).sub(\\n unlockedBal\\n );\\n\\n emit Deposited(msg.sender, _userAddress, _sovAmount, _basisPoint);\\n }\\n\\n /**\\n * @notice A function to withdraw the unlocked balance.\\n * @param _receiverAddress If specified, the unlocked balance will go to this address, else to msg.sender.\\n */\\n function withdraw(address _receiverAddress) public {\\n _withdraw(msg.sender, _receiverAddress);\\n }\\n\\n function _withdraw(address _sender, address _receiverAddress) private {\\n address userAddr = _receiverAddress;\\n if (_receiverAddress == address(0)) {\\n userAddr = _sender;\\n }\\n\\n uint256 amount = unlockedBalances[_sender];\\n unlockedBalances[_sender] = 0;\\n\\n bool txStatus = SOV.transfer(userAddr, amount);\\n require(txStatus, \\\"Token transfer was not successful. Check receiver address.\\\");\\n\\n emit Withdrawn(_sender, userAddr, amount);\\n }\\n\\n /**\\n * @notice Creates vesting if not already created and Stakes tokens for a user.\\n * @dev Only use this function if the `duration` is small.\\n */\\n function createVestingAndStake() public {\\n _createVestingAndStake(msg.sender);\\n }\\n\\n function _createVestingAndStake(address _sender) private {\\n address vestingAddr = _getVesting(_sender);\\n\\n if (vestingAddr == address(0)) {\\n vestingAddr = _createVesting(_sender);\\n }\\n\\n _stakeTokens(_sender, vestingAddr);\\n }\\n\\n /**\\n * @notice Creates vesting contract (if it hasn't been created yet) for the calling user.\\n * @return _vestingAddress The New Vesting Contract Created.\\n */\\n function createVesting() public returns (address _vestingAddress) {\\n _vestingAddress = _createVesting(msg.sender);\\n }\\n\\n /**\\n * @notice Stakes tokens for a user who already have a vesting created.\\n * @dev The user should already have a vesting created, else this function will throw error.\\n */\\n function stakeTokens() public {\\n VestingLogic vesting = VestingLogic(_getVesting(msg.sender));\\n\\n require(\\n cliff == vesting.cliff() && duration == vesting.duration(),\\n \\\"Wrong Vesting Schedule.\\\"\\n );\\n\\n _stakeTokens(msg.sender, address(vesting));\\n }\\n\\n /**\\n * @notice Withdraws unlocked tokens and Stakes Locked tokens for a user who already have a vesting created.\\n * @param _receiverAddress If specified, the unlocked balance will go to this address, else to msg.sender.\\n */\\n function withdrawAndStakeTokens(address _receiverAddress) external {\\n _withdraw(msg.sender, _receiverAddress);\\n _createVestingAndStake(msg.sender);\\n }\\n\\n /**\\n * @notice Withdraws unlocked tokens and Stakes Locked tokens for a user who already have a vesting created.\\n * @param _userAddress The address of user tokens will be withdrawn.\\n */\\n function withdrawAndStakeTokensFrom(address _userAddress) external {\\n _withdraw(_userAddress, _userAddress);\\n _createVestingAndStake(_userAddress);\\n }\\n\\n /**\\n * @notice Function to start the process of migration to new contract.\\n * @param _newLockedSOV The new locked sov contract address.\\n */\\n function startMigration(address _newLockedSOV) external onlyAdmin {\\n require(_newLockedSOV != address(0), \\\"New Locked SOV Address is Invalid.\\\");\\n newLockedSOV = ILockedSOV(_newLockedSOV);\\n SOV.approve(_newLockedSOV, SOV.balanceOf(address(this)));\\n migration = true;\\n\\n emit MigrationStarted(msg.sender, _newLockedSOV);\\n }\\n\\n /**\\n * @notice Function to transfer the locked balance from this contract to new LockedSOV Contract.\\n * @dev Address is not specified to discourage selling lockedSOV to other address.\\n */\\n function transfer() external migrationAllowed {\\n uint256 amount = lockedBalances[msg.sender];\\n lockedBalances[msg.sender] = 0;\\n\\n newLockedSOV.depositSOV(msg.sender, amount);\\n\\n emit UserTransfered(msg.sender, amount);\\n }\\n\\n /* Internal Functions */\\n\\n /**\\n * @notice Creates a Vesting Contract for a user.\\n * @param _tokenOwner The owner of the vesting contract.\\n * @return _vestingAddress The Vesting Contract Address.\\n * @dev Does not do anything if Vesting Contract was already created.\\n */\\n function _createVesting(address _tokenOwner) internal returns (address _vestingAddress) {\\n /// Here zero is given in place of amount, as amount is not really used in `vestingRegistry.createVesting()`.\\n vestingRegistry.createVesting(_tokenOwner, 0, cliff, duration);\\n _vestingAddress = _getVesting(_tokenOwner);\\n emit VestingCreated(msg.sender, _tokenOwner, _vestingAddress);\\n }\\n\\n /**\\n * @notice Returns the Vesting Contract Address.\\n * @param _tokenOwner The owner of the vesting contract.\\n * @return _vestingAddress The Vesting Contract Address.\\n */\\n function _getVesting(address _tokenOwner) internal view returns (address _vestingAddress) {\\n return vestingRegistry.getVesting(_tokenOwner);\\n }\\n\\n /**\\n * @notice Stakes the tokens in a particular vesting contract.\\n * @param _vesting The Vesting Contract Address.\\n */\\n function _stakeTokens(address _sender, address _vesting) internal {\\n uint256 amount = lockedBalances[_sender];\\n lockedBalances[_sender] = 0;\\n\\n require(SOV.approve(_vesting, amount), \\\"Approve failed.\\\");\\n VestingLogic(_vesting).stakeTokens(amount);\\n\\n emit TokenStaked(_sender, _vesting, amount);\\n }\\n\\n /* Getter or Read Functions */\\n\\n /**\\n * @notice The function to get the locked balance of a user.\\n * @param _addr The address of the user to check the locked balance.\\n * @return _balance The locked balance of the address `_addr`.\\n */\\n function getLockedBalance(address _addr) external view returns (uint256 _balance) {\\n return lockedBalances[_addr];\\n }\\n\\n /**\\n * @notice The function to get the unlocked balance of a user.\\n * @param _addr The address of the user to check the unlocked balance.\\n * @return _balance The unlocked balance of the address `_addr`.\\n */\\n function getUnlockedBalance(address _addr) external view returns (uint256 _balance) {\\n return unlockedBalances[_addr];\\n }\\n\\n /**\\n * @notice The function to check is an address is admin or not.\\n * @param _addr The address of the user to check the admin status.\\n * @return _status True if admin, False otherwise.\\n */\\n function adminStatus(address _addr) external view returns (bool _status) {\\n return isAdmin[_addr];\\n }\\n}\\n\",\"keccak256\":\"0xadc3b976d89a409f04644071a9f40c57d5e96a0b13d364f1aa4324e648607999\"},\"contracts/openzeppelin/Context.sol\":{\"content\":\"pragma solidity >=0.5.0 <0.6.0;\\n\\n/*\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with GSN meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\ncontract Context {\\n // Empty internal constructor, to prevent people from mistakenly deploying\\n // an instance of this contract, which should be used via inheritance.\\n constructor() internal {}\\n\\n // solhint-disable-previous-line no-empty-blocks\\n\\n function _msgSender() internal view returns (address payable) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view returns (bytes memory) {\\n this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691\\n return msg.data;\\n }\\n}\\n\",\"keccak256\":\"0x7860cb1591dbd66bb497c60c46866d9fcdb56c73306ed86b25801000af1c7b2b\"},\"contracts/openzeppelin/Initializable.sol\":{\"content\":\"pragma solidity >=0.5.0 <0.6.0;\\n\\n/**\\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\\n * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an\\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\\n *\\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\\n *\\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\\n */\\ncontract Initializable {\\n /**\\n * @dev Indicates that the contract has been initialized.\\n */\\n bool private _initialized;\\n\\n /**\\n * @dev Indicates that the contract is in the process of being initialized.\\n */\\n bool private _initializing;\\n\\n /**\\n * @dev Modifier to protect an initializer function from being invoked twice.\\n */\\n modifier initializer() {\\n require(_initializing || !_initialized, \\\"Initializable: contract is already initialized\\\");\\n\\n bool isTopLevelCall = !_initializing;\\n if (isTopLevelCall) {\\n _initializing = true;\\n _initialized = true;\\n }\\n\\n _;\\n\\n if (isTopLevelCall) {\\n _initializing = false;\\n }\\n }\\n}\\n\",\"keccak256\":\"0xa5064e2730ae1037f4efc83654676174484b827890d30ee618048004a04d6b51\"},\"contracts/openzeppelin/Ownable.sol\":{\"content\":\"pragma solidity >=0.5.0 <0.6.0;\\n\\nimport \\\"./Context.sol\\\";\\n\\n/**\\n * @dev Contract module which provides a basic access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * This module is used through inheritance. It will make available the modifier\\n * `onlyOwner`, which can be applied to your functions to restrict their use to\\n * the owner.\\n */\\ncontract Ownable is Context {\\n address private _owner;\\n\\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\\n\\n /**\\n * @dev Initializes the contract setting the deployer as the initial owner.\\n */\\n constructor() internal {\\n address msgSender = _msgSender();\\n _owner = msgSender;\\n emit OwnershipTransferred(address(0), msgSender);\\n }\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() public view returns (address) {\\n return _owner;\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the owner.\\n */\\n modifier onlyOwner() {\\n require(isOwner(), \\\"unauthorized\\\");\\n _;\\n }\\n\\n /**\\n * @dev Returns true if the caller is the current owner.\\n */\\n function isOwner() public view returns (bool) {\\n return _msgSender() == _owner;\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public onlyOwner {\\n _transferOwnership(newOwner);\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n */\\n function _transferOwnership(address newOwner) internal {\\n require(newOwner != address(0), \\\"Ownable: new owner is the zero address\\\");\\n emit OwnershipTransferred(_owner, newOwner);\\n _owner = newOwner;\\n }\\n}\\n\",\"keccak256\":\"0x94496c375b3e82d87d7f01ce1577f008fab374312cf93012a0eca716e6aadb3a\"},\"contracts/openzeppelin/SafeMath.sol\":{\"content\":\"pragma solidity >=0.5.0 <0.6.0;\\n\\n/**\\n * @dev Wrappers over Solidity's arithmetic operations with added overflow\\n * checks.\\n *\\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\\n * in bugs, because programmers usually assume that an overflow raises an\\n * error, which is the standard behavior in high level programming languages.\\n * `SafeMath` restores this intuition by reverting the transaction when an\\n * operation overflows.\\n *\\n * Using this library instead of the unchecked operations eliminates an entire\\n * class of bugs, so it's recommended to use it always.\\n */\\nlibrary SafeMath {\\n /**\\n * @dev Returns the addition of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `+` operator.\\n *\\n * Requirements:\\n * - Addition cannot overflow.\\n */\\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\\n uint256 c = a + b;\\n require(c >= a, \\\"SafeMath: addition overflow\\\");\\n\\n return c;\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting on\\n * overflow (when the result is negative).\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n * - Subtraction cannot overflow.\\n */\\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\\n return sub(a, b, \\\"SafeMath: subtraction overflow\\\");\\n }\\n\\n /**\\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\\n * overflow (when the result is negative).\\n *\\n * Counterpart to Solidity's `-` operator.\\n *\\n * Requirements:\\n * - Subtraction cannot overflow.\\n *\\n * _Available since v2.4.0._\\n */\\n function sub(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n require(b <= a, errorMessage);\\n uint256 c = a - b;\\n\\n return c;\\n }\\n\\n /**\\n * @dev Returns the multiplication of two unsigned integers, reverting on\\n * overflow.\\n *\\n * Counterpart to Solidity's `*` operator.\\n *\\n * Requirements:\\n * - Multiplication cannot overflow.\\n */\\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\\n // benefit is lost if 'b' is also tested.\\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\\n if (a == 0) {\\n return 0;\\n }\\n\\n uint256 c = a * b;\\n require(c / a == b, \\\"SafeMath: multiplication overflow\\\");\\n\\n return c;\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers. Reverts on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator. Note: this function uses a\\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\\n * uses an invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n * - The divisor cannot be zero.\\n */\\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\\n return div(a, b, \\\"SafeMath: division by zero\\\");\\n }\\n\\n /**\\n * @dev Returns the integer division of two unsigned integers. Reverts with custom message on\\n * division by zero. The result is rounded towards zero.\\n *\\n * Counterpart to Solidity's `/` operator. Note: this function uses a\\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\\n * uses an invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n * - The divisor cannot be zero.\\n *\\n * _Available since v2.4.0._\\n */\\n function div(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n // Solidity only automatically asserts when dividing by 0\\n require(b != 0, errorMessage);\\n uint256 c = a / b;\\n // assert(a == b * c + a % b); // There is no case in which this doesn't hold\\n\\n return c;\\n }\\n\\n /**\\n * @dev Integer division of two numbers, rounding up and truncating the quotient\\n */\\n function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {\\n return divCeil(a, b, \\\"SafeMath: division by zero\\\");\\n }\\n\\n /**\\n * @dev Integer division of two numbers, rounding up and truncating the quotient\\n */\\n function divCeil(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n // Solidity only automatically asserts when dividing by 0\\n require(b != 0, errorMessage);\\n\\n if (a == 0) {\\n return 0;\\n }\\n uint256 c = ((a - 1) / b) + 1;\\n\\n return c;\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * Reverts when dividing by zero.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n * - The divisor cannot be zero.\\n */\\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\\n return mod(a, b, \\\"SafeMath: modulo by zero\\\");\\n }\\n\\n /**\\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\\n * Reverts with custom message when dividing by zero.\\n *\\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\\n * opcode (which leaves remaining gas untouched) while Solidity uses an\\n * invalid opcode to revert (consuming all remaining gas).\\n *\\n * Requirements:\\n * - The divisor cannot be zero.\\n *\\n * _Available since v2.4.0._\\n */\\n function mod(\\n uint256 a,\\n uint256 b,\\n string memory errorMessage\\n ) internal pure returns (uint256) {\\n require(b != 0, errorMessage);\\n return a % b;\\n }\\n\\n function min256(uint256 _a, uint256 _b) internal pure returns (uint256) {\\n return _a < _b ? _a : _b;\\n }\\n}\\n\",\"keccak256\":\"0xbff8d6273e1a6870d1a142c0c23acd63a4dd47760f250390f49ee56333bcb6e8\"},\"contracts/token/IApproveAndCall.sol\":{\"content\":\"pragma solidity ^0.5.17;\\n\\n/**\\n * @title Interface for contract governance/ApprovalReceiver.sol\\n * @dev Interfaces are used to cast a contract address into a callable instance.\\n */\\ninterface IApproveAndCall {\\n /**\\n * @notice Receives approval from SOV token.\\n * @param _sender The sender of SOV.approveAndCall function.\\n * @param _amount The amount was approved.\\n * @param _token The address of token.\\n * @param _data The data will be used for low level call.\\n * */\\n function receiveApproval(\\n address _sender,\\n uint256 _amount,\\n address _token,\\n bytes calldata _data\\n ) external;\\n}\\n\",\"keccak256\":\"0x0ca93f8436a4d81d80de5ea9214139b490d96f708f09c975a0869ce9abc61635\"},\"contracts/utils/AdminManagerRole.sol\":{\"content\":\"pragma solidity 0.5.17;\\n\\nimport \\\"../openzeppelin/Ownable.sol\\\";\\n\\ncontract AdminManagerRole is Ownable {\\n /// @dev user => flag whether user has adminManager role.\\n bytes32 private constant KEY_ADMIN_MANAGER_ROLE = keccak256(\\\"key.admin.manager.role\\\");\\n\\n event AdminManagerChanged(\\n address indexed sender,\\n address indexed oldAdminManager,\\n address indexed newAdminManager\\n );\\n event AdminManagerRemoved(address indexed sender, address indexed removedAdminManager);\\n\\n /**\\n * @dev Throws if called by any account other than the owner or adminManager.\\n * or on our own overriding sovrynOwnable.\\n */\\n modifier onlyOwnerOrAdminManager() {\\n require(isOwner() || msg.sender == getAdminManager(), \\\"unauthorized\\\");\\n _;\\n }\\n\\n /**\\n * @notice Set new admin manager.\\n * @param _newAdminManager The addresses of the account to grant permissions.\\n * */\\n function setAdminManager(address _newAdminManager) public onlyOwner {\\n require(_newAdminManager != address(0), \\\"invalid admin manager\\\");\\n emit AdminManagerChanged(msg.sender, getAdminManager(), _newAdminManager);\\n\\n bytes32 key = KEY_ADMIN_MANAGER_ROLE;\\n assembly {\\n sstore(key, _newAdminManager)\\n }\\n }\\n\\n /**\\n * @notice Set admin manager to 0 address.\\n * */\\n function removeAdminManager() public onlyOwner {\\n require(getAdminManager() != address(0), \\\"Admin manager is not set\\\");\\n emit AdminManagerRemoved(msg.sender, getAdminManager());\\n address _newAdminManager = address(0);\\n bytes32 key = KEY_ADMIN_MANAGER_ROLE;\\n assembly {\\n sstore(key, _newAdminManager)\\n }\\n }\\n\\n /**\\n * @notice Return address of the admin manager.\\n * @return Address of admin manager.\\n * */\\n function getAdminManager() public view returns (address _adminManager) {\\n bytes32 key = KEY_ADMIN_MANAGER_ROLE;\\n assembly {\\n _adminManager := sload(key)\\n }\\n }\\n}\\n\",\"keccak256\":\"0x24af34df0602395198ac167e02d42e36b929e4f3575cec86e9ad88d4edecae9d\"},\"contracts/utils/AdminRoleManaged.sol\":{\"content\":\"pragma solidity 0.5.17;\\n\\nimport \\\"../openzeppelin/Ownable.sol\\\";\\nimport \\\"./AdminManagerRole.sol\\\";\\n\\ncontract AdminRoleManaged is Ownable, AdminManagerRole {\\n /// @dev user => flag whether user has admin role.\\n mapping(address => bool) public admins;\\n\\n event AdminAdded(address admin);\\n event AdminRemoved(address admin);\\n\\n /**\\n * @dev Throws if called by any account other than the owner or admin.\\n * or on our own overriding sovrynOwnable.\\n */\\n modifier onlyAuthorized() {\\n require(isOwner() || admins[msg.sender], \\\"unauthorized\\\");\\n _;\\n }\\n\\n /**\\n * @notice Add account to ACL.\\n * @param _admin The addresses of the account to grant permissions.\\n * */\\n function addAdmin(address _admin) public onlyOwnerOrAdminManager {\\n admins[_admin] = true;\\n emit AdminAdded(_admin);\\n }\\n\\n /**\\n * @notice Remove account from ACL.\\n * @param _admin The addresses of the account to revoke permissions.\\n * */\\n function removeAdmin(address _admin) public onlyOwnerOrAdminManager {\\n admins[_admin] = false;\\n emit AdminRemoved(_admin);\\n }\\n}\\n\",\"keccak256\":\"0xd46d8388e348295ec2a9c6a417dd22eaadda880aaa7bb90673582c108b0c188d\"}},\"version\":1}", + "bytecode": "0x608060405260006100176001600160e01b0361007016565b6000805462010000600160b01b031916620100006001600160a01b038416908102919091178255604051929350917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350610074565b3390565b613352806100836000396000f3fe608060405234801561001057600080fd5b50600436106102275760003560e01c80638dedf00911610130578063c36519d1116100b8578063efb957331161007c578063efb957331461049f578063f2f46b3b146104bf578063f2fde38b146104c7578063f421ed7e146104da578063f60826ee146104ed57610227565b8063c36519d114610433578063c680c0b714610446578063ca210d8c14610459578063cc49ede71461046c578063dfb9366d1461047f57610227565b8063b810c648116100ff578063b810c648146103df578063bc160758146103f2578063bc84f9ca14610405578063bd7b590814610418578063c0e098521461042057610227565b80638dedf009146103a65780638f32d59b146103c75780639af05157146103cf578063b29d2514146103d757610227565b80636b7dbb2d116101b357806380fcb9921161018257806380fcb99214610327578063821bee7314610349578063842a49d51461036b578063862e229d1461038b5780638da5cb5b1461039e57610227565b80636b7dbb2d146102e657806370480275146102ee578063786931da1461030157806379a83f5a1461031457610227565b80631f509326116101fa5780631f50932614610285578063377220fd14610298578063429b62e5146102ab57806342a82b4f146102cb5780634cf088d9146102de57610227565b806302df04761461022c5780630665a06f1461025557806308dcb3601461026a5780631785f53c14610272575b600080fd5b61023f61023a36600461264e565b6104f5565b60405161024c9190612ef0565b60405180910390f35b61026861026336600461264e565b610556565b005b61023f6105b1565b610268610280366004612520565b6105c0565b6102686102933660046126af565b610662565b6102686102a6366004612520565b610704565b6102be6102b9366004612520565b610734565b60405161024c9190612fe5565b6102686102d936600461255c565b610749565b61023f6109b8565b61023f6109c7565b6102686102fc366004612520565b6109d6565b61026861030f366004612724565b610a72565b610268610322366004612614565b610d61565b61033a610335366004612520565b610f0d565b60405161024c93929190612ff3565b61035c610357366004612811565b610f42565b60405161024c939291906131b3565b61037e610379366004612811565b610f6c565b60405161024c919061301b565b6102686103993660046126af565b610f93565b61023f611022565b6103b96103b4366004612520565b611037565b60405161024c9291906131a5565b6102be61112b565b610268611155565b61023f611212565b6102686103ed366004612724565b611231565b610268610400366004612520565b611341565b610268610413366004612794565b6113ea565b61023f61154a565b6102be61042e366004612520565b611559565b61023f61044136600461264e565b61156e565b610268610454366004612614565b611577565b6102be610467366004612520565b6116b7565b61023f61047a366004612520565b6116d5565b61049261048d366004612614565b6117f4565b60405161024c9190613197565b6104b26104ad366004612520565b611822565b60405161024c9190612fd4565b61037e61196c565b6102686104d5366004612520565b61197b565b6102be6104e8366004612520565b6119a8565b61037e6119f3565b60008060015b905060008682878787604051602001610518959493929190612e8c565b60408051601f198184030181529181528151602092830120600090815260099092529020600201546001600160a01b0316925050505b949350505050565b61055e61112b565b8061057857503360009081526001602052604090205460ff165b61059d5760405162461bcd60e51b8152600401610594906130d9565b60405180910390fd5b6105ab848484846003610662565b50505050565b6005546001600160a01b031681565b6105c861112b565b806105eb57506105d6611212565b6001600160a01b0316336001600160a01b0316145b6106075760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b03811660009081526001602052604090819020805460ff19169055517fa3b62bc36326052d97ea62d63c3d60308ed4c3ea8ac079dd8499f1e9c4f80c0f90610657908390612ef0565b60405180910390a150565b61066a61112b565b8061068457503360009081526001602052604090205460ff165b6106a05760405162461bcd60e51b8152600401610594906130d9565b60006106b186858560015b86611a02565b9050856001600160a01b03167fd6fcfd83804f6b6b63260c7b99eb10060bc1319dbc9177fb6defc7bd614017bf82868689876040516106f4959493929190612f88565b60405180910390a2505050505050565b61070c61112b565b6107285760405162461bcd60e51b8152600401610594906130d9565b61073181611d6a565b50565b60016020526000908152604090205460ff1681565b61075161112b565b61076d5760405162461bcd60e51b8152600401610594906130d9565b600054610100900460ff1680610786575060005460ff16155b6107a25760405162461bcd60e51b8152600401610594906130c9565b600054610100900460ff161580156107cd576000805460ff1961ff0019909116610100171660011790555b6001600160a01b0388166107f35760405162461bcd60e51b815260040161059490613179565b6001600160a01b0387166108195760405162461bcd60e51b815260040161059490613159565b6001600160a01b03861661083f5760405162461bcd60e51b8152600401610594906130e9565b6001600160a01b0385166108655760405162461bcd60e51b8152600401610594906130b9565b6001600160a01b03841661088b5760405162461bcd60e51b815260040161059490613149565b61089489611d6a565b600580546001600160a01b03199081166001600160a01b038b8116919091179092556006805482168a84161790556007805482168984161790556008805482168884161790556003805490911691861691909117905560005b8281101561099a57600084848381811061090357fe5b90506020020160206109189190810190612520565b6001600160a01b0316141561093f5760405162461bcd60e51b815260040161059490613029565b600484848381811061094d57fe5b90506020020160206109629190810190612520565b815460018082018455600093845260209093200180546001600160a01b0319166001600160a01b0392909216919091179055016108ed565b5080156109ad576000805461ff00191690555b505050505050505050565b6006546001600160a01b031681565b6007546001600160a01b031681565b6109de61112b565b80610a0157506109ec611212565b6001600160a01b0316336001600160a01b0316145b610a1d5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b038116600090815260016020819052604091829020805460ff19169091179055517f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e33990610657908390612ef0565b610a7a61112b565b80610a9457503360009081526001602052604090205460ff165b610ab05760405162461bcd60e51b8152600401610594906130d9565b828114610acf5760405162461bcd60e51b815260040161059490613039565b60046224ea0063059fa60060005b86811015610d5757600b6000878784818110610af557fe5b9050602002016020610b0a9190810190612520565b6001600160a01b0316815260208101919091526040016000205460ff1615610b445760405162461bcd60e51b8152600401610594906130a9565b6000888883818110610b5257fe5b9050602002016020610b679190810190612520565b6001600160a01b03161415610b8e5760405162461bcd60e51b815260040161059490613049565b6000868683818110610b9c57fe5b9050602002016020610bb19190810190612520565b6001600160a01b03161415610bd85760405162461bcd60e51b815260040161059490613059565b6000888883818110610be657fe5b9050602002016020610bfb9190810190612520565b6001858588604051602001610c14959493929190612e8c565b60408051808303601f1901815282825280516020918201206060840183526001845290830188905292508101888885818110610c4c57fe5b9050602002016020610c619190810190612520565b6001600160a01b039081169091526000838152600960209081526040808320855181559185015160018301559390930151600290930180546001600160a01b03191693909216929092179055600a908a8a85818110610cbc57fe5b9050602002016020610cd19190810190612520565b6001600160a01b031681526020808201929092526040016000908120805460018181018355918352928220909201839055600b90898986818110610d1157fe5b9050602002016020610d269190810190612520565b6001600160a01b031681526020810191909152604001600020805460ff191691151591909117905550600101610add565b5050505050505050565b610d6961112b565b80610d8357503360009081526001602052604090205460ff165b610d9f5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b038216610dc55760405162461bcd60e51b815260040161059490613099565b60008111610de55760405162461bcd60e51b815260040161059490613089565b60055460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390610e179085908590600401612f66565b602060405180830381600087803b158015610e3157600080fd5b505af1158015610e45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e6991908101906127f3565b50604051637547c7a360e01b81526001600160a01b03831690637547c7a390610e96908490600401613197565b600060405180830381600087803b158015610eb057600080fd5b505af1158015610ec4573d6000803e3d6000fd5b50505050816001600160a01b03167fb539ca1e5c8d398ddf1c41c30166f33404941683be4683319b57669a93dad4ef82604051610f019190613197565b60405180910390a25050565b600c6020526000908152604090205460ff811690610100810463ffffffff16906501000000000090046001600160801b031683565b6009602052600090815260409020805460018201546002909201549091906001600160a01b031683565b60048181548110610f7957fe5b6000918252602090912001546001600160a01b0316905081565b610f9b61112b565b80610fb557503360009081526001602052604090205460ff165b610fd15760405162461bcd60e51b8152600401610594906130d9565b6000610fdf868585846106ab565b9050856001600160a01b03167f3791c6c90c276d011b4b885c0bfba0554342acf50a539baca1b06f070af25ff482868689876040516106f4959493929190612f88565b6000546201000090046001600160a01b031690565b6000806000839050806001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561107857600080fd5b505afa15801561108c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110b0919081019061282f565b816001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156110e957600080fd5b505afa1580156110fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611121919081019061282f565b9250925050915091565b600080546201000090046001600160a01b0316611146611db2565b6001600160a01b031614905090565b61115d61112b565b6111795760405162461bcd60e51b8152600401610594906130d9565b6000611183611212565b6001600160a01b031614156111aa5760405162461bcd60e51b815260040161059490613129565b6111b2611212565b6001600160a01b0316336001600160a01b03167f560ce9bc23860aa4ec0898e6c59b295d3461332304d14f1ac216282407da435f60405160405180910390a3604051600090819061120290612ee5565b6040519081900390209190915550565b60008060405161122190612ee5565b6040519081900390205492915050565b61123961112b565b8061125357503360009081526001602052604090205460ff165b61126f5760405162461bcd60e51b8152600401610594906130d9565b60005b8381101561133a57600085858381811061128857fe5b905060200201602061129d9190810190612520565b6001600160a01b031614156112c45760405162461bcd60e51b815260040161059490613049565b60008383838181106112d257fe5b90506020020135116112f65760405162461bcd60e51b8152600401610594906130f9565b61133285858381811061130557fe5b905060200201602061131a9190810190612520565b84848481811061132657fe5b90506020020135611db6565b600101611272565b5050505050565b61134961112b565b6113655760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b03811661138b5760405162461bcd60e51b815260040161059490613119565b806001600160a01b031661139d611212565b6001600160a01b0316336001600160a01b03167f6bf6a88b1bb767c00d69f519bfabc075817f28425abfc80cfb779ef4e9b0bbe560405160405180910390a4600060405161120290612ee5565b6113f261112b565b8061140c57503360009081526001602052604090205460ff165b6114285760405162461bcd60e51b8152600401610594906130d9565b80518251146114495760405162461bcd60e51b815260040161059490613139565b60005b81518110156115455761145d6122e9565b82828151811061146957fe5b60200260200101519050600084838151811061148157fe5b6020908102919091018101516001600160a01b0381166000818152600c845260409081902086518154958801518884015160ff199097169115159190911764ffffffff00191661010063ffffffff909216919091021765010000000000600160a81b031916650100000000006001600160801b039096169590950294909417845551919350917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca34916115339190613189565b60405180910390a2505060010161144c565b505050565b6008546001600160a01b031681565b600b6020526000908152604090205460ff1681565b600080806104fb565b61157f61112b565b61159b5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b0382166115c15760405162461bcd60e51b815260040161059490613109565b806115de5760405162461bcd60e51b815260040161059490613089565b60055460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906116109085908590600401612f66565b602060405180830381600087803b15801561162a57600080fd5b505af115801561163e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061166291908101906127f3565b61167e5760405162461bcd60e51b815260040161059490613169565b816001600160a01b03167fe1b0ada289bf82fb641c8c1e1c78f820fae44e8f845760725cdeb09167a289ad82604051610f019190613197565b6001600160a01b03166000908152600b602052604090205460ff1690565b60006117ee82600360009054906101000a90046001600160a01b03166001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561172957600080fd5b505afa15801561173d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611761919081019061282f565b600360009054906101000a90046001600160a01b03166001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156117af57600080fd5b505afa1580156117c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117e7919081019061282f565b60036104f5565b92915050565b600a602052816000526040600020818154811061180d57fe5b90600052602060002001600091509150505481565b606080600a6000846001600160a01b03166001600160a01b0316815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561189457602002820191906000526020600020905b815481526020019060010190808311611880575b50505050509050600081519050606082516040519080825280602002602001820160405280156118de57816020015b6118cb612309565b8152602001906001900390816118c35790505b50905060005b8281101561196357600960008583815181106118fc57fe5b6020908102919091018101518252818101929092526040908101600020815160608101835281548152600182015493810193909352600201546001600160a01b031690820152825183908390811061195057fe5b60209081029190910101526001016118e4565b50949350505050565b6003546001600160a01b031681565b61198361112b565b61199f5760405162461bcd60e51b8152600401610594906130d9565b6107318161225a565b6001600160a01b0381166000908152600c602052604081205460ff1680156117ee5750506001600160a01b03166000908152600c6020526040902054610100900463ffffffff161590565b6002546001600160a01b031681565b60008060008785888887604051602001611a20959493929190612e8c565b60408051601f198184030181529181528151602092830120600081815260099093529120600201549091506001600160a01b0316611d44578460011415611b0457600254600554600654600754604051637d2fbb8f60e11b81526001600160a01b039485169463fa5f771e94611aab9490821693908216928f928f928f929116908490600401612efe565b602060405180830381600087803b158015611ac557600080fd5b505af1158015611ad9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611afd919081019061253e565b9150611bfa565b600260009054906101000a90046001600160a01b03166001600160a01b031663546344f0600560009054906101000a90046001600160a01b0316600660009054906101000a90046001600160a01b03168b8b8b600760009054906101000a90046001600160a01b0316600860009054906101000a90046001600160a01b03166040518863ffffffff1660e01b8152600401611ba59796959493929190612efe565b602060405180830381600087803b158015611bbf57600080fd5b505af1158015611bd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611bf7919081019061253e565b91505b604080516060808201835287825260208083018881526001600160a01b038088168587018181526000898152600986528881209751885593516001808901919091559051600290970180549784166001600160a01b031990981697909717909655908e168252600a83528582208054808701825590835283832001879055808252600b8352858220805460ff1990811687179091558651948501875294845263ffffffff808c168585019081526001600160801b03808d16878a01908152848652600c909652938890209551865491519551909416650100000000000265010000000000600160a81b0319959092166101000264ffffffff00199415159190971617929092169490941791909116178155915190917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca3491611d3b9190613189565b60405180910390a25b6000908152600960205260409020600201546001600160a01b0316979650505050505050565b6001600160a01b038116611d905760405162461bcd60e51b815260040161059490613079565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b600080600183039050600060048281548110611dce57fe5b60009182526020909120015460405163cc49ede760e01b81526001600160a01b039091169063cc49ede790611e07908890600401612ef0565b60206040518083038186803b158015611e1f57600080fd5b505afa158015611e33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e57919081019061253e565b90506001600160a01b038116156120075780856001826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611ea557600080fd5b505afa158015611eb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611edd919081019061282f565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611f1657600080fd5b505afa158015611f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f4e919081019061282f565b88604051602001611f63959493929190612e8c565b60408051601f19818403018152828252805160209182012060608401835260018085528285018a81526001600160a01b0388811687870181815260008681526009885288812099518a5593518986015551600290980180546001600160a01b031916988316989098179097558c168152600a84528481208054808401825590825284822001839055948552600b90925291909220805460ff19169092179091559350505b60006004838154811061201657fe5b60009182526020909120015460405163c810a3e360e01b81526001600160a01b039091169063c810a3e39061204f908990600401612ef0565b60206040518083038186803b15801561206757600080fd5b505afa15801561207b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061209f919081019061253e565b90506001600160a01b038116156122525780866000826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b1580156120ed57600080fd5b505afa158015612101573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612125919081019061282f565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561215e57600080fd5b505afa158015612172573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612196919081019061282f565b896040516020016121ab959493929190612e8c565b60408051601f19818403018152828252805160209182012060608401835260008085528285018b81526001600160a01b03888116878701818152858552600987528785209851895592516001808a01919091559251600290980180546001600160a01b031916988316989098179097558d168252600a84528482208054808301825590835284832001839055948152600b909252919020805460ff19169092179091559450505b505050505050565b6001600160a01b0381166122805760405162461bcd60e51b815260040161059490613069565b600080546040516001600160a01b03808516936201000090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b604080516060810182526000808252602082018190529181019190915290565b6040518060600160405280600081526020016000815260200160006001600160a01b031681525090565b80356117ee816132d7565b80516117ee816132d7565b60008083601f84011261235b57600080fd5b50813567ffffffffffffffff81111561237357600080fd5b60208301915083602082028301111561238b57600080fd5b9250929050565b600082601f8301126123a357600080fd5b81356123b66123b182613202565b6131db565b915081818352602084019350602081019050838560208402820111156123db57600080fd5b60005b8381101561240757816123f18882612333565b84525060209283019291909101906001016123de565b5050505092915050565b600082601f83011261242257600080fd5b81356124306123b182613202565b9150818183526020840193506020810190508385606084028201111561245557600080fd5b60005b83811015612407578161246b8882612499565b84525060209092019160609190910190600101612458565b80356117ee816132eb565b80516117ee816132eb565b6000606082840312156124ab57600080fd5b6124b560606131db565b905060006124c38484612483565b82525060206124d484848301612515565b60208301525060406124e8848285016124f4565b60408301525092915050565b80356117ee816132f4565b80356117ee816132fd565b80516117ee816132fd565b80356117ee81613306565b60006020828403121561253257600080fd5b600061054e8484612333565b60006020828403121561255057600080fd5b600061054e848461233e565b60008060008060008060008060e0898b03121561257857600080fd5b60006125848b8b612333565b98505060206125958b828c01612333565b97505060406125a68b828c01612333565b96505060606125b78b828c01612333565b95505060806125c88b828c01612333565b94505060a06125d98b828c01612333565b93505060c089013567ffffffffffffffff8111156125f657600080fd5b6126028b828c01612349565b92509250509295985092959890939650565b6000806040838503121561262757600080fd5b60006126338585612333565b9250506020612644858286016124ff565b9150509250929050565b6000806000806080858703121561266457600080fd5b60006126708787612333565b9450506020612681878288016124ff565b9350506040612692878288016124ff565b92505060606126a3878288016124ff565b91505092959194509250565b600080600080600060a086880312156126c757600080fd5b60006126d38888612333565b95505060206126e4888289016124ff565b94505060406126f5888289016124ff565b9350506060612706888289016124ff565b9250506080612717888289016124ff565b9150509295509295909350565b6000806000806040858703121561273a57600080fd5b843567ffffffffffffffff81111561275157600080fd5b61275d87828801612349565b9450945050602085013567ffffffffffffffff81111561277c57600080fd5b61278887828801612349565b95989497509550505050565b600080604083850312156127a757600080fd5b823567ffffffffffffffff8111156127be57600080fd5b6127ca85828601612392565b925050602083013567ffffffffffffffff8111156127e757600080fd5b61264485828601612411565b60006020828403121561280557600080fd5b600061054e848461248e565b60006020828403121561282357600080fd5b600061054e84846124ff565b60006020828403121561284157600080fd5b600061054e848461250a565b60006128598383612e29565b505060600190565b61286a81613256565b82525050565b61286a61287c82613256565b6132b9565b600061288c82613229565b612896818561322d565b93506128a183613223565b8060005b838110156128cf5781516128b9888261284d565b97506128c483613223565b9250506001016128a5565b509495945050505050565b61286a81613261565b61286a81613275565b60006128f960208361322d565b7f56657374696e67207265676973747279206164647265737320696e76616c6964815260200192915050565b6000612932600f8361322d565b6e0c2e4e4c2f2e640dad2e6dac2e8c6d608b1b815260200192915050565b600061295d601f8361322d565b7f746f6b656e206f776e65722063616e6e6f742062652030206164647265737300815260200192915050565b6000612996601b8361322d565b7f76657374696e672063616e6e6f74206265203020616464726573730000000000815260200192915050565b60006129cf60268361322d565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b6000612a17601e8361322d565b7f76657374696e67466163746f7279206164647265737320696e76616c69640000815260200192915050565b6000612a50600e8361322d565b6d185b5bdd5b9d081a5b9d985b1a5960921b815260200192915050565b6000612a7a60178361322d565b7f76657374696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612ab3600e8361322d565b6d76657374696e672065786973747360901b815260200192915050565b6000612add601c8361322d565b7f76657374696e674f776e6572206164647265737320696e76616c696400000000815260200192915050565b6000612b16602e8361322d565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656181526d191e481a5b9a5d1a585b1a5e995960921b602082015260400192915050565b6000612b66600c8361322d565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000612b8e60238361322d565b7f66656553686172696e67436f6c6c6563746f72206164647265737320696e76618152621b1a5960ea1b602082015260400192915050565b6000612bd3602c8361322d565b7f76657374696e67206372656174696f6e2074797065206d75737420626520677281526b06561746572207468616e20360a41b602082015260400192915050565b6000612c2160188361322d565b7f7265636569766572206164647265737320696e76616c69640000000000000000815260200192915050565b6000612c5a60158361322d565b7434b73b30b634b21030b236b4b71036b0b730b3b2b960591b815260200192915050565b6000612c8b60188361322d565b7f41646d696e206d616e61676572206973206e6f74207365740000000000000000815260200192915050565b6000612cc460108361322d565b6f0aadcdac2e8c6d0cac840d8cadccee8d60831b815260200192915050565b6000612cf060198361322d565b7f4c6f636b6564534f56206164647265737320696e76616c696400000000000000815260200192915050565b6000612d2960178361322d565b7f7374616b696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612d62600f8361322d565b6e1d1c985b9cd9995c8819985a5b1959608a1b815260200192915050565b6000612d8d60138361322d565b7214d3d5881859191c995cdcc81a5b9d985b1a59606a1b815260200192915050565b6000612dbc601683613236565b756b65792e61646d696e2e6d616e616765722e726f6c6560501b815260160192915050565b80546060830190612df181613280565b612dfb85826128da565b50612e0581613293565b612e126020860182612e83565b50612e1c816132a6565b61133a6040860182612e60565b80516060830190612e3a8482612e69565b506020820151612e4d6020850182612e69565b5060408201516105ab6040850182612861565b61286a81613241565b61286a81613272565b61286a612e7e82613272565b613272565b61286a8161324d565b6000612e988288612870565b601482019150612ea88287612e72565b602082019150612eb88286612e72565b602082019150612ec88285612e72565b602082019150612ed88284612e72565b5060200195945050505050565b60006117ee82612daf565b602081016117ee8284612861565b60e08101612f0c828a612861565b612f196020830189612861565b612f266040830188612861565b612f336060830187612e69565b612f406080830186612e69565b612f4d60a0830185612861565b612f5a60c0830184612861565b98975050505050505050565b60408101612f748285612861565b612f816020830184612e69565b9392505050565b60a08101612f968288612861565b612fa36020830187612e69565b612fb06040830186612e69565b612fbd6060830185612e69565b612fca6080830184612e69565b9695505050505050565b60208082528101612f818184612881565b602081016117ee82846128da565b6060810161300182866128da565b61300e6020830185612e83565b61054e6040830184612e60565b602081016117ee82846128e3565b602080825281016117ee816128ec565b602080825281016117ee81612925565b602080825281016117ee81612950565b602080825281016117ee81612989565b602080825281016117ee816129c2565b602080825281016117ee81612a0a565b602080825281016117ee81612a43565b602080825281016117ee81612a6d565b602080825281016117ee81612aa6565b602080825281016117ee81612ad0565b602080825281016117ee81612b09565b602080825281016117ee81612b59565b602080825281016117ee81612b81565b602080825281016117ee81612bc6565b602080825281016117ee81612c14565b602080825281016117ee81612c4d565b602080825281016117ee81612c7e565b602080825281016117ee81612cb7565b602080825281016117ee81612ce3565b602080825281016117ee81612d1c565b602080825281016117ee81612d55565b602080825281016117ee81612d80565b606081016117ee8284612de1565b602081016117ee8284612e69565b60408101612f748285612e69565b606081016131c18286612e69565b6131ce6020830185612e69565b61054e6040830184612861565b60405181810167ffffffffffffffff811182821017156131fa57600080fd5b604052919050565b600067ffffffffffffffff82111561321957600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b919050565b60ff1690565b6001600160801b031690565b63ffffffff1690565b60006117ee82613266565b151590565b6001600160a01b031690565b90565b60006117ee82613256565b60006117ee61328e83613272565b61323b565b60006117ee6132a1836132d1565b61324d565b60006117ee6132b4836132cb565b613241565b60006117ee8260006117ee8260601b90565b60281c90565b60081c90565b6132e081613256565b811461073157600080fd5b6132e081613261565b6132e081613241565b6132e081613272565b6132e08161324d56fea365627a7a72315820dc2ada767f6100aeabb2dd76de36097a63988151b323d89b951fad1348d343756c6578706572696d656e74616cf564736f6c63430005110040", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102275760003560e01c80638dedf00911610130578063c36519d1116100b8578063efb957331161007c578063efb957331461049f578063f2f46b3b146104bf578063f2fde38b146104c7578063f421ed7e146104da578063f60826ee146104ed57610227565b8063c36519d114610433578063c680c0b714610446578063ca210d8c14610459578063cc49ede71461046c578063dfb9366d1461047f57610227565b8063b810c648116100ff578063b810c648146103df578063bc160758146103f2578063bc84f9ca14610405578063bd7b590814610418578063c0e098521461042057610227565b80638dedf009146103a65780638f32d59b146103c75780639af05157146103cf578063b29d2514146103d757610227565b80636b7dbb2d116101b357806380fcb9921161018257806380fcb99214610327578063821bee7314610349578063842a49d51461036b578063862e229d1461038b5780638da5cb5b1461039e57610227565b80636b7dbb2d146102e657806370480275146102ee578063786931da1461030157806379a83f5a1461031457610227565b80631f509326116101fa5780631f50932614610285578063377220fd14610298578063429b62e5146102ab57806342a82b4f146102cb5780634cf088d9146102de57610227565b806302df04761461022c5780630665a06f1461025557806308dcb3601461026a5780631785f53c14610272575b600080fd5b61023f61023a36600461264e565b6104f5565b60405161024c9190612ef0565b60405180910390f35b61026861026336600461264e565b610556565b005b61023f6105b1565b610268610280366004612520565b6105c0565b6102686102933660046126af565b610662565b6102686102a6366004612520565b610704565b6102be6102b9366004612520565b610734565b60405161024c9190612fe5565b6102686102d936600461255c565b610749565b61023f6109b8565b61023f6109c7565b6102686102fc366004612520565b6109d6565b61026861030f366004612724565b610a72565b610268610322366004612614565b610d61565b61033a610335366004612520565b610f0d565b60405161024c93929190612ff3565b61035c610357366004612811565b610f42565b60405161024c939291906131b3565b61037e610379366004612811565b610f6c565b60405161024c919061301b565b6102686103993660046126af565b610f93565b61023f611022565b6103b96103b4366004612520565b611037565b60405161024c9291906131a5565b6102be61112b565b610268611155565b61023f611212565b6102686103ed366004612724565b611231565b610268610400366004612520565b611341565b610268610413366004612794565b6113ea565b61023f61154a565b6102be61042e366004612520565b611559565b61023f61044136600461264e565b61156e565b610268610454366004612614565b611577565b6102be610467366004612520565b6116b7565b61023f61047a366004612520565b6116d5565b61049261048d366004612614565b6117f4565b60405161024c9190613197565b6104b26104ad366004612520565b611822565b60405161024c9190612fd4565b61037e61196c565b6102686104d5366004612520565b61197b565b6102be6104e8366004612520565b6119a8565b61037e6119f3565b60008060015b905060008682878787604051602001610518959493929190612e8c565b60408051601f198184030181529181528151602092830120600090815260099092529020600201546001600160a01b0316925050505b949350505050565b61055e61112b565b8061057857503360009081526001602052604090205460ff165b61059d5760405162461bcd60e51b8152600401610594906130d9565b60405180910390fd5b6105ab848484846003610662565b50505050565b6005546001600160a01b031681565b6105c861112b565b806105eb57506105d6611212565b6001600160a01b0316336001600160a01b0316145b6106075760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b03811660009081526001602052604090819020805460ff19169055517fa3b62bc36326052d97ea62d63c3d60308ed4c3ea8ac079dd8499f1e9c4f80c0f90610657908390612ef0565b60405180910390a150565b61066a61112b565b8061068457503360009081526001602052604090205460ff165b6106a05760405162461bcd60e51b8152600401610594906130d9565b60006106b186858560015b86611a02565b9050856001600160a01b03167fd6fcfd83804f6b6b63260c7b99eb10060bc1319dbc9177fb6defc7bd614017bf82868689876040516106f4959493929190612f88565b60405180910390a2505050505050565b61070c61112b565b6107285760405162461bcd60e51b8152600401610594906130d9565b61073181611d6a565b50565b60016020526000908152604090205460ff1681565b61075161112b565b61076d5760405162461bcd60e51b8152600401610594906130d9565b600054610100900460ff1680610786575060005460ff16155b6107a25760405162461bcd60e51b8152600401610594906130c9565b600054610100900460ff161580156107cd576000805460ff1961ff0019909116610100171660011790555b6001600160a01b0388166107f35760405162461bcd60e51b815260040161059490613179565b6001600160a01b0387166108195760405162461bcd60e51b815260040161059490613159565b6001600160a01b03861661083f5760405162461bcd60e51b8152600401610594906130e9565b6001600160a01b0385166108655760405162461bcd60e51b8152600401610594906130b9565b6001600160a01b03841661088b5760405162461bcd60e51b815260040161059490613149565b61089489611d6a565b600580546001600160a01b03199081166001600160a01b038b8116919091179092556006805482168a84161790556007805482168984161790556008805482168884161790556003805490911691861691909117905560005b8281101561099a57600084848381811061090357fe5b90506020020160206109189190810190612520565b6001600160a01b0316141561093f5760405162461bcd60e51b815260040161059490613029565b600484848381811061094d57fe5b90506020020160206109629190810190612520565b815460018082018455600093845260209093200180546001600160a01b0319166001600160a01b0392909216919091179055016108ed565b5080156109ad576000805461ff00191690555b505050505050505050565b6006546001600160a01b031681565b6007546001600160a01b031681565b6109de61112b565b80610a0157506109ec611212565b6001600160a01b0316336001600160a01b0316145b610a1d5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b038116600090815260016020819052604091829020805460ff19169091179055517f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e33990610657908390612ef0565b610a7a61112b565b80610a9457503360009081526001602052604090205460ff165b610ab05760405162461bcd60e51b8152600401610594906130d9565b828114610acf5760405162461bcd60e51b815260040161059490613039565b60046224ea0063059fa60060005b86811015610d5757600b6000878784818110610af557fe5b9050602002016020610b0a9190810190612520565b6001600160a01b0316815260208101919091526040016000205460ff1615610b445760405162461bcd60e51b8152600401610594906130a9565b6000888883818110610b5257fe5b9050602002016020610b679190810190612520565b6001600160a01b03161415610b8e5760405162461bcd60e51b815260040161059490613049565b6000868683818110610b9c57fe5b9050602002016020610bb19190810190612520565b6001600160a01b03161415610bd85760405162461bcd60e51b815260040161059490613059565b6000888883818110610be657fe5b9050602002016020610bfb9190810190612520565b6001858588604051602001610c14959493929190612e8c565b60408051808303601f1901815282825280516020918201206060840183526001845290830188905292508101888885818110610c4c57fe5b9050602002016020610c619190810190612520565b6001600160a01b039081169091526000838152600960209081526040808320855181559185015160018301559390930151600290930180546001600160a01b03191693909216929092179055600a908a8a85818110610cbc57fe5b9050602002016020610cd19190810190612520565b6001600160a01b031681526020808201929092526040016000908120805460018181018355918352928220909201839055600b90898986818110610d1157fe5b9050602002016020610d269190810190612520565b6001600160a01b031681526020810191909152604001600020805460ff191691151591909117905550600101610add565b5050505050505050565b610d6961112b565b80610d8357503360009081526001602052604090205460ff165b610d9f5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b038216610dc55760405162461bcd60e51b815260040161059490613099565b60008111610de55760405162461bcd60e51b815260040161059490613089565b60055460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390610e179085908590600401612f66565b602060405180830381600087803b158015610e3157600080fd5b505af1158015610e45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e6991908101906127f3565b50604051637547c7a360e01b81526001600160a01b03831690637547c7a390610e96908490600401613197565b600060405180830381600087803b158015610eb057600080fd5b505af1158015610ec4573d6000803e3d6000fd5b50505050816001600160a01b03167fb539ca1e5c8d398ddf1c41c30166f33404941683be4683319b57669a93dad4ef82604051610f019190613197565b60405180910390a25050565b600c6020526000908152604090205460ff811690610100810463ffffffff16906501000000000090046001600160801b031683565b6009602052600090815260409020805460018201546002909201549091906001600160a01b031683565b60048181548110610f7957fe5b6000918252602090912001546001600160a01b0316905081565b610f9b61112b565b80610fb557503360009081526001602052604090205460ff165b610fd15760405162461bcd60e51b8152600401610594906130d9565b6000610fdf868585846106ab565b9050856001600160a01b03167f3791c6c90c276d011b4b885c0bfba0554342acf50a539baca1b06f070af25ff482868689876040516106f4959493929190612f88565b6000546201000090046001600160a01b031690565b6000806000839050806001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561107857600080fd5b505afa15801561108c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110b0919081019061282f565b816001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156110e957600080fd5b505afa1580156110fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611121919081019061282f565b9250925050915091565b600080546201000090046001600160a01b0316611146611db2565b6001600160a01b031614905090565b61115d61112b565b6111795760405162461bcd60e51b8152600401610594906130d9565b6000611183611212565b6001600160a01b031614156111aa5760405162461bcd60e51b815260040161059490613129565b6111b2611212565b6001600160a01b0316336001600160a01b03167f560ce9bc23860aa4ec0898e6c59b295d3461332304d14f1ac216282407da435f60405160405180910390a3604051600090819061120290612ee5565b6040519081900390209190915550565b60008060405161122190612ee5565b6040519081900390205492915050565b61123961112b565b8061125357503360009081526001602052604090205460ff165b61126f5760405162461bcd60e51b8152600401610594906130d9565b60005b8381101561133a57600085858381811061128857fe5b905060200201602061129d9190810190612520565b6001600160a01b031614156112c45760405162461bcd60e51b815260040161059490613049565b60008383838181106112d257fe5b90506020020135116112f65760405162461bcd60e51b8152600401610594906130f9565b61133285858381811061130557fe5b905060200201602061131a9190810190612520565b84848481811061132657fe5b90506020020135611db6565b600101611272565b5050505050565b61134961112b565b6113655760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b03811661138b5760405162461bcd60e51b815260040161059490613119565b806001600160a01b031661139d611212565b6001600160a01b0316336001600160a01b03167f6bf6a88b1bb767c00d69f519bfabc075817f28425abfc80cfb779ef4e9b0bbe560405160405180910390a4600060405161120290612ee5565b6113f261112b565b8061140c57503360009081526001602052604090205460ff165b6114285760405162461bcd60e51b8152600401610594906130d9565b80518251146114495760405162461bcd60e51b815260040161059490613139565b60005b81518110156115455761145d6122e9565b82828151811061146957fe5b60200260200101519050600084838151811061148157fe5b6020908102919091018101516001600160a01b0381166000818152600c845260409081902086518154958801518884015160ff199097169115159190911764ffffffff00191661010063ffffffff909216919091021765010000000000600160a81b031916650100000000006001600160801b039096169590950294909417845551919350917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca34916115339190613189565b60405180910390a2505060010161144c565b505050565b6008546001600160a01b031681565b600b6020526000908152604090205460ff1681565b600080806104fb565b61157f61112b565b61159b5760405162461bcd60e51b8152600401610594906130d9565b6001600160a01b0382166115c15760405162461bcd60e51b815260040161059490613109565b806115de5760405162461bcd60e51b815260040161059490613089565b60055460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906116109085908590600401612f66565b602060405180830381600087803b15801561162a57600080fd5b505af115801561163e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061166291908101906127f3565b61167e5760405162461bcd60e51b815260040161059490613169565b816001600160a01b03167fe1b0ada289bf82fb641c8c1e1c78f820fae44e8f845760725cdeb09167a289ad82604051610f019190613197565b6001600160a01b03166000908152600b602052604090205460ff1690565b60006117ee82600360009054906101000a90046001600160a01b03166001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b15801561172957600080fd5b505afa15801561173d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611761919081019061282f565b600360009054906101000a90046001600160a01b03166001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b1580156117af57600080fd5b505afa1580156117c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117e7919081019061282f565b60036104f5565b92915050565b600a602052816000526040600020818154811061180d57fe5b90600052602060002001600091509150505481565b606080600a6000846001600160a01b03166001600160a01b0316815260200190815260200160002080548060200260200160405190810160405280929190818152602001828054801561189457602002820191906000526020600020905b815481526020019060010190808311611880575b50505050509050600081519050606082516040519080825280602002602001820160405280156118de57816020015b6118cb612309565b8152602001906001900390816118c35790505b50905060005b8281101561196357600960008583815181106118fc57fe5b6020908102919091018101518252818101929092526040908101600020815160608101835281548152600182015493810193909352600201546001600160a01b031690820152825183908390811061195057fe5b60209081029190910101526001016118e4565b50949350505050565b6003546001600160a01b031681565b61198361112b565b61199f5760405162461bcd60e51b8152600401610594906130d9565b6107318161225a565b6001600160a01b0381166000908152600c602052604081205460ff1680156117ee5750506001600160a01b03166000908152600c6020526040902054610100900463ffffffff161590565b6002546001600160a01b031681565b60008060008785888887604051602001611a20959493929190612e8c565b60408051601f198184030181529181528151602092830120600081815260099093529120600201549091506001600160a01b0316611d44578460011415611b0457600254600554600654600754604051637d2fbb8f60e11b81526001600160a01b039485169463fa5f771e94611aab9490821693908216928f928f928f929116908490600401612efe565b602060405180830381600087803b158015611ac557600080fd5b505af1158015611ad9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611afd919081019061253e565b9150611bfa565b600260009054906101000a90046001600160a01b03166001600160a01b031663546344f0600560009054906101000a90046001600160a01b0316600660009054906101000a90046001600160a01b03168b8b8b600760009054906101000a90046001600160a01b0316600860009054906101000a90046001600160a01b03166040518863ffffffff1660e01b8152600401611ba59796959493929190612efe565b602060405180830381600087803b158015611bbf57600080fd5b505af1158015611bd3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611bf7919081019061253e565b91505b604080516060808201835287825260208083018881526001600160a01b038088168587018181526000898152600986528881209751885593516001808901919091559051600290970180549784166001600160a01b031990981697909717909655908e168252600a83528582208054808701825590835283832001879055808252600b8352858220805460ff1990811687179091558651948501875294845263ffffffff808c168585019081526001600160801b03808d16878a01908152848652600c909652938890209551865491519551909416650100000000000265010000000000600160a81b0319959092166101000264ffffffff00199415159190971617929092169490941791909116178155915190917f4deb6fa84b5232023ce753128000047116f9a6795b40a3b701edf5d8e265ca3491611d3b9190613189565b60405180910390a25b6000908152600960205260409020600201546001600160a01b0316979650505050505050565b6001600160a01b038116611d905760405162461bcd60e51b815260040161059490613079565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b600080600183039050600060048281548110611dce57fe5b60009182526020909120015460405163cc49ede760e01b81526001600160a01b039091169063cc49ede790611e07908890600401612ef0565b60206040518083038186803b158015611e1f57600080fd5b505afa158015611e33573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e57919081019061253e565b90506001600160a01b038116156120075780856001826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b158015611ea557600080fd5b505afa158015611eb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611edd919081019061282f565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b158015611f1657600080fd5b505afa158015611f2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f4e919081019061282f565b88604051602001611f63959493929190612e8c565b60408051601f19818403018152828252805160209182012060608401835260018085528285018a81526001600160a01b0388811687870181815260008681526009885288812099518a5593518986015551600290980180546001600160a01b031916988316989098179097558c168152600a84528481208054808401825590825284822001839055948552600b90925291909220805460ff19169092179091559350505b60006004838154811061201657fe5b60009182526020909120015460405163c810a3e360e01b81526001600160a01b039091169063c810a3e39061204f908990600401612ef0565b60206040518083038186803b15801561206757600080fd5b505afa15801561207b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061209f919081019061253e565b90506001600160a01b038116156122525780866000826001600160a01b03166313d033c06040518163ffffffff1660e01b815260040160206040518083038186803b1580156120ed57600080fd5b505afa158015612101573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612125919081019061282f565b836001600160a01b0316630fb5a6b46040518163ffffffff1660e01b815260040160206040518083038186803b15801561215e57600080fd5b505afa158015612172573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612196919081019061282f565b896040516020016121ab959493929190612e8c565b60408051601f19818403018152828252805160209182012060608401835260008085528285018b81526001600160a01b03888116878701818152858552600987528785209851895592516001808a01919091559251600290980180546001600160a01b031916988316989098179097558d168252600a84528482208054808301825590835284832001839055948152600b909252919020805460ff19169092179091559450505b505050505050565b6001600160a01b0381166122805760405162461bcd60e51b815260040161059490613069565b600080546040516001600160a01b03808516936201000090930416917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b03909216620100000262010000600160b01b0319909216919091179055565b604080516060810182526000808252602082018190529181019190915290565b6040518060600160405280600081526020016000815260200160006001600160a01b031681525090565b80356117ee816132d7565b80516117ee816132d7565b60008083601f84011261235b57600080fd5b50813567ffffffffffffffff81111561237357600080fd5b60208301915083602082028301111561238b57600080fd5b9250929050565b600082601f8301126123a357600080fd5b81356123b66123b182613202565b6131db565b915081818352602084019350602081019050838560208402820111156123db57600080fd5b60005b8381101561240757816123f18882612333565b84525060209283019291909101906001016123de565b5050505092915050565b600082601f83011261242257600080fd5b81356124306123b182613202565b9150818183526020840193506020810190508385606084028201111561245557600080fd5b60005b83811015612407578161246b8882612499565b84525060209092019160609190910190600101612458565b80356117ee816132eb565b80516117ee816132eb565b6000606082840312156124ab57600080fd5b6124b560606131db565b905060006124c38484612483565b82525060206124d484848301612515565b60208301525060406124e8848285016124f4565b60408301525092915050565b80356117ee816132f4565b80356117ee816132fd565b80516117ee816132fd565b80356117ee81613306565b60006020828403121561253257600080fd5b600061054e8484612333565b60006020828403121561255057600080fd5b600061054e848461233e565b60008060008060008060008060e0898b03121561257857600080fd5b60006125848b8b612333565b98505060206125958b828c01612333565b97505060406125a68b828c01612333565b96505060606125b78b828c01612333565b95505060806125c88b828c01612333565b94505060a06125d98b828c01612333565b93505060c089013567ffffffffffffffff8111156125f657600080fd5b6126028b828c01612349565b92509250509295985092959890939650565b6000806040838503121561262757600080fd5b60006126338585612333565b9250506020612644858286016124ff565b9150509250929050565b6000806000806080858703121561266457600080fd5b60006126708787612333565b9450506020612681878288016124ff565b9350506040612692878288016124ff565b92505060606126a3878288016124ff565b91505092959194509250565b600080600080600060a086880312156126c757600080fd5b60006126d38888612333565b95505060206126e4888289016124ff565b94505060406126f5888289016124ff565b9350506060612706888289016124ff565b9250506080612717888289016124ff565b9150509295509295909350565b6000806000806040858703121561273a57600080fd5b843567ffffffffffffffff81111561275157600080fd5b61275d87828801612349565b9450945050602085013567ffffffffffffffff81111561277c57600080fd5b61278887828801612349565b95989497509550505050565b600080604083850312156127a757600080fd5b823567ffffffffffffffff8111156127be57600080fd5b6127ca85828601612392565b925050602083013567ffffffffffffffff8111156127e757600080fd5b61264485828601612411565b60006020828403121561280557600080fd5b600061054e848461248e565b60006020828403121561282357600080fd5b600061054e84846124ff565b60006020828403121561284157600080fd5b600061054e848461250a565b60006128598383612e29565b505060600190565b61286a81613256565b82525050565b61286a61287c82613256565b6132b9565b600061288c82613229565b612896818561322d565b93506128a183613223565b8060005b838110156128cf5781516128b9888261284d565b97506128c483613223565b9250506001016128a5565b509495945050505050565b61286a81613261565b61286a81613275565b60006128f960208361322d565b7f56657374696e67207265676973747279206164647265737320696e76616c6964815260200192915050565b6000612932600f8361322d565b6e0c2e4e4c2f2e640dad2e6dac2e8c6d608b1b815260200192915050565b600061295d601f8361322d565b7f746f6b656e206f776e65722063616e6e6f742062652030206164647265737300815260200192915050565b6000612996601b8361322d565b7f76657374696e672063616e6e6f74206265203020616464726573730000000000815260200192915050565b60006129cf60268361322d565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b6000612a17601e8361322d565b7f76657374696e67466163746f7279206164647265737320696e76616c69640000815260200192915050565b6000612a50600e8361322d565b6d185b5bdd5b9d081a5b9d985b1a5960921b815260200192915050565b6000612a7a60178361322d565b7f76657374696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612ab3600e8361322d565b6d76657374696e672065786973747360901b815260200192915050565b6000612add601c8361322d565b7f76657374696e674f776e6572206164647265737320696e76616c696400000000815260200192915050565b6000612b16602e8361322d565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656181526d191e481a5b9a5d1a585b1a5e995960921b602082015260400192915050565b6000612b66600c8361322d565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000612b8e60238361322d565b7f66656553686172696e67436f6c6c6563746f72206164647265737320696e76618152621b1a5960ea1b602082015260400192915050565b6000612bd3602c8361322d565b7f76657374696e67206372656174696f6e2074797065206d75737420626520677281526b06561746572207468616e20360a41b602082015260400192915050565b6000612c2160188361322d565b7f7265636569766572206164647265737320696e76616c69640000000000000000815260200192915050565b6000612c5a60158361322d565b7434b73b30b634b21030b236b4b71036b0b730b3b2b960591b815260200192915050565b6000612c8b60188361322d565b7f41646d696e206d616e61676572206973206e6f74207365740000000000000000815260200192915050565b6000612cc460108361322d565b6f0aadcdac2e8c6d0cac840d8cadccee8d60831b815260200192915050565b6000612cf060198361322d565b7f4c6f636b6564534f56206164647265737320696e76616c696400000000000000815260200192915050565b6000612d2960178361322d565b7f7374616b696e67206164647265737320696e76616c6964000000000000000000815260200192915050565b6000612d62600f8361322d565b6e1d1c985b9cd9995c8819985a5b1959608a1b815260200192915050565b6000612d8d60138361322d565b7214d3d5881859191c995cdcc81a5b9d985b1a59606a1b815260200192915050565b6000612dbc601683613236565b756b65792e61646d696e2e6d616e616765722e726f6c6560501b815260160192915050565b80546060830190612df181613280565b612dfb85826128da565b50612e0581613293565b612e126020860182612e83565b50612e1c816132a6565b61133a6040860182612e60565b80516060830190612e3a8482612e69565b506020820151612e4d6020850182612e69565b5060408201516105ab6040850182612861565b61286a81613241565b61286a81613272565b61286a612e7e82613272565b613272565b61286a8161324d565b6000612e988288612870565b601482019150612ea88287612e72565b602082019150612eb88286612e72565b602082019150612ec88285612e72565b602082019150612ed88284612e72565b5060200195945050505050565b60006117ee82612daf565b602081016117ee8284612861565b60e08101612f0c828a612861565b612f196020830189612861565b612f266040830188612861565b612f336060830187612e69565b612f406080830186612e69565b612f4d60a0830185612861565b612f5a60c0830184612861565b98975050505050505050565b60408101612f748285612861565b612f816020830184612e69565b9392505050565b60a08101612f968288612861565b612fa36020830187612e69565b612fb06040830186612e69565b612fbd6060830185612e69565b612fca6080830184612e69565b9695505050505050565b60208082528101612f818184612881565b602081016117ee82846128da565b6060810161300182866128da565b61300e6020830185612e83565b61054e6040830184612e60565b602081016117ee82846128e3565b602080825281016117ee816128ec565b602080825281016117ee81612925565b602080825281016117ee81612950565b602080825281016117ee81612989565b602080825281016117ee816129c2565b602080825281016117ee81612a0a565b602080825281016117ee81612a43565b602080825281016117ee81612a6d565b602080825281016117ee81612aa6565b602080825281016117ee81612ad0565b602080825281016117ee81612b09565b602080825281016117ee81612b59565b602080825281016117ee81612b81565b602080825281016117ee81612bc6565b602080825281016117ee81612c14565b602080825281016117ee81612c4d565b602080825281016117ee81612c7e565b602080825281016117ee81612cb7565b602080825281016117ee81612ce3565b602080825281016117ee81612d1c565b602080825281016117ee81612d55565b602080825281016117ee81612d80565b606081016117ee8284612de1565b602081016117ee8284612e69565b60408101612f748285612e69565b606081016131c18286612e69565b6131ce6020830185612e69565b61054e6040830184612861565b60405181810167ffffffffffffffff811182821017156131fa57600080fd5b604052919050565b600067ffffffffffffffff82111561321957600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b919050565b60ff1690565b6001600160801b031690565b63ffffffff1690565b60006117ee82613266565b151590565b6001600160a01b031690565b90565b60006117ee82613256565b60006117ee61328e83613272565b61323b565b60006117ee6132a1836132d1565b61324d565b60006117ee6132b4836132cb565b613241565b60006117ee8260006117ee8260601b90565b60281c90565b60081c90565b6132e081613256565b811461073157600080fd5b6132e081613261565b6132e081613241565b6132e081613272565b6132e08161324d56fea365627a7a72315820dc2ada767f6100aeabb2dd76de36097a63988151b323d89b951fad1348d343756c6578706572696d656e74616cf564736f6c63430005110040", "devdoc": { "methods": { "addAdmin(address)": { @@ -1079,6 +1162,9 @@ "_vestingCreationType": "the type of vesting created(e.g. Origin, Bug Bounty etc.)" } }, + "getAdminManager()": { + "return": "Address of admin manager." + }, "getVesting(address)": { "details": "Calls a public getVestingAddr function with cliff and duration. This is to accomodate the existing logic for LockedSOVWe need to use LockedSOV.changeRegistryCliffAndDuration function very judiciouslyvestingCreationType 0 - LockedSOV", "params": { @@ -1113,6 +1199,11 @@ "_admin": "The addresses of the account to revoke permissions." } }, + "setAdminManager(address)": { + "params": { + "_newAdminManager": "The addresses of the account to grant permissions." + } + }, "setVestingFactory(address)": { "params": { "_vestingFactory": "the address of vesting factory contract" @@ -1155,6 +1246,9 @@ "createVestingAddr(address,uint256,uint256,uint256,uint256)": { "notice": "creates Vesting contract" }, + "getAdminManager()": { + "notice": "Return address of the admin manager." + }, "getTeamVesting(address,uint256,uint256,uint256)": { "notice": "returns team vesting contract address for the given token owner, cliff, duration" }, @@ -1179,6 +1273,12 @@ "removeAdmin(address)": { "notice": "Remove account from ACL." }, + "removeAdminManager()": { + "notice": "Set admin manager to 0 address." + }, + "setAdminManager(address)": { + "notice": "Set new admin manager." + }, "setVestingFactory(address)": { "notice": "sets vesting factory address" }, @@ -1193,7 +1293,7 @@ "storageLayout": { "storage": [ { - "astId": 52747, + "astId": 54561, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "_initialized", "offset": 0, @@ -1201,7 +1301,7 @@ "type": "t_bool" }, { - "astId": 52749, + "astId": 54563, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "_initializing", "offset": 1, @@ -1209,7 +1309,7 @@ "type": "t_bool" }, { - "astId": 52792, + "astId": 54606, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "_owner", "offset": 2, @@ -1217,7 +1317,7 @@ "type": "t_address" }, { - "astId": 59869, + "astId": 62325, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "admins", "offset": 0, @@ -1225,31 +1325,31 @@ "type": "t_mapping(t_address,t_bool)" }, { - "astId": 30484, + "astId": 31932, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingFactory", "offset": 0, "slot": "2", - "type": "t_contract(IVestingFactory)24652" + "type": "t_contract(IVestingFactory)26100" }, { - "astId": 30486, + "astId": 31934, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "lockedSOV", "offset": 0, "slot": "3", - "type": "t_contract(ILockedSOV)33603" + "type": "t_contract(ILockedSOV)35121" }, { - "astId": 30489, + "astId": 31937, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingRegistries", "offset": 0, "slot": "4", - "type": "t_array(t_contract(IVestingRegistry)24688)dyn_storage" + "type": "t_array(t_contract(IVestingRegistry)26136)dyn_storage" }, { - "astId": 30491, + "astId": 31939, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "SOV", "offset": 0, @@ -1257,7 +1357,7 @@ "type": "t_address" }, { - "astId": 30493, + "astId": 31941, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "staking", "offset": 0, @@ -1265,7 +1365,7 @@ "type": "t_address" }, { - "astId": 30495, + "astId": 31943, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "feeSharingCollector", "offset": 0, @@ -1273,7 +1373,7 @@ "type": "t_address" }, { - "astId": 30497, + "astId": 31945, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingOwner", "offset": 0, @@ -1281,15 +1381,15 @@ "type": "t_address" }, { - "astId": 30511, + "astId": 31959, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestings", "offset": 0, "slot": "9", - "type": "t_mapping(t_uint256,t_struct(Vesting)30507_storage)" + "type": "t_mapping(t_uint256,t_struct(Vesting)31955_storage)" }, { - "astId": 30516, + "astId": 31964, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingsOf", "offset": 0, @@ -1297,7 +1397,7 @@ "type": "t_mapping(t_address,t_array(t_uint256)dyn_storage)" }, { - "astId": 30520, + "astId": 31968, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "isVesting", "offset": 0, @@ -1305,12 +1405,12 @@ "type": "t_mapping(t_address,t_bool)" }, { - "astId": 30531, + "astId": 31979, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingCreationAndTypes", "offset": 0, "slot": "12", - "type": "t_mapping(t_address,t_struct(VestingCreationAndTypeDetails)30527_storage)" + "type": "t_mapping(t_address,t_struct(VestingCreationAndTypeDetails)31975_storage)" } ], "types": { @@ -1319,8 +1419,8 @@ "label": "address", "numberOfBytes": "20" }, - "t_array(t_contract(IVestingRegistry)24688)dyn_storage": { - "base": "t_contract(IVestingRegistry)24688", + "t_array(t_contract(IVestingRegistry)26136)dyn_storage": { + "base": "t_contract(IVestingRegistry)26136", "encoding": "dynamic_array", "label": "contract IVestingRegistry[]", "numberOfBytes": "32" @@ -1336,17 +1436,17 @@ "label": "bool", "numberOfBytes": "1" }, - "t_contract(ILockedSOV)33603": { + "t_contract(ILockedSOV)35121": { "encoding": "inplace", "label": "contract ILockedSOV", "numberOfBytes": "20" }, - "t_contract(IVestingFactory)24652": { + "t_contract(IVestingFactory)26100": { "encoding": "inplace", "label": "contract IVestingFactory", "numberOfBytes": "20" }, - "t_contract(IVestingRegistry)24688": { + "t_contract(IVestingRegistry)26136": { "encoding": "inplace", "label": "contract IVestingRegistry", "numberOfBytes": "20" @@ -1365,26 +1465,26 @@ "numberOfBytes": "32", "value": "t_bool" }, - "t_mapping(t_address,t_struct(VestingCreationAndTypeDetails)30527_storage)": { + "t_mapping(t_address,t_struct(VestingCreationAndTypeDetails)31975_storage)": { "encoding": "mapping", "key": "t_address", "label": "mapping(address => struct VestingRegistryStorage.VestingCreationAndTypeDetails)", "numberOfBytes": "32", - "value": "t_struct(VestingCreationAndTypeDetails)30527_storage" + "value": "t_struct(VestingCreationAndTypeDetails)31975_storage" }, - "t_mapping(t_uint256,t_struct(Vesting)30507_storage)": { + "t_mapping(t_uint256,t_struct(Vesting)31955_storage)": { "encoding": "mapping", "key": "t_uint256", "label": "mapping(uint256 => struct VestingRegistryStorage.Vesting)", "numberOfBytes": "32", - "value": "t_struct(Vesting)30507_storage" + "value": "t_struct(Vesting)31955_storage" }, - "t_struct(Vesting)30507_storage": { + "t_struct(Vesting)31955_storage": { "encoding": "inplace", "label": "struct VestingRegistryStorage.Vesting", "members": [ { - "astId": 30502, + "astId": 31950, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingType", "offset": 0, @@ -1392,7 +1492,7 @@ "type": "t_uint256" }, { - "astId": 30504, + "astId": 31952, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingCreationType", "offset": 0, @@ -1400,7 +1500,7 @@ "type": "t_uint256" }, { - "astId": 30506, + "astId": 31954, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingAddress", "offset": 0, @@ -1410,12 +1510,12 @@ ], "numberOfBytes": "96" }, - "t_struct(VestingCreationAndTypeDetails)30527_storage": { + "t_struct(VestingCreationAndTypeDetails)31975_storage": { "encoding": "inplace", "label": "struct VestingRegistryStorage.VestingCreationAndTypeDetails", "members": [ { - "astId": 30522, + "astId": 31970, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "isSet", "offset": 0, @@ -1423,7 +1523,7 @@ "type": "t_bool" }, { - "astId": 30524, + "astId": 31972, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingType", "offset": 1, @@ -1431,7 +1531,7 @@ "type": "t_uint32" }, { - "astId": 30526, + "astId": 31974, "contract": "contracts/governance/Vesting/VestingRegistryLogic.sol:VestingRegistryLogic", "label": "vestingCreationType", "offset": 5, diff --git a/deployment/deployments/rskSovrynTestnet/solcInputs/757b9c0328cdd77755520b9ff358f21a.json b/deployment/deployments/rskSovrynTestnet/solcInputs/757b9c0328cdd77755520b9ff358f21a.json new file mode 100644 index 000000000..871d2af6f --- /dev/null +++ b/deployment/deployments/rskSovrynTestnet/solcInputs/757b9c0328cdd77755520b9ff358f21a.json @@ -0,0 +1,714 @@ +{ + "language": "Solidity", + "sources": { + "contracts/connectors/loantoken/AdvancedToken.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"./AdvancedTokenStorage.sol\";\n\n/**\n * @title Advanced Token contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized margin\n * trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * AdvancedToken implements standard ERC-20 approval, mint and burn token functionality.\n * Logic (AdvancedToken) is kept aside from storage (AdvancedTokenStorage).\n *\n * For example, LoanTokenLogicDai contract uses AdvancedToken::_mint() to mint\n * its Loan Dai iTokens.\n * */\ncontract AdvancedToken is AdvancedTokenStorage {\n using SafeMath for uint256;\n\n /**\n * @notice Set an amount as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n *\n * @param _spender The account address that will be able to spend the tokens.\n * @param _value The amount of tokens allowed to spend.\n * */\n function approve(address _spender, uint256 _value) public returns (bool) {\n allowed[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n /**\n * @notice The iToken minting process. Meant to issue Loan iTokens.\n * Lenders are able to open an iToken position, by minting them.\n * This function is called by LoanTokenLogicStandard::_mintToken\n * @param _to The recipient of the minted tTokens.\n * @param _tokenAmount The amount of iTokens to be minted.\n * @param _assetAmount The amount of lended tokens (asset to lend).\n * @param _price The price of the lended tokens.\n * @return The updated balance of the recipient.\n * */\n function _mint(\n address _to,\n uint256 _tokenAmount,\n uint256 _assetAmount,\n uint256 _price\n ) internal returns (uint256) {\n require(_to != address(0), \"15\");\n\n uint256 _balance = balances[_to].add(_tokenAmount);\n balances[_to] = _balance;\n\n totalSupply_ = totalSupply_.add(_tokenAmount);\n\n emit Mint(_to, _tokenAmount, _assetAmount, _price);\n emit Transfer(address(0), _to, _tokenAmount);\n\n return _balance;\n }\n\n /**\n * @notice The iToken burning process. Meant to destroy Loan iTokens.\n * Lenders are able to close an iToken position, by burning them.\n * This function is called by LoanTokenLogicStandard::_burnToken\n * @param _who The owner of the iTokens to burn.\n * @param _tokenAmount The amount of iTokens to burn.\n * @param _assetAmount The amount of lended tokens.\n * @param _price The price of the lended tokens.\n * @return The updated balance of the iTokens owner.\n * */\n function _burn(\n address _who,\n uint256 _tokenAmount,\n uint256 _assetAmount,\n uint256 _price\n ) internal returns (uint256) {\n //bzx compare\n //TODO: Unit test\n uint256 _balance = balances[_who].sub(_tokenAmount, \"16\");\n\n // a rounding error may leave dust behind, so we clear this out\n if (_balance <= 10) {\n // We can't leave such small balance quantities.\n _tokenAmount = _tokenAmount.add(_balance);\n _balance = 0;\n }\n balances[_who] = _balance;\n\n totalSupply_ = totalSupply_.sub(_tokenAmount);\n\n emit Burn(_who, _tokenAmount, _assetAmount, _price);\n emit Transfer(_who, address(0), _tokenAmount);\n return _balance;\n }\n}\n" + }, + "contracts/connectors/loantoken/AdvancedTokenStorage.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"./LoanTokenBase.sol\";\n\n/**\n * @title Advanced Token Storage contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * AdvancedTokenStorage implements standard ERC-20 getters functionality:\n * totalSupply, balanceOf, allowance and some events.\n * iToken logic is divided into several contracts AdvancedToken,\n * AdvancedTokenStorage and LoanTokenBase.\n * */\ncontract AdvancedTokenStorage is LoanTokenBase {\n using SafeMath for uint256;\n\n /* Events */\n\n /// topic: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /// topic: 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /// topic: 0x628e75c63c1873bcd3885f7aee9f58ee36f60dc789b2a6b3a978c4189bc548ba\n event AllowanceUpdate(\n address indexed owner,\n address indexed spender,\n uint256 valueBefore,\n uint256 valueAfter\n );\n\n /// topic: 0xb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb\n event Mint(address indexed minter, uint256 tokenAmount, uint256 assetAmount, uint256 price);\n\n /// topic: 0x743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b4644\n event Burn(address indexed burner, uint256 tokenAmount, uint256 assetAmount, uint256 price);\n\n /// topic: 0xc688ff9bd4a1c369dd44c5cf64efa9db6652fb6b280aa765cd43f17d256b816e\n event FlashBorrow(address borrower, address target, address loanToken, uint256 loanAmount);\n\n /* Storage */\n\n mapping(address => uint256) internal balances;\n mapping(address => mapping(address => uint256)) internal allowed;\n uint256 internal totalSupply_;\n\n /* Functions */\n\n /**\n * @notice Get the total supply of iTokens.\n * @return The total number of iTokens in existence as of now.\n * */\n function totalSupply() public view returns (uint256) {\n return totalSupply_;\n }\n\n /**\n * @notice Get the amount of iTokens owned by an account.\n * @param _owner The account owner of the iTokens.\n * @return The number of iTokens an account owns.\n * */\n function balanceOf(address _owner) public view returns (uint256) {\n return balances[_owner];\n }\n\n /**\n * @notice Get the amount of iTokens allowed to be spent by a\n * given account on behalf of the owner.\n * @param _owner The account owner of the iTokens.\n * @param _spender The account allowed to send the iTokens.\n * @return The number of iTokens an account is allowing the spender\n * to send on its behalf.\n * */\n function allowance(address _owner, address _spender) public view returns (uint256) {\n return allowed[_owner][_spender];\n }\n}\n" + }, + "contracts/connectors/loantoken/interfaces/FeedsLike.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\ninterface FeedsLike {\n function queryRate(address sourceTokenAddress, address destTokenAddress)\n external\n view\n returns (uint256 rate, uint256 precision);\n}\n" + }, + "contracts/connectors/loantoken/interfaces/ProtocolLike.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../lib/MarginTradeStructHelpers.sol\";\n\ninterface ProtocolLike {\n function borrowOrTradeFromPool(\n bytes32 loanParamsId,\n bytes32 loanId, // if 0, start a new loan\n bool isTorqueLoan,\n uint256 initialMargin,\n MarginTradeStructHelpers.SentAddresses calldata sentAddresses,\n // lender: must match loan if loanId provided\n // borrower: must match loan if loanId provided\n // receiver: receiver of funds (address(0) assumes borrower address)\n // manager: delegated manager of loan unless address(0)\n MarginTradeStructHelpers.SentAmounts calldata sentValues,\n // newRate: new loan interest rate\n // newPrincipal: new loan size (borrowAmount + any borrowed interest)\n // torqueInterest: new amount of interest to escrow for Torque loan (determines initial loan length)\n // loanTokenReceived: total loanToken deposit (amount not sent to borrower in the case of Torque loans)\n // collateralTokenReceived: total collateralToken deposit\n bytes calldata loanDataBytes\n ) external payable returns (uint256 newPrincipal, uint256 newCollateral);\n\n function getTotalPrincipal(address lender, address loanToken) external view returns (uint256);\n\n function withdrawAccruedInterest(address loanToken) external;\n\n function getLenderInterestData(address lender, address loanToken)\n external\n view\n returns (\n uint256 interestPaid,\n uint256 interestPaidDate,\n uint256 interestOwedPerDay,\n uint256 interestUnPaid,\n uint256 interestFeePercent,\n uint256 principalTotal\n );\n\n function priceFeeds() external view returns (address);\n\n function getEstimatedMarginExposure(\n address loanToken,\n address collateralToken,\n uint256 loanTokenSent,\n uint256 collateralTokenSent,\n uint256 interestRate,\n uint256 newPrincipal\n ) external view returns (uint256);\n\n function getRequiredCollateral(\n address loanToken,\n address collateralToken,\n uint256 newPrincipal,\n uint256 marginAmount,\n bool isTorqueLoan\n ) external view returns (uint256 collateralAmountRequired);\n\n function getBorrowAmount(\n address loanToken,\n address collateralToken,\n uint256 collateralTokenAmount,\n uint256 marginAmount,\n bool isTorqueLoan\n ) external view returns (uint256 borrowAmount);\n\n function isLoanPool(address loanPool) external view returns (bool);\n\n function lendingFeePercent() external view returns (uint256);\n\n function getSwapExpectedReturn(\n address sourceToken,\n address destToken,\n uint256 sourceTokenAmount\n ) external view returns (uint256);\n\n function borrowerNonce(address) external view returns (uint256);\n\n function closeWithSwap(\n bytes32 loanId,\n address receiver,\n uint256 swapAmount, // denominated in collateralToken\n bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken\n bytes calldata // for future use /*loanDataBytes*/\n )\n external\n returns (\n uint256 loanCloseAmount,\n uint256 withdrawAmount,\n address withdrawToken\n );\n\n function closeWithDeposit(\n bytes32 loanId,\n address receiver,\n uint256 depositAmount // denominated in loanToken\n )\n external\n payable\n returns (\n uint256 loanCloseAmount,\n uint256 withdrawAmount,\n address withdrawToken\n );\n}\n" + }, + "contracts/connectors/loantoken/interfaces/ProtocolSettingsLike.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../../core/objects/LoanParamsStruct.sol\";\n\ninterface ProtocolSettingsLike {\n function setupLoanParams(LoanParamsStruct.LoanParams[] calldata loanParamsList)\n external\n returns (bytes32[] memory loanParamsIdList);\n\n function disableLoanParams(bytes32[] calldata loanParamsIdList) external;\n\n function minInitialMargin(bytes32 loanParamsId) external view returns (uint256);\n}\n" + }, + "contracts/connectors/loantoken/lib/MarginTradeStructHelpers.sol": { + "content": "pragma solidity 0.5.17;\n\nlibrary MarginTradeStructHelpers {\n struct SentAddresses {\n address lender;\n address borrower;\n address receiver;\n address manager;\n }\n\n struct SentAmounts {\n uint256 interestRate;\n uint256 newPrincipal;\n uint256 interestInitialAmount;\n uint256 loanTokenSent;\n uint256 collateralTokenSent;\n uint256 minEntryPrice;\n uint256 loanToCollateralSwapRate;\n uint256 interestDuration;\n uint256 entryLeverage;\n }\n}\n" + }, + "contracts/connectors/loantoken/LoanToken.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"./AdvancedTokenStorage.sol\";\n\n/**\n * @title Loan Token contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * A loan token (iToken) is created as a proxy to an upgradable token contract.\n *\n * Examples of loan tokens on Sovryn are iRBTC, iDOC, iUSDT, iBPro,\n * iSOV (near future).\n *\n * Lenders receive iTokens that collect interest from the lending pool\n * which they can redeem by withdrawing them. The i in iToken stands for interest.\n *\n * Do not confuse iTokens with underlying tokens. iDOC is an iToken (loan token)\n * whilest DOC is the underlying token (currency).\n *\n * @dev TODO: can I change this proxy to EIP-1822 proxy standard, please.\n * https://eips.ethereum.org/EIPS/eip-1822. It's really hard to work with this.\n * */\ncontract LoanToken is AdvancedTokenStorage {\n /// @dev It is important to maintain the variables order so the delegate\n /// calls can access sovrynContractAddress and wrbtcTokenAddress\n address public sovrynContractAddress;\n address public wrbtcTokenAddress;\n address internal target_;\n address public admin;\n\n /**\n * @notice Deploy loan token proxy.\n * Sets ERC20 parameters of the token.\n *\n * @param _newOwner The address of the new owner.\n * @param _newTarget The address of the new target contract instance.\n * @param _sovrynContractAddress The address of the new sovrynContract instance.\n * @param _wrbtcTokenAddress The address of the new wrBTC instance.\n * */\n constructor(\n address _newOwner,\n address _newTarget,\n address _sovrynContractAddress,\n address _wrbtcTokenAddress\n ) public {\n transferOwnership(_newOwner);\n _setTarget(_newTarget);\n _setSovrynContractAddress(_sovrynContractAddress);\n _setWrbtcTokenAddress(_wrbtcTokenAddress);\n }\n\n /**\n * @notice Fallback function performs a delegate call\n * to the actual implementation address is pointing this proxy.\n * Returns whatever the implementation call returns.\n * */\n function() external payable {\n if (gasleft() <= 2300) {\n return;\n }\n\n address target = target_;\n bytes memory data = msg.data;\n assembly {\n let result := delegatecall(gas, target, add(data, 0x20), mload(data), 0, 0)\n let size := returndatasize\n let ptr := mload(0x40)\n returndatacopy(ptr, 0, size)\n switch result\n case 0 {\n revert(ptr, size)\n }\n default {\n return(ptr, size)\n }\n }\n }\n\n /**\n * @notice Public owner setter for target address.\n * @dev Calls internal setter.\n * @param _newTarget The address of the new target contract instance.\n * */\n function setTarget(address _newTarget) public onlyOwner {\n _setTarget(_newTarget);\n }\n\n /**\n * @notice Internal setter for target address.\n * @param _newTarget The address of the new target contract instance.\n * */\n function _setTarget(address _newTarget) internal {\n require(Address.isContract(_newTarget), \"target not a contract\");\n target_ = _newTarget;\n }\n\n /**\n * @notice Internal setter for sovrynContract address.\n * @param _sovrynContractAddress The address of the new sovrynContract instance.\n * */\n function _setSovrynContractAddress(address _sovrynContractAddress) internal {\n require(Address.isContract(_sovrynContractAddress), \"sovryn not a contract\");\n sovrynContractAddress = _sovrynContractAddress;\n }\n\n /**\n * @notice Internal setter for wrBTC address.\n * @param _wrbtcTokenAddress The address of the new wrBTC instance.\n * */\n function _setWrbtcTokenAddress(address _wrbtcTokenAddress) internal {\n require(Address.isContract(_wrbtcTokenAddress), \"wrbtc not a contract\");\n wrbtcTokenAddress = _wrbtcTokenAddress;\n }\n\n /**\n * @notice Public owner cloner for pointed loan token.\n * Sets ERC20 parameters of the token.\n *\n * @dev TODO: add check for double init.\n * idk but init usually can be called only once.\n *\n * @param _loanTokenAddress The address of the pointed loan token instance.\n * @param _name The ERC20 token name.\n * @param _symbol The ERC20 token symbol.\n * */\n function initialize(\n address _loanTokenAddress,\n string memory _name,\n string memory _symbol\n ) public onlyOwner {\n loanTokenAddress = _loanTokenAddress;\n\n name = _name;\n symbol = _symbol;\n decimals = IERC20(loanTokenAddress).decimals();\n\n initialPrice = 10**18; /// starting price of 1\n }\n}\n" + }, + "contracts/connectors/loantoken/LoanTokenBase.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../../openzeppelin/SafeMath.sol\";\nimport \"../../openzeppelin/SignedSafeMath.sol\";\nimport \"../../openzeppelin/ReentrancyGuard.sol\";\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"../../openzeppelin/Address.sol\";\nimport \"../../interfaces/IWrbtcERC20.sol\";\nimport \"./Pausable.sol\";\nimport \"../../reentrancy/SharedReentrancyGuard.sol\";\n\n/**\n * @title Loan Token Base contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized margin\n * trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * Specific loan related storage for iTokens.\n *\n * An loan token or iToken is a representation of a user funds in the pool and the\n * interest they've earned. The redemption value of iTokens continually increase\n * from the accretion of interest paid into the lending pool by borrowers. The user\n * can sell iTokens to exit its position. The user might potentially use them as\n * collateral wherever applicable.\n *\n * There are three main tokens in the bZx system, iTokens, pTokens, and BZRX tokens.\n * The bZx system of lending and borrowing depends on iTokens and pTokens, and when\n * users lend or borrow money on bZx, their crypto assets go into or come out of\n * global liquidity pools, which are pools of funds shared between many different\n * exchanges. When lenders supply funds into the global liquidity pools, they\n * automatically receive iTokens; When users borrow money to open margin trading\n * positions, they automatically receive pTokens. The system is also designed to\n * use the BZRX tokens, which are only used to pay fees on the network currently.\n * */\ncontract LoanTokenBase is ReentrancyGuard, SharedReentrancyGuard, Ownable, Pausable {\n uint256 internal constant WEI_PRECISION = 10**18;\n uint256 internal constant WEI_PERCENT_PRECISION = 10**20;\n\n int256 internal constant sWEI_PRECISION = 10**18;\n\n /// @notice Standard ERC-20 properties\n string public name;\n string public symbol;\n uint8 public decimals;\n\n /// @notice The address of the loan token (asset to lend) instance.\n address public loanTokenAddress;\n\n uint256 public baseRate;\n uint256 public rateMultiplier;\n uint256 public lowUtilBaseRate;\n uint256 public lowUtilRateMultiplier;\n\n uint256 public targetLevel;\n uint256 public kinkLevel;\n uint256 public maxScaleRate;\n\n uint256 internal _flTotalAssetSupply;\n uint256 public checkpointSupply;\n uint256 public initialPrice;\n\n /// uint88 for tight packing -> 8 + 88 + 160 = 256\n uint88 internal lastSettleTime_;\n\n /// Mapping of keccak256(collateralToken, isTorqueLoan) to loanParamsId.\n mapping(uint256 => bytes32) public loanParamsIds;\n\n /// Price of token at last user checkpoint.\n mapping(address => uint256) internal checkpointPrices_;\n\n // the maximum trading/borrowing/lending limit per token address\n mapping(address => uint256) public transactionLimit;\n // 0 -> no limit\n}\n" + }, + "contracts/connectors/loantoken/LoanTokenLogicBeacon.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../mixins/EnumerableBytes32Set.sol\";\nimport \"../../mixins/EnumerableBytes4Set.sol\";\nimport \"../../utils/PausableRole.sol\";\nimport \"../../openzeppelin/Address.sol\";\n\n/**\n * @title Loan Token Logic Beacon contract.\n *\n * @notice This contract stored the target logic implementation of LoanTokens which has the same logic implementation (LoanTokenLogicLM / LoanTokenLogicWrbtc)\n * Apart from storing the target logic implementation, this contract also has a pause functionality.\n * By implementing pause/unpause functionality in this beacon contract, we can pause the loan token that has the same Logic (LoanTokenLogicLM / LoanTokenLogicWrbtc) at one call.\n * Meanwhile the pause/unpause function in the LoanTokenLogicProxy is used to pause/unpause specific LoanToken\n */\n\ncontract LoanTokenLogicBeacon is PausableRole {\n using EnumerableBytes32Set for EnumerableBytes32Set.Bytes32Set; // enumerable map of bytes32 or addresses\n using EnumerableBytes4Set for EnumerableBytes4Set.Bytes4Set; // enumerable map of bytes4 or addresses\n\n mapping(bytes4 => address) private logicTargets;\n\n struct LoanTokenLogicModuleUpdate {\n address implementation; // address implementaion of the module\n uint256 updateTimestamp; // time of update\n }\n\n mapping(bytes32 => LoanTokenLogicModuleUpdate[]) public moduleUpgradeLog; /** the module name as the key */\n\n mapping(bytes32 => uint256) public activeModuleIndex; /** To store the current active index log for module */\n\n mapping(bytes32 => EnumerableBytes4Set.Bytes4Set) private activeFuncSignatureList; /** Store the current active function signature */\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n * This is the overriden function from the pausable contract, so that we can use custom error message.\n */\n modifier whenNotPaused() {\n require(!_paused, \"LoanTokenLogicBeacon:paused mode\");\n _;\n }\n\n /**\n * @notice Register the loanTokenModule (LoanTokenSettingsLowerAdmin, LoanTokenLogicLM / LoanTokenLogicWrbtc, etc)\n *\n * @dev This function will store the updated protocol module to the storage (For rollback purposes)\n *\n * @param loanTokenModuleAddress The module target address\n */\n function registerLoanTokenModule(address loanTokenModuleAddress) external onlyOwner {\n bytes32 moduleName = _registerLoanTokenModule(loanTokenModuleAddress);\n\n // Store the upgrade to the log\n moduleUpgradeLog[moduleName].push(\n LoanTokenLogicModuleUpdate(loanTokenModuleAddress, block.timestamp)\n );\n activeModuleIndex[moduleName] = moduleUpgradeLog[moduleName].length - 1;\n }\n\n /**\n * @notice Register the loanTokenModule (LoanTokenSettingsLowerAdmin, LoanTokenLogicLM / LoanTokenLogicWrbtc, etc)\n *\n * @dev This registration will require target contract to have the exact function getListFunctionSignatures() which will return functionSignatureList and the moduleName in bytes32\n *\n * @param loanTokenModuleAddress the target logic of the loan token module\n *\n * @return the module name\n */\n function _registerLoanTokenModule(address loanTokenModuleAddress) private returns (bytes32) {\n require(\n Address.isContract(loanTokenModuleAddress),\n \"LoanTokenModuleAddress is not a contract\"\n );\n\n // Get the list of function signature on this loanTokenModulesAddress\n (bytes4[] memory functionSignatureList, bytes32 moduleName) =\n ILoanTokenLogicModules(loanTokenModuleAddress).getListFunctionSignatures();\n\n /// register / update the module function signature address implementation\n for (uint256 i; i < functionSignatureList.length; i++) {\n require(functionSignatureList[i] != bytes4(0x0), \"ERR_EMPTY_FUNC_SIGNATURE\");\n logicTargets[functionSignatureList[i]] = loanTokenModuleAddress;\n if (!activeFuncSignatureList[moduleName].contains(functionSignatureList[i]))\n activeFuncSignatureList[moduleName].addBytes4(functionSignatureList[i]);\n }\n\n /// delete the \"removed\" module function signature in the current implementation\n bytes4[] memory activeSignatureListEnum =\n activeFuncSignatureList[moduleName].enumerate(\n 0,\n activeFuncSignatureList[moduleName].length()\n );\n for (uint256 i; i < activeSignatureListEnum.length; i++) {\n bytes4 activeSigBytes = activeSignatureListEnum[i];\n if (logicTargets[activeSigBytes] != loanTokenModuleAddress) {\n logicTargets[activeSigBytes] = address(0);\n activeFuncSignatureList[moduleName].removeBytes4(activeSigBytes);\n }\n }\n\n return moduleName;\n }\n\n /**\n * @dev get all active function signature list based on the module name.\n *\n * @param moduleName in bytes32.\n *\n * @return the array of function signature.\n */\n function getActiveFuncSignatureList(bytes32 moduleName)\n public\n view\n returns (bytes4[] memory signatureList)\n {\n signatureList = activeFuncSignatureList[moduleName].enumerate(\n 0,\n activeFuncSignatureList[moduleName].length()\n );\n return signatureList;\n }\n\n /**\n * @dev Get total length of the module upgrade log.\n *\n * @param moduleName in bytes32.\n *\n * @return length of module upgrade log.\n */\n function getModuleUpgradeLogLength(bytes32 moduleName) external view returns (uint256) {\n return moduleUpgradeLog[moduleName].length;\n }\n\n /**\n * @notice This function will rollback particular module to the spesific index / version of deployment\n *\n * @param moduleName Name of module in bytes32 format\n * @param index index / version of previous deployment\n */\n function rollback(bytes32 moduleName, uint256 index) external onlyOwner {\n address loanTokenModuleAddress = moduleUpgradeLog[moduleName][index].implementation;\n moduleName = _registerLoanTokenModule(loanTokenModuleAddress);\n activeModuleIndex[moduleName] = index;\n }\n\n /**\n * @notice External getter for target addresses.\n * @param sig The signature.\n * @return The address for a given signature.\n * */\n function getTarget(bytes4 sig) external view whenNotPaused returns (address) {\n return logicTargets[sig];\n }\n}\n\ninterface ILoanTokenLogicModules {\n function getListFunctionSignatures()\n external\n pure\n returns (bytes4[] memory, bytes32 moduleName);\n}\n" + }, + "contracts/connectors/loantoken/LoanTokenLogicProxy.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./AdvancedTokenStorage.sol\";\nimport \"../../openzeppelin/Initializable.sol\";\n\n/**\n * @title Loan Token Logic Proxy contract.\n *\n * @notice This contract contains the proxy functionality and it will query the logic target from LoanTokenLogicBeacon\n * This contract will also has the pause/unpause functionality. The purpose of this pausability is so that we can pause/unpause from the loan token level.\n *\n */\ncontract LoanTokenLogicProxy is AdvancedTokenStorage {\n /**\n * @notice PLEASE DO NOT ADD ANY VARIABLES HERE UNLESS FOR SPESIFIC SLOT\n */\n\n /// ------------- MUST BE THE SAME AS IN LoanToken CONTRACT -------------------\n address public sovrynContractAddress;\n address public wrbtcTokenAddress;\n address public target_;\n address public admin;\n /// ------------- END MUST BE THE SAME AS IN LoanToken CONTRACT -------------------\n\n /**\n * @notice PLEASE DO NOT ADD ANY VARIABLES HERE UNLESS FOR SPESIFIC SLOT (CONSTANT / IMMUTABLE)\n */\n\n bytes32 internal constant LOAN_TOKEN_LOGIC_BEACON_ADDRESS_SLOT =\n keccak256(\"LOAN_TOKEN_LOGIC_BEACON_ADDRESS_SLOT\");\n\n modifier onlyAdmin() {\n require(isOwner(), \"LoanTokenLogicProxy:unauthorized\");\n _;\n }\n\n /**\n * @notice Fallback function performs a logic implementation address query to LoanTokenLogicBeacon and then do delegate call to that query result address.\n * Returns whatever the implementation call returns.\n * */\n function() external payable {\n // query the logic target implementation address from the LoanTokenLogicBeacon\n address target = ILoanTokenLogicBeacon(_beaconAddress()).getTarget(msg.sig);\n require(target != address(0), \"LoanTokenLogicProxy:target not active\");\n\n bytes memory data = msg.data;\n assembly {\n let result := delegatecall(gas, target, add(data, 0x20), mload(data), 0, 0)\n let size := returndatasize\n let ptr := mload(0x40)\n returndatacopy(ptr, 0, size)\n switch result\n case 0 {\n revert(ptr, size)\n }\n default {\n return(ptr, size)\n }\n }\n }\n\n /**\n * @dev Returns the current Loan Token logic Beacon.\n * @return Address of the current LoanTokenLogicBeacon.\n */\n function _beaconAddress() internal view returns (address beaconAddress) {\n bytes32 slot = LOAN_TOKEN_LOGIC_BEACON_ADDRESS_SLOT;\n assembly {\n beaconAddress := sload(slot)\n }\n }\n\n /**\n * @return The address of the current LoanTokenLogicBeacon.\n */\n function beaconAddress() external view returns (address) {\n return _beaconAddress();\n }\n\n /**\n * @dev Set/update the new beacon address.\n * @param _newBeaconAddress Address of the new LoanTokenLogicBeacon.\n */\n function _setBeaconAddress(address _newBeaconAddress) private {\n require(\n Address.isContract(_newBeaconAddress),\n \"Cannot set beacon address to a non-contract address\"\n );\n\n bytes32 slot = LOAN_TOKEN_LOGIC_BEACON_ADDRESS_SLOT;\n\n assembly {\n sstore(slot, _newBeaconAddress)\n }\n }\n\n /**\n * @dev External function to set the new LoanTokenLogicBeacon Address\n * @param _newBeaconAddress Address of the new LoanTokenLogicBeacon\n */\n function setBeaconAddress(address _newBeaconAddress) external onlyAdmin {\n _setBeaconAddress(_newBeaconAddress);\n }\n\n /**\n * @dev External function to return the LoanTokenLogicProxy of loan token (target of LoanToken contract).\n * Ideally this getter should be added in the LoanToken contract\n * but since LoanToken contract can't be changed, adding the getter in this contract will do\n * because it will use the context of LoanToken contract.\n *\n * @return target address of LoanToken contract\n */\n function getTarget() external view returns (address) {\n return target_;\n }\n}\n\ninterface ILoanTokenLogicBeacon {\n function getTarget(bytes4 functionSignature)\n external\n view\n returns (address logicTargetAddress);\n}\n" + }, + "contracts/connectors/loantoken/LoanTokenLogicShared.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./LoanTokenLogicStorage.sol\";\nimport \"./interfaces/ProtocolLike.sol\";\nimport \"./interfaces/FeedsLike.sol\";\nimport \"./interfaces/ProtocolSettingsLike.sol\";\nimport \"../../modules/interfaces/ProtocolAffiliatesInterface.sol\";\nimport \"../../farm/ILiquidityMining.sol\";\nimport \"../../governance/Staking/interfaces/IStaking.sol\";\nimport \"../../governance/Vesting/IVesting.sol\";\n\n/**\n * @dev This contract shares functions used by both LoanTokenLogicSplit and LoanTokenLogicStandard\n */\ncontract LoanTokenLogicShared is LoanTokenLogicStorage {\n using SafeMath for uint256;\n using SignedSafeMath for int256;\n\n /// DON'T ADD VARIABLES HERE, PLEASE\n\n /**\n * @notice Update the user's checkpoint price and profit so far.\n * In this loan token contract, whenever some tokens are minted or burned,\n * the _updateCheckpoints() function is invoked to update the stats to\n * reflect the balance changes.\n *\n * @param _user The user address.\n * @param _oldBalance The user's previous balance.\n * @param _newBalance The user's updated balance.\n * @param _currentPrice The current loan token price.\n * */\n function _updateCheckpoints(\n address _user,\n uint256 _oldBalance,\n uint256 _newBalance,\n uint256 _currentPrice\n ) internal {\n /// @dev keccak256(\"iToken_ProfitSoFar\")\n bytes32 slot = keccak256(abi.encodePacked(_user, iToken_ProfitSoFar));\n\n int256 _currentProfit;\n if (_newBalance == 0) {\n _currentPrice = 0;\n } else if (_oldBalance != 0) {\n _currentProfit = _profitOf(slot, _oldBalance, _currentPrice, checkpointPrices_[_user]);\n }\n\n assembly {\n sstore(slot, _currentProfit)\n }\n\n checkpointPrices_[_user] = _currentPrice;\n }\n\n /** INTERNAL FUNCTION */\n\n /**\n * @notice Transfer tokens, low level.\n * Checks allowance, updates sender and recipient balances\n * and updates checkpoints too.\n *\n * @param _from The tokens' owner.\n * @param _to The recipient of the tokens.\n * @param _value The amount of tokens sent.\n * @param _allowanceAmount The amount of tokens allowed to transfer.\n *\n * @return Success true/false.\n * */\n function _internalTransferFrom(\n address _from,\n address _to,\n uint256 _value,\n uint256 _allowanceAmount\n ) internal returns (bool) {\n if (_allowanceAmount != uint256(-1)) {\n allowed[_from][msg.sender] = _allowanceAmount.sub(_value, \"14\");\n /// @dev Allowance mapping update requires an event log\n emit AllowanceUpdate(_from, msg.sender, _allowanceAmount, allowed[_from][msg.sender]);\n }\n\n require(_to != address(0), \"15\");\n\n uint256 _balancesFrom = balances[_from];\n uint256 _balancesFromNew = _balancesFrom.sub(_value, \"16\");\n balances[_from] = _balancesFromNew;\n\n uint256 _balancesTo = balances[_to];\n uint256 _balancesToNew = _balancesTo.add(_value);\n balances[_to] = _balancesToNew;\n\n /// @dev Handle checkpoint update.\n uint256 _currentPrice = tokenPrice();\n\n //checkpoints are not being used by the smart contract logic itself, but just for external use (query the profit)\n //only update the checkpoints of a user if he's not depositing to / withdrawing from the lending pool\n if (_from != liquidityMiningAddress && _to != liquidityMiningAddress) {\n _updateCheckpoints(_from, _balancesFrom, _balancesFromNew, _currentPrice);\n _updateCheckpoints(_to, _balancesTo, _balancesToNew, _currentPrice);\n }\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n /**\n * @notice Profit calculation based on checkpoints of price.\n * @param slot The user slot.\n * @param _balance The user balance.\n * @param _currentPrice The current price of the loan token.\n * @param _checkpointPrice The price of the loan token on checkpoint.\n * @return The profit of a user.\n * */\n function _profitOf(\n bytes32 slot,\n uint256 _balance,\n uint256 _currentPrice,\n uint256 _checkpointPrice\n ) internal view returns (int256 profitSoFar) {\n if (_checkpointPrice == 0) {\n return 0;\n }\n\n assembly {\n profitSoFar := sload(slot)\n }\n\n profitSoFar = int256(_currentPrice)\n .sub(int256(_checkpointPrice))\n .mul(int256(_balance))\n .div(sWEI_PRECISION)\n .add(profitSoFar);\n }\n\n /**\n * @notice Loan token price calculation considering unpaid interests.\n * @return The loan token price.\n * */\n function tokenPrice() public view returns (uint256 price) {\n uint256 interestUnPaid;\n if (lastSettleTime_ != uint88(block.timestamp)) {\n (, interestUnPaid) = _getAllInterest();\n }\n\n return _tokenPrice(_totalAssetSupply(interestUnPaid));\n }\n\n /**\n * @notice Get the total amount of loan tokens on debt.\n * Calls protocol getTotalPrincipal function.\n * In the context of borrowing, principal is the initial size of a loan.\n * It can also be the amount still owed on a loan. If you take out a\n * $50,000 mortgage, for example, the principal is $50,000. If you pay off\n * $30,000, the principal balance now consists of the remaining $20,000.\n *\n * @return The total amount of loan tokens on debt.\n * */\n function totalAssetBorrow() public view returns (uint256) {\n return\n ProtocolLike(sovrynContractAddress).getTotalPrincipal(address(this), loanTokenAddress);\n }\n\n /** INTERNAL FUNCTION */\n\n /**\n * @notice .\n *\n * @param collateralTokenAddress The address of the token to be used as\n * collateral. Cannot be the loan token address.\n * @param sentAddresses The addresses to send tokens: lender, borrower,\n * receiver and manager.\n * @param sentAmounts The amounts to send to each address.\n * @param withdrawalAmount The amount of tokens to withdraw.\n *\n * @return msgValue The amount of rBTC sent minus the collateral on tokens.\n * */\n function _verifyTransfers(\n address collateralTokenAddress,\n MarginTradeStructHelpers.SentAddresses memory sentAddresses,\n MarginTradeStructHelpers.SentAmounts memory sentAmounts,\n uint256 withdrawalAmount\n ) internal returns (uint256 msgValue) {\n address _wrbtcToken = wrbtcTokenAddress;\n address _loanTokenAddress = loanTokenAddress;\n uint256 newPrincipal = sentAmounts.newPrincipal;\n uint256 loanTokenSent = sentAmounts.loanTokenSent;\n uint256 collateralTokenSent = sentAmounts.collateralTokenSent;\n\n require(_loanTokenAddress != collateralTokenAddress, \"26\");\n\n msgValue = msg.value;\n\n if (withdrawalAmount != 0) {\n /// withdrawOnOpen == true\n _safeTransfer(_loanTokenAddress, sentAddresses.receiver, withdrawalAmount, \"\");\n if (newPrincipal > withdrawalAmount) {\n _safeTransfer(\n _loanTokenAddress,\n sovrynContractAddress,\n newPrincipal - withdrawalAmount,\n \"\"\n );\n }\n } else {\n _safeTransfer(_loanTokenAddress, sovrynContractAddress, newPrincipal, \"27\");\n }\n /**\n * This is a critical piece of code!\n * rBTC are supposed to be held by the contract itself, while other tokens are being transfered from the sender directly.\n * */\n if (collateralTokenSent != 0) {\n if (\n collateralTokenAddress == _wrbtcToken &&\n msgValue != 0 &&\n msgValue >= collateralTokenSent\n ) {\n IWrbtc(_wrbtcToken).deposit.value(collateralTokenSent)();\n _safeTransfer(\n collateralTokenAddress,\n sovrynContractAddress,\n collateralTokenSent,\n \"28-a\"\n );\n msgValue -= collateralTokenSent;\n } else {\n _safeTransferFrom(\n collateralTokenAddress,\n msg.sender,\n sovrynContractAddress,\n collateralTokenSent,\n \"28-b\"\n );\n }\n }\n\n if (loanTokenSent != 0) {\n _safeTransferFrom(\n _loanTokenAddress,\n msg.sender,\n sovrynContractAddress,\n loanTokenSent,\n \"29\"\n );\n }\n }\n\n /**\n * @notice Withdraw loan token interests from protocol.\n * This function only operates once per block.\n * It asks protocol to withdraw accrued interests for the loan token.\n *\n * @dev Internal sync required on every loan trade before starting.\n * */\n function _settleInterest() internal {\n uint88 ts = uint88(block.timestamp);\n if (lastSettleTime_ != ts) {\n ProtocolLike(sovrynContractAddress).withdrawAccruedInterest(loanTokenAddress);\n\n lastSettleTime_ = ts;\n }\n }\n\n /**\n * @notice Imitate a Solidity high-level call (i.e. a regular function\n * call to a contract), relaxing the requirement on the return value:\n * the return value is optional (but if data is returned, it must not be\n * false).\n *\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n * @param errorMsg The error message on failure.\n * */\n function _callOptionalReturn(\n address token,\n bytes memory data,\n string memory errorMsg\n ) internal {\n require(Address.isContract(token), \"call to a non-contract address\");\n (bool success, bytes memory returndata) = token.call(data);\n require(success, errorMsg);\n\n if (returndata.length != 0) {\n require(abi.decode(returndata, (bool)), errorMsg);\n }\n }\n\n /**\n * @notice Execute the ERC20 token's `transfer` function and reverts\n * upon failure the main purpose of this function is to prevent a non\n * standard ERC20 token from failing silently.\n *\n * @dev Wrappers around ERC20 operations that throw on failure (when the\n * token contract returns false). Tokens that return no value (and instead\n * revert or throw on failure) are also supported, non-reverting calls are\n * assumed to be successful.\n *\n * @param token The ERC20 token address.\n * @param to The target address.\n * @param amount The transfer amount.\n * @param errorMsg The error message on failure.\n */\n function _safeTransfer(\n address token,\n address to,\n uint256 amount,\n string memory errorMsg\n ) internal {\n _callOptionalReturn(\n token,\n abi.encodeWithSelector(IERC20(token).transfer.selector, to, amount),\n errorMsg\n );\n }\n\n /**\n * @notice Execute the ERC20 token's `transferFrom` function and reverts\n * upon failure the main purpose of this function is to prevent a non\n * standard ERC20 token from failing silently.\n *\n * @dev Wrappers around ERC20 operations that throw on failure (when the\n * token contract returns false). Tokens that return no value (and instead\n * revert or throw on failure) are also supported, non-reverting calls are\n * assumed to be successful.\n *\n * @param token The ERC20 token address.\n * @param from The source address.\n * @param to The target address.\n * @param amount The transfer amount.\n * @param errorMsg The error message on failure.\n */\n function _safeTransferFrom(\n address token,\n address from,\n address to,\n uint256 amount,\n string memory errorMsg\n ) internal {\n _callOptionalReturn(\n token,\n abi.encodeWithSelector(IERC20(token).transferFrom.selector, from, to, amount),\n errorMsg\n );\n }\n\n /** Internal view function */\n /**\n * @notice Compute the token price.\n * @param assetSupply The amount of loan tokens supplied.\n * @return The token price.\n * */\n function _tokenPrice(uint256 assetSupply) internal view returns (uint256) {\n uint256 totalTokenSupply = totalSupply_;\n\n return\n totalTokenSupply != 0 ? assetSupply.mul(10**18).div(totalTokenSupply) : initialPrice;\n }\n\n /**\n * @notice Get two kind of interests: owed per day and yet to be paid.\n * @return interestOwedPerDay The interest per day.\n * @return interestUnPaid The interest not yet paid.\n * */\n function _getAllInterest()\n internal\n view\n returns (uint256 interestOwedPerDay, uint256 interestUnPaid)\n {\n /// interestPaid, interestPaidDate, interestOwedPerDay, interestUnPaid, interestFeePercent, principalTotal\n uint256 interestFeePercent;\n (, , interestOwedPerDay, interestUnPaid, interestFeePercent, ) = ProtocolLike(\n sovrynContractAddress\n )\n .getLenderInterestData(address(this), loanTokenAddress);\n\n interestUnPaid = interestUnPaid.mul(SafeMath.sub(10**20, interestFeePercent)).div(10**20);\n }\n\n /**\n * @notice Compute the total amount of loan tokens on supply.\n * @param interestUnPaid The interest not yet paid.\n * @return assetSupply The total amount of loan tokens on supply.\n * */\n function _totalAssetSupply(uint256 interestUnPaid)\n internal\n view\n returns (uint256 assetSupply)\n {\n if (totalSupply_ != 0) {\n uint256 assetsBalance = _flTotalAssetSupply; /// Temporary locked totalAssetSupply during a flash loan transaction.\n if (assetsBalance == 0) {\n assetsBalance = _underlyingBalance().add(totalAssetBorrow());\n }\n\n return assetsBalance.add(interestUnPaid);\n }\n }\n\n /**\n * @notice Get the loan contract balance.\n * @return The balance of the loan token for this contract.\n * */\n function _underlyingBalance() internal view returns (uint256) {\n return IERC20(loanTokenAddress).balanceOf(address(this));\n }\n}\n" + }, + "contracts/connectors/loantoken/LoanTokenLogicSplit.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./LoanTokenLogicShared.sol\";\n\n/**\n * @title Loan Token Logic Standard contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized margin\n * trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * Logic around loan tokens (iTokens) required to operate borrowing,\n * and margin trading financial processes.\n *\n * The user provides funds to the lending pool using the mint function and\n * withdraws funds from the lending pool using the burn function. Mint and\n * burn refer to minting and burning loan tokens. Loan tokens represent a\n * share of the pool and gather interest over time.\n *\n * Interest rates are determined by supply and demand. When a lender deposits\n * funds, the interest rates go down. When a trader borrows funds, the\n * interest rates go up. Fulcrum uses a simple linear interest rate formula\n * of the form y = mx + b. The interest rate starts at 1% when loans aren't\n * being utilized and scales up to 40% when all the funds in the loan pool\n * are being borrowed.\n *\n * The borrow rate is determined at the time of the loan and represents the\n * net contribution of each borrower. Each borrower's interest contribution\n * is determined by the utilization rate of the pool and is netted against\n * all prior borrows. This means that the total amount of interest flowing\n * into the lending pool is not directly changed by lenders entering or\n * exiting the pool. The entrance or exit of lenders only impacts how the\n * interest payments are split up.\n *\n * For example, if there are 2 lenders with equal holdings each earning\n * 5% APR, but one of the lenders leave, then the remaining lender will earn\n * 10% APR since the interest payments don't have to be split between two\n * individuals.\n * */\ncontract LoanTokenLogicSplit is LoanTokenLogicShared {\n using SafeMath for uint256;\n using SignedSafeMath for int256;\n\n /// DON'T ADD VARIABLES HERE, PLEASE\n\n /* Public functions */\n\n /**\n * @notice Mint loan token wrapper.\n * Adds a check before calling low level _mintToken function.\n * The function retrieves the tokens from the message sender, so make sure\n * to first approve the loan token contract to access your funds. This is\n * done by calling approve(address spender, uint amount) on the ERC20\n * token contract, where spender is the loan token contract address and\n * amount is the amount to be deposited.\n *\n * @param receiver The account getting the minted tokens.\n * @param depositAmount The amount of underlying tokens provided on the\n * loan. (Not the number of loan tokens to mint).\n *\n * @return The amount of loan tokens minted.\n * */\n function mint(address receiver, uint256 depositAmount)\n external\n nonReentrant\n globallyNonReentrant\n returns (uint256 mintAmount)\n {\n return _mintToken(receiver, depositAmount);\n }\n\n /**\n * @notice Burn loan token wrapper.\n * Adds a pay-out transfer after calling low level _burnToken function.\n * In order to withdraw funds to the pool, call burn on the respective\n * loan token contract. This will burn your loan tokens and send you the\n * underlying token in exchange.\n *\n * @param receiver The account getting the minted tokens.\n * @param burnAmount The amount of loan tokens to redeem.\n *\n * @return The amount of underlying tokens payed to lender.\n * */\n function burn(address receiver, uint256 burnAmount)\n external\n nonReentrant\n globallyNonReentrant\n returns (uint256 loanAmountPaid)\n {\n loanAmountPaid = _burnToken(burnAmount);\n\n //this needs to be here and not in _burnTokens because of the WRBTC implementation\n if (loanAmountPaid != 0) {\n _safeTransfer(loanTokenAddress, receiver, loanAmountPaid, \"5\");\n }\n }\n\n /**\n * @notice transfers the underlying asset from the msg.sender and mints tokens for the receiver\n * @param receiver the address of the iToken receiver\n * @param depositAmount the amount of underlying assets to be deposited\n * @return the amount of iTokens issued\n */\n function _mintToken(address receiver, uint256 depositAmount)\n internal\n returns (uint256 mintAmount)\n {\n uint256 currentPrice;\n\n //calculate amount to mint and transfer the underlying asset\n (mintAmount, currentPrice) = _prepareMinting(depositAmount);\n\n //compute balances needed for checkpoint update, considering that the user might have a pool token balance\n //on the liquidity mining contract\n uint256 balanceOnLM = 0;\n if (liquidityMiningAddress != address(0))\n balanceOnLM = ILiquidityMining(liquidityMiningAddress).getUserPoolTokenBalance(\n address(this),\n receiver\n );\n uint256 oldBalance = balances[receiver].add(balanceOnLM);\n uint256 newBalance = oldBalance.add(mintAmount);\n\n //mint the tokens to the receiver\n _mint(receiver, mintAmount, depositAmount, currentPrice);\n\n //update the checkpoint of the receiver\n _updateCheckpoints(receiver, oldBalance, newBalance, currentPrice);\n }\n\n /**\n * calculates the amount of tokens to mint and transfers the underlying asset to this contract\n * @param depositAmount the amount of the underyling asset deposited\n * @return the amount to be minted\n */\n function _prepareMinting(uint256 depositAmount)\n internal\n returns (uint256 mintAmount, uint256 currentPrice)\n {\n require(depositAmount != 0, \"17\");\n\n _settleInterest();\n\n currentPrice = _tokenPrice(_totalAssetSupply(0));\n mintAmount = depositAmount.mul(10**18).div(currentPrice);\n\n if (msg.value == 0) {\n _safeTransferFrom(loanTokenAddress, msg.sender, address(this), depositAmount, \"18\");\n } else {\n IWrbtc(wrbtcTokenAddress).deposit.value(depositAmount)();\n }\n }\n\n /**\n * @notice A wrapper for AdvancedToken::_burn\n *\n * @param burnAmount The amount of loan tokens to redeem.\n *\n * @return The amount of underlying tokens payed to lender.\n * */\n function _burnToken(uint256 burnAmount) internal returns (uint256 loanAmountPaid) {\n require(burnAmount != 0, \"19\");\n\n if (burnAmount > balanceOf(msg.sender)) {\n require(burnAmount == uint256(-1), \"32\");\n burnAmount = balanceOf(msg.sender);\n }\n\n _settleInterest();\n\n uint256 currentPrice = _tokenPrice(_totalAssetSupply(0));\n\n uint256 loanAmountOwed = burnAmount.mul(currentPrice).div(10**18);\n uint256 loanAmountAvailableInContract = _underlyingBalance();\n\n loanAmountPaid = loanAmountOwed;\n require(loanAmountPaid <= loanAmountAvailableInContract, \"37\");\n\n //compute balances needed for checkpoint update, considering that the user might have a pool token balance\n //on the liquidity mining contract\n uint256 balanceOnLM = 0;\n if (liquidityMiningAddress != address(0))\n balanceOnLM = ILiquidityMining(liquidityMiningAddress).getUserPoolTokenBalance(\n address(this),\n msg.sender\n );\n uint256 oldBalance = balances[msg.sender].add(balanceOnLM);\n uint256 newBalance = oldBalance.sub(burnAmount);\n\n _burn(msg.sender, burnAmount, loanAmountPaid, currentPrice);\n\n //this function does not only update the checkpoints but also the current profit of the user\n //all for external use only\n _updateCheckpoints(msg.sender, oldBalance, newBalance, currentPrice);\n }\n\n function _mintWithLM(address receiver, uint256 depositAmount)\n internal\n returns (uint256 minted)\n {\n //mint the tokens for the receiver\n minted = _mintToken(receiver, depositAmount);\n\n //transfer the tokens from the receiver to the LM address\n _internalTransferFrom(receiver, liquidityMiningAddress, minted, minted);\n\n //inform the LM mining contract\n ILiquidityMining(liquidityMiningAddress).onTokensDeposited(receiver, minted);\n }\n\n function _burnFromLM(uint256 burnAmount) internal returns (uint256) {\n uint256 balanceOnLM =\n ILiquidityMining(liquidityMiningAddress).getUserPoolTokenBalance(\n address(this),\n msg.sender\n );\n require(balanceOnLM.add(balanceOf(msg.sender)) >= burnAmount, \"not enough balance\");\n\n if (balanceOnLM > 0) {\n //withdraw pool tokens and LM rewards to the passed address\n if (balanceOnLM < burnAmount) {\n ILiquidityMining(liquidityMiningAddress).withdraw(\n address(this),\n balanceOnLM,\n msg.sender\n );\n } else {\n ILiquidityMining(liquidityMiningAddress).withdraw(\n address(this),\n burnAmount,\n msg.sender\n );\n }\n }\n //burn the tokens of the msg.sender\n return _burnToken(burnAmount);\n }\n}\n" + }, + "contracts/connectors/loantoken/LoanTokenLogicStandard.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./LoanTokenLogicShared.sol\";\n\ncontract LoanTokenLogicStandard is LoanTokenLogicShared {\n /**\n * @notice Transfer tokens wrapper.\n * Sets token owner the msg.sender.\n * Sets maximun allowance uint256(-1) to ensure tokens are always transferred.\n *\n * If the recipient (_to) is a vesting contract address, transfer the token to the tokenOwner of the vesting contract itself.\n *\n * @param _to The recipient of the tokens.\n * @param _value The amount of tokens sent.\n * @return Success true/false.\n * */\n function transfer(address _to, uint256 _value) external returns (bool) {\n /** need additional check address(0) here to support backward compatibility\n * in case we don't want to activate this check, just need to set the stakingContractAddress to 0 address\n */\n if (\n stakingContractAddress != address(0) &&\n IStaking(stakingContractAddress).isVestingContract(_to)\n ) {\n (bool success, bytes memory data) =\n _to.staticcall(abi.encodeWithSelector(IVesting(_to).tokenOwner.selector));\n\n if (success) _to = abi.decode(data, (address));\n }\n\n return _internalTransferFrom(msg.sender, _to, _value, uint256(-1));\n }\n\n /**\n * @notice Moves `_value` loan tokens from `_from` to `_to` using the\n * allowance mechanism. Calls internal _internalTransferFrom function.\n *\n * @return A boolean value indicating whether the operation succeeded.\n */\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool) {\n return\n _internalTransferFrom(\n _from,\n _to,\n _value,\n //allowed[_from][msg.sender]\n ProtocolLike(sovrynContractAddress).isLoanPool(msg.sender)\n ? uint256(-1)\n : allowed[_from][msg.sender]\n );\n }\n\n /**\n * @notice Borrow funds from the pool.\n * The underlying loan token may not be used as collateral.\n *\n * @param loanId The ID of the loan, 0 for a new loan.\n * @param withdrawAmount The amount to be withdrawn (actually borrowed).\n * @param initialLoanDuration The duration of the loan in seconds.\n * If the loan is not paid back until then, it'll need to be rolled over.\n * @param collateralTokenSent The amount of collateral tokens provided by the user.\n * (150% of the withdrawn amount worth in collateral tokens).\n * @param collateralTokenAddress The address of the token to be used as\n * collateral. Cannot be the loan token address.\n * @param borrower The one paying for the collateral.\n * @param receiver The one receiving the withdrawn amount.\n *\n * @return New principal and new collateral added to loan.\n * */\n function borrow(\n bytes32 loanId, /// 0 if new loan.\n uint256 withdrawAmount,\n uint256 initialLoanDuration, /// Duration in seconds.\n uint256 collateralTokenSent, /// If 0, loanId must be provided; any rBTC sent must equal this value.\n address collateralTokenAddress, /// If address(0), this means rBTC and rBTC must be sent with the call or loanId must be provided.\n address borrower,\n address receiver,\n bytes memory /// loanDataBytes: arbitrary order data (for future use).\n )\n public\n payable\n nonReentrant /// Note: needs to be removed to allow flashloan use cases.\n globallyNonReentrant\n returns (\n uint256,\n uint256 /// Returns new principal and new collateral added to loan.\n )\n {\n require(withdrawAmount != 0, \"6\");\n\n _checkPause();\n\n /// Temporary: limit transaction size.\n if (transactionLimit[collateralTokenAddress] > 0)\n require(collateralTokenSent <= transactionLimit[collateralTokenAddress]);\n\n require(\n (msg.value == 0 || msg.value == collateralTokenSent) &&\n (collateralTokenSent != 0 || loanId != 0) &&\n (collateralTokenAddress != address(0) || msg.value != 0 || loanId != 0) &&\n (loanId == 0 || msg.sender == borrower),\n \"7\"\n );\n\n /// @dev We have an issue regarding contract size code is too big. 1 of the solution is need to keep the error message 32 bytes length\n // Temporarily, we combine this require to the above, so can save the contract size code\n // require(collateralTokenSent != 0 || loanId != 0, \"8\");\n // require(collateralTokenAddress != address(0) || msg.value != 0 || loanId != 0, \"9\");\n\n /// @dev Ensure authorized use of existing loan.\n // require(loanId == 0 || msg.sender == borrower, \"401 use of existing loan\");\n\n /// @dev The condition is never met.\n /// Address zero is not allowed by previous require validation.\n /// This check is unneeded and was lowering the test coverage index.\n // if (collateralTokenAddress == address(0)) {\n // \tcollateralTokenAddress = wrbtcTokenAddress;\n // }\n\n require(collateralTokenAddress != loanTokenAddress, \"10\");\n\n _settleInterest();\n\n MarginTradeStructHelpers.SentAddresses memory sentAddresses;\n MarginTradeStructHelpers.SentAmounts memory sentAmounts;\n\n sentAddresses.lender = address(this); /// The lender.\n sentAddresses.borrower = borrower;\n sentAddresses.receiver = receiver;\n /// sentAddresses.manager = address(0); /// The manager.\n\n sentAmounts.newPrincipal = withdrawAmount;\n\n /// interestRate, interestInitialAmount, borrowAmount (newBorrowAmount).\n (\n sentAmounts.interestRate,\n sentAmounts.interestInitialAmount,\n sentAmounts.newPrincipal\n ) = _getInterestRateAndBorrowAmount(\n sentAmounts.newPrincipal,\n _totalAssetSupply(0), /// Interest is settled above.\n initialLoanDuration\n );\n\n /// sentAmounts.loanTokenSent = 0; /// loanTokenSent\n sentAmounts.collateralTokenSent = collateralTokenSent;\n\n return\n _borrowOrTrade(\n loanId,\n withdrawAmount,\n ProtocolSettingsLike(sovrynContractAddress).minInitialMargin(\n loanParamsIds[\n uint256(keccak256(abi.encodePacked(collateralTokenAddress, true)))\n ]\n ),\n collateralTokenAddress,\n sentAddresses,\n sentAmounts,\n \"\" /// loanDataBytes\n );\n }\n\n /**\n * @notice Borrow and immediately get into a position.\n *\n * Trading on margin is used to increase an investor's buying power.\n * Margin is the amount of money required to open a position, while\n * leverage is the multiple of exposure to account equity.\n *\n * Leverage allows you to trade positions LARGER than the amount\n * of money in your trading account. Leverage is expressed as a ratio.\n *\n * When trading on margin, investors first deposit some token that then\n * serves as collateral for the loan, and then pay ongoing interest\n * payments on the money they borrow.\n *\n * Margin trading = taking a loan and swapping it:\n * In order to open a margin trade position,\n * 1.- The user calls marginTrade on the loan token contract.\n * 2.- The loan token contract provides the loan and sends it for processing\n * to the protocol proxy contract.\n * 3.- The protocol proxy contract uses the module LoanOpening to create a\n * position and swaps the loan tokens to collateral tokens.\n * 4.- The Sovryn Swap network looks up the correct converter and swaps the\n * tokens.\n * If successful, the position is being held by the protocol proxy contract,\n * which is why positions need to be closed at the protocol proxy contract.\n *\n * @param loanId The ID of the loan, 0 for a new loan.\n * @param leverageAmount The multiple of exposure: 2x ... 5x. The leverage with 18 decimals.\n * @param loanTokenSent The number of loan tokens provided by the user.\n * @param collateralTokenSent The amount of collateral tokens provided by the user.\n * @param collateralTokenAddress The token address of collateral.\n * @param trader The account that performs this trade.\n * @param minEntryPrice Value of loan token in collateral.\n * @param loanDataBytes Additional loan data (not in use for token swaps).\n *\n * @return New principal and new collateral added to trade.\n * */\n function marginTrade(\n bytes32 loanId, /// 0 if new loan\n uint256 leverageAmount, /// Expected in x * 10**18 where x is the actual leverage (2, 3, 4, or 5).\n uint256 loanTokenSent,\n uint256 collateralTokenSent,\n address collateralTokenAddress,\n address trader,\n uint256 minEntryPrice, // value of loan token in collateral\n bytes memory loanDataBytes /// Arbitrary order data.\n )\n public\n payable\n nonReentrant /// Note: needs to be removed to allow flashloan use cases.\n globallyNonReentrant\n returns (\n uint256,\n uint256 /// Returns new principal and new collateral added to trade.\n )\n {\n _checkPause();\n\n if (collateralTokenAddress == address(0)) {\n collateralTokenAddress = wrbtcTokenAddress;\n }\n\n require(collateralTokenAddress != loanTokenAddress, \"11\");\n\n /// @dev Ensure authorized use of existing loan.\n require(loanId == 0 || msg.sender == trader, \"401 use of existing loan\");\n\n /// Temporary: limit transaction size.\n if (transactionLimit[collateralTokenAddress] > 0)\n require(collateralTokenSent <= transactionLimit[collateralTokenAddress]);\n if (transactionLimit[loanTokenAddress] > 0)\n require(loanTokenSent <= transactionLimit[loanTokenAddress]);\n\n /// @dev Compute the worth of the total deposit in loan tokens.\n /// (loanTokenSent + convert(collateralTokenSent))\n /// No actual swap happening here.\n uint256 totalDeposit =\n _totalDeposit(collateralTokenAddress, collateralTokenSent, loanTokenSent);\n require(totalDeposit != 0, \"12\");\n\n MarginTradeStructHelpers.SentAddresses memory sentAddresses;\n MarginTradeStructHelpers.SentAmounts memory sentAmounts;\n\n sentAddresses.lender = address(this);\n sentAddresses.borrower = trader;\n sentAddresses.receiver = trader;\n /// sentAddresses.manager = address(0); /// The manager.\n\n /// sentAmounts.interestRate = 0; /// interestRate (found later).\n sentAmounts.newPrincipal = totalDeposit;\n /// sentAmounts.interestInitialAmount = 0; /// interestInitialAmount (interest is calculated based on fixed-term loan).\n sentAmounts.loanTokenSent = loanTokenSent;\n sentAmounts.collateralTokenSent = collateralTokenSent;\n\n _settleInterest();\n\n (sentAmounts.newPrincipal, sentAmounts.interestRate) = _getMarginBorrowAmountAndRate( /// borrowAmount, interestRate\n leverageAmount,\n sentAmounts.newPrincipal /// depositAmount\n );\n\n require(\n _getAmountInRbtc(loanTokenAddress, sentAmounts.newPrincipal) > TINY_AMOUNT,\n \"principal too small\"\n );\n\n /// @dev Converting to initialMargin\n leverageAmount = SafeMath.div(10**38, leverageAmount);\n sentAmounts.minEntryPrice = minEntryPrice;\n return\n _borrowOrTrade(\n loanId,\n 0, /// withdrawAmount\n leverageAmount, //initial margin\n collateralTokenAddress,\n sentAddresses,\n sentAmounts,\n loanDataBytes\n );\n }\n\n /**\n * @notice Wrapper for marginTrade invoking setAffiliatesReferrer to track\n * referral trade by affiliates program.\n *\n * @param loanId The ID of the loan, 0 for a new loan.\n * @param leverageAmount The multiple of exposure: 2x ... 5x. The leverage with 18 decimals.\n * @param loanTokenSent The number of loan tokens provided by the user.\n * @param collateralTokenSent The amount of collateral tokens provided by the user.\n * @param collateralTokenAddress The token address of collateral.\n * @param trader The account that performs this trade.\n * @param minEntryPrice Value of loan token in collateral.\n * @param affiliateReferrer The address of the referrer from affiliates program.\n * @param loanDataBytes Additional loan data (not in use for token swaps).\n *\n * @return New principal and new collateral added to trade.\n */\n function marginTradeAffiliate(\n bytes32 loanId, // 0 if new loan\n uint256 leverageAmount, // expected in x * 10**18 where x is the actual leverage (2, 3, 4, or 5)\n uint256 loanTokenSent,\n uint256 collateralTokenSent,\n address collateralTokenAddress,\n address trader,\n uint256 minEntryPrice, /// Value of loan token in collateral\n address affiliateReferrer, /// The user was brought by the affiliate (referrer).\n bytes calldata loanDataBytes /// Arbitrary order data.\n )\n external\n payable\n returns (\n uint256,\n uint256 /// Returns new principal and new collateral added to trade.\n )\n {\n if (affiliateReferrer != address(0))\n ProtocolAffiliatesInterface(sovrynContractAddress).setAffiliatesReferrer(\n trader,\n affiliateReferrer\n );\n return\n marginTrade(\n loanId,\n leverageAmount,\n loanTokenSent,\n collateralTokenSent,\n collateralTokenAddress,\n trader,\n minEntryPrice,\n loanDataBytes\n );\n }\n\n /* Public View functions */\n\n /**\n * @notice Wrapper for internal _profitOf low level function.\n * @param user The user address.\n * @return The profit of a user.\n * */\n function profitOf(address user) external view returns (int256) {\n /// @dev keccak256(\"iToken_ProfitSoFar\")\n bytes32 slot = keccak256(abi.encodePacked(user, iToken_ProfitSoFar));\n //TODO + LM balance\n return _profitOf(slot, balances[user], tokenPrice(), checkpointPrices_[user]);\n }\n\n /**\n * @notice Getter for the price checkpoint mapping.\n * @param _user The user account as the mapping index.\n * @return The price on the checkpoint for this user.\n * */\n function checkpointPrice(address _user) public view returns (uint256 price) {\n return checkpointPrices_[_user];\n }\n\n /**\n * @notice Get current liquidity.\n * A part of total funds supplied are borrowed. Liquidity = supply - borrow\n * @return The market liquidity.\n * */\n function marketLiquidity() public view returns (uint256) {\n uint256 totalSupply = _totalAssetSupply(0);\n uint256 totalBorrow = totalAssetBorrow();\n if (totalSupply > totalBorrow) {\n return totalSupply - totalBorrow;\n }\n }\n\n /**\n * @notice Wrapper for average borrow interest.\n * @return The average borrow interest.\n * */\n function avgBorrowInterestRate() public view returns (uint256) {\n return _avgBorrowInterestRate(totalAssetBorrow());\n }\n\n /**\n * @notice Get borrow interest rate.\n * The minimum rate the next base protocol borrower will receive\n * for variable-rate loans.\n * @return The borrow interest rate.\n * */\n function borrowInterestRate() public view returns (uint256) {\n return _nextBorrowInterestRate(0);\n }\n\n /**\n * @notice Public wrapper for internal call.\n * @param borrowAmount The amount of tokens to borrow.\n * @return The next borrow interest rate.\n * */\n function nextBorrowInterestRate(uint256 borrowAmount) public view returns (uint256) {\n return _nextBorrowInterestRate(borrowAmount);\n }\n\n /**\n * @notice Get interest rate.\n *\n * @return Interest that lenders are currently receiving when supplying to\n * the pool.\n * */\n function supplyInterestRate() public view returns (uint256) {\n return totalSupplyInterestRate(_totalAssetSupply(0));\n }\n\n /**\n * @notice Get interest rate w/ added supply.\n * @param supplyAmount The amount of tokens supplied.\n * @return Interest that lenders are currently receiving when supplying\n * a given amount of tokens to the pool.\n * */\n function nextSupplyInterestRate(uint256 supplyAmount) public view returns (uint256) {\n return totalSupplyInterestRate(_totalAssetSupply(0).add(supplyAmount));\n }\n\n /**\n * @notice Get interest rate w/ added supply assets.\n * @param assetSupply The amount of loan tokens supplied.\n * @return Interest that lenders are currently receiving when supplying\n * a given amount of loan tokens to the pool.\n * */\n function totalSupplyInterestRate(uint256 assetSupply) public view returns (uint256) {\n uint256 assetBorrow = totalAssetBorrow();\n if (assetBorrow != 0) {\n return calculateSupplyInterestRate(assetBorrow, assetSupply);\n }\n }\n\n /**\n * @notice Get the total amount of loan tokens on supply.\n * @dev Wrapper for internal _totalAssetSupply function.\n * @return The total amount of loan tokens on supply.\n * */\n function totalAssetSupply() public view returns (uint256) {\n uint256 interestUnPaid;\n if (lastSettleTime_ != uint88(block.timestamp)) {\n (, interestUnPaid) = _getAllInterest();\n }\n\n return _totalAssetSupply(interestUnPaid);\n }\n\n /**\n * @notice Compute the maximum deposit amount under current market conditions.\n * @dev maxEscrowAmount = liquidity * (100 - interestForDuration) / 100\n * @param leverageAmount The chosen multiplier with 18 decimals.\n * */\n function getMaxEscrowAmount(uint256 leverageAmount)\n public\n view\n returns (uint256 maxEscrowAmount)\n {\n /**\n * @dev Mathematical imperfection: depending on liquidity we might be able\n * to borrow more if utilization is below the kink level.\n * */\n uint256 interestForDuration = maxScaleRate.mul(28).div(365);\n uint256 factor = uint256(10**20).sub(interestForDuration);\n uint256 maxLoanSize = marketLiquidity().mul(factor).div(10**20);\n maxEscrowAmount = maxLoanSize.mul(10**18).div(leverageAmount);\n }\n\n /**\n * @notice Get loan token balance.\n * @return The user's balance of underlying token.\n * */\n function assetBalanceOf(address _owner) public view returns (uint256) {\n uint256 balanceOnLM = 0;\n if (liquidityMiningAddress != address(0)) {\n balanceOnLM = ILiquidityMining(liquidityMiningAddress).getUserPoolTokenBalance(\n address(this),\n _owner\n );\n }\n return balanceOf(_owner).add(balanceOnLM).mul(tokenPrice()).div(10**18);\n }\n\n /**\n * @notice Get margin information on a trade.\n *\n * @param leverageAmount The multiple of exposure: 2x ... 5x. The leverage with 18 decimals.\n * @param loanTokenSent The number of loan tokens provided by the user.\n * @param collateralTokenSent The amount of collateral tokens provided by the user.\n * @param collateralTokenAddress The token address of collateral.\n *\n * @return The principal, the collateral and the interestRate.\n * */\n function getEstimatedMarginDetails(\n uint256 leverageAmount,\n uint256 loanTokenSent,\n uint256 collateralTokenSent,\n address collateralTokenAddress // address(0) means ETH\n )\n public\n view\n returns (\n uint256 principal,\n uint256 collateral,\n uint256 interestRate\n )\n {\n if (collateralTokenAddress == address(0)) {\n collateralTokenAddress = wrbtcTokenAddress;\n }\n\n uint256 totalDeposit =\n _totalDeposit(collateralTokenAddress, collateralTokenSent, loanTokenSent);\n\n (principal, interestRate) = _getMarginBorrowAmountAndRate(leverageAmount, totalDeposit);\n if (principal > _underlyingBalance()) {\n return (0, 0, 0);\n }\n\n loanTokenSent = loanTokenSent.add(principal);\n\n collateral = ProtocolLike(sovrynContractAddress).getEstimatedMarginExposure(\n loanTokenAddress,\n collateralTokenAddress,\n loanTokenSent,\n collateralTokenSent,\n interestRate,\n principal\n );\n }\n\n /**\n * @notice Calculate the deposit required to a given borrow.\n *\n * The function for doing over-collateralized borrows against loan tokens\n * expects a minimum amount of collateral be sent to satisfy collateral\n * requirements of the loan, for borrow amount, interest rate, and\n * initial loan duration. To determine appropriate values to pass to this\n * function for a given loan, `getDepositAmountForBorrow` and\n * 'getBorrowAmountForDeposit` are required.\n *\n * @param borrowAmount The amount of borrow.\n * @param initialLoanDuration The duration of the loan.\n * @param collateralTokenAddress The token address of collateral.\n *\n * @return The amount of deposit required.\n * */\n function getDepositAmountForBorrow(\n uint256 borrowAmount,\n uint256 initialLoanDuration, /// Duration in seconds.\n address collateralTokenAddress /// address(0) means rBTC\n ) public view returns (uint256 depositAmount) {\n if (borrowAmount != 0) {\n (, , uint256 newBorrowAmount) =\n _getInterestRateAndBorrowAmount(\n borrowAmount,\n totalAssetSupply(),\n initialLoanDuration\n );\n\n if (newBorrowAmount <= _underlyingBalance()) {\n if (collateralTokenAddress == address(0))\n collateralTokenAddress = wrbtcTokenAddress;\n bytes32 loanParamsId =\n loanParamsIds[\n uint256(keccak256(abi.encodePacked(collateralTokenAddress, true)))\n ];\n return\n ProtocolLike(sovrynContractAddress)\n .getRequiredCollateral(\n loanTokenAddress,\n collateralTokenAddress,\n newBorrowAmount,\n ProtocolSettingsLike(sovrynContractAddress).minInitialMargin(loanParamsId), /// initialMargin\n true /// isTorqueLoan\n )\n .add(10); /// Some dust to compensate for rounding errors.\n }\n }\n }\n\n /**\n * @notice Calculate the borrow allowed for a given deposit.\n *\n * The function for doing over-collateralized borrows against loan tokens\n * expects a minimum amount of collateral be sent to satisfy collateral\n * requirements of the loan, for borrow amount, interest rate, and\n * initial loan duration. To determine appropriate values to pass to this\n * function for a given loan, `getDepositAmountForBorrow` and\n * 'getBorrowAmountForDeposit` are required.\n *\n * @param depositAmount The amount of deposit.\n * @param initialLoanDuration The duration of the loan.\n * @param collateralTokenAddress The token address of collateral.\n *\n * @return The amount of borrow allowed.\n * */\n function getBorrowAmountForDeposit(\n uint256 depositAmount,\n uint256 initialLoanDuration, /// Duration in seconds.\n address collateralTokenAddress /// address(0) means rBTC\n ) public view returns (uint256 borrowAmount) {\n if (depositAmount != 0) {\n if (collateralTokenAddress == address(0)) collateralTokenAddress = wrbtcTokenAddress;\n bytes32 loanParamsId =\n loanParamsIds[uint256(keccak256(abi.encodePacked(collateralTokenAddress, true)))];\n borrowAmount = ProtocolLike(sovrynContractAddress).getBorrowAmount(\n loanTokenAddress,\n collateralTokenAddress,\n depositAmount,\n ProtocolSettingsLike(sovrynContractAddress).minInitialMargin(loanParamsId), /// initialMargin,\n true /// isTorqueLoan\n );\n\n (, , borrowAmount) = _getInterestRateAndBorrowAmount(\n borrowAmount,\n totalAssetSupply(),\n initialLoanDuration\n );\n\n if (borrowAmount > _underlyingBalance()) {\n borrowAmount = 0;\n }\n }\n }\n\n /**\n * @notice Check if entry price lies above a minimum\n *\n * @param loanTokenSent The amount of deposit.\n * @param collateralTokenAddress The token address of collateral.\n * @param minEntryPrice Value of loan token in collateral\n * */\n function checkPriceDivergence(\n uint256 loanTokenSent,\n address collateralTokenAddress,\n uint256 minEntryPrice\n ) public view {\n /// @dev See how many collateralTokens we would get if exchanging this amount of loan tokens to collateral tokens.\n uint256 collateralTokensReceived =\n ProtocolLike(sovrynContractAddress).getSwapExpectedReturn(\n loanTokenAddress,\n collateralTokenAddress,\n loanTokenSent\n );\n uint256 collateralTokenPrice =\n (collateralTokensReceived.mul(WEI_PRECISION)).div(loanTokenSent);\n require(collateralTokenPrice >= minEntryPrice, \"entry price above the minimum\");\n }\n\n /**\n * @notice Compute the next supply interest adjustment.\n * @param assetBorrow The amount of loan tokens on debt.\n * @param assetSupply The amount of loan tokens supplied.\n * @return The next supply interest adjustment.\n * */\n function calculateSupplyInterestRate(uint256 assetBorrow, uint256 assetSupply)\n public\n view\n returns (uint256)\n {\n if (assetBorrow != 0 && assetSupply >= assetBorrow) {\n return\n _avgBorrowInterestRate(assetBorrow)\n .mul(_utilizationRate(assetBorrow, assetSupply))\n .mul(\n SafeMath.sub(10**20, ProtocolLike(sovrynContractAddress).lendingFeePercent())\n )\n .div(10**40);\n }\n }\n\n /* Internal functions */\n\n /**\n * @notice Compute what the deposit is worth in loan tokens using the swap rate\n * used for loan size computation.\n *\n * @param collateralTokenAddress The token address of the collateral.\n * @param collateralTokenSent The amount of collateral tokens provided by the user.\n * @param loanTokenSent The number of loan tokens provided by the user.\n *\n * @return The value of the deposit in loan tokens.\n * */\n function _totalDeposit(\n address collateralTokenAddress,\n uint256 collateralTokenSent,\n uint256 loanTokenSent\n ) internal view returns (uint256 totalDeposit) {\n totalDeposit = loanTokenSent;\n\n if (collateralTokenSent != 0) {\n /// @dev Get the oracle rate from collateral -> loan\n (uint256 collateralToLoanRate, uint256 collateralToLoanPrecision) =\n FeedsLike(ProtocolLike(sovrynContractAddress).priceFeeds()).queryRate(\n collateralTokenAddress,\n loanTokenAddress\n );\n require(\n (collateralToLoanRate != 0) && (collateralToLoanPrecision != 0),\n \"invalid rate collateral token\"\n );\n\n /// @dev Compute the loan token amount with the oracle rate.\n uint256 loanTokenAmount =\n collateralTokenSent.mul(collateralToLoanRate).div(collateralToLoanPrecision);\n\n /// @dev See how many collateralTokens we would get if exchanging this amount of loan tokens to collateral tokens.\n uint256 collateralTokenAmount =\n ProtocolLike(sovrynContractAddress).getSwapExpectedReturn(\n loanTokenAddress,\n collateralTokenAddress,\n loanTokenAmount\n );\n\n /// @dev Probably not the same due to the price difference.\n if (collateralTokenAmount != collateralTokenSent) {\n //scale the loan token amount accordingly, so we'll get the expected position size in the end\n loanTokenAmount = loanTokenAmount.mul(collateralTokenAmount).div(\n collateralTokenSent\n );\n }\n\n totalDeposit = loanTokenAmount.add(totalDeposit);\n }\n }\n\n /**\n * @dev returns amount of the asset converted to RBTC\n * @param asset the asset to be transferred\n * @param amount the amount to be transferred\n * @return amount in RBTC\n * */\n function _getAmountInRbtc(address asset, uint256 amount) internal returns (uint256) {\n (uint256 rbtcRate, uint256 rbtcPrecision) =\n FeedsLike(ProtocolLike(sovrynContractAddress).priceFeeds()).queryRate(\n asset,\n wrbtcTokenAddress\n );\n return amount.mul(rbtcRate).div(rbtcPrecision);\n }\n\n /*\n * @notice Compute interest rate and other loan parameters.\n *\n * @param borrowAmount The amount of tokens to borrow.\n * @param assetSupply The amount of loan tokens supplied.\n * @param initialLoanDuration The duration of the loan in seconds.\n * If the loan is not paid back until then, it'll need to be rolled over.\n *\n * @return The interest rate, the interest calculated based on fixed-term\n * loan, and the new borrow amount.\n * */\n function _getInterestRateAndBorrowAmount(\n uint256 borrowAmount,\n uint256 assetSupply,\n uint256 initialLoanDuration /// Duration in seconds.\n )\n internal\n view\n returns (\n uint256 interestRate,\n uint256 interestInitialAmount,\n uint256 newBorrowAmount\n )\n {\n interestRate = _nextBorrowInterestRate2(borrowAmount, assetSupply);\n\n /// newBorrowAmount = borrowAmount * 10^18 / (10^18 - interestRate * 7884000 * 10^18 / 31536000 / 10^20)\n newBorrowAmount = borrowAmount.mul(10**18).div(\n SafeMath.sub(\n 10**18,\n interestRate.mul(initialLoanDuration).mul(10**18).div(31536000 * 10**20) /// 365 * 86400 * 10**20\n )\n );\n\n interestInitialAmount = newBorrowAmount.sub(borrowAmount);\n }\n\n /**\n * @notice Compute principal and collateral.\n *\n * @param loanId The ID of the loan, 0 for a new loan.\n * @param withdrawAmount The amount to be withdrawn (actually borrowed).\n * @param initialMargin The initial margin with 18 decimals\n * @param collateralTokenAddress The address of the token to be used as\n * collateral. Cannot be the loan token address.\n * @param sentAddresses The addresses to send tokens: lender, borrower,\n * receiver and manager.\n * @param sentAmounts The amounts to send to each address.\n * @param loanDataBytes Additional loan data (not in use for token swaps).\n *\n * @return The new principal and the new collateral. Principal is the\n * complete borrowed amount (in loan tokens). Collateral is the complete\n * position size (loan + margin) (in collateral tokens).\n * */\n function _borrowOrTrade(\n bytes32 loanId,\n uint256 withdrawAmount,\n uint256 initialMargin,\n address collateralTokenAddress,\n MarginTradeStructHelpers.SentAddresses memory sentAddresses,\n MarginTradeStructHelpers.SentAmounts memory sentAmounts,\n bytes memory loanDataBytes\n ) internal returns (uint256, uint256) {\n _checkPause();\n require(\n sentAmounts.newPrincipal <= _underlyingBalance() && /// newPrincipal (borrowed amount + fees)\n sentAddresses.borrower != address(0), /// The borrower.\n \"24\"\n );\n\n if (sentAddresses.receiver == address(0)) {\n sentAddresses.receiver = sentAddresses.borrower; /// The receiver = the borrower.\n }\n\n /// @dev Handle transfers prior to adding newPrincipal to loanTokenSent\n uint256 msgValue =\n _verifyTransfers(collateralTokenAddress, sentAddresses, sentAmounts, withdrawAmount);\n\n /**\n * @dev Adding the loan token portion from the lender to loanTokenSent\n * (add the loan to the loan tokens sent from the user).\n * */\n sentAmounts.loanTokenSent = sentAmounts.loanTokenSent.add(sentAmounts.newPrincipal); /// newPrincipal\n\n if (withdrawAmount != 0) {\n /// @dev withdrawAmount already sent to the borrower, so we aren't sending it to the protocol.\n sentAmounts.loanTokenSent = sentAmounts.loanTokenSent.sub(withdrawAmount);\n }\n\n bool withdrawAmountExist = false; /// Default is false, but added just as to make sure.\n\n if (withdrawAmount != 0) {\n withdrawAmountExist = true;\n }\n\n bytes32 loanParamsId =\n loanParamsIds[\n uint256(keccak256(abi.encodePacked(collateralTokenAddress, withdrawAmountExist)))\n ];\n\n (sentAmounts.newPrincipal, sentAmounts.collateralTokenSent) = ProtocolLike(\n sovrynContractAddress\n )\n .borrowOrTradeFromPool\n .value(msgValue)(\n loanParamsId,\n loanId,\n withdrawAmountExist,\n initialMargin,\n sentAddresses,\n sentAmounts,\n loanDataBytes\n ); /// newPrincipal, newCollateral\n require(sentAmounts.newPrincipal != 0, \"25\");\n\n /// @dev Setting not-first-trade flag to prevent binding to an affiliate existing users post factum.\n /// @dev REFACTOR: move to a general interface: ProtocolSettingsLike?\n ProtocolAffiliatesInterface(sovrynContractAddress).setUserNotFirstTradeFlag(\n sentAddresses.borrower\n );\n\n return (sentAmounts.newPrincipal, sentAmounts.collateralTokenSent); // newPrincipal, newCollateral\n }\n\n /* Internal View functions */\n\n /**\n * @notice Compute the average borrow interest rate.\n * @param assetBorrow The amount of loan tokens on debt.\n * @return The average borrow interest rate.\n * */\n function _avgBorrowInterestRate(uint256 assetBorrow) internal view returns (uint256) {\n if (assetBorrow != 0) {\n (uint256 interestOwedPerDay, ) = _getAllInterest();\n return interestOwedPerDay.mul(10**20).mul(365).div(assetBorrow);\n }\n }\n\n /**\n * @notice Compute the next borrow interest adjustment.\n * @param borrowAmount The amount of tokens to borrow.\n * @return The next borrow interest adjustment.\n * */\n function _nextBorrowInterestRate(uint256 borrowAmount) internal view returns (uint256) {\n uint256 interestUnPaid;\n if (borrowAmount != 0) {\n if (lastSettleTime_ != uint88(block.timestamp)) {\n (, interestUnPaid) = _getAllInterest();\n }\n\n uint256 balance = _underlyingBalance().add(interestUnPaid);\n if (borrowAmount > balance) {\n borrowAmount = balance;\n }\n }\n\n return _nextBorrowInterestRate2(borrowAmount, _totalAssetSupply(interestUnPaid));\n }\n\n /**\n * @notice Compute the next borrow interest adjustment under target-kink\n * level analysis.\n *\n * The \"kink\" in the cDAI interest rate model reflects the utilization rate\n * at which the slope of the interest rate goes from \"gradual\" to \"steep\".\n * That is, below this utilization rate, the slope of the interest rate\n * curve is gradual. Above this utilization rate, it is steep.\n *\n * Because of this dynamic between the interest rate curves before and\n * after the \"kink\", the \"kink\" can be thought of as the target utilization\n * rate. Above that rate, it quickly becomes expensive to borrow (and\n * commensurately lucrative for suppliers).\n *\n * @param newBorrowAmount The new amount of tokens to borrow.\n * @param assetSupply The amount of loan tokens supplied.\n * @return The next borrow interest adjustment.\n * */\n function _nextBorrowInterestRate2(uint256 newBorrowAmount, uint256 assetSupply)\n internal\n view\n returns (uint256 nextRate)\n {\n uint256 utilRate = _utilizationRate(totalAssetBorrow().add(newBorrowAmount), assetSupply);\n\n uint256 thisMinRate;\n uint256 thisRateAtKink;\n uint256 thisBaseRate = baseRate;\n uint256 thisRateMultiplier = rateMultiplier;\n uint256 thisTargetLevel = targetLevel;\n uint256 thisKinkLevel = kinkLevel;\n uint256 thisMaxScaleRate = maxScaleRate;\n\n if (utilRate < thisTargetLevel) {\n // target targetLevel utilization when utilization is under targetLevel\n utilRate = thisTargetLevel;\n }\n\n if (utilRate > thisKinkLevel) {\n /// @dev Scale rate proportionally up to 100%\n uint256 thisMaxRange = WEI_PERCENT_PRECISION - thisKinkLevel; /// Will not overflow.\n\n utilRate -= thisKinkLevel;\n if (utilRate > thisMaxRange) utilRate = thisMaxRange;\n\n // Modified the rate calculation as it is slightly exaggerated around kink level\n // thisRateAtKink = thisRateMultiplier.add(thisBaseRate).mul(thisKinkLevel).div(WEI_PERCENT_PRECISION);\n thisRateAtKink = thisKinkLevel.mul(thisRateMultiplier).div(WEI_PERCENT_PRECISION).add(\n thisBaseRate\n );\n\n nextRate = utilRate\n .mul(SafeMath.sub(thisMaxScaleRate, thisRateAtKink))\n .div(thisMaxRange)\n .add(thisRateAtKink);\n } else {\n nextRate = utilRate.mul(thisRateMultiplier).div(WEI_PERCENT_PRECISION).add(\n thisBaseRate\n );\n\n thisMinRate = thisBaseRate;\n thisRateAtKink = thisRateMultiplier.add(thisBaseRate);\n\n if (nextRate < thisMinRate) nextRate = thisMinRate;\n else if (nextRate > thisRateAtKink) nextRate = thisRateAtKink;\n }\n }\n\n /**\n * @notice Compute the loan size and interest rate.\n * @param leverageAmount The leverage with 18 decimals.\n * @param depositAmount The amount the user deposited in underlying loan tokens.\n * @return borrowAmount The amount of tokens to borrow.\n * @return interestRate The interest rate to pay on the position.\n * */\n function _getMarginBorrowAmountAndRate(uint256 leverageAmount, uint256 depositAmount)\n internal\n view\n returns (uint256 borrowAmount, uint256 interestRate)\n {\n uint256 loanSizeBeforeInterest = depositAmount.mul(leverageAmount).div(10**18);\n /**\n * @dev Mathematical imperfection. we calculate the interest rate based on\n * the loanSizeBeforeInterest, but the actual borrowed amount will be bigger.\n * */\n interestRate = _nextBorrowInterestRate2(loanSizeBeforeInterest, _totalAssetSupply(0));\n /// @dev Assumes that loan, collateral, and interest token are the same.\n borrowAmount = _adjustLoanSize(interestRate, 28 days, loanSizeBeforeInterest);\n }\n\n /**\n * @notice Make sure call is not paused.\n * @dev Used for internal verification if the called function is paused.\n * It throws an exception in case it's not.\n * */\n function _checkPause() internal view {\n /// keccak256(\"iToken_FunctionPause\")\n bytes32 slot =\n keccak256(\n abi.encodePacked(\n msg.sig,\n uint256(0xd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f2)\n )\n );\n bool isPaused;\n assembly {\n isPaused := sload(slot)\n }\n require(!isPaused, \"unauthorized\");\n }\n\n /**\n * @notice Adjusts the loan size to make sure the expected exposure remains after prepaying the interest.\n * @dev loanSizeWithInterest = loanSizeBeforeInterest * 100 / (100 - interestForDuration)\n * @param interestRate The interest rate to pay on the position.\n * @param maxDuration The maximum duration of the position (until rollover).\n * @param loanSizeBeforeInterest The loan size before interest is added.\n * */\n function _adjustLoanSize(\n uint256 interestRate,\n uint256 maxDuration,\n uint256 loanSizeBeforeInterest\n ) internal pure returns (uint256 loanSizeWithInterest) {\n uint256 interestForDuration = interestRate.mul(maxDuration).div(365 days);\n uint256 divisor = uint256(10**20).sub(interestForDuration);\n loanSizeWithInterest = loanSizeBeforeInterest.mul(10**20).div(divisor);\n }\n\n /**\n * @notice Calculate the utilization rate.\n * @dev Utilization rate = assetBorrow / assetSupply\n * @param assetBorrow The amount of loan tokens on debt.\n * @param assetSupply The amount of loan tokens supplied.\n * @return The utilization rate.\n * */\n function _utilizationRate(uint256 assetBorrow, uint256 assetSupply)\n internal\n pure\n returns (uint256)\n {\n if (assetBorrow != 0 && assetSupply != 0) {\n /// U = total_borrow / total_supply\n return assetBorrow.mul(10**20).div(assetSupply);\n }\n }\n}\n" + }, + "contracts/connectors/loantoken/LoanTokenLogicStorage.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"./AdvancedToken.sol\";\n\ncontract LoanTokenLogicStorage is AdvancedToken {\n /// DO NOT ADD VARIABLES HERE - SEE BELOW\n\n /// @dev It is important to maintain the variables order so the delegate\n /// calls can access sovrynContractAddress\n\n /// ------------- MUST BE THE SAME AS IN LoanToken CONTRACT -------------------\n address public sovrynContractAddress;\n address public wrbtcTokenAddress;\n address public target_;\n address public admin;\n /// ------------- END MUST BE THE SAME AS IN LoanToken CONTRACT -------------------\n\n /// @dev Add new variables here on the bottom.\n address public earlyAccessToken; //not used anymore, but staying for upgradability\n address public pauser;\n /** The address of the liquidity mining contract */\n address public liquidityMiningAddress;\n\n /** The address of the staking contract */\n address public stakingContractAddress;\n\n /// @dev Used by flashBorrow function.\n uint256 public constant VERSION = 6;\n /// @dev Used by flashBorrow function.\n address internal constant arbitraryCaller = 0x000F400e6818158D541C3EBE45FE3AA0d47372FF;\n bytes32 internal constant iToken_ProfitSoFar =\n 0x37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb6; // keccak256(\"iToken_ProfitSoFar\")\n uint256 public constant TINY_AMOUNT = 25e13;\n\n function stringToBytes32(string memory source) public pure returns (bytes32 result) {\n bytes memory tempEmptyStringTest = bytes(source);\n if (tempEmptyStringTest.length == 0) {\n return 0x0;\n }\n\n assembly {\n result := mload(add(source, 32))\n }\n }\n\n modifier onlyPauserOrOwner() {\n require(isOwner() || msg.sender == pauser, \"unauthorized\"); // SS02\n _;\n }\n}\n" + }, + "contracts/connectors/loantoken/modules/beaconLogicLM/LoanTokenLogic.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../LoanTokenLogicStandard.sol\";\n\ncontract LoanTokenLogic is LoanTokenLogicStandard {\n /**\n * @notice This function is MANDATORY, which will be called by LoanTokenLogicBeacon and be registered.\n * Every new public function, the signature needs to be included in this function.\n *\n * @dev This function will return the list of function signature in this contract that are available for public call\n * Then this function will be called by LoanTokenLogicBeacon, and the function signatures will be registred in LoanTokenLogicBeacon.\n * @dev To save the gas we can just directly return the list of function signature from this pure function.\n * The other workaround (fancy way) is we can create a storage for the list of the function signature, and then we can store each function signature to that storage from the constructor.\n * Then, in this function we just need to return that storage variable.\n *\n * @return The list of function signatures (bytes4[])\n */\n function getListFunctionSignatures()\n external\n pure\n returns (bytes4[] memory functionSignatures, bytes32 moduleName)\n {\n bytes4[] memory res = new bytes4[](28);\n\n // Loan Token Logic Standard, Trade & Borrow\n res[0] = this.borrow.selector;\n res[1] = this.marginTrade.selector;\n res[2] = this.marginTradeAffiliate.selector;\n res[3] = this.transfer.selector;\n res[4] = this.transferFrom.selector;\n res[5] = this.profitOf.selector;\n res[6] = this.tokenPrice.selector;\n res[7] = this.checkpointPrice.selector;\n res[8] = this.marketLiquidity.selector;\n res[9] = this.avgBorrowInterestRate.selector;\n res[10] = this.borrowInterestRate.selector;\n res[11] = this.nextBorrowInterestRate.selector;\n res[12] = this.supplyInterestRate.selector;\n res[13] = this.nextSupplyInterestRate.selector;\n res[14] = this.totalSupplyInterestRate.selector;\n res[15] = this.totalAssetBorrow.selector;\n res[16] = this.totalAssetSupply.selector;\n res[17] = this.getMaxEscrowAmount.selector;\n res[18] = this.assetBalanceOf.selector;\n res[19] = this.getEstimatedMarginDetails.selector;\n res[20] = this.getDepositAmountForBorrow.selector;\n res[21] = this.getBorrowAmountForDeposit.selector;\n res[22] = this.checkPriceDivergence.selector;\n res[23] = this.calculateSupplyInterestRate.selector;\n\n // Loan Token LM & OVERLOADING function\n /**\n * @notice BE CAREFUL,\n * LoanTokenMintAndBurn also has mint & burn function (overloading).\n * You need to compute the function signature manually --> bytes4(keccak256(\"mint(address,uint256,bool)\"))\n */\n\n // Advanced Token\n res[24] = this.approve.selector;\n\n // Advanced Token Storage\n res[25] = this.totalSupply.selector;\n res[26] = this.balanceOf.selector;\n res[27] = this.allowance.selector;\n\n return (res, stringToBytes32(\"LoanTokenLogic\"));\n }\n}\n" + }, + "contracts/connectors/loantoken/modules/beaconLogicLM/LoanTokenLogicLM.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../LoanTokenLogicSplit.sol\";\n\ncontract LoanTokenLogicLM is LoanTokenLogicSplit {\n /**\n * @notice This function is MANDATORY, which will be called by LoanTokenLogicBeacon and be registered.\n * Every new public function, the signature needs to be included in this function.\n *\n * @dev This function will return the list of function signature in this contract that are available for public call\n * Then this function will be called by LoanTokenLogicBeacon, and the function signatures will be registred in LoanTokenLogicBeacon.\n * @dev To save the gas we can just directly return the list of function signature from this pure function.\n * The other workaround (fancy way) is we can create a storage for the list of the function signature, and then we can store each function signature to that storage from the constructor.\n * Then, in this function we just need to return that storage variable.\n *\n * @return The list of function signatures (bytes4[])\n */\n function getListFunctionSignatures()\n external\n pure\n returns (bytes4[] memory functionSignatures, bytes32 moduleName)\n {\n bytes4[] memory res = new bytes4[](4);\n\n // Loan Token LM & OVERLOADING function\n /**\n * @notice BE CAREFUL,\n * LoanTokenLogicStandard also has mint & burn function (overloading).\n * You need to compute the function signature manually --> bytes4(keccak256(\"mint(address,uint256,bool)\"))\n */\n res[0] = bytes4(keccak256(\"mint(address,uint256)\")); /// LoanTokenLogicStandard\n res[1] = bytes4(keccak256(\"mint(address,uint256,bool)\")); /// LoanTokenLogicLM\n res[2] = bytes4(keccak256(\"burn(address,uint256)\")); /// LoanTokenLogicStandard\n res[3] = bytes4(keccak256(\"burn(address,uint256,bool)\")); /// LoanTokenLogicLM\n\n return (res, stringToBytes32(\"LoanTokenLogicLM\"));\n }\n\n /**\n * @notice deposit into the lending pool and optionally participate at the Liquidity Mining Program\n * @param receiver the receiver of the tokens\n * @param depositAmount The amount of underlying tokens provided on the loan.\n *\t\t\t\t\t\t(Not the number of loan tokens to mint).\n * @param useLM if true -> deposit the pool tokens into the Liquidity Mining contract\n */\n function mint(\n address receiver,\n uint256 depositAmount,\n bool useLM\n ) external nonReentrant globallyNonReentrant returns (uint256 minted) {\n if (useLM) return _mintWithLM(receiver, depositAmount);\n else return _mintToken(receiver, depositAmount);\n }\n\n /**\n * @notice withdraws from the lending pool and optionally retrieves the pool tokens from the\n * Liquidity Mining Contract\n * @param receiver the receiver of the underlying tokens. note: potetial LM rewards are always sent to the msg.sender\n * @param burnAmount The amount of pool tokens to redeem.\n * @param useLM if true -> deposit the pool tokens into the Liquidity Mining contract\n */\n function burn(\n address receiver,\n uint256 burnAmount,\n bool useLM\n ) external nonReentrant globallyNonReentrant returns (uint256 redeemed) {\n if (useLM) redeemed = _burnFromLM(burnAmount);\n else redeemed = _burnToken(burnAmount);\n //this needs to be here and not in _burnTokens because of the WRBTC implementation\n if (redeemed != 0) {\n _safeTransfer(loanTokenAddress, receiver, redeemed, \"asset transfer failed\");\n }\n }\n}\n" + }, + "contracts/connectors/loantoken/modules/beaconLogicWRBTC/LoanTokenLogicWrbtc.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../LoanTokenLogicStandard.sol\";\n\ncontract LoanTokenLogicWrbtc is LoanTokenLogicStandard {\n /**\n * @notice This function is MANDATORY, which will be called by LoanTokenLogicBeacon and be registered.\n * Every new public function, the signature needs to be included in this function.\n *\n * @dev This function will return the list of function signature in this contract that are available for public call\n * Then this function will be called by LoanTokenLogicBeacon, and the function signatures will be registred in LoanTokenLogicBeacon.\n * @dev To save the gas we can just directly return the list of function signature from this pure function.\n * The other workaround (fancy way) is we can create a storage for the list of the function signature, and then we can store each function signature to that storage from the constructor.\n * Then, in this function we just need to return that storage variable.\n *\n * @return The list of function signatures (bytes4[])\n */\n function getListFunctionSignatures()\n external\n pure\n returns (bytes4[] memory functionSignatures, bytes32 moduleName)\n {\n bytes4[] memory res = new bytes4[](28);\n\n // Loan Token Logic Standard, Trade & Borrow\n res[0] = this.borrow.selector;\n res[1] = this.marginTrade.selector;\n res[2] = this.marginTradeAffiliate.selector;\n res[3] = this.transfer.selector;\n res[4] = this.transferFrom.selector;\n res[5] = this.profitOf.selector;\n res[6] = this.tokenPrice.selector;\n res[7] = this.checkpointPrice.selector;\n res[8] = this.marketLiquidity.selector;\n res[9] = this.avgBorrowInterestRate.selector;\n res[10] = this.borrowInterestRate.selector;\n res[11] = this.nextBorrowInterestRate.selector;\n res[12] = this.supplyInterestRate.selector;\n res[13] = this.nextSupplyInterestRate.selector;\n res[14] = this.totalSupplyInterestRate.selector;\n res[15] = this.totalAssetBorrow.selector;\n res[16] = this.totalAssetSupply.selector;\n res[17] = this.getMaxEscrowAmount.selector;\n res[18] = this.assetBalanceOf.selector;\n res[19] = this.getEstimatedMarginDetails.selector;\n res[20] = this.getDepositAmountForBorrow.selector;\n res[21] = this.getBorrowAmountForDeposit.selector;\n res[22] = this.checkPriceDivergence.selector;\n res[23] = this.calculateSupplyInterestRate.selector;\n\n // Advanced Token\n res[24] = this.approve.selector;\n\n // Advanced Token Storage\n res[25] = this.totalSupply.selector;\n res[26] = this.balanceOf.selector;\n res[27] = this.allowance.selector;\n\n return (res, stringToBytes32(\"LoanTokenLogicWrbtc\"));\n }\n\n /**\n * @dev internal override functions\n * @dev Put all of internal override function dedicated to the loanTokenWrtbc module here\n * e.g: _verifyTransfers will override the implementation of _verifyTransfers in loanTokenLogicSplit\n */\n\n /**\n * @notice Handle transfers prior to adding newPrincipal to loanTokenSent.\n *\n * @param collateralTokenAddress The address of the collateral token.\n * @param sentAddresses The struct which contains addresses of\n * - lender\n * - borrower\n * - receiver\n * - manager\n *\n * @param sentAmounts The struct which contains uint256 of:\n * - interestRate\n * - newPrincipal\n * - interestInitialAmount\n * - loanTokenSent\n * - collateralTokenSent\n *\n * @param withdrawalAmount The amount to withdraw.\n *\n * @return msgValue The amount of value sent.\n * */\n function _verifyTransfers(\n address collateralTokenAddress,\n MarginTradeStructHelpers.SentAddresses memory sentAddresses,\n MarginTradeStructHelpers.SentAmounts memory sentAmounts,\n uint256 withdrawalAmount\n ) internal returns (uint256 msgValue) {\n address _wrbtcToken = wrbtcTokenAddress;\n address _loanTokenAddress = _wrbtcToken;\n address receiver = sentAddresses.receiver;\n uint256 newPrincipal = sentAmounts.newPrincipal;\n uint256 loanTokenSent = sentAmounts.loanTokenSent;\n uint256 collateralTokenSent = sentAmounts.collateralTokenSent;\n\n require(_loanTokenAddress != collateralTokenAddress, \"26\");\n\n msgValue = msg.value;\n\n if (withdrawalAmount != 0) {\n /// withdrawOnOpen == true\n IWrbtcERC20(_wrbtcToken).withdraw(withdrawalAmount);\n Address.sendValue(receiver, withdrawalAmount);\n if (newPrincipal > withdrawalAmount) {\n _safeTransfer(\n _loanTokenAddress,\n sovrynContractAddress,\n newPrincipal - withdrawalAmount,\n \"\"\n );\n }\n } else {\n _safeTransfer(_loanTokenAddress, sovrynContractAddress, newPrincipal, \"27\");\n }\n\n if (collateralTokenSent != 0) {\n _safeTransferFrom(\n collateralTokenAddress,\n msg.sender,\n sovrynContractAddress,\n collateralTokenSent,\n \"28\"\n );\n }\n\n if (loanTokenSent != 0) {\n if (msgValue != 0 && msgValue >= loanTokenSent) {\n IWrbtc(_wrbtcToken).deposit.value(loanTokenSent)();\n _safeTransfer(_loanTokenAddress, sovrynContractAddress, loanTokenSent, \"29\");\n msgValue -= loanTokenSent;\n } else {\n _safeTransferFrom(\n _loanTokenAddress,\n msg.sender,\n sovrynContractAddress,\n loanTokenSent,\n \"29\"\n );\n }\n }\n }\n}\n" + }, + "contracts/connectors/loantoken/modules/beaconLogicWRBTC/LoanTokenLogicWrbtcLM.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../LoanTokenLogicSplit.sol\";\n\ncontract LoanTokenLogicWrbtcLM is LoanTokenLogicSplit {\n /**\n * @notice This function is MANDATORY, which will be called by LoanTokenLogicBeacon and be registered.\n * Every new public function, the signature needs to be included in this function.\n *\n * @dev This function will return the list of function signature in this contract that are available for public call\n * Then this function will be called by LoanTokenLogicBeacon, and the function signatures will be registred in LoanTokenLogicBeacon.\n * @dev To save the gas we can just directly return the list of function signature from this pure function.\n * The other workaround (fancy way) is we can create a storage for the list of the function signature, and then we can store each function signature to that storage from the constructor.\n * Then, in this function we just need to return that storage variable.\n *\n * @return The list of function signatures (bytes4[])\n */\n function getListFunctionSignatures()\n external\n pure\n returns (bytes4[] memory functionSignatures, bytes32 moduleName)\n {\n bytes4[] memory res = new bytes4[](4);\n\n // Loan Token Mint and Burn.\n res[0] = this.mint.selector;\n res[1] = this.burn.selector;\n\n // Loan Token WRBTC\n res[2] = this.mintWithBTC.selector;\n res[3] = this.burnToBTC.selector;\n\n return (res, stringToBytes32(\"LoanTokenLogicWrbtcLM\"));\n }\n\n function mintWithBTC(address receiver, bool useLM)\n external\n payable\n nonReentrant\n globallyNonReentrant\n returns (uint256 mintAmount)\n {\n if (useLM) return _mintWithLM(receiver, msg.value);\n else return _mintToken(receiver, msg.value);\n }\n\n function burnToBTC(\n address receiver,\n uint256 burnAmount,\n bool useLM\n ) external nonReentrant globallyNonReentrant returns (uint256 loanAmountPaid) {\n loanAmountPaid = useLM ? _burnFromLM(burnAmount) : _burnToken(burnAmount);\n\n if (loanAmountPaid != 0) {\n IWrbtcERC20(wrbtcTokenAddress).withdraw(loanAmountPaid);\n Address.sendValue(receiver, loanAmountPaid);\n }\n }\n}\n" + }, + "contracts/connectors/loantoken/modules/shared/LoanTokenSettingsLowerAdmin.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../AdvancedToken.sol\";\nimport \"../../interfaces/ProtocolSettingsLike.sol\";\nimport \"../../LoanTokenLogicStorage.sol\";\n\ncontract LoanTokenSettingsLowerAdmin is LoanTokenLogicStorage {\n using SafeMath for uint256;\n\n /// @dev TODO: Check for restrictions in this contract.\n modifier onlyAdmin() {\n require(isOwner() || msg.sender == admin, \"unauthorized\");\n _;\n }\n\n /* Events */\n\n event SetTransactionLimits(address[] addresses, uint256[] limits);\n event ToggledFunctionPaused(string functionId, bool prevFlag, bool newFlag);\n event WithdrawRBTCTo(address indexed to, uint256 amount);\n\n /* Functions */\n\n /**\n * @notice This function is MANDATORY, which will be called by LoanTokenLogicBeacon and be registered.\n * Every new public function, the signature needs to be included in this function.\n *\n * @dev This function will return the list of function signature in this contract that are available for public call\n * Then this function will be called by LoanTokenLogicBeacon, and the function signatures will be registred in LoanTokenLogicBeacon.\n * @dev To save the gas we can just directly return the list of function signature from this pure function.\n * The other workaround (fancy way) is we can create a storage for the list of the function signature, and then we can store each function signature to that storage from the constructor.\n * Then, in this function we just need to return that storage variable.\n *\n * @return The list of function signatures (bytes4[])\n */\n function getListFunctionSignatures()\n external\n pure\n returns (bytes4[] memory functionSignatures, bytes32 moduleName)\n {\n bytes4[] memory res = new bytes4[](15);\n res[0] = this.setAdmin.selector;\n res[1] = this.setPauser.selector;\n res[2] = this.setupLoanParams.selector;\n res[3] = this.disableLoanParams.selector;\n res[4] = this.setDemandCurve.selector;\n res[5] = this.toggleFunctionPause.selector;\n res[6] = this.setTransactionLimits.selector;\n res[7] = this.changeLoanTokenNameAndSymbol.selector;\n res[8] = this.pauser.selector;\n res[9] = this.setLiquidityMiningAddress.selector;\n res[10] = this.withdrawRBTCTo.selector;\n res[11] = this.getLiquidityMiningAddress.selector;\n res[12] = this.checkPause.selector;\n res[13] = this.setStakingContractAddress.selector;\n res[14] = this.getStakingContractAddress.selector;\n return (res, stringToBytes32(\"LoanTokenSettingsLowerAdmin\"));\n }\n\n /**\n * @notice Set admin account.\n * @param _admin The address of the account to grant admin permissions.\n * */\n function setAdmin(address _admin) public onlyOwner {\n admin = _admin;\n }\n\n /**\n * @notice Set pauser account.\n * @param _pauser The address of the account to grant pause permissions.\n * */\n function setPauser(address _pauser) public onlyOwner {\n pauser = _pauser;\n }\n\n /**\n * @notice Fallback function not allowed\n * */\n function() external {\n revert(\"LoanTokenSettingsLowerAdmin - fallback not allowed\");\n }\n\n /**\n * @notice Set loan token parameters.\n *\n * @param loanParamsList The array of loan parameters.\n * @param areTorqueLoans Whether the loan is a torque loan.\n * */\n function setupLoanParams(\n LoanParamsStruct.LoanParams[] memory loanParamsList,\n bool areTorqueLoans\n ) public onlyAdmin {\n bytes32[] memory loanParamsIdList;\n address _loanTokenAddress = loanTokenAddress;\n\n for (uint256 i = 0; i < loanParamsList.length; i++) {\n loanParamsList[i].loanToken = _loanTokenAddress;\n loanParamsList[i].maxLoanTerm = areTorqueLoans ? 0 : 28 days;\n }\n\n loanParamsIdList = ProtocolSettingsLike(sovrynContractAddress).setupLoanParams(\n loanParamsList\n );\n for (uint256 i = 0; i < loanParamsIdList.length; i++) {\n loanParamsIds[\n uint256(\n keccak256(\n abi.encodePacked(\n loanParamsList[i].collateralToken,\n areTorqueLoans /// isTorqueLoan\n )\n )\n )\n ] = loanParamsIdList[i];\n }\n }\n\n /**\n * @notice Disable loan token parameters.\n *\n * @param collateralTokens The array of collateral tokens.\n * @param isTorqueLoans Whether the loan is a torque loan.\n * */\n function disableLoanParams(address[] calldata collateralTokens, bool[] calldata isTorqueLoans)\n external\n onlyAdmin\n {\n require(collateralTokens.length == isTorqueLoans.length, \"count mismatch\");\n\n bytes32[] memory loanParamsIdList = new bytes32[](collateralTokens.length);\n for (uint256 i = 0; i < collateralTokens.length; i++) {\n uint256 id =\n uint256(keccak256(abi.encodePacked(collateralTokens[i], isTorqueLoans[i])));\n loanParamsIdList[i] = loanParamsIds[id];\n delete loanParamsIds[id];\n }\n\n ProtocolSettingsLike(sovrynContractAddress).disableLoanParams(loanParamsIdList);\n }\n\n /**\n * @notice Set loan token parameters about the demand curve.\n *\n * @dev These params should be percentages represented\n * like so: 5% = 5000000000000000000 /// 18 digits precision.\n * rateMultiplier + baseRate can't exceed 100%\n *\n * To maintain a healthy credit score, it's important to keep your\n * credit utilization rate (CUR) low (_lowUtilBaseRate). In general\n * you don't want your CUR to exceed 30%, but increasingly financial\n * experts are recommending that you don't want to go above 10% if you\n * really want an excellent credit score.\n *\n * Interest rates tend to cluster around the kink level of a kinked\n * interest rate model. More info at https://arxiv.org/pdf/2006.13922.pdf\n * and https://compound.finance/governance/proposals/12\n *\n * @param _baseRate The interest rate.\n * @param _rateMultiplier The precision multiplier for base rate.\n * @param _lowUtilBaseRate The credit utilization rate (CUR) low value.\n * @param _lowUtilRateMultiplier The precision multiplier for low util base rate.\n * @param _targetLevel The target level.\n * @param _kinkLevel The level that interest rates cluster on kinked model.\n * @param _maxScaleRate The maximum rate of the scale.\n * */\n function setDemandCurve(\n uint256 _baseRate,\n uint256 _rateMultiplier,\n uint256 _lowUtilBaseRate,\n uint256 _lowUtilRateMultiplier,\n uint256 _targetLevel,\n uint256 _kinkLevel,\n uint256 _maxScaleRate\n ) public onlyAdmin {\n require(_rateMultiplier.add(_baseRate) <= WEI_PERCENT_PRECISION, \"curve params too high\");\n require(\n _lowUtilRateMultiplier.add(_lowUtilBaseRate) <= WEI_PERCENT_PRECISION,\n \"curve params too high\"\n );\n\n require(\n _targetLevel <= WEI_PERCENT_PRECISION && _kinkLevel <= WEI_PERCENT_PRECISION,\n \"levels too high\"\n );\n\n baseRate = _baseRate;\n rateMultiplier = _rateMultiplier;\n lowUtilBaseRate = _lowUtilBaseRate;\n lowUtilRateMultiplier = _lowUtilRateMultiplier;\n\n targetLevel = _targetLevel; /// 80 ether\n kinkLevel = _kinkLevel; /// 90 ether\n maxScaleRate = _maxScaleRate; /// 100 ether\n }\n\n /**\n * @notice Set the pause flag for a function to true or false.\n *\n * @dev Combining the hash of \"iToken_FunctionPause\" string and a function\n * selector gets a slot to write a flag for pause state.\n *\n * @param funcId The ID of a function, the selector.\n * @param isPaused true/false value of the flag.\n * */\n function toggleFunctionPause(\n string memory funcId, /// example: \"mint(uint256,uint256)\"\n bool isPaused\n ) public onlyPauserOrOwner {\n bool paused;\n /// keccak256(\"iToken_FunctionPause\")\n bytes32 slot =\n keccak256(\n abi.encodePacked(\n bytes4(keccak256(abi.encodePacked(funcId))),\n uint256(0xd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f2)\n )\n );\n assembly {\n paused := sload(slot)\n }\n require(paused != isPaused, \"isPaused is already set to that value\");\n assembly {\n sstore(slot, isPaused)\n }\n emit ToggledFunctionPaused(funcId, !isPaused, isPaused);\n }\n\n /**\n * Set the transaction limit per token address.\n * @param addresses The token addresses.\n * @param limits The limit denominated in the currency of the token address.\n * */\n function setTransactionLimits(address[] memory addresses, uint256[] memory limits)\n public\n onlyAdmin\n {\n require(addresses.length == limits.length, \"mismatched array lengths\");\n for (uint256 i = 0; i < addresses.length; i++) {\n transactionLimit[addresses[i]] = limits[i];\n }\n emit SetTransactionLimits(addresses, limits);\n }\n\n /**\n *\t@notice Update the loan token parameters.\n *\t@param _name The new name of the loan token.\n *\t@param _symbol The new symbol of the loan token.\n * */\n function changeLoanTokenNameAndSymbol(string memory _name, string memory _symbol)\n public\n onlyAdmin\n {\n name = _name;\n symbol = _symbol;\n }\n\n /**\n * @notice Withdraws RBTC from the contract by Multisig.\n * @param _receiverAddress The address where the rBTC has to be transferred.\n * @param _amount The amount of rBTC to be transferred.\n */\n function withdrawRBTCTo(address payable _receiverAddress, uint256 _amount) external onlyOwner {\n require(_receiverAddress != address(0), \"receiver address invalid\");\n require(_amount > 0, \"non-zero withdraw amount expected\");\n require(_amount <= address(this).balance, \"withdraw amount cannot exceed balance\");\n _receiverAddress.transfer(_amount);\n emit WithdrawRBTCTo(_receiverAddress, _amount);\n }\n\n /**\n * @notice sets the liquidity mining contract address\n * @param LMAddress the address of the liquidity mining contract\n */\n function setLiquidityMiningAddress(address LMAddress) external onlyOwner {\n liquidityMiningAddress = LMAddress;\n }\n\n /**\n\t * @notice We need separate getter for newly added storage variable\n\t * @notice Getter for liquidityMiningAddress\n\n\t * @return liquidityMiningAddress\n\t */\n function getLiquidityMiningAddress() public view returns (address) {\n return liquidityMiningAddress;\n }\n\n /**\n * @notice sets the staking contract address\n * @param _stakingContractAddress the address of the staking contract\n */\n function setStakingContractAddress(address _stakingContractAddress) external onlyOwner {\n stakingContractAddress = _stakingContractAddress;\n }\n\n /**\n\t * @notice We need separate getter for newly added storage variable\n\t * @notice Getter for stakingContractAddress\n\n\t * @return stakingContractAddress\n\t */\n function getStakingContractAddress() public view returns (address) {\n return stakingContractAddress;\n }\n\n /**\n * @notice Check whether a function is paused.\n *\n * @dev Used to read externally from the smart contract to see if a\n * function is paused.\n *\n * @param funcId The function ID, the selector.\n *\n * @return isPaused Whether the function is paused: true or false.\n * */\n function checkPause(string memory funcId) public view returns (bool isPaused) {\n bytes4 sig = bytes4(keccak256(abi.encodePacked(funcId)));\n bytes32 slot =\n keccak256(\n abi.encodePacked(\n sig,\n uint256(0xd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f2)\n )\n );\n assembly {\n isPaused := sload(slot)\n }\n return isPaused;\n }\n}\n" + }, + "contracts/connectors/loantoken/Pausable.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\n/**\n * @title Pausable contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized margin\n * trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * The contract implements pausable functionality by reading on slots the\n * pause state of contract functions.\n * */\ncontract Pausable {\n /// keccak256(\"Pausable_FunctionPause\")\n bytes32 internal constant Pausable_FunctionPause =\n 0xa7143c84d793a15503da6f19bf9119a2dac94448ca45d77c8bf08f57b2e91047;\n\n modifier pausable(bytes4 sig) {\n require(!_isPaused(sig), \"unauthorized\");\n _;\n }\n\n /**\n * @notice Check whether a function is paused.\n *\n * @dev Used to read externally from the smart contract to see if a\n * function is paused.\n *\n * @param sig The function ID, the selector on bytes4.\n *\n * @return isPaused Whether the function is paused: true or false.\n * */\n function _isPaused(bytes4 sig) internal view returns (bool isPaused) {\n bytes32 slot = keccak256(abi.encodePacked(sig, Pausable_FunctionPause));\n assembly {\n isPaused := sload(slot)\n }\n }\n}\n" + }, + "contracts/core/Objects.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"./objects/LoanStruct.sol\";\nimport \"./objects/LoanParamsStruct.sol\";\nimport \"./objects/OrderStruct.sol\";\nimport \"./objects/LenderInterestStruct.sol\";\nimport \"./objects/LoanInterestStruct.sol\";\n\n/**\n * @title Objects contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract inherints and aggregates several structures needed to handle\n * loans on the protocol.\n * */\ncontract Objects is\n LoanStruct,\n LoanParamsStruct,\n OrderStruct,\n LenderInterestStruct,\n LoanInterestStruct\n{\n\n}\n" + }, + "contracts/core/objects/LenderInterestStruct.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\n/**\n * @title The Lender Interest.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the storage structure of the Lender Interest.\n * */\ncontract LenderInterestStruct {\n struct LenderInterest {\n uint256 principalTotal; /// Total borrowed amount outstanding of asset.\n uint256 owedPerDay; /// Interest owed per day for all loans of asset.\n uint256 owedTotal; /// Total interest owed for all loans of asset (assuming they go to full term).\n uint256 paidTotal; /// Total interest paid so far for asset.\n uint256 updatedTimestamp; /// Last update.\n }\n}\n" + }, + "contracts/core/objects/LoanInterestStruct.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\n/**\n * @title The Loan Interest.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the storage structure of the Loan Interest.\n * */\ncontract LoanInterestStruct {\n struct LoanInterest {\n uint256 owedPerDay; /// Interest owed per day for loan.\n uint256 depositTotal; /// Total escrowed interest for loan.\n uint256 updatedTimestamp; /// Last update.\n }\n}\n" + }, + "contracts/core/objects/LoanParamsStruct.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\n/**\n * @title The Loan Parameters.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the storage structure of the Loan Parameters.\n * */\ncontract LoanParamsStruct {\n struct LoanParams {\n /// @dev ID of loan params object.\n bytes32 id;\n /// @dev If false, this object has been disabled by the owner and can't\n /// be used for future loans.\n bool active;\n /// @dev Owner of this object.\n address owner;\n /// @dev The token being loaned.\n address loanToken;\n /// @dev The required collateral token.\n address collateralToken;\n /// @dev The minimum allowed initial margin.\n uint256 minInitialMargin;\n /// @dev An unhealthy loan when current margin is at or below this value.\n uint256 maintenanceMargin;\n /// @dev The maximum term for new loans (0 means there's no max term).\n uint256 maxLoanTerm;\n }\n}\n" + }, + "contracts/core/objects/LoanStruct.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\n/**\n * @title The Loan Object.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the storage structure of the Loan Object.\n * */\ncontract LoanStruct {\n struct Loan {\n bytes32 id; /// ID of the loan.\n bytes32 loanParamsId; /// The linked loan params ID.\n bytes32 pendingTradesId; /// The linked pending trades ID.\n bool active; /// If false, the loan has been fully closed.\n uint256 principal; /// Total borrowed amount outstanding.\n uint256 collateral; /// Total collateral escrowed for the loan.\n uint256 startTimestamp; /// Loan start time.\n uint256 endTimestamp; /// For active loans, this is the expected loan end time, for in-active loans, is the actual (past) end time.\n uint256 startMargin; /// Initial margin when the loan opened.\n uint256 startRate; /// Reference rate when the loan opened for converting collateralToken to loanToken.\n address borrower; /// Borrower of this loan.\n address lender; /// Lender of this loan.\n }\n}\n" + }, + "contracts/core/objects/OrderStruct.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\n/**\n * @title The Loan Order.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the storage structure of the Loan Order.\n * */\ncontract OrderStruct {\n struct Order {\n uint256 lockedAmount; /// Escrowed amount waiting for a counterparty.\n uint256 interestRate; /// Interest rate defined by the creator of this order.\n uint256 minLoanTerm; /// Minimum loan term allowed.\n uint256 maxLoanTerm; /// Maximum loan term allowed.\n uint256 createdTimestamp; /// Timestamp when this order was created.\n uint256 expirationTimestamp; /// Timestamp when this order expires.\n }\n}\n" + }, + "contracts/core/Protocol.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./State.sol\";\n\n/**\n * @title Sovryn Protocol contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the proxy functionality to deploy Protocol anchor\n * and logic apart, turning it upgradable.\n *\n * @dev TODO: can I change this proxy to EIP-1822 proxy standard, please.\n * https://eips.ethereum.org/EIPS/eip-1822\n * */\ncontract sovrynProtocol is State {\n /**\n * @notice Fallback function performs a delegate call\n * to the actual implementation address is pointing this proxy.\n * Returns whatever the implementation call returns.\n * */\n function() external payable {\n if (gasleft() <= 2300) {\n return;\n }\n\n address target = logicTargets[msg.sig];\n require(target != address(0), \"target not active\");\n\n bytes memory data = msg.data;\n assembly {\n let result := delegatecall(gas, target, add(data, 0x20), mload(data), 0, 0)\n let size := returndatasize\n let ptr := mload(0x40)\n returndatacopy(ptr, 0, size)\n switch result\n case 0 {\n revert(ptr, size)\n }\n default {\n return(ptr, size)\n }\n }\n }\n\n /**\n * @notice External owner target initializer.\n * @param target The target addresses.\n * */\n function replaceContract(address target) external onlyOwner {\n (bool success, ) =\n target.delegatecall(abi.encodeWithSignature(\"initialize(address)\", target));\n require(success, \"setup failed\");\n }\n\n /**\n * @notice External owner setter for target addresses.\n * @param sigsArr The array of signatures.\n * @param targetsArr The array of addresses.\n * */\n function setTargets(string[] calldata sigsArr, address[] calldata targetsArr)\n external\n onlyOwner\n {\n require(sigsArr.length == targetsArr.length, \"count mismatch\");\n\n for (uint256 i = 0; i < sigsArr.length; i++) {\n _setTarget(bytes4(keccak256(abi.encodePacked(sigsArr[i]))), targetsArr[i]);\n }\n }\n\n /**\n * @notice External getter for target addresses.\n * @param sig The signature.\n * @return The address for a given signature.\n * */\n function getTarget(string calldata sig) external view returns (address) {\n return logicTargets[bytes4(keccak256(abi.encodePacked(sig)))];\n }\n}\n" + }, + "contracts/core/State.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"./Objects.sol\";\nimport \"../mixins/EnumerableAddressSet.sol\";\nimport \"../mixins/EnumerableBytes32Set.sol\";\nimport \"../openzeppelin/ReentrancyGuard.sol\";\nimport \"../openzeppelin/Ownable.sol\";\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"../interfaces/IWrbtcERC20.sol\";\nimport \"../reentrancy/SharedReentrancyGuard.sol\";\n\n/**\n * @title State contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the storage values of the Protocol.\n * */\ncontract State is Objects, ReentrancyGuard, SharedReentrancyGuard, Ownable {\n using SafeMath for uint256;\n using EnumerableAddressSet for EnumerableAddressSet.AddressSet; // enumerable map of addresses\n using EnumerableBytes32Set for EnumerableBytes32Set.Bytes32Set; // enumerable map of bytes32 or addresses\n\n /// Handles asset reference price lookups.\n address public priceFeeds;\n\n /// Handles asset swaps using dex liquidity.\n address public swapsImpl;\n\n /// Contract registry address of the Sovryn swap network.\n address public sovrynSwapContractRegistryAddress;\n\n /// Implementations of protocol functions.\n mapping(bytes4 => address) public logicTargets;\n\n /// Loans: loanId => Loan\n mapping(bytes32 => Loan) public loans;\n\n /// Loan parameters: loanParamsId => LoanParams\n mapping(bytes32 => LoanParams) public loanParams;\n\n /// lender => orderParamsId => Order\n mapping(address => mapping(bytes32 => Order)) public lenderOrders;\n\n /// borrower => orderParamsId => Order\n mapping(address => mapping(bytes32 => Order)) public borrowerOrders;\n\n /// loanId => delegated => approved\n mapping(bytes32 => mapping(address => bool)) public delegatedManagers;\n\n /**\n *** Interest ***\n **/\n\n /// lender => loanToken => LenderInterest object\n mapping(address => mapping(address => LenderInterest)) public lenderInterest;\n\n /// loanId => LoanInterest object\n mapping(bytes32 => LoanInterest) public loanInterest;\n\n /**\n *** Internals ***\n **/\n\n /// Implementations set.\n EnumerableBytes32Set.Bytes32Set internal logicTargetsSet;\n\n /// Active loans set.\n EnumerableBytes32Set.Bytes32Set internal activeLoansSet;\n\n /// Lender loans set.\n mapping(address => EnumerableBytes32Set.Bytes32Set) internal lenderLoanSets;\n\n /// Borrow loans set.\n mapping(address => EnumerableBytes32Set.Bytes32Set) internal borrowerLoanSets;\n\n /// User loan params set.\n mapping(address => EnumerableBytes32Set.Bytes32Set) internal userLoanParamSets;\n\n /// Address controlling fee withdrawals.\n address public feesController;\n\n /// 10% fee /// Fee taken from lender interest payments.\n uint256 public lendingFeePercent = 10**19;\n\n /// Total interest fees received and not withdrawn per asset.\n mapping(address => uint256) public lendingFeeTokensHeld;\n\n /// Total interest fees withdraw per asset.\n /// lifetime fees = lendingFeeTokensHeld + lendingFeeTokensPaid\n mapping(address => uint256) public lendingFeeTokensPaid;\n\n /// 0.15% fee /// Fee paid for each trade.\n uint256 public tradingFeePercent = 15 * 10**16;\n\n /// Total trading fees received and not withdrawn per asset.\n mapping(address => uint256) public tradingFeeTokensHeld;\n\n /// Total trading fees withdraw per asset\n /// lifetime fees = tradingFeeTokensHeld + tradingFeeTokensPaid\n mapping(address => uint256) public tradingFeeTokensPaid;\n\n /// 0.09% fee /// Origination fee paid for each loan.\n uint256 public borrowingFeePercent = 9 * 10**16;\n\n /// Total borrowing fees received and not withdrawn per asset.\n mapping(address => uint256) public borrowingFeeTokensHeld;\n\n /// Total borrowing fees withdraw per asset.\n /// lifetime fees = borrowingFeeTokensHeld + borrowingFeeTokensPaid\n mapping(address => uint256) public borrowingFeeTokensPaid;\n\n /// Current protocol token deposit balance.\n uint256 public protocolTokenHeld;\n\n /// Lifetime total payout of protocol token.\n uint256 public protocolTokenPaid;\n\n /// 5% fee share in form of SOV /// Fee share for affiliate program.\n uint256 public affiliateFeePercent = 5 * 10**18;\n\n /// 5% collateral discount /// Discount on collateral for liquidators.\n uint256 public liquidationIncentivePercent = 5 * 10**18;\n\n /// loanPool => underlying\n mapping(address => address) public loanPoolToUnderlying;\n\n /// underlying => loanPool\n mapping(address => address) public underlyingToLoanPool;\n\n /// Loan pools set.\n EnumerableBytes32Set.Bytes32Set internal loanPoolsSet;\n\n /// Supported tokens for swaps.\n mapping(address => bool) public supportedTokens;\n\n /// % disagreement between swap rate and reference rate.\n uint256 public maxDisagreement = 5 * 10**18;\n\n /// Used as buffer for swap source amount estimations.\n uint256 public sourceBuffer = 10000;\n\n /// Maximum support swap size in rBTC\n uint256 public maxSwapSize = 50 ether;\n\n /// Nonce per borrower. Used for loan id creation.\n mapping(address => uint256) public borrowerNonce;\n\n /// Rollover transaction costs around 0.0000168 rBTC, it is denominated in wrBTC.\n uint256 public rolloverBaseReward = 16800000000000;\n uint256 public rolloverFlexFeePercent = 0.1 ether; /// 0.1%\n\n IWrbtcERC20 public wrbtcToken;\n address public protocolTokenAddress;\n\n /// 50% fee rebate\n /// potocolToken reward to user, it is worth % of trading/borrowing fee.\n uint256 public feeRebatePercent = 50 * 10**18;\n\n address public admin;\n\n /// For modules interaction.\n address public protocolAddress;\n\n /**\n *** Affiliates ***\n **/\n\n /// The flag is set on the user's first trade.\n mapping(address => bool) public userNotFirstTradeFlag;\n\n /// User => referrer (affiliate).\n mapping(address => address) public affiliatesUserReferrer;\n\n /// List of referral addresses affiliated to the referrer.\n mapping(address => EnumerableAddressSet.AddressSet) internal referralsList;\n\n /// @dev Referral threshold for paying out to the referrer.\n /// The referrer reward is being accumulated and locked until the threshold is passed.\n uint256 public minReferralsToPayout = 3;\n\n /// @dev Total affiliate SOV rewards that held in the protocol\n /// (Because the minimum referrals is less than the rule)\n mapping(address => uint256) public affiliateRewardsHeld;\n\n /// @dev For affiliates SOV Bonus proccess.\n address public sovTokenAddress;\n address public lockedSOVAddress;\n\n /// @dev 20% fee share of trading token fee.\n /// Fee share of trading token fee for affiliate program.\n uint256 public affiliateTradingTokenFeePercent = 20 * 10**18;\n\n /// @dev Addresses of tokens in which commissions were paid to referrers.\n mapping(address => EnumerableAddressSet.AddressSet) internal affiliatesReferrerTokensList;\n\n /// @dev [referrerAddress][tokenAddress] is a referrer's token balance of accrued fees.\n mapping(address => mapping(address => uint256)) public affiliatesReferrerBalances;\n\n mapping(address => mapping(address => uint256)) public specialRebates; // Special rate rebates for spesific pair -- if not set, then use the default one\n bool public pause; //Flag to pause all protocol modules\n\n uint256 internal swapExtrernalFeePercent; /// Fee percentage for protocol swap\n\n /// @dev Defines the portion of the trading rebate rewards (SOV) which is to be paid out in a liquid form in basis points. The rest is vested. The max value is 9999 (means 99.99% liquid, 0.01% vested)\n uint256 internal tradingRebateRewardsBasisPoint;\n\n /// @dev Defines the defaultPath of conversion swap. This is created to prevent the non-rbtc pairs returning the shortest path which will not give the best rate.\n /// Will be used in internal swap.\n mapping(address => mapping(address => IERC20[])) internal defaultPathConversion;\n\n address internal pauser;\n\n /**\n * @notice Add signature and target to storage.\n * @dev Protocol is a proxy and requires a way to add every\n * module function dynamically during deployment.\n * */\n function _setTarget(bytes4 sig, address target) internal {\n logicTargets[sig] = target;\n\n if (target != address(0)) {\n logicTargetsSet.addBytes32(bytes32(sig));\n } else {\n logicTargetsSet.removeBytes32(bytes32(sig));\n }\n }\n\n modifier onlyAdminOrOwner() {\n require(isOwner() || admin == (msg.sender), \"unauthorized\");\n _;\n }\n\n modifier onlyPauserOrOwner() {\n require(isOwner() || pauser == (msg.sender), \"unauthorized\");\n _;\n }\n}\n" + }, + "contracts/escrow/Escrow.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"../interfaces/IERC20.sol\";\n\n/**\n * @title A holding contract for Sovryn Ethereum Pool to accept SOV Token.\n * @author Franklin Richards - powerhousefrank@protonmail.com\n * @notice You can use this contract for deposit of SOV tokens for some time and withdraw later.\n */\ncontract Escrow {\n using SafeMath for uint256;\n\n /* Storage */\n\n /// @notice The total tokens deposited.\n /// @dev Used for calculating the reward % share of users related to total deposit.\n uint256 public totalDeposit;\n /// @notice The release timestamp for the tokens deposited.\n uint256 public releaseTime;\n /// @notice The amount of token we would be accepting as deposit at max.\n uint256 public depositLimit;\n\n /// @notice The SOV token contract.\n IERC20 public SOV;\n\n /// @notice The multisig contract which handles the fund.\n address public multisig;\n\n /// @notice The user balances.\n mapping(address => uint256) userBalances;\n\n /// @notice The current contract status.\n /// @notice Deployed - Deployed the contract.\n /// @notice Deposit - Time to deposit in the contract by the users.\n /// @notice Holding - Deposit is closed and now the holding period starts.\n /// @notice Withdraw - Time to withdraw in the contract by the users.\n /// @notice Expired - The contract is now closed completely.\n enum Status { Deployed, Deposit, Holding, Withdraw, Expired }\n Status public status;\n\n /* Events */\n\n /// @notice Emitted when the contract deposit starts.\n event EscrowActivated();\n\n /// @notice Emitted when the contract is put in holding state. No new token deposit accepted by User.\n event EscrowInHoldingState();\n\n /// @notice Emitted when the contract is put in withdraw state. Users can now withdraw tokens.\n event EscrowInWithdrawState();\n\n /// @notice Emitted when the contract is expired after withdraws are made/total token transfer.\n event EscrowFundExpired();\n\n /// @notice Emitted when a new multisig is added to the contract.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _newMultisig The address which is added as the new multisig.\n /// @dev Can only be initiated by the current multisig.\n event NewMultisig(address indexed _initiator, address indexed _newMultisig);\n\n /// @notice Emitted when the release timestamp is updated.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _releaseTimestamp The updated release timestamp for the withdraw.\n event TokenReleaseUpdated(address indexed _initiator, uint256 _releaseTimestamp);\n\n /// @notice Emitted when the deposit limit is updated.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _depositLimit The updated deposit limit.\n event TokenDepositLimitUpdated(address indexed _initiator, uint256 _depositLimit);\n\n /// @notice Emitted when a new token deposit is done by User.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _amount The amount of token deposited.\n event TokenDeposit(address indexed _initiator, uint256 _amount);\n\n /// @notice Emitted when we reach the token deposit limit.\n event DepositLimitReached();\n\n /// @notice Emitted when a token withdraw is done by Multisig.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _amount The amount of token withdrawed.\n event TokenWithdrawByMultisig(address indexed _initiator, uint256 _amount);\n\n /// @notice Emitted when a new token deposit is done by Multisig.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _amount The amount of token deposited.\n event TokenDepositByMultisig(address indexed _initiator, uint256 _amount);\n\n /// @notice Emitted when a token withdraw is done by User.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _amount The amount of token withdrawed.\n event TokenWithdraw(address indexed _initiator, uint256 _amount);\n\n /* Modifiers */\n\n modifier onlyMultisig() {\n require(msg.sender == multisig, \"Only Multisig can call this.\");\n _;\n }\n\n modifier checkStatus(Status s) {\n require(status == s, \"The contract is not in the right state.\");\n _;\n }\n\n modifier checkRelease() {\n require(\n releaseTime != 0 && releaseTime <= block.timestamp,\n \"The release time has not started yet.\"\n );\n _;\n }\n\n /* Functions */\n\n /**\n * @notice Setup the required parameters.\n * @param _SOV The SOV token address.\n * @param _multisig The owner of the tokens & contract.\n * @param _releaseTime The token release time, zero if undecided.\n * @param _depositLimit The amount of tokens we will be accepting.\n */\n constructor(\n address _SOV,\n address _multisig,\n uint256 _releaseTime,\n uint256 _depositLimit\n ) public {\n require(_SOV != address(0), \"Invalid SOV Address.\");\n require(_multisig != address(0), \"Invalid Multisig Address.\");\n\n SOV = IERC20(_SOV);\n multisig = _multisig;\n\n emit NewMultisig(msg.sender, _multisig);\n\n releaseTime = _releaseTime;\n depositLimit = _depositLimit;\n\n status = Status.Deployed;\n }\n\n /**\n * @notice This function is called once after deployment for starting the deposit action.\n * @dev Without calling this function, the contract will not start accepting tokens.\n */\n function init() external onlyMultisig checkStatus(Status.Deployed) {\n status = Status.Deposit;\n\n emit EscrowActivated();\n }\n\n /**\n * @notice Update Multisig.\n * @param _newMultisig The new owner of the tokens & contract.\n */\n function updateMultisig(address _newMultisig) external onlyMultisig {\n require(_newMultisig != address(0), \"New Multisig address invalid.\");\n\n multisig = _newMultisig;\n\n emit NewMultisig(msg.sender, _newMultisig);\n }\n\n /**\n * @notice Update Release Timestamp.\n * @param _newReleaseTime The new release timestamp for token release.\n * @dev Zero is also a valid timestamp, if the release time is not scheduled yet.\n */\n function updateReleaseTimestamp(uint256 _newReleaseTime) external onlyMultisig {\n releaseTime = _newReleaseTime;\n\n emit TokenReleaseUpdated(msg.sender, _newReleaseTime);\n }\n\n /**\n * @notice Update Deposit Limit.\n * @param _newDepositLimit The new deposit limit.\n * @dev IMPORTANT: Should not decrease than already deposited.\n */\n function updateDepositLimit(uint256 _newDepositLimit) external onlyMultisig {\n require(\n _newDepositLimit >= totalDeposit,\n \"Deposit already higher than the limit trying to be set.\"\n );\n depositLimit = _newDepositLimit;\n\n emit TokenDepositLimitUpdated(msg.sender, _newDepositLimit);\n }\n\n /**\n * @notice Deposit tokens to this contract by User.\n * @param _amount the amount of tokens deposited.\n * @dev The contract has to be approved by the user inorder for this function to work.\n * These tokens can be withdrawn/transferred during Holding State by the Multisig.\n */\n function depositTokens(uint256 _amount) external checkStatus(Status.Deposit) {\n require(_amount > 0, \"Amount needs to be bigger than zero.\");\n uint256 amount = _amount;\n\n if (totalDeposit.add(_amount) >= depositLimit) {\n amount = depositLimit.sub(totalDeposit);\n emit DepositLimitReached();\n }\n\n bool txStatus = SOV.transferFrom(msg.sender, address(this), amount);\n require(txStatus, \"Token transfer was not successful.\");\n\n userBalances[msg.sender] = userBalances[msg.sender].add(amount);\n totalDeposit = totalDeposit.add(amount);\n\n emit TokenDeposit(msg.sender, amount);\n }\n\n /**\n * @notice Update contract state to Holding.\n * @dev Once called, the contract no longer accepts any more deposits.\n * The multisig can now withdraw tokens from the contract after the contract is in Holding State.\n */\n function changeStateToHolding() external onlyMultisig checkStatus(Status.Deposit) {\n status = Status.Holding;\n\n emit EscrowInHoldingState();\n }\n\n /**\n * @notice Withdraws all token from the contract by Multisig.\n * @param _receiverAddress The address where the tokens has to be transferred. Zero address if the withdraw is to be done in Multisig.\n * @dev Can only be called after the token state is changed to Holding.\n */\n function withdrawTokensByMultisig(address _receiverAddress)\n external\n onlyMultisig\n checkStatus(Status.Holding)\n {\n address receiverAddress = msg.sender;\n if (_receiverAddress != address(0)) {\n receiverAddress = _receiverAddress;\n }\n\n uint256 value = SOV.balanceOf(address(this));\n /// Sending the amount to multisig.\n bool txStatus = SOV.transfer(receiverAddress, value);\n require(txStatus, \"Token transfer was not successful. Check receiver address.\");\n\n emit TokenWithdrawByMultisig(msg.sender, value);\n }\n\n /**\n * @notice Deposit tokens to this contract by the Multisig.\n * @param _amount the amount of tokens deposited.\n * @dev The contract has to be approved by the multisig inorder for this function to work.\n * Once the token deposit is higher than the total deposits done, the contract state is changed to Withdraw.\n */\n function depositTokensByMultisig(uint256 _amount)\n external\n onlyMultisig\n checkStatus(Status.Holding)\n {\n require(_amount > 0, \"Amount needs to be bigger than zero.\");\n\n bool txStatus = SOV.transferFrom(msg.sender, address(this), _amount);\n require(txStatus, \"Token transfer was not successful.\");\n\n emit TokenDepositByMultisig(msg.sender, _amount);\n\n if (SOV.balanceOf(address(this)) >= totalDeposit) {\n status = Status.Withdraw;\n emit EscrowInWithdrawState();\n }\n }\n\n /**\n * @notice Withdraws token from the contract by User.\n * @dev Only works after the contract state is in Withdraw.\n */\n function withdrawTokens() public checkRelease checkStatus(Status.Withdraw) {\n uint256 amount = userBalances[msg.sender];\n userBalances[msg.sender] = 0;\n bool txStatus = SOV.transfer(msg.sender, amount);\n require(txStatus, \"Token transfer was not successful. Check receiver address.\");\n\n emit TokenWithdraw(msg.sender, amount);\n }\n\n /* Getter Functions */\n\n /**\n * @notice Function to read the current token balance of a particular user.\n * @return _addr The user address whose balance has to be checked.\n */\n function getUserBalance(address _addr) external view returns (uint256 balance) {\n return userBalances[_addr];\n }\n}\n" + }, + "contracts/escrow/EscrowReward.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./Escrow.sol\";\nimport \"../locked/ILockedSOV.sol\";\n\n/**\n * @title A reward distribution contract for Sovryn Ethereum Pool Escrow Contract.\n * @author Franklin Richards - powerhousefrank@protonmail.com\n * @notice Multisig can use this contract for depositing of Reward tokens based on the total token deposit.\n */\ncontract EscrowReward is Escrow {\n using SafeMath for uint256;\n\n /* Storage */\n\n /// @notice The total reward tokens deposited.\n /// @dev Used for calculating the reward % share of users related to total deposit.\n uint256 public totalRewardDeposit;\n\n /// @notice The Locked SOV contract.\n ILockedSOV public lockedSOV;\n\n /* Events */\n\n /// @notice Emitted when the Locked SOV Contract address is updated.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _lockedSOV The address of the Locked SOV Contract.\n event LockedSOVUpdated(address indexed _initiator, address indexed _lockedSOV);\n\n /// @notice Emitted when a new reward token deposit is done by Multisig.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _amount The amount of token deposited.\n event RewardDepositByMultisig(address indexed _initiator, uint256 _amount);\n\n /// @notice Emitted when a Reward token withdraw is done by User.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _amount The amount of token withdrawed.\n event RewardTokenWithdraw(address indexed _initiator, uint256 _amount);\n\n /* Functions */\n\n /**\n * @notice Setup the required parameters.\n * @param _lockedSOV The Locked SOV Contract address.\n * @param _SOV The SOV token address.\n * @param _multisig The owner of the tokens & contract.\n * @param _releaseTime The token release time, zero if undecided.\n * @param _depositLimit The amount of tokens we will be accepting.\n */\n constructor(\n address _lockedSOV,\n address _SOV,\n address _multisig,\n uint256 _releaseTime,\n uint256 _depositLimit\n ) public Escrow(_SOV, _multisig, _releaseTime, _depositLimit) {\n if (_lockedSOV != address(0)) {\n lockedSOV = ILockedSOV(_lockedSOV);\n }\n }\n\n /**\n * @notice Set the Locked SOV Contract Address if not already done.\n * @param _lockedSOV The Locked SOV Contract address.\n */\n function updateLockedSOV(address _lockedSOV) external onlyMultisig {\n require(_lockedSOV != address(0), \"Invalid Reward Token Address.\");\n\n lockedSOV = ILockedSOV(_lockedSOV);\n\n emit LockedSOVUpdated(msg.sender, _lockedSOV);\n }\n\n /**\n * @notice Deposit tokens to this contract by the Multisig.\n * @param _amount the amount of tokens deposited.\n * @dev The contract has to be approved by the multisig inorder for this function to work.\n */\n function depositRewardByMultisig(uint256 _amount) external onlyMultisig {\n require(\n status != Status.Withdraw,\n \"Reward Token deposit is only allowed before User Withdraw starts.\"\n );\n require(_amount > 0, \"Amount needs to be bigger than zero.\");\n\n bool txStatus = SOV.transferFrom(msg.sender, address(this), _amount);\n require(txStatus, \"Token transfer was not successful.\");\n\n totalRewardDeposit = totalRewardDeposit.add(_amount);\n txStatus = SOV.approve(address(lockedSOV), totalRewardDeposit);\n require(txStatus, \"Token Approval was not successful.\");\n\n emit RewardDepositByMultisig(msg.sender, _amount);\n }\n\n /**\n * @notice Withdraws token and reward from the contract by User. Reward is gone to lockedSOV contract for future vesting.\n * @dev Only works after the contract state is in Withdraw.\n */\n function withdrawTokensAndReward() external checkRelease checkStatus(Status.Withdraw) {\n // Reward calculation have to be done initially as the User Balance is zeroed out .\n uint256 reward = userBalances[msg.sender].mul(totalRewardDeposit).div(totalDeposit);\n withdrawTokens();\n\n lockedSOV.depositSOV(msg.sender, reward);\n\n emit RewardTokenWithdraw(msg.sender, reward);\n }\n\n /* Getter Functions */\n\n /**\n * @notice Function to read the reward a particular user can get.\n * @param _addr The address of the user whose reward is to be read.\n * @return reward The reward received by the user.\n */\n function getReward(address _addr) external view returns (uint256 reward) {\n if (userBalances[_addr].mul(totalRewardDeposit) == 0) {\n return 0;\n }\n return userBalances[_addr].mul(totalRewardDeposit).div(totalDeposit);\n }\n}\n" + }, + "contracts/events/AffiliatesEvents.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"./ModulesCommonEvents.sol\";\n\ncontract AffiliatesEvents is ModulesCommonEvents {\n event SetAffiliatesReferrer(address indexed user, address indexed referrer);\n\n event SetAffiliatesReferrerFail(\n address indexed user,\n address indexed referrer,\n bool alreadySet,\n bool userNotFirstTrade\n );\n\n event SetUserNotFirstTradeFlag(address indexed user);\n\n event PayTradingFeeToAffiliate(\n address indexed referrer,\n address trader,\n address indexed token,\n bool indexed isHeld,\n uint256 tradingFeeTokenAmount,\n uint256 tokenBonusAmount,\n uint256 sovBonusAmount,\n uint256 sovBonusAmountPaid\n );\n\n event PayTradingFeeToAffiliateFail(\n address indexed referrer,\n address trader,\n address indexed token,\n uint256 tradingFeeTokenAmount,\n uint256 tokenBonusAmount,\n uint256 sovBonusAmount,\n uint256 sovBonusAmountTryingToPaid\n );\n\n event WithdrawAffiliatesReferrerTokenFees(\n address indexed referrer,\n address indexed receiver,\n address indexed tokenAddress,\n uint256 amount\n );\n}\n" + }, + "contracts/events/FeesEvents.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\n/**\n * @title The Fees Events contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the events for fee payments.\n * */\ncontract FeesEvents {\n event PayLendingFee(address indexed payer, address indexed token, uint256 amount);\n\n event PayTradingFee(\n address indexed payer,\n address indexed token,\n bytes32 indexed loanId,\n uint256 amount\n );\n\n event PayBorrowingFee(\n address indexed payer,\n address indexed token,\n bytes32 indexed loanId,\n uint256 amount\n );\n\n event EarnReward(\n address indexed receiver,\n address indexed token,\n bytes32 indexed loanId,\n uint256 feeRebatePercent,\n uint256 amount,\n uint256 basisPoint\n );\n\n event EarnRewardFail(\n address indexed receiver,\n address indexed token,\n bytes32 indexed loanId,\n uint256 feeRebatePercent,\n uint256 amount,\n uint256 basisPoint\n );\n}\n" + }, + "contracts/events/LoanClosingsEvents.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"./ModulesCommonEvents.sol\";\n\n/**\n * @title The Loan Closing Events contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the events for loan closing operations.\n * */\ncontract LoanClosingsEvents is ModulesCommonEvents {\n /// topic0: 0x6349c1a02ec126f7f4fc6e6837e1859006e90e9901635c442d29271e77b96fb6\n event CloseWithDeposit(\n address indexed user,\n address indexed lender,\n bytes32 indexed loanId,\n address closer,\n address loanToken,\n address collateralToken,\n uint256 repayAmount,\n uint256 collateralWithdrawAmount,\n uint256 collateralToLoanRate,\n uint256 currentMargin\n );\n\n /// topic0: 0x2ed7b29b4ca95cf3bb9a44f703872a66e6aa5e8f07b675fa9a5c124a1e5d7352\n event CloseWithSwap(\n address indexed user,\n address indexed lender,\n bytes32 indexed loanId,\n address collateralToken,\n address loanToken,\n address closer,\n uint256 positionCloseSize,\n uint256 loanCloseAmount,\n uint256 exitPrice, // one unit of collateralToken, denominated in loanToken\n uint256 currentLeverage\n );\n\n /// topic0: 0x46fa03303782eb2f686515f6c0100f9a62dabe587b0d3f5a4fc0c822d6e532d3\n event Liquidate(\n address indexed user,\n address indexed liquidator,\n bytes32 indexed loanId,\n address lender,\n address loanToken,\n address collateralToken,\n uint256 repayAmount,\n uint256 collateralWithdrawAmount,\n uint256 collateralToLoanRate,\n uint256 currentMargin\n );\n\n event Rollover(\n address indexed user,\n address indexed lender,\n bytes32 indexed loanId,\n uint256 principal,\n uint256 collateral,\n uint256 endTimestamp,\n address rewardReceiver,\n uint256 reward\n );\n\n event swapExcess(bool shouldRefund, uint256 amount, uint256 amountInRbtc, uint256 threshold);\n}\n" + }, + "contracts/events/LoanMaintenanceEvents.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"./ModulesCommonEvents.sol\";\n\n/**\n * @title The Loan Maintenance Events contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the events for loan maintenance operations.\n * */\ncontract LoanMaintenanceEvents is ModulesCommonEvents {\n event DepositCollateral(bytes32 indexed loanId, uint256 depositAmount, uint256 rate);\n}\n" + }, + "contracts/events/LoanOpeningsEvents.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"./ModulesCommonEvents.sol\";\n\n/**\n * @title The Loan Openings Events contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the events for loan openings operations.\n * */\ncontract LoanOpeningsEvents is ModulesCommonEvents {\n /// topic0: 0x7bd8cbb7ba34b33004f3deda0fd36c92fc0360acbd97843360037b467a538f90\n event Borrow(\n address indexed user,\n address indexed lender,\n bytes32 indexed loanId,\n address loanToken,\n address collateralToken,\n uint256 newPrincipal,\n uint256 newCollateral,\n uint256 interestRate,\n uint256 interestDuration,\n uint256 collateralToLoanRate,\n uint256 currentMargin\n );\n\n /// topic0: 0xf640c1cfe1a912a0b0152b5a542e5c2403142eed75b06cde526cee54b1580e5c\n event Trade(\n address indexed user,\n address indexed lender,\n bytes32 indexed loanId,\n address collateralToken,\n address loanToken,\n uint256 positionSize,\n uint256 borrowedAmount,\n uint256 interestRate,\n uint256 settlementDate,\n uint256 entryPrice, /// one unit of collateralToken, denominated in loanToken\n uint256 entryLeverage,\n uint256 currentLeverage\n );\n\n /// topic0: 0x0eef4f90457a741c97d76fcf13fa231fefdcc7649bdb3cb49157c37111c98433\n event DelegatedManagerSet(\n bytes32 indexed loanId,\n address indexed delegator,\n address indexed delegated,\n bool isActive\n );\n}\n" + }, + "contracts/events/LoanSettingsEvents.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"./ModulesCommonEvents.sol\";\n\n/**\n * @title The Loan Settings Events contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the events for loan settings operations.\n * */\ncontract LoanSettingsEvents is ModulesCommonEvents {\n event LoanParamsSetup(\n bytes32 indexed id,\n address owner,\n address indexed loanToken,\n address indexed collateralToken,\n uint256 minInitialMargin,\n uint256 maintenanceMargin,\n uint256 maxLoanTerm\n );\n event LoanParamsIdSetup(bytes32 indexed id, address indexed owner);\n\n event LoanParamsDisabled(\n bytes32 indexed id,\n address owner,\n address indexed loanToken,\n address indexed collateralToken,\n uint256 minInitialMargin,\n uint256 maintenanceMargin,\n uint256 maxLoanTerm\n );\n event LoanParamsIdDisabled(bytes32 indexed id, address indexed owner);\n}\n" + }, + "contracts/events/ModulesCommonEvents.sol": { + "content": "pragma solidity 0.5.17;\n\n/**\n * @title The common events for all modules\n * @notice This contract contains the events which will be used by all modules\n **/\n\ncontract ModulesCommonEvents {\n event ProtocolModuleContractReplaced(\n address indexed prevModuleContractAddress,\n address indexed newModuleContractAddress,\n bytes32 indexed module\n );\n}\n" + }, + "contracts/events/ProtocolSettingsEvents.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"./ModulesCommonEvents.sol\";\nimport \"../interfaces/IERC20.sol\";\n\n/**\n * @title The Protocol Settings Events contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the events for protocol settings operations.\n * */\ncontract ProtocolSettingsEvents is ModulesCommonEvents {\n event SetPriceFeedContract(address indexed sender, address oldValue, address newValue);\n\n event SetSwapsImplContract(address indexed sender, address oldValue, address newValue);\n\n event SetLoanPool(\n address indexed sender,\n address indexed loanPool,\n address indexed underlying\n );\n\n event SetSupportedTokens(address indexed sender, address indexed token, bool isActive);\n\n event SetLendingFeePercent(address indexed sender, uint256 oldValue, uint256 newValue);\n\n event SetTradingFeePercent(address indexed sender, uint256 oldValue, uint256 newValue);\n\n event SetBorrowingFeePercent(address indexed sender, uint256 oldValue, uint256 newValue);\n\n event SetSwapExternalFeePercent(address indexed sender, uint256 oldValue, uint256 newValue);\n\n event SetAffiliateFeePercent(address indexed sender, uint256 oldValue, uint256 newValue);\n\n event SetAffiliateTradingTokenFeePercent(\n address indexed sender,\n uint256 oldValue,\n uint256 newValue\n );\n\n event SetLiquidationIncentivePercent(\n address indexed sender,\n uint256 oldValue,\n uint256 newValue\n );\n\n event SetMaxSwapSize(address indexed sender, uint256 oldValue, uint256 newValue);\n\n event SetFeesController(\n address indexed sender,\n address indexed oldController,\n address indexed newController\n );\n\n event SetWrbtcToken(\n address indexed sender,\n address indexed oldWethToken,\n address indexed newWethToken\n );\n\n event SetSovrynSwapContractRegistryAddress(\n address indexed sender,\n address indexed oldSovrynSwapContractRegistryAddress,\n address indexed newSovrynSwapContractRegistryAddress\n );\n\n event SetProtocolTokenAddress(\n address indexed sender,\n address indexed oldProtocolToken,\n address indexed newProtocolToken\n );\n\n event WithdrawFees(\n address indexed sender,\n address indexed token,\n address indexed receiver,\n uint256 lendingAmount,\n uint256 tradingAmount,\n uint256 borrowingAmount,\n uint256 wRBTCConverted\n );\n\n event WithdrawLendingFees(\n address indexed sender,\n address indexed token,\n address indexed receiver,\n uint256 amount\n );\n\n event WithdrawTradingFees(\n address indexed sender,\n address indexed token,\n address indexed receiver,\n uint256 amount\n );\n\n event WithdrawBorrowingFees(\n address indexed sender,\n address indexed token,\n address indexed receiver,\n uint256 amount\n );\n\n event SetRolloverBaseReward(address indexed sender, uint256 oldValue, uint256 newValue);\n\n event SetRebatePercent(\n address indexed sender,\n uint256 oldRebatePercent,\n uint256 newRebatePercent\n );\n\n event SetSpecialRebates(\n address indexed sender,\n address indexed sourceToken,\n address indexed destToken,\n uint256 oldSpecialRebatesPercent,\n uint256 newSpecialRebatesPercent\n );\n\n event SetProtocolAddress(\n address indexed sender,\n address indexed oldProtocol,\n address indexed newProtocol\n );\n\n event SetMinReferralsToPayoutAffiliates(\n address indexed sender,\n uint256 oldMinReferrals,\n uint256 newMinReferrals\n );\n\n event SetSOVTokenAddress(\n address indexed sender,\n address indexed oldTokenAddress,\n address indexed newTokenAddress\n );\n\n event SetLockedSOVAddress(\n address indexed sender,\n address indexed oldAddress,\n address indexed newAddress\n );\n\n event TogglePaused(address indexed sender, bool indexed oldFlag, bool indexed newFlag);\n\n event SetTradingRebateRewardsBasisPoint(\n address indexed sender,\n uint256 oldBasisPoint,\n uint256 newBasisPoint\n );\n\n event SetRolloverFlexFeePercent(\n address indexed sender,\n uint256 oldRolloverFlexFeePercent,\n uint256 newRolloverFlexFeePercent\n );\n\n event SetDefaultPathConversion(\n address indexed sender,\n address indexed sourceTokenAddress,\n address indexed destTokenAddress,\n IERC20[] defaultPath\n );\n\n event RemoveDefaultPathConversion(\n address indexed sender,\n address indexed sourceTokenAddress,\n address indexed destTokenAddress,\n IERC20[] defaultPath\n );\n\n event SetAdmin(address indexed sender, address indexed oldAdmin, address indexed newAdmin);\n\n event SetPauser(address indexed sender, address indexed oldPauser, address indexed newPauser);\n}\n" + }, + "contracts/events/SwapsEvents.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"./ModulesCommonEvents.sol\";\n\n/**\n * @title The Swaps Events contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the events for swap operations.\n * */\ncontract SwapsEvents is ModulesCommonEvents {\n event LoanSwap(\n bytes32 indexed loanId,\n address indexed sourceToken,\n address indexed destToken,\n address borrower,\n uint256 sourceAmount,\n uint256 destAmount\n );\n\n event ExternalSwap(\n address indexed user,\n address indexed sourceToken,\n address indexed destToken,\n uint256 sourceAmount,\n uint256 destAmount\n );\n}\n" + }, + "contracts/farm/ILiquidityMining.sol": { + "content": "pragma solidity 0.5.17;\n\ninterface ILiquidityMining {\n function withdraw(\n address _poolToken,\n uint256 _amount,\n address _user\n ) external;\n\n function onTokensDeposited(address _user, uint256 _amount) external;\n\n function getUserPoolTokenBalance(address _poolToken, address _user)\n external\n view\n returns (uint256);\n}\n" + }, + "contracts/farm/LiquidityMining.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../openzeppelin/ERC20.sol\";\nimport \"../openzeppelin/SafeERC20.sol\";\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"./LiquidityMiningStorage.sol\";\nimport \"./ILiquidityMining.sol\";\n\ncontract LiquidityMining is ILiquidityMining, LiquidityMiningStorage {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n /* Constants */\n\n uint256 public constant PRECISION = 1e12;\n // Bonus multiplier for early liquidity providers.\n // During bonus period each passed block will be calculated like N passed blocks, where N = BONUS_MULTIPLIER\n uint256 public constant BONUS_BLOCK_MULTIPLIER = 10;\n\n uint256 public constant SECONDS_PER_BLOCK = 30;\n\n /* Events */\n\n event SOVTransferred(address indexed receiver, uint256 amount);\n event PoolTokenAdded(address indexed user, address indexed poolToken, uint256 allocationPoint);\n event PoolTokenUpdated(\n address indexed user,\n address indexed poolToken,\n uint256 newAllocationPoint,\n uint256 oldAllocationPoint\n );\n event Deposit(address indexed user, address indexed poolToken, uint256 amount);\n event RewardClaimed(address indexed user, address indexed poolToken, uint256 amount);\n event Withdraw(address indexed user, address indexed poolToken, uint256 amount);\n event EmergencyWithdraw(\n address indexed user,\n address indexed poolToken,\n uint256 amount,\n uint256 accumulatedReward\n );\n\n /* Functions */\n\n /**\n * @notice Initialize mining.\n *\n * @param _SOV The SOV token.\n * @param _rewardTokensPerBlock The number of reward tokens per block.\n * @param _startDelayBlocks The number of blocks should be passed to start\n * mining.\n * @param _numberOfBonusBlocks The number of blocks when each block will\n * be calculated as N blocks (BONUS_BLOCK_MULTIPLIER).\n * @param _lockedSOV The contract instance address of the lockedSOV vault.\n * SOV rewards are not paid directly to liquidity providers. Instead they\n * are deposited into a lockedSOV vault contract.\n * @param _unlockedImmediatelyPercent The % which determines how much will be unlocked immediately.\n */\n function initialize(\n IERC20 _SOV,\n uint256 _rewardTokensPerBlock,\n uint256 _startDelayBlocks,\n uint256 _numberOfBonusBlocks,\n address _wrapper,\n ILockedSOV _lockedSOV,\n uint256 _unlockedImmediatelyPercent\n ) external onlyAuthorized {\n /// @dev Non-idempotent function. Must be called just once.\n require(address(SOV) == address(0), \"Already initialized\");\n require(address(_SOV) != address(0), \"Invalid token address\");\n require(_startDelayBlocks > 0, \"Invalid start block\");\n require(\n _unlockedImmediatelyPercent < 10000,\n \"Unlocked immediately percent has to be less than 10000.\"\n );\n\n SOV = _SOV;\n rewardTokensPerBlock = _rewardTokensPerBlock;\n startBlock = block.number + _startDelayBlocks;\n bonusEndBlock = startBlock + _numberOfBonusBlocks;\n wrapper = _wrapper;\n lockedSOV = _lockedSOV;\n unlockedImmediatelyPercent = _unlockedImmediatelyPercent;\n }\n\n /**\n * @notice Sets lockedSOV contract.\n * @param _lockedSOV The contract instance address of the lockedSOV vault.\n */\n function setLockedSOV(ILockedSOV _lockedSOV) external onlyAuthorized {\n require(address(_lockedSOV) != address(0), \"Invalid lockedSOV Address.\");\n lockedSOV = _lockedSOV;\n }\n\n /**\n * @notice Sets unlocked immediately percent.\n * @param _unlockedImmediatelyPercent The % which determines how much will be unlocked immediately.\n * @dev 10000 is 100%\n */\n function setUnlockedImmediatelyPercent(uint256 _unlockedImmediatelyPercent)\n external\n onlyAuthorized\n {\n require(\n _unlockedImmediatelyPercent <= 10000,\n \"Unlocked immediately percent has to be less than equal to 10000.\"\n );\n unlockedImmediatelyPercent = _unlockedImmediatelyPercent;\n }\n\n /**\n * @notice Sets unlocked immediately percent overwrite for specific pool token.\n * @param _poolToken the address of pool token\n * @param _poolTokenUnlockedImmediatelyPercent The % which determines how much will be unlocked immediately.\n * @dev 10000 is 100%\n */\n function setPoolTokenUnlockedImmediatelyPercent(\n address _poolToken,\n uint256 _poolTokenUnlockedImmediatelyPercent\n ) external onlyAuthorized {\n require(\n _poolTokenUnlockedImmediatelyPercent <= 10000,\n \"Unlocked immediately percent has to be less than equal to 10000.\"\n );\n poolTokensUnlockedImmediatelyPercent[_poolToken] = _poolTokenUnlockedImmediatelyPercent;\n }\n\n /**\n * @notice sets wrapper proxy contract\n * @dev can be set to zero address to remove wrapper\n */\n function setWrapper(address _wrapper) external onlyAuthorized {\n wrapper = _wrapper;\n }\n\n /**\n * @notice stops mining by setting end block\n */\n function stopMining() external onlyAuthorized {\n require(endBlock == 0, \"Already stopped\");\n\n endBlock = block.number;\n }\n\n /**\n * @notice Transfers SOV tokens to given address.\n * Owner use this function to withdraw SOV from LM contract\n * into another account.\n * @param _receiver The address of the SOV receiver.\n * @param _amount The amount to be transferred.\n * */\n function transferSOV(address _receiver, uint256 _amount) external onlyAuthorized {\n require(_receiver != address(0), \"Receiver address invalid\");\n require(_amount != 0, \"Amount invalid\");\n\n /// @dev Do not transfer more SOV than available.\n uint256 SOVBal = SOV.balanceOf(address(this));\n if (_amount > SOVBal) {\n _amount = SOVBal;\n }\n\n /// @dev The actual transfer.\n require(SOV.transfer(_receiver, _amount), \"Transfer failed\");\n\n /// @dev Event log.\n emit SOVTransferred(_receiver, _amount);\n }\n\n /**\n * @notice Get the missed SOV balance of LM contract.\n *\n * @return The amount of SOV tokens according to totalUsersBalance\n * in excess of actual SOV balance of the LM contract.\n * */\n function getMissedBalance() external view returns (uint256) {\n uint256 balance = SOV.balanceOf(address(this));\n return balance >= totalUsersBalance ? 0 : totalUsersBalance.sub(balance);\n }\n\n /**\n * @notice adds a new lp to the pool. Can only be called by the owner or an admin\n * @param _poolToken the address of pool token\n * @param _allocationPoint the allocation point (weight) for the given pool\n * @param _withUpdate the flag whether we need to update all pools\n */\n function add(\n address _poolToken,\n uint96 _allocationPoint,\n bool _withUpdate\n ) external onlyAuthorized {\n require(_allocationPoint > 0, \"Invalid allocation point\");\n require(_poolToken != address(0), \"Invalid token address\");\n require(poolIdList[_poolToken] == 0, \"Token already added\");\n\n if (_withUpdate) {\n updateAllPools();\n }\n\n uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;\n totalAllocationPoint = totalAllocationPoint.add(_allocationPoint);\n\n poolInfoList.push(\n PoolInfo({\n poolToken: IERC20(_poolToken),\n allocationPoint: _allocationPoint,\n lastRewardBlock: lastRewardBlock,\n accumulatedRewardPerShare: 0\n })\n );\n //indexing starts from 1 in order to check whether token was already added\n poolIdList[_poolToken] = poolInfoList.length;\n\n emit PoolTokenAdded(msg.sender, _poolToken, _allocationPoint);\n }\n\n /**\n * @notice updates the given pool's reward tokens allocation point\n * @param _poolToken the address of pool token\n * @param _allocationPoint the allocation point (weight) for the given pool\n * @param _updateAllFlag the flag whether we need to update all pools\n */\n function update(\n address _poolToken,\n uint96 _allocationPoint,\n bool _updateAllFlag\n ) external onlyAuthorized {\n if (_updateAllFlag) {\n updateAllPools();\n } else {\n updatePool(_poolToken);\n }\n _updateToken(_poolToken, _allocationPoint);\n }\n\n function _updateToken(address _poolToken, uint96 _allocationPoint) internal {\n uint256 poolId = _getPoolId(_poolToken);\n\n uint256 previousAllocationPoint = poolInfoList[poolId].allocationPoint;\n totalAllocationPoint = totalAllocationPoint.sub(previousAllocationPoint).add(\n _allocationPoint\n );\n poolInfoList[poolId].allocationPoint = _allocationPoint;\n\n emit PoolTokenUpdated(msg.sender, _poolToken, _allocationPoint, previousAllocationPoint);\n }\n\n /**\n * @notice updates the given pools' reward tokens allocation points\n * @param _poolTokens array of addresses of pool tokens\n * @param _allocationPoints array of allocation points (weight) for the given pools\n * @param _updateAllFlag the flag whether we need to update all pools\n */\n function updateTokens(\n address[] calldata _poolTokens,\n uint96[] calldata _allocationPoints,\n bool _updateAllFlag\n ) external onlyAuthorized {\n require(_poolTokens.length == _allocationPoints.length, \"Arrays mismatch\");\n\n if (_updateAllFlag) {\n updateAllPools();\n }\n uint256 length = _poolTokens.length;\n for (uint256 i = 0; i < length; i++) {\n if (!_updateAllFlag) {\n updatePool(_poolTokens[i]);\n }\n _updateToken(_poolTokens[i], _allocationPoints[i]);\n }\n }\n\n /**\n * @notice returns reward multiplier over the given _from to _to block\n * @param _from the first block for a calculation\n * @param _to the last block for a calculation\n */\n function _getPassedBlocksWithBonusMultiplier(uint256 _from, uint256 _to)\n internal\n view\n returns (uint256)\n {\n if (_from < startBlock) {\n _from = startBlock;\n }\n if (endBlock > 0 && _to > endBlock) {\n _to = endBlock;\n }\n if (_to <= bonusEndBlock) {\n return _to.sub(_from).mul(BONUS_BLOCK_MULTIPLIER);\n } else if (_from >= bonusEndBlock) {\n return _to.sub(_from);\n } else {\n return\n bonusEndBlock.sub(_from).mul(BONUS_BLOCK_MULTIPLIER).add(_to.sub(bonusEndBlock));\n }\n }\n\n function _getUserAccumulatedReward(uint256 _poolId, address _user)\n internal\n view\n returns (uint256)\n {\n PoolInfo storage pool = poolInfoList[_poolId];\n UserInfo storage user = userInfoMap[_poolId][_user];\n\n uint256 accumulatedRewardPerShare = pool.accumulatedRewardPerShare;\n uint256 poolTokenBalance = pool.poolToken.balanceOf(address(this));\n if (block.number > pool.lastRewardBlock && poolTokenBalance != 0) {\n (, uint256 accumulatedRewardPerShare_) = _getPoolAccumulatedReward(pool);\n accumulatedRewardPerShare = accumulatedRewardPerShare.add(accumulatedRewardPerShare_);\n }\n\n return\n user.accumulatedReward.add(\n user.amount.mul(accumulatedRewardPerShare).div(PRECISION).sub(user.rewardDebt)\n );\n }\n\n /**\n * @notice returns accumulated reward\n * @param _poolToken the address of pool token\n * @param _user the user address\n */\n function getUserAccumulatedReward(address _poolToken, address _user)\n external\n view\n returns (uint256)\n {\n uint256 poolId = _getPoolId(_poolToken);\n return _getUserAccumulatedReward(poolId, _user);\n }\n\n /**\n * @notice returns estimated reward\n * @param _poolToken the address of pool token\n * @param _amount the amount of tokens to be deposited\n * @param _duration the duration of liquidity providing in seconds\n */\n function getEstimatedReward(\n address _poolToken,\n uint256 _amount,\n uint256 _duration\n ) external view returns (uint256) {\n uint256 poolId = _getPoolId(_poolToken);\n PoolInfo storage pool = poolInfoList[poolId];\n uint256 start = block.number;\n uint256 end = start.add(_duration.div(SECONDS_PER_BLOCK));\n (, uint256 accumulatedRewardPerShare) =\n _getPoolAccumulatedReward(pool, _amount, start, end);\n return _amount.mul(accumulatedRewardPerShare).div(PRECISION);\n }\n\n /**\n * @notice Updates reward variables for all pools.\n * @dev Be careful of gas spending!\n */\n function updateAllPools() public {\n uint256 length = poolInfoList.length;\n for (uint256 i = 0; i < length; i++) {\n _updatePool(i);\n }\n }\n\n /**\n * @notice Updates reward variables of the given pool to be up-to-date\n * @param _poolToken the address of pool token\n */\n function updatePool(address _poolToken) public {\n uint256 poolId = _getPoolId(_poolToken);\n _updatePool(poolId);\n }\n\n function _updatePool(uint256 _poolId) internal {\n PoolInfo storage pool = poolInfoList[_poolId];\n\n //this pool has been updated recently\n if (block.number <= pool.lastRewardBlock) {\n return;\n }\n\n uint256 poolTokenBalance = pool.poolToken.balanceOf(address(this));\n if (poolTokenBalance == 0) {\n pool.lastRewardBlock = block.number;\n return;\n }\n\n (uint256 accumulatedReward_, uint256 accumulatedRewardPerShare_) =\n _getPoolAccumulatedReward(pool);\n pool.accumulatedRewardPerShare = pool.accumulatedRewardPerShare.add(\n accumulatedRewardPerShare_\n );\n pool.lastRewardBlock = block.number;\n\n totalUsersBalance = totalUsersBalance.add(accumulatedReward_);\n }\n\n function _getPoolAccumulatedReward(PoolInfo storage _pool)\n internal\n view\n returns (uint256, uint256)\n {\n return _getPoolAccumulatedReward(_pool, 0, _pool.lastRewardBlock, block.number);\n }\n\n function _getPoolAccumulatedReward(\n PoolInfo storage _pool,\n uint256 _additionalAmount,\n uint256 _startBlock,\n uint256 _endBlock\n ) internal view returns (uint256, uint256) {\n uint256 passedBlocks = _getPassedBlocksWithBonusMultiplier(_startBlock, _endBlock);\n uint256 accumulatedReward =\n passedBlocks.mul(rewardTokensPerBlock).mul(_pool.allocationPoint).div(\n totalAllocationPoint\n );\n\n uint256 poolTokenBalance = _pool.poolToken.balanceOf(address(this));\n poolTokenBalance = poolTokenBalance.add(_additionalAmount);\n uint256 accumulatedRewardPerShare = accumulatedReward.mul(PRECISION).div(poolTokenBalance);\n return (accumulatedReward, accumulatedRewardPerShare);\n }\n\n /**\n * @notice deposits pool tokens\n * @param _poolToken the address of pool token\n * @param _amount the amount of pool tokens\n * @param _user the address of user, tokens will be deposited to it or to msg.sender\n */\n function deposit(\n address _poolToken,\n uint256 _amount,\n address _user\n ) external {\n _deposit(_poolToken, _amount, _user, false);\n }\n\n /**\n * @notice if the lending pools directly mint/transfer tokens to this address, process it like a user deposit\n * @dev only callable by the pool which issues the tokens\n * @param _user the user address\n * @param _amount the minted amount\n */\n function onTokensDeposited(address _user, uint256 _amount) external {\n //the msg.sender is the pool token. if the msg.sender is not a valid pool token, _deposit will revert\n _deposit(msg.sender, _amount, _user, true);\n }\n\n /**\n * @notice internal function for depositing pool tokens\n * @param _poolToken the address of pool token\n * @param _amount the amount of pool tokens\n * @param _user the address of user, tokens will be deposited to it\n * @param alreadyTransferred true if the pool tokens have already been transferred\n */\n function _deposit(\n address _poolToken,\n uint256 _amount,\n address _user,\n bool alreadyTransferred\n ) internal {\n require(poolIdList[_poolToken] != 0, \"Pool token not found\");\n address userAddress = _user != address(0) ? _user : msg.sender;\n\n uint256 poolId = _getPoolId(_poolToken);\n PoolInfo storage pool = poolInfoList[poolId];\n UserInfo storage user = userInfoMap[poolId][userAddress];\n\n _updatePool(poolId);\n //sends reward directly to the user\n _updateReward(pool, user);\n\n if (_amount > 0) {\n //receives pool tokens from msg.sender, it can be user or WrapperProxy contract\n if (!alreadyTransferred)\n pool.poolToken.safeTransferFrom(address(msg.sender), address(this), _amount);\n user.amount = user.amount.add(_amount);\n }\n _updateRewardDebt(pool, user);\n emit Deposit(userAddress, _poolToken, _amount);\n }\n\n /**\n * @notice transfers reward tokens\n * @param _poolToken the address of pool token\n * @param _user the address of user to claim reward from (can be passed only by wrapper contract)\n */\n function claimReward(address _poolToken, address _user) external {\n address userAddress = _getUserAddress(_user);\n\n uint256 poolId = _getPoolId(_poolToken);\n _claimReward(poolId, userAddress, true);\n }\n\n function _claimReward(\n uint256 _poolId,\n address _userAddress,\n bool _isStakingTokens\n ) internal {\n PoolInfo storage pool = poolInfoList[_poolId];\n UserInfo storage user = userInfoMap[_poolId][_userAddress];\n\n _updatePool(_poolId);\n _updateReward(pool, user);\n _transferReward(address(pool.poolToken), user, _userAddress, _isStakingTokens, true);\n _updateRewardDebt(pool, user);\n }\n\n /**\n * @notice transfers reward tokens from all pools\n * @param _user the address of user to claim reward from (can be passed only by wrapper contract)\n */\n function claimRewardFromAllPools(address _user) external {\n address userAddress = _getUserAddress(_user);\n\n uint256 length = poolInfoList.length;\n for (uint256 i = 0; i < length; i++) {\n uint256 poolId = i;\n _claimReward(poolId, userAddress, false);\n }\n\n if (\n lockedSOV.getLockedBalance(userAddress) > 0 ||\n lockedSOV.getUnlockedBalance(userAddress) > 0\n ) {\n lockedSOV.withdrawAndStakeTokensFrom(userAddress);\n }\n }\n\n /**\n * @notice withdraws pool tokens and transfers reward tokens\n * @param _poolToken the address of pool token\n * @param _amount the amount of pool tokens\n * @param _user the user address will be used to process a withdrawal (can be passed only by wrapper contract)\n */\n function withdraw(\n address _poolToken,\n uint256 _amount,\n address _user\n ) external {\n require(poolIdList[_poolToken] != 0, \"Pool token not found\");\n address userAddress = _getUserAddress(_user);\n\n uint256 poolId = _getPoolId(_poolToken);\n PoolInfo storage pool = poolInfoList[poolId];\n UserInfo storage user = userInfoMap[poolId][userAddress];\n require(user.amount >= _amount, \"Not enough balance\");\n\n _updatePool(poolId);\n _updateReward(pool, user);\n _transferReward(_poolToken, user, userAddress, false, false);\n\n user.amount = user.amount.sub(_amount);\n\n //msg.sender is wrapper -> send to wrapper\n if (msg.sender == wrapper) {\n pool.poolToken.safeTransfer(address(msg.sender), _amount);\n }\n //msg.sender is user or pool token (lending pool) -> send to user\n else {\n pool.poolToken.safeTransfer(userAddress, _amount);\n }\n\n _updateRewardDebt(pool, user);\n emit Withdraw(userAddress, _poolToken, _amount);\n }\n\n function _getUserAddress(address _user) internal view returns (address) {\n address userAddress = msg.sender;\n if (_user != address(0)) {\n //only wrapper can pass _user parameter\n require(\n msg.sender == wrapper || poolIdList[msg.sender] != 0,\n \"only wrapper or pools may withdraw for a user\"\n );\n userAddress = _user;\n }\n return userAddress;\n }\n\n function _updateReward(PoolInfo storage pool, UserInfo storage user) internal {\n //update user accumulated reward\n if (user.amount > 0) {\n //add reward for the previous amount of deposited tokens\n uint256 accumulatedReward =\n user.amount.mul(pool.accumulatedRewardPerShare).div(PRECISION).sub(\n user.rewardDebt\n );\n user.accumulatedReward = user.accumulatedReward.add(accumulatedReward);\n }\n }\n\n function _updateRewardDebt(PoolInfo storage pool, UserInfo storage user) internal {\n //reward accumulated before amount update (should be subtracted during next reward calculation)\n user.rewardDebt = user.amount.mul(pool.accumulatedRewardPerShare).div(PRECISION);\n }\n\n /**\n * @notice Send reward in SOV to the lockedSOV vault.\n * @param _user The user info, to get its reward share.\n * @param _userAddress The address of the user, to send SOV in its behalf.\n * @param _isStakingTokens The flag whether we need to stake tokens\n * @param _isCheckingBalance The flag whether we need to throw error or don't process reward if SOV balance isn't enough\n */\n function _transferReward(\n address _poolToken,\n UserInfo storage _user,\n address _userAddress,\n bool _isStakingTokens,\n bool _isCheckingBalance\n ) internal {\n uint256 userAccumulatedReward = _user.accumulatedReward;\n /// @dev get unlock immediate percent of the pool token.\n uint256 calculatedUnlockedImmediatelyPercent = calcUnlockedImmediatelyPercent(_poolToken);\n\n /// @dev Transfer if enough SOV balance on this LM contract.\n uint256 balance = SOV.balanceOf(address(this));\n if (balance >= userAccumulatedReward) {\n totalUsersBalance = totalUsersBalance.sub(userAccumulatedReward);\n _user.accumulatedReward = 0;\n\n /// @dev If calculatedUnlockedImmediatelyPercent is 100%, transfer the reward to the LP (user).\n /// else, deposit it into lockedSOV vault contract, but first\n /// SOV deposit must be approved to move the SOV tokens\n /// from this LM contract into the lockedSOV vault.\n if (calculatedUnlockedImmediatelyPercent == 10000) {\n SOV.transfer(_userAddress, userAccumulatedReward);\n } else {\n require(SOV.approve(address(lockedSOV), userAccumulatedReward), \"Approve failed\");\n lockedSOV.deposit(\n _userAddress,\n userAccumulatedReward,\n calculatedUnlockedImmediatelyPercent\n );\n\n if (_isStakingTokens) {\n lockedSOV.withdrawAndStakeTokensFrom(_userAddress);\n }\n }\n\n /// @dev Event log.\n emit RewardClaimed(_userAddress, _poolToken, userAccumulatedReward);\n } else {\n require(!_isCheckingBalance, \"Claiming reward failed\");\n }\n }\n\n /**\n * @notice withdraws pool tokens without transferring reward tokens\n * @param _poolToken the address of pool token\n * @dev EMERGENCY ONLY\n */\n function emergencyWithdraw(address _poolToken) external {\n uint256 poolId = _getPoolId(_poolToken);\n PoolInfo storage pool = poolInfoList[poolId];\n UserInfo storage user = userInfoMap[poolId][msg.sender];\n\n _updatePool(poolId);\n _updateReward(pool, user);\n\n totalUsersBalance = totalUsersBalance.sub(user.accumulatedReward);\n uint256 userAmount = user.amount;\n uint256 userAccumulatedReward = user.accumulatedReward;\n user.amount = 0;\n user.rewardDebt = 0;\n user.accumulatedReward = 0;\n pool.poolToken.safeTransfer(address(msg.sender), userAmount);\n\n emit EmergencyWithdraw(msg.sender, _poolToken, userAmount, userAccumulatedReward);\n }\n\n /**\n * @notice returns pool id\n * @param _poolToken the address of pool token\n */\n function getPoolId(address _poolToken) external view returns (uint256) {\n return _getPoolId(_poolToken);\n }\n\n function _getPoolId(address _poolToken) internal view returns (uint256) {\n uint256 poolId = poolIdList[_poolToken];\n require(poolId > 0, \"Pool token not found\");\n return poolId - 1;\n }\n\n /**\n * @notice returns count of pool tokens\n */\n function getPoolLength() external view returns (uint256) {\n return poolInfoList.length;\n }\n\n /**\n * @notice returns list of pool token's info\n */\n function getPoolInfoList() external view returns (PoolInfo[] memory) {\n return poolInfoList;\n }\n\n /**\n * @notice returns pool info for the given token\n * @param _poolToken the address of pool token\n */\n function getPoolInfo(address _poolToken) external view returns (PoolInfo memory) {\n uint256 poolId = _getPoolId(_poolToken);\n return poolInfoList[poolId];\n }\n\n /**\n * @notice returns list of [amount, accumulatedReward] for the given user for each pool token\n * @param _user the address of the user\n */\n function getUserBalanceList(address _user) external view returns (uint256[2][] memory) {\n uint256 length = poolInfoList.length;\n uint256[2][] memory userBalanceList = new uint256[2][](length);\n for (uint256 i = 0; i < length; i++) {\n userBalanceList[i][0] = userInfoMap[i][_user].amount;\n userBalanceList[i][1] = _getUserAccumulatedReward(i, _user);\n }\n return userBalanceList;\n }\n\n /**\n * @notice returns UserInfo for the given pool and user\n * @param _poolToken the address of pool token\n * @param _user the address of the user\n */\n function getUserInfo(address _poolToken, address _user) public view returns (UserInfo memory) {\n uint256 poolId = _getPoolId(_poolToken);\n return userInfoMap[poolId][_user];\n }\n\n /**\n * @notice returns list of UserInfo for the given user for each pool token\n * @param _user the address of the user\n */\n function getUserInfoList(address _user) external view returns (UserInfo[] memory) {\n uint256 length = poolInfoList.length;\n UserInfo[] memory userInfoList = new UserInfo[](length);\n for (uint256 i = 0; i < length; i++) {\n userInfoList[i] = userInfoMap[i][_user];\n }\n return userInfoList;\n }\n\n /**\n * @notice returns accumulated reward for the given user for each pool token\n * @param _user the address of the user\n */\n function getUserAccumulatedRewardList(address _user) external view returns (uint256[] memory) {\n uint256 length = poolInfoList.length;\n uint256[] memory rewardList = new uint256[](length);\n for (uint256 i = 0; i < length; i++) {\n rewardList[i] = _getUserAccumulatedReward(i, _user);\n }\n return rewardList;\n }\n\n /**\n * @notice returns the pool token balance a user has on the contract\n * @param _poolToken the address of pool token\n * @param _user the address of the user\n */\n function getUserPoolTokenBalance(address _poolToken, address _user)\n external\n view\n returns (uint256)\n {\n UserInfo memory ui = getUserInfo(_poolToken, _user);\n return ui.amount;\n }\n\n /**\n * @notice returns the accumulated liquid reward for the given user for each pool token\n * @param _user the address of the user\n */\n function getUserAccumulatedRewardToBePaidLiquid(address _user)\n external\n view\n returns (uint256)\n {\n uint256 length = poolInfoList.length;\n uint256 result;\n for (uint256 i = 0; i < length; i++) {\n address _poolToken = address(poolInfoList[i].poolToken);\n uint256 calculatedUnlockedImmediatelyPercent =\n calcUnlockedImmediatelyPercent(_poolToken);\n result = result.add(\n calculatedUnlockedImmediatelyPercent.mul(_getUserAccumulatedReward(i, _user)).div(\n 10000\n )\n );\n }\n\n return result;\n }\n\n /**\n * @notice returns the accumulated vested reward for the given user for each pool token\n * @param _user the address of the user\n */\n function getUserAccumulatedRewardToBeVested(address _user) external view returns (uint256) {\n uint256 length = poolInfoList.length;\n uint256 result;\n for (uint256 i = 0; i < length; i++) {\n address _poolToken = address(poolInfoList[i].poolToken);\n uint256 calculatedUnlockedImmediatelyPercent =\n calcUnlockedImmediatelyPercent(_poolToken);\n result = result.add(\n (10000 - calculatedUnlockedImmediatelyPercent)\n .mul(_getUserAccumulatedReward(i, _user))\n .div(10000)\n );\n }\n\n return result;\n }\n\n /**\n * @dev calculate the unlocked immediate percentage of specific pool token\n * use the poolTokensUnlockedImmediatelyPercent by default, if it is not set, then use the unlockedImmediatelyPercent\n */\n function calcUnlockedImmediatelyPercent(address _poolToken) public view returns (uint256) {\n uint256 poolTokenUnlockedImmediatelyPercent =\n poolTokensUnlockedImmediatelyPercent[_poolToken];\n return\n poolTokenUnlockedImmediatelyPercent > 0\n ? poolTokenUnlockedImmediatelyPercent\n : unlockedImmediatelyPercent;\n }\n}\n" + }, + "contracts/farm/LiquidityMiningConfigToken.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../openzeppelin/IERC20_.sol\";\n\n/**\n * @title Dummy token with 0 total supply.\n *\n * @dev We need this token for having a flexibility with LiquidityMining configuration\n */\ncontract LiquidityMiningConfigToken is IERC20_ {\n function totalSupply() external view returns (uint256) {\n return 0;\n }\n\n function balanceOf(address account) external view returns (uint256) {\n return 0;\n }\n\n function transfer(address recipient, uint256 amount) external returns (bool) {\n return false;\n }\n\n function allowance(address owner, address spender) external view returns (uint256) {\n return 0;\n }\n\n function approve(address spender, uint256 amount) external returns (bool) {\n return false;\n }\n\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool) {\n return false;\n }\n}\n" + }, + "contracts/farm/LiquidityMiningProxy.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./LiquidityMiningStorage.sol\";\nimport \"../proxy/UpgradableProxy.sol\";\n\n/**\n * @dev LiquidityMining contract should be upgradable, use UpgradableProxy\n */\ncontract LiquidityMiningProxy is LiquidityMiningStorage, UpgradableProxy {\n\n}\n" + }, + "contracts/farm/LiquidityMiningStorage.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../openzeppelin/ERC20.sol\";\nimport \"../openzeppelin/SafeERC20.sol\";\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"../locked/ILockedSOV.sol\";\nimport \"../utils/AdminRole.sol\";\n\ncontract LiquidityMiningStorage is AdminRole {\n // Info of each user.\n struct UserInfo {\n uint256 amount; // How many pool tokens the user has provided.\n uint256 rewardDebt; // Reward debt. See explanation below.\n uint256 accumulatedReward; //Reward that's ready to be transferred\n //\n // We do some fancy math here. Basically, any point in time, the amount of reward tokens\n // entitled to a user but is accumulated to be distributed is:\n //\n // accumulated reward = (user.amount * pool.accumulatedRewardPerShare) - user.rewardDebt\n //\n // Whenever a user deposits or withdraws LP tokens to a pool. Here's what happens:\n // 1. The pool's `accumulatedRewardPerShare` (and `lastRewardBlock`) gets updated.\n // 2. User receives the accumulated reward sent to his/her address.\n // 3. User's `amount` gets updated.\n // 4. User's `rewardDebt` gets updated.\n }\n\n // Info of each pool.\n struct PoolInfo {\n IERC20 poolToken; // Address of LP token contract.\n uint96 allocationPoint; // How many allocation points assigned to this pool. Amount of reward tokens to distribute per block.\n uint256 lastRewardBlock; // Last block number that reward tokens distribution occurs.\n uint256 accumulatedRewardPerShare; // Accumulated amount of reward tokens per share, times 1e12. See below.\n }\n\n // Rewards tokens created per block.\n uint256 public rewardTokensPerBlock;\n // The block number when reward token mining starts.\n uint256 public startBlock;\n // Block number when bonus reward token period ends.\n uint256 public bonusEndBlock;\n // Block number when reward token period ends.\n uint256 public endBlock;\n\n //Wrapper contract which will be a proxy between user and LM\n address public wrapper;\n\n // Info of each pool.\n PoolInfo[] public poolInfoList;\n // Mapping pool token address => pool id\n mapping(address => uint256) poolIdList;\n // Total allocation points. Must be the sum of all allocation points in all pools.\n uint256 public totalAllocationPoint;\n\n // Info of each user that stakes LP tokens.\n mapping(uint256 => mapping(address => UserInfo)) public userInfoMap;\n // Total balance this contract should have to handle withdrawal for all users\n uint256 public totalUsersBalance;\n\n /// @dev The SOV token\n IERC20 public SOV;\n\n /// @dev The locked vault contract to deposit LP's rewards into.\n ILockedSOV public lockedSOV;\n\n // The % which determines how much will be unlocked immediately.\n /// @dev 10000 is 100%\n uint256 public unlockedImmediatelyPercent;\n\n /// @dev overwrite the unlockedImmediatelyPercent for specific token.\n mapping(address => uint256) public poolTokensUnlockedImmediatelyPercent;\n}\n" + }, + "contracts/feeds/BProPriceFeed.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\nimport \"./PriceFeeds.sol\";\nimport \"./IMoCState.sol\";\nimport \"../openzeppelin/Ownable.sol\";\nimport \"../openzeppelin/Address.sol\";\n\n/**\n * @title The BPro Price Feed contract.\n *\n * This contract gets/sets the MoC (Money on Chain) address of its state\n * contract and queries its method bproUsdPrice to get bPro/USD valuation.\n * */\ncontract BProPriceFeed is IPriceFeedsExt, Ownable {\n address public mocStateAddress;\n\n event SetMoCStateAddress(address indexed mocStateAddress, address changerAddress);\n\n /**\n * @notice Initializes a new MoC state.\n *\n * @param _mocStateAddress MoC state address\n * */\n constructor(address _mocStateAddress) public {\n setMoCStateAddress(_mocStateAddress);\n }\n\n /**\n * @notice Get BPro USD price.\n *\n * @return the BPro USD Price [using mocPrecision]\n */\n function latestAnswer() external view returns (uint256) {\n IMoCState _mocState = IMoCState(mocStateAddress);\n return _mocState.bproUsdPrice();\n }\n\n /**\n * @notice Supposed to get the MoC update time, but instead\n * get the current timestamp.\n *\n * @return Always returns current block's timestamp.\n * */\n function latestTimestamp() external view returns (uint256) {\n return now; /// MoC state doesn't return update timestamp.\n }\n\n /**\n * @notice Set MoC state address.\n *\n * @param _mocStateAddress The MoC state address.\n * */\n function setMoCStateAddress(address _mocStateAddress) public onlyOwner {\n require(Address.isContract(_mocStateAddress), \"_mocStateAddress not a contract\");\n mocStateAddress = _mocStateAddress;\n emit SetMoCStateAddress(mocStateAddress, msg.sender);\n }\n}\n" + }, + "contracts/feeds/IMoCState.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\ninterface IMoCState {\n function getRbtcInBitPro(bytes32 bucket) external view returns (uint256);\n\n function globalMaxBPro() external view returns (uint256);\n\n function maxBPro(bytes32 bucket) external view returns (uint256);\n\n function absoluteMaxBPro() external view returns (uint256);\n\n function maxBProWithDiscount() external view returns (uint256);\n\n function bproTecPrice() external view returns (uint256);\n\n function bucketBProTecPrice(bytes32 bucket) external view returns (uint256);\n\n function bproDiscountPrice() external view returns (uint256);\n\n function bproUsdPrice() external view returns (uint256);\n\n function bproSpotDiscountRate() external view returns (uint256);\n\n function getBucketNBPro(bytes32 bucket) external view returns (uint256);\n}\n" + }, + "contracts/feeds/IPriceFeeds.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\ninterface IPriceFeeds {\n function queryRate(address sourceToken, address destToken)\n external\n view\n returns (uint256 rate, uint256 precision);\n\n function queryPrecision(address sourceToken, address destToken)\n external\n view\n returns (uint256 precision);\n\n function queryReturn(\n address sourceToken,\n address destToken,\n uint256 sourceAmount\n ) external view returns (uint256 destAmount);\n\n function checkPriceDisagreement(\n address sourceToken,\n address destToken,\n uint256 sourceAmount,\n uint256 destAmount,\n uint256 maxSlippage\n ) external view returns (uint256 sourceToDestSwapRate);\n\n function amountInEth(address Token, uint256 amount) external view returns (uint256 ethAmount);\n\n function getMaxDrawdown(\n address loanToken,\n address collateralToken,\n uint256 loanAmount,\n uint256 collateralAmount,\n uint256 maintenanceMargin\n ) external view returns (uint256);\n\n function getCurrentMarginAndCollateralSize(\n address loanToken,\n address collateralToken,\n uint256 loanAmount,\n uint256 collateralAmount\n ) external view returns (uint256 currentMargin, uint256 collateralInEthAmount);\n\n function getCurrentMargin(\n address loanToken,\n address collateralToken,\n uint256 loanAmount,\n uint256 collateralAmount\n ) external view returns (uint256 currentMargin, uint256 collateralToLoanRate);\n\n function shouldLiquidate(\n address loanToken,\n address collateralToken,\n uint256 loanAmount,\n uint256 collateralAmount,\n uint256 maintenanceMargin\n ) external view returns (bool);\n\n function getFastGasPrice(address payToken) external view returns (uint256);\n}\n" + }, + "contracts/feeds/IRSKOracle.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\ninterface IRSKOracle {\n function updatePrice(uint256 price, uint256 timestamp) external;\n\n function getPricing() external view returns (uint256, uint256);\n\n function setOracleAddress(address addr) external;\n\n function clearOracleAddress() external;\n}\n" + }, + "contracts/feeds/IV1PoolOracle.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\ninterface IV1PoolOracle {\n function read(uint256 price, uint256 timestamp)\n external\n view\n returns (\n uint256,\n uint256,\n uint256,\n uint256,\n uint256,\n uint256\n );\n\n function latestAnswer() external view returns (uint256);\n\n function liquidityPool() external view returns (address);\n\n function latestPrice(address _baseToken) external view returns (uint256 answer);\n}\n\ninterface ILiquidityPoolV1Converter {\n function reserveTokens(uint256 index) external view returns (address);\n}\n" + }, + "contracts/feeds/PriceFeedRSKOracle.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\nimport \"./PriceFeeds.sol\";\nimport \"./IRSKOracle.sol\";\nimport \"../openzeppelin/Ownable.sol\";\nimport \"../openzeppelin/Address.sol\";\n\n/**\n * @notice The Price Feed RSK Oracle contract.\n *\n * This contract implements RSK Oracle query functionality,\n * getting the price and the last timestamp from an external oracle contract.\n * */\ncontract PriceFeedRSKOracle is IPriceFeedsExt, Ownable {\n /* Storage */\n\n address public rskOracleAddress;\n\n /* Events */\n\n event SetRSKOracleAddress(address indexed rskOracleAddress, address changerAddress);\n\n /* Functions */\n\n /**\n * @notice Initialize a new RSK Oracle.\n *\n * @param _rskOracleAddress The RSK Oracle address.\n * */\n constructor(address _rskOracleAddress) public {\n setRSKOracleAddress(_rskOracleAddress);\n }\n\n /**\n * @notice Get the oracle price.\n * @return The price from Oracle.\n * */\n function latestAnswer() external view returns (uint256 _price) {\n IRSKOracle _rskOracle = IRSKOracle(rskOracleAddress);\n (_price, ) = _rskOracle.getPricing();\n }\n\n /**\n * @notice Get the las time oracle updated the price.\n * @return The latest time.\n */\n function latestTimestamp() external view returns (uint256 _timestamp) {\n IRSKOracle _rskOracle = IRSKOracle(rskOracleAddress);\n (, _timestamp) = _rskOracle.getPricing();\n }\n\n /**\n * @notice Set the RSK Oracle address.\n *\n * @param _rskOracleAddress The RSK Oracle address.\n */\n function setRSKOracleAddress(address _rskOracleAddress) public onlyOwner {\n require(Address.isContract(_rskOracleAddress), \"_rskOracleAddress not a contract\");\n rskOracleAddress = _rskOracleAddress;\n emit SetRSKOracleAddress(rskOracleAddress, msg.sender);\n }\n}\n" + }, + "contracts/feeds/PriceFeeds.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"../openzeppelin/Ownable.sol\";\nimport \"../interfaces/IERC20.sol\";\nimport \"./PriceFeedsConstants.sol\";\n\ninterface IPriceFeedsExt {\n function latestAnswer() external view returns (uint256);\n}\n\n/**\n * @title The Price Feeds contract.\n *\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract queries the price feeds contracts where\n * oracles updates token prices computing relative token prices.\n * And besides it includes some calculations about loans such as\n * drawdown, margin and collateral.\n * */\ncontract PriceFeeds is Constants, Ownable {\n using SafeMath for uint256;\n\n /* Events */\n\n event GlobalPricingPaused(address indexed sender, bool indexed isPaused);\n\n /* Storage */\n\n /// Mapping of PriceFeedsExt instances.\n /// token => pricefeed\n mapping(address => IPriceFeedsExt) public pricesFeeds;\n\n /// Decimals of supported tokens.\n mapping(address => uint256) public decimals;\n\n /// Value on rBTC weis for the protocol token.\n uint256 public protocolTokenEthPrice = 0.0002 ether;\n\n /// Flag to pause pricings.\n bool public globalPricingPaused = false;\n\n /* Functions */\n\n /**\n * @notice Contract deployment requires 3 parameters.\n *\n * @param _wrbtcTokenAddress The address of the wrapped wrBTC token.\n * @param _protocolTokenAddress The address of the protocol token.\n * @param _baseTokenAddress The address of the base token.\n * */\n constructor(\n address _wrbtcTokenAddress,\n address _protocolTokenAddress,\n address _baseTokenAddress\n ) public {\n /// Set decimals for this token.\n decimals[address(0)] = 18;\n decimals[_wrbtcTokenAddress] = 18;\n _setWrbtcToken(_wrbtcTokenAddress);\n _setProtocolTokenAddress(_protocolTokenAddress);\n _setBaseToken(_baseTokenAddress);\n }\n\n /**\n * @notice Calculate the price ratio between two tokens.\n *\n * @dev Public wrapper for _queryRate internal function.\n *\n * @param sourceToken The address of the source tokens.\n * @param destToken The address of the destiny tokens.\n *\n * @return rate The price ratio source/dest.\n * @return precision The ratio precision.\n * */\n function queryRate(address sourceToken, address destToken)\n public\n view\n returns (uint256 rate, uint256 precision)\n {\n return _queryRate(sourceToken, destToken);\n }\n\n /**\n * @notice Calculate the relative precision between two tokens.\n *\n * @dev Public wrapper for _getDecimalPrecision internal function.\n *\n * @param sourceToken The address of the source tokens.\n * @param destToken The address of the destiny tokens.\n *\n * @return The precision ratio source/dest.\n * */\n function queryPrecision(address sourceToken, address destToken) public view returns (uint256) {\n return sourceToken != destToken ? _getDecimalPrecision(sourceToken, destToken) : 10**18;\n }\n\n /**\n * @notice Price conversor: Calculate the price of an amount of source\n * tokens in destiny token units.\n *\n * @dev NOTE: This function returns 0 during a pause, rather than a revert.\n * Ensure calling contracts handle correctly.\n *\n * @param sourceToken The address of the source tokens.\n * @param destToken The address of the destiny tokens.\n * @param sourceAmount The amount of the source tokens.\n *\n * @return destAmount The amount of destiny tokens equivalent in price\n * to the amount of source tokens.\n * */\n function queryReturn(\n address sourceToken,\n address destToken,\n uint256 sourceAmount\n ) public view returns (uint256 destAmount) {\n if (globalPricingPaused) {\n return 0;\n }\n\n (uint256 rate, uint256 precision) = _queryRate(sourceToken, destToken);\n\n destAmount = sourceAmount.mul(rate).div(precision);\n }\n\n /**\n * @notice Calculate the swap rate between two tokens.\n *\n * Regarding slippage, there is a hardcoded slippage limit of 5%, enforced\n * by this function for all borrowing, lending and margin trading\n * originated swaps performed in the Sovryn exchange.\n *\n * This means all operations in the Sovryn exchange are subject to losing\n * up to 5% from the internal swap performed.\n *\n * @param sourceToken The address of the source tokens.\n * @param destToken The address of the destiny tokens.\n * @param sourceAmount The amount of source tokens.\n * @param destAmount The amount of destiny tokens.\n * @param maxSlippage The maximum slippage limit.\n *\n * @return sourceToDestSwapRate The swap rate between tokens.\n * */\n function checkPriceDisagreement(\n address sourceToken,\n address destToken,\n uint256 sourceAmount,\n uint256 destAmount,\n uint256 maxSlippage\n ) public view returns (uint256 sourceToDestSwapRate) {\n require(!globalPricingPaused, \"pricing is paused\");\n (uint256 rate, uint256 precision) = _queryRate(sourceToken, destToken);\n\n sourceToDestSwapRate = destAmount.mul(precision).div(sourceAmount);\n\n if (rate > sourceToDestSwapRate) {\n uint256 spreadValue = rate - sourceToDestSwapRate;\n spreadValue = spreadValue.mul(10**20).div(sourceToDestSwapRate);\n require(spreadValue <= maxSlippage, \"price disagreement\");\n }\n }\n\n /**\n * @notice Calculate the rBTC amount equivalent to a given token amount.\n * Native coin on RSK is rBTC. This code comes from Ethereum applications,\n * so Eth refers to 10**18 weis of native coin, i.e.: 1 rBTC.\n *\n * @param tokenAddress The address of the token to calculate price.\n * @param amount The amount of tokens to calculate price.\n *\n * @return ethAmount The amount of rBTC equivalent.\n * */\n function amountInEth(address tokenAddress, uint256 amount)\n public\n view\n returns (uint256 ethAmount)\n {\n /// Token is wrBTC, amount in rBTC is the same.\n if (tokenAddress == address(wrbtcToken)) {\n ethAmount = amount;\n } else {\n (uint256 toEthRate, uint256 toEthPrecision) =\n queryRate(tokenAddress, address(wrbtcToken));\n ethAmount = amount.mul(toEthRate).div(toEthPrecision);\n }\n }\n\n /**\n * @notice Calculate the maximum drawdown of a loan.\n *\n * A drawdown is commonly defined as the decline from a high peak to a\n * pullback low of a specific investment or equity in an account.\n *\n * Drawdown magnitude refers to the amount of value that a user loses\n * during the drawdown period.\n *\n * @param loanToken The address of the loan token.\n * @param collateralToken The address of the collateral token.\n * @param loanAmount The amount of the loan.\n * @param collateralAmount The amount of the collateral.\n * @param margin The relation between the position size and the loan.\n * margin = (total position size - loan) / loan\n *\n * @return maxDrawdown The maximum drawdown.\n * */\n function getMaxDrawdown(\n address loanToken,\n address collateralToken,\n uint256 loanAmount,\n uint256 collateralAmount,\n uint256 margin\n ) public view returns (uint256 maxDrawdown) {\n uint256 loanToCollateralAmount;\n if (collateralToken == loanToken) {\n loanToCollateralAmount = loanAmount;\n } else {\n (uint256 rate, uint256 precision) = queryRate(loanToken, collateralToken);\n loanToCollateralAmount = loanAmount.mul(rate).div(precision);\n }\n\n uint256 combined =\n loanToCollateralAmount.add(loanToCollateralAmount.mul(margin).div(10**20));\n\n maxDrawdown = collateralAmount > combined ? collateralAmount - combined : 0;\n }\n\n /**\n * @notice Calculate the margin and the collateral on rBTC.\n *\n * @param loanToken The address of the loan token.\n * @param collateralToken The address of the collateral token.\n * @param loanAmount The amount of the loan.\n * @param collateralAmount The amount of the collateral.\n *\n * @return currentMargin The margin of the loan.\n * @return collateralInEthAmount The amount of collateral on rBTC.\n * */\n function getCurrentMarginAndCollateralSize(\n address loanToken,\n address collateralToken,\n uint256 loanAmount,\n uint256 collateralAmount\n ) public view returns (uint256 currentMargin, uint256 collateralInEthAmount) {\n (currentMargin, ) = getCurrentMargin(\n loanToken,\n collateralToken,\n loanAmount,\n collateralAmount\n );\n\n collateralInEthAmount = amountInEth(collateralToken, collateralAmount);\n }\n\n /**\n * @notice Calculate the margin of a loan.\n *\n * @dev current margin = (total position size - loan) / loan\n * The collateral amount passed as parameter equals the total position size.\n *\n * @param loanToken The address of the loan token.\n * @param collateralToken The address of the collateral token.\n * @param loanAmount The amount of the loan.\n * @param collateralAmount The amount of the collateral.\n *\n * @return currentMargin The margin of the loan.\n * @return collateralToLoanRate The price ratio between collateral and\n * loan tokens.\n * */\n function getCurrentMargin(\n address loanToken,\n address collateralToken,\n uint256 loanAmount,\n uint256 collateralAmount\n ) public view returns (uint256 currentMargin, uint256 collateralToLoanRate) {\n uint256 collateralToLoanAmount;\n if (collateralToken == loanToken) {\n collateralToLoanAmount = collateralAmount;\n collateralToLoanRate = 10**18;\n } else {\n uint256 collateralToLoanPrecision;\n (collateralToLoanRate, collateralToLoanPrecision) = queryRate(\n collateralToken,\n loanToken\n );\n\n collateralToLoanRate = collateralToLoanRate.mul(10**18).div(collateralToLoanPrecision);\n\n collateralToLoanAmount = collateralAmount.mul(collateralToLoanRate).div(10**18);\n }\n\n if (loanAmount != 0 && collateralToLoanAmount >= loanAmount) {\n return (\n collateralToLoanAmount.sub(loanAmount).mul(10**20).div(loanAmount),\n collateralToLoanRate\n );\n } else {\n return (0, collateralToLoanRate);\n }\n }\n\n /**\n * @notice Get assessment about liquidating a loan.\n *\n * @param loanToken The address of the loan token.\n * @param collateralToken The address of the collateral token.\n * @param loanAmount The amount of the loan.\n * @param collateralAmount The amount of the collateral.\n * @param maintenanceMargin The minimum margin before liquidation.\n *\n * @return True/false to liquidate the loan.\n * */\n function shouldLiquidate(\n address loanToken,\n address collateralToken,\n uint256 loanAmount,\n uint256 collateralAmount,\n uint256 maintenanceMargin\n ) public view returns (bool) {\n (uint256 currentMargin, ) =\n getCurrentMargin(loanToken, collateralToken, loanAmount, collateralAmount);\n\n return currentMargin <= maintenanceMargin;\n }\n\n /*\n * Owner functions\n */\n\n /**\n * @notice Set new value for protocolTokenEthPrice\n *\n * @param newPrice The new value for protocolTokenEthPrice\n * */\n function setProtocolTokenEthPrice(uint256 newPrice) external onlyOwner {\n require(newPrice != 0, \"invalid price\");\n protocolTokenEthPrice = newPrice;\n }\n\n /**\n * @notice Populate pricesFeeds mapping w/ values from feeds[]\n *\n * @param tokens The array of tokens to loop and get addresses.\n * @param feeds The array of contract instances for every token.\n * */\n function setPriceFeed(address[] calldata tokens, IPriceFeedsExt[] calldata feeds)\n external\n onlyOwner\n {\n require(tokens.length == feeds.length, \"count mismatch\");\n\n for (uint256 i = 0; i < tokens.length; i++) {\n pricesFeeds[tokens[i]] = feeds[i];\n }\n }\n\n /**\n * @notice Populate decimals mapping w/ values from tokens[].decimals\n *\n * @param tokens The array of tokens to loop and get values from.\n * */\n function setDecimals(IERC20[] calldata tokens) external onlyOwner {\n for (uint256 i = 0; i < tokens.length; i++) {\n decimals[address(tokens[i])] = tokens[i].decimals();\n }\n }\n\n /**\n * @notice Set flag globalPricingPaused\n *\n * @param isPaused The new status of pause (true/false).\n * */\n function setGlobalPricingPaused(bool isPaused) external onlyOwner {\n if (globalPricingPaused != isPaused) {\n globalPricingPaused = isPaused;\n\n emit GlobalPricingPaused(msg.sender, isPaused);\n }\n }\n\n /*\n * Internal functions\n */\n\n /**\n * @notice Calculate the price ratio between two tokens.\n *\n * @param sourceToken The address of the source tokens.\n * @param destToken The address of the destiny tokens.\n *\n * @return rate The price ratio source/dest.\n * @return precision The ratio precision.\n * */\n function _queryRate(address sourceToken, address destToken)\n internal\n view\n returns (uint256 rate, uint256 precision)\n {\n require(!globalPricingPaused, \"pricing is paused\");\n\n /// Different tokens, query prices and perform division.\n if (sourceToken != destToken) {\n uint256 sourceRate;\n if (sourceToken != address(baseToken) && sourceToken != protocolTokenAddress) {\n IPriceFeedsExt _sourceFeed = pricesFeeds[sourceToken];\n require(address(_sourceFeed) != address(0), \"unsupported src feed\");\n\n /// Query token price on priceFeedsExt instance.\n sourceRate = _sourceFeed.latestAnswer();\n require(sourceRate != 0 && (sourceRate >> 128) == 0, \"price error\");\n } else {\n sourceRate = sourceToken == protocolTokenAddress ? protocolTokenEthPrice : 10**18;\n }\n\n uint256 destRate;\n if (destToken != address(baseToken) && destToken != protocolTokenAddress) {\n IPriceFeedsExt _destFeed = pricesFeeds[destToken];\n require(address(_destFeed) != address(0), \"unsupported dst feed\");\n\n /// Query token price on priceFeedsExt instance.\n destRate = _destFeed.latestAnswer();\n require(destRate != 0 && (destRate >> 128) == 0, \"price error\");\n } else {\n destRate = destToken == protocolTokenAddress ? protocolTokenEthPrice : 10**18;\n }\n\n rate = sourceRate.mul(10**18).div(destRate);\n\n precision = _getDecimalPrecision(sourceToken, destToken);\n\n /// Same tokens, return 1 with decimals.\n } else {\n rate = 10**18;\n precision = 10**18;\n }\n }\n\n /**\n * @notice Calculate the relative precision between two tokens.\n *\n * @param sourceToken The address of the source tokens.\n * @param destToken The address of the destiny tokens.\n *\n * @return The precision ratio source/dest.\n * */\n function _getDecimalPrecision(address sourceToken, address destToken)\n internal\n view\n returns (uint256)\n {\n /// Same tokens, return 1 with decimals.\n if (sourceToken == destToken) {\n return 10**18;\n\n /// Different tokens, query ERC20 precisions and return 18 +- diff.\n } else {\n uint256 sourceTokenDecimals = decimals[sourceToken];\n if (sourceTokenDecimals == 0) sourceTokenDecimals = IERC20(sourceToken).decimals();\n\n uint256 destTokenDecimals = decimals[destToken];\n if (destTokenDecimals == 0) destTokenDecimals = IERC20(destToken).decimals();\n\n if (destTokenDecimals >= sourceTokenDecimals)\n return 10**(SafeMath.sub(18, destTokenDecimals - sourceTokenDecimals));\n else return 10**(SafeMath.add(18, sourceTokenDecimals - destTokenDecimals));\n }\n }\n}\n" + }, + "contracts/feeds/PriceFeedsConstants.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../interfaces/IWrbtcERC20.sol\";\nimport \"../openzeppelin/Address.sol\";\n\n/**\n * @title The Price Feeds Constants contract.\n *\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract keep the addresses of token instances for wrBTC, base token\n * and protocol token.\n * */\ncontract Constants {\n IWrbtcERC20 public wrbtcToken;\n IWrbtcERC20 public baseToken;\n address internal protocolTokenAddress;\n\n /**\n * @notice Set wrBTC token address.\n *\n * @param _wrbtcTokenAddress The address of the wrapped wrBTC token.\n * */\n function _setWrbtcToken(address _wrbtcTokenAddress) internal {\n require(Address.isContract(_wrbtcTokenAddress), \"_wrbtcTokenAddress not a contract\");\n wrbtcToken = IWrbtcERC20(_wrbtcTokenAddress);\n }\n\n /**\n * @notice Set protocol token address.\n *\n * @param _protocolTokenAddress The address of the protocol token.\n * */\n function _setProtocolTokenAddress(address _protocolTokenAddress) internal {\n require(Address.isContract(_protocolTokenAddress), \"_protocolTokenAddress not a contract\");\n protocolTokenAddress = _protocolTokenAddress;\n }\n\n /**\n * @notice Set base token address.\n *\n * @param _baseTokenAddress The address of the base token.\n * */\n function _setBaseToken(address _baseTokenAddress) internal {\n require(Address.isContract(_baseTokenAddress), \"_baseTokenAddress not a contract\");\n baseToken = IWrbtcERC20(_baseTokenAddress);\n }\n}\n" + }, + "contracts/feeds/PriceFeedV1PoolOracle.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\nimport \"./PriceFeeds.sol\";\nimport \"./IV1PoolOracle.sol\";\nimport \"../openzeppelin/Ownable.sol\";\nimport \"../openzeppelin/Address.sol\";\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"./IPriceFeeds.sol\";\n\n/**\n * @notice The Price Feed V1 Pool Oracle contract.\n *\n * This contract implements V1 Pool Oracle query functionality,\n * getting the price from v1 pool oracle.\n * */\ncontract PriceFeedV1PoolOracle is IPriceFeedsExt, Ownable {\n using SafeMath for uint256;\n /* Storage */\n\n address public v1PoolOracleAddress;\n address public wRBTCAddress;\n address public docAddress;\n address public baseCurrency;\n\n /* Events */\n event SetV1PoolOracleAddress(address indexed v1PoolOracleAddress, address changerAddress);\n event SetWRBTCAddress(address indexed wRBTCAddress, address changerAddress);\n event SetDOCAddress(address indexed docAddress, address changerAddress);\n event SetBaseCurrency(address indexed baseCurrency, address changerAddress);\n\n /* Functions */\n\n /**\n * @notice Initialize a new V1 Pool Oracle.\n *\n * @param _v1PoolOracleAddress The V1 Pool Oracle address.\n * @param _wRBTCAddress The wrbtc token address.\n * @param _docAddress The doc token address.\n * */\n constructor(\n address _v1PoolOracleAddress,\n address _wRBTCAddress,\n address _docAddress,\n address _baseCurrency\n ) public {\n setRBTCAddress(_wRBTCAddress);\n setDOCAddress(_docAddress);\n setV1PoolOracleAddress(_v1PoolOracleAddress);\n setBaseCurrency(_baseCurrency);\n }\n\n /**\n * @notice Get the oracle price.\n * @return The price from Oracle.\n * */\n function latestAnswer() external view returns (uint256) {\n IV1PoolOracle _v1PoolOracle = IV1PoolOracle(v1PoolOracleAddress);\n\n uint256 _price = _v1PoolOracle.latestPrice(baseCurrency);\n\n // Need to convert to USD, since the V1 pool return value is based on BTC\n uint256 priceInUSD = _convertAnswerToUsd(_price);\n require(priceInUSD != 0, \"price error\");\n\n return priceInUSD;\n }\n\n function _convertAnswerToUsd(uint256 _valueInBTC) private view returns (uint256) {\n address _priceFeeds = msg.sender;\n\n uint256 precision = IPriceFeeds(_priceFeeds).queryPrecision(wRBTCAddress, docAddress);\n uint256 valueInUSD =\n IPriceFeeds(_priceFeeds).queryReturn(wRBTCAddress, docAddress, _valueInBTC);\n\n /// Need to multiply by query precision (doc's precision) and divide by 1*10^18 (Because the based price in v1 pool is using 18 decimals)\n return valueInUSD.mul(precision).div(1e18);\n }\n\n /**\n * @notice Set the V1 Pool Oracle address.\n *\n * @param _v1PoolOracleAddress The V1 Pool Oracle address.\n */\n function setV1PoolOracleAddress(address _v1PoolOracleAddress) public onlyOwner {\n require(Address.isContract(_v1PoolOracleAddress), \"_v1PoolOracleAddress not a contract\");\n IV1PoolOracle _v1PoolOracle = IV1PoolOracle(_v1PoolOracleAddress);\n address liquidityPool = _v1PoolOracle.liquidityPool();\n require(\n ILiquidityPoolV1Converter(liquidityPool).reserveTokens(0) == wRBTCAddress ||\n ILiquidityPoolV1Converter(liquidityPool).reserveTokens(1) == wRBTCAddress,\n \"one of the two reserves needs to be wrbtc\"\n );\n v1PoolOracleAddress = _v1PoolOracleAddress;\n emit SetV1PoolOracleAddress(v1PoolOracleAddress, msg.sender);\n }\n\n /**\n * @notice Set the rBtc address. V1 pool based price is BTC, so need to convert the value from v1 pool to USD. That's why we need to get the price of the rBtc\n *\n * @param _wRBTCAddress The rBTC address\n */\n function setRBTCAddress(address _wRBTCAddress) public onlyOwner {\n require(_wRBTCAddress != address(0), \"wRBTC address cannot be zero address\");\n wRBTCAddress = _wRBTCAddress;\n emit SetWRBTCAddress(wRBTCAddress, msg.sender);\n }\n\n /**\n * @notice Set the DoC address. V1 pool based price is BTC, so need to convert the value from v1 pool to USD. That's why we need to get the price of the DoC\n *\n * @param _docAddress The DoC address\n */\n function setDOCAddress(address _docAddress) public onlyOwner {\n require(_docAddress != address(0), \"DOC address cannot be zero address\");\n docAddress = _docAddress;\n emit SetDOCAddress(_docAddress, msg.sender);\n }\n\n /**\n * @notice Set the base currency address. That's the reserve address which is not WRBTC\n *\n * @param _baseCurrency The base currency address\n */\n function setBaseCurrency(address _baseCurrency) public onlyOwner {\n require(_baseCurrency != address(0), \"Base currency address cannot be zero address\");\n baseCurrency = _baseCurrency;\n emit SetBaseCurrency(_baseCurrency, msg.sender);\n }\n}\n" + }, + "contracts/feeds/testnet/PriceFeedsLocal.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../PriceFeeds.sol\";\n\n/**\n * @title Price Feeds Local contract.\n *\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the logic of setting and getting rates between two tokens.\n * */\ncontract PriceFeedsLocal is PriceFeeds {\n mapping(address => mapping(address => uint256)) public rates;\n\n /// uint256 public slippageMultiplier = 100 ether;\n\n /**\n * @notice Deploy local price feed contract.\n *\n * @param _wrbtcTokenAddress The address of the wrBTC instance.\n * @param _protocolTokenAddress The address of the protocol token instance.\n * */\n constructor(address _wrbtcTokenAddress, address _protocolTokenAddress)\n public\n PriceFeeds(_wrbtcTokenAddress, _protocolTokenAddress, _wrbtcTokenAddress)\n {}\n\n /**\n * @notice Calculate the price ratio between two tokens.\n *\n * @param sourceToken The address of the source tokens.\n * @param destToken The address of the destiny tokens.\n *\n * @return rate The price ratio source/dest.\n * @return precision The ratio precision.\n * */\n function _queryRate(address sourceToken, address destToken)\n internal\n view\n returns (uint256 rate, uint256 precision)\n {\n require(!globalPricingPaused, \"pricing is paused\");\n\n if (sourceToken == destToken) {\n rate = 10**18;\n precision = 10**18;\n } else {\n if (sourceToken == protocolTokenAddress) {\n /// Hack for testnet; only returns price in rBTC.\n rate = protocolTokenEthPrice;\n } else if (destToken == protocolTokenAddress) {\n /// Hack for testnet; only returns price in rBTC.\n rate = SafeMath.div(10**36, protocolTokenEthPrice);\n } else {\n if (rates[sourceToken][destToken] != 0) {\n rate = rates[sourceToken][destToken];\n } else {\n uint256 sourceToEther =\n rates[sourceToken][address(wrbtcToken)] != 0\n ? rates[sourceToken][address(wrbtcToken)]\n : 10**18;\n uint256 etherToDest =\n rates[address(wrbtcToken)][destToken] != 0\n ? rates[address(wrbtcToken)][destToken]\n : 10**18;\n\n rate = sourceToEther.mul(etherToDest).div(10**18);\n }\n }\n precision = _getDecimalPrecision(sourceToken, destToken);\n }\n }\n\n /**\n * @notice Owner set price ratio between two tokens.\n *\n * @param sourceToken The address of the source tokens.\n * @param destToken The address of the destiny tokens.\n * @param rate The price ratio source/dest.\n * */\n function setRates(\n address sourceToken,\n address destToken,\n uint256 rate\n ) public onlyOwner {\n if (sourceToken != destToken) {\n rates[sourceToken][destToken] = rate;\n rates[destToken][sourceToken] = SafeMath.div(10**36, rate);\n }\n }\n\n /*function setSlippageMultiplier(\n uint256 _slippageMultiplier)\n public\n onlyOwner\n {\n require (slippageMultiplier != _slippageMultiplier && _slippageMultiplier <= 100 ether);\n slippageMultiplier = _slippageMultiplier;\n }*/\n}\n" + }, + "contracts/feeds/testnet/PriceFeedsMoC.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../PriceFeeds.sol\";\nimport \"../IRSKOracle.sol\";\nimport \"../../openzeppelin/Address.sol\";\n\ninterface Medianizer {\n function peek() external view returns (bytes32, bool);\n}\n\n/**\n * @title Price Feed of MoC (Money on Chain) contract.\n *\n * This contract contains the logic to set MoC oracles\n * and query last price update.\n * */\ncontract PriceFeedsMoC is IPriceFeedsExt, Ownable {\n /* Storage */\n\n address public mocOracleAddress;\n address public rskOracleAddress;\n\n /* Events */\n\n event SetMoCOracleAddress(address indexed mocOracleAddress, address changerAddress);\n event SetRSKOracleAddress(address indexed rskOracleAddress, address changerAddress);\n\n /* Functions */\n\n /**\n * @notice Initialize a new MoC Oracle.\n *\n * @param _mocOracleAddress The MoC Oracle address.\n * @param _rskOracleAddress The RSK Oracle address.\n * */\n constructor(address _mocOracleAddress, address _rskOracleAddress) public {\n setMoCOracleAddress(_mocOracleAddress);\n setRSKOracleAddress(_rskOracleAddress);\n }\n\n /**\n * @notice Get the las time oracle updated the price.\n * @return The latest time.\n */\n function latestAnswer() external view returns (uint256) {\n (bytes32 value, bool hasValue) = Medianizer(mocOracleAddress).peek();\n if (hasValue) {\n return uint256(value);\n } else {\n (uint256 price, ) = IRSKOracle(rskOracleAddress).getPricing();\n return price;\n }\n }\n\n /**\n * @notice Set the MoC Oracle address.\n *\n * @param _mocOracleAddress The MoC Oracle address.\n */\n function setMoCOracleAddress(address _mocOracleAddress) public onlyOwner {\n require(Address.isContract(_mocOracleAddress), \"_mocOracleAddress not a contract\");\n mocOracleAddress = _mocOracleAddress;\n emit SetMoCOracleAddress(mocOracleAddress, msg.sender);\n }\n\n /**\n * @notice Set the RSK Oracle address.\n *\n * @param _rskOracleAddress The RSK Oracle address.\n */\n function setRSKOracleAddress(address _rskOracleAddress) public onlyOwner {\n require(Address.isContract(_rskOracleAddress), \"_rskOracleAddress not a contract\");\n rskOracleAddress = _rskOracleAddress;\n emit SetRSKOracleAddress(rskOracleAddress, msg.sender);\n }\n}\n" + }, + "contracts/feeds/USDTPriceFeed.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"./PriceFeeds.sol\";\n\n/**\n * @notice The Price Feed USDT contract.\n *\n * This contract implements USDT query functionality,\n * getting the price and the last timestamp from a\n * trivial formula, always returning 1 and now.\n * */\ncontract USDTPriceFeed is IPriceFeedsExt {\n uint256 private constant USDT_RATE = 1 ether;\n\n /**\n * @notice Get the USDT price.\n *\n * @return Always returns the trivial rate of 1.\n * */\n function latestAnswer() external view returns (uint256) {\n return USDT_RATE;\n }\n\n /**\n * @notice Get the las time the price was updated.\n * @return Always trivial current block's timestamp.\n */\n function latestTimestamp() external view returns (uint256) {\n return now;\n }\n}\n" + }, + "contracts/governance/ApprovalReceiver.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./ErrorDecoder.sol\";\nimport \"../token/IApproveAndCall.sol\";\n\n/**\n * @title Base contract for receiving approval from SOV token.\n */\ncontract ApprovalReceiver is ErrorDecoder, IApproveAndCall {\n modifier onlyThisContract() {\n // Accepts calls only from receiveApproval function.\n require(msg.sender == address(this), \"unauthorized\");\n _;\n }\n\n /**\n * @notice Receives approval from SOV token.\n * @param _data The data will be used for low level call.\n */\n function receiveApproval(\n address _sender,\n uint256 _amount,\n address _token,\n bytes calldata _data\n ) external {\n // Accepts calls only from SOV token.\n require(msg.sender == _getToken(), \"unauthorized\");\n require(msg.sender == _token, \"unauthorized\");\n\n // Only allowed methods.\n bool isAllowed = false;\n bytes4[] memory selectors = _getSelectors();\n bytes4 sig = _getSig(_data);\n for (uint256 i = 0; i < selectors.length; i++) {\n if (sig == selectors[i]) {\n isAllowed = true;\n break;\n }\n }\n require(isAllowed, \"method is not allowed\");\n\n // Check sender and amount.\n address sender;\n uint256 amount;\n (, sender, amount) = abi.decode(\n abi.encodePacked(bytes28(0), _data),\n (bytes32, address, uint256)\n );\n require(sender == _sender, \"sender mismatch\");\n require(amount == _amount, \"amount mismatch\");\n\n _call(_data);\n }\n\n /**\n * @notice Returns token address, only this address can be a sender for receiveApproval.\n * @dev Should be overridden in child contracts, otherwise error will be thrown.\n * @return By default, 0x. When overriden, the token address making the call.\n */\n function _getToken() internal view returns (address) {\n return address(0);\n }\n\n /**\n * @notice Returns list of function selectors allowed to be invoked.\n * @dev Should be overridden in child contracts, otherwise error will be thrown.\n * @return By default, empty array. When overriden, allowed selectors.\n */\n function _getSelectors() internal pure returns (bytes4[] memory) {\n return new bytes4[](0);\n }\n\n /**\n * @notice Makes call and reverts w/ enhanced error message.\n * @param _data Error message as bytes.\n */\n function _call(bytes memory _data) internal {\n (bool success, bytes memory returnData) = address(this).call(_data);\n if (!success) {\n if (returnData.length <= ERROR_MESSAGE_SHIFT) {\n revert(\"receiveApproval: Transaction execution reverted.\");\n } else {\n revert(_addErrorMessage(\"receiveApproval: \", string(returnData)));\n }\n }\n }\n\n /**\n * @notice Extracts the called function selector, a hash of the signature.\n * @dev The first four bytes of the call data for a function call specifies\n * the function to be called. It is the first (left, high-order in big-endian)\n * four bytes of the Keccak-256 (SHA-3) hash of the signature of the function.\n * Solidity doesn't yet support a casting of byte[4] to bytes4.\n * Example:\n * msg.data:\n * 0xcdcd77c000000000000000000000000000000000000000000000000000000000000\n * 000450000000000000000000000000000000000000000000000000000000000000001\n * selector (or method ID): 0xcdcd77c0\n * signature: baz(uint32,bool)\n * @param _data The msg.data from the low level call.\n * @return sig First 4 bytes of msg.data i.e. the selector, hash of the signature.\n */\n function _getSig(bytes memory _data) internal pure returns (bytes4 sig) {\n assembly {\n sig := mload(add(_data, 32))\n }\n }\n}\n" + }, + "contracts/governance/ErrorDecoder.sol": { + "content": "pragma solidity ^0.5.17;\n\n/**\n * @title Base contract to properly handle returned data on failed calls\n * @dev On EVM if the return data length of a call is less than 68,\n * then the transaction fails silently without a revert message!\n *\n * As described in the Solidity documentation\n * https://solidity.readthedocs.io/en/v0.5.17/control-structures.html#revert\n * the revert reason is an ABI-encoded string consisting of:\n * 0x08c379a0 // Function selector (method id) for \"Error(string)\" signature\n * 0x0000000000000000000000000000000000000000000000000000000000000020 // Data offset\n * 0x000000000000000000000000000000000000000000000000000000000000001a // String length\n * 0x4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 // String data\n *\n * Another example, debug data from test:\n * 0x08c379a0\n * 0000000000000000000000000000000000000000000000000000000000000020\n * 0000000000000000000000000000000000000000000000000000000000000034\n * 54696d656c6f636b3a3a73657444656c61793a2044656c6179206d7573742065\n * 7863656564206d696e696d756d2064656c61792e000000000000000000000000\n *\n * Parsed into:\n * Data offset: 20\n * Length: 34\n * Error message:\n * 54696d656c6f636b3a3a73657444656c61793a2044656c6179206d7573742065\n * 7863656564206d696e696d756d2064656c61792e000000000000000000000000\n */\ncontract ErrorDecoder {\n uint256 constant ERROR_MESSAGE_SHIFT = 68; // EVM silent revert error string length\n\n /**\n * @notice Concats two error strings taking into account ERROR_MESSAGE_SHIFT.\n * @param str1 First string, usually a hardcoded context written by dev.\n * @param str2 Second string, usually the error message from the reverted call.\n * @return The concatenated error string\n */\n function _addErrorMessage(string memory str1, string memory str2)\n internal\n pure\n returns (string memory)\n {\n bytes memory bytesStr1 = bytes(str1);\n bytes memory bytesStr2 = bytes(str2);\n string memory str12 =\n new string(bytesStr1.length + bytesStr2.length - ERROR_MESSAGE_SHIFT);\n bytes memory bytesStr12 = bytes(str12);\n uint256 j = 0;\n for (uint256 i = 0; i < bytesStr1.length; i++) {\n bytesStr12[j++] = bytesStr1[i];\n }\n for (uint256 i = ERROR_MESSAGE_SHIFT; i < bytesStr2.length; i++) {\n bytesStr12[j++] = bytesStr2[i];\n }\n return string(bytesStr12);\n }\n}\n" + }, + "contracts/governance/FeeSharingCollector/FeeSharingCollector.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../Staking/SafeMath96.sol\";\nimport \"../../openzeppelin/SafeMath.sol\";\nimport \"../../openzeppelin/SafeERC20.sol\";\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"../IFeeSharingCollector.sol\";\nimport \"../../openzeppelin/Address.sol\";\nimport \"./FeeSharingCollectorStorage.sol\";\nimport \"../../interfaces/IConverterAMM.sol\";\n\n/**\n * @title The FeeSharingCollector contract.\n * @notice This contract withdraws fees to be paid to SOV Stakers from the protocol.\n * Stakers call withdraw() to get their share of the fees.\n *\n * @notice Staking is not only granting voting rights, but also access to fee\n * sharing according to the own voting power in relation to the total. Whenever\n * somebody decides to collect the fees from the protocol, they get transferred\n * to a proxy contract which invests the funds in the lending pool and keeps\n * the pool tokens.\n *\n * The fee sharing proxy will be set as feesController of the protocol contract.\n * This allows the fee sharing proxy to withdraw the fees. The fee sharing\n * proxy holds the pool tokens and keeps track of which user owns how many\n * tokens. In order to know how many tokens a user owns, the fee sharing proxy\n * needs to know the user’s weighted stake in relation to the total weighted\n * stake (aka total voting power).\n *\n * Because both values are subject to change, they may be different on each fee\n * withdrawal. To be able to calculate a user’s share of tokens when he wants\n * to withdraw, we need checkpoints.\n *\n * This contract is intended to be set as the protocol fee collector.\n * Anybody can invoke the withdrawFees function which uses\n * protocol.withdrawFees to obtain available fees from operations on a\n * certain token. These fees are deposited in the corresponding loanPool.\n * Also, the staking contract sends slashed tokens to this contract.\n * When a user calls the withdraw function, the contract transfers the fee sharing\n * rewards in proportion to the user’s weighted stake since the last withdrawal.\n *\n * The protocol initially collects fees in all tokens.\n * Then the FeeSharingCollector wihtdraws fees from the protocol.\n * When the fees are withdrawn all the tokens except SOV will be converted to wRBTC\n * and then transferred to wRBTC loan pool.\n * For SOV, it will be directly deposited into the feeSharingCollector from the protocol.\n * */\ncontract FeeSharingCollector is\n SafeMath96,\n IFeeSharingCollector,\n Ownable,\n FeeSharingCollectorStorage\n{\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n address constant ZERO_ADDRESS = address(0);\n address public constant RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT =\n address(uint160(uint256(keccak256(\"RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT\"))));\n\n /* Events */\n\n /// @notice Deprecated event after the unification between wrbtc & rbtc\n // event FeeWithdrawn(address indexed sender, address indexed token, uint256 amount);\n event FeeWithdrawnInRBTC(address indexed sender, uint256 amount);\n\n /// @notice An event emitted when tokens transferred.\n event TokensTransferred(address indexed sender, address indexed token, uint256 amount);\n\n /// @notice An event emitted when checkpoint added.\n event CheckpointAdded(address indexed sender, address indexed token, uint256 amount);\n\n /// @notice An event emitted when user fee get withdrawn.\n event UserFeeWithdrawn(\n address indexed sender,\n address indexed receiver,\n address indexed token,\n uint256 amount\n );\n\n /// @notice An event emitted when user fee get withdrawn.\n event UserFeeProcessedNoWithdraw(\n address indexed sender,\n address indexed token,\n uint256 prevProcessedCheckpoints,\n uint256 newProcessedCheckpoints\n );\n\n /**\n * @notice An event emitted when fee from AMM get withdrawn.\n *\n * @param sender sender who initiate the withdrawn amm fees.\n * @param converter the converter address.\n * @param amount total amount of fee (Already converted to WRBTC).\n */\n event FeeAMMWithdrawn(address indexed sender, address indexed converter, uint256 amount);\n\n /// @notice An event emitted when converter address has been registered to be whitelisted.\n event WhitelistedConverter(address indexed sender, address converter);\n\n /// @notice An event emitted when converter address has been removed from whitelist.\n event UnwhitelistedConverter(address indexed sender, address converter);\n\n event RBTCWithdrawn(address indexed sender, address indexed receiver, uint256 amount);\n\n event SetWrbtcToken(\n address indexed sender,\n address indexed oldWrbtcToken,\n address indexed newWrbtcToken\n );\n\n event SetLoanTokenWrbtc(\n address indexed sender,\n address indexed oldLoanTokenWrbtc,\n address indexed newLoanTokenWrbtc\n );\n\n /* Modifier */\n modifier oneTimeExecution(bytes4 _funcSig) {\n require(\n !isFunctionExecuted[_funcSig],\n \"FeeSharingCollector: function can only be called once\"\n );\n _;\n isFunctionExecuted[_funcSig] = true;\n }\n\n /* Functions */\n\n /// @dev fallback function to support rbtc transfer when unwrap the wrbtc.\n function() external payable {}\n\n /**\n * @dev initialize function for fee sharing collector proxy\n * @param wrbtcToken wrbtc token address\n * @param loanWrbtcToken address of loan token wrbtc (IWrbtc)\n */\n function initialize(address wrbtcToken, address loanWrbtcToken)\n external\n onlyOwner\n oneTimeExecution(this.initialize.selector)\n {\n require(\n wrbtcTokenAddress == address(0) && loanTokenWrbtcAddress == address(0),\n \"wrbtcToken or loanWrbtcToken has been initialized\"\n );\n setWrbtcToken(wrbtcToken);\n setLoanTokenWrbtc(loanWrbtcToken);\n }\n\n /**\n * @notice Set the wrbtc token address of fee sharing collector.\n *\n * only owner can perform this action.\n *\n * @param newWrbtcTokenAddress The new address of the wrbtc token.\n * */\n function setWrbtcToken(address newWrbtcTokenAddress) public onlyOwner {\n require(Address.isContract(newWrbtcTokenAddress), \"newWrbtcTokenAddress not a contract\");\n emit SetWrbtcToken(msg.sender, wrbtcTokenAddress, newWrbtcTokenAddress);\n wrbtcTokenAddress = newWrbtcTokenAddress;\n }\n\n /**\n * @notice Set the loan wrbtc token address of fee sharing collector.\n *\n * only owner can perform this action.\n *\n * @param newLoanTokenWrbtcAddress The new address of the loan wrbtc token.\n * */\n function setLoanTokenWrbtc(address newLoanTokenWrbtcAddress) public onlyOwner {\n require(\n Address.isContract(newLoanTokenWrbtcAddress),\n \"newLoanTokenWrbtcAddress not a contract\"\n );\n emit SetLoanTokenWrbtc(msg.sender, loanTokenWrbtcAddress, newLoanTokenWrbtcAddress);\n loanTokenWrbtcAddress = newLoanTokenWrbtcAddress;\n }\n\n /**\n * @notice Withdraw fees for the given token:\n * lendingFee + tradingFee + borrowingFee\n * the fees (except SOV) will be converted in wRBTC form, and then will be transferred to wRBTC loan pool.\n * For SOV, it will be directly deposited into the feeSharingCollector from the protocol.\n *\n * @param _tokens array address of the token\n * */\n function withdrawFees(address[] calldata _tokens) external {\n for (uint256 i = 0; i < _tokens.length; i++) {\n require(\n Address.isContract(_tokens[i]),\n \"FeeSharingCollector::withdrawFees: token is not a contract\"\n );\n }\n\n uint256 wrbtcAmountWithdrawn = protocol.withdrawFees(_tokens, address(this));\n\n IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress);\n\n if (wrbtcAmountWithdrawn > 0) {\n // unwrap the wrbtc to rbtc, and hold the rbtc.\n wrbtcToken.withdraw(wrbtcAmountWithdrawn);\n\n /// @notice Update unprocessed amount of tokens\n uint96 amount96 =\n safe96(\n wrbtcAmountWithdrawn,\n \"FeeSharingCollector::withdrawFees: wrbtc token amount exceeds 96 bits\"\n );\n\n _addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, amount96);\n }\n\n // note deprecated event since we unify the wrbtc & rbtc\n // emit FeeWithdrawn(msg.sender, RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, poolTokenAmount);\n\n // note new emitted event\n emit FeeWithdrawnInRBTC(msg.sender, wrbtcAmountWithdrawn);\n }\n\n /**\n * @notice Withdraw amm fees for the given converter addresses:\n * protocolFee from the conversion\n * the fees will be converted in wRBTC form, and then will be transferred to wRBTC loan pool\n *\n * @param _converters array addresses of the converters\n * */\n function withdrawFeesAMM(address[] memory _converters) public {\n IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress);\n\n // Validate\n _validateWhitelistedConverter(_converters);\n\n uint96 totalPoolTokenAmount;\n for (uint256 i = 0; i < _converters.length; i++) {\n uint256 wrbtcAmountWithdrawn =\n IConverterAMM(_converters[i]).withdrawFees(address(this));\n\n if (wrbtcAmountWithdrawn > 0) {\n // unwrap wrbtc to rbtc, and hold the rbtc\n wrbtcToken.withdraw(wrbtcAmountWithdrawn);\n\n /// @notice Update unprocessed amount of tokens\n uint96 amount96 =\n safe96(\n wrbtcAmountWithdrawn,\n \"FeeSharingCollector::withdrawFeesAMM: wrbtc token amount exceeds 96 bits\"\n );\n\n totalPoolTokenAmount = add96(\n totalPoolTokenAmount,\n amount96,\n \"FeeSharingCollector::withdrawFeesAMM: total wrbtc token amount exceeds 96 bits\"\n );\n\n emit FeeAMMWithdrawn(msg.sender, _converters[i], wrbtcAmountWithdrawn);\n }\n }\n\n if (totalPoolTokenAmount > 0) {\n _addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, totalPoolTokenAmount);\n }\n }\n\n /**\n * @notice Transfer tokens to this contract.\n * @dev We just update amount of tokens here and write checkpoint in a separate methods\n * in order to prevent adding checkpoints too often.\n * @param _token Address of the token.\n * @param _amount Amount to be transferred.\n * */\n function transferTokens(address _token, uint96 _amount) public {\n require(_token != ZERO_ADDRESS, \"FeeSharingCollector::transferTokens: invalid address\");\n require(_amount > 0, \"FeeSharingCollector::transferTokens: invalid amount\");\n\n /// @notice Transfer tokens from msg.sender\n bool success = IERC20(_token).transferFrom(address(msg.sender), address(this), _amount);\n require(success, \"Staking::transferTokens: token transfer failed\");\n\n // if _token is wrbtc, need to unwrap it to rbtc\n IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress);\n if (_token == address(wrbtcToken)) {\n wrbtcToken.withdraw(_amount);\n _token = RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT;\n }\n\n _addCheckpoint(_token, _amount);\n\n emit TokensTransferred(msg.sender, _token, _amount);\n }\n\n /**\n * @notice Transfer RBTC / native tokens to this contract.\n * @dev We just write checkpoint here (based on the rbtc value that is sent) in a separate methods\n * in order to prevent adding checkpoints too often.\n * */\n function transferRBTC() external payable {\n uint96 _amount = uint96(msg.value);\n require(_amount > 0, \"FeeSharingCollector::transferRBTC: invalid value\");\n\n _addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, _amount);\n\n emit TokensTransferred(msg.sender, ZERO_ADDRESS, _amount);\n }\n\n /**\n * @notice Add checkpoint with accumulated amount by function invocation.\n * @param _token Address of the token.\n * */\n function _addCheckpoint(address _token, uint96 _amount) internal {\n if (block.timestamp - lastFeeWithdrawalTime[_token] >= FEE_WITHDRAWAL_INTERVAL) {\n lastFeeWithdrawalTime[_token] = block.timestamp;\n uint96 amount =\n add96(\n unprocessedAmount[_token],\n _amount,\n \"FeeSharingCollector::_addCheckpoint: amount exceeds 96 bits\"\n );\n\n /// @notice Reset unprocessed amount of tokens to zero.\n unprocessedAmount[_token] = 0;\n\n /// @notice Write a regular checkpoint.\n _writeTokenCheckpoint(_token, amount);\n } else {\n unprocessedAmount[_token] = add96(\n unprocessedAmount[_token],\n _amount,\n \"FeeSharingCollector::_addCheckpoint: unprocessedAmount exceeds 96 bits\"\n );\n }\n }\n\n function _withdraw(\n address _token,\n uint32 _maxCheckpoints,\n address _receiver\n ) internal returns (uint256 totalAmount, uint256 endTokenCheckpoint) {\n /// @dev Prevents block gas limit hit when processing checkpoints\n require(\n _maxCheckpoints > 0,\n \"FeeSharingCollector::withdraw: _maxCheckpoints should be positive\"\n );\n\n address user = msg.sender;\n if (_receiver == ZERO_ADDRESS) {\n _receiver = msg.sender;\n }\n uint256 processedUserCheckpoints = processedCheckpoints[user][_token];\n (uint256 amount, uint256 end) =\n _getAccumulatedFees(user, _token, processedUserCheckpoints, _maxCheckpoints);\n if (amount == 0) {\n if (end > processedUserCheckpoints) {\n emit UserFeeProcessedNoWithdraw(msg.sender, _token, processedUserCheckpoints, end);\n processedCheckpoints[user][_token] = end;\n return (0, end);\n } else {\n // getting here most likely means smth wrong with the state\n revert(\"FeeSharingCollector::withdrawFees: no tokens for withdrawal\");\n }\n }\n\n processedCheckpoints[user][_token] = end;\n if (loanTokenWrbtcAddress == _token) {\n // We will change, so that feeSharingCollector will directly burn then loanToken (IWRBTC) to rbtc and send to the user --- by call burnToBTC function\n ILoanTokenWRBTC(_token).burnToBTC(_receiver, amount, false);\n } else {\n // Previously it directly send the loanToken to the user\n require(\n IERC20(_token).transfer(_receiver, amount),\n \"FeeSharingCollector::withdraw: withdrawal failed\"\n );\n }\n\n emit UserFeeWithdrawn(msg.sender, _receiver, _token, amount);\n\n return (amount, end);\n }\n\n /**\n * @notice Withdraw accumulated fee to the message sender.\n *\n * The Sovryn protocol collects fees on every trade/swap and loan.\n * These fees will be distributed to SOV stakers based on their voting\n * power as a percentage of total voting power. Therefore, staking more\n * SOV and/or staking for longer will increase your share of the fees\n * generated, meaning you will earn more from staking.\n *\n * This function will directly burnToBTC and use the msg.sender (user) as the receiver\n *\n * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.\n * @param _maxCheckpoints Maximum number of checkpoints to be processed. Must be positive value.\n * @param _receiver The receiver of tokens or msg.sender\n * */\n function withdraw(\n address _token,\n uint32 _maxCheckpoints,\n address _receiver\n ) public nonReentrant {\n _withdraw(_token, _maxCheckpoints, _receiver);\n }\n\n /// @notice Validates if the checkpoint is payable for the user\n function validFromCheckpointsParam(\n TokenWithSkippedCheckpointsWithdraw[] memory _tokens,\n address _user\n ) private view {\n for (uint256 i = 0; i < _tokens.length; i++) {\n TokenWithSkippedCheckpointsWithdraw memory tokenData = _tokens[i];\n // _fromCheckpoint is checkpoint number, not array index, so should be > 1\n require(tokenData.fromCheckpoint > 1, \"_fromCheckpoint param must be > 1\");\n uint256 fromCheckpointIndex = tokenData.fromCheckpoint - 1;\n require(\n tokenData.fromCheckpoint > processedCheckpoints[_user][tokenData.tokenAddress],\n \"_fromCheckpoint param must be > userProcessedCheckpoints\"\n );\n require(\n tokenData.fromCheckpoint <= totalTokenCheckpoints[tokenData.tokenAddress],\n \"_fromCheckpoint should be <= totalTokenCheckpoints\"\n );\n\n Checkpoint memory prevCheckpoint =\n tokenCheckpoints[tokenData.tokenAddress][fromCheckpointIndex - 1];\n\n uint96 weightedStake =\n staking.getPriorWeightedStake(\n _user,\n prevCheckpoint.blockNumber - 1,\n prevCheckpoint.timestamp\n );\n require(\n weightedStake == 0,\n \"User weighted stake should be zero at previous checkpoint\"\n );\n\n Checkpoint memory fromCheckpoint =\n tokenCheckpoints[tokenData.tokenAddress][fromCheckpointIndex];\n weightedStake = staking.getPriorWeightedStake(\n _user,\n fromCheckpoint.blockNumber - 1,\n fromCheckpoint.timestamp\n );\n\n require(weightedStake > 0, \"User weighted stake should be > 0 at _fromCheckpoint\");\n }\n }\n\n function validRBTCBasedTokens(address[] memory _tokens) private view {\n for (uint256 i = 0; i < _tokens.length; i++) {\n address _token = _tokens[i];\n if (\n _token != RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT &&\n _token != wrbtcTokenAddress &&\n _token != loanTokenWrbtcAddress\n ) {\n revert(\"only rbtc-based tokens are allowed\");\n }\n }\n }\n\n /**\n * @notice Withdraw accumulated fee to the message sender/receiver.\n *\n * The Sovryn protocol collects fees on every trade/swap and loan.\n * These fees will be distributed to SOV stakers based on their voting\n * power as a percentage of total voting power.\n *\n * This function will directly burnToBTC and use the msg.sender (user) as the receiver\n *\n * @dev WARNING! This function skips all the checkpoints before '_fromCheckpoint' irreversibly, use with care\n *\n * @param _tokens Array of TokenWithSkippedCheckpointsWithdraw struct, which contains the token address, and fromCheckpoiint\n * fromCheckpoints Skips all the checkpoints before '_fromCheckpoint'\n * should be calculated offchain with getNextPositiveUserCheckpoint function\n * @param _maxCheckpoints Maximum number of checkpoints to be processed.\n * @param _receiver The receiver of tokens or msg.sender\n *\n * @return total processed checkpoints\n * */\n function _withdrawStartingFromCheckpoints(\n TokenWithSkippedCheckpointsWithdraw[] memory _tokens,\n uint32 _maxCheckpoints,\n address _receiver\n ) internal returns (uint256 totalProcessedCheckpoints) {\n validFromCheckpointsParam(_tokens, msg.sender);\n\n if (_receiver == ZERO_ADDRESS) {\n _receiver = msg.sender;\n }\n\n uint256 rbtcAmountToSend;\n\n for (uint256 i = 0; i < _tokens.length; i++) {\n TokenWithSkippedCheckpointsWithdraw memory tokenData = _tokens[i];\n if (_maxCheckpoints == 0) break;\n uint256 endToken;\n uint256 totalAmount;\n\n uint256 previousProcessedUserCheckpoints =\n processedCheckpoints[msg.sender][tokenData.tokenAddress];\n uint256 startingCheckpoint =\n tokenData.fromCheckpoint > previousProcessedUserCheckpoints\n ? tokenData.fromCheckpoint\n : previousProcessedUserCheckpoints;\n\n if (\n tokenData.tokenAddress == wrbtcTokenAddress ||\n tokenData.tokenAddress == loanTokenWrbtcAddress ||\n tokenData.tokenAddress == RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT\n ) {\n (totalAmount, endToken) = _withdrawRbtcTokenStartingFromCheckpoint(\n tokenData.tokenAddress,\n tokenData.fromCheckpoint,\n _maxCheckpoints,\n _receiver\n );\n rbtcAmountToSend = rbtcAmountToSend.add(totalAmount);\n } else {\n (, endToken) = _withdrawStartingFromCheckpoint(\n tokenData.tokenAddress,\n tokenData.fromCheckpoint,\n _maxCheckpoints,\n _receiver\n );\n }\n\n uint256 _previousUsedCheckpoint = endToken.sub(startingCheckpoint).add(1);\n totalProcessedCheckpoints += _previousUsedCheckpoint;\n _maxCheckpoints = safe32(\n _maxCheckpoints - _previousUsedCheckpoint,\n \"FeeSharingCollector: maxCheckpoint iteration exceeds 32 bits\"\n );\n }\n\n if (rbtcAmountToSend > 0) {\n // send all rbtc withdrawal\n (bool success, ) = _receiver.call.value(rbtcAmountToSend)(\"\");\n require(success, \"FeeSharingCollector::withdrawRBTC: Withdrawal failed\");\n\n emit RBTCWithdrawn(msg.sender, _receiver, rbtcAmountToSend);\n }\n }\n\n /**\n * @dev Function to wrap:\n * 1. regular withdrawal for both rbtc & non-rbtc token\n * 2. skipped checkpoints withdrawal for both rbtc & non-rbtc token\n *\n * @param _nonRbtcTokensRegularWithdraw array of non-rbtc token address with no skipped checkpoints that will be withdrawn\n * @param _rbtcTokensRegularWithdraw array of rbtc token address with no skipped checkpoints that will be withdrawn\n * @param _tokensWithSkippedCheckpoints array of rbtc & non-rbtc TokenWithSkippedCheckpointsWithdraw struct, which has skipped checkpoints that will be withdrawn\n *\n */\n function claimAllCollectedFees(\n address[] calldata _nonRbtcTokensRegularWithdraw,\n address[] calldata _rbtcTokensRegularWithdraw,\n TokenWithSkippedCheckpointsWithdraw[] calldata _tokensWithSkippedCheckpoints,\n uint32 _maxCheckpoints,\n address _receiver\n ) external nonReentrant {\n uint256 totalProcessedCheckpoints;\n\n /** Process normal multiple withdrawal for RBTC based tokens */\n if (_rbtcTokensRegularWithdraw.length > 0) {\n totalProcessedCheckpoints = _withdrawRbtcTokens(\n _rbtcTokensRegularWithdraw,\n _maxCheckpoints,\n _receiver\n );\n _maxCheckpoints = safe32(\n _maxCheckpoints - totalProcessedCheckpoints,\n \"FeeSharingCollector: maxCheckpoint iteration exceeds 32 bits\"\n );\n }\n\n /** Process normal non-rbtc token withdrawal */\n for (uint256 i = 0; i < _nonRbtcTokensRegularWithdraw.length; i++) {\n if (_maxCheckpoints == 0) break;\n uint256 endTokenCheckpoint;\n\n address _nonRbtcTokenAddress = _nonRbtcTokensRegularWithdraw[i];\n\n /** starting checkpoint is the previous processedCheckpoints for token */\n uint256 startingCheckpoint = processedCheckpoints[msg.sender][_nonRbtcTokenAddress];\n\n (, endTokenCheckpoint) = _withdraw(_nonRbtcTokenAddress, _maxCheckpoints, _receiver);\n\n uint256 _previousUsedCheckpoint = endTokenCheckpoint.sub(startingCheckpoint);\n if (startingCheckpoint > 0) {\n _previousUsedCheckpoint.add(1);\n }\n\n _maxCheckpoints = safe32(\n _maxCheckpoints - _previousUsedCheckpoint,\n \"FeeSharingCollector: maxCheckpoint iteration exceeds 32 bits\"\n );\n }\n\n /** Process token with skipped checkpoints withdrawal */\n if (_tokensWithSkippedCheckpoints.length > 0) {\n totalProcessedCheckpoints = _withdrawStartingFromCheckpoints(\n _tokensWithSkippedCheckpoints,\n _maxCheckpoints,\n _receiver\n );\n _maxCheckpoints = safe32(\n _maxCheckpoints - totalProcessedCheckpoints,\n \"FeeSharingCollector: maxCheckpoint iteration exceeds 32 bits\"\n );\n }\n }\n\n function _withdrawStartingFromCheckpoint(\n address _token,\n uint256 _fromCheckpoint,\n uint32 _maxCheckpoints,\n address _receiver\n ) internal returns (uint256 totalAmount, uint256 endTokenCheckpoint) {\n // @dev e.g. _fromCheckpoint == 10 meaning we should set 9 user's processed checkpoints\n // after _withdraw() the user's processedCheckpoints should be 10\n uint256 prevFromCheckpoint = _fromCheckpoint.sub(1);\n if (prevFromCheckpoint > processedCheckpoints[msg.sender][_token]) {\n processedCheckpoints[msg.sender][_token] = prevFromCheckpoint;\n }\n (totalAmount, endTokenCheckpoint) = _withdraw(_token, _maxCheckpoints, _receiver);\n }\n\n function _withdrawRbtcToken(address _token, uint32 _maxCheckpoints)\n internal\n returns (uint256 totalAmount, uint256 endTokenCheckpoint)\n {\n address user = msg.sender;\n\n IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress);\n\n (totalAmount, endTokenCheckpoint) = _getRBTCBalance(_token, user, _maxCheckpoints);\n\n if (totalAmount > 0) {\n processedCheckpoints[user][_token] = endTokenCheckpoint;\n if (_token == address(wrbtcToken)) {\n // unwrap the wrbtc\n wrbtcToken.withdraw(totalAmount);\n } else if (_token == loanTokenWrbtcAddress) {\n // pull out the iWRBTC to rbtc to this feeSharingCollector contract\n /** @dev will use the burned result from IWRBTC to RBTC as return total amount */\n totalAmount = ILoanTokenWRBTC(loanTokenWrbtcAddress).burnToBTC(\n address(this),\n totalAmount,\n false\n );\n }\n }\n }\n\n /**\n * @dev withdraw all of the RBTC balance based on particular checkpoints\n *\n * This function will withdraw RBTC balance which is passed as _token param, so it could be either of these:\n * - rbtc balance or\n * - wrbtc balance which will be unwrapped to rbtc or\n * - iwrbtc balance which will be unwrapped to rbtc or\n *\n *\n * @param _tokens array of either RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT or wrbtc address or iwrbtc address\n * @param _maxCheckpoints Maximum number of checkpoints to be processed to workaround block gas limit\n * @param _receiver An optional tokens receiver (msg.sender used if 0)\n */\n function _withdrawRbtcTokens(\n address[] memory _tokens,\n uint32 _maxCheckpoints,\n address _receiver\n ) internal returns (uint256 totalProcessedCheckpoints) {\n validRBTCBasedTokens(_tokens);\n\n if (_receiver == ZERO_ADDRESS) {\n _receiver = msg.sender;\n }\n\n uint256 rbtcAmountToSend;\n\n for (uint256 i = 0; i < _tokens.length; i++) {\n if (_maxCheckpoints == 0) break;\n address _token = _tokens[i];\n uint256 startingCheckpoint = processedCheckpoints[msg.sender][_token];\n\n (uint256 totalAmount, uint256 endToken) =\n _withdrawRbtcToken(_tokens[i], _maxCheckpoints);\n rbtcAmountToSend = rbtcAmountToSend.add(totalAmount);\n\n uint256 _previousUsedCheckpoint = endToken.sub(startingCheckpoint);\n if (startingCheckpoint > 0) {\n // we only need to add used checkpoint by 1 only if starting checkpoint > 0\n _previousUsedCheckpoint.add(1);\n }\n totalProcessedCheckpoints += _previousUsedCheckpoint;\n _maxCheckpoints = safe32(\n _maxCheckpoints - _previousUsedCheckpoint,\n \"FeeSharingCollector: maxCheckpoint iteration exceeds 32 bits\"\n );\n }\n\n // send all rbtc\n if (rbtcAmountToSend > 0) {\n (bool success, ) = _receiver.call.value(rbtcAmountToSend)(\"\");\n require(success, \"FeeSharingCollector::withdrawRBTC: Withdrawal failed\");\n\n emit RBTCWithdrawn(msg.sender, _receiver, rbtcAmountToSend);\n }\n }\n\n /**\n * @dev Withdraw either specific RBTC related token balance or all RBTC related tokens balances.\n * RBTC related here means, it could be either rbtc, wrbtc, or iwrbtc, depends on the _token param.\n */\n function _withdrawRbtcTokenStartingFromCheckpoint(\n address _token,\n uint256 _fromCheckpoint,\n uint32 _maxCheckpoints,\n address _receiver\n ) private returns (uint256 totalAmount, uint256 endTokenCheckpoint) {\n // @dev e.g. _fromCheckpoint == 10\n // after _withdraw() user's processedCheckpoints should be 10 =>\n // set processed checkpoints = 9, next maping index = 9 (10th checkpoint)\n uint256 prevFromCheckpoint = _fromCheckpoint.sub(1);\n if (prevFromCheckpoint > processedCheckpoints[msg.sender][_token]) {\n processedCheckpoints[msg.sender][_token] = prevFromCheckpoint;\n }\n return _withdrawRbtcToken(_token, _maxCheckpoints);\n }\n\n /**\n * @dev Returns first user's checkpoint with weighted stake > 0\n *\n * @param _user The address of the user or contract.\n * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.\n * @param _startFrom Checkpoint number to start from. If _startFrom < processedUserCheckpoints then starts from processedUserCheckpoints.\n * @param _maxCheckpoints Max checkpoints to process in a row to avoid timeout error\n * @return [checkpointNum: checkpoint number where user's weighted stake > 0, hasSkippedCheckpoints, hasFees]\n */\n function getNextPositiveUserCheckpoint(\n address _user,\n address _token,\n uint256 _startFrom,\n uint256 _maxCheckpoints\n )\n external\n view\n returns (\n uint256 checkpointNum,\n bool hasSkippedCheckpoints,\n bool hasFees\n )\n {\n return _getNextPositiveUserCheckpoint(_user, _token, _startFrom, _maxCheckpoints);\n }\n\n /**\n * @dev Returns first user's checkpoint with weighted stake > 0\n *\n * @param _user The address of the user or contract.\n * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.\n * @param _startFrom Checkpoint number to start from. If _startFrom < processedUserCheckpoints then starts from processedUserCheckpoints.\n * @param _maxCheckpoints Max checkpoints to process in a row to avoid timeout error\n * @return [checkpointNum: checkpoint number where user's weighted stake > 0, hasSkippedCheckpoints, hasFees]\n */\n function _getNextPositiveUserCheckpoint(\n address _user,\n address _token,\n uint256 _startFrom,\n uint256 _maxCheckpoints\n )\n internal\n view\n returns (\n uint256 checkpointNum,\n bool hasSkippedCheckpoints,\n bool hasFees\n )\n {\n if (staking.isVestingContract(_user)) {\n return (0, false, false);\n }\n require(_maxCheckpoints > 0, \"_maxCheckpoints must be > 0\");\n\n uint256 totalCheckpoints = totalTokenCheckpoints[_token];\n uint256 processedUserCheckpoints = processedCheckpoints[_user][_token];\n\n if (processedUserCheckpoints >= totalCheckpoints || totalCheckpoints == 0) {\n return (totalCheckpoints, false, false);\n }\n\n uint256 startFrom =\n _startFrom > processedUserCheckpoints ? _startFrom : processedUserCheckpoints;\n\n uint256 end = startFrom.add(_maxCheckpoints);\n if (end >= totalCheckpoints) {\n end = totalCheckpoints;\n }\n\n // @note here processedUserCheckpoints is a number of processed checkpoints and\n // also an index for the next checkpoint because an array index starts wtih 0\n for (uint256 i = startFrom; i < end; i++) {\n Checkpoint storage tokenCheckpoint = tokenCheckpoints[_token][i];\n uint96 weightedStake =\n staking.getPriorWeightedStake(\n _user,\n tokenCheckpoint.blockNumber - 1,\n tokenCheckpoint.timestamp\n );\n if (weightedStake > 0) {\n // i is the index and we need to return checkpoint num which is i + 1\n return (i + 1, i > processedUserCheckpoints, true);\n }\n }\n return (end, end > processedUserCheckpoints, false);\n }\n\n /**\n * @notice Get the accumulated loan pool fee of the message sender.\n * @param _user The address of the user or contract.\n * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.\n * @return The accumulated fee for the message sender.\n * */\n function getAccumulatedFees(address _user, address _token) public view returns (uint256) {\n uint256 amount;\n (amount, ) = _getAccumulatedFees({\n _user: _user,\n _token: _token,\n _startFrom: 0,\n _maxCheckpoints: 0\n });\n return amount;\n }\n\n /**\n * @notice Get the accumulated fee rewards for the message sender for a checkpoints range\n *\n * @dev This function is required to keep consistent with caching of weighted voting power when claiming fees\n *\n * @param _user The address of a user (staker) or contract.\n * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.\n * @param _startFrom Checkpoint to start calculating fees from.\n * @param _maxCheckpoints maxCheckpoints to get accumulated fees for the _user\n * @return The accumulated fees rewards for the _user in the given checkpoints interval: [_startFrom, _startFrom + maxCheckpoints].\n * */\n function getAccumulatedFeesForCheckpointsRange(\n address _user,\n address _token,\n uint256 _startFrom,\n uint32 _maxCheckpoints\n ) external view returns (uint256) {\n uint256 amount;\n (amount, ) = _getAccumulatedFees(_user, _token, _startFrom, _maxCheckpoints);\n return amount;\n }\n\n /**\n * @dev Get all user fees reward per maxCheckpoint starting from latest processed checkpoint\n *\n * @dev e.g: Total user checkpoint for the particualar token = 300,\n * when we call this function with 50 maxCheckpoint, it will return 6 fee values in array form.\n * if there is no more fees, it will return empty array.\n *\n * @param _user The address of a user (staker) or contract.\n * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.\n * @param _startFrom Checkpoint to start calculating fees from.\n * @param _maxCheckpoints maxCheckpoints to get accumulated fees for the _user\n * @return The next checkpoint num which is the starting point to fetch all of the fees, array of calculated fees.\n * */\n function getAllUserFeesPerMaxCheckpoints(\n address _user,\n address _token,\n uint256 _startFrom,\n uint32 _maxCheckpoints\n ) external view returns (uint256[] memory fees) {\n require(_maxCheckpoints > 0, \"_maxCheckpoints must be > 0\");\n\n uint256 totalCheckpoints = totalTokenCheckpoints[_token];\n uint256 totalTokensCheckpointsIndex = totalCheckpoints > 0 ? totalCheckpoints - 1 : 0;\n\n if (totalTokensCheckpointsIndex < _startFrom) return fees;\n\n uint256 arrSize = totalTokensCheckpointsIndex.sub(_startFrom).div(_maxCheckpoints) + 1;\n\n fees = new uint256[](arrSize);\n\n for (uint256 i = 0; i < fees.length; i++) {\n (uint256 fee, ) =\n _getAccumulatedFees(\n _user,\n _token,\n _startFrom + i * _maxCheckpoints,\n _maxCheckpoints\n );\n fees[i] = fee;\n }\n\n return fees;\n }\n\n /**\n * @notice Gets accumulated fees for a user starting from a given checkpoint\n *\n * @param _user Address of the user's account.\n * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.\n * @param _maxCheckpoints Max checkpoints to process at once to fit into block gas limit\n * @param _startFrom Checkpoint num to start calculations from\n *\n * @return feesAmount - accumulated fees amount\n * @return endCheckpoint - last checkpoint of fees calculation\n * */\n function _getAccumulatedFees(\n address _user,\n address _token,\n uint256 _startFrom,\n uint32 _maxCheckpoints\n ) internal view returns (uint256 feesAmount, uint256 endCheckpoint) {\n if (staking.isVestingContract(_user)) {\n return (0, 0);\n }\n uint256 processedUserCheckpoints = processedCheckpoints[_user][_token];\n uint256 startOfRange =\n _startFrom > processedUserCheckpoints ? _startFrom : processedUserCheckpoints;\n endCheckpoint = _maxCheckpoints > 0\n ? _getEndOfRange(startOfRange, _token, _maxCheckpoints)\n : totalTokenCheckpoints[_token];\n\n if (startOfRange >= totalTokenCheckpoints[_token]) {\n return (0, endCheckpoint);\n }\n\n uint256 cachedLockDate = 0;\n uint96 cachedWeightedStake = 0;\n // @note here processedUserCheckpoints is a number of processed checkpoints and\n // also an index for the next checkpoint because an array index starts wtih 0\n for (uint256 i = startOfRange; i < endCheckpoint; i++) {\n Checkpoint memory checkpoint = tokenCheckpoints[_token][i];\n uint256 lockDate = staking.timestampToLockDate(checkpoint.timestamp);\n uint96 weightedStake;\n if (lockDate == cachedLockDate) {\n weightedStake = cachedWeightedStake;\n } else {\n /// @dev We need to use \"checkpoint.blockNumber - 1\" here to calculate weighted stake\n /// For the same block like we did for total voting power in _writeTokenCheckpoint\n weightedStake = staking.getPriorWeightedStake(\n _user,\n checkpoint.blockNumber - 1,\n checkpoint.timestamp\n );\n cachedWeightedStake = weightedStake;\n cachedLockDate = lockDate;\n }\n uint256 share =\n uint256(checkpoint.numTokens).mul(weightedStake).div(\n uint256(checkpoint.totalWeightedStake)\n );\n feesAmount = feesAmount.add(share);\n }\n return (feesAmount, endCheckpoint);\n }\n\n /**\n * @notice Withdrawal should only be possible for blocks which were already\n * mined. If the fees are withdrawn in the same block as the user withdrawal\n * they are not considered by the withdrawing logic (to avoid inconsistencies).\n *\n * @param _start Start of the range.\n * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of a pool token.\n * @param _maxCheckpoints Checkpoint index incremental.\n * */\n function _getEndOfRange(\n uint256 _start,\n address _token,\n uint32 _maxCheckpoints\n ) internal view returns (uint256) {\n uint256 nextCheckpointIndex = totalTokenCheckpoints[_token];\n if (nextCheckpointIndex == 0) {\n return 0;\n }\n uint256 end;\n\n if (_maxCheckpoints == 0) {\n /// @dev All checkpoints will be processed (only for getter outside of a transaction).\n end = nextCheckpointIndex;\n } else {\n end = safe32(\n _start + _maxCheckpoints,\n \"FeeSharingCollector::withdraw: checkpoint index exceeds 32 bits\"\n );\n if (end > nextCheckpointIndex) {\n end = nextCheckpointIndex;\n }\n }\n\n /// @dev Withdrawal should only be possible for blocks which were already mined.\n uint32 lastBlockNumber = tokenCheckpoints[_token][end - 1].blockNumber;\n if (block.number == lastBlockNumber) {\n end--;\n }\n return end;\n }\n\n /**\n * @notice Write a regular checkpoint w/ the foolowing data:\n * block number, block timestamp, total weighted stake and num of tokens.\n * @param _token The pool token address.\n * @param _numTokens The amount of pool tokens.\n * */\n function _writeTokenCheckpoint(address _token, uint96 _numTokens) internal {\n uint32 blockNumber =\n safe32(\n block.number,\n \"FeeSharingCollector::_writeCheckpoint: block number exceeds 32 bits\"\n );\n uint32 blockTimestamp =\n safe32(\n block.timestamp,\n \"FeeSharingCollector::_writeCheckpoint: block timestamp exceeds 32 bits\"\n );\n uint256 nextCheckpointsIndex = totalTokenCheckpoints[_token];\n\n uint96 totalWeightedStake = _getVoluntaryWeightedStake(blockNumber - 1, block.timestamp);\n require(totalWeightedStake > 0, \"Invalid totalWeightedStake\");\n if (\n nextCheckpointsIndex > 0 &&\n tokenCheckpoints[_token][nextCheckpointsIndex - 1].blockNumber == blockNumber\n ) {\n tokenCheckpoints[_token][nextCheckpointsIndex - 1]\n .totalWeightedStake = totalWeightedStake;\n tokenCheckpoints[_token][nextCheckpointsIndex - 1].numTokens = _numTokens;\n } else {\n tokenCheckpoints[_token][nextCheckpointsIndex] = Checkpoint(\n blockNumber,\n blockTimestamp,\n totalWeightedStake,\n _numTokens\n );\n totalTokenCheckpoints[_token] = nextCheckpointsIndex + 1;\n }\n emit CheckpointAdded(msg.sender, _token, _numTokens);\n }\n\n /**\n * Queries the total weighted stake and the weighted stake of vesting contracts and returns the difference\n * @param blockNumber the blocknumber\n * @param timestamp the timestamp\n */\n function _getVoluntaryWeightedStake(uint32 blockNumber, uint256 timestamp)\n internal\n view\n returns (uint96 totalWeightedStake)\n {\n uint96 vestingWeightedStake = staking.getPriorVestingWeightedStake(blockNumber, timestamp);\n totalWeightedStake = staking.getPriorTotalVotingPower(blockNumber, timestamp);\n totalWeightedStake = sub96(\n totalWeightedStake,\n vestingWeightedStake,\n \"FeeSharingCollector::_getTotalVoluntaryWeightedStake: vested stake exceeds total stake\"\n );\n }\n\n /**\n * @dev Whitelisting converter address.\n *\n * @param converterAddress converter address to be whitelisted.\n */\n function addWhitelistedConverterAddress(address converterAddress) external onlyOwner {\n require(Address.isContract(converterAddress), \"Non contract address given\");\n whitelistedConverterList.add(converterAddress);\n emit WhitelistedConverter(msg.sender, converterAddress);\n }\n\n /**\n * @dev Removing converter address from whitelist.\n *\n * @param converterAddress converter address to be removed from whitelist.\n */\n function removeWhitelistedConverterAddress(address converterAddress) external onlyOwner {\n whitelistedConverterList.remove(converterAddress);\n emit UnwhitelistedConverter(msg.sender, converterAddress);\n }\n\n /**\n * @notice Getter to query all of the whitelisted converter.\n * @return All of the whitelisted converter list.\n */\n function getWhitelistedConverterList() external view returns (address[] memory converterList) {\n converterList = whitelistedConverterList.enumerate();\n }\n\n /**\n * @dev validate array of given address whether is whitelisted or not.\n * @dev if one of them is not whitelisted, then revert.\n *\n * @param converterAddresses array of converter addresses.\n */\n function _validateWhitelistedConverter(address[] memory converterAddresses) private view {\n for (uint256 i = 0; i < converterAddresses.length; i++) {\n require(whitelistedConverterList.contains(converterAddresses[i]), \"Invalid Converter\");\n }\n }\n\n function withdrawWRBTC(address receiver, uint256 wrbtcAmount) external onlyOwner {\n IERC20 wrbtcToken = IERC20(wrbtcTokenAddress);\n\n uint256 balance = wrbtcToken.balanceOf(address(this));\n require(wrbtcAmount <= balance, \"Insufficient balance\");\n\n wrbtcToken.safeTransfer(receiver, wrbtcAmount);\n }\n\n /**\n * @dev This function is dedicated to recover the wrong fee allocation for the 4 year vesting contracts.\n * This function can only be called once\n * The affected tokens to be withdrawn\n * 1. RBTC\n * 2. ZUSD\n * 3. SOV\n * The amount for all of the tokens above is hardcoded\n * The withdrawn tokens will be sent to the owner.\n */\n function recoverIncorrectAllocatedFees()\n external\n oneTimeExecution(this.recoverIncorrectAllocatedFees.selector)\n onlyOwner\n {\n uint256 rbtcAmount = 878778886164898400;\n uint256 zusdAmount = 16658600400155126000000;\n uint256 sovAmount = 6275898259771202000000;\n\n address zusdToken = 0xdB107FA69E33f05180a4C2cE9c2E7CB481645C2d;\n address sovToken = 0xEFc78fc7d48b64958315949279Ba181c2114ABBd;\n\n // Withdraw rbtc\n (bool success, ) = owner().call.value(rbtcAmount)(\"\");\n require(\n success,\n \"FeeSharingCollector::recoverIncorrectAllocatedFees: Withdrawal rbtc failed\"\n );\n\n // Withdraw ZUSD\n IERC20(zusdToken).safeTransfer(owner(), zusdAmount);\n\n // Withdraw SOV\n IERC20(sovToken).safeTransfer(owner(), sovAmount);\n }\n\n /**\n * @dev view function that calculate the total RBTC that includes:\n * - RBTC\n * - WRBTC\n * - iWRBTC * iWRBTC.tokenPrice()\n * @param _user address of the user.\n * @return rbtc balance of the given user's address.\n */\n function getAccumulatedRBTCFeeBalances(address _user) external view returns (uint256) {\n (uint256 _rbtcAmount, uint256 _wrbtcAmount, uint256 _iWrbtcAmount, , , ) =\n _getRBTCBalances(_user, 0);\n uint256 iWRBTCAmountInRBTC =\n _iWrbtcAmount.mul(ILoanTokenWRBTC(loanTokenWrbtcAddress).tokenPrice()).div(1e18);\n return _rbtcAmount.add(_wrbtcAmount).add(iWRBTCAmountInRBTC);\n }\n\n /**\n * @dev private function that responsible to calculate the user's token that has RBTC as underlying token (rbtc, wrbtc, iWrbtc)\n *\n * @param _user address of the user.\n * @param _maxCheckpoints maximum checkpoints.\n *\n * @return _rbtcAmount rbtc amount\n * @return _wrbtcAmount wrbtc amount\n * @return _iWrbtcAmount iWrbtc (wrbtc lending pool token) amount * token price\n * @return _endRBTC end time of accumulated fee calculation for rbtc\n * @return _endWRBTC end time of accumulated fee calculation for wrbtc\n * @return _endIWRBTC end time of accumulated fee calculation for iwrbtc\n */\n function _getRBTCBalances(address _user, uint32 _maxCheckpoints)\n private\n view\n returns (\n uint256 _rbtcAmount,\n uint256 _wrbtcAmount,\n uint256 _iWrbtcAmount,\n uint256 _endRBTC,\n uint256 _endWRBTC,\n uint256 _endIWRBTC\n )\n {\n (_rbtcAmount, _endRBTC) = _getAccumulatedFees({\n _user: _user,\n _token: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,\n _startFrom: 0,\n _maxCheckpoints: _maxCheckpoints\n });\n\n (_wrbtcAmount, _endWRBTC) = _getAccumulatedFees({\n _user: _user,\n _token: wrbtcTokenAddress,\n _startFrom: 0,\n _maxCheckpoints: _maxCheckpoints\n });\n (_iWrbtcAmount, _endIWRBTC) = _getAccumulatedFees({\n _user: _user,\n _token: loanTokenWrbtcAddress,\n _startFrom: 0,\n _maxCheckpoints: _maxCheckpoints\n });\n }\n\n /**\n * @dev private function that responsible to calculate the user's token that has RBTC as underlying token (rbtc, wrbtc, iWrbtc)\n *\n * @param _token either RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT or wrbtc address or iwrbtc address\n * @param _user address of the user.\n * @param _maxCheckpoints maximum checkpoints.\n *\n * @return _tokenAmount token (rbtc, or wrbtc, or iwrbtc) amount\n * @return _endToken end time of accumulated fee calculation for token (rbtc, or wrbtc, or iwrbtc )\n */\n function _getRBTCBalance(\n address _token,\n address _user,\n uint32 _maxCheckpoints\n ) internal view returns (uint256 _tokenAmount, uint256 _endToken) {\n if (\n _token == RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ||\n _token == wrbtcTokenAddress ||\n _token == loanTokenWrbtcAddress\n ) {\n (_tokenAmount, _endToken) = _getAccumulatedFees({\n _user: _user,\n _token: _token,\n _startFrom: 0,\n _maxCheckpoints: _maxCheckpoints\n });\n } else {\n revert(\"FeeSharingCollector::_getRBTCBalance: only rbtc-based tokens are allowed\");\n }\n }\n\n // @todo update dependency `numTokenCheckpoints` -> `totalTokenCheckpoints` and deprecate numTokenCheckpoints function\n /**\n * @dev This getter function `numTokenCheckpoints` is added for backwards compatibility\n * broken when renamed `numTokenCheckpoints` storage variable to `totalTokenCheckpoints`.\n *\n * @param _token token address to get checkpoints for\n *\n * @return Total token checkpoints\n */\n function numTokenCheckpoints(address _token) external view returns (uint256) {\n return totalTokenCheckpoints[_token];\n }\n}\n\n/* Interfaces */\ninterface ILoanToken {\n function mint(address receiver, uint256 depositAmount) external returns (uint256 mintAmount);\n}\n\ninterface ILoanTokenWRBTC {\n function burnToBTC(\n address receiver,\n uint256 burnAmount,\n bool useLM\n ) external returns (uint256 loanAmountPaid);\n\n function tokenPrice() external view returns (uint256 price);\n}\n" + }, + "contracts/governance/FeeSharingCollector/FeeSharingCollectorProxy.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./FeeSharingCollectorStorage.sol\";\nimport \"../../proxy/UpgradableProxy.sol\";\n\n/**\n * @title FeeSharingCollectorProxy contract.\n * @dev FeeSharingCollectorProxy contract should be upgradable, use UpgradableProxy.\n * FeeSharingCollectorStorage is deployed with the upgradable functionality\n * by using this contract instead, that inherits from UpgradableProxy\n * the possibility of being enhanced and re-deployed.\n * */\ncontract FeeSharingCollectorProxy is FeeSharingCollectorStorage, UpgradableProxy {\n /**\n * @notice Construct a new feeSharingCollectorProxy contract.\n * @param _protocol The address of the sovryn protocol.\n * @param _staking The address of the staking\n */\n constructor(IProtocol _protocol, IStaking _staking) public {\n protocol = _protocol;\n staking = _staking;\n }\n}\n" + }, + "contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"../../interfaces/IERC20.sol\";\nimport \"../IFeeSharingCollector.sol\";\nimport \"../Staking/interfaces/IStaking.sol\";\nimport \"../../mixins/EnumerableAddressSet.sol\";\nimport \"../../interfaces/IWrbtcERC20.sol\";\n\n/**\n * @title FeeSharingCollectorStorage contact\n * @notice Just the storage part of FeeSharingCollector contract, and FeeSharingCollectorProxy. No functions,\n * only constant, variables and required structures (mappings)\n * */\ncontract FeeSharingCollectorStorage is Ownable {\n using EnumerableAddressSet for EnumerableAddressSet.AddressSet;\n uint256 constant FEE_WITHDRAWAL_INTERVAL = 172800;\n\n IProtocol public protocol;\n IStaking public staking;\n\n /// @notice Checkpoints by index per pool token address\n mapping(address => mapping(uint256 => Checkpoint)) public tokenCheckpoints;\n\n /// @notice The number of checkpoints for each token address.\n mapping(address => uint256) public totalTokenCheckpoints;\n\n /// @notice\n /// user => token => processed checkpoints\n mapping(address => mapping(address => uint256)) public processedCheckpoints;\n\n /// @notice Last time fees were withdrawn per pool token address:\n /// token => time\n mapping(address => uint256) public lastFeeWithdrawalTime;\n\n /// @notice Amount of tokens that were transferred, but not saved in checkpoints.\n /// token => amount\n mapping(address => uint96) public unprocessedAmount;\n\n struct Checkpoint {\n uint32 blockNumber;\n uint32 timestamp;\n uint96 totalWeightedStake;\n uint96 numTokens;\n }\n\n struct TokenWithSkippedCheckpointsWithdraw {\n address tokenAddress;\n uint256 fromCheckpoint;\n }\n\n /**\n * @dev Add extra modifier (Reentrancy) below.\n * Because we cannot add any additional storage slot before this storage contract after initial deployment\n */\n\n /// @dev Constant for unlocked guard state - non-zero to prevent extra gas costs.\n /// See: https://github.com/OpenZeppelin/openzeppelin-solidity/issues/1056\n uint256 internal constant REENTRANCY_GUARD_FREE = 1;\n\n /// @dev Constant for locked guard state\n uint256 internal constant REENTRANCY_GUARD_LOCKED = 2;\n\n /**\n * @dev We use a single lock for the whole contract.\n */\n uint256 internal reentrancyLock = REENTRANCY_GUARD_FREE;\n\n /**\n * @dev Additional storage for converter whitelist mechanism.\n * @dev Initialization here does not works. We need to create a separate setter & getter.\n * @dev Just set the visibility to internal should be fine.\n */\n EnumerableAddressSet.AddressSet internal whitelistedConverterList;\n\n mapping(bytes4 => bool) public isFunctionExecuted;\n\n /**\n * @dev Wrbtc token address\n */\n address public wrbtcTokenAddress;\n\n /**\n * @dev iWrbtc loan token address\n */\n address public loanTokenWrbtcAddress;\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * If you mark a function `nonReentrant`, you should also\n * mark it `external`. Calling one `nonReentrant` function from\n * another is not supported. Instead, you can implement a\n * `private` function doing the actual work, and an `external`\n * wrapper marked as `nonReentrant`.\n */\n modifier nonReentrant() {\n require(reentrancyLock == REENTRANCY_GUARD_FREE, \"nonReentrant\");\n reentrancyLock = REENTRANCY_GUARD_LOCKED;\n _;\n reentrancyLock = REENTRANCY_GUARD_FREE;\n }\n}\n\n/* Interfaces */\n\ninterface IProtocol {\n /**\n *\n * @param tokens The array address of the token instance.\n * @param receiver The address of the withdrawal recipient.\n *\n * @return The withdrawn total amount in wRBTC\n * */\n function withdrawFees(address[] calldata tokens, address receiver)\n external\n returns (uint256 totalWRBTCWithdrawn);\n\n function underlyingToLoanPool(address token) external view returns (address);\n\n function wrbtcToken() external view returns (IWrbtcERC20);\n\n function getSovTokenAddress() external view returns (address);\n}\n" + }, + "contracts/governance/GovernorAlpha.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./Staking/SafeMath96.sol\";\nimport \"./Timelock.sol\";\nimport \"./Staking/interfaces/IStaking.sol\";\nimport \"../rsk/RSKAddrValidator.sol\";\n\n/**\n * @title Governance Contract.\n * @notice This is an adapted clone of compound’s governance model. In general,\n * the process is the same: Token holders can make (executable) proposals if\n * they possess enough voting power, vote on proposals during a predefined\n * voting period and in the end evaluate the outcome. If successful, the\n * proposal will be scheduled on the timelock contract. Only after sufficient\n * time passed, it can be executed. A minimum voting power is required for\n * making a proposal as well as a minimum quorum.\n *\n * Voting power in the Bitocracy:\n * Stakers will receive voting power in the Bitocracy in return for their\n * staking commitment. This voting power is weighted by how much SOV is staked\n * and for how long the staking period is - staking more SOV over longer staking\n * periods results in higher voting power. With this voting power, users can\n * vote for or against any SIP in bitocracy.sovryn.app.\n * */\ncontract GovernorAlpha is SafeMath96 {\n /* Storage */\n\n /// @notice The name of this contract.\n string public constant NAME = \"Sovryn Governor Alpha\";\n\n /// @notice The maximum number of actions that can be included in a proposal.\n function proposalMaxOperations() public pure returns (uint256) {\n return 10;\n } // 10 actions\n\n /// @notice The delay before voting on a proposal may take place, once proposed.\n function votingDelay() public pure returns (uint256) {\n return 1;\n } // 1 block\n\n /// @notice The duration of voting on a proposal, in blocks.\n function votingPeriod() public pure returns (uint256) {\n return 2880;\n } // ~1 day in blocks (assuming 30s blocks)\n\n /// @notice The address of the Sovryn Protocol Timelock.\n ITimelock public timelock;\n\n /// @notice The address of the Sovryn staking contract.\n IStaking public staking;\n\n /// @notice The address of the Governor Guardian.\n address public guardian;\n\n /// @notice The total number of proposals.\n uint256 public proposalCount;\n\n /// @notice Percentage of current total voting power require to vote.\n uint96 public quorumPercentageVotes;\n\n // @notice Majority percentage.\n uint96 public majorityPercentageVotes;\n\n struct Proposal {\n /// @notice Unique id for looking up a proposal.\n uint256 id;\n /// @notice The block at which voting begins: holders must delegate their votes prior to this block.\n uint32 startBlock;\n /// @notice The block at which voting ends: votes must be cast prior to this block.\n uint32 endBlock;\n /// @notice Current number of votes in favor of this proposal.\n uint96 forVotes;\n /// @notice Current number of votes in opposition to this proposal.\n uint96 againstVotes;\n ///@notice the quorum required for this proposal.\n uint96 quorum;\n ///@notice the majority percentage required for this proposal.\n uint96 majorityPercentage;\n /// @notice The timestamp that the proposal will be available for execution, set once the vote succeeds.\n uint64 eta;\n /// @notice the start time is required for the staking contract.\n uint64 startTime;\n /// @notice Flag marking whether the proposal has been canceled.\n bool canceled;\n /// @notice Flag marking whether the proposal has been executed.\n bool executed;\n /// @notice Creator of the proposal.\n address proposer;\n /// @notice the ordered list of target addresses for calls to be made.\n address[] targets;\n /// @notice The ordered list of values (i.e. msg.value) to be passed to the calls to be made.\n uint256[] values;\n /// @notice The ordered list of function signatures to be called.\n string[] signatures;\n /// @notice The ordered list of calldata to be passed to each call.\n bytes[] calldatas;\n /// @notice Receipts of ballots for the entire set of voters.\n mapping(address => Receipt) receipts;\n }\n\n /// @notice Ballot receipt record for a voter\n struct Receipt {\n /// @notice Whether or not a vote has been cast.\n bool hasVoted;\n /// @notice Whether or not the voter supports the proposal.\n bool support;\n /// @notice The number of votes the voter had, which were cast.\n uint96 votes;\n }\n\n /// @notice Possible states that a proposal may be in.\n enum ProposalState {\n Pending,\n Active,\n Canceled,\n Defeated,\n Succeeded,\n Queued,\n Expired,\n Executed\n }\n\n /// @notice The official record of all proposals ever proposed.\n mapping(uint256 => Proposal) public proposals;\n\n /// @notice The latest proposal for each proposer.\n mapping(address => uint256) public latestProposalIds;\n\n /// @notice The EIP-712 typehash for the contract's domain.\n bytes32 public constant DOMAIN_TYPEHASH =\n keccak256(\"EIP712Domain(string name,uint256 chainId,address verifyingContract)\");\n\n /// @notice The EIP-712 typehash for the ballot struct used by the contract.\n bytes32 public constant BALLOT_TYPEHASH = keccak256(\"Ballot(uint256 proposalId,bool support)\");\n\n /* Events */\n\n /// @notice An event emitted when a new proposal is created.\n event ProposalCreated(\n uint256 id,\n address proposer,\n address[] targets,\n uint256[] values,\n string[] signatures,\n bytes[] calldatas,\n uint256 startBlock,\n uint256 endBlock,\n string description\n );\n\n /// @notice An event emitted when a vote has been cast on a proposal.\n event VoteCast(address voter, uint256 proposalId, bool support, uint256 votes);\n\n /// @notice An event emitted when a proposal has been canceled.\n event ProposalCanceled(uint256 id);\n\n /// @notice An event emitted when a proposal has been queued in the Timelock.\n event ProposalQueued(uint256 id, uint256 eta);\n\n /// @notice An event emitted when a proposal has been executed in the Timelock.\n event ProposalExecuted(uint256 id);\n\n /* Functions */\n\n constructor(\n address timelock_,\n address staking_,\n address guardian_,\n uint96 _quorumPercentageVotes,\n uint96 _majorityPercentageVotes\n ) public {\n timelock = ITimelock(timelock_);\n staking = IStaking(staking_);\n guardian = guardian_;\n quorumPercentageVotes = _quorumPercentageVotes;\n majorityPercentageVotes = _majorityPercentageVotes;\n }\n\n /// @notice The number of votes required in order for a voter to become a proposer.\n function proposalThreshold() public view returns (uint96) {\n uint96 totalVotingPower =\n staking.getPriorTotalVotingPower(\n safe32(\n block.number - 1,\n \"GovernorAlpha::proposalThreshold: block number overflow\"\n ),\n block.timestamp\n );\n // 1% of current total voting power.\n return totalVotingPower / 100;\n }\n\n /// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed.\n function quorumVotes() public view returns (uint96) {\n uint96 totalVotingPower =\n staking.getPriorTotalVotingPower(\n safe32(block.number - 1, \"GovernorAlpha::quorumVotes: block number overflow\"),\n block.timestamp\n );\n // 4% of current total voting power.\n return\n mul96(\n quorumPercentageVotes,\n totalVotingPower,\n \"GovernorAlpha::quorumVotes:multiplication overflow\"\n ) / 100;\n }\n\n /**\n * @notice Create a new proposal.\n * @param targets Array of contract addresses to perform proposal execution.\n * @param values Array of rBTC amounts to send on proposal execution.\n * @param signatures Array of function signatures to call on proposal execution.\n * @param calldatas Array of payloads for the calls on proposal execution.\n * @param description Text describing the purpose of the proposal.\n * */\n function propose(\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas,\n string memory description\n ) public returns (uint256) {\n // note: passing this block's timestamp, but the number of the previous block.\n // todo: think if it would be better to pass block.timestamp - 30 (average block time)\n // (probably not because proposal starts in 1 block from now).\n uint96 threshold = proposalThreshold();\n require(\n staking.getPriorVotes(msg.sender, sub256(block.number, 1), block.timestamp) >\n threshold,\n \"GovernorAlpha::propose: proposer votes below proposal threshold\"\n );\n require(\n targets.length == values.length &&\n targets.length == signatures.length &&\n targets.length == calldatas.length,\n \"GovernorAlpha::propose: proposal function information arity mismatch\"\n );\n require(targets.length != 0, \"GovernorAlpha::propose: must provide actions\");\n require(\n targets.length <= proposalMaxOperations(),\n \"GovernorAlpha::propose: too many actions\"\n );\n\n uint256 latestProposalId = latestProposalIds[msg.sender];\n if (latestProposalId != 0) {\n ProposalState proposersLatestProposalState = state(latestProposalId);\n require(\n proposersLatestProposalState != ProposalState.Active,\n \"GovernorAlpha::propose: one live proposal per proposer, found an already active proposal\"\n );\n require(\n proposersLatestProposalState != ProposalState.Pending,\n \"GovernorAlpha::propose: one live proposal per proposer, found an already pending proposal\"\n );\n }\n\n uint256 startBlock = add256(block.number, votingDelay());\n uint256 endBlock = add256(startBlock, votingPeriod());\n\n proposalCount++;\n\n /// @dev quorum: proposalThreshold is 1% of total votes, we can save gas using this pre calculated value.\n /// @dev startTime: Required by the staking contract. not used by the governance contract itself.\n Proposal memory newProposal =\n Proposal({\n id: proposalCount,\n startBlock: safe32(\n startBlock,\n \"GovernorAlpha::propose: start block number overflow\"\n ),\n endBlock: safe32(endBlock, \"GovernorAlpha::propose: end block number overflow\"),\n forVotes: 0,\n againstVotes: 0,\n quorum: mul96(\n quorumPercentageVotes,\n threshold,\n \"GovernorAlpha::propose: overflow on quorum computation\"\n ),\n majorityPercentage: mul96(\n majorityPercentageVotes,\n threshold,\n \"GovernorAlpha::propose: overflow on majorityPercentage computation\"\n ),\n eta: 0,\n startTime: safe64(block.timestamp, \"GovernorAlpha::propose: startTime overflow\"),\n canceled: false,\n executed: false,\n proposer: msg.sender,\n targets: targets,\n values: values,\n signatures: signatures,\n calldatas: calldatas\n });\n\n proposals[newProposal.id] = newProposal;\n latestProposalIds[newProposal.proposer] = newProposal.id;\n\n emit ProposalCreated(\n newProposal.id,\n msg.sender,\n targets,\n values,\n signatures,\n calldatas,\n startBlock,\n endBlock,\n description\n );\n return newProposal.id;\n }\n\n /**\n * @notice Enqueue a proposal and everyone of its calls.\n * @param proposalId Proposal index to access the list proposals[] from storage.\n * */\n function queue(uint256 proposalId) public {\n require(\n state(proposalId) == ProposalState.Succeeded,\n \"GovernorAlpha::queue: proposal can only be queued if it is succeeded\"\n );\n Proposal storage proposal = proposals[proposalId];\n uint256 eta = add256(block.timestamp, timelock.delay());\n\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n _queueOrRevert(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n eta\n );\n }\n proposal.eta = safe64(eta, \"GovernorAlpha::queue: ETA overflow\");\n emit ProposalQueued(proposalId, eta);\n }\n\n /**\n * @notice Tries to enqueue a proposal, verifying it has not been previously queued.\n * @param target Contract addresses to perform proposal execution.\n * @param value rBTC amount to send on proposal execution.\n * @param signature Function signature to call on proposal execution.\n * @param data Payload for the call on proposal execution.\n * @param eta Estimated Time of Accomplishment. The timestamp that the\n * proposal will be available for execution, set once the vote succeeds.\n * */\n function _queueOrRevert(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) internal {\n require(\n !timelock.queuedTransactions(\n keccak256(abi.encode(target, value, signature, data, eta))\n ),\n \"GovernorAlpha::_queueOrRevert: proposal action already queued at eta\"\n );\n timelock.queueTransaction(target, value, signature, data, eta);\n }\n\n /**\n * @notice Execute a proposal by looping and performing everyone of its calls.\n * @param proposalId Proposal index to access the list proposals[] from storage.\n * */\n function execute(uint256 proposalId) public payable {\n require(\n state(proposalId) == ProposalState.Queued,\n \"GovernorAlpha::execute: proposal can only be executed if it is queued\"\n );\n Proposal storage proposal = proposals[proposalId];\n proposal.executed = true;\n\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n timelock.executeTransaction.value(proposal.values[i])(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n proposal.eta\n );\n }\n emit ProposalExecuted(proposalId);\n }\n\n /**\n * @notice Cancel a proposal by looping and cancelling everyone of its calls.\n * @param proposalId Proposal index to access the list proposals[] from storage.\n * */\n function cancel(uint256 proposalId) public {\n ProposalState state = state(proposalId);\n require(\n state != ProposalState.Executed,\n \"GovernorAlpha::cancel: cannot cancel executed proposal\"\n );\n\n Proposal storage proposal = proposals[proposalId];\n /// @notice Cancel only if sent by the guardian.\n require(msg.sender == guardian, \"GovernorAlpha::cancel: sender isn't a guardian\");\n\n proposal.canceled = true;\n\n for (uint256 i = 0; i < proposal.targets.length; i++) {\n timelock.cancelTransaction(\n proposal.targets[i],\n proposal.values[i],\n proposal.signatures[i],\n proposal.calldatas[i],\n proposal.eta\n );\n }\n\n emit ProposalCanceled(proposalId);\n }\n\n /**\n * @notice Get a proposal list of its calls.\n * @param proposalId Proposal index to access the list proposals[] from storage.\n * @return Arrays of the 4 call parameters: targets, values, signatures, calldatas.\n * */\n function getActions(uint256 proposalId)\n public\n view\n returns (\n address[] memory targets,\n uint256[] memory values,\n string[] memory signatures,\n bytes[] memory calldatas\n )\n {\n Proposal storage p = proposals[proposalId];\n return (p.targets, p.values, p.signatures, p.calldatas);\n }\n\n /**\n * @notice Get a proposal receipt.\n * @param proposalId Proposal index to access the list proposals[] from storage.\n * @param voter A governance stakeholder with voting power.\n * @return The voter receipt of the proposal.\n * */\n function getReceipt(uint256 proposalId, address voter) public view returns (Receipt memory) {\n return proposals[proposalId].receipts[voter];\n }\n\n /**\n * @notice Casts a vote by sender.\n * @param proposalId Proposal index to access the list proposals[] from storage.\n * @param support Vote value, yes or no.\n * */\n function castVote(uint256 proposalId, bool support) public {\n return _castVote(msg.sender, proposalId, support);\n }\n\n /**\n * @notice Voting with EIP-712 Signatures.\n *\n * Voting power can be delegated to any address, and then can be used to\n * vote on proposals. A key benefit to users of by-signature functionality\n * is that they can create a signed vote transaction for free, and have a\n * trusted third-party spend rBTC(or ETH) on gas fees and write it to the\n * blockchain for them.\n *\n * The third party in this scenario, submitting the SOV-holder’s signed\n * transaction holds a voting power that is for only a single proposal.\n * The signatory still holds the power to vote on their own behalf in\n * the proposal if the third party has not yet published the signed\n * transaction that was given to them.\n *\n * @dev The signature needs to be broken up into 3 parameters, known as\n * v, r and s:\n * const r = '0x' + sig.substring(2).substring(0, 64);\n * const s = '0x' + sig.substring(2).substring(64, 128);\n * const v = '0x' + sig.substring(2).substring(128, 130);\n *\n * @param proposalId Proposal index to access the list proposals[] from storage.\n * @param support Vote value, yes or no.\n * @param v The recovery byte of the signature.\n * @param r Half of the ECDSA signature pair.\n * @param s Half of the ECDSA signature pair.\n * */\n function castVoteBySig(\n uint256 proposalId,\n bool support,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) public {\n /**\n * @dev The DOMAIN_SEPARATOR is a hash that uniquely identifies a\n * smart contract. It is built from a string denoting it as an\n * EIP712 Domain, the name of the token contract, the version,\n * the chainId in case it changes, and the address that the\n * contract is deployed at.\n * */\n bytes32 domainSeparator =\n keccak256(\n abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(NAME)), getChainId(), address(this))\n );\n\n /// @dev GovernorAlpha uses BALLOT_TYPEHASH, while Staking uses DELEGATION_TYPEHASH\n bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));\n\n bytes32 digest = keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n address signatory = ecrecover(digest, v, r, s);\n\n /// @dev Verify address is not null and PK is not null either.\n require(\n RSKAddrValidator.checkPKNotZero(signatory),\n \"GovernorAlpha::castVoteBySig: invalid signature\"\n );\n return _castVote(signatory, proposalId, support);\n }\n\n /**\n * @notice Cast a vote, adding it to the total counting.\n * @param voter A governance stakeholder with voting power that is casting the vote.\n * @param proposalId Proposal index to access the list proposals[] from storage.\n * @param support Vote value, yes or no.\n * */\n function _castVote(\n address voter,\n uint256 proposalId,\n bool support\n ) internal {\n require(\n state(proposalId) == ProposalState.Active,\n \"GovernorAlpha::_castVote: voting is closed\"\n );\n Proposal storage proposal = proposals[proposalId];\n Receipt storage receipt = proposal.receipts[voter];\n require(receipt.hasVoted == false, \"GovernorAlpha::_castVote: voter already voted\");\n uint96 votes = staking.getPriorVotes(voter, proposal.startBlock, proposal.startTime);\n\n if (support) {\n proposal.forVotes = add96(\n proposal.forVotes,\n votes,\n \"GovernorAlpha::_castVote: vote overflow\"\n );\n } else {\n proposal.againstVotes = add96(\n proposal.againstVotes,\n votes,\n \"GovernorAlpha::_castVote: vote overflow\"\n );\n }\n\n receipt.hasVoted = true;\n receipt.support = support;\n receipt.votes = votes;\n\n emit VoteCast(voter, proposalId, support, votes);\n }\n\n /// @dev Timelock wrapper w/ sender check.\n function __acceptAdmin() public {\n require(\n msg.sender == guardian,\n \"GovernorAlpha::__acceptAdmin: sender must be gov guardian\"\n );\n timelock.acceptAdmin();\n }\n\n /// @notice Sets guardian address to zero.\n function __abdicate() public {\n require(msg.sender == guardian, \"GovernorAlpha::__abdicate: sender must be gov guardian\");\n guardian = address(0);\n }\n\n /// @dev Timelock wrapper w/ sender check.\n function __queueSetTimelockPendingAdmin(address newPendingAdmin, uint256 eta) public {\n require(\n msg.sender == guardian,\n \"GovernorAlpha::__queueSetTimelockPendingAdmin: sender must be gov guardian\"\n );\n timelock.queueTransaction(\n address(timelock),\n 0,\n \"setPendingAdmin(address)\",\n abi.encode(newPendingAdmin),\n eta\n );\n }\n\n /// @dev Timelock wrapper w/ sender check.\n function __executeSetTimelockPendingAdmin(address newPendingAdmin, uint256 eta) public {\n require(\n msg.sender == guardian,\n \"GovernorAlpha::__executeSetTimelockPendingAdmin: sender must be gov guardian\"\n );\n timelock.executeTransaction(\n address(timelock),\n 0,\n \"setPendingAdmin(address)\",\n abi.encode(newPendingAdmin),\n eta\n );\n }\n\n /**\n * @notice Get a proposal state.\n * @param proposalId Proposal index to access the list proposals[] from storage.\n * @return The state of the proposal: Canceled, Pending, Active, Defeated,\n * Succeeded, Executed, Expired.\n * */\n function state(uint256 proposalId) public view returns (ProposalState) {\n require(\n proposalCount >= proposalId && proposalId > 0,\n \"GovernorAlpha::state: invalid proposal id\"\n );\n Proposal storage proposal = proposals[proposalId];\n\n if (proposal.canceled) {\n return ProposalState.Canceled;\n }\n\n if (block.number <= proposal.startBlock) {\n return ProposalState.Pending;\n }\n\n if (block.number <= proposal.endBlock) {\n return ProposalState.Active;\n }\n\n uint96 totalVotes =\n add96(\n proposal.forVotes,\n proposal.againstVotes,\n \"GovernorAlpha:: state: forVotes + againstVotes > uint96\"\n );\n uint96 totalVotesMajorityPercentage =\n div96(totalVotes, 100, \"GovernorAlpha:: state: division error\");\n totalVotesMajorityPercentage = mul96(\n totalVotesMajorityPercentage,\n majorityPercentageVotes,\n \"GovernorAlpha:: state: totalVotes * majorityPercentage > uint96\"\n );\n if (proposal.forVotes <= totalVotesMajorityPercentage || totalVotes < proposal.quorum) {\n return ProposalState.Defeated;\n }\n\n if (proposal.eta == 0) {\n return ProposalState.Succeeded;\n }\n\n if (proposal.executed) {\n return ProposalState.Executed;\n }\n\n if (block.timestamp >= add256(proposal.eta, timelock.GRACE_PERIOD())) {\n return ProposalState.Expired;\n }\n\n return ProposalState.Queued;\n }\n\n /// @dev TODO: use OpenZeppelin's SafeMath function instead.\n function add256(uint256 a, uint256 b) internal pure returns (uint256) {\n uint256 c = a + b;\n require(c >= a, \"addition overflow\");\n return c;\n }\n\n /// @dev TODO: use OpenZeppelin's SafeMath function instead.\n function sub256(uint256 a, uint256 b) internal pure returns (uint256) {\n require(b <= a, \"subtraction underflow\");\n return a - b;\n }\n\n /**\n * @notice Retrieve CHAIN_ID of the executing chain.\n *\n * Chain identifier (chainID) introduced in EIP-155 protects transaction\n * included into one chain from being included into another chain.\n * Basically, chain identifier is an integer number being used in the\n * processes of signing transactions and verifying transaction signatures.\n *\n * @dev As of version 0.5.12, Solidity includes an assembly function\n * chainid() that provides access to the new CHAINID opcode.\n *\n * TODO: chainId is included in block. So you can get chain id like\n * block timestamp or block number: block.chainid;\n * */\n function getChainId() internal pure returns (uint256) {\n uint256 chainId;\n assembly {\n chainId := chainid()\n }\n return chainId;\n }\n}\n\n/* Interfaces */\n\ninterface TimelockInterface {\n function delay() external view returns (uint256);\n\n function GRACE_PERIOD() external view returns (uint256);\n\n function acceptAdmin() external;\n\n function queuedTransactions(bytes32 hash) external view returns (bool);\n\n function queueTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external returns (bytes32);\n\n function cancelTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external;\n\n function executeTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external payable returns (bytes memory);\n}\n\ninterface StakingInterface {\n function getPriorVotes(\n address account,\n uint256 blockNumber,\n uint256 date\n ) external view returns (uint96);\n\n function getPriorTotalVotingPower(uint32 blockNumber, uint256 time)\n external\n view\n returns (uint96);\n}\n" + }, + "contracts/governance/GovernorVault.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../openzeppelin/Ownable.sol\";\nimport \"../interfaces/IERC20.sol\";\n\n/**\n * @title Governance Vault.\n * @notice This contract stores tokens and rBTC only transfereble by owner,\n * i.e. Sovryn governance.\n * */\ncontract GovernorVault is Ownable {\n /* Events */\n\n event Deposited(address indexed sender, uint256 amount);\n event TokensTransferred(address indexed receiver, address indexed token, uint256 amount);\n event RbtcTransferred(address indexed receiver, uint256 amount);\n\n /* Functions */\n\n /**\n * @notice Transfer tokens.\n * @param _receiver The receiver of tokens.\n * @param _token The address of token contract.\n * @param _amount The amount to be transferred.\n * */\n function transferTokens(\n address _receiver,\n address _token,\n uint256 _amount\n ) public onlyOwner {\n require(_receiver != address(0), \"Invalid receiver address\");\n require(_token != address(0), \"Invalid token address\");\n\n require(IERC20(_token).transfer(_receiver, _amount), \"Transfer failed\");\n emit TokensTransferred(_receiver, _token, _amount);\n }\n\n /**\n * @notice Transfer RBTC.\n * @param _receiver The receiver of RBTC.\n * @param _amount The amount to be transferred.\n * */\n function transferRbtc(address payable _receiver, uint256 _amount) public onlyOwner {\n require(_receiver != address(0), \"Invalid receiver address\");\n\n address(_receiver).transfer(_amount);\n emit RbtcTransferred(_receiver, _amount);\n }\n\n /**\n * @notice Fallback function is to react to receiving value (rBTC).\n * */\n function() external payable {\n if (msg.value > 0) {\n emit Deposited(msg.sender, msg.value);\n }\n }\n}\n" + }, + "contracts/governance/IFeeSharingCollector.sol": { + "content": "pragma solidity ^0.5.17;\n\n/**\n * @title Interface for contract governance/FeeSharingCollector/FeeSharingCollector.sol\n * @dev Interfaces are used to cast a contract address into a callable instance.\n * */\ninterface IFeeSharingCollector {\n function withdrawFees(address[] calldata _token) external;\n\n function transferTokens(address _token, uint96 _amount) external;\n\n function withdraw(\n address _loanPoolToken,\n uint32 _maxCheckpoints,\n address _receiver\n ) external;\n}\n" + }, + "contracts/governance/Staking/interfaces/IStaking.sol": { + "content": "pragma solidity ^0.5.17;\n\npragma experimental ABIEncoderV2;\n\n/**\n * @title Interface for Staking modules governance/Staking/modules\n */\n\ninterface IStaking {\n /*************************** StakingAdminModule ***************************/\n\n /**\n * @notice Add account to Admins ACL.\n * @param _admin The addresses of the account to grant permissions.\n * */\n function addAdmin(address _admin) external;\n\n /**\n * @notice Remove account from Admins ACL.\n * @param _admin The addresses of the account to revoke permissions.\n * */\n function removeAdmin(address _admin) external;\n\n /**\n * @notice Add account to pausers ACL.\n * @param _pauser The address to grant pauser permissions.\n * */\n function addPauser(address _pauser) external;\n\n /**\n * @notice Remove account from pausers ACL.\n * @param _pauser The address to grant pauser permissions.\n * */\n function removePauser(address _pauser) external;\n\n /**\n * @notice Pause/unpause contract\n * @param _pause true when pausing, false when unpausing\n * */\n function pauseUnpause(bool _pause) external;\n\n /**\n * @notice Freeze contract - disable all functions\n * @param _freeze true when freezing, false when unfreezing\n * @dev When freezing, pause is always applied too. When unfreezing, the contract is left in paused stated.\n * */\n function freezeUnfreeze(bool _freeze) external;\n\n /**\n * @notice Allows the owner to set a fee sharing proxy contract.\n * We need it for unstaking with slashing.\n * @param _feeSharing The address of FeeSharingCollectorProxy contract.\n * */\n function setFeeSharing(address _feeSharing) external;\n\n /**\n * @notice Allow the owner to set weight scaling.\n * We need it for unstaking with slashing.\n * @param _weightScaling The weight scaling.\n * */\n function setWeightScaling(uint96 _weightScaling) external;\n\n /**\n * @notice Allow the owner to set a new staking contract.\n * As a consequence it allows the stakers to migrate their positions\n * to the new contract.\n * @dev Doesn't have any influence as long as migrateToNewStakingContract\n * is not implemented.\n * @param _newStakingContract The address of the new staking contract.\n * */\n function setNewStakingContract(address _newStakingContract) external;\n\n /**\n * @notice Allow a staker to migrate his positions to the new staking contract.\n * @dev Staking contract needs to be set before by the owner.\n * Currently not implemented, just needed for the interface.\n * In case it's needed at some point in the future,\n * the implementation needs to be changed first.\n * */\n function migrateToNewStakingContract() external; // dummy - not implemented as of now\n\n /*************************** StakingGovernanceModule ***************************/\n\n /**\n * @notice Compute the total voting power at a given time.\n * @param blockNumber The block number, needed for checkpointing.\n * @param time The timestamp for which to calculate the total voting power.\n * @return The total voting power at the given time.\n * */\n function getPriorTotalVotingPower(uint32 blockNumber, uint256 time)\n external\n view\n returns (uint96);\n\n /**\n * @notice Get the current votes balance for a user account.\n * @param account The address to get votes balance.\n * @dev This is a wrapper to simplify arguments. The actual computation is\n * performed on WeightedStaking parent contract.\n * @return The number of current votes for a user account.\n * */\n function getCurrentVotes(address account) external view returns (uint96);\n\n /**\n * @notice Determine the prior number of votes for a delegatee as of a block number.\n * Iterate through checkpoints adding up voting power.\n * @dev Block number must be a finalized block or else this function will revert\n * to prevent misinformation.\n * Used for Voting, not for fee sharing.\n * @param account The address of the account to check.\n * @param blockNumber The block number to get the vote balance at.\n * @param date The staking date to compute the power for.\n * @return The number of votes the delegatee had as of the given block.\n * */\n function getPriorVotes(\n address account,\n uint256 blockNumber,\n uint256 date\n ) external view returns (uint96);\n\n /**\n * @notice Determine the prior number of stake for an account as of a block number.\n * @dev Block number must be a finalized block or else this function will\n * revert to prevent misinformation.\n * @param account The address of the account to check.\n * @param date The staking date to compute the power for.\n * @param blockNumber The block number to get the vote balance at.\n * @return The number of votes the account had as of the given block.\n * */\n function getPriorStakeByDateForDelegatee(\n address account,\n uint256 date,\n uint256 blockNumber\n ) external view returns (uint96);\n\n /**\n * @notice Determine the prior number of stake for an unlocking date as of a block number.\n * @dev Block number must be a finalized block or else this function will\n * revert to prevent misinformation.\n * TODO: WeightedStaking::getPriorTotalStakesForDate should probably better\n * be internal instead of a public function.\n * @param date The date to check the stakes for.\n * @param blockNumber The block number to get the vote balance at.\n * @return The number of votes the account had as of the given block.\n * */\n function getPriorTotalStakesForDate(uint256 date, uint256 blockNumber)\n external\n view\n returns (uint96);\n\n /**\n * @notice Delegate votes from `msg.sender` which are locked until lockDate to `delegatee`.\n * @param delegatee The address to delegate votes to.\n * @param lockDate the date if the position to delegate.\n * */\n function delegate(address delegatee, uint256 lockDate) external;\n\n /*************************** StakingStakeModule ***************************/\n\n event TokensStaked(\n address indexed staker,\n uint256 amount,\n uint256 lockedUntil,\n uint256 totalStaked\n );\n\n /**\n * @notice Stake the given amount for the given duration of time.\n * @param amount The number of tokens to stake.\n * @param until Timestamp indicating the date until which to stake.\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\n * @param delegatee The address of the delegatee or 0x0 if there is none.\n * */\n function stake(\n uint96 amount,\n uint256 until,\n address stakeFor,\n address delegatee\n ) external;\n\n /**\n * @notice Stake the given amount for the given duration of time.\n * @dev This function will be invoked from receiveApproval\n * @dev SOV.approveAndCall -> this.receiveApproval -> this.stakeWithApproval\n * @param sender The sender of SOV.approveAndCall\n * @param amount The number of tokens to stake.\n * @param until Timestamp indicating the date until which to stake.\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\n * @param delegatee The address of the delegatee or 0x0 if there is none.\n * */\n function stakeWithApproval(\n address sender,\n uint96 amount,\n uint256 until,\n address stakeFor,\n address delegatee\n ) external;\n\n /**\n * @notice Receives approval from SOV token.\n * @param _data The data will be used for low level call.\n */\n function receiveApproval(\n address _sender,\n uint256 _amount,\n address _token,\n bytes calldata _data\n ) external;\n\n /**\n * @notice Extend the staking duration until the specified date.\n * @param previousLock The old unlocking timestamp.\n * @param until The new unlocking timestamp in seconds.\n * */\n function extendStakingDuration(uint256 previousLock, uint256 until) external;\n\n /**\n * @dev DO NOT USE this misspelled function. Use stakeBySchedule function instead.\n * This function cannot be deprecated while we have non-upgradeable vesting contracts.\n * */\n function stakesBySchedule(\n uint256 amount,\n uint256 cliff,\n uint256 duration,\n uint256 intervalLength,\n address stakeFor,\n address delegatee\n ) external;\n\n /**\n * @notice Stake tokens according to the vesting schedule.\n * @param amount The amount of tokens to stake.\n * @param cliff The time interval to the first withdraw.\n * @param duration The staking duration.\n * @param intervalLength The length of each staking interval when cliff passed.\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\n * @param delegatee The address of the delegatee or 0x0 if there is none.\n * */\n function stakeBySchedule(\n uint256 amount,\n uint256 cliff,\n uint256 duration,\n uint256 intervalLength,\n address stakeFor,\n address delegatee\n ) external;\n\n /**\n * @notice Get the number of staked tokens held by the user account.\n * @dev Iterate checkpoints adding up stakes.\n * @param account The address of the account to get the balance of.\n * @return The number of tokens held.\n * */\n function balanceOf(address account) external view returns (uint96 balance);\n\n /**\n * @notice Get the current number of tokens staked for a day.\n * @param lockedTS The timestamp to get the staked tokens for.\n * */\n function getCurrentStakedUntil(uint256 lockedTS) external view returns (uint96);\n\n /**\n * @notice Get list of stakes for a user account.\n * @param account The address to get stakes.\n * @return The arrays of dates and stakes.\n * */\n function getStakes(address account)\n external\n view\n returns (uint256[] memory dates, uint96[] memory stakes);\n\n /**\n * @notice Unstaking is possible every 2 weeks only. This means, to\n * calculate the key value for the staking checkpoints, we need to\n * map the intended timestamp to the closest available date.\n * @param timestamp The unlocking timestamp.\n * @return The actual unlocking date (might be up to 2 weeks shorter than intended).\n * */\n function timestampToLockDate(uint256 timestamp) external view returns (uint256);\n\n /*************************** StakingStorageModule ***************************/\n\n /// @notice The maximum duration to stake tokens\n /// @return MAX_DURATION to stake tokens\n function getStorageMaxDurationToStakeTokens() external pure returns (uint256);\n\n /// @notice The maximum possible voting weight before adding +1 (actually 10, but need 9 for computation).\n /// @return uint256(MAX_VOTING_WEIGHT);\n function getStorageMaxVotingWeight() external pure returns (uint256);\n\n /// @notice weight is multiplied with this factor (for allowing decimals, like 1.2x).\n /// @dev MAX_VOTING_WEIGHT * WEIGHT_FACTOR needs to be < 792, because there are 100,000,000 SOV with 18 decimals\n /// @return uint256(WEIGHT_FACTOR);\n function getStorageWeightFactor() external pure returns (uint256);\n\n /// @return uint256(DEFAULT_WEIGHT_SCALING);\n function getStorageDefaultWeightScaling() external pure returns (uint256);\n\n /// @notice return (uint256(MIN_WEIGHT_SCALING), uint256(MAX_WEIGHT_SCALING))\n function getStorageRangeForWeightScaling()\n external\n pure\n returns (uint256 minWeightScaling, uint256 maxWeightScaling);\n\n /// @notice The EIP-712 typehash for the contract's domain.\n /// @return uint256(DOMAIN_TYPEHASH);\n function getStorageDomainTypehash() external pure returns (uint256);\n\n /// @notice The EIP-712 typehash for the delegation struct used by the contract.\n /// @return uint256(DELEGATION_TYPEHASH);\n function getStorageDelegationTypehash() external pure returns (uint256);\n\n /// @return name;\n function getStorageName() external view returns (string memory);\n\n /// AUTOGENERATED FUNCTIONS FROM THE STAKING STORAGE PUBLIC VARIABLES ///\n\n /// @notice The timestamp of contract creation. Base for the staking period calculation.\n function kickoffTS() external view returns (uint256);\n\n /// @notice The token to be staked\n function SOVToken() external view returns (address);\n\n /// @notice Stakers delegated voting power\n /// @param staker - the delegating address\n /// @param until - delegated voting\n /// @return _delegate - voting power delegated to address\n function delegates(address staker, uint256 until) external view returns (address _delegate);\n\n /// @notice If this flag is set to true, all tokens are unlocked immediately\n /// see function unlockAllTokens() for details\n function allUnlocked() external view returns (bool);\n\n /// @notice Used for stake migrations to a new staking contract with a different storage structure\n function newStakingContract() external view returns (address);\n\n /// CHECKPOINTS\n struct Checkpoint {\n uint32 fromBlock;\n uint96 stake;\n }\n\n /// @notice A record of tokens to be unstaked at a given time in total.\n /// For total voting power computation. Voting weights get adjusted bi-weekly.\n /// @dev totalStakingCheckpoints[date][index] is a checkpoint\n function totalStakingCheckpoints(uint256 date, uint32 index)\n external\n view\n returns (Checkpoint memory);\n\n /// @notice The number of total staking checkpoints for each date.\n /// @dev numTotalStakingCheckpoints[date] is a number.\n function numTotalStakingCheckpoints(uint256 date)\n external\n view\n returns (uint32 checkpointsQty);\n\n /// @notice A record of tokens to be unstaked at a given time which were delegated to a certain address.\n /// For delegatee voting power computation. Voting weights get adjusted bi-weekly.\n /// @dev delegateStakingCheckpoints[delegatee][date][index] is a checkpoint.\n function delegateStakingCheckpoints(\n address delagatee,\n uint256 date,\n uint32 index\n ) external view returns (Checkpoint memory);\n\n /// @notice The number of total staking checkpoints for each date per delegate.\n /// @dev numDelegateStakingCheckpoints[delegatee][date] is a number.\n function numDelegateStakingCheckpoints(address delegatee, uint256 date)\n external\n view\n returns (uint32 checkpointsQty);\n\n /// @notice A record of tokens to be unstaked at a given time which per user address (address -> lockDate -> stake checkpoint)\n /// @dev userStakingCheckpoints[user][date][index] is a checkpoint.\n function userStakingCheckpoints(\n address user,\n uint256 date,\n uint32 index\n ) external view returns (Checkpoint memory);\n\n /// @notice The number of total staking checkpoints for each date per user.\n /// @dev numUserStakingCheckpoints[user][date] is a number\n function numUserStakingCheckpoints(address user, uint256 date)\n external\n view\n returns (uint32 checkpointsQty);\n\n /// @notice A record of states for signing / validating signatures\n /// @dev nonces[user] is a number.\n function nonces(address user) external view returns (uint256 nonce);\n\n /// SLASHING ///\n\n /// @notice the address of FeeSharingCollectorProxy contract, we need it for unstaking with slashing.\n function feeSharing() external view returns (address);\n\n /// @notice used for weight scaling when unstaking with slashing.\n /// @return uint96 DEFAULT_WEIGHT_SCALING\n function weightScaling() external view returns (uint96);\n\n /// @notice List of vesting contracts, tokens for these contracts won't be slashed if unstaked by governance.\n /// @dev vestingWhitelist[contract] is true/false.\n function vestingWhitelist(address isWhitelisted) external view returns (bool);\n\n /// @dev user => flag whether user has admin role.\n /// @dev multisig should be an admin, admin can invoke only governanceWithdrawVesting function,\n /// \tthis function works only with Team Vesting contracts\n function admins(address isAdmin) external view returns (bool);\n\n /// @dev vesting contract code hash => flag whether it's registered code hash\n function vestingCodeHashes(bytes32 vestingLogicCodeHash) external view returns (bool);\n\n /// @notice A record of tokens to be unstaked from vesting contract at a given time (lockDate -> vest checkpoint)\n /// @dev vestingCheckpoints[date][index] is a checkpoint.\n function vestingCheckpoints(uint256 date, uint32 index)\n external\n view\n returns (Checkpoint memory);\n\n /// @notice The number of total vesting checkpoints for each date.\n /// @dev numVestingCheckpoints[date] is a number.\n function numVestingCheckpoints(uint256 date) external view returns (uint32 checkpointsQty);\n\n ///@notice vesting registry contract PROXY address\n function vestingRegistryLogic() external view returns (address);\n\n /// @dev user => flag whether user has pauser role.\n function pausers(address isPauser) external view returns (bool);\n\n /// @dev Staking contract is paused\n function paused() external view returns (bool);\n\n /// @dev Staking contract is frozen\n function frozen() external view returns (bool);\n\n /*************************** StakingVestingModule ***************************/\n\n event VestingStakeSet(uint256 lockedTS, uint96 value);\n\n /**\n * @notice Return flag whether the given address is a registered vesting contract.\n * @param stakerAddress the address to check\n */\n function isVestingContract(address stakerAddress) external view returns (bool);\n\n /**\n * @notice Remove vesting contract's code hash to a map of code hashes.\n * @param vesting The address of Vesting contract.\n * @dev We need it to use isVestingContract() function instead of isContract()\n */\n function removeContractCodeHash(address vesting) external;\n\n /**\n * @notice Add vesting contract's code hash to a map of code hashes.\n * @param vesting The address of Vesting contract.\n * @dev We need it to use isVestingContract() function instead of isContract()\n */\n function addContractCodeHash(address vesting) external;\n\n /**\n * @notice Determine the prior number of vested stake for an account until a\n * certain lock date as of a block number.\n * @dev Block number must be a finalized block or else this function\n * will revert to prevent misinformation.\n * @param date The lock date.\n * @param blockNumber The block number to get the vote balance at.\n * @return The number of votes the account had as of the given block.\n * */\n function getPriorVestingStakeByDate(uint256 date, uint256 blockNumber)\n external\n view\n returns (uint96);\n\n /**\n * @notice Compute the voting power for a specific date.\n * Power = stake * weight\n * @param date The staking date to compute the power for. Adjusted to the next valid lock date, if necessary.\n * @param startDate The date for which we need to know the power of the stake.\n * @param blockNumber The block number, needed for checkpointing.\n * @return The stacking power.\n * */\n function weightedVestingStakeByDate(\n uint256 date,\n uint256 startDate,\n uint256 blockNumber\n ) external view returns (uint96 power);\n\n /**\n * @notice Determine the prior weighted vested amount for an account as of a block number.\n * Iterate through checkpoints adding up voting power.\n * @dev Block number must be a finalized block or else this function will\n * revert to prevent misinformation.\n * Used for fee sharing, not voting.\n * TODO: WeightedStaking::getPriorVestingWeightedStake is using the variable name \"votes\"\n * to add up token stake, and that could be misleading.\n *\n * @param blockNumber The block number to get the vote balance at.\n * @param date The staking date to compute the power for.\n * @return The weighted stake the account had as of the given block.\n * */\n function getPriorVestingWeightedStake(uint256 blockNumber, uint256 date)\n external\n view\n returns (uint96 votes);\n\n /**\n * @notice Determine the prior number of stake for an account until a\n * certain lock date as of a block number.\n * @dev Block number must be a finalized block or else this function\n * will revert to prevent misinformation.\n * @param account The address of the account to check.\n * @param date The lock date.\n * @param blockNumber The block number to get the vote balance at.\n * @return The number of votes the account had as of the given block.\n * */\n function getPriorUserStakeByDate(\n address account,\n uint256 date,\n uint256 blockNumber\n ) external view returns (uint96);\n\n /**\n * @notice Sets the users' vesting stakes for a giving lock dates and writes checkpoints.\n * @param lockedDates The arrays of lock dates.\n * @param values The array of values to add to the staked balance.\n */\n function setVestingStakes(uint256[] calldata lockedDates, uint96[] calldata values) external;\n\n /**\n * @notice sets vesting registry\n * @param _vestingRegistryProxy the address of vesting registry proxy contract\n * @dev _vestingRegistryProxy can be set to 0 as this function can be reused by\n * various other functionalities without the necessity of linking it with Vesting Registry\n */\n function setVestingRegistry(address _vestingRegistryProxy) external;\n\n /*************************** StakingWithdrawModule ***************************/\n\n /**\n * @notice Withdraw the given amount of tokens if they are unlocked.\n * @param amount The number of tokens to withdraw.\n * @param until The date until which the tokens were staked.\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\n * */\n function withdraw(\n uint96 amount,\n uint256 until,\n address receiver\n ) external;\n\n /**\n * @notice Withdraw the given amount of tokens.\n * @param amount The number of tokens to withdraw.\n * @param until The date until which the tokens were staked.\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\n * @dev Can be invoked only by whitelisted contract passed to governanceWithdrawVesting\n * @dev **WARNING** This function should not be no longer used by Sovryn Protocol.\n * Sovryn protocol will use the cancelTeamVesting function for the withdrawal moving forward.\n * */\n function governanceWithdraw(\n uint96 amount,\n uint256 until,\n address receiver\n ) external;\n\n /**\n * @notice Withdraw tokens for vesting contract.\n * @param vesting The address of Vesting contract.\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\n * @dev Can be invoked only by whitelisted contract passed to governanceWithdrawVesting.\n * */\n function governanceWithdrawVesting(address vesting, address receiver) external;\n\n /**\n * @notice Get available and punished amount for withdrawing.\n * @param amount The number of tokens to withdraw.\n * @param until The date until which the tokens were staked.\n * */\n function getWithdrawAmounts(uint96 amount, uint256 until)\n external\n view\n returns (uint96, uint96);\n\n /**\n * @notice Allow the owner to unlock all tokens in case the staking contract\n * is going to be replaced\n * Note: Not reversible on purpose. once unlocked, everything is unlocked.\n * The owner should not be able to just quickly unlock to withdraw his own\n * tokens and lock again.\n * @dev Last resort.\n * */\n function unlockAllTokens() external;\n\n /*************************** WeightedStakingModule ***************************/\n\n /**\n * @notice Determine the prior weighted stake for an account as of a block number.\n * Iterate through checkpoints adding up voting power.\n * @dev Block number must be a finalized block or else this function will\n * revert to prevent misinformation.\n * Used for fee sharing, not voting.\n *\n * @param account The address of the account to check.\n * @param blockNumber The block number to get the vote balance at.\n * @param date The date/timestamp of the unstaking time.\n * @return The weighted stake the account had as of the given block.\n * */\n function getPriorWeightedStake(\n address account,\n uint256 blockNumber,\n uint256 date\n ) external view returns (uint96 priorWeightedStake);\n\n /**\n * @notice Compute the voting power for a specific date.\n * Power = stake * weight\n * TODO: WeightedStaking::weightedStakeByDate should probably better\n * be internal instead of a public function.\n * @param account The user address.\n * @param date The staking date to compute the power for.\n * @param startDate The date for which we need to know the power of the stake.\n * @param blockNumber The block number, needed for checkpointing.\n * @return The stacking power.\n * */\n function weightedStakeByDate(\n address account,\n uint256 date,\n uint256 startDate,\n uint256 blockNumber\n ) external view returns (uint96 power);\n\n /**\n * @notice Compute the weight for a specific date.\n * @param date The unlocking date.\n * @param startDate We compute the weight for the tokens staked until 'date' on 'startDate'.\n * @return The weighted stake the account had as of the given block.\n * */\n function computeWeightByDate(uint256 date, uint256 startDate)\n external\n pure\n returns (uint96 weight);\n\n /**\n * @notice Returns public constant MAX_DURATION\n * preserved for backwards compatibility\n * Use getStorageMaxDurationToStakeTokens()\n * @return uint96 MAX_DURATION for staking\n **/\n function MAX_DURATION() external view returns (uint256);\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() external view returns (address);\n\n /**\n * @dev Returns true if the caller is the current owner.\n */\n function isOwner() external view returns (bool);\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) external;\n\n /**\n * @notice Governance withdraw vesting directly through staking contract.\n * This direct withdraw vesting solves the out of gas issue when there are too many iterations when withdrawing.\n * This function only allows cancelling vesting contract of the TeamVesting type.\n *\n * @param vesting The vesting address.\n * @param receiver The receiving address.\n * @param startFrom The start value for the iterations.\n */\n function cancelTeamVesting(\n address vesting,\n address receiver,\n uint256 startFrom\n ) external;\n\n /**\n * @notice Max iteration for direct withdrawal from staking to prevent out of gas issue.\n *\n * @return max iteration value.\n */\n function getMaxVestingWithdrawIterations() external view returns (uint256);\n\n /**\n * @dev set max withdraw iterations.\n *\n * @param maxIterations new max iterations value.\n */\n function setMaxVestingWithdrawIterations(uint256 maxIterations) external;\n}\n" + }, + "contracts/governance/Staking/modules/shared/CheckpointsShared.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./StakingStorageShared.sol\";\nimport \"../../SafeMath96.sol\";\n\n/**\n * @title Checkpoints contract.\n * @notice Increases and decreases storage values for users, delegatees and\n * total daily stake.\n * */\ncontract CheckpointsShared is StakingStorageShared, SafeMath96 {\n /// @notice An event emitted when an account changes its delegate.\n event DelegateChanged(\n address indexed delegator,\n uint256 lockedUntil,\n address indexed fromDelegate,\n address indexed toDelegate\n );\n\n /// @notice An event emitted when a delegate account's stake balance changes.\n event DelegateStakeChanged(\n address indexed delegate,\n uint256 lockedUntil,\n uint256 previousBalance,\n uint256 newBalance\n );\n\n /// @notice An event emitted when tokens get staked.\n event TokensStaked(\n address indexed staker,\n uint256 amount,\n uint256 lockedUntil,\n uint256 totalStaked\n );\n\n /// @notice An event emitted when staked tokens get withdrawn.\n event StakingWithdrawn(\n address indexed staker,\n uint256 amount,\n uint256 until,\n address indexed receiver,\n bool isGovernance\n );\n\n /// @notice An event emitted when vesting tokens get withdrawn.\n event VestingTokensWithdrawn(address vesting, address receiver);\n\n /// @notice An event emitted when the owner unlocks all tokens.\n event TokensUnlocked(uint256 amount);\n\n /// @notice An event emitted when a staking period gets extended.\n event ExtendedStakingDuration(\n address indexed staker,\n uint256 previousDate,\n uint256 newDate,\n uint256 amountStaked\n );\n\n event AdminAdded(address admin);\n\n event AdminRemoved(address admin);\n\n /// @param pauser address to grant power to pause the contract\n /// @param added true - added, false - removed\n event PauserAddedOrRemoved(address indexed pauser, bool indexed added);\n\n /// @notice An event emitted when a staking is paused or unpaused\n /// @param setPaused true - pause, false - unpause\n event StakingPaused(bool indexed setPaused);\n\n /// @notice An event emitted when a staking is frozen or unfrozen\n /// @param setFrozen true - freeze, false - unfreeze\n event StakingFrozen(bool indexed setFrozen);\n\n event ContractCodeHashAdded(bytes32 hash);\n\n event ContractCodeHashRemoved(bytes32 hash);\n\n event VestingStakeSet(uint256 lockedTS, uint96 value);\n\n event TeamVestingCancelled(address indexed caller, address receiver);\n\n event TeamVestingPartiallyCancelled(\n address indexed caller,\n address receiver,\n uint256 lastProcessedDate\n );\n\n constructor() internal {\n // abstract\n }\n\n /**\n * @notice Increases the user's vesting stake for a giving lock date and writes a checkpoint.\n * @param lockedTS The lock date.\n * @param value The value to add to the staked balance.\n * */\n function _increaseVestingStake(uint256 lockedTS, uint96 value) internal {\n uint32 nCheckpoints = numVestingCheckpoints[lockedTS];\n uint96 vested = vestingCheckpoints[lockedTS][nCheckpoints - 1].stake;\n uint96 newVest = add96(vested, value, \"CP01\"); // vested overflow\n _writeVestingCheckpoint(lockedTS, nCheckpoints, newVest);\n }\n\n /**\n * @notice Decreases the user's vesting stake for a giving lock date and writes a checkpoint.\n * @param lockedTS The lock date.\n * @param value The value to substract to the staked balance.\n * */\n function _decreaseVestingStake(uint256 lockedTS, uint96 value) internal {\n uint32 nCheckpoints = numVestingCheckpoints[lockedTS];\n uint96 vested = vestingCheckpoints[lockedTS][nCheckpoints - 1].stake;\n uint96 newVest = sub96(vested, value, \"CP02\"); // vested underflow\n _writeVestingCheckpoint(lockedTS, nCheckpoints, newVest);\n }\n\n /**\n * @notice Writes on storage the user vested amount.\n * @param lockedTS The lock date.\n * @param nCheckpoints The number of checkpoints, to find out the last one index.\n * @param newVest The new vest balance.\n * */\n function _writeVestingCheckpoint(\n uint256 lockedTS,\n uint32 nCheckpoints,\n uint96 newVest\n ) internal {\n uint32 blockNumber = safe32(block.number, \"CP03\"); // block num > 32 bits\n\n if (\n nCheckpoints > 0 &&\n vestingCheckpoints[lockedTS][nCheckpoints - 1].fromBlock == blockNumber\n ) {\n vestingCheckpoints[lockedTS][nCheckpoints - 1].stake = newVest;\n } else {\n vestingCheckpoints[lockedTS][nCheckpoints] = Checkpoint(blockNumber, newVest);\n numVestingCheckpoints[lockedTS] = nCheckpoints + 1;\n }\n }\n\n /**\n * @notice Increases the user's stake for a giving lock date and writes a checkpoint.\n * @param account The user address.\n * @param lockedTS The lock date.\n * @param value The value to add to the staked balance.\n * */\n function _increaseUserStake(\n address account,\n uint256 lockedTS,\n uint96 value\n ) internal {\n uint32 nCheckpoints = numUserStakingCheckpoints[account][lockedTS];\n uint96 staked = userStakingCheckpoints[account][lockedTS][nCheckpoints - 1].stake;\n uint96 newStake = add96(staked, value, \"CP04\"); // staked overflow\n _writeUserCheckpoint(account, lockedTS, nCheckpoints, newStake);\n }\n\n /**\n * @notice Decreases the user's stake for a giving lock date and writes a checkpoint.\n * @param account The user address.\n * @param lockedTS The lock date.\n * @param value The value to substract to the staked balance.\n * */\n function _decreaseUserStake(\n address account,\n uint256 lockedTS,\n uint96 value\n ) internal {\n uint32 nCheckpoints = numUserStakingCheckpoints[account][lockedTS];\n uint96 staked = userStakingCheckpoints[account][lockedTS][nCheckpoints - 1].stake;\n uint96 newStake = sub96(staked, value, \"CP05\"); // staked underflow\n _writeUserCheckpoint(account, lockedTS, nCheckpoints, newStake);\n }\n\n /**\n * @notice Writes on storage the user stake.\n * @param account The user address.\n * @param lockedTS The lock date.\n * @param nCheckpoints The number of checkpoints, to find out the last one index.\n * @param newStake The new staked balance.\n * */\n function _writeUserCheckpoint(\n address account,\n uint256 lockedTS,\n uint32 nCheckpoints,\n uint96 newStake\n ) internal {\n uint32 blockNumber = safe32(block.number, \"CP06\"); // block number > 32 bits\n\n if (\n nCheckpoints > 0 &&\n userStakingCheckpoints[account][lockedTS][nCheckpoints - 1].fromBlock == blockNumber\n ) {\n userStakingCheckpoints[account][lockedTS][nCheckpoints - 1].stake = newStake;\n } else {\n userStakingCheckpoints[account][lockedTS][nCheckpoints] = Checkpoint(\n blockNumber,\n newStake\n );\n numUserStakingCheckpoints[account][lockedTS] = nCheckpoints + 1;\n }\n }\n\n /**\n * @notice Increases the delegatee's stake for a giving lock date and writes a checkpoint.\n * @param delegatee The delegatee address.\n * @param lockedTS The lock date.\n * @param value The value to add to the staked balance.\n * */\n function _increaseDelegateStake(\n address delegatee,\n uint256 lockedTS,\n uint96 value\n ) internal {\n uint32 nCheckpoints = numDelegateStakingCheckpoints[delegatee][lockedTS];\n uint96 staked = delegateStakingCheckpoints[delegatee][lockedTS][nCheckpoints - 1].stake;\n uint96 newStake = add96(staked, value, \"CP07\"); // block number > 32 bits\n _writeDelegateCheckpoint(delegatee, lockedTS, nCheckpoints, newStake);\n }\n\n /**\n * @notice Decreases the delegatee's stake for a giving lock date and writes a checkpoint.\n * @param delegatee The delegatee address.\n * @param lockedTS The lock date.\n * @param value The value to substract to the staked balance.\n * */\n function _decreaseDelegateStake(\n address delegatee,\n uint256 lockedTS,\n uint96 value\n ) internal {\n uint32 nCheckpoints = numDelegateStakingCheckpoints[delegatee][lockedTS];\n uint96 staked = delegateStakingCheckpoints[delegatee][lockedTS][nCheckpoints - 1].stake;\n uint96 newStake = 0;\n // @dev We need to check delegate checkpoint value here,\n //\t\tbecause we had an issue in `stake` function:\n //\t\tdelegate checkpoint wasn't updating for the second and next stakes for the same date\n //\t\tif first stake was withdrawn completely and stake was delegated to the staker\n //\t\t(no delegation to another address).\n // @dev It can be greater than 0, but inconsistent after 3 transactions\n if (staked > value) {\n newStake = sub96(staked, value, \"CP08\"); // staked underflow\n }\n _writeDelegateCheckpoint(delegatee, lockedTS, nCheckpoints, newStake);\n }\n\n /**\n * @notice Writes on storage the delegate stake.\n * @param delegatee The delegate address.\n * @param lockedTS The lock date.\n * @param nCheckpoints The number of checkpoints, to find out the last one index.\n * @param newStake The new staked balance.\n * */\n function _writeDelegateCheckpoint(\n address delegatee,\n uint256 lockedTS,\n uint32 nCheckpoints,\n uint96 newStake\n ) internal {\n uint32 blockNumber = safe32(block.number, \"CP09\"); // block numb > 32 bits\n uint96 oldStake = delegateStakingCheckpoints[delegatee][lockedTS][nCheckpoints - 1].stake;\n\n if (\n nCheckpoints > 0 &&\n delegateStakingCheckpoints[delegatee][lockedTS][nCheckpoints - 1].fromBlock ==\n blockNumber\n ) {\n delegateStakingCheckpoints[delegatee][lockedTS][nCheckpoints - 1].stake = newStake;\n } else {\n delegateStakingCheckpoints[delegatee][lockedTS][nCheckpoints] = Checkpoint(\n blockNumber,\n newStake\n );\n numDelegateStakingCheckpoints[delegatee][lockedTS] = nCheckpoints + 1;\n }\n emit DelegateStakeChanged(delegatee, lockedTS, oldStake, newStake);\n }\n\n /**\n * @notice Increases the total stake for a giving lock date and writes a checkpoint.\n * @param lockedTS The lock date.\n * @param value The value to add to the staked balance.\n * */\n function _increaseDailyStake(uint256 lockedTS, uint96 value) internal {\n uint32 nCheckpoints = numTotalStakingCheckpoints[lockedTS];\n uint96 staked = totalStakingCheckpoints[lockedTS][nCheckpoints - 1].stake;\n uint96 newStake = add96(staked, value, \"CP10\"); // staked overflow\n _writeStakingCheckpoint(lockedTS, nCheckpoints, newStake);\n }\n\n /**\n * @notice Decreases the total stake for a giving lock date and writes a checkpoint.\n * @param lockedTS The lock date.\n * @param value The value to substract to the staked balance.\n * */\n function _decreaseDailyStake(uint256 lockedTS, uint96 value) internal {\n uint32 nCheckpoints = numTotalStakingCheckpoints[lockedTS];\n uint96 staked = totalStakingCheckpoints[lockedTS][nCheckpoints - 1].stake;\n uint96 newStake = sub96(staked, value, \"CP11\"); // staked underflow\n _writeStakingCheckpoint(lockedTS, nCheckpoints, newStake);\n }\n\n /**\n * @notice Writes on storage the total stake.\n * @param lockedTS The lock date.\n * @param nCheckpoints The number of checkpoints, to find out the last one index.\n * @param newStake The new staked balance.\n * */\n function _writeStakingCheckpoint(\n uint256 lockedTS,\n uint32 nCheckpoints,\n uint96 newStake\n ) internal {\n uint32 blockNumber = safe32(block.number, \"CP12\"); // block num > 32 bits\n\n if (\n nCheckpoints > 0 &&\n totalStakingCheckpoints[lockedTS][nCheckpoints - 1].fromBlock == blockNumber\n ) {\n totalStakingCheckpoints[lockedTS][nCheckpoints - 1].stake = newStake;\n } else {\n totalStakingCheckpoints[lockedTS][nCheckpoints] = Checkpoint(blockNumber, newStake);\n numTotalStakingCheckpoints[lockedTS] = nCheckpoints + 1;\n }\n }\n\n /**\n * @notice Get the current balance of an account locked until a certain date.\n * @param account The user address.\n * @param lockDate The lock date.\n * @return The stake amount.\n * */\n function _currentBalance(address account, uint256 lockDate) internal view returns (uint96) {\n uint32 _numUnserStakingCheckpoints = numUserStakingCheckpoints[account][lockDate] - 1;\n return userStakingCheckpoints[account][lockDate][_numUnserStakingCheckpoints].stake;\n }\n}\n" + }, + "contracts/governance/Staking/modules/shared/StakingShared.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./StakingStorageShared.sol\";\nimport \"../../SafeMath96.sol\";\nimport \"../../../../openzeppelin/SafeMath.sol\";\nimport \"../../../../openzeppelin/Ownable.sol\";\n\n/**\n * @title Staking modules shared functionality\n */\ncontract StakingShared is StakingStorageShared, SafeMath96 {\n using SafeMath for uint256;\n\n uint256 internal constant FOUR_WEEKS = 4 weeks;\n\n /**\n * @dev Throws if paused.\n */\n modifier whenNotPaused() {\n require(!paused, \"paused\"); // SS03\n _;\n }\n\n /**\n * @dev Throws if called by any account other than the owner or admin.\n */\n modifier onlyAuthorized() {\n require(isOwner() || admins[msg.sender], \"unauthorized\"); // SS01\n _;\n }\n\n /**\n\t * @dev Throws if called by any account other than the owner or admin or pauser.\n\t \n\tmodifier onlyAuthorizedOrPauser() {\n\t\trequire(isOwner() || admins[msg.sender] || pausers[msg.sender], \"unauthorized\"); // WS02\n\t\t_;\n\t}\n\t*/\n\n /**\n * @dev Throws if called by any account other than the owner or pauser.\n */\n modifier onlyPauserOrOwner() {\n require(isOwner() || pausers[msg.sender], \"unauthorized\"); // SS02\n _;\n }\n\n /**\n * @dev Throws if called by any account other than pauser.\n * @notice Uncomment when needed\n */\n /*\n\tmodifier onlyPauser() {\n\t\trequire(pausers[msg.sender], \"Not pauser\");\n\t\t_;\n\t}\n\t*/\n\n /**\n * @dev Throws if frozen.\n */\n modifier whenNotFrozen() {\n require(!frozen, \"paused\"); // SS04\n _;\n }\n\n constructor() internal {\n // abstract\n }\n\n function _notSameBlockAsStakingCheckpoint(uint256 lockDate, address stakeFor) internal view {\n uint32 nCheckpoints = numUserStakingCheckpoints[stakeFor][lockDate];\n bool notSameBlock =\n userStakingCheckpoints[stakeFor][lockDate][nCheckpoints - 1].fromBlock != block.number;\n require(notSameBlock, \"cannot be mined in the same block as last stake\"); // S20\n }\n\n /**\n * @notice Unstaking is possible every 2 weeks only. This means, to\n * calculate the key value for the staking checkpoints, we need to\n * map the intended timestamp to the closest available date.\n * @param timestamp The unlocking timestamp.\n * @return The actual unlocking date (might be up to 2 weeks shorter than intended).\n * */\n function _timestampToLockDate(uint256 timestamp) internal view returns (uint256 lockDate) {\n // Optimize gas costs by reading kickoffTS from storage only once.\n uint256 start = kickoffTS;\n require(timestamp >= start, \"timestamp < contract creation\"); // WS23\n /**\n * @dev If staking timestamp does not match any of the unstaking dates\n * , set the lockDate to the closest one before the timestamp.\n * E.g. Passed timestamps lies 7 weeks after kickoff -> only stake for 6 weeks.\n * */\n uint256 periodFromKickoff = (timestamp - start) / TWO_WEEKS;\n lockDate = periodFromKickoff * TWO_WEEKS + start;\n }\n\n /**\n * @notice Determine the current Block Number\n * @dev This is segregated from the _getPriorUserStakeByDate function to better test\n * advancing blocks functionality using Mock Contracts\n * */\n function _getCurrentBlockNumber() internal view returns (uint256) {\n return block.number;\n }\n\n /**\n * @notice Determine the prior number of stake for an account until a\n * \t\tcertain lock date as of a block number.\n * @dev All functions of Staking contract use this internal version,\n * \t\twe need to modify public function in order to workaround issue with Vesting.withdrawTokens:\n * return 1 instead of 0 if message sender is a contract.\n * @param account The address of the account to check.\n * @param date The lock date. Adjusted to the next valid lock date, if necessary.\n * @param blockNumber The block number to get the vote balance at.\n * @return The number of votes the account had as of the given block.\n * */\n function _getPriorUserStakeByDate(\n address account,\n uint256 date,\n uint256 blockNumber\n ) internal view returns (uint96) {\n require(blockNumber < _getCurrentBlockNumber(), \"not determined\"); // WS14\n\n date = _adjustDateForOrigin(date);\n uint32 nCheckpoints = numUserStakingCheckpoints[account][date];\n if (nCheckpoints == 0) {\n return 0;\n }\n\n /// @dev First check most recent balance.\n if (userStakingCheckpoints[account][date][nCheckpoints - 1].fromBlock <= blockNumber) {\n return userStakingCheckpoints[account][date][nCheckpoints - 1].stake;\n }\n\n /// @dev Next check implicit zero balance.\n if (userStakingCheckpoints[account][date][0].fromBlock > blockNumber) {\n return 0;\n }\n\n uint32 lower = 0;\n uint32 upper = nCheckpoints - 1;\n while (upper > lower) {\n uint32 center = upper - (upper - lower) / 2; /// @dev ceil, avoiding overflow.\n Checkpoint memory cp = userStakingCheckpoints[account][date][center];\n if (cp.fromBlock == blockNumber) {\n return cp.stake;\n } else if (cp.fromBlock < blockNumber) {\n lower = center;\n } else {\n upper = center - 1;\n }\n }\n return userStakingCheckpoints[account][date][lower].stake;\n }\n\n /**\n * @dev origin vesting contracts have different dates\n * we need to add 2 weeks to get end of period (by default, it's start)\n * @param date The staking date to compute the power for.\n * @return unlocking date.\n */\n function _adjustDateForOrigin(uint256 date) internal view returns (uint256) {\n uint256 adjustedDate = _timestampToLockDate(date);\n //origin vesting contracts have different dates\n //we need to add 2 weeks to get end of period (by default, it's start)\n if (adjustedDate != date) {\n date = adjustedDate + TWO_WEEKS;\n }\n return date;\n }\n\n /**\n * @notice Compute the weight for a specific date.\n * @param date The unlocking date.\n * @param startDate We compute the weight for the tokens staked until 'date' on 'startDate'.\n * @return The weighted stake the account had as of the given block.\n * */\n function _computeWeightByDate(uint256 date, uint256 startDate)\n internal\n pure\n returns (uint96 weight)\n {\n require(date >= startDate, \"date < startDate\"); // WS18\n uint256 remainingTime = (date - startDate);\n require(MAX_DURATION >= remainingTime, \"remaining time > max duration\"); // WS19\n /// @dev x = max days - remaining days\n uint96 x = uint96(MAX_DURATION - remainingTime) / (1 days);\n /// @dev w = (m^2 - x^2)/m^2 +1 (multiplied by the weight factor)\n weight = add96(\n WEIGHT_FACTOR,\n mul96(\n MAX_VOTING_WEIGHT * WEIGHT_FACTOR,\n sub96(\n MAX_DURATION_POW_2,\n x * x,\n \"weight underflow\" // WS20\n ),\n \"weight mul overflow\" // WS21\n ) / MAX_DURATION_POW_2,\n \"overflow on weight\" // WS22\n );\n }\n\n /**\n * @notice Return flag whether the given address is a registered vesting contract.\n * @param stakerAddress the address to check\n */\n function _isVestingContract(address stakerAddress) internal view returns (bool) {\n bool isVesting;\n bytes32 codeHash;\n\n assembly {\n codeHash := extcodehash(stakerAddress)\n }\n if (address(vestingRegistryLogic) != address(0)) {\n isVesting = vestingRegistryLogic.isVestingAddress(stakerAddress);\n }\n\n if (isVesting) return true;\n if (vestingCodeHashes[codeHash]) return true;\n return false;\n }\n}\n" + }, + "contracts/governance/Staking/modules/shared/StakingStorageShared.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../../../openzeppelin/Ownable.sol\";\nimport \"../../../../interfaces/IERC20.sol\";\nimport \"../../../IFeeSharingCollector.sol\";\nimport \"../../../Vesting/IVestingRegistry.sol\";\n\n/**\n * @title StakingStorageShared contract is inherited by Staking modules.\n * @notice Just the storage part of stacking contract, no functions,\n * only constant, variables and required structures (mappings).\n * Used by StackingProxy and Checkpoints contracts.\n *\n * What is SOV staking?\n * The purpose of the SOV token is to provide a pseudonymous,\n * censorship-resistant mechanism for governing the parameters of the Sovryn\n * protocol, while aligning the incentives of protocol governors with the\n * long-term success of the protocol. Any SOV token holder can choose to\n * stake (lock up) their tokens for a fixed period of time in return for\n * voting rights in the Bitocracy. Stakers are further incentivised through\n * fee and slashing rewards.\n * */\ncontract StakingStorageShared is Ownable {\n /// @notice 2 weeks in seconds.\n uint256 constant TWO_WEEKS = 1209600;\n\n /// @notice The maximum possible voting weight before adding +1 (actually 10, but need 9 for computation).\n uint96 public constant MAX_VOTING_WEIGHT = 9;\n\n /// @notice weight is multiplied with this factor (for allowing decimals, like 1.2x).\n /// @dev MAX_VOTING_WEIGHT * WEIGHT_FACTOR needs to be < 792, because there are 100,000,000 SOV with 18 decimals\n uint96 public constant WEIGHT_FACTOR = 10;\n\n /// @notice The maximum duration to stake tokens for.\n uint256 public constant MAX_DURATION = 1092 days;\n\n /// @notice The maximum duration ^2\n uint96 constant MAX_DURATION_POW_2 = 1092 * 1092;\n\n /// @notice Default weight scaling.\n uint96 constant DEFAULT_WEIGHT_SCALING = 3;\n\n /// @notice Range for weight scaling.\n uint96 constant MIN_WEIGHT_SCALING = 1;\n uint96 constant MAX_WEIGHT_SCALING = 9;\n\n /// @notice The timestamp of contract creation. Base for the staking period calculation.\n uint256 public kickoffTS;\n\n string name = \"SOVStaking\";\n\n /// @notice The token to be staked.\n IERC20 public SOVToken;\n\n /// @notice A record of each accounts delegate.\n mapping(address => mapping(uint256 => address)) public delegates;\n\n /// @notice If this flag is set to true, all tokens are unlocked immediately.\n bool public allUnlocked = false;\n\n /// @notice The EIP-712 typehash for the contract's domain.\n bytes32 public constant DOMAIN_TYPEHASH =\n keccak256(\"EIP712Domain(string name,uint256 chainId,address verifyingContract)\");\n\n /// @notice The EIP-712 typehash for the delegation struct used by the contract.\n bytes32 public constant DELEGATION_TYPEHASH =\n keccak256(\"Delegation(address delegatee,uint256 lockDate,uint256 nonce,uint256 expiry)\");\n\n /// @notice Used for stake migrations to a new staking contract with a different storage structure.\n address public newStakingContract;\n\n /*************************** Checkpoints *******************************/\n\n /// @notice A checkpoint for marking the stakes from a given block\n struct Checkpoint {\n uint32 fromBlock;\n uint96 stake;\n }\n\n /// @notice A record of tokens to be unstaked at a given time in total.\n /// For total voting power computation. Voting weights get adjusted bi-weekly.\n /// @dev totalStakingCheckpoints[date][index] is a checkpoint.\n mapping(uint256 => mapping(uint32 => Checkpoint)) public totalStakingCheckpoints;\n\n /// @notice The number of total staking checkpoints for each date.\n /// @dev numTotalStakingCheckpoints[date] is a number.\n mapping(uint256 => uint32) public numTotalStakingCheckpoints;\n\n /// @notice A record of tokens to be unstaked at a given time which were delegated to a certain address.\n /// For delegatee voting power computation. Voting weights get adjusted bi-weekly.\n /// @dev delegateStakingCheckpoints[delegatee][date][index] is a checkpoint.\n mapping(address => mapping(uint256 => mapping(uint32 => Checkpoint)))\n public delegateStakingCheckpoints;\n\n /// @notice The number of total staking checkpoints for each date per delegate.\n /// @dev numDelegateStakingCheckpoints[delegatee][date] is a number.\n mapping(address => mapping(uint256 => uint32)) public numDelegateStakingCheckpoints;\n\n /// @notice A record of tokens to be unstaked at a given time which per user address (address -> lockDate -> stake checkpoint)\n /// @dev userStakingCheckpoints[user][date][index] is a checkpoint.\n mapping(address => mapping(uint256 => mapping(uint32 => Checkpoint)))\n public userStakingCheckpoints;\n\n /// @notice The number of total staking checkpoints for each date per user.\n /// @dev numUserStakingCheckpoints[user][date] is a number.\n mapping(address => mapping(uint256 => uint32)) public numUserStakingCheckpoints;\n\n /// @notice A record of states for signing / validating signatures\n /// @dev nonces[user] is a number.\n mapping(address => uint256) public nonces;\n\n /*************************** Slashing *******************************/\n\n /// @notice the address of FeeSharingCollectorProxy contract, we need it for unstaking with slashing.\n IFeeSharingCollector public feeSharing;\n\n /// @notice used for weight scaling when unstaking with slashing.\n uint96 public weightScaling = DEFAULT_WEIGHT_SCALING;\n\n /// @notice List of vesting contracts, tokens for these contracts won't be slashed if unstaked by governance.\n /// @dev vestingWhitelist[contract] is true/false.\n mapping(address => bool) public vestingWhitelist;\n\n /// @dev user => flag whether user has admin role.\n /// @dev multisig should be an admin, admin can invoke only governanceWithdrawVesting function,\n /// \tthis function works only with Team Vesting contracts\n mapping(address => bool) public admins;\n\n /// @dev vesting contract code hash => flag whether it's registered code hash\n mapping(bytes32 => bool) public vestingCodeHashes;\n\n /// @notice A record of tokens to be unstaked from vesting contract at a given time (lockDate -> vest checkpoint)\n /// @dev vestingCheckpoints[date][index] is a checkpoint.\n mapping(uint256 => mapping(uint32 => Checkpoint)) public vestingCheckpoints;\n\n /// @notice The number of total vesting checkpoints for each date.\n /// @dev numVestingCheckpoints[date] is a number.\n mapping(uint256 => uint32) public numVestingCheckpoints;\n\n ///@notice vesting registry contract\n IVestingRegistry public vestingRegistryLogic;\n\n /// @dev user => flag whether user has pauser role.\n mapping(address => bool) public pausers;\n\n /// @dev Staking contract is paused\n bool public paused;\n\n /// @dev Staking contract is frozen\n bool public frozen;\n\n /// @dev max iterations that can be supported in 1 tx for the withdrawal\n uint256 internal maxVestingWithdrawIterations;\n\n constructor() internal {\n //abstract\n }\n}\n" + }, + "contracts/governance/Staking/modules/StakingAdminModule.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../../openzeppelin/Address.sol\";\nimport \"./shared/StakingShared.sol\";\nimport \"../../../proxy/modules/interfaces/IFunctionsList.sol\";\n\n/**\n * @title Staking Admin Module.\n * @notice Implements administrative functionality pause, freeze and setting addresses and parameters\n * related to staking\n * */\ncontract StakingAdminModule is IFunctionsList, StakingShared {\n using Address for address payable;\n\n event AdminAdded(address admin);\n\n event AdminRemoved(address admin);\n\n /// @param pauser address to grant power to pause the contract\n /// @param added true - added, false - removed\n event PauserAddedOrRemoved(address indexed pauser, bool indexed added);\n\n /// @notice An event emitted when a staking is paused or unpaused\n /// @param setPaused true - pause, false - unpause\n event StakingPaused(bool indexed setPaused);\n\n /// @notice An event emitted when a staking is frozen or unfrozen\n /// @param setFrozen true - freeze, false - unfreeze\n event StakingFrozen(bool indexed setFrozen);\n\n /**\n * @notice Add account to Admins ACL.\n * @param _admin The addresses of the account to grant permissions.\n * */\n function addAdmin(address _admin) external onlyOwner whenNotFrozen {\n require(_admin != address(0), \"cannot add the zero address as an admin\");\n admins[_admin] = true;\n emit AdminAdded(_admin);\n }\n\n /**\n * @notice Remove account from Admins ACL.\n * @param _admin The addresses of the account to revoke permissions.\n * */\n function removeAdmin(address _admin) external onlyOwner whenNotFrozen {\n require(admins[_admin], \"address is not an admin\");\n admins[_admin] = false;\n emit AdminRemoved(_admin);\n }\n\n /**\n * @notice Add account to pausers ACL.\n * @param _pauser The address to grant pauser permissions.\n * */\n function addPauser(address _pauser) external onlyOwner whenNotFrozen {\n require(_pauser != address(0), \"cannot add the zero address as a pauser\");\n pausers[_pauser] = true;\n emit PauserAddedOrRemoved(_pauser, true);\n }\n\n /**\n * @notice Remove account from pausers ACL.\n * @param _pauser The address to grant pauser permissions.\n * */\n function removePauser(address _pauser) external onlyOwner whenNotFrozen {\n require(pausers[_pauser], \"address is not a pauser\");\n delete pausers[_pauser];\n emit PauserAddedOrRemoved(_pauser, false);\n }\n\n /**\n * @notice Pause/unpause contract\n * @param _pause true when pausing, false when unpausing\n * */\n function pauseUnpause(bool _pause) public onlyPauserOrOwner whenNotFrozen {\n paused = _pause;\n emit StakingPaused(_pause);\n }\n\n /**\n * @notice Freeze contract - disable all functions\n * @param _freeze true when freezing, false when unfreezing\n * @dev When freezing, pause is always applied too. When unfreezing, the contract is left in paused stated.\n * */\n function freezeUnfreeze(bool _freeze) external onlyPauserOrOwner {\n require(_freeze != frozen, \"Cannot freeze/unfreeze to the same state\"); // WS25\n if (_freeze) pauseUnpause(true);\n frozen = _freeze;\n emit StakingFrozen(_freeze);\n }\n\n /**\n * @notice Allow the owner to set a fee sharing proxy contract.\n * We need it for unstaking with slashing.\n * @param _feeSharing The address of FeeSharingCollectorProxy contract.\n * */\n function setFeeSharing(address _feeSharing) external onlyOwner whenNotFrozen {\n require(_feeSharing != address(0), \"FeeSharing address shouldn't be 0\"); // S17\n feeSharing = IFeeSharingCollector(_feeSharing);\n }\n\n /**\n * @notice Allow the owner to set weight scaling.\n * We need it for unstaking with slashing.\n * @param _weightScaling The weight scaling.\n * */\n function setWeightScaling(uint96 _weightScaling) external onlyOwner whenNotFrozen {\n require(\n MIN_WEIGHT_SCALING <= _weightScaling && _weightScaling <= MAX_WEIGHT_SCALING,\n \"scaling doesn't belong to range [1, 9]\" // S18\n );\n weightScaling = _weightScaling;\n }\n\n /**\n * @notice Allow the owner to set a new staking contract.\n * As a consequence it allows the stakers to migrate their positions\n * to the new contract.\n * @dev Doesn't have any influence as long as migrateToNewStakingContract\n * is not implemented.\n * @param _newStakingContract The address of the new staking contract.\n * */\n function setNewStakingContract(address _newStakingContract) external onlyOwner whenNotFrozen {\n require(_newStakingContract != address(0), \"can't reset the new staking contract to 0\"); // S16\n newStakingContract = _newStakingContract;\n }\n\n /**\n * @notice Allow a staker to migrate his positions to the new staking contract.\n * @dev Staking contract needs to be set before by the owner.\n * Currently not implemented, just needed for the interface.\n * In case it's needed at some point in the future,\n * the implementation needs to be changed first.\n * */\n function migrateToNewStakingContract() external whenNotFrozen {\n require(newStakingContract != address(0), \"there is no new staking contract set\"); // S19\n revert(\"not implemented\");\n /// @dev implementation:\n /// @dev Iterate over all possible lock dates from now until now + MAX_DURATION.\n /// @dev Read the stake & delegate of the msg.sender\n /// @dev If stake > 0, stake it at the new contract until the lock date with the current delegate.\n }\n\n function getFunctionsList() external pure returns (bytes4[] memory) {\n bytes4[] memory functionsList = new bytes4[](13);\n functionsList[0] = this.addAdmin.selector;\n functionsList[1] = this.removeAdmin.selector;\n functionsList[2] = this.addPauser.selector;\n functionsList[3] = this.removePauser.selector;\n functionsList[4] = this.pauseUnpause.selector;\n functionsList[5] = this.freezeUnfreeze.selector;\n functionsList[6] = this.setFeeSharing.selector;\n functionsList[7] = this.setWeightScaling.selector;\n functionsList[8] = this.setNewStakingContract.selector;\n functionsList[9] = this.owner.selector;\n functionsList[10] = this.isOwner.selector;\n functionsList[11] = this.transferOwnership.selector;\n functionsList[12] = this.migrateToNewStakingContract.selector;\n return functionsList;\n }\n}\n" + }, + "contracts/governance/Staking/modules/StakingGovernanceModule.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./shared/CheckpointsShared.sol\";\nimport \"../../../openzeppelin/Address.sol\";\nimport \"./shared/StakingShared.sol\";\nimport \"../../../proxy/modules/interfaces/IFunctionsList.sol\";\nimport \"../../../rsk/RSKAddrValidator.sol\";\nimport \"../../Vesting/IVesting.sol\";\n\n/**\n * @title Staking Governance Module contract\n * @notice Implements voting power and delegation functionality\n * */\ncontract StakingGovernanceModule is IFunctionsList, StakingShared, CheckpointsShared {\n using Address for address payable;\n\n /************* TOTAL VOTING POWER COMPUTATION ************************/\n\n /**\n * @notice Compute the total voting power at a given time.\n * @param blockNumber The block number, needed for checkpointing.\n * @param time The timestamp for which to calculate the total voting power.\n * @return The total voting power at the given time.\n * */\n function getPriorTotalVotingPower(uint32 blockNumber, uint256 time)\n public\n view\n returns (uint96 totalVotingPower)\n {\n /// @dev Start the computation with the exact or previous unlocking date (voting weight remians the same until the next break point).\n uint256 start = _timestampToLockDate(time);\n uint256 end = start + MAX_DURATION;\n\n /// @dev Max 78 iterations.\n for (uint256 i = start; i <= end; i += TWO_WEEKS) {\n totalVotingPower = add96(\n totalVotingPower,\n _totalPowerByDate(i, start, blockNumber),\n \"arrays mismatch\"\n ); // WS06\n }\n }\n\n /**\n * @notice Compute the voting power for a specific date.\n * Power = stake * weight\n * @param date The staking date to compute the power for.\n * @param startDate The date for which we need to know the power of the stake.\n * @param blockNumber The block number, needed for checkpointing.\n * @return The stacking power.\n * */\n function _totalPowerByDate(\n uint256 date,\n uint256 startDate,\n uint256 blockNumber\n ) internal view returns (uint96 power) {\n uint96 weight = _computeWeightByDate(date, startDate);\n uint96 staked = _getPriorTotalStakesForDate(date, blockNumber);\n /// @dev weight is multiplied by some factor to allow decimals.\n power = mul96(staked, weight, \"mul overflow\") / WEIGHT_FACTOR; // WS07\n }\n\n /****************************** DELEGATED VOTING POWER COMPUTATION ************************/\n\n /**\n * @notice Get the current votes balance for a user account.\n * @param account The address to get votes balance.\n * @dev This is a wrapper to simplify arguments. The actual computation is\n * performed on WeightedStaking parent contract.\n * @return The number of current votes for a user account.\n * */\n function getCurrentVotes(address account) external view returns (uint96) {\n return getPriorVotes(account, block.number - 1, block.timestamp);\n }\n\n /**\n * @notice Determine the prior number of votes for a delegatee as of a block number.\n * Iterate through checkpoints adding up voting power.\n * @dev Block number must be a finalized block or else this function will revert\n * to prevent misinformation.\n * Used for Voting, not for fee sharing.\n * @param account The address of the account to check.\n * @param blockNumber The block number to get the vote balance at.\n * @param date The staking date to compute the power for.\n * @return The number of votes the delegatee had as of the given block.\n * */\n function getPriorVotes(\n address account,\n uint256 blockNumber,\n uint256 date\n ) public view returns (uint96 votes) {\n /// @dev If date is not an exact break point, start weight computation from the previous break point (alternative would be the next).\n uint256 start = _timestampToLockDate(date);\n uint256 end = start + MAX_DURATION;\n\n /// @dev Max 78 iterations.\n for (uint256 i = start; i <= end; i += TWO_WEEKS) {\n votes = add96(\n votes,\n _totalPowerByDateForDelegatee(account, i, start, blockNumber),\n \"overflow - total VP\"\n ); // WS09\n }\n }\n\n /**\n * @notice Compute the voting power for a specific date.\n * Power = stake * weight\n * @param account The address of the account to check.\n * @param date The staking date to compute the power for.\n * @param startDate The date for which we need to know the power of the stake.\n * @param blockNumber The block number, needed for checkpointing.\n * @return The stacking power.\n * */\n function _totalPowerByDateForDelegatee(\n address account,\n uint256 date,\n uint256 startDate,\n uint256 blockNumber\n ) internal view returns (uint96 power) {\n uint96 weight = _computeWeightByDate(date, startDate);\n uint96 staked = _getPriorStakeByDateForDelegatee(account, date, blockNumber);\n power = mul96(staked, weight, \"mul overflow\") / WEIGHT_FACTOR; // WS10\n }\n\n /**\n * @notice Determine the prior number of stake for an account as of a block number.\n * @dev Block number must be a finalized block or else this function will\n * revert to prevent misinformation.\n * @param account The address of the account to check.\n * @param date The staking date to compute the power for. Adjusted to the next valid lock date, if necessary.\n * @param blockNumber The block number to get the vote balance at.\n * @return The number of votes the account had as of the given block.\n * */\n function getPriorStakeByDateForDelegatee(\n address account,\n uint256 date,\n uint256 blockNumber\n ) external view returns (uint96) {\n date = _adjustDateForOrigin(date);\n return _getPriorStakeByDateForDelegatee(account, date, blockNumber);\n }\n\n /**\n * @notice Determine the prior number of stake for an account as of a block number.\n * @dev Block number must be a finalized block or else this function will\n * revert to prevent misinformation.\n * @param account The address of the account to check.\n * @param date The staking date to compute the power for.\n * @param blockNumber The block number to get the vote balance at.\n * @return The number of votes the account had as of the given block.\n * */\n function _getPriorStakeByDateForDelegatee(\n address account,\n uint256 date,\n uint256 blockNumber\n ) internal view returns (uint96) {\n require(blockNumber < _getCurrentBlockNumber(), \"not determined yet\"); // WS11\n\n uint32 nCheckpoints = numDelegateStakingCheckpoints[account][date];\n if (nCheckpoints == 0) {\n return 0;\n }\n\n /// @dev First check most recent balance.\n if (delegateStakingCheckpoints[account][date][nCheckpoints - 1].fromBlock <= blockNumber) {\n return delegateStakingCheckpoints[account][date][nCheckpoints - 1].stake;\n }\n\n /// @dev Next check implicit zero balance.\n if (delegateStakingCheckpoints[account][date][0].fromBlock > blockNumber) {\n return 0;\n }\n\n uint32 lower = 0;\n uint32 upper = nCheckpoints - 1;\n while (upper > lower) {\n uint32 center = upper - (upper - lower) / 2; /// @dev ceil, avoiding overflow.\n Checkpoint memory cp = delegateStakingCheckpoints[account][date][center];\n if (cp.fromBlock == blockNumber) {\n return cp.stake;\n } else if (cp.fromBlock < blockNumber) {\n lower = center;\n } else {\n upper = center - 1;\n }\n }\n return delegateStakingCheckpoints[account][date][lower].stake;\n }\n\n /**************** SHARED FUNCTIONS *********************/\n\n /**\n * @notice Determine the prior number of stake for an unlocking date as of a block number.\n * @dev Block number must be a finalized block or else this function will\n * revert to prevent misinformation.\n * @param date The date to check the stakes for. Adjusted to the next valid lock date, as necessary\n * @param blockNumber The block number to get the vote balance at.\n * @return The total number of votes as of the given block.\n * */\n function getPriorTotalStakesForDate(uint256 date, uint256 blockNumber)\n public\n view\n returns (uint96)\n {\n date = _adjustDateForOrigin(date);\n return _getPriorTotalStakesForDate(date, blockNumber);\n }\n\n /**\n * @notice Determine the prior number of stake for an unlocking date as of a block number.\n * @dev Block number must be a finalized block or else this function will\n * revert to prevent misinformation.\n * @param date The date to check the stakes for.\n * @param blockNumber The block number to get the vote balance at.\n * @return The total number of votes as of the given block.\n * */\n function _getPriorTotalStakesForDate(uint256 date, uint256 blockNumber)\n internal\n view\n returns (uint96)\n {\n require(blockNumber < _getCurrentBlockNumber(), \"not determined\"); // WS08\n\n uint32 nCheckpoints = numTotalStakingCheckpoints[date];\n if (nCheckpoints == 0) {\n return 0;\n }\n\n // First check most recent balance\n if (totalStakingCheckpoints[date][nCheckpoints - 1].fromBlock <= blockNumber) {\n return totalStakingCheckpoints[date][nCheckpoints - 1].stake;\n }\n\n // Next check implicit zero balance\n if (totalStakingCheckpoints[date][0].fromBlock > blockNumber) {\n return 0;\n }\n\n uint32 lower = 0;\n uint32 upper = nCheckpoints - 1;\n while (upper > lower) {\n uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow\n Checkpoint memory cp = totalStakingCheckpoints[date][center];\n if (cp.fromBlock == blockNumber) {\n return cp.stake;\n } else if (cp.fromBlock < blockNumber) {\n lower = center;\n } else {\n upper = center - 1;\n }\n }\n return totalStakingCheckpoints[date][lower].stake;\n }\n\n /**\n * @notice Set new delegatee. Move from user's current delegate to a new\n * delegatee the stake balance.\n * @param delegator The user address to move stake balance from its current delegatee.\n * @param delegatee The new delegatee. The address to move stake balance to.\n * @param lockedTS The lock date.\n * @dev Reverts if delegator balance or delegatee is not valid, unless the sender is a vesting contract.\n * */\n function _delegate(\n address delegator,\n address delegatee,\n uint256 lockedTS\n ) internal {\n address currentDelegate = delegates[delegator][lockedTS];\n uint96 delegatorBalance = _currentBalance(delegator, lockedTS);\n\n // vesting contracts will in multiple cases try to delegate a zero balance\n // or to the existing delegatee\n if (_isVestingContract(msg.sender)) {\n if (delegatorBalance == 0 || currentDelegate == delegatee) {\n return;\n }\n } else {\n require(delegatorBalance > 0, \"no stake to delegate\");\n require(currentDelegate != delegatee, \"cannot delegate to the existing delegatee\");\n }\n\n delegates[delegator][lockedTS] = delegatee;\n\n emit DelegateChanged(delegator, lockedTS, currentDelegate, delegatee);\n\n _moveDelegates(currentDelegate, delegatee, delegatorBalance, lockedTS);\n }\n\n // @dev delegates tokens for lock date 2 weeks later than given lock date\n //\t\tif message sender is a contract\n function _delegateNext(\n address delegator,\n address delegatee,\n uint256 lockedTS\n ) internal {\n if (_isVestingContract(msg.sender)) {\n uint256 nextLock = lockedTS.add(TWO_WEEKS);\n address currentDelegate = delegates[delegator][nextLock];\n if (currentDelegate != delegatee) {\n _delegate(delegator, delegatee, nextLock);\n }\n\n // @dev workaround for the issue with a delegation of the latest stake\n uint256 endDate = IVesting(msg.sender).endDate();\n nextLock = lockedTS.add(FOUR_WEEKS);\n if (nextLock == endDate) {\n currentDelegate = delegates[delegator][nextLock];\n if (currentDelegate != delegatee) {\n _delegate(delegator, delegatee, nextLock);\n }\n }\n }\n }\n\n /**\n * @notice Move an amount of delegate stake from a source address to a\n * destination address.\n * @param srcRep The address to get the staked amount from.\n * @param dstRep The address to send the staked amount to.\n * @param amount The staked amount to move.\n * @param lockedTS The lock date.\n * */\n function _moveDelegates(\n address srcRep,\n address dstRep,\n uint96 amount,\n uint256 lockedTS\n ) internal {\n if (srcRep != dstRep && amount > 0) {\n if (srcRep != address(0)) _decreaseDelegateStake(srcRep, lockedTS, amount);\n\n if (dstRep != address(0)) _increaseDelegateStake(dstRep, lockedTS, amount);\n }\n }\n\n /**\n * @notice Retrieve CHAIN_ID of the executing chain.\n *\n * Chain identifier (chainID) introduced in EIP-155 protects transaction\n * included into one chain from being included into another chain.\n * Basically, chain identifier is an integer number being used in the\n * processes of signing transactions and verifying transaction signatures.\n *\n * @dev As of version 0.5.12, Solidity includes an assembly function\n * chainid() that provides access to the new CHAINID opcode.\n *\n * TODO: chainId is included in block. So you can get chain id like\n * block timestamp or block number: block.chainid;\n * */\n function _getChainId() internal pure returns (uint256) {\n uint256 chainId;\n assembly {\n chainId := chainid()\n }\n return chainId;\n }\n\n /**\n * @notice Delegate votes from `msg.sender` which are locked until lockDate to `delegatee`.\n * @param delegatee The address to delegate votes to.\n * @param lockDate the date if the position to delegate.\n * */\n function delegate(address delegatee, uint256 lockDate) external whenNotPaused {\n require(delegatee != address(0), \"cannot delegate to the zero address\");\n _notSameBlockAsStakingCheckpoint(lockDate, msg.sender);\n\n _delegate(msg.sender, delegatee, lockDate);\n // @dev delegates tokens for lock date 2 weeks later than given lock date\n //\t\tif message sender is a contract\n _delegateNext(msg.sender, delegatee, lockDate);\n }\n\n function getFunctionsList() external pure returns (bytes4[] memory) {\n bytes4[] memory functionsList = new bytes4[](6);\n functionsList[0] = this.getPriorTotalVotingPower.selector;\n functionsList[1] = this.getCurrentVotes.selector;\n functionsList[2] = this.getPriorVotes.selector;\n functionsList[3] = this.getPriorStakeByDateForDelegatee.selector;\n functionsList[4] = this.getPriorTotalStakesForDate.selector;\n functionsList[5] = this.delegate.selector;\n return functionsList;\n }\n}\n" + }, + "contracts/governance/Staking/modules/StakingStakeModule.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../../proxy/modules/interfaces/IFunctionsList.sol\";\nimport \"./shared/CheckpointsShared.sol\";\nimport \"../../ApprovalReceiver.sol\";\nimport \"./shared/StakingShared.sol\";\n\n/**\n * @title Staking contract staking functionality module\n * @notice Implements staking functionality\n **/\ncontract StakingStakeModule is IFunctionsList, StakingShared, CheckpointsShared, ApprovalReceiver {\n using SafeMath for uint256;\n\n /// @notice An event emitted when tokens get staked.\n event TokensStaked(\n address indexed staker,\n uint256 amount,\n uint256 lockedUntil,\n uint256 totalStaked\n );\n\n /// @notice An event emitted when a staking period gets extended.\n event ExtendedStakingDuration(\n address indexed staker,\n uint256 previousDate,\n uint256 newDate,\n uint256 amountStaked\n );\n\n /**\n * @notice Stake the given amount for the given duration of time.\n * @param amount The number of tokens to stake.\n * @param until Timestamp indicating the date until which to stake.\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\n * @param delegatee The address of the delegatee or 0x0 if there is none.\n * */\n function stake(\n uint96 amount,\n uint256 until,\n address stakeFor,\n address delegatee\n ) external whenNotPaused whenNotFrozen {\n _stake(msg.sender, amount, until, stakeFor, delegatee, false);\n }\n\n /**\n * @notice Stake the given amount for the given duration of time.\n * @dev This function will be invoked from receiveApproval\n * @dev SOV.approveAndCall -> this.receiveApproval -> this.stakeWithApproval\n * @param sender The sender of SOV.approveAndCall\n * @param amount The number of tokens to stake.\n * @param until Timestamp indicating the date until which to stake.\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\n * @param delegatee The address of the delegatee or 0x0 if there is none.\n * */\n\n function stakeWithApproval(\n address sender,\n uint96 amount,\n uint256 until,\n address stakeFor,\n address delegatee\n ) external onlyThisContract whenNotPaused whenNotFrozen {\n _stake(sender, amount, until, stakeFor, delegatee, false);\n }\n\n /**\n * @notice Send sender's tokens to this contract and update its staked balance.\n * @param sender The sender of the tokens.\n * @param amount The number of tokens to send.\n * @param until The date until which the tokens will be staked.\n * @param stakeFor The beneficiary whose stake will be increased.\n * @param delegatee The address of the delegatee or stakeFor if default 0x0.\n * @param timeAdjusted Whether fixing date to stacking periods or not.\n * */\n function _stake(\n address sender,\n uint96 amount,\n uint256 until,\n address stakeFor,\n address delegatee,\n bool timeAdjusted\n ) internal {\n _stakeOptionalTokenTransfer(\n sender,\n amount,\n until,\n stakeFor,\n delegatee,\n timeAdjusted,\n true // transfer SOV\n );\n }\n\n /**\n * @notice Send sender's tokens to this contract and update its staked balance.\n * @param sender The sender of the tokens.\n * @param amount The number of tokens to send.\n * @param until The date until which the tokens will be staked.\n * @param stakeFor The beneficiary whose stake will be increased.\n * @param delegatee The address of the delegatee or stakeFor if default 0x0.\n * @param timeAdjusted Whether fixing date to stacking periods or not.\n * @param transferToken Should transfer SOV - false for multiple iterations like in stakeBySchedule\n * */\n function _stakeOptionalTokenTransfer(\n address sender,\n uint96 amount,\n uint256 until,\n address stakeFor,\n address delegatee,\n bool timeAdjusted,\n bool transferToken\n ) internal {\n require(amount > 0, \"amount needs to be bigger than 0\"); // S01\n\n if (!timeAdjusted) {\n until = _timestampToLockDate(until);\n }\n require(\n until > block.timestamp,\n \"Staking::_timestampToLockDate: staking period too short\"\n ); // S02\n\n /// @dev Stake for the sender if not specified otherwise.\n if (stakeFor == address(0)) {\n stakeFor = sender;\n }\n // must wait a block before staking again for that same deadline\n _notSameBlockAsStakingCheckpoint(until, stakeFor);\n\n /// @dev Delegate for stakeFor if not specified otherwise.\n if (delegatee == address(0)) {\n delegatee = stakeFor;\n }\n\n /// @dev Do not stake longer than the max duration.\n if (!timeAdjusted) {\n uint256 latest = _timestampToLockDate(block.timestamp + MAX_DURATION);\n if (until > latest) until = latest;\n }\n\n uint96 previousBalance = _currentBalance(stakeFor, until);\n\n /// @dev Increase stake.\n _increaseStake(sender, amount, stakeFor, until, transferToken);\n\n // @dev Previous version wasn't working properly for the following case:\n //\t\tdelegate checkpoint wasn't updating for the second and next stakes for the same date\n //\t\tif first stake was withdrawn completely and stake was delegated to the staker\n //\t\t(no delegation to another address).\n address previousDelegatee = delegates[stakeFor][until];\n\n if (previousDelegatee != delegatee) {\n // @dev only the user that stakes for himself is allowed to delegate VP to another address\n // which works with vesting stakes and prevents vulnerability of delegating VP to an arbitrary address from\n // any address\n\n if (delegatee != stakeFor) {\n require(\n stakeFor == sender,\n \"Only stakeFor account is allowed to change delegatee\"\n );\n } else if (sender != stakeFor && previousDelegatee != address(0)) {\n require(stakeFor == sender, \"Only sender is allowed to change delegatee\");\n }\n\n /// @dev Update delegatee.\n delegates[stakeFor][until] = delegatee;\n\n /// @dev Decrease stake on previous balance for previous delegatee.\n _decreaseDelegateStake(previousDelegatee, until, previousBalance);\n\n /// @dev Add previousBalance to amount.\n amount = add96(previousBalance, amount, \"add amounts failed\");\n }\n\n /// @dev Increase stake.\n _increaseDelegateStake(delegatee, until, amount);\n emit DelegateChanged(stakeFor, until, previousDelegatee, delegatee);\n }\n\n /**\n * @notice Extend the staking duration until the specified date.\n * @param previousLock The old unlocking timestamp.\n * @param until The new unlocking timestamp in seconds.\n * */\n function extendStakingDuration(uint256 previousLock, uint256 until)\n external\n whenNotPaused\n whenNotFrozen\n {\n previousLock = _timestampToLockDate(previousLock);\n until = _timestampToLockDate(until);\n\n _notSameBlockAsStakingCheckpoint(previousLock, msg.sender);\n\n /// @dev Do not exceed the max duration, no overflow possible.\n uint256 latest = _timestampToLockDate(block.timestamp + MAX_DURATION);\n if (until > latest) until = latest;\n\n require(previousLock < until, \"must increase staking duration\"); // S04\n\n /// @dev Update checkpoints.\n /// @dev TODO James: Can reading stake at block.number -1 cause trouble with multiple tx in a block?\n uint96 amount = _getPriorUserStakeByDate(msg.sender, previousLock, block.number - 1);\n require(amount > 0, \"no stakes till the prev lock date\"); // S05\n _decreaseUserStake(msg.sender, previousLock, amount);\n _increaseUserStake(msg.sender, until, amount);\n\n if (_isVestingContract(msg.sender)) {\n _decreaseVestingStake(previousLock, amount);\n _increaseVestingStake(until, amount);\n }\n\n _decreaseDailyStake(previousLock, amount);\n _increaseDailyStake(until, amount);\n\n /// @dev Delegate might change: if there is already a delegate set for the until date, it will remain the delegate for this position\n address delegateFrom = delegates[msg.sender][previousLock];\n delegates[msg.sender][previousLock] = address(0); //the previousLock delegates nullifying before reading that form `until` guards in case delegateTo == until\n address delegateTo = delegates[msg.sender][until];\n if (delegateTo == address(0)) {\n delegateTo = delegateFrom;\n delegates[msg.sender][until] = delegateFrom;\n }\n _decreaseDelegateStake(delegateFrom, previousLock, amount);\n _increaseDelegateStake(delegateTo, until, amount);\n\n emit ExtendedStakingDuration(msg.sender, previousLock, until, amount);\n }\n\n /**\n * @notice Send sender's tokens to this contract and update its staked balance.\n * @param sender The sender of the tokens.\n * @param amount The number of tokens to send.\n * @param stakeFor The beneficiary whose stake will be increased.\n * @param until The date until which the tokens will be staked.\n * @param transferToken if false - token transfer should be handled separately\n * */\n function _increaseStake(\n address sender,\n uint96 amount,\n address stakeFor,\n uint256 until,\n bool transferToken\n ) internal {\n /// @dev Retrieve the SOV tokens.\n if (transferToken)\n require(\n SOVToken.transferFrom(sender, address(this), amount),\n \"Should transfer tokens successfully\"\n ); // IS10\n\n /// @dev Increase staked balance.\n uint96 balance = _currentBalance(stakeFor, until);\n balance = add96(balance, amount, \"increaseStake: overflow\"); // IS20\n\n /// @dev Update checkpoints.\n _increaseDailyStake(until, amount);\n _increaseUserStake(stakeFor, until, amount);\n\n if (_isVestingContract(stakeFor)) _increaseVestingStake(until, amount);\n\n emit TokensStaked(stakeFor, amount, until, balance);\n }\n\n /**\n * @dev DO NOT USE this misspelled function. Use stakeBySchedule function instead.\n * This function cannot be deprecated while we have non-upgradeable vesting contracts.\n * */\n function stakesBySchedule(\n uint256 amount,\n uint256 cliff,\n uint256 duration,\n uint256 intervalLength,\n address stakeFor,\n address delegatee\n ) external whenNotPaused whenNotFrozen {\n _stakeBySchedule(amount, cliff, duration, intervalLength, stakeFor, delegatee);\n }\n\n /**\n * @notice Stake tokens according to the vesting schedule.\n * @param amount The amount of tokens to stake.\n * @param cliff The time interval to the first withdraw.\n * @param duration The staking duration.\n * @param intervalLength The length of each staking interval when cliff passed.\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\n * @param delegatee The address of the delegatee or 0x0 if there is none.\n * */\n function stakeBySchedule(\n uint256 amount,\n uint256 cliff,\n uint256 duration,\n uint256 intervalLength,\n address stakeFor,\n address delegatee\n ) external whenNotPaused whenNotFrozen {\n _stakeBySchedule(amount, cliff, duration, intervalLength, stakeFor, delegatee);\n }\n\n /**\n * @notice Stake tokens according to the vesting schedule.\n * @param amount The amount of tokens to stake.\n * @param cliff The time interval to the first withdraw.\n * @param duration The staking duration.\n * @param intervalLength The length of each staking interval when cliff passed.\n * @param stakeFor The address to stake the tokens for or 0x0 if staking for oneself.\n * @param delegatee The address of the delegatee or 0x0 if there is none.\n * */\n function _stakeBySchedule(\n uint256 amount,\n uint256 cliff,\n uint256 duration,\n uint256 intervalLength,\n address stakeFor,\n address delegatee\n ) internal {\n require(amount > 0, \"Invalid amount\");\n require(duration <= MAX_DURATION, \"Invalid duration\");\n require(intervalLength > 0, \"Invalid interval length\");\n require(intervalLength % TWO_WEEKS == 0, \"Invalid interval length\");\n if (delegatee != stakeFor && delegatee != address(0)) {\n require(\n stakeFor == msg.sender,\n \"Only stakeFor account is allowed to change delegatee\"\n );\n }\n /**\n * @dev Stake them until lock dates according to the vesting schedule.\n * Note: because staking is only possible in periods of 2 weeks,\n * the total duration might end up a bit shorter than specified\n * depending on the date of staking.\n * */\n uint256 start = _timestampToLockDate(block.timestamp + cliff);\n uint256 end = _timestampToLockDate(block.timestamp + duration);\n require(start <= end, \"Invalid schedule\");\n uint256 numIntervals;\n if (start < end) {\n numIntervals = (end - start) / intervalLength + 1;\n } else {\n numIntervals = 1;\n }\n uint256 stakedPerInterval = amount / numIntervals;\n\n /// @dev transferring total SOV amount before staking\n require(\n SOVToken.transferFrom(msg.sender, address(this), amount),\n \"Should transfer tokens successfully\"\n ); // SS10\n /// @dev stakedPerInterval might lose some dust on rounding. Add it to the first staking date.\n if (numIntervals >= 1) {\n _stakeOptionalTokenTransfer(\n msg.sender,\n uint96(amount - stakedPerInterval * (numIntervals - 1)),\n start,\n stakeFor,\n delegatee,\n true,\n false\n );\n }\n /// @dev Stake the rest in 4 week intervals.\n for (uint256 i = start + intervalLength; i <= end; i += intervalLength) {\n /// @dev Stakes for itself, delegates to the owner.\n _notSameBlockAsStakingCheckpoint(i, stakeFor); // must wait a block before staking again for that same deadline\n _stakeOptionalTokenTransfer(\n msg.sender,\n uint96(stakedPerInterval),\n i,\n stakeFor,\n delegatee,\n true,\n false\n );\n }\n }\n\n /**\n * @notice Get the number of staked tokens held by the user account.\n * @dev Iterate checkpoints adding up stakes.\n * @param account The address of the account to get the balance of.\n * @return The number of tokens held.\n * */\n function balanceOf(address account) external view returns (uint96 balance) {\n for (uint256 i = kickoffTS; i <= block.timestamp + MAX_DURATION; i += TWO_WEEKS) {\n balance = add96(balance, _currentBalance(account, i), \"Staking::balanceOf: overflow\"); // S12\n }\n }\n\n /**\n * @notice Get the current number of tokens staked for a day.\n * @param lockedTS The timestamp to get the staked tokens for.\n * */\n function getCurrentStakedUntil(uint256 lockedTS) external view returns (uint96) {\n uint32 nCheckpoints = numTotalStakingCheckpoints[lockedTS];\n return nCheckpoints > 0 ? totalStakingCheckpoints[lockedTS][nCheckpoints - 1].stake : 0;\n }\n\n /**\n * @notice Get list of stakes for a user account.\n * @param account The address to get stakes.\n * @return The arrays of dates and stakes.\n * */\n function getStakes(address account)\n external\n view\n returns (uint256[] memory dates, uint96[] memory stakes)\n {\n uint256 latest = _timestampToLockDate(block.timestamp + MAX_DURATION);\n\n /// @dev Calculate stakes.\n uint256 count = 0;\n /// @dev We need to iterate from first possible stake date after deployment to the latest from current time.\n for (uint256 i = kickoffTS + TWO_WEEKS; i <= latest; i += TWO_WEEKS) {\n if (_currentBalance(account, i) > 0) {\n count++;\n }\n }\n dates = new uint256[](count);\n stakes = new uint96[](count);\n\n /// @dev We need to iterate from first possible stake date after deployment to the latest from current time.\n uint256 j = 0;\n for (uint256 i = kickoffTS + TWO_WEEKS; i <= latest; i += TWO_WEEKS) {\n uint96 balance = _currentBalance(account, i);\n if (balance > 0) {\n dates[j] = i;\n stakes[j] = balance;\n j++;\n }\n }\n }\n\n /**\n * @notice Overrides default ApprovalReceiver._getToken function to\n * register SOV token on this contract.\n * @return The address of SOV token.\n * */\n function _getToken() internal view returns (address) {\n return address(SOVToken);\n }\n\n /**\n * @notice Overrides default ApprovalReceiver._getSelectors function to\n * register stakeWithApproval selector on this contract.\n * @return The array of registered selectors on this contract.\n */\n function _getSelectors() internal pure returns (bytes4[] memory) {\n bytes4[] memory selectors = new bytes4[](1);\n selectors[0] = this.stakeWithApproval.selector;\n return selectors;\n }\n\n /**\n * @notice Unstaking is possible every 2 weeks only. This means, to\n * calculate the key value for the staking checkpoints, we need to\n * map the intended timestamp to the closest available date.\n * @param timestamp The unlocking timestamp.\n * @return The actual unlocking date (might be up to 2 weeks shorter than intended).\n * */\n function timestampToLockDate(uint256 timestamp) external view returns (uint256) {\n return _timestampToLockDate(timestamp);\n }\n\n function getFunctionsList() external pure returns (bytes4[] memory) {\n bytes4[] memory functionsList = new bytes4[](10);\n functionsList[0] = this.stake.selector;\n functionsList[1] = this.stakeWithApproval.selector;\n functionsList[2] = this.extendStakingDuration.selector;\n functionsList[3] = this.stakesBySchedule.selector;\n functionsList[4] = this.stakeBySchedule.selector;\n functionsList[5] = this.balanceOf.selector;\n functionsList[6] = this.getCurrentStakedUntil.selector;\n functionsList[7] = this.getStakes.selector;\n functionsList[8] = this.timestampToLockDate.selector;\n functionsList[9] = this.receiveApproval.selector;\n return functionsList;\n }\n}\n" + }, + "contracts/governance/Staking/modules/StakingStorageModule.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../../proxy/modules/interfaces/IFunctionsList.sol\";\nimport \"./shared/StakingStorageShared.sol\";\n\n/**\n * @title Staking Storage Module\n * @notice Provides getters for public storage variables\n **/\ncontract StakingStorageModule is IFunctionsList, StakingStorageShared {\n function getStorageDefaultWeightScaling() external pure returns (uint256) {\n return uint256(DEFAULT_WEIGHT_SCALING);\n }\n\n /// @notice The maximum duration to stake tokens\n /// @return MAX_DURATION to stake tokens\n function getStorageMaxDurationToStakeTokens() external pure returns (uint256) {\n return MAX_DURATION;\n }\n\n /// @notice The maximum possible voting weight before adding +1 (actually 10, but need 9 for computation).\n function getStorageMaxVotingWeight() external pure returns (uint256) {\n return uint256(MAX_VOTING_WEIGHT);\n }\n\n /// @notice weight is multiplied with this factor (for allowing decimals, like 1.2x).\n /// @dev MAX_VOTING_WEIGHT * WEIGHT_FACTOR needs to be < 792, because there are 100,000,000 SOV with 18 decimals\n function getStorageWeightFactor() external pure returns (uint256) {\n return uint256(WEIGHT_FACTOR);\n }\n\n /// @notice Default weight scaling.\n function getStorageDefaulWeightScaling() external pure returns (uint256) {\n return uint256(DEFAULT_WEIGHT_SCALING);\n }\n\n function getStorageRangeForWeightScaling()\n external\n pure\n returns (uint256 minWeightScaling, uint256 maxWeightScaling)\n {\n return (uint256(MIN_WEIGHT_SCALING), uint256(MAX_WEIGHT_SCALING));\n }\n\n /// @notice The EIP-712 typehash for the contract's domain.\n function getStorageDomainTypehash() external pure returns (uint256) {\n return uint256(DOMAIN_TYPEHASH);\n }\n\n /// @notice The EIP-712 typehash for the delegation struct used by the contract.\n function getStorageDelegationTypehash() external pure returns (uint256) {\n return uint256(DELEGATION_TYPEHASH);\n }\n\n function getStorageName() external view returns (string memory) {\n return name;\n }\n\n /**\n * @notice Max iteration for direct withdrawal from staking to prevent out of gas issue.\n *\n * @return max iteration value.\n */\n function getMaxVestingWithdrawIterations() public view returns (uint256) {\n return maxVestingWithdrawIterations;\n }\n\n function getFunctionsList() external pure returns (bytes4[] memory) {\n bytes4[] memory functionsList = new bytes4[](32);\n functionsList[0] = this.getStorageMaxDurationToStakeTokens.selector;\n functionsList[1] = this.getStorageMaxVotingWeight.selector;\n functionsList[2] = this.getStorageWeightFactor.selector;\n functionsList[3] = this.getStorageDefaulWeightScaling.selector;\n functionsList[4] = this.getStorageRangeForWeightScaling.selector;\n functionsList[5] = this.getStorageDomainTypehash.selector;\n functionsList[6] = this.getStorageDelegationTypehash.selector;\n functionsList[7] = this.getStorageName.selector;\n functionsList[8] = this.kickoffTS.selector;\n functionsList[9] = this.SOVToken.selector;\n functionsList[10] = this.delegates.selector;\n functionsList[11] = this.allUnlocked.selector;\n functionsList[12] = this.newStakingContract.selector;\n functionsList[13] = this.totalStakingCheckpoints.selector;\n functionsList[14] = this.numTotalStakingCheckpoints.selector;\n functionsList[15] = this.delegateStakingCheckpoints.selector;\n functionsList[16] = this.numDelegateStakingCheckpoints.selector;\n functionsList[17] = this.userStakingCheckpoints.selector;\n functionsList[18] = this.numUserStakingCheckpoints.selector;\n functionsList[19] = this.nonces.selector;\n functionsList[20] = this.feeSharing.selector;\n functionsList[21] = this.weightScaling.selector;\n functionsList[22] = this.vestingWhitelist.selector;\n functionsList[23] = this.admins.selector;\n functionsList[24] = this.vestingCodeHashes.selector;\n functionsList[25] = this.vestingCheckpoints.selector;\n functionsList[26] = this.numVestingCheckpoints.selector;\n functionsList[27] = this.vestingRegistryLogic.selector;\n functionsList[28] = this.pausers.selector;\n functionsList[29] = this.paused.selector;\n functionsList[30] = this.frozen.selector;\n functionsList[31] = this.getMaxVestingWithdrawIterations.selector;\n\n return functionsList;\n }\n}\n" + }, + "contracts/governance/Staking/modules/StakingVestingModule.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./shared/StakingShared.sol\";\nimport \"../../../proxy/modules/interfaces/IFunctionsList.sol\";\n\n/**\n * @title Staking Vesting Module contract\n * @notice Implements interaction with Vesting functionality: vesting registry, vesting staking\n * */\ncontract StakingVestingModule is IFunctionsList, StakingShared {\n event ContractCodeHashAdded(bytes32 hash);\n event ContractCodeHashRemoved(bytes32 hash);\n event VestingStakeSet(uint256 lockedTS, uint96 value);\n\n /**\n * @notice sets vesting registry\n * @param _vestingRegistryProxy the address of vesting registry proxy contract\n * @dev _vestingRegistryProxy can be set to 0 as this function can be reused by\n * various other functionalities without the necessity of linking it with Vesting Registry\n */\n function setVestingRegistry(address _vestingRegistryProxy) external onlyOwner whenNotFrozen {\n vestingRegistryLogic = IVestingRegistry(_vestingRegistryProxy);\n }\n\n /**\n * @notice Sets the users' vesting stakes for a giving lock dates and writes checkpoints.\n * @param lockedDates The arrays of lock dates.\n * @param values The array of values to add to the staked balance.\n * TODO: remove - it was designed as a disposable function to initialize vesting checkpoints\n */\n function setVestingStakes(uint256[] calldata lockedDates, uint96[] calldata values)\n external\n onlyAuthorized\n whenNotFrozen\n {\n require(lockedDates.length == values.length, \"arrays mismatch\"); // WS05\n\n uint256 length = lockedDates.length;\n for (uint256 i = 0; i < length; i++) {\n _setVestingStake(lockedDates[i], values[i]);\n }\n }\n\n /**\n * @notice Sets the users' vesting stake for a giving lock date and writes a checkpoint.\n * @param lockedTS The lock date.\n * @param value The value to be set.\n * TODO: remove - it was designed as a disposable function to initialize vesting checkpoints\n */\n function _setVestingStake(uint256 lockedTS, uint96 value) internal {\n require(\n lockedTS > kickoffTS,\n \"Invalid lock dates: must greater than contract creation timestamp\"\n );\n\n // locked date must be multiples of 14 days / TWO_WEEKS\n require(\n (lockedTS - kickoffTS) % TWO_WEEKS == 0,\n \"Invalid lock dates: not multiples of 14 days\"\n );\n\n // locked date must not exceed the MAX_DURATION\n if (lockedTS > block.timestamp) {\n require(\n lockedTS - block.timestamp <= MAX_DURATION,\n \"Invalid lock dates: exceed max duration\"\n );\n }\n\n // the value must not exceed the total staked at the given locked date\n uint32 nStakeCheckpoints = numTotalStakingCheckpoints[lockedTS];\n uint96 totalStaked = totalStakingCheckpoints[lockedTS][nStakeCheckpoints - 1].stake;\n require(\n value <= totalStaked,\n \"Invalid stake amount: greater than the total staked for given date\"\n );\n\n uint32 nCheckpoints = numVestingCheckpoints[lockedTS];\n uint32 blockNumber;\n\n Checkpoint memory recentCP = vestingCheckpoints[lockedTS][nCheckpoints - 1];\n if (nCheckpoints == 0) blockNumber = uint32(block.number) - 1;\n else blockNumber = recentCP.fromBlock + 1;\n\n vestingCheckpoints[lockedTS][nCheckpoints] = Checkpoint(blockNumber, value);\n numVestingCheckpoints[lockedTS] = nCheckpoints + 1;\n\n emit VestingStakeSet(lockedTS, value);\n }\n\n /**\n * @notice Determine the prior number of stake for an account until a\n * certain lock date as of a block number.\n * @dev Block number must be a finalized block or else this function\n * will revert to prevent misinformation.\n * @param account The address of the account to check.\n * @param date The lock date. Adjusted to the next valid lock date, if necessary.\n * @param blockNumber The block number to get the vote balance at.\n * @return The number of votes the account had as of the given block.\n * */\n function getPriorUserStakeByDate(\n address account,\n uint256 date,\n uint256 blockNumber\n ) external view returns (uint96) {\n uint96 priorStake = _getPriorUserStakeByDate(account, date, blockNumber);\n // @dev we need to modify function in order to workaround issue with Vesting.withdrawTokens:\n //\t\treturn 1 instead of 0 if message sender is a contract.\n if (priorStake == 0 && _isVestingContract(msg.sender)) {\n priorStake = 1;\n }\n return priorStake;\n }\n\n /*************************** Weighted Vesting Stake computation for fee sharing *******************************/\n\n /**\n * @notice Determine the prior weighted vested amount for an account as of a block number.\n * Iterate through checkpoints adding up voting power.\n * @dev Block number must be a finalized block or else this function will\n * revert to prevent misinformation.\n * Used for fee sharing, not voting.\n * TODO: WeightedStaking::getPriorVestingWeightedStake is using the variable name \"votes\"\n * to add up token stake, and that could be misleading.\n *\n * @param blockNumber The block number to get the vote balance at.\n * @param date The staking date to compute the power for.\n * @return The weighted stake the account had as of the given block.\n * */\n function getPriorVestingWeightedStake(uint256 blockNumber, uint256 date)\n external\n view\n returns (uint96 votes)\n {\n /// @dev If date is not an exact break point, start weight computation from the previous break point (alternative would be the next).\n uint256 start = _timestampToLockDate(date);\n uint256 end = start + MAX_DURATION;\n\n /// @dev Max 78 iterations.\n for (uint256 i = start; i <= end; i += TWO_WEEKS) {\n uint96 weightedStake = _weightedVestingStakeByDate(i, start, blockNumber);\n if (weightedStake > 0) {\n votes = add96(votes, weightedStake, \"overflow on total weight\"); // WS15\n }\n }\n }\n\n /**\n * @notice Compute the voting power for a specific date.\n * Power = stake * weight\n * @param date The staking date to compute the power for. Adjusted to the previous valid lock date, if necessary.\n * @param startDate The date for which we need to know the power of the stake. Adjusted to the previous valid lock date, if necessary.\n * @param blockNumber The block number, needed for checkpointing.\n * @return The stacking power.\n * */\n function weightedVestingStakeByDate(\n uint256 date,\n uint256 startDate,\n uint256 blockNumber\n ) external view returns (uint96 power) {\n date = _timestampToLockDate(date);\n startDate = _timestampToLockDate(startDate);\n power = _weightedVestingStakeByDate(date, startDate, blockNumber);\n }\n\n /**\n * @notice Compute the voting power for a specific date.\n * Power = stake * weight\n * @param date The staking date to compute the power for.\n * @param startDate The date for which we need to know the power of the stake.\n * @param blockNumber The block number, needed for checkpointing.\n * @return The stacking power.\n * */\n function _weightedVestingStakeByDate(\n uint256 date,\n uint256 startDate,\n uint256 blockNumber\n ) internal view returns (uint96 power) {\n uint96 staked = _getPriorVestingStakeByDate(date, blockNumber);\n if (staked > 0) {\n uint96 weight = _computeWeightByDate(date, startDate);\n power = mul96(staked, weight, \"mul oveflow\") / WEIGHT_FACTOR; // WS16\n } else {\n power = 0;\n }\n }\n\n /**\n * @notice Determine the prior number of vested stake for an account until a\n * certain lock date as of a block number.\n * @dev Block number must be a finalized block or else this function\n * will revert to prevent misinformation.\n * @param date The lock date. Adjusted to the next valid lock date, if necessary.\n * @param blockNumber The block number to get the vote balance at.\n * @return The number of votes the account had as of the given block.\n * */\n function getPriorVestingStakeByDate(uint256 date, uint256 blockNumber)\n external\n view\n returns (uint96)\n {\n date = _adjustDateForOrigin(date);\n return _getPriorVestingStakeByDate(date, blockNumber);\n }\n\n /**\n * @notice Determine the prior number of vested stake for an account until a\n * \t\tcertain lock date as of a block number.\n * @dev All functions of Staking contract use this internal version,\n * \t\twe need to modify public function in order to workaround issue with Vesting.withdrawTokens:\n * return 1 instead of 0 if message sender is a contract.\n * @param date The lock date.\n * @param blockNumber The block number to get the vote balance at.\n * @return The number of votes the account had as of the given block.\n * */\n function _getPriorVestingStakeByDate(uint256 date, uint256 blockNumber)\n internal\n view\n returns (uint96)\n {\n require(blockNumber < _getCurrentBlockNumber(), \"not determined\"); // WS17\n\n uint32 nCheckpoints = numVestingCheckpoints[date];\n if (nCheckpoints == 0) {\n return 0;\n }\n\n /// @dev First check most recent balance.\n if (vestingCheckpoints[date][nCheckpoints - 1].fromBlock <= blockNumber) {\n return vestingCheckpoints[date][nCheckpoints - 1].stake;\n }\n\n /// @dev Next check implicit zero balance.\n if (vestingCheckpoints[date][0].fromBlock > blockNumber) {\n return 0;\n }\n\n uint32 lower = 0;\n uint32 upper = nCheckpoints - 1;\n while (upper > lower) {\n uint32 center = upper - (upper - lower) / 2; /// @dev ceil, avoiding overflow.\n Checkpoint memory cp = vestingCheckpoints[date][center];\n if (cp.fromBlock == blockNumber) {\n return cp.stake;\n } else if (cp.fromBlock < blockNumber) {\n lower = center;\n } else {\n upper = center - 1;\n }\n }\n return vestingCheckpoints[date][lower].stake;\n }\n\n /**\n * @notice Add vesting contract's code hash to a map of code hashes.\n * @param vesting The address of Vesting contract.\n * @dev We need it to use isVestingContract() function instead of isContract()\n */\n function addContractCodeHash(address vesting) external onlyAuthorized whenNotFrozen {\n bytes32 codeHash = _getCodeHash(vesting);\n vestingCodeHashes[codeHash] = true;\n emit ContractCodeHashAdded(codeHash);\n }\n\n /**\n * @notice Remove vesting contract's code hash to a map of code hashes.\n * @param vesting The address of Vesting contract.\n * @dev We need it to use isVestingContract() function instead of isContract()\n */\n function removeContractCodeHash(address vesting) external onlyAuthorized whenNotFrozen {\n bytes32 codeHash = _getCodeHash(vesting);\n require(vestingCodeHashes[codeHash], \"not a registered vesting code hash\");\n vestingCodeHashes[codeHash] = false;\n emit ContractCodeHashRemoved(codeHash);\n }\n\n /**\n * @notice Return flag whether the given address is a registered vesting contract.\n * @param stakerAddress the address to check\n */\n function isVestingContract(address stakerAddress) external view returns (bool) {\n bool isVesting;\n bytes32 codeHash = _getCodeHash(stakerAddress);\n if (address(vestingRegistryLogic) != address(0)) {\n isVesting = vestingRegistryLogic.isVestingAddress(stakerAddress);\n }\n\n if (isVesting) return true;\n if (vestingCodeHashes[codeHash]) return true;\n return false;\n }\n\n /**\n * @notice Return hash of contract code\n */\n function _getCodeHash(address _contract) internal view returns (bytes32) {\n bytes32 codeHash;\n assembly {\n codeHash := extcodehash(_contract)\n }\n return codeHash;\n }\n\n function getFunctionsList() external pure returns (bytes4[] memory) {\n bytes4[] memory functionsList = new bytes4[](9);\n functionsList[0] = this.setVestingRegistry.selector;\n functionsList[1] = this.setVestingStakes.selector;\n functionsList[2] = this.getPriorUserStakeByDate.selector;\n functionsList[3] = this.getPriorVestingWeightedStake.selector;\n functionsList[4] = this.getPriorVestingStakeByDate.selector;\n functionsList[5] = this.addContractCodeHash.selector;\n functionsList[6] = this.removeContractCodeHash.selector;\n functionsList[7] = this.isVestingContract.selector;\n functionsList[8] = this.weightedVestingStakeByDate.selector;\n return functionsList;\n }\n}\n" + }, + "contracts/governance/Staking/modules/StakingWithdrawModule.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../../proxy/modules/interfaces/IFunctionsList.sol\";\n\nimport \"./shared/CheckpointsShared.sol\";\nimport \"../../../rsk/RSKAddrValidator.sol\";\nimport \"../../Vesting/ITeamVesting.sol\";\nimport \"../../Vesting/IVesting.sol\";\nimport \"./shared/StakingShared.sol\";\n\n/**\n * @title Staking withdrawal functionality module\n **/\ncontract StakingWithdrawModule is IFunctionsList, StakingShared, CheckpointsShared {\n using SafeMath for uint256;\n\n event MaxVestingWithdrawIterationsUpdated(uint256 oldMaxIterations, uint256 newMaxIterations);\n\n /// @dev Struct for direct withdraw function -- to avoid stack too deep issue\n struct VestingConfig {\n address vestingAddress;\n uint256 startDate;\n uint256 endDate;\n uint256 cliff;\n uint256 duration;\n address tokenOwner;\n }\n\n /// @notice An event emitted when staked tokens get withdrawn.\n event StakingWithdrawn(\n address indexed staker,\n uint256 amount,\n uint256 until,\n address indexed receiver,\n bool isGovernance\n );\n\n /// @notice An event emitted when vesting tokens get withdrawn.\n event VestingTokensWithdrawn(address vesting, address receiver);\n\n /// @notice An event emitted when the owner unlocks all tokens.\n event TokensUnlocked(uint256 amount);\n\n /**\n * @notice Withdraw the given amount of tokens if they are unlocked.\n * @param amount The number of tokens to withdraw.\n * @param until The date until which the tokens were staked.\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\n * @dev If until is not a valid lock date, the next lock date after until is used.\n * */\n function withdraw(\n uint96 amount,\n uint256 until,\n address receiver\n ) external whenNotFrozen {\n // adjust until here to avoid adjusting multiple times, and to make sure an adjusted date is passed to\n // _notSameBlockAsStakingCheckpoint\n until = _adjustDateForOrigin(until);\n\n _notSameBlockAsStakingCheckpoint(until, msg.sender);\n\n _withdraw(amount, until, receiver, false);\n // @dev withdraws tokens for lock date 2 weeks later than given lock date if sender is a contract\n //\t\twe need to check block.timestamp here\n _withdrawNext(until, receiver, false);\n }\n\n /**\n * @notice Governance withdraw vesting directly through staking contract.\n * This direct withdraw vesting solves the out of gas issue when there are too many iterations when withdrawing.\n * This function only allows cancelling vesting contract of the TeamVesting type.\n *\n * @param vesting The vesting address.\n * @param receiver The receiving address.\n * @param startFrom The start value for the iterations.\n */\n function cancelTeamVesting(\n address vesting,\n address receiver,\n uint256 startFrom\n ) external onlyAuthorized whenNotFrozen {\n /// require the caller only for team vesting contract.\n require(vestingRegistryLogic.isTeamVesting(vesting), \"Only team vesting allowed\");\n\n _cancelTeamVesting(vesting, receiver, startFrom);\n }\n\n /**\n * @notice Withdraws tokens from the staking contract and forwards them\n * to an address specified by the token owner. Low level function.\n * @dev Once here the caller permission is taken for granted.\n * @param _vesting The vesting address.\n * @param _receiver The receiving address.\n * @param _startFrom The start value for the iterations.\n * or just unlocked tokens (false).\n * */\n function _cancelTeamVesting(\n address _vesting,\n address _receiver,\n uint256 _startFrom\n ) private {\n require(_receiver != address(0), \"receiver address invalid\");\n\n ITeamVesting teamVesting = ITeamVesting(_vesting);\n\n VestingConfig memory vestingConfig =\n VestingConfig(\n _vesting,\n teamVesting.startDate(),\n teamVesting.endDate(),\n teamVesting.cliff(),\n teamVesting.duration(),\n teamVesting.tokenOwner()\n );\n\n /// @dev In the unlikely case that all tokens have been unlocked early,\n /// allow to withdraw all of them, as long as the itrations less than maxVestingWithdrawIterations.\n uint256 end = vestingConfig.endDate;\n\n uint256 defaultStart = vestingConfig.startDate + vestingConfig.cliff;\n\n _startFrom = _startFrom >= defaultStart ? _startFrom : defaultStart;\n\n /// @dev max iterations need to be decreased by 1, otherwise the iteration will always be surplus by 1\n uint256 totalIterationValue =\n (_startFrom + (TWO_WEEKS * (maxVestingWithdrawIterations - 1)));\n uint256 adjustedEnd = end < totalIterationValue ? end : totalIterationValue;\n\n /// @dev Withdraw for each unlocked position.\n for (uint256 i = _startFrom; i <= adjustedEnd; i += TWO_WEEKS) {\n /// @dev Read amount to withdraw.\n uint96 tempStake = _getPriorUserStakeByDate(_vesting, i, block.number - 1);\n\n if (tempStake > 0) {\n /// @dev do governance direct withdraw for team vesting\n _withdrawFromTeamVesting(tempStake, i, _receiver, vestingConfig);\n }\n }\n\n if (adjustedEnd < end) {\n emit TeamVestingPartiallyCancelled(msg.sender, _receiver, adjustedEnd);\n } else {\n emit TeamVestingCancelled(msg.sender, _receiver);\n }\n }\n\n /**\n * @notice Send user' staked tokens to a receiver taking into account punishments.\n * Sovryn encourages long-term commitment and thinking. When/if you unstake before\n * the end of the staking period, a percentage of the original staking amount will\n * be slashed. This amount is also added to the reward pool and is distributed\n * between all other stakers.\n *\n * @param amount The number of tokens to withdraw.\n * @param until The date until which the tokens were staked.\n * Needs to be adjusted to the next valid lock date before calling this function.\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\n * @param isGovernance Whether all tokens (true)\n * or just unlocked tokens (false).\n * */\n function _withdraw(\n uint96 amount,\n uint256 until,\n address receiver,\n bool isGovernance\n ) internal {\n // @dev it's very unlikely some one will have 1/10**18 SOV staked in Vesting contract\n //\t\tthis check is a part of workaround for Vesting.withdrawTokens issue\n if (amount == 1 && _isVestingContract(msg.sender)) {\n return;\n }\n _validateWithdrawParams(msg.sender, amount, until);\n\n /// @dev Determine the receiver.\n if (receiver == address(0)) receiver = msg.sender;\n\n /// @dev Update the checkpoints.\n _decreaseDailyStake(until, amount);\n _decreaseUserStake(msg.sender, until, amount);\n if (_isVestingContract(msg.sender)) _decreaseVestingStake(until, amount);\n _decreaseDelegateStake(delegates[msg.sender][until], until, amount);\n\n /// @dev Early unstaking should be punished.\n if (block.timestamp < until && !allUnlocked && !isGovernance) {\n uint96 punishedAmount = _getPunishedAmount(amount, until);\n amount -= punishedAmount;\n\n /// @dev punishedAmount can be 0 if block.timestamp are very close to 'until'\n if (punishedAmount > 0) {\n require(address(feeSharing) != address(0), \"FeeSharing address wasn't set\"); // S08\n /// @dev Move punished amount to fee sharing.\n /// @dev Approve transfer here and let feeSharing do transfer and write checkpoint.\n SOVToken.approve(address(feeSharing), punishedAmount);\n feeSharing.transferTokens(address(SOVToken), punishedAmount);\n }\n }\n\n /// @dev transferFrom\n bool success = SOVToken.transfer(receiver, amount);\n require(success, \"Token transfer failed\"); // S09\n\n emit StakingWithdrawn(msg.sender, amount, until, receiver, isGovernance);\n }\n\n /**\n * @notice Send user' staked tokens to a receiver.\n * This function is dedicated only for direct withdrawal from staking contract.\n * Currently only being used by cancelTeamVesting()\n *\n * @param amount The number of tokens to withdraw.\n * @param until The date until which the tokens were staked.\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender.\n * @param vestingConfig The vesting config.\n * @dev VestingConfig struct intended to avoid stack too deep issue, and it contains this properties:\n address vestingAddress; // vesting contract address\n uint256 startDate; //start date of vesting\n uint256 endDate; // end date of vesting\n uint256 cliff; // after this time period the tokens begin to unlock\n uint256 duration; // after this period all the tokens will be unlocked\n address tokenOwner; // owner of the vested tokens\n * */\n function _withdrawFromTeamVesting(\n uint96 amount,\n uint256 until,\n address receiver,\n VestingConfig memory vestingConfig\n ) internal {\n address vesting = vestingConfig.vestingAddress;\n\n until = _timestampToLockDate(until);\n _validateWithdrawParams(vesting, amount, until);\n\n /// @dev Update the checkpoints.\n _decreaseDailyStake(until, amount);\n _decreaseUserStake(vesting, until, amount);\n\n _decreaseVestingStake(until, amount);\n _decreaseDelegateStake(delegates[vesting][until], until, amount);\n\n /// @dev transferFrom\n bool success = SOVToken.transfer(receiver, amount);\n require(success, \"Token transfer failed\"); // S09\n\n emit StakingWithdrawn(vesting, amount, until, receiver, true);\n }\n\n // @dev withdraws tokens for lock date 2 weeks later than given lock date\n function _withdrawNext(\n uint256 until,\n address receiver,\n bool isGovernance\n ) internal {\n if (_isVestingContract(msg.sender)) {\n // nextLock needs to be adjusted to the next valid lock date to make sure we don't accidentally\n // withdraw stakes that are in the future and would get slashed (if until is not\n // a valid lock date). but until is already handled in the withdraw function\n uint256 nextLock = until.add(TWO_WEEKS);\n if (isGovernance || block.timestamp >= nextLock) {\n uint96 stakes = _getPriorUserStakeByDate(msg.sender, nextLock, block.number - 1);\n if (stakes > 0) {\n _withdraw(stakes, nextLock, receiver, isGovernance);\n }\n }\n }\n }\n\n /**\n * @notice Get available and punished amount for withdrawing.\n * @param amount The number of tokens to withdraw.\n * @param until The date until which the tokens were staked. Adjusted to the next valid lock date, if necessary.\n * @return Amount to withraw and penalty amount\n * */\n function getWithdrawAmounts(uint96 amount, uint256 until)\n external\n view\n returns (uint96, uint96)\n {\n until = _adjustDateForOrigin(until);\n _validateWithdrawParams(msg.sender, amount, until);\n uint96 punishedAmount = _getPunishedAmount(amount, until);\n return (amount - punishedAmount, punishedAmount);\n }\n\n /**\n * @notice Get punished amount for withdrawing.\n * @param amount The number of tokens to withdraw.\n * @param until The date until which the tokens were staked.\n * */\n function _getPunishedAmount(uint96 amount, uint256 until) internal view returns (uint96) {\n uint256 date = _timestampToLockDate(block.timestamp);\n uint96 weight = _computeWeightByDate(until, date); /// @dev (10 - 1) * WEIGHT_FACTOR\n weight = weight * weightScaling;\n return (amount * weight) / WEIGHT_FACTOR / 100;\n }\n\n /**\n * @notice Validate withdraw parameters.\n * @param account Address to be validated.\n * @param amount The number of tokens to withdraw.\n * @param until The date until which the tokens were staked.\n * */\n function _validateWithdrawParams(\n address account,\n uint96 amount,\n uint256 until\n ) internal view {\n require(amount > 0, \"Amount of tokens to withdraw must be > 0\"); // S10\n uint96 balance = _getPriorUserStakeByDate(account, until, block.number - 1);\n require(amount <= balance, \"Staking::withdraw: not enough balance\"); // S11\n }\n\n /**\n * @notice Allow the owner to unlock all tokens in case the staking contract\n * is going to be replaced\n * Note: Not reversible on purpose. once unlocked, everything is unlocked.\n * The owner should not be able to just quickly unlock to withdraw his own\n * tokens and lock again.\n * @dev Last resort.\n * */\n function unlockAllTokens() external onlyOwner whenNotFrozen {\n allUnlocked = true;\n emit TokensUnlocked(SOVToken.balanceOf(address(this)));\n }\n\n /**\n * @dev set max withdraw iterations.\n *\n * @param newMaxIterations new max iterations value.\n */\n function setMaxVestingWithdrawIterations(uint256 newMaxIterations)\n external\n onlyAuthorized\n whenNotFrozen\n {\n require(newMaxIterations > 0, \"Invalid max iterations\");\n emit MaxVestingWithdrawIterationsUpdated(maxVestingWithdrawIterations, newMaxIterations);\n maxVestingWithdrawIterations = newMaxIterations;\n }\n\n /**\n * @notice Withdraw tokens for vesting contract.\n * @param vesting The address of Vesting contract.\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\n * @dev Can be invoked only by whitelisted contract passed to governanceWithdrawVesting.\n * @dev This function is dedicated only to support backward compatibility for sovryn ecosystem that has been implementing this staking contract.\n * @dev Sovryn protocol will use the cancelTeamVesting function for the withdrawal moving forward.\n * https://github.com/DistributedCollective/Sovryn-smart-contracts/blob/4bbfe5bd0311ca71e4ef0e3af810d3791d8e4061/contracts/governance/Staking/modules/StakingWithdrawModule.sol#L78\n * */\n function governanceWithdrawVesting(address vesting, address receiver)\n public\n onlyAuthorized\n whenNotFrozen\n {\n vestingWhitelist[vesting] = true;\n ITeamVesting(vesting).governanceWithdrawTokens(receiver);\n vestingWhitelist[vesting] = false;\n\n emit VestingTokensWithdrawn(vesting, receiver);\n }\n\n /**\n * @notice Withdraw the given amount of tokens.\n * @param amount The number of tokens to withdraw.\n * @param until The date until which the tokens were staked.\n * @param receiver The receiver of the tokens. If not specified, send to the msg.sender\n * @dev Can be invoked only by whitelisted contract passed to governanceWithdrawVesting\n * */\n function governanceWithdraw(\n uint96 amount,\n uint256 until,\n address receiver\n ) external whenNotFrozen {\n require(vestingWhitelist[msg.sender], \"unauthorized\"); // S07\n\n _notSameBlockAsStakingCheckpoint(until, msg.sender);\n\n _withdraw(amount, until, receiver, true);\n // @dev withdraws tokens for lock date 2 weeks later than given lock date if sender is a contract\n //\t\twe don't need to check block.timestamp here\n _withdrawNext(until, receiver, true);\n }\n\n function getFunctionsList() external pure returns (bytes4[] memory) {\n bytes4[] memory functionsList = new bytes4[](7);\n functionsList[0] = this.withdraw.selector;\n functionsList[1] = this.cancelTeamVesting.selector;\n functionsList[2] = this.getWithdrawAmounts.selector;\n functionsList[3] = this.unlockAllTokens.selector;\n functionsList[4] = this.setMaxVestingWithdrawIterations.selector;\n functionsList[5] = this.governanceWithdraw.selector;\n functionsList[6] = this.governanceWithdrawVesting.selector;\n return functionsList;\n }\n}\n" + }, + "contracts/governance/Staking/modules/WeightedStakingModule.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./shared/CheckpointsShared.sol\";\nimport \"./shared/StakingShared.sol\";\nimport \"../../../proxy/modules/interfaces/IFunctionsList.sol\";\n\n/**\n * @title Weighted Staking module contract.\n * @notice Implements getters for weighted staking functionality\n * */\ncontract WeightedStakingModule is IFunctionsList, StakingShared, CheckpointsShared {\n /*************************** User Weighted Stake computation for fee sharing *******************************/\n\n /**\n * @notice Determine the prior weighted stake for an account as of a block number.\n * Iterate through checkpoints adding up voting power.\n * @dev Block number must be a finalized block or else this function will\n * revert to prevent misinformation.\n * Used for fee sharing, not voting.\n *\n * @param account The address of the account to check.\n * @param blockNumber The block number to get the vote balance at.\n * @param date The start date/timestamp from which to calculate the weighted stake.\n * @return The weighted stake the account had as of the given block.\n * */\n function getPriorWeightedStake(\n address account,\n uint256 blockNumber,\n uint256 date\n ) external view returns (uint96 priorWeightedStake) {\n return _getPriorWeightedStake(account, blockNumber, date);\n }\n\n function _getPriorWeightedStake(\n address account,\n uint256 blockNumber,\n uint256 date\n ) internal view returns (uint96 priorWeightedStake) {\n /// @dev If date is not an exact break point, start weight computation from the previous break point (alternative would be the next).\n uint256 start = _timestampToLockDate(date);\n uint256 end = start + MAX_DURATION;\n\n /// @dev Max 78 iterations.\n for (uint256 i = start; i <= end; i += TWO_WEEKS) {\n uint96 weightedStake = _weightedStakeByDate(account, i, start, blockNumber);\n if (weightedStake > 0) {\n priorWeightedStake = add96(\n priorWeightedStake,\n weightedStake,\n \"overflow on total weight calc\"\n ); // WS12\n }\n }\n }\n\n /**\n * @notice Compute the voting power for a specific date.\n * Power = stake * weight\n * @param account The user address.\n * @param date The staking date to compute the power for. Adjusted to the previous valid lock date, if necessary.\n * @param startDate The date for which we need to know the power of the stake. Adjusted to the previous valid lock date, if necessary.\n * @param blockNumber The block number, needed for checkpointing.\n * @return The staking power.\n * */\n function weightedStakeByDate(\n address account,\n uint256 date,\n uint256 startDate,\n uint256 blockNumber\n ) external view returns (uint96 power) {\n date = _timestampToLockDate(date);\n startDate = _timestampToLockDate(startDate);\n return _weightedStakeByDate(account, date, startDate, blockNumber);\n }\n\n /**\n * @notice Compute the voting power for a specific date.\n * Power = stake * weight\n * @param account The user address.\n * @param date The staking date to compute the power for.\n * @param startDate The date for which we need to know the power of the stake.\n * @param blockNumber The block number, needed for checkpointing.\n * @return The staking power.\n * */\n function _weightedStakeByDate(\n address account,\n uint256 date,\n uint256 startDate,\n uint256 blockNumber\n ) internal view returns (uint96 power) {\n uint96 staked = _getPriorUserStakeByDate(account, date, blockNumber);\n if (staked > 0) {\n uint96 weight = _computeWeightByDate(date, startDate);\n power = mul96(staked, weight, \"mul overflow\") / WEIGHT_FACTOR; // WS13\n } else {\n power = 0;\n }\n }\n\n /**\n * @notice Compute the weight for a specific date.\n * @param date The unlocking date.\n * @param startDate We compute the weight for the tokens staked until 'date' on 'startDate'.\n * @return The weighted stake the account had as of the given block.\n * */\n function computeWeightByDate(uint256 date, uint256 startDate)\n external\n pure\n returns (uint96 weight)\n {\n return _computeWeightByDate(date, startDate);\n }\n\n function getFunctionsList() external pure returns (bytes4[] memory) {\n bytes4[] memory functionsList = new bytes4[](3);\n functionsList[0] = this.getPriorWeightedStake.selector;\n functionsList[1] = this.weightedStakeByDate.selector;\n functionsList[2] = this.computeWeightByDate.selector;\n return functionsList;\n }\n}\n" + }, + "contracts/governance/Staking/SafeMath96.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\n/**\n * @title SafeMath96 contract.\n * @notice Improved Solidity's arithmetic operations with added overflow checks.\n * @dev SafeMath96 uses uint96, unsigned integers of 96 bits length, so every\n * integer from 0 to 2^96-1 can be operated.\n *\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\n * in bugs, because programmers usually assume that an overflow raises an\n * error, which is the standard behavior in high level programming languages.\n * SafeMath restores this intuition by reverting the transaction when an\n * operation overflows.\n *\n * Using this contract instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n * */\ncontract SafeMath96 {\n function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {\n require(n < 2**32, errorMessage);\n return uint32(n);\n }\n\n function safe64(uint256 n, string memory errorMessage) internal pure returns (uint64) {\n require(n < 2**64, errorMessage);\n return uint64(n);\n }\n\n function safe96(uint256 n, string memory errorMessage) internal pure returns (uint96) {\n require(n < 2**96, errorMessage);\n return uint96(n);\n }\n\n /**\n * @notice Adds two unsigned integers, reverting on overflow.\n * @dev Counterpart to Solidity's `+` operator.\n * @param a First integer.\n * @param b Second integer.\n * @param errorMessage The revert message on overflow.\n * @return The safe addition a+b.\n * */\n function add96(\n uint96 a,\n uint96 b,\n string memory errorMessage\n ) internal pure returns (uint96) {\n uint96 c = a + b;\n require(c >= a, errorMessage);\n return c;\n }\n\n /**\n * @notice Substracts two unsigned integers, reverting on underflow.\n * @dev Counterpart to Solidity's `-` operator.\n * @param a First integer.\n * @param b Second integer.\n * @param errorMessage The revert message on underflow.\n * @return The safe substraction a-b.\n * */\n function sub96(\n uint96 a,\n uint96 b,\n string memory errorMessage\n ) internal pure returns (uint96) {\n require(b <= a, errorMessage);\n return a - b;\n }\n\n /**\n * @notice Multiplies two unsigned integers, reverting on overflow.\n * @dev Counterpart to Solidity's `*` operator.\n * @param a First integer.\n * @param b Second integer.\n * @param errorMessage The revert message on overflow.\n * @return The safe product a*b.\n * */\n function mul96(\n uint96 a,\n uint96 b,\n string memory errorMessage\n ) internal pure returns (uint96) {\n if (a == 0) {\n return 0;\n }\n\n uint96 c = a * b;\n require(c / a == b, errorMessage);\n\n return c;\n }\n\n /**\n * @notice Divides two unsigned integers, reverting on overflow.\n * @dev Counterpart to Solidity's `/` operator.\n * @param a First integer.\n * @param b Second integer.\n * @param errorMessage The revert message on overflow.\n * @return The safe division a/b.\n * */\n function div96(\n uint96 a,\n uint96 b,\n string memory errorMessage\n ) internal pure returns (uint96) {\n // Solidity only automatically asserts when dividing by 0\n require(b > 0, errorMessage);\n uint96 c = a / b;\n // assert(a == b * c + a % b); // There is no case in which this doesn't hold\n\n return c;\n }\n}\n" + }, + "contracts/governance/Staking/StakingProxy.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./modules/shared/StakingStorageShared.sol\";\nimport \"../../proxy/UpgradableProxy.sol\";\n\n/**\n * @title Staking Proxy contract.\n * @dev Staking contract should be upgradable, use UpgradableProxy.\n * StakingStorage is deployed with the upgradable functionality\n * by using this contract instead, that inherits from UpgradableProxy\n * the possibility of being enhanced and re-deployed.\n * */\ncontract StakingProxy is StakingStorageShared, UpgradableProxy {\n /**\n * @notice Construct a new staking contract.\n * @param SOV The address of the SOV token address.\n */\n constructor(address SOV) public {\n SOVToken = IERC20(SOV);\n kickoffTS = block.timestamp;\n }\n}\n" + }, + "contracts/governance/StakingRewards/StakingRewards.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./StakingRewardsStorage.sol\";\nimport \"../../openzeppelin/SafeMath.sol\";\nimport \"../../openzeppelin/Address.sol\";\n\n/**\n * @title Staking Rewards Contract.\n * @notice This is a trial incentive program.\n * In this, the SOV emitted and becoming liquid from the Adoption Fund could be utilized\n * to offset the higher APY's offered for Liquidity Mining events.\n * Vesting contract stakes are excluded from these rewards.\n * Only wallets which have staked previously liquid SOV are eligible for these rewards.\n * Tokenholders who stake their SOV receive staking rewards, a pro-rata share\n * of the revenue that the platform generates from various transaction fees\n * plus revenues from stakers who have a portion of their SOV slashed for\n * early unstaking.\n * */\ncontract StakingRewards is StakingRewardsStorage {\n using SafeMath for uint256;\n\n /// @notice Emitted when SOV is withdrawn\n /// @param receiver The address which recieves the SOV\n /// @param amount The amount withdrawn from the Smart Contract\n event RewardWithdrawn(address indexed receiver, uint256 amount);\n\n /**\n * @notice Replacement of constructor by initialize function for Upgradable Contracts\n * This function will be called only once by the owner.\n * @param _SOV SOV token address\n * @param _staking StakingProxy address should be passed\n * */\n function initialize(address _SOV, IStaking _staking) external onlyOwner {\n require(_SOV != address(0), \"Invalid SOV Address.\");\n require(Address.isContract(_SOV), \"_SOV not a contract\");\n SOV = IERC20(_SOV);\n staking = _staking;\n startTime = staking.timestampToLockDate(block.timestamp);\n setMaxDuration(15 * TWO_WEEKS);\n deploymentBlock = _getCurrentBlockNumber();\n }\n\n /**\n * @notice Stops the current rewards program.\n * @dev All stakes existing on the contract at the point in time of\n * cancellation continue accruing rewards until the end of the staking\n * period being rewarded\n * */\n function stop() external onlyOwner {\n require(stopBlock == 0, \"Already stopped\");\n stopBlock = _getCurrentBlockNumber();\n }\n\n /**\n * @notice Collect rewards\n * @dev User calls this function to collect SOV staking rewards as per the SIP-0024 program.\n * The weighted stake is calculated using getPriorWeightedStake. Block number sent to the functon\n * must be a finalised block, hence we deduct 1 from the current block. User is only allowed to withdraw\n * after intervals of 14 days.\n * @param restartTime The time from which the staking rewards calculation shall restart.\n * The issue is that we can only run for a max duration and if someone stakes for the\n * first time after the max duration is over, the reward will always return 0. Thus, we need to restart\n * from the duration that elapsed without generating rewards.\n * */\n function collectReward(uint256 restartTime) external {\n (uint256 withdrawalTime, uint256 amount) = getStakerCurrentReward(true, restartTime);\n require(withdrawalTime > 0 && amount > 0, \"no valid reward\");\n withdrawals[msg.sender] = withdrawalTime;\n _payReward(msg.sender, amount);\n }\n\n /**\n * @notice Withdraws all token from the contract by Multisig.\n * @param _receiverAddress The address where the tokens has to be transferred.\n */\n function withdrawTokensByOwner(address _receiverAddress) external onlyOwner {\n uint256 value = SOV.balanceOf(address(this));\n _transferSOV(_receiverAddress, value);\n }\n\n /**\n * @notice Changes average block time - based on blockchain\n * @dev If average block time significantly changes, we can update it here and use for block number calculation\n */\n function setAverageBlockTime(uint256 _averageBlockTime) external onlyOwner {\n averageBlockTime = _averageBlockTime;\n }\n\n /**\n * @notice This function computes the last staking checkpoint and calculates the corresponding\n * block number using the average block time which is then added to the mapping `checkpointBlockDetails`.\n */\n function setBlock() external {\n uint256 lastCheckpointTime = staking.timestampToLockDate(block.timestamp);\n _setBlock(lastCheckpointTime);\n }\n\n /**\n * @notice This function computes the block number using the average block time for a given historical\n * checkpoint which is added to the mapping `checkpointBlockDetails`.\n * @param _time Exact staking checkpoint time\n */\n function setHistoricalBlock(uint256 _time) external {\n _setBlock(_time);\n }\n\n /**\n * @notice Sets the max duration\n * @dev Rewards can be collected for a maximum duration at a time. This\n * is to avoid Block Gas Limit failures. Setting it zero would mean that it will loop\n * through the entire duration since the start of rewards program.\n * It should ideally be set to a value, for which the rewards can be easily processed.\n * @param _duration Max duration for which rewards can be collected at a go (in seconds)\n * */\n function setMaxDuration(uint256 _duration) public onlyOwner {\n maxDuration = _duration;\n }\n\n /**\n * @notice Internal function to calculate weighted stake\n * @dev If the rewards program is stopped, the user will still continue to\n * earn till the end of staking period based on the stop block.\n * @param _staker Staker address\n * @param _block Last finalised block\n * @param _date The date to compute prior weighted stakes\n * @return The weighted stake\n * */\n function _computeRewardForDate(\n address _staker,\n uint256 _block,\n uint256 _date\n ) internal view returns (uint256 weightedStake) {\n weightedStake = staking.getPriorWeightedStake(_staker, _block, _date);\n if (stopBlock > 0 && stopBlock < _block) {\n uint256 previousWeightedStake =\n staking.getPriorWeightedStake(_staker, stopBlock, _date);\n if (previousWeightedStake < weightedStake) {\n weightedStake = previousWeightedStake;\n }\n }\n }\n\n /**\n * @notice Internal function to pay rewards\n * @dev Base rate is annual, but we pay interest for 14 days,\n * which is 1/26 of one staking year (1092 days)\n * @param _staker User address\n * @param amount the reward amount\n * */\n function _payReward(address _staker, uint256 amount) internal {\n require(SOV.balanceOf(address(this)) >= amount, \"not enough funds to reward user\");\n claimedBalances[_staker] = claimedBalances[_staker].add(amount);\n _transferSOV(_staker, amount);\n }\n\n /**\n * @notice transfers SOV tokens to given address\n * @param _receiver the address of the SOV receiver\n * @param _amount the amount to be transferred\n */\n function _transferSOV(address _receiver, uint256 _amount) internal {\n require(_amount != 0, \"amount invalid\");\n require(SOV.transfer(_receiver, _amount), \"transfer failed\");\n emit RewardWithdrawn(_receiver, _amount);\n }\n\n /**\n * @notice Determine the current Block Number\n * @dev This is segregated from the _getPriorUserStakeByDate function to better test\n * advancing blocks functionality using Mock Contracts\n * */\n function _getCurrentBlockNumber() internal view returns (uint256) {\n return block.number;\n }\n\n /**\n * @notice Internal function to calculate and set block\n * */\n function _setBlock(uint256 _checkpointTime) internal {\n uint256 currentTS = block.timestamp;\n uint256 lastFinalisedBlock = _getCurrentBlockNumber() - 1;\n require(checkpointBlockDetails[_checkpointTime] == 0, \"block number already set\");\n uint256 checkpointBlock =\n lastFinalisedBlock.sub(((currentTS.sub(_checkpointTime)).div(averageBlockTime)));\n checkpointBlockDetails[_checkpointTime] = checkpointBlock;\n }\n\n /**\n * @notice Get staker's current accumulated reward\n * @dev The collectReward() function internally calls this function to calculate reward amount\n * @param considerMaxDuration True: Runs for the maximum duration - used in tx not to run out of gas\n * False - to query total rewards\n * @param restartTime The time from which the staking rewards calculation shall restart.\n * @return The timestamp of last withdrawal\n * @return The accumulated reward\n */\n function getStakerCurrentReward(bool considerMaxDuration, uint256 restartTime)\n public\n view\n returns (uint256 lastWithdrawalInterval, uint256 amount)\n {\n uint256 weightedStake;\n uint256 lastFinalisedBlock = _getCurrentBlockNumber() - 1;\n uint256 currentTS = block.timestamp;\n uint256 duration;\n address staker = msg.sender;\n uint256 lastWithdrawal = withdrawals[staker];\n\n uint256 lastStakingInterval = staking.timestampToLockDate(currentTS);\n lastWithdrawalInterval = lastWithdrawal > 0 ? lastWithdrawal : startTime;\n if (lastStakingInterval <= lastWithdrawalInterval) return (0, 0);\n /* Normally the restart time is 0. If this function returns a valid lastWithdrawalInterval\n\t\tand zero amount - that means there were no valid rewards for that period. So the new period must start\n\t\tfrom the end of the last interval or till the time no rewards are accumulated i.e. restartTime */\n if (restartTime >= lastWithdrawalInterval) {\n uint256 latestRestartTime = staking.timestampToLockDate(restartTime);\n lastWithdrawalInterval = latestRestartTime;\n }\n\n if (considerMaxDuration) {\n uint256 addedMaxDuration = lastWithdrawalInterval.add(maxDuration);\n duration = addedMaxDuration < currentTS\n ? staking.timestampToLockDate(addedMaxDuration)\n : lastStakingInterval;\n } else {\n duration = lastStakingInterval;\n }\n for (uint256 i = lastWithdrawalInterval; i < duration; i += TWO_WEEKS) {\n uint256 referenceBlock = checkpointBlockDetails[i];\n if (referenceBlock == 0) {\n referenceBlock = lastFinalisedBlock.sub(\n ((currentTS.sub(i)).div(averageBlockTime))\n );\n }\n if (referenceBlock < deploymentBlock) referenceBlock = deploymentBlock;\n weightedStake = weightedStake.add(_computeRewardForDate(staker, referenceBlock, i));\n }\n lastWithdrawalInterval = duration;\n amount = weightedStake.mul(BASE_RATE).div(DIVISOR);\n }\n}\n" + }, + "contracts/governance/StakingRewards/StakingRewardsProxy.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./StakingRewardsStorage.sol\";\nimport \"../../proxy/UpgradableProxy.sol\";\n\n/**\n * @title StakingRewards Proxy contract.\n * @dev StakingRewards contract should be upgradable. Used UpgradableProxy.\n * StakingRewardsStorage is deployed with the upgradable functionality\n * by using this contract instead, that inherits from UpgradableProxy with\n * the possibility of being enhanced and re-deployed.\n * */\ncontract StakingRewardsProxy is StakingRewardsStorage, UpgradableProxy {\n\n}\n" + }, + "contracts/governance/StakingRewards/StakingRewardsStorage.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../interfaces/IERC20.sol\";\nimport \"../Staking/interfaces/IStaking.sol\";\nimport \"../../openzeppelin/Ownable.sol\";\n\n/**\n * @title Staking Rewards Storage Contract.\n * @notice Just the storage part of staking rewards contract, no functions,\n * only constant, variables and required structures (mappings).\n * Used by StackingRewardsProxy.\n *\n * What is SOV staking rewards - SIP-0024?\n * The purpose of the SOV staking rewards - SIP-0024 is to reward,\n * \"marginal stakers\" (ie, stakers by choice, not currently vesting) with liquid SOV\n * at the beginning of each new staking interval.\n * */\ncontract StakingRewardsStorage is Ownable {\n /// @notice The SOV token contract.\n IERC20 public SOV;\n\n ///@notice the staking proxy contract address\n IStaking public staking;\n\n /// @notice 2 weeks in seconds.\n uint256 public constant TWO_WEEKS = 1209600;\n\n /// @notice Annual Base Rate - it is the maximum interest rate(APY)\n uint256 public constant BASE_RATE = 2975;\n\n /// @notice DIVISOR is set as 2600000 = 26 (num periods per year) * 10 (max voting weight) * 10000 (2975 -> 0.2975)\n uint256 public constant DIVISOR = 2600000;\n\n /// @notice Maximum duration to collect rewards at one go\n uint256 public maxDuration;\n\n /// @notice Represents the time when the contract is deployed\n uint256 public startTime;\n\n /// @notice Represents the block when the Staking Rewards pogram is stopped\n uint256 public stopBlock;\n\n /// @notice User Address -> Last Withdrawn Timestamp\n mapping(address => uint256) public withdrawals;\n\n /// @notice User Address -> Claimed Balance\n mapping(address => uint256) public claimedBalances;\n\n /// @notice Represents the block when the StakingRwards Program is started\n uint256 public deploymentBlock;\n\n /// Moved the variables from Initializable contract to resolve issue caused by incorrect Inheritance Order\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /// @notice BlockTime -> BlockNumber for a Staking Checkpoint\n mapping(uint256 => uint256) public checkpointBlockDetails;\n\n /// @notice Average Block Time - making it flexible\n uint256 public averageBlockTime;\n}\n" + }, + "contracts/governance/Timelock.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"./ErrorDecoder.sol\";\n\ninterface ITimelock {\n function delay() external view returns (uint256);\n\n function GRACE_PERIOD() external view returns (uint256);\n\n function acceptAdmin() external;\n\n function queuedTransactions(bytes32 hash) external view returns (bool);\n\n function queueTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external returns (bytes32);\n\n function cancelTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external;\n\n function executeTransaction(\n address target,\n uint256 value,\n string calldata signature,\n bytes calldata data,\n uint256 eta\n ) external payable returns (bytes memory);\n}\n\n/**\n * @title Sovryn Protocol Timelock contract, based on Compound system.\n *\n * @notice This contract lets Sovryn governance system set up its\n * own Time Lock instance to execute transactions proposed through the\n * GovernorAlpha contract instance.\n *\n * The Timelock contract allows its admin (Sovryn governance on\n * GovernorAlpha contract) to add arbitrary function calls to a\n * queue. This contract can only execute a function call if the\n * function call has been in the queue for at least 3 hours.\n *\n * Anytime the Timelock contract makes a function call, it must be the\n * case that the function call was first made public by having been publicly\n * added to the queue at least 3 hours prior.\n *\n * The intention is to provide GovernorAlpha contract the functionality to\n * queue proposal actions. This would mean that any changes made by Sovryn\n * governance of any contract would necessarily come with at least an\n * advanced warning. This makes the Sovryn system follow a “time-delayed,\n * opt-out” upgrade pattern (rather than an “instant, forced” upgrade pattern).\n *\n * Time-delaying admin actions gives users a chance to exit system if its\n * admins become malicious or compromised (or make a change that the users\n * do not like). Downside is that honest admins would be unable\n * to lock down functionality to protect users if a critical bug was found.\n *\n * Delayed transactions reduce the amount of trust required by users of Sovryn\n * and the overall risk for contracts building on top of it, as GovernorAlpha.\n * */\ncontract Timelock is ErrorDecoder, ITimelock {\n using SafeMath for uint256;\n\n uint256 public constant GRACE_PERIOD = 14 days;\n uint256 public constant MINIMUM_DELAY = 3 hours;\n uint256 public constant MAXIMUM_DELAY = 30 days;\n\n address public admin;\n address public pendingAdmin;\n uint256 public delay;\n\n mapping(bytes32 => bool) public queuedTransactions;\n\n event NewAdmin(address indexed newAdmin);\n event NewPendingAdmin(address indexed newPendingAdmin);\n event NewDelay(uint256 indexed newDelay);\n event CancelTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n event ExecuteTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n event QueueTransaction(\n bytes32 indexed txHash,\n address indexed target,\n uint256 value,\n string signature,\n bytes data,\n uint256 eta\n );\n\n /**\n * @notice Function called on instance deployment of the contract.\n * @param admin_ Governance contract address.\n * @param delay_ Time to wait for queued transactions to be executed.\n * */\n constructor(address admin_, uint256 delay_) public {\n require(\n delay_ >= MINIMUM_DELAY,\n \"Timelock::constructor: Delay must exceed minimum delay.\"\n );\n require(\n delay_ <= MAXIMUM_DELAY,\n \"Timelock::setDelay: Delay must not exceed maximum delay.\"\n );\n\n admin = admin_;\n delay = delay_;\n }\n\n /**\n * @notice Fallback function is to react to receiving value (rBTC).\n * */\n function() external payable {}\n\n /**\n * @notice Set a new delay when executing the contract calls.\n * @param delay_ The amount of time to wait until execution.\n * */\n function setDelay(uint256 delay_) public {\n require(msg.sender == address(this), \"Timelock::setDelay: Call must come from Timelock.\");\n require(delay_ >= MINIMUM_DELAY, \"Timelock::setDelay: Delay must exceed minimum delay.\");\n require(\n delay_ <= MAXIMUM_DELAY,\n \"Timelock::setDelay: Delay must not exceed maximum delay.\"\n );\n delay = delay_;\n\n emit NewDelay(delay);\n }\n\n /**\n * @notice Accept a new admin for the timelock.\n * */\n function acceptAdmin() public {\n require(\n msg.sender == pendingAdmin,\n \"Timelock::acceptAdmin: Call must come from pendingAdmin.\"\n );\n admin = msg.sender;\n pendingAdmin = address(0);\n\n emit NewAdmin(admin);\n }\n\n /**\n * @notice Set a new pending admin for the timelock.\n * @param pendingAdmin_ The new pending admin address.\n * */\n function setPendingAdmin(address pendingAdmin_) public {\n require(\n msg.sender == address(this),\n \"Timelock::setPendingAdmin: Call must come from Timelock.\"\n );\n pendingAdmin = pendingAdmin_;\n\n emit NewPendingAdmin(pendingAdmin);\n }\n\n /**\n * @notice Queue a new transaction from the governance contract.\n * @param target The contract to call.\n * @param value The amount to send in the transaction.\n * @param signature The stanndard representation of the function called.\n * @param data The ethereum transaction input data payload.\n * @param eta Estimated Time of Accomplishment. The timestamp that the\n * proposal will be available for execution, set once the vote succeeds.\n * */\n function queueTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) public returns (bytes32) {\n require(msg.sender == admin, \"Timelock::queueTransaction: Call must come from admin.\");\n require(\n eta >= getBlockTimestamp().add(delay),\n \"Timelock::queueTransaction: Estimated execution block must satisfy delay.\"\n );\n\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n queuedTransactions[txHash] = true;\n\n emit QueueTransaction(txHash, target, value, signature, data, eta);\n return txHash;\n }\n\n /**\n * @notice Cancel a transaction.\n * @param target The contract to call.\n * @param value The amount to send in the transaction.\n * @param signature The stanndard representation of the function called.\n * @param data The ethereum transaction input data payload.\n * @param eta Estimated Time of Accomplishment. The timestamp that the\n * proposal will be available for execution, set once the vote succeeds.\n * */\n function cancelTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) public {\n require(msg.sender == admin, \"Timelock::cancelTransaction: Call must come from admin.\");\n\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n queuedTransactions[txHash] = false;\n\n emit CancelTransaction(txHash, target, value, signature, data, eta);\n }\n\n /**\n * @notice Executes a previously queued transaction from the governance.\n * @param target The contract to call.\n * @param value The amount to send in the transaction.\n * @param signature The stanndard representation of the function called.\n * @param data The ethereum transaction input data payload.\n * @param eta Estimated Time of Accomplishment. The timestamp that the\n * proposal will be available for execution, set once the vote succeeds.\n * */\n function executeTransaction(\n address target,\n uint256 value,\n string memory signature,\n bytes memory data,\n uint256 eta\n ) public payable returns (bytes memory) {\n require(msg.sender == admin, \"Timelock::executeTransaction: Call must come from admin.\");\n\n bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));\n require(\n queuedTransactions[txHash],\n \"Timelock::executeTransaction: Transaction hasn't been queued.\"\n );\n require(\n getBlockTimestamp() >= eta,\n \"Timelock::executeTransaction: Transaction hasn't surpassed time lock.\"\n );\n require(\n getBlockTimestamp() <= eta.add(GRACE_PERIOD),\n \"Timelock::executeTransaction: Transaction is stale.\"\n );\n\n queuedTransactions[txHash] = false;\n\n bytes memory callData;\n\n if (bytes(signature).length == 0) {\n callData = data;\n } else {\n callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);\n }\n\n // solium-disable-next-line security/no-call-value\n (bool success, bytes memory returnData) = target.call.value(value)(callData);\n if (!success) {\n if (returnData.length <= ERROR_MESSAGE_SHIFT) {\n revert(\"Timelock::executeTransaction: Transaction execution reverted.\");\n } else {\n revert(_addErrorMessage(\"Timelock::executeTransaction: \", string(returnData)));\n }\n }\n\n emit ExecuteTransaction(txHash, target, value, signature, data, eta);\n\n return returnData;\n }\n\n /**\n * @notice A function used to get the current Block Timestamp.\n * @dev Timestamp of the current block in seconds since the epoch.\n * It is a Unix time stamp. So, it has the complete information about\n * the date, hours, minutes, and seconds (in UTC) when the block was\n * created.\n * */\n function getBlockTimestamp() internal view returns (uint256) {\n // solium-disable-next-line security/no-block-members\n return block.timestamp;\n }\n}\n" + }, + "contracts/governance/Vesting/DevelopmentFund.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../openzeppelin/SafeMath.sol\";\nimport \"../../interfaces/IERC20.sol\";\n\n/**\n * @title A holding contract for Sovryn Development Fund.\n * @author Franklin Richards\n * @notice You can use this contract for timed token release from Dev Fund.\n */\ncontract DevelopmentFund {\n using SafeMath for uint256;\n\n /* Storage */\n\n /// @notice The SOV token contract.\n IERC20 public SOV;\n\n /// @notice The current contract status.\n enum Status { Deployed, Active, Expired }\n Status public status;\n\n /// @notice The owner of the locked tokens (usually Governance).\n address public lockedTokenOwner;\n /// @notice The owner of the unlocked tokens (usually MultiSig).\n address public unlockedTokenOwner;\n /// @notice The emergency transfer wallet/contract.\n address public safeVault;\n /// @notice The new locked token owner waiting to be approved.\n address public newLockedTokenOwner;\n\n /// @notice The last token release timestamp or the time of contract creation.\n uint256 public lastReleaseTime;\n\n /// @notice The release duration array in seconds.\n uint256[] public releaseDuration;\n /// @notice The release token amount.\n uint256[] public releaseTokenAmount;\n\n /* Events */\n\n /// @notice Emitted when the contract is activated.\n event DevelopmentFundActivated();\n\n /// @notice Emitted when the contract is expired due to total token transfer.\n event DevelopmentFundExpired();\n\n /// @notice Emitted when a new locked owner is added to the contract.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _newLockedOwner The address which is added as the new locked owner.\n /// @dev Can only be initiated by the current locked owner.\n event NewLockedOwnerAdded(address indexed _initiator, address indexed _newLockedOwner);\n\n /// @notice Emitted when a new locked owner is approved to the contract.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _oldLockedOwner The address of the previous locked owner.\n /// @param _newLockedOwner The address which is added as the new locked owner.\n /// @dev Can only be initiated by the current unlocked owner.\n event NewLockedOwnerApproved(\n address indexed _initiator,\n address indexed _oldLockedOwner,\n address indexed _newLockedOwner\n );\n\n /// @notice Emitted when a new unlocked owner is updated in the contract.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _newUnlockedOwner The address which is updated as the new unlocked owner.\n /// @dev Can only be initiated by the current locked owner.\n event UnlockedOwnerUpdated(address indexed _initiator, address indexed _newUnlockedOwner);\n\n /// @notice Emitted when a new token deposit is done.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _amount The total amount of token deposited.\n event TokenDeposit(address indexed _initiator, uint256 _amount);\n\n /// @notice Emitted when a new release schedule is created.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _releaseCount The number of releases planned in the schedule.\n event TokenReleaseChanged(address indexed _initiator, uint256 _releaseCount);\n\n /// @notice Emitted when a unlocked owner transfers all the tokens to a safe vault.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _receiver The address which receives this token withdrawn.\n /// @param _amount The total amount of token transferred.\n /// @dev This is done in an emergency situation only to a predetermined wallet by locked token owner.\n event LockedTokenTransferByUnlockedOwner(\n address indexed _initiator,\n address indexed _receiver,\n uint256 _amount\n );\n\n /// @notice Emitted when a unlocked owner withdraws the released tokens.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _amount The total amount of token withdrawn.\n /// @param _releaseCount The total number of releases done based on duration.\n event UnlockedTokenWithdrawalByUnlockedOwner(\n address indexed _initiator,\n uint256 _amount,\n uint256 _releaseCount\n );\n\n /// @notice Emitted when a locked owner transfers all the tokens to a receiver.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _receiver The address which receives this token transfer.\n /// @param _amount The total amount of token transferred.\n /// @dev This is done only by locked token owner.\n event LockedTokenTransferByLockedOwner(\n address indexed _initiator,\n address indexed _receiver,\n uint256 _amount\n );\n\n /* Modifiers */\n\n modifier onlyLockedTokenOwner() {\n require(msg.sender == lockedTokenOwner, \"Only Locked Token Owner can call this.\");\n _;\n }\n\n modifier onlyUnlockedTokenOwner() {\n require(msg.sender == unlockedTokenOwner, \"Only Unlocked Token Owner can call this.\");\n _;\n }\n\n modifier checkStatus(Status s) {\n require(status == s, \"The contract is not in the right state.\");\n _;\n }\n\n /* Functions */\n\n /**\n * @notice Setup the required parameters.\n * @param _SOV The SOV token address.\n * @param _lockedTokenOwner The owner of the locked tokens & contract.\n * @param _safeVault The emergency wallet/contract to transfer token.\n * @param _unlockedTokenOwner The owner of the unlocked tokens.\n * @param _lastReleaseTime If the last release time is to be changed, zero if no change required.\n * @param _releaseDuration The time duration between each release calculated from `lastReleaseTime` in seconds.\n * @param _releaseTokenAmount The amount of token to be released in each duration/interval.\n * @dev Initial release schedule should be verified, error will result in either redeployment or calling changeTokenReleaseSchedule() after init() along with token transfer.\n */\n constructor(\n address _SOV,\n address _lockedTokenOwner,\n address _safeVault,\n address _unlockedTokenOwner,\n uint256 _lastReleaseTime,\n uint256[] memory _releaseDuration,\n uint256[] memory _releaseTokenAmount\n ) public {\n require(_SOV != address(0), \"Invalid SOV Address.\");\n require(_lockedTokenOwner != address(0), \"Locked token & contract owner address invalid.\");\n require(_safeVault != address(0), \"Safe Vault address invalid.\");\n require(_unlockedTokenOwner != address(0), \"Unlocked token address invalid.\");\n\n SOV = IERC20(_SOV);\n lockedTokenOwner = _lockedTokenOwner;\n safeVault = _safeVault;\n unlockedTokenOwner = _unlockedTokenOwner;\n\n lastReleaseTime = _lastReleaseTime;\n /// If last release time passed is zero, then current time stamp will be used as the last release time.\n if (_lastReleaseTime == 0) {\n lastReleaseTime = block.timestamp;\n }\n\n /// Checking if the schedule duration and token allocation length matches.\n require(\n _releaseDuration.length == _releaseTokenAmount.length,\n \"Release Schedule does not match.\"\n );\n\n /// Finally we update the token release schedule.\n releaseDuration = _releaseDuration;\n releaseTokenAmount = _releaseTokenAmount;\n }\n\n /**\n * @notice This function is called once after deployment for token transfer based on schedule.\n * @dev Without calling this function, the contract will not work.\n */\n function init() public checkStatus(Status.Deployed) {\n uint256[] memory _releaseTokenAmount = releaseTokenAmount;\n require(_releaseTokenAmount.length != 0, \"Release Schedule not set.\");\n\n /// Getting the current release schedule total token amount.\n uint256 _releaseTotalTokenAmount;\n for (uint256 amountIndex = 0; amountIndex < _releaseTokenAmount.length; amountIndex++) {\n _releaseTotalTokenAmount = _releaseTotalTokenAmount.add(\n _releaseTokenAmount[amountIndex]\n );\n }\n\n bool txStatus = SOV.transferFrom(msg.sender, address(this), _releaseTotalTokenAmount);\n require(txStatus, \"Not enough token sent to change release schedule.\");\n\n status = Status.Active;\n\n emit DevelopmentFundActivated();\n }\n\n /**\n * @notice Update Locked Token Owner.\n * @param _newLockedTokenOwner The owner of the locked tokens & contract.\n */\n function updateLockedTokenOwner(address _newLockedTokenOwner)\n public\n onlyLockedTokenOwner\n checkStatus(Status.Active)\n {\n require(_newLockedTokenOwner != address(0), \"New locked token owner address invalid.\");\n\n newLockedTokenOwner = _newLockedTokenOwner;\n\n emit NewLockedOwnerAdded(msg.sender, _newLockedTokenOwner);\n }\n\n /**\n * @notice Approve Locked Token Owner.\n * @dev This approval is an added security to avoid development fund takeover by a compromised locked token owner.\n */\n function approveLockedTokenOwner() public onlyUnlockedTokenOwner checkStatus(Status.Active) {\n require(newLockedTokenOwner != address(0), \"No new locked owner added.\");\n\n emit NewLockedOwnerApproved(msg.sender, lockedTokenOwner, newLockedTokenOwner);\n\n lockedTokenOwner = newLockedTokenOwner;\n\n newLockedTokenOwner = address(0);\n }\n\n /**\n * @notice Update Unlocked Token Owner.\n * @param _newUnlockedTokenOwner The new unlocked token owner.\n */\n function updateUnlockedTokenOwner(address _newUnlockedTokenOwner)\n public\n onlyLockedTokenOwner\n checkStatus(Status.Active)\n {\n require(_newUnlockedTokenOwner != address(0), \"New unlocked token owner address invalid.\");\n\n unlockedTokenOwner = _newUnlockedTokenOwner;\n\n emit UnlockedOwnerUpdated(msg.sender, _newUnlockedTokenOwner);\n }\n\n /**\n * @notice Deposit tokens to this contract.\n * @param _amount the amount of tokens deposited.\n * @dev These tokens can be withdrawn/transferred any time by the lockedTokenOwner.\n */\n function depositTokens(uint256 _amount) public checkStatus(Status.Active) {\n require(_amount > 0, \"Amount needs to be bigger than zero.\");\n\n bool txStatus = SOV.transferFrom(msg.sender, address(this), _amount);\n require(txStatus, \"Token transfer was not successful.\");\n\n emit TokenDeposit(msg.sender, _amount);\n }\n\n /**\n * @notice Change the Token release schedule. It creates a completely new schedule, and does not append on the previous one.\n * @param _newLastReleaseTime If the last release time is to be changed, zero if no change required.\n * @param _releaseDuration The time duration between each release calculated from `lastReleaseTime` in seconds.\n * @param _releaseTokenAmount The amount of token to be released in each duration/interval.\n * @dev _releaseDuration and _releaseTokenAmount should be specified in reverse order of release.\n */\n function changeTokenReleaseSchedule(\n uint256 _newLastReleaseTime,\n uint256[] memory _releaseDuration,\n uint256[] memory _releaseTokenAmount\n ) public onlyLockedTokenOwner checkStatus(Status.Active) {\n /// Checking if the schedule duration and token allocation length matches.\n require(\n _releaseDuration.length == _releaseTokenAmount.length,\n \"Release Schedule does not match.\"\n );\n\n /// If the last release time has to be changed, then you can pass a new one here.\n /// Or else, the duration of release will be calculated based on this timestamp.\n /// Even a future timestamp can be mentioned here.\n if (_newLastReleaseTime != 0) {\n lastReleaseTime = _newLastReleaseTime;\n }\n\n /// Checking if the contract have enough token balance for the release.\n uint256 _releaseTotalTokenAmount;\n for (uint256 amountIndex = 0; amountIndex < _releaseTokenAmount.length; amountIndex++) {\n _releaseTotalTokenAmount = _releaseTotalTokenAmount.add(\n _releaseTokenAmount[amountIndex]\n );\n }\n\n /// Getting the current token balance of the contract.\n uint256 remainingTokens = SOV.balanceOf(address(this));\n\n /// If the token balance is not sufficient, then we transfer the change to contract.\n if (remainingTokens < _releaseTotalTokenAmount) {\n bool txStatus =\n SOV.transferFrom(\n msg.sender,\n address(this),\n _releaseTotalTokenAmount.sub(remainingTokens)\n );\n require(txStatus, \"Not enough token sent to change release schedule.\");\n } else if (remainingTokens > _releaseTotalTokenAmount) {\n /// If there are more tokens than required, send the extra tokens back.\n bool txStatus =\n SOV.transfer(msg.sender, remainingTokens.sub(_releaseTotalTokenAmount));\n require(txStatus, \"Token not received by the Locked Owner.\");\n }\n\n /// Finally we update the token release schedule.\n releaseDuration = _releaseDuration;\n releaseTokenAmount = _releaseTokenAmount;\n\n emit TokenReleaseChanged(msg.sender, _releaseDuration.length);\n }\n\n /**\n * @notice Transfers all of the remaining tokens in an emergency situation.\n * @dev This could be called when governance or development fund might be compromised.\n */\n function transferTokensByUnlockedTokenOwner()\n public\n onlyUnlockedTokenOwner\n checkStatus(Status.Active)\n {\n uint256 remainingTokens = SOV.balanceOf(address(this));\n bool txStatus = SOV.transfer(safeVault, remainingTokens);\n require(txStatus, \"Token transfer was not successful. Check receiver address.\");\n status = Status.Expired;\n\n emit LockedTokenTransferByUnlockedOwner(msg.sender, safeVault, remainingTokens);\n emit DevelopmentFundExpired();\n }\n\n /**\n * @notice Withdraws all unlocked/released token.\n * @param _amount The amount to be withdrawn.\n */\n function withdrawTokensByUnlockedTokenOwner(uint256 _amount)\n public\n onlyUnlockedTokenOwner\n checkStatus(Status.Active)\n {\n require(_amount > 0, \"Zero can't be withdrawn.\");\n\n uint256 count; /// To know how many elements to be removed from the release schedule.\n uint256 amount = _amount; /// To know the total amount to be transferred.\n uint256 newLastReleaseTimeMemory = lastReleaseTime; /// Better to use memory than storage.\n uint256 releaseLength = releaseDuration.length.sub(1); /// Also checks if there are any elements in the release schedule.\n\n /// Getting the amount of tokens, the number of releases and calculating the total duration.\n while (\n amount > 0 &&\n newLastReleaseTimeMemory.add(releaseDuration[releaseLength]) < block.timestamp\n ) {\n if (amount >= releaseTokenAmount[releaseLength]) {\n amount = amount.sub(releaseTokenAmount[releaseLength]);\n newLastReleaseTimeMemory = newLastReleaseTimeMemory.add(\n releaseDuration[releaseLength]\n );\n count++;\n } else {\n /// This will be the last case, if correct amount is passed.\n releaseTokenAmount[releaseLength] = releaseTokenAmount[releaseLength].sub(amount);\n amount = 0;\n }\n releaseLength--;\n }\n\n /// Checking to see if atleast a single schedule was reached or not.\n require(count > 0 || amount == 0, \"No release schedule reached.\");\n\n /// If locked token owner tries to send a higher amount that schedule\n uint256 value = _amount.sub(amount);\n\n /// Now clearing up the release schedule.\n releaseDuration.length -= count;\n releaseTokenAmount.length -= count;\n\n /// Updating the last release time.\n lastReleaseTime = newLastReleaseTimeMemory;\n\n /// Sending the amount to unlocked token owner.\n bool txStatus = SOV.transfer(msg.sender, value);\n require(txStatus, \"Token transfer was not successful. Check receiver address.\");\n\n emit UnlockedTokenWithdrawalByUnlockedOwner(msg.sender, value, count);\n }\n\n /**\n * @notice Transfers all of the remaining tokens by the owner maybe for an upgrade.\n * @dev This could be called when the current development fund has to be upgraded.\n * @param _receiver The address which receives this token transfer.\n */\n function transferTokensByLockedTokenOwner(address _receiver)\n public\n onlyLockedTokenOwner\n checkStatus(Status.Active)\n {\n uint256 remainingTokens = SOV.balanceOf(address(this));\n bool txStatus = SOV.transfer(_receiver, remainingTokens);\n require(txStatus, \"Token transfer was not successful. Check receiver address.\");\n status = Status.Expired;\n\n emit LockedTokenTransferByLockedOwner(msg.sender, _receiver, remainingTokens);\n emit DevelopmentFundExpired();\n }\n\n /* Getter Functions */\n\n /**\n * @notice Function to read the current token release duration.\n * @return _currentReleaseDuration The current release duration.\n */\n function getReleaseDuration() public view returns (uint256[] memory _releaseTokenDuration) {\n return releaseDuration;\n }\n\n /**\n * @notice Function to read the current token release amount.\n * @return _currentReleaseTokenAmount The current release token amount.\n */\n function getReleaseTokenAmount()\n public\n view\n returns (uint256[] memory _currentReleaseTokenAmount)\n {\n return releaseTokenAmount;\n }\n}\n" + }, + "contracts/governance/Vesting/fouryear/FourYearVesting.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../../openzeppelin/Ownable.sol\";\nimport \"../../../interfaces/IERC20.sol\";\nimport \"../../IFeeSharingCollector.sol\";\nimport \"../../ApprovalReceiver.sol\";\nimport \"./FourYearVestingStorage.sol\";\nimport \"../../../proxy/UpgradableProxy.sol\";\nimport \"../../../openzeppelin/Address.sol\";\n\n/**\n * @title Four Year Vesting Contract.\n *\n * @notice A four year vesting contract.\n *\n * @dev Vesting contract is upgradable,\n * Make sure the vesting owner is multisig otherwise it will be\n * catastrophic.\n * */\ncontract FourYearVesting is FourYearVestingStorage, UpgradableProxy {\n /**\n * @notice Setup the vesting schedule.\n * @param _logic The address of logic contract.\n * @param _SOV The SOV token address.\n * @param _tokenOwner The owner of the tokens.\n * @param _feeSharingCollector Fee sharing proxy address.\n * @param _extendDurationFor Duration till the unlocked tokens are extended.\n * */\n constructor(\n address _logic,\n address _SOV,\n address _stakingAddress,\n address _tokenOwner,\n address _feeSharingCollector,\n uint256 _extendDurationFor\n ) public {\n require(Address.isContract(_logic), \"_logic not a contract\");\n require(_SOV != address(0), \"SOV address invalid\");\n require(Address.isContract(_SOV), \"_SOV not a contract\");\n require(_stakingAddress != address(0), \"staking address invalid\");\n require(Address.isContract(_stakingAddress), \"_stakingAddress not a contract\");\n require(_tokenOwner != address(0), \"token owner address invalid\");\n require(_feeSharingCollector != address(0), \"feeSharingCollector address invalid\");\n require(Address.isContract(_feeSharingCollector), \"_feeSharingCollector not a contract\");\n require((_extendDurationFor % FOUR_WEEKS) == 0, \"invalid duration\");\n\n _setImplementation(_logic);\n SOV = IERC20(_SOV);\n staking = IStaking(_stakingAddress);\n tokenOwner = _tokenOwner;\n feeSharingCollector = IFeeSharingCollector(_feeSharingCollector);\n maxInterval = 18 * FOUR_WEEKS;\n extendDurationFor = _extendDurationFor;\n }\n\n /**\n * @notice Set address of the implementation - vesting owner.\n * @dev Overriding setImplementation function of UpgradableProxy. The logic can only be\n * modified when both token owner and veting owner approve. Since\n * setImplementation can only be called by vesting owner, we also need to check\n * if the new logic is already approved by the token owner.\n * @param _implementation Address of the implementation. Must match with what is set by token owner.\n * */\n function setImplementation(address _implementation) public onlyProxyOwner {\n require(Address.isContract(_implementation), \"_implementation not a contract\");\n require(newImplementation == _implementation, \"address mismatch\");\n _setImplementation(_implementation);\n newImplementation = address(0);\n }\n}\n" + }, + "contracts/governance/Vesting/fouryear/FourYearVestingFactory.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../../openzeppelin/Ownable.sol\";\nimport \"./FourYearVesting.sol\";\nimport \"./IFourYearVestingFactory.sol\";\n\n/**\n * @title Four Year Vesting Factory: Contract to deploy four year vesting contracts.\n * @notice Factory pattern allows to create multiple instances\n * of the same contract and keep track of them easier.\n * */\ncontract FourYearVestingFactory is IFourYearVestingFactory, Ownable {\n /// @dev Added an event to keep track of the vesting contract created for a token owner\n event FourYearVestingCreated(address indexed tokenOwner, address indexed vestingAddress);\n\n /**\n * @notice Deploys four year vesting contract.\n * @param _SOV the address of SOV token.\n * @param _staking The address of staking contract.\n * @param _tokenOwner The owner of the tokens.\n * @param _feeSharing The address of fee sharing contract.\n * @param _vestingOwnerMultisig The address of an owner of vesting contract.\n * @dev _vestingOwnerMultisig should ALWAYS be multisig.\n * @param _fourYearVestingLogic The implementation contract.\n * @param _extendDurationFor Duration till the unlocked tokens are extended.\n * @return The four year vesting contract address.\n * */\n function deployFourYearVesting(\n address _SOV,\n address _staking,\n address _tokenOwner,\n address _feeSharing,\n address _vestingOwnerMultisig,\n address _fourYearVestingLogic,\n uint256 _extendDurationFor\n ) external onlyOwner returns (address) {\n address fourYearVesting =\n address(\n new FourYearVesting(\n _fourYearVestingLogic,\n _SOV,\n _staking,\n _tokenOwner,\n _feeSharing,\n _extendDurationFor\n )\n );\n Ownable(fourYearVesting).transferOwnership(_vestingOwnerMultisig);\n emit FourYearVestingCreated(_tokenOwner, fourYearVesting);\n return fourYearVesting;\n }\n}\n" + }, + "contracts/governance/Vesting/fouryear/FourYearVestingLogic.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./IFourYearVesting.sol\";\nimport \"../../ApprovalReceiver.sol\";\nimport \"./FourYearVestingStorage.sol\";\nimport \"../../../openzeppelin/SafeMath.sol\";\n\n/**\n * @title Four Year Vesting Logic contract.\n * @notice Staking, delegating and withdrawal functionality.\n * @dev Deployed by FourYearVestingFactory contract.\n * */\ncontract FourYearVestingLogic is IFourYearVesting, FourYearVestingStorage, ApprovalReceiver {\n using SafeMath for uint256;\n\n /* Events */\n event TokensStaked(address indexed caller, uint256 amount);\n event VotesDelegated(address indexed caller, address delegatee);\n event TokensWithdrawn(address indexed caller, address receiver);\n event DividendsCollected(\n address indexed caller,\n address loanPoolToken,\n address receiver,\n uint32 maxCheckpoints\n );\n event MigratedToNewStakingContract(address indexed caller, address newStakingContract);\n event TokenOwnerChanged(address indexed newOwner, address indexed oldOwner);\n\n /* Modifiers */\n /**\n * @dev Throws if called by any account other than the token owner or the contract owner.\n */\n modifier onlyOwners() {\n require(msg.sender == tokenOwner || isOwner(), \"unauthorized\");\n _;\n }\n\n /**\n * @dev Throws if called by any account other than the token owner.\n */\n modifier onlyTokenOwner() {\n require(msg.sender == tokenOwner, \"unauthorized\");\n _;\n }\n\n /* Functions */\n\n /**\n * @notice Sets the max interval.\n * @param _interval Max interval for which tokens scheduled shall be staked.\n * */\n function setMaxInterval(uint256 _interval) external onlyOwner {\n require(_interval.mod(FOUR_WEEKS) == 0, \"invalid interval\");\n maxInterval = _interval;\n }\n\n /**\n * @notice Stakes tokens according to the vesting schedule.\n * @param _amount The amount of tokens to stake.\n * @param _restartStakeSchedule The time from which staking schedule restarts.\n * The issue is that we can only stake tokens for a max duration. Thus, we need to restart\n * from the lastSchedule.\n * @return lastSchedule The max duration for which tokens were staked.\n * @return remainingAmount The amount outstanding - to be staked.\n * */\n function stakeTokens(uint256 _amount, uint256 _restartStakeSchedule)\n external\n returns (uint256 lastSchedule, uint256 remainingAmount)\n {\n (lastSchedule, remainingAmount) = _stakeTokens(msg.sender, _amount, _restartStakeSchedule);\n }\n\n /**\n * @notice Stakes tokens according to the vesting schedule.\n * @dev This function will be invoked from receiveApproval.\n * @dev SOV.approveAndCall -> this.receiveApproval -> this.stakeTokensWithApproval\n * @param _sender The sender of SOV.approveAndCall\n * @param _amount The amount of tokens to stake.\n * @param _restartStakeSchedule The time from which staking schedule restarts.\n * The issue is that we can only stake tokens for a max duration. Thus, we need to restart\n * from the lastSchedule.\n * @return lastSchedule The max duration for which tokens were staked.\n * @return remainingAmount The amount outstanding - to be staked.\n * */\n function stakeTokensWithApproval(\n address _sender,\n uint256 _amount,\n uint256 _restartStakeSchedule\n ) external onlyThisContract returns (uint256 lastSchedule, uint256 remainingAmount) {\n (lastSchedule, remainingAmount) = _stakeTokens(_sender, _amount, _restartStakeSchedule);\n }\n\n /**\n * @notice Delegate votes from `msg.sender` which are locked until lockDate\n * to `delegatee`.\n * @param _delegatee The address to delegate votes to.\n * */\n function delegate(address _delegatee) external onlyTokenOwner {\n require(_delegatee != address(0), \"delegatee address invalid\");\n uint256 stakingEndDate = endDate;\n /// @dev Withdraw for each unlocked position.\n /// @dev Don't change FOUR_WEEKS to TWO_WEEKS, a lot of vestings already deployed with FOUR_WEEKS\n ///\t\tworkaround found, but it doesn't work with TWO_WEEKS\n for (uint256 i = startDate.add(cliff); i <= stakingEndDate; i += FOUR_WEEKS) {\n staking.delegate(_delegatee, i);\n }\n emit VotesDelegated(msg.sender, _delegatee);\n }\n\n /**\n * @notice Withdraws unlocked tokens from the staking contract and\n * forwards them to an address specified by the token owner.\n * @param receiver The receiving address.\n * */\n function withdrawTokens(address receiver) external onlyTokenOwner {\n _withdrawTokens(receiver, false);\n }\n\n /**\n * @notice Collect dividends from fee sharing proxy.\n * @param _loanPoolToken The loan pool token address.\n * @param _maxCheckpoints Maximum number of checkpoints to be processed.\n * @param _receiver The receiver of tokens or msg.sender\n * */\n function collectDividends(\n address _loanPoolToken,\n uint32 _maxCheckpoints,\n address _receiver\n ) external onlyTokenOwner {\n require(_receiver != address(0), \"receiver address invalid\");\n\n /// @dev Invokes the fee sharing proxy.\n feeSharingCollector.withdraw(_loanPoolToken, _maxCheckpoints, _receiver);\n\n emit DividendsCollected(msg.sender, _loanPoolToken, _receiver, _maxCheckpoints);\n }\n\n /**\n * @notice Change token owner - only vesting owner is allowed to change.\n * @dev Modifies token owner. This must be followed by approval\n * from token owner.\n * @param _newTokenOwner Address of new token owner.\n * */\n function changeTokenOwner(address _newTokenOwner) public onlyOwner {\n require(_newTokenOwner != address(0), \"invalid new token owner address\");\n require(_newTokenOwner != tokenOwner, \"same owner not allowed\");\n newTokenOwner = _newTokenOwner;\n }\n\n /**\n * @notice Approve token owner change - only token Owner.\n * @dev Token owner can only be modified\n * when both vesting owner and token owner have approved. This\n * function ascertains the approval of token owner.\n * */\n function approveOwnershipTransfer() public onlyTokenOwner {\n require(newTokenOwner != address(0), \"invalid address\");\n tokenOwner = newTokenOwner;\n newTokenOwner = address(0);\n emit TokenOwnerChanged(tokenOwner, msg.sender);\n }\n\n /**\n * @notice Set address of the implementation - only Token Owner.\n * @dev This function sets the new implementation address.\n * It must also be approved by the Vesting owner.\n * @param _newImplementation Address of the new implementation.\n * */\n function setImpl(address _newImplementation) public onlyTokenOwner {\n require(_newImplementation != address(0), \"invalid new implementation address\");\n newImplementation = _newImplementation;\n }\n\n /**\n * @notice Allows the owners to migrate the positions\n * to a new staking contract.\n * */\n function migrateToNewStakingContract() external onlyOwners {\n staking.migrateToNewStakingContract();\n staking = IStaking(staking.newStakingContract());\n emit MigratedToNewStakingContract(msg.sender, address(staking));\n }\n\n /**\n * @notice Extends stakes(unlocked till timeDuration) for four year vesting contracts.\n * @dev Tokens are vested for 4 years. Since the max staking\n * period is 3 years and the tokens are unlocked only after the first year(timeDuration) is\n * passed, hence, we usually extend the duration of staking for all unlocked tokens for the first\n * year by 3 years. In some cases, the timeDuration can differ.\n * */\n function extendStaking() external {\n uint256 timeDuration = startDate.add(extendDurationFor);\n uint256[] memory dates;\n uint96[] memory stakes;\n (dates, stakes) = staking.getStakes(address(this));\n\n for (uint256 i = 0; i < dates.length; i++) {\n if ((dates[i] < block.timestamp) && (dates[i] <= timeDuration) && (stakes[i] > 0)) {\n staking.extendStakingDuration(dates[i], dates[i].add(156 weeks));\n endDate = dates[i].add(156 weeks);\n } else {\n break;\n }\n }\n }\n\n /**\n * @notice Stakes tokens according to the vesting schedule. Low level function.\n * @dev Once here the allowance of tokens is taken for granted.\n * @param _sender The sender of tokens to stake.\n * @param _amount The amount of tokens to stake.\n * @param _restartStakeSchedule The time from which staking schedule restarts.\n * The issue is that we can only stake tokens for a max duration. Thus, we need to restart\n * from the lastSchedule.\n * @return lastSchedule The max duration for which tokens were staked.\n * @return remainingAmount The amount outstanding - to be staked.\n * */\n function _stakeTokens(\n address _sender,\n uint256 _amount,\n uint256 _restartStakeSchedule\n ) internal returns (uint256 lastSchedule, uint256 remainingAmount) {\n // Creating a new staking schedule for the same vesting contract is disallowed unlike normal vesting\n require(\n (startDate == 0) ||\n (startDate > 0 && remainingStakeAmount > 0 && _restartStakeSchedule > 0),\n \"create new vesting address\"\n );\n uint256 restartDate;\n uint256 relativeAmount;\n // Calling the _stakeTokens function first time for the vesting contract\n // Runs for maxInterval only (consider maxInterval = 18 * 4 = 72 weeks)\n if (startDate == 0 && _restartStakeSchedule == 0) {\n startDate = staking.timestampToLockDate(block.timestamp); // Set only once\n durationLeft = duration; // We do not touch duration and cliff as they are used throughout\n cliffAdded = cliff; // Hence, durationLeft and cliffAdded is created\n }\n // Calling the _stakeTokens second/third time - we start from the end of previous interval\n // and the remaining amount(amount left after tokens are staked in the previous interval)\n if (_restartStakeSchedule > 0) {\n require(\n _restartStakeSchedule == lastStakingSchedule && _amount == remainingStakeAmount,\n \"invalid params\"\n );\n restartDate = _restartStakeSchedule;\n } else {\n restartDate = startDate;\n }\n // Runs only once when the _stakeTokens is called for the first time\n if (endDate == 0) {\n endDate = staking.timestampToLockDate(block.timestamp.add(duration));\n }\n uint256 addedMaxInterval = restartDate.add(maxInterval); // run for maxInterval\n if (addedMaxInterval < endDate) {\n // Runs for max interval\n lastStakingSchedule = addedMaxInterval;\n relativeAmount = (_amount.mul(maxInterval)).div(durationLeft); // (_amount * 18) / 39\n durationLeft = durationLeft.sub(maxInterval); // durationLeft - 18 periods(72 weeks)\n remainingStakeAmount = _amount.sub(relativeAmount); // Amount left to be staked in subsequent intervals\n } else {\n // Normal run\n lastStakingSchedule = endDate; // if staking intervals left < 18 periods(72 weeks)\n remainingStakeAmount = 0;\n durationLeft = 0;\n relativeAmount = _amount; // Stake all amount left\n }\n\n /// @dev Transfer the tokens to this contract.\n bool success = SOV.transferFrom(_sender, address(this), relativeAmount);\n require(success, \"transfer failed\");\n\n /// @dev Allow the staking contract to access them.\n SOV.approve(address(staking), relativeAmount);\n\n staking.stakesBySchedule(\n relativeAmount,\n cliffAdded,\n duration.sub(durationLeft),\n FOUR_WEEKS,\n address(this),\n tokenOwner\n );\n if (durationLeft == 0) {\n // All tokens staked\n cliffAdded = 0;\n } else {\n cliffAdded = cliffAdded.add(maxInterval); // Add cliff to the end of previous maxInterval\n }\n\n emit TokensStaked(_sender, relativeAmount);\n return (lastStakingSchedule, remainingStakeAmount);\n }\n\n /**\n * @notice Withdraws tokens from the staking contract and forwards them\n * to an address specified by the token owner. Low level function.\n * @dev Once here the caller permission is taken for granted.\n * @param receiver The receiving address.\n * @param isGovernance Whether all tokens (true)\n * or just unlocked tokens (false).\n * */\n function _withdrawTokens(address receiver, bool isGovernance) internal {\n require(receiver != address(0), \"receiver address invalid\");\n\n uint96 stake;\n\n /// @dev Usually we just need to iterate over the possible dates until now.\n uint256 end;\n\n /// @dev In the unlikely case that all tokens have been unlocked early,\n /// allow to withdraw all of them.\n if (staking.allUnlocked() || isGovernance) {\n end = endDate;\n } else {\n end = block.timestamp;\n }\n\n /// @dev Withdraw for each unlocked position.\n /// @dev Don't change FOUR_WEEKS to TWO_WEEKS, a lot of vestings already deployed with FOUR_WEEKS\n ///\t\tworkaround found, but it doesn't work with TWO_WEEKS\n /// @dev For four year vesting, withdrawal of stakes for the first year is not allowed. These\n /// stakes are extended for three years. In some cases the withdrawal may be allowed at a different\n /// time and hence we use extendDurationFor.\n for (uint256 i = startDate.add(extendDurationFor); i <= end; i += FOUR_WEEKS) {\n /// @dev Read amount to withdraw.\n stake = staking.getPriorUserStakeByDate(address(this), i, block.number.sub(1));\n\n /// @dev Withdraw if > 0\n if (stake > 0) {\n staking.withdraw(stake, i, receiver);\n }\n }\n\n emit TokensWithdrawn(msg.sender, receiver);\n }\n\n /**\n * @notice Overrides default ApprovalReceiver._getToken function to\n * register SOV token on this contract.\n * @return The address of SOV token.\n * */\n function _getToken() internal view returns (address) {\n return address(SOV);\n }\n\n /**\n * @notice Overrides default ApprovalReceiver._getSelectors function to\n * register stakeTokensWithApproval selector on this contract.\n * @return The array of registered selectors on this contract.\n * */\n function _getSelectors() internal pure returns (bytes4[] memory) {\n bytes4[] memory selectors = new bytes4[](1);\n selectors[0] = this.stakeTokensWithApproval.selector;\n return selectors;\n }\n}\n" + }, + "contracts/governance/Vesting/fouryear/FourYearVestingStorage.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../../openzeppelin/Ownable.sol\";\nimport \"../../../interfaces/IERC20.sol\";\nimport \"../../Staking/interfaces/IStaking.sol\";\nimport \"../../IFeeSharingCollector.sol\";\n\n/**\n * @title Four Year Vesting Storage Contract.\n *\n * @notice This contract is just the storage required for four year vesting.\n * It is parent of FourYearVestingLogic and FourYearVesting.\n *\n * @dev Use Ownable as a parent to align storage structure for Logic and Proxy contracts.\n * */\ncontract FourYearVestingStorage is Ownable {\n /// @notice The SOV token contract.\n IERC20 public SOV;\n\n /// @notice The staking contract address.\n IStaking public staking;\n\n /// @notice The owner of the vested tokens.\n address public tokenOwner;\n\n /// @notice Fee sharing Proxy.\n IFeeSharingCollector public feeSharingCollector;\n\n // Used lower case for cliff and duration to maintain consistency with normal vesting\n /// @notice The cliff. After this time period the tokens begin to unlock.\n uint256 public constant cliff = 4 weeks;\n\n /// @notice The duration. After this period all tokens will have been unlocked.\n uint256 public constant duration = 156 weeks;\n\n /// @notice The start date of the vesting.\n uint256 public startDate;\n\n /// @notice The end date of the vesting.\n uint256 public endDate;\n\n /// @notice Constant used for computing the vesting dates.\n uint256 public constant FOUR_WEEKS = 4 weeks;\n\n /// @notice Maximum interval to stake tokens at one go\n uint256 public maxInterval;\n\n /// @notice End of previous staking schedule.\n uint256 public lastStakingSchedule;\n\n /// @notice Amount of shares left to be staked.\n uint256 public remainingStakeAmount;\n\n /// @notice Durations left.\n uint256 public durationLeft;\n\n /// @notice Cliffs added.\n uint256 public cliffAdded;\n\n /// @notice Address of new token owner.\n address public newTokenOwner;\n\n /// @notice Address of new implementation.\n address public newImplementation;\n\n /// @notice Duration(from start) till the time unlocked tokens are extended(for 3 years)\n uint256 public extendDurationFor;\n\n /// @dev Please add new state variables below this line. Mark them internal and\n /// add a getter function while upgrading the contracts.\n}\n" + }, + "contracts/governance/Vesting/fouryear/IFourYearVesting.sol": { + "content": "pragma solidity ^0.5.17;\n\n/**\n * @title Interface for Four Year Vesting contract.\n * @dev Interfaces are used to cast a contract address into a callable instance.\n * This interface is used by FourYearVestingLogic contract to implement stakeTokens function\n * and on VestingRegistry contract to call IFourYearVesting(vesting).stakeTokens function\n * at a vesting instance.\n */\ninterface IFourYearVesting {\n function endDate() external returns (uint256);\n\n function stakeTokens(uint256 _amount, uint256 _restartStakeSchedule)\n external\n returns (uint256 lastSchedule, uint256 remainingAmount);\n}\n" + }, + "contracts/governance/Vesting/fouryear/IFourYearVestingFactory.sol": { + "content": "pragma solidity ^0.5.17;\n\n/**\n * @title Interface for Four Year Vesting Factory contract.\n * @dev Interfaces are used to cast a contract address into a callable instance.\n * This interface is used by FourYearVestingFactory contract to override empty\n * implemention of deployFourYearVesting function\n * and use an instance of FourYearVestingFactory.\n */\ninterface IFourYearVestingFactory {\n function deployFourYearVesting(\n address _SOV,\n address _staking,\n address _tokenOwner,\n address _feeSharing,\n address _vestingOwnerMultisig,\n address _fourYearVestingLogic,\n uint256 _extendDurationFor\n ) external returns (address);\n}\n" + }, + "contracts/governance/Vesting/GenericTokenSender.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"../../interfaces/IERC20.sol\";\nimport \"../../utils/AdminRole.sol\";\n\n/**\n * @title Token sender contract.\n *\n * @notice This contract includes functions to transfer tokens\n * to a recipient or to several recipients in a list. There is\n * an ACL control check by modifier.\n *\n */\ncontract GenericTokenSender is AdminRole {\n /* Events */\n\n event TokensTransferred(address indexed token, address indexed receiver, uint256 amount);\n\n /* Functions */\n\n /**\n * @notice Transfer given amounts of tokens to the given addresses.\n * @param _token The address of the token.\n * @param _receivers The addresses of the receivers.\n * @param _amounts The amounts to be transferred.\n * */\n function transferTokensUsingList(\n address _token,\n address[] calldata _receivers,\n uint256[] calldata _amounts\n ) external onlyAuthorized {\n require(_receivers.length == _amounts.length, \"arrays mismatch\");\n\n for (uint256 i = 0; i < _receivers.length; i++) {\n _transferTokens(_token, _receivers[i], _amounts[i]);\n }\n }\n\n function() external payable {}\n\n /**\n * @notice Transfer tokens to given address.\n * @param _token The address of the token.\n * @param _receiver The address of the token receiver.\n * @param _amount The amount to be transferred.\n * */\n function transferTokens(\n address _token,\n address _receiver,\n uint256 _amount\n ) external onlyAuthorized {\n _transferTokens(_token, _receiver, _amount);\n }\n\n function _transferTokens(\n address _token,\n address _receiver,\n uint256 _amount\n ) internal {\n require(_receiver != address(0), \"receiver address invalid\");\n require(_amount != 0, \"amount invalid\");\n if (_token != address(0)) {\n require(IERC20(_token).transfer(_receiver, _amount), \"transfer failed\");\n } else {\n (bool success, ) = _receiver.call.value(_amount)(\"\");\n require(success, \"RBTC transfer failed\");\n }\n emit TokensTransferred(_token, _receiver, _amount);\n }\n}\n" + }, + "contracts/governance/Vesting/ITeamVesting.sol": { + "content": "pragma solidity ^0.5.17;\n\n/**\n * @title Interface for TeamVesting contract.\n * @dev Interfaces are used to cast a contract address into a callable instance.\n * This interface is used by Staking contract to call governanceWithdrawTokens\n * function having the vesting contract instance address.\n */\ninterface ITeamVesting {\n function startDate() external view returns (uint256);\n\n function cliff() external view returns (uint256);\n\n function endDate() external view returns (uint256);\n\n function duration() external view returns (uint256);\n\n function tokenOwner() external view returns (address);\n\n function governanceWithdrawTokens(address receiver) external;\n}\n" + }, + "contracts/governance/Vesting/IVesting.sol": { + "content": "pragma solidity ^0.5.17;\n\n/**\n * @title Interface for Vesting contract.\n * @dev Interfaces are used to cast a contract address into a callable instance.\n * This interface is used by VestingLogic contract to implement stakeTokens function\n * and on VestingRegistry contract to call IVesting(vesting).stakeTokens function\n * at a vesting instance.\n */\ninterface IVesting {\n function duration() external returns (uint256);\n\n function endDate() external returns (uint256);\n\n function stakeTokens(uint256 amount) external;\n\n function tokenOwner() external view returns (address);\n}\n" + }, + "contracts/governance/Vesting/IVestingFactory.sol": { + "content": "pragma solidity ^0.5.17;\n\n/**\n * @title Interface for Vesting Factory contract.\n * @dev Interfaces are used to cast a contract address into a callable instance.\n * This interface is used by VestingFactory contract to override empty\n * implemention of deployVesting and deployTeamVesting functions\n * and on VestingRegistry contract to use an instance of VestingFactory.\n */\ninterface IVestingFactory {\n function deployVesting(\n address _SOV,\n address _staking,\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration,\n address _feeSharing,\n address _owner\n ) external returns (address);\n\n function deployTeamVesting(\n address _SOV,\n address _staking,\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration,\n address _feeSharing,\n address _owner\n ) external returns (address);\n}\n" + }, + "contracts/governance/Vesting/IVestingRegistry.sol": { + "content": "pragma solidity ^0.5.17;\n\n/**\n * @title Interface for upgradable Vesting Registry contract.\n * @dev Interfaces are used to cast a contract address into a callable instance.\n */\ninterface IVestingRegistry {\n function getVesting(address _tokenOwner) external view returns (address);\n\n function getTeamVesting(address _tokenOwner) external view returns (address);\n\n function setVestingRegistry(address _vestingRegistryProxy) external;\n\n function isVestingAddress(address _vestingAddress) external view returns (bool);\n\n function isTeamVesting(address _vestingAddress) external view returns (bool);\n}\n" + }, + "contracts/governance/Vesting/OrigingVestingCreator.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"./VestingRegistry.sol\";\n\n/**\n * @title Temp contract for checking address, creating and staking tokens.\n * @notice It casts an instance of vestingRegistry and by using createVesting\n * function it creates a vesting, gets it and stakes some tokens w/ this vesting.\n * */\ncontract OrigingVestingCreator is Ownable {\n VestingRegistry public vestingRegistry;\n\n mapping(address => bool) processedList;\n\n constructor(address _vestingRegistry) public {\n vestingRegistry = VestingRegistry(_vestingRegistry);\n }\n\n /**\n * @notice Create a vesting, get it and stake some tokens w/ this vesting.\n * @param _tokenOwner The owner of the tokens.\n * @param _amount The amount of tokens to be vested.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * */\n function createVesting(\n address _tokenOwner,\n uint256 _amount,\n uint256 _cliff,\n uint256 _duration\n ) public onlyOwner {\n require(_tokenOwner != address(0), \"Invalid address\");\n require(!processedList[_tokenOwner], \"Already processed\");\n\n processedList[_tokenOwner] = true;\n\n vestingRegistry.createVesting(_tokenOwner, _amount, _cliff, _duration);\n address vesting = vestingRegistry.getVesting(_tokenOwner);\n vestingRegistry.stakeTokens(vesting, _amount);\n }\n}\n" + }, + "contracts/governance/Vesting/OriginInvestorsClaim.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./VestingRegistry.sol\";\nimport \"../Staking/interfaces/IStaking.sol\";\n\n/**\n * @title Origin investors claim vested cSOV tokens.\n * @notice // TODO: fund this contract with a total amount of SOV needed to distribute.\n * */\ncontract OriginInvestorsClaim is Ownable {\n using SafeMath for uint256;\n\n /* Storage */\n\n /// VestingRegistry public constant vestingRegistry = VestingRegistry(0x80B036ae59B3e38B573837c01BB1DB95515b7E6B);\n\n uint256 public totalAmount;\n\n /// @notice Constant used for computing the vesting dates.\n uint256 public constant SOV_VESTING_CLIFF = 6 weeks;\n\n uint256 public kickoffTS;\n uint256 public vestingTerm;\n uint256 public investorsQty;\n bool public investorsListInitialized;\n VestingRegistry public vestingRegistry;\n IStaking public staking;\n IERC20 public SOVToken;\n\n /// @dev user => flag : Whether user has admin role.\n mapping(address => bool) public admins;\n\n /// @dev investor => Amount : Origin investors entitled to claim SOV.\n mapping(address => uint256) public investorsAmountsList;\n\n /* Events */\n\n event AdminAdded(address admin);\n event AdminRemoved(address admin);\n event InvestorsAmountsListAppended(uint256 qty, uint256 amount);\n event ClaimVested(address indexed investor, uint256 amount);\n event ClaimTransferred(address indexed investor, uint256 amount);\n event InvestorsAmountsListInitialized(uint256 qty, uint256 totalAmount);\n\n /* Modifiers */\n\n /// @dev Throws if called by any account other than the owner or admin.\n modifier onlyAuthorized() {\n require(\n isOwner() || admins[msg.sender],\n \"OriginInvestorsClaim::onlyAuthorized: should be authorized\"\n );\n _;\n }\n\n /// @dev Throws if called by any account not whitelisted.\n modifier onlyWhitelisted() {\n require(\n investorsAmountsList[msg.sender] != 0,\n \"OriginInvestorsClaim::onlyWhitelisted: not whitelisted or already claimed\"\n );\n _;\n }\n\n /// @dev Throws if called w/ an initialized investors list.\n modifier notInitialized() {\n require(\n !investorsListInitialized,\n \"OriginInvestorsClaim::notInitialized: the investors list should not be set as initialized\"\n );\n _;\n }\n\n /// @dev Throws if called w/ an uninitialized investors list.\n modifier initialized() {\n require(\n investorsListInitialized,\n \"OriginInvestorsClaim::initialized: the investors list has not been set yet\"\n );\n _;\n }\n\n /* Functions */\n\n /**\n * @notice Contract deployment requires one parameter:\n * @param vestingRegistryAddress The vestingRegistry contract instance address.\n * */\n constructor(address vestingRegistryAddress) public {\n vestingRegistry = VestingRegistry(vestingRegistryAddress);\n staking = IStaking(vestingRegistry.staking());\n kickoffTS = staking.kickoffTS();\n SOVToken = IERC20(staking.SOVToken());\n vestingTerm = kickoffTS + SOV_VESTING_CLIFF;\n }\n\n /**\n * @notice Add account to ACL.\n * @param _admin The addresses of the account to grant permissions.\n * */\n function addAdmin(address _admin) public onlyOwner {\n admins[_admin] = true;\n emit AdminAdded(_admin);\n }\n\n /**\n * @notice Remove account from ACL.\n * @param _admin The addresses of the account to revoke permissions.\n * */\n function removeAdmin(address _admin) public onlyOwner {\n admins[_admin] = false;\n emit AdminRemoved(_admin);\n }\n\n /**\n * @notice In case we have unclaimed tokens or in emergency case\n * this function transfers all SOV tokens to a given address.\n * @param toAddress The recipient address of all this contract tokens.\n * */\n function authorizedBalanceWithdraw(address toAddress) public onlyAuthorized {\n require(\n SOVToken.transfer(toAddress, SOVToken.balanceOf(address(this))),\n \"OriginInvestorsClaim::authorizedTransferBalance: transfer failed\"\n );\n }\n\n /**\n * @notice Should be called after the investors list setup completed.\n * This function checks whether the SOV token balance of the contract is\n * enough and sets status list to initialized.\n * */\n function setInvestorsAmountsListInitialized() public onlyAuthorized notInitialized {\n require(\n SOVToken.balanceOf(address(this)) >= totalAmount,\n \"OriginInvestorsClaim::setInvestorsAmountsList: the contract is not enough financed\"\n );\n\n investorsListInitialized = true;\n\n emit InvestorsAmountsListInitialized(investorsQty, totalAmount);\n }\n\n /**\n * @notice The contract should be approved or transferred necessary\n * amount of SOV prior to calling the function.\n * @param investors The list of investors addresses to add to the list.\n * Duplicates will be skipped.\n * @param claimAmounts The list of amounts for investors investors[i]\n * will receive claimAmounts[i] of SOV.\n * */\n function appendInvestorsAmountsList(\n address[] calldata investors,\n uint256[] calldata claimAmounts\n ) external onlyAuthorized notInitialized {\n uint256 subQty;\n uint256 sumAmount;\n require(\n investors.length == claimAmounts.length,\n \"OriginInvestorsClaim::appendInvestorsAmountsList: investors.length != claimAmounts.length\"\n );\n\n for (uint256 i = 0; i < investors.length; i++) {\n if (investorsAmountsList[investors[i]] == 0) {\n investorsAmountsList[investors[i]] = claimAmounts[i];\n sumAmount = sumAmount.add(claimAmounts[i]);\n } else {\n subQty = subQty.add(1);\n }\n }\n\n investorsQty = investorsQty.add(investors.length.sub(subQty));\n totalAmount = totalAmount.add(sumAmount);\n emit InvestorsAmountsListAppended(investors.length.sub(subQty), sumAmount);\n }\n\n /**\n * @notice Claim tokens from this contract.\n * If vestingTerm is not yet achieved a vesting is created.\n * Otherwise tokens are tranferred.\n * */\n function claim() external onlyWhitelisted initialized {\n if (now < vestingTerm) {\n createVesting();\n } else {\n transfer();\n }\n }\n\n /**\n * @notice Transfer tokens from this contract to a vestingRegistry contract.\n * Sender is removed from investor list and all its unvested tokens\n * are sent to vesting contract.\n * */\n function createVesting() internal {\n uint256 cliff = vestingTerm.sub(now);\n uint256 duration = cliff;\n uint256 amount = investorsAmountsList[msg.sender];\n address vestingContractAddress;\n\n vestingContractAddress = vestingRegistry.getVesting(msg.sender);\n require(\n vestingContractAddress == address(0),\n \"OriginInvestorsClaim::withdraw: the claimer has an active vesting contract\"\n );\n\n delete investorsAmountsList[msg.sender];\n\n vestingRegistry.createVesting(msg.sender, amount, cliff, duration);\n vestingContractAddress = vestingRegistry.getVesting(msg.sender);\n require(\n SOVToken.transfer(address(vestingRegistry), amount),\n \"OriginInvestorsClaim::withdraw: SOV transfer failed\"\n );\n vestingRegistry.stakeTokens(vestingContractAddress, amount);\n\n emit ClaimVested(msg.sender, amount);\n }\n\n /**\n * @notice Transfer tokens from this contract to the sender.\n * Sender is removed from investor list and all its unvested tokens\n * are sent to its account.\n * */\n function transfer() internal {\n uint256 amount = investorsAmountsList[msg.sender];\n\n delete investorsAmountsList[msg.sender];\n\n /**\n * @dev Withdraw only for those claiming after the cliff, i.e. without vesting contracts.\n * Those with vestingContracts should withdraw using Vesting.withdrawTokens\n * from Vesting (VestingLogic) contract.\n * */\n require(\n SOVToken.transfer(msg.sender, amount),\n \"OriginInvestorsClaim::withdraw: SOV transfer failed\"\n );\n\n emit ClaimTransferred(msg.sender, amount);\n }\n}\n" + }, + "contracts/governance/Vesting/TeamVesting.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"../../interfaces/IERC20.sol\";\n//import \"../Staking/interfaces/IStaking.sol\";\nimport \"../IFeeSharingCollector.sol\";\nimport \"./IVesting.sol\";\nimport \"../ApprovalReceiver.sol\";\nimport \"./VestingStorage.sol\";\nimport \"../../proxy/Proxy.sol\";\n\n/**\n * @title Team Vesting Contract.\n *\n * @notice A regular vesting contract, but the owner (governance) is able to\n * withdraw earlier without a slashing.\n *\n * @dev Vesting contracts shouldn't be upgradable,\n * use Proxy instead of UpgradableProxy.\n * */\ncontract TeamVesting is VestingStorage, Proxy {\n /**\n * @notice Setup the vesting schedule.\n * @param _logic The address of logic contract.\n * @param _SOV The SOV token address.\n * @param _tokenOwner The owner of the tokens.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * */\n constructor(\n address _logic,\n address _SOV,\n address _stakingAddress,\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration,\n address _feeSharingCollector\n ) public {\n require(_SOV != address(0), \"SOV address invalid\");\n require(_stakingAddress != address(0), \"staking address invalid\");\n require(_tokenOwner != address(0), \"token owner address invalid\");\n require(_duration >= _cliff, \"duration must be bigger than or equal to the cliff\");\n require(_feeSharingCollector != address(0), \"feeSharingCollector address invalid\");\n\n _setImplementation(_logic);\n SOV = IERC20(_SOV);\n staking = IStaking(_stakingAddress);\n require(_duration <= staking.MAX_DURATION(), \"duration may not exceed the max duration\");\n tokenOwner = _tokenOwner;\n cliff = _cliff;\n duration = _duration;\n feeSharingCollector = IFeeSharingCollector(_feeSharingCollector);\n }\n}\n" + }, + "contracts/governance/Vesting/TokenSender.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"../../interfaces/IERC20.sol\";\n\n/**\n * @title SOV Token sender contract.\n *\n * @notice This contract includes functions to transfer SOV tokens\n * to a recipient or to several recipients in a list. There is\n * an ACL control check by modifier.\n *\n */\ncontract TokenSender is Ownable {\n /* Storage */\n\n /// @notice The SOV token contract.\n address public SOV;\n\n /// @dev user => flag whether user has admin role\n mapping(address => bool) public admins;\n\n /* Events */\n\n event SOVTransferred(address indexed receiver, uint256 amount);\n event AdminAdded(address admin);\n event AdminRemoved(address admin);\n\n /* Functions */\n\n constructor(address _SOV) public {\n require(_SOV != address(0), \"SOV address invalid\");\n\n SOV = _SOV;\n }\n\n /* Modifiers */\n\n /**\n * @dev Throws if called by any account other than the owner or admin.\n * */\n modifier onlyAuthorized() {\n require(isOwner() || admins[msg.sender], \"unauthorized\");\n _;\n }\n\n /* Functions */\n\n /**\n * @notice Add account to ACL.\n * @param _admin The addresses of the account to grant permissions.\n * */\n function addAdmin(address _admin) public onlyOwner {\n admins[_admin] = true;\n emit AdminAdded(_admin);\n }\n\n /**\n * @notice Remove account from ACL.\n * @param _admin The addresses of the account to revoke permissions.\n * */\n function removeAdmin(address _admin) public onlyOwner {\n admins[_admin] = false;\n emit AdminRemoved(_admin);\n }\n\n /**\n * @notice Transfer given amounts of SOV to the given addresses.\n * @param _receivers The addresses of the SOV receivers.\n * @param _amounts The amounts to be transferred.\n * */\n function transferSOVusingList(address[] memory _receivers, uint256[] memory _amounts)\n public\n onlyAuthorized\n {\n require(_receivers.length == _amounts.length, \"arrays mismatch\");\n\n for (uint256 i = 0; i < _receivers.length; i++) {\n _transferSOV(_receivers[i], _amounts[i]);\n }\n }\n\n /**\n * @notice Transfer SOV tokens to given address.\n * @param _receiver The address of the SOV receiver.\n * @param _amount The amount to be transferred.\n * */\n function transferSOV(address _receiver, uint256 _amount) public onlyAuthorized {\n _transferSOV(_receiver, _amount);\n }\n\n function _transferSOV(address _receiver, uint256 _amount) internal {\n require(_receiver != address(0), \"receiver address invalid\");\n require(_amount != 0, \"amount invalid\");\n\n require(IERC20(SOV).transfer(_receiver, _amount), \"transfer failed\");\n emit SOVTransferred(_receiver, _amount);\n }\n}\n" + }, + "contracts/governance/Vesting/Vesting.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./TeamVesting.sol\";\n\n/**\n * @title Vesting Contract.\n * @notice Team tokens and investor tokens are vested. Therefore, a smart\n * contract needs to be developed to enforce the vesting schedule.\n *\n * @dev TODO add tests for governanceWithdrawTokens.\n * */\ncontract Vesting is TeamVesting {\n /**\n * @notice Setup the vesting schedule.\n * @param _logic The address of logic contract.\n * @param _SOV The SOV token address.\n * @param _tokenOwner The owner of the tokens.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * */\n constructor(\n address _logic,\n address _SOV,\n address _stakingAddress,\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration,\n address _feeSharingCollectorProxy\n )\n public\n TeamVesting(\n _logic,\n _SOV,\n _stakingAddress,\n _tokenOwner,\n _cliff,\n _duration,\n _feeSharingCollectorProxy\n )\n {}\n\n /**\n * @dev We need to add this implementation to prevent proxy call VestingLogic.governanceWithdrawTokens\n * @param receiver The receiver of the token withdrawal.\n * */\n function governanceWithdrawTokens(address receiver) public {\n revert(\"operation not supported\");\n }\n}\n" + }, + "contracts/governance/Vesting/VestingCreator.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../interfaces/IERC20.sol\";\nimport \"../../utils/AdminRole.sol\";\nimport \"./VestingRegistryLogic.sol\";\nimport \"./VestingLogic.sol\";\nimport \"../../openzeppelin/SafeMath.sol\";\n\ncontract VestingCreator is AdminRole {\n using SafeMath for uint256;\n\n ///@notice Boolean to check both vesting creation and staking is completed for a record\n bool vestingCreated;\n\n /// @notice 2 weeks in seconds.\n uint256 public constant TWO_WEEKS = 2 weeks;\n\n ///@notice the SOV token contract\n IERC20 public SOV;\n\n ///@notice the vesting registry contract\n VestingRegistryLogic public vestingRegistryLogic;\n\n ///@notice Holds Vesting Data\n struct VestingData {\n uint256 amount;\n uint256 cliff;\n uint256 duration;\n bool governanceControl; ///@dev true - tokens can be withdrawn by governance\n address tokenOwner;\n uint256 vestingCreationType;\n }\n\n ///@notice list of vesting to be processed\n VestingData[] public vestingDataList;\n\n event SOVTransferred(address indexed receiver, uint256 amount);\n event TokensStaked(address indexed vesting, address indexed tokenOwner, uint256 amount);\n event VestingDataRemoved(address indexed caller, address indexed tokenOwner);\n event DataCleared(address indexed caller);\n\n constructor(address _SOV, address _vestingRegistryProxy) public {\n require(_SOV != address(0), \"SOV address invalid\");\n require(_vestingRegistryProxy != address(0), \"Vesting registry address invalid\");\n\n SOV = IERC20(_SOV);\n vestingRegistryLogic = VestingRegistryLogic(_vestingRegistryProxy);\n }\n\n /**\n * @notice transfers SOV tokens to given address\n * @param _receiver the address of the SOV receiver\n * @param _amount the amount to be transferred\n */\n function transferSOV(address _receiver, uint256 _amount) external onlyOwner {\n require(_amount != 0, \"amount invalid\");\n require(SOV.transfer(_receiver, _amount), \"transfer failed\");\n emit SOVTransferred(_receiver, _amount);\n }\n\n /**\n * @notice adds vestings to be processed to the list\n */\n function addVestings(\n address[] calldata _tokenOwners,\n uint256[] calldata _amounts,\n uint256[] calldata _cliffs,\n uint256[] calldata _durations,\n bool[] calldata _governanceControls,\n uint256[] calldata _vestingCreationTypes\n ) external onlyAuthorized {\n require(\n _tokenOwners.length == _amounts.length &&\n _tokenOwners.length == _cliffs.length &&\n _tokenOwners.length == _durations.length &&\n _tokenOwners.length == _governanceControls.length,\n \"arrays mismatch\"\n );\n\n for (uint256 i = 0; i < _tokenOwners.length; i++) {\n require(\n _durations[i] >= _cliffs[i],\n \"duration must be bigger than or equal to the cliff\"\n );\n require(_amounts[i] > 0, \"vesting amount cannot be 0\");\n require(_tokenOwners[i] != address(0), \"token owner cannot be 0 address\");\n require(_cliffs[i].mod(TWO_WEEKS) == 0, \"cliffs should have intervals of two weeks\");\n require(\n _durations[i].mod(TWO_WEEKS) == 0,\n \"durations should have intervals of two weeks\"\n );\n VestingData memory vestingData =\n VestingData({\n amount: _amounts[i],\n cliff: _cliffs[i],\n duration: _durations[i],\n governanceControl: _governanceControls[i],\n tokenOwner: _tokenOwners[i],\n vestingCreationType: _vestingCreationTypes[i]\n });\n vestingDataList.push(vestingData);\n }\n }\n\n /**\n * @notice Creates vesting contract and stakes tokens\n * @dev Vesting and Staking are merged for calls that fits the gas limit\n */\n function processNextVesting() external {\n processVestingCreation();\n processStaking();\n }\n\n /**\n * @notice Creates vesting contract without staking any tokens\n * @dev Separating the Vesting and Staking to tackle Block Gas Limit\n */\n function processVestingCreation() public {\n require(!vestingCreated, \"staking not done for the previous vesting\");\n if (vestingDataList.length > 0) {\n VestingData storage vestingData = vestingDataList[vestingDataList.length - 1];\n _createAndGetVesting(vestingData);\n vestingCreated = true;\n }\n }\n\n /**\n * @notice Staking vested tokens\n * @dev it can be the case when vesting creation and tokens staking can't be done in one transaction because of block gas limit\n */\n function processStaking() public {\n require(vestingCreated, \"cannot stake without vesting creation\");\n if (vestingDataList.length > 0) {\n VestingData storage vestingData = vestingDataList[vestingDataList.length - 1];\n address vestingAddress =\n _getVesting(\n vestingData.tokenOwner,\n vestingData.cliff,\n vestingData.duration,\n vestingData.governanceControl,\n vestingData.vestingCreationType\n );\n if (vestingAddress != address(0)) {\n VestingLogic vesting = VestingLogic(vestingAddress);\n require(SOV.approve(address(vesting), vestingData.amount), \"Approve failed\");\n vesting.stakeTokens(vestingData.amount);\n emit TokensStaked(vestingAddress, vestingData.tokenOwner, vestingData.amount);\n address tokenOwnerDetails = vestingData.tokenOwner;\n vestingDataList.pop();\n emit VestingDataRemoved(msg.sender, tokenOwnerDetails);\n }\n }\n vestingCreated = false;\n }\n\n /**\n * @notice removes next vesting data from the list\n * @dev we process inverted list\n * @dev we should be able to remove incorrect vesting data that can't be processed\n */\n function removeNextVesting() external onlyAuthorized {\n address tokenOwnerDetails;\n if (vestingDataList.length > 0) {\n VestingData storage vestingData = vestingDataList[vestingDataList.length - 1];\n tokenOwnerDetails = vestingData.tokenOwner;\n vestingDataList.pop();\n emit VestingDataRemoved(msg.sender, tokenOwnerDetails);\n }\n }\n\n /**\n * @notice removes all data about unprocessed vestings to be processed\n */\n function clearVestingDataList() public onlyAuthorized {\n delete vestingDataList;\n emit DataCleared(msg.sender);\n }\n\n /**\n * @notice returns address after vesting creation\n */\n function getVestingAddress() external view returns (address) {\n return\n _getVesting(\n vestingDataList[vestingDataList.length - 1].tokenOwner,\n vestingDataList[vestingDataList.length - 1].cliff,\n vestingDataList[vestingDataList.length - 1].duration,\n vestingDataList[vestingDataList.length - 1].governanceControl,\n vestingDataList[vestingDataList.length - 1].vestingCreationType\n );\n }\n\n /**\n * @notice returns period i.e. ((duration - cliff) / 4 WEEKS)\n * @dev will be used for deciding if vesting and staking needs to be processed\n * in a single transaction or separate transactions\n */\n function getVestingPeriod() external view returns (uint256) {\n uint256 duration = vestingDataList[vestingDataList.length - 1].duration;\n uint256 cliff = vestingDataList[vestingDataList.length - 1].cliff;\n uint256 fourWeeks = TWO_WEEKS.mul(2);\n uint256 period = duration.sub(cliff).div(fourWeeks);\n return period;\n }\n\n /**\n * @notice returns count of vestings to be processed\n */\n function getUnprocessedCount() external view returns (uint256) {\n return vestingDataList.length;\n }\n\n /**\n * @notice returns total amount of vestings to be processed\n */\n function getUnprocessedAmount() public view returns (uint256) {\n uint256 amount = 0;\n uint256 length = vestingDataList.length;\n for (uint256 i = 0; i < length; i++) {\n amount = amount.add(vestingDataList[i].amount);\n }\n return amount;\n }\n\n /**\n * @notice checks if contract balance is enough to process all vestings\n */\n function isEnoughBalance() public view returns (bool) {\n return SOV.balanceOf(address(this)) >= getUnprocessedAmount();\n }\n\n /**\n * @notice returns missed balance to process all vestings\n */\n function getMissingBalance() external view returns (uint256) {\n if (isEnoughBalance()) {\n return 0;\n }\n return getUnprocessedAmount() - SOV.balanceOf(address(this));\n }\n\n /**\n * @notice creates TeamVesting or Vesting contract\n * @dev new contract won't be created if account already has contract of the same type\n */\n function _createAndGetVesting(VestingData memory vestingData)\n internal\n returns (address vesting)\n {\n if (vestingData.governanceControl) {\n vestingRegistryLogic.createTeamVesting(\n vestingData.tokenOwner,\n vestingData.amount,\n vestingData.cliff,\n vestingData.duration,\n vestingData.vestingCreationType\n );\n } else {\n vestingRegistryLogic.createVestingAddr(\n vestingData.tokenOwner,\n vestingData.amount,\n vestingData.cliff,\n vestingData.duration,\n vestingData.vestingCreationType\n );\n }\n return\n _getVesting(\n vestingData.tokenOwner,\n vestingData.cliff,\n vestingData.duration,\n vestingData.governanceControl,\n vestingData.vestingCreationType\n );\n }\n\n /**\n * @notice returns an address of TeamVesting or Vesting contract (depends on a governance control)\n */\n function _getVesting(\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration,\n bool _governanceControl,\n uint256 _vestingCreationType\n ) internal view returns (address vestingAddress) {\n if (_governanceControl) {\n vestingAddress = vestingRegistryLogic.getTeamVesting(\n _tokenOwner,\n _cliff,\n _duration,\n _vestingCreationType\n );\n } else {\n vestingAddress = vestingRegistryLogic.getVestingAddr(\n _tokenOwner,\n _cliff,\n _duration,\n _vestingCreationType\n );\n }\n }\n}\n" + }, + "contracts/governance/Vesting/VestingFactory.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"./Vesting.sol\";\nimport \"./TeamVesting.sol\";\nimport \"./IVestingFactory.sol\";\n\n/**\n * @title Vesting Factory: Contract to deploy vesting contracts\n * of two types: vesting (TokenHolder) and team vesting (Multisig).\n * @notice Factory pattern allows to create multiple instances\n * of the same contract and keep track of them easier.\n * */\ncontract VestingFactory is IVestingFactory, Ownable {\n address public vestingLogic;\n\n constructor(address _vestingLogic) public {\n require(_vestingLogic != address(0), \"invalid vesting logic address\");\n vestingLogic = _vestingLogic;\n }\n\n /**\n * @notice Deploys Vesting contract.\n * @param _SOV the address of SOV token.\n * @param _staking The address of staking contract.\n * @param _tokenOwner The owner of the tokens.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * @param _feeSharing The address of fee sharing contract.\n * @param _vestingOwner The address of an owner of vesting contract.\n * @return The vesting contract address.\n * */\n function deployVesting(\n address _SOV,\n address _staking,\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration,\n address _feeSharing,\n address _vestingOwner\n )\n external\n onlyOwner /// @dev owner - VestingRegistry\n returns (address)\n {\n address vesting =\n address(\n new Vesting(\n vestingLogic,\n _SOV,\n _staking,\n _tokenOwner,\n _cliff,\n _duration,\n _feeSharing\n )\n );\n Ownable(vesting).transferOwnership(_vestingOwner);\n return vesting;\n }\n\n /**\n * @notice Deploys Team Vesting contract.\n * @param _SOV The address of SOV token.\n * @param _staking The address of staking contract.\n * @param _tokenOwner The owner of the tokens.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * @param _feeSharing The address of fee sharing contract.\n * @param _vestingOwner The address of an owner of vesting contract.\n * @return The vesting contract address.\n * */\n function deployTeamVesting(\n address _SOV,\n address _staking,\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration,\n address _feeSharing,\n address _vestingOwner\n )\n external\n onlyOwner //owner - VestingRegistry\n returns (address)\n {\n address vesting =\n address(\n new TeamVesting(\n vestingLogic,\n _SOV,\n _staking,\n _tokenOwner,\n _cliff,\n _duration,\n _feeSharing\n )\n );\n Ownable(vesting).transferOwnership(_vestingOwner);\n return vesting;\n }\n}\n" + }, + "contracts/governance/Vesting/VestingLogic.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"../../interfaces/IERC20.sol\";\nimport \"../Staking/interfaces/IStaking.sol\";\nimport \"../IFeeSharingCollector.sol\";\nimport \"./IVesting.sol\";\nimport \"../ApprovalReceiver.sol\";\nimport \"./VestingStorage.sol\";\n\n/**\n * @title Vesting Logic contract.\n * @notice Staking, delegating and withdrawal functionality.\n * @dev Deployed by a VestingFactory contract.\n * */\ncontract VestingLogic is IVesting, VestingStorage, ApprovalReceiver {\n /* Events */\n\n event TokensStaked(address indexed caller, uint256 amount);\n event VotesDelegated(address indexed caller, address delegatee);\n event TokensWithdrawn(address indexed caller, address receiver);\n event DividendsCollected(\n address indexed caller,\n address loanPoolToken,\n address receiver,\n uint32 maxCheckpoints\n );\n event MigratedToNewStakingContract(address indexed caller, address newStakingContract);\n\n /* Modifiers */\n\n /**\n * @dev Throws if called by any account other than the token owner or the contract owner.\n */\n modifier onlyOwners() {\n require(msg.sender == tokenOwner || isOwner(), \"unauthorized\");\n _;\n }\n\n /**\n * @dev Throws if called by any account other than the token owner.\n */\n modifier onlyTokenOwner() {\n require(msg.sender == tokenOwner, \"unauthorized\");\n _;\n }\n\n /* Functions */\n\n /**\n * @notice Stakes tokens according to the vesting schedule.\n * @param _amount The amount of tokens to stake.\n * */\n function stakeTokens(uint256 _amount) public {\n _stakeTokens(msg.sender, _amount);\n }\n\n /**\n * @notice Stakes tokens according to the vesting schedule.\n * @dev This function will be invoked from receiveApproval.\n * @dev SOV.approveAndCall -> this.receiveApproval -> this.stakeTokensWithApproval\n * @param _sender The sender of SOV.approveAndCall\n * @param _amount The amount of tokens to stake.\n * */\n function stakeTokensWithApproval(address _sender, uint256 _amount) public onlyThisContract {\n _stakeTokens(_sender, _amount);\n }\n\n /**\n * @notice Stakes tokens according to the vesting schedule. Low level function.\n * @dev Once here the allowance of tokens is taken for granted.\n * @param _sender The sender of tokens to stake.\n * @param _amount The amount of tokens to stake.\n * */\n function _stakeTokens(address _sender, uint256 _amount) internal {\n /// @dev Maybe better to allow staking unil the cliff was reached.\n if (startDate == 0) {\n startDate = staking.timestampToLockDate(block.timestamp);\n }\n endDate = staking.timestampToLockDate(block.timestamp + duration);\n\n /// @dev Transfer the tokens to this contract.\n bool success = SOV.transferFrom(_sender, address(this), _amount);\n require(success);\n\n /// @dev Allow the staking contract to access them.\n SOV.approve(address(staking), _amount);\n\n staking.stakeBySchedule(_amount, cliff, duration, FOUR_WEEKS, address(this), tokenOwner);\n\n emit TokensStaked(_sender, _amount);\n }\n\n /**\n * @notice Delegate votes from `msg.sender` which are locked until lockDate\n * to `delegatee`.\n * @param _delegatee The address to delegate votes to.\n * */\n function delegate(address _delegatee) public onlyTokenOwner {\n require(_delegatee != address(0), \"delegatee address invalid\");\n\n /// @dev Withdraw for each unlocked position.\n /// @dev Don't change FOUR_WEEKS to TWO_WEEKS, a lot of vestings already deployed with FOUR_WEEKS\n ///\t\tworkaround found, but it doesn't work with TWO_WEEKS\n for (uint256 i = startDate + cliff; i <= endDate; i += FOUR_WEEKS) {\n staking.delegate(_delegatee, i);\n }\n emit VotesDelegated(msg.sender, _delegatee);\n }\n\n /**\n * @notice Withdraws all tokens from the staking contract and\n * forwards them to an address specified by the token owner.\n * @param receiver The receiving address.\n * @dev Can be called only by owner.\n * @dev **WARNING** This function should not be no longer used by Sovryn Protocol.\n * Sovryn protocol will use the cancelTeamVesting function for the withdrawal moving forward.\n * */\n function governanceWithdrawTokens(address receiver) public {\n require(msg.sender == address(staking), \"unauthorized\");\n\n _withdrawTokens(receiver, true);\n }\n\n /**\n * @notice Withdraws unlocked tokens from the staking contract and\n * forwards them to an address specified by the token owner.\n * @param receiver The receiving address.\n * */\n function withdrawTokens(address receiver) public onlyOwners {\n _withdrawTokens(receiver, false);\n }\n\n /**\n * @notice Withdraws tokens from the staking contract and forwards them\n * to an address specified by the token owner. Low level function.\n * @dev Once here the caller permission is taken for granted.\n * @param receiver The receiving address.\n * @param isGovernance Whether all tokens (true)\n * or just unlocked tokens (false).\n * */\n function _withdrawTokens(address receiver, bool isGovernance) internal {\n require(receiver != address(0), \"receiver address invalid\");\n\n uint96 stake;\n\n /// @dev Usually we just need to iterate over the possible dates until now.\n uint256 end;\n\n /// @dev In the unlikely case that all tokens have been unlocked early,\n /// allow to withdraw all of them.\n if (staking.allUnlocked() || isGovernance) {\n end = endDate;\n } else {\n end = block.timestamp;\n }\n\n /// @dev Withdraw for each unlocked position.\n /// @dev Don't change FOUR_WEEKS to TWO_WEEKS, a lot of vestings already deployed with FOUR_WEEKS\n ///\t\tworkaround found, but it doesn't work with TWO_WEEKS\n for (uint256 i = startDate + cliff; i <= end; i += FOUR_WEEKS) {\n /// @dev Read amount to withdraw.\n stake = staking.getPriorUserStakeByDate(address(this), i, block.number - 1);\n\n /// @dev Withdraw if > 0\n if (stake > 0) {\n if (isGovernance) {\n staking.governanceWithdraw(stake, i, receiver);\n } else {\n staking.withdraw(stake, i, receiver);\n }\n }\n }\n\n emit TokensWithdrawn(msg.sender, receiver);\n }\n\n /**\n * @notice Collect dividends from fee sharing proxy.\n * @param _loanPoolToken The loan pool token address.\n * @param _maxCheckpoints Maximum number of checkpoints to be processed.\n * @param _receiver The receiver of tokens or msg.sender\n * */\n function collectDividends(\n address _loanPoolToken,\n uint32 _maxCheckpoints,\n address _receiver\n ) public onlyOwners {\n require(_receiver != address(0), \"receiver address invalid\");\n\n /// @dev Invokes the fee sharing proxy.\n feeSharingCollector.withdraw(_loanPoolToken, _maxCheckpoints, _receiver);\n\n emit DividendsCollected(msg.sender, _loanPoolToken, _receiver, _maxCheckpoints);\n }\n\n /**\n * @notice Allows the owners to migrate the positions\n * to a new staking contract.\n * */\n function migrateToNewStakingContract() public onlyOwners {\n staking.migrateToNewStakingContract();\n staking = IStaking(staking.newStakingContract());\n emit MigratedToNewStakingContract(msg.sender, address(staking));\n }\n\n /**\n * @notice Overrides default ApprovalReceiver._getToken function to\n * register SOV token on this contract.\n * @return The address of SOV token.\n * */\n function _getToken() internal view returns (address) {\n return address(SOV);\n }\n\n /**\n * @notice Overrides default ApprovalReceiver._getSelectors function to\n * register stakeTokensWithApproval selector on this contract.\n * @return The array of registered selectors on this contract.\n * */\n function _getSelectors() internal pure returns (bytes4[] memory) {\n bytes4[] memory selectors = new bytes4[](1);\n selectors[0] = this.stakeTokensWithApproval.selector;\n return selectors;\n }\n}\n" + }, + "contracts/governance/Vesting/VestingRegistry.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"../../interfaces/IERC20.sol\";\nimport \"../Staking/interfaces/IStaking.sol\";\nimport \"../IFeeSharingCollector.sol\";\nimport \"./IVestingFactory.sol\";\nimport \"./IVesting.sol\";\nimport \"./ITeamVesting.sol\";\nimport \"../../openzeppelin/SafeMath.sol\";\n\n/**\n * @title Vesting Registry contract.\n *\n * @notice On January 25, 2020, Sovryn launched the Genesis Reservation system.\n * Sovryn community members who controlled a special NFT were granted access to\n * stake BTC or rBTC for cSOV tokens at a rate of 2500 satoshis per cSOV. Per\n * SIP-0003, up to 2,000,000 cSOV were made available in the Genesis event,\n * which will be redeemable on a 1:1 basis for cSOV, subject to approval by\n * existing SOV holders.\n *\n * On 15 Feb 2021 Sovryn is taking another step in its journey to decentralized\n * financial sovereignty with the vote on SIP 0005. This proposal will enable\n * participants of the Genesis Reservation system to redeem their reserved cSOV\n * tokens for SOV. They will also have the choice to redeem cSOV for rBTC if\n * they decide to exit the system.\n *\n * This contract deals with the vesting and redemption of cSOV tokens.\n * */\ncontract VestingRegistry is Ownable {\n using SafeMath for uint256;\n\n /* Storage */\n\n /// @notice Constant used for computing the vesting dates.\n uint256 public constant FOUR_WEEKS = 4 weeks;\n\n uint256 public constant CSOV_VESTING_CLIFF = FOUR_WEEKS;\n uint256 public constant CSOV_VESTING_DURATION = 10 * FOUR_WEEKS;\n\n IVestingFactory public vestingFactory;\n\n /// @notice The SOV token contract.\n address public SOV;\n\n /// @notice The cSOV token contracts.\n address[] public CSOVtokens;\n\n uint256 public priceSats;\n\n /// @notice The staking contract address.\n address public staking;\n\n /// @notice Fee sharing proxy.\n address public feeSharingCollector;\n\n /// @notice The vesting owner (e.g. governance timelock address).\n address public vestingOwner;\n\n /// @dev TODO: Add to the documentation: address can have only one vesting of each type.\n /// @dev user => vesting type => vesting contract.\n mapping(address => mapping(uint256 => address)) public vestingContracts;\n\n /**\n * @dev Struct can be created to save storage slots, but it doesn't make\n * sense. We don't have a lot of blacklisted accounts or account with\n * locked amount.\n * */\n\n /// @dev user => flag whether user has already exchange cSOV or got a reimbursement.\n mapping(address => bool) public processedList;\n\n /// @dev user => flag whether user shouldn't be able to exchange or reimburse.\n mapping(address => bool) public blacklist;\n\n /// @dev user => amount of tokens should not be processed.\n mapping(address => uint256) public lockedAmount;\n\n /// @dev user => flag whether user has admin role.\n mapping(address => bool) public admins;\n\n enum VestingType {\n TeamVesting, // MultisigVesting\n Vesting // TokenHolderVesting\n }\n\n /* Events */\n\n event CSOVReImburse(address from, uint256 CSOVamount, uint256 reImburseAmount);\n event CSOVTokensExchanged(address indexed caller, uint256 amount);\n event SOVTransferred(address indexed receiver, uint256 amount);\n event VestingCreated(\n address indexed tokenOwner,\n address vesting,\n uint256 cliff,\n uint256 duration,\n uint256 amount\n );\n event TeamVestingCreated(\n address indexed tokenOwner,\n address vesting,\n uint256 cliff,\n uint256 duration,\n uint256 amount\n );\n event TokensStaked(address indexed vesting, uint256 amount);\n event AdminAdded(address admin);\n event AdminRemoved(address admin);\n\n /* Functions */\n\n /**\n * @notice Contract deployment settings.\n * @param _vestingFactory The address of vesting factory contract.\n * @param _SOV The SOV token address.\n * @param _CSOVtokens The array of cSOV tokens.\n * @param _priceSats The price of cSOV tokens in satoshis.\n * @param _staking The address of staking contract.\n * @param _feeSharingCollector The address of fee sharing collector proxy contract.\n * @param _vestingOwner The address of an owner of vesting contract.\n * @dev On Sovryn the vesting owner is Exchequer Multisig.\n * According to SIP-0007 The Exchequer Multisig is designated to hold\n * certain funds in the form of rBTC and SOV, in order to allow for\n * flexible deployment of such funds on:\n * + facilitating rBTC redemptions for Genesis pre-sale participants.\n * + deploying of SOV for the purposes of exchange listings, market\n * making, and partnerships with third parties.\n * */\n constructor(\n address _vestingFactory,\n address _SOV,\n address[] memory _CSOVtokens,\n uint256 _priceSats,\n address _staking,\n address _feeSharingCollector,\n address _vestingOwner\n ) public {\n require(_SOV != address(0), \"SOV address invalid\");\n require(_staking != address(0), \"staking address invalid\");\n require(_feeSharingCollector != address(0), \"feeSharingCollector address invalid\");\n require(_vestingOwner != address(0), \"vestingOwner address invalid\");\n\n _setVestingFactory(_vestingFactory);\n _setCSOVtokens(_CSOVtokens);\n\n SOV = _SOV;\n priceSats = _priceSats;\n staking = _staking;\n feeSharingCollector = _feeSharingCollector;\n vestingOwner = _vestingOwner;\n }\n\n //---ACL------------------------------------------------------------------\n\n /**\n * @dev Throws if called by any account other than the owner or admin.\n * TODO: This ACL logic should be available on OpenZeppeling Ownable.sol\n * or on our own overriding sovrynOwnable. This same logic is repeated\n * on OriginInvestorsClaim.sol, TokenSender.sol and VestingRegistry2.sol\n */\n modifier onlyAuthorized() {\n require(isOwner() || admins[msg.sender], \"unauthorized\");\n _;\n }\n\n /**\n * @notice Add account to ACL.\n * @param _admin The addresses of the account to grant permissions.\n * */\n function addAdmin(address _admin) public onlyOwner {\n admins[_admin] = true;\n emit AdminAdded(_admin);\n }\n\n /**\n * @notice Remove account from ACL.\n * @param _admin The addresses of the account to revoke permissions.\n * */\n function removeAdmin(address _admin) public onlyOwner {\n admins[_admin] = false;\n emit AdminRemoved(_admin);\n }\n\n //---PostCSOV--------------------------------------------------------------\n\n modifier isNotProcessed() {\n require(!processedList[msg.sender], \"Address cannot be processed twice\");\n _;\n }\n\n modifier isNotBlacklisted() {\n require(!blacklist[msg.sender], \"Address blacklisted\");\n _;\n }\n\n /**\n * @notice cSOV payout to sender with rBTC currency.\n * 1.- Check holder cSOV balance by adding up every cSOV token balance.\n * 2.- ReImburse rBTC if funds available.\n * 3.- And store holder address in processedList.\n */\n function reImburse() public isNotProcessed isNotBlacklisted {\n uint256 CSOVAmountWei = 0;\n for (uint256 i = 0; i < CSOVtokens.length; i++) {\n address CSOV = CSOVtokens[i];\n uint256 balance = IERC20(CSOV).balanceOf(msg.sender);\n CSOVAmountWei = CSOVAmountWei.add(balance);\n }\n\n require(CSOVAmountWei > lockedAmount[msg.sender], \"holder has no CSOV\");\n CSOVAmountWei -= lockedAmount[msg.sender];\n processedList[msg.sender] = true;\n\n /**\n * @dev Found and fixed the SIP-0007 bug on VestingRegistry::reImburse formula.\n * More details at Documenting Code issues at point 11 in\n * https://docs.google.com/document/d/10idTD1K6JvoBmtPKGuJ2Ub_mMh6qTLLlTP693GQKMyU/\n * Previous buggy code: uint256 reImburseAmount = (CSOVAmountWei.mul(priceSats)).div(10**10);\n * */\n uint256 reImburseAmount = (CSOVAmountWei.mul(priceSats)).div(10**8);\n require(address(this).balance >= reImburseAmount, \"Not enough funds to reimburse\");\n msg.sender.transfer(reImburseAmount);\n\n emit CSOVReImburse(msg.sender, CSOVAmountWei, reImburseAmount);\n }\n\n /**\n * @notice Get contract balance.\n * @return The token balance of the contract.\n * */\n function budget() external view returns (uint256) {\n uint256 SCBudget = address(this).balance;\n return SCBudget;\n }\n\n /**\n * @notice Deposit function to receiving value (rBTC).\n * */\n function deposit() public payable {}\n\n /**\n * @notice Send all contract balance to an account.\n * @param to The account address to send the balance to.\n * */\n function withdrawAll(address payable to) public onlyOwner {\n to.transfer(address(this).balance);\n }\n\n //--------------------------------------------------------------------------------------------------------------------------------------\n\n /**\n * @notice Sets vesting factory address. High level endpoint.\n * @param _vestingFactory The address of vesting factory contract.\n *\n * @dev Splitting code on two functions: high level and low level\n * is a pattern that makes easy to extend functionality in a readable way,\n * without accidentally breaking the actual action being performed.\n * For example, checks should be done on high level endpoint, while core\n * functionality should be coded on the low level function.\n * */\n function setVestingFactory(address _vestingFactory) public onlyOwner {\n _setVestingFactory(_vestingFactory);\n }\n\n /**\n * @notice Sets vesting factory address. Low level core function.\n * @param _vestingFactory The address of vesting factory contract.\n * */\n function _setVestingFactory(address _vestingFactory) internal {\n require(_vestingFactory != address(0), \"vestingFactory address invalid\");\n vestingFactory = IVestingFactory(_vestingFactory);\n }\n\n /**\n * @notice Sets cSOV tokens array. High level endpoint.\n * @param _CSOVtokens The array of cSOV tokens.\n * */\n function setCSOVtokens(address[] memory _CSOVtokens) public onlyOwner {\n _setCSOVtokens(_CSOVtokens);\n }\n\n /**\n * @notice Sets cSOV tokens array by looping through input. Low level function.\n * @param _CSOVtokens The array of cSOV tokens.\n * */\n function _setCSOVtokens(address[] memory _CSOVtokens) internal {\n for (uint256 i = 0; i < _CSOVtokens.length; i++) {\n require(_CSOVtokens[i] != address(0), \"CSOV address invalid\");\n }\n CSOVtokens = _CSOVtokens;\n }\n\n /**\n * @notice Set blacklist flag (true/false).\n * @param _account The address to be blacklisted.\n * @param _blacklisted The flag to add/remove to/from a blacklist.\n * */\n function setBlacklistFlag(address _account, bool _blacklisted) public onlyOwner {\n require(_account != address(0), \"account address invalid\");\n\n blacklist[_account] = _blacklisted;\n }\n\n /**\n * @notice Set amount to be subtracted from user token balance.\n * @param _account The address with locked amount.\n * @param _amount The amount to be locked.\n * */\n function setLockedAmount(address _account, uint256 _amount) public onlyOwner {\n require(_account != address(0), \"account address invalid\");\n require(_amount != 0, \"amount invalid\");\n\n lockedAmount[_account] = _amount;\n }\n\n /**\n * @notice Transfer SOV tokens to given address.\n *\n * @dev This is a wrapper for ERC-20 transfer function w/\n * additional checks and triggering an event.\n *\n * @param _receiver The address of the SOV receiver.\n * @param _amount The amount to be transferred.\n * */\n function transferSOV(address _receiver, uint256 _amount) public onlyOwner {\n require(_receiver != address(0), \"receiver address invalid\");\n require(_amount != 0, \"amount invalid\");\n\n IERC20(SOV).transfer(_receiver, _amount);\n emit SOVTransferred(_receiver, _amount);\n }\n\n /**\n * @notice Exchange cSOV to SOV with 1:1 rate\n */\n function exchangeAllCSOV() public isNotProcessed isNotBlacklisted {\n processedList[msg.sender] = true;\n\n uint256 amount = 0;\n for (uint256 i = 0; i < CSOVtokens.length; i++) {\n address CSOV = CSOVtokens[i];\n uint256 balance = IERC20(CSOV).balanceOf(msg.sender);\n amount += balance;\n }\n\n require(amount > lockedAmount[msg.sender], \"amount invalid\");\n amount -= lockedAmount[msg.sender];\n\n _createVestingForCSOV(amount);\n }\n\n /**\n * @notice cSOV tokens are moved and staked on Vesting contract.\n * @param _amount The amount of tokens to be vested.\n * */\n function _createVestingForCSOV(uint256 _amount) internal {\n address vesting =\n _getOrCreateVesting(msg.sender, CSOV_VESTING_CLIFF, CSOV_VESTING_DURATION);\n\n IERC20(SOV).approve(vesting, _amount);\n IVesting(vesting).stakeTokens(_amount);\n\n emit CSOVTokensExchanged(msg.sender, _amount);\n }\n\n /**\n * @notice Check a token address is among the cSOV token addresses.\n * @param _CSOV The cSOV token address.\n * */\n function _validateCSOV(address _CSOV) internal view {\n bool isValid = false;\n for (uint256 i = 0; i < CSOVtokens.length; i++) {\n if (_CSOV == CSOVtokens[i]) {\n isValid = true;\n break;\n }\n }\n require(isValid, \"wrong CSOV address\");\n }\n\n /**\n * @notice Create Vesting contract.\n * @param _tokenOwner The owner of the tokens.\n * @param _amount The amount to be staked.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * */\n function createVesting(\n address _tokenOwner,\n uint256 _amount,\n uint256 _cliff,\n uint256 _duration\n ) public onlyAuthorized {\n address vesting = _getOrCreateVesting(_tokenOwner, _cliff, _duration);\n emit VestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount);\n }\n\n /**\n * @notice Create Team Vesting contract.\n * @param _tokenOwner The owner of the tokens.\n * @param _amount The amount to be staked.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * */\n function createTeamVesting(\n address _tokenOwner,\n uint256 _amount,\n uint256 _cliff,\n uint256 _duration\n ) public onlyAuthorized {\n address vesting = _getOrCreateTeamVesting(_tokenOwner, _cliff, _duration);\n emit TeamVestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount);\n }\n\n /**\n * @notice Stake tokens according to the vesting schedule.\n * @param _vesting The address of Vesting contract.\n * @param _amount The amount of tokens to stake.\n * */\n function stakeTokens(address _vesting, uint256 _amount) public onlyAuthorized {\n require(_vesting != address(0), \"vesting address invalid\");\n require(_amount > 0, \"amount invalid\");\n\n IERC20(SOV).approve(_vesting, _amount);\n IVesting(_vesting).stakeTokens(_amount);\n emit TokensStaked(_vesting, _amount);\n }\n\n /**\n * @notice Query the vesting contract for an account.\n * @param _tokenOwner The owner of the tokens.\n * @return The vesting contract address for the given token owner.\n * */\n function getVesting(address _tokenOwner) public view returns (address) {\n return vestingContracts[_tokenOwner][uint256(VestingType.Vesting)];\n }\n\n /**\n * @notice Query the team vesting contract for an account.\n * @param _tokenOwner The owner of the tokens.\n * @return The team vesting contract address for the given token owner.\n * */\n function getTeamVesting(address _tokenOwner) public view returns (address) {\n return vestingContracts[_tokenOwner][uint256(VestingType.TeamVesting)];\n }\n\n /**\n * @notice If not exists, deploy a vesting contract through factory.\n * @param _tokenOwner The owner of the tokens.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * @return The vesting contract address for the given token owner\n * whether it existed previously or not.\n * */\n function _getOrCreateVesting(\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration\n ) internal returns (address) {\n uint256 type_ = uint256(VestingType.Vesting);\n if (vestingContracts[_tokenOwner][type_] == address(0)) {\n /// @dev TODO: Owner of OwnerVesting contracts - the same address as tokenOwner.\n address vesting =\n vestingFactory.deployVesting(\n SOV,\n staking,\n _tokenOwner,\n _cliff,\n _duration,\n feeSharingCollector,\n _tokenOwner\n );\n vestingContracts[_tokenOwner][type_] = vesting;\n }\n return vestingContracts[_tokenOwner][type_];\n }\n\n /**\n * @notice If not exists, deploy a team vesting contract through factory.\n * @param _tokenOwner The owner of the tokens.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * @return The team vesting contract address for the given token owner\n * whether it existed previously or not.\n * */\n function _getOrCreateTeamVesting(\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration\n ) internal returns (address) {\n uint256 type_ = uint256(VestingType.TeamVesting);\n if (vestingContracts[_tokenOwner][type_] == address(0)) {\n address vesting =\n vestingFactory.deployTeamVesting(\n SOV,\n staking,\n _tokenOwner,\n _cliff,\n _duration,\n feeSharingCollector,\n vestingOwner\n );\n vestingContracts[_tokenOwner][type_] = vesting;\n }\n return vestingContracts[_tokenOwner][type_];\n }\n}\n" + }, + "contracts/governance/Vesting/VestingRegistry2.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"../../interfaces/IERC20.sol\";\nimport \"../Staking/interfaces/IStaking.sol\";\nimport \"../IFeeSharingCollector.sol\";\nimport \"./IVestingFactory.sol\";\nimport \"./IVesting.sol\";\nimport \"./ITeamVesting.sol\";\nimport \"../../openzeppelin/SafeMath.sol\";\n\n/**\n * @title VestingRegistry 2 contract.\n * @notice One time contract needed to distribute tokens to origin sales investors.\n * */\ncontract VestingRegistry2 is Ownable {\n using SafeMath for uint256;\n\n /* Storage */\n\n /// @notice Constant used for computing the vesting dates.\n uint256 public constant FOUR_WEEKS = 4 weeks;\n\n uint256 public constant CSOV_VESTING_CLIFF = FOUR_WEEKS;\n uint256 public constant CSOV_VESTING_DURATION = 10 * FOUR_WEEKS;\n\n IVestingFactory public vestingFactory;\n\n /// @notice The SOV token contract.\n address public SOV;\n\n /// @notice The CSOV token contracts.\n address[] public CSOVtokens;\n\n uint256 public priceSats;\n\n /// @notice The staking contract address.\n address public staking;\n\n /// @notice Fee sharing proxy.\n address public feeSharingCollector;\n\n /// @notice The vesting owner (e.g. governance timelock address).\n address public vestingOwner;\n\n /// @dev TODO: Add to the documentation: address can have only one vesting of each type.\n /// @dev user => vesting type => vesting contract\n mapping(address => mapping(uint256 => address)) public vestingContracts;\n\n /**\n * @dev Struct can be created to save storage slots, but it doesn't make\n * sense. We don't have a lot of blacklisted accounts or account with\n * locked amount.\n * */\n\n /// @dev user => flag whether user has already exchange cSOV or got a reimbursement.\n mapping(address => bool) public processedList;\n\n /// @dev user => flag whether user shouldn't be able to exchange or reimburse.\n mapping(address => bool) public blacklist;\n\n /// @dev user => amount of tokens should not be processed.\n mapping(address => uint256) public lockedAmount;\n\n /// @dev user => flag whether user has admin role.\n mapping(address => bool) public admins;\n\n enum VestingType {\n TeamVesting, // MultisigVesting\n Vesting // TokenHolderVesting\n }\n\n /* Events */\n\n event CSOVTokensExchanged(address indexed caller, uint256 amount);\n event SOVTransferred(address indexed receiver, uint256 amount);\n event VestingCreated(\n address indexed tokenOwner,\n address vesting,\n uint256 cliff,\n uint256 duration,\n uint256 amount\n );\n event TeamVestingCreated(\n address indexed tokenOwner,\n address vesting,\n uint256 cliff,\n uint256 duration,\n uint256 amount\n );\n event TokensStaked(address indexed vesting, uint256 amount);\n event AdminAdded(address admin);\n event AdminRemoved(address admin);\n\n /* Functions */\n\n /**\n * @notice Contract deployment settings.\n * @param _vestingFactory The address of vesting factory contract.\n * @param _SOV The SOV token address.\n * @param _CSOVtokens The array of cSOV tokens.\n * @param _priceSats The price of cSOV tokens in satoshis.\n * @param _staking The address of staking contract.\n * @param _feeSharingCollector The address of fee sharing proxy contract.\n * @param _vestingOwner The address of an owner of vesting contract.\n * @dev On Sovryn the vesting owner is Exchequer Multisig.\n * According to SIP-0007 The Exchequer Multisig is designated to hold\n * certain funds in the form of rBTC and SOV, in order to allow for\n * flexible deployment of such funds on:\n * + facilitating rBTC redemptions for Genesis pre-sale participants.\n * + deploying of SOV for the purposes of exchange listings, market\n * making, and partnerships with third parties.\n * */\n constructor(\n address _vestingFactory,\n address _SOV,\n address[] memory _CSOVtokens,\n uint256 _priceSats,\n address _staking,\n address _feeSharingCollector,\n address _vestingOwner\n ) public {\n require(_SOV != address(0), \"SOV address invalid\");\n require(_staking != address(0), \"staking address invalid\");\n require(_feeSharingCollector != address(0), \"feeSharingCollector address invalid\");\n require(_vestingOwner != address(0), \"vestingOwner address invalid\");\n\n _setVestingFactory(_vestingFactory);\n _setCSOVtokens(_CSOVtokens);\n\n SOV = _SOV;\n priceSats = _priceSats;\n staking = _staking;\n feeSharingCollector = _feeSharingCollector;\n vestingOwner = _vestingOwner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner or admin.\n */\n modifier onlyAuthorized() {\n require(isOwner() || admins[msg.sender], \"unauthorized\");\n _;\n }\n\n /**\n * @notice Add account to ACL.\n * @param _admin The addresses of the account to grant permissions.\n * */\n function addAdmin(address _admin) public onlyOwner {\n admins[_admin] = true;\n emit AdminAdded(_admin);\n }\n\n /**\n * @notice Remove account from ACL.\n * @param _admin The addresses of the account to revoke permissions.\n * */\n function removeAdmin(address _admin) public onlyOwner {\n admins[_admin] = false;\n emit AdminRemoved(_admin);\n }\n\n //---PostCSOV--------------------------------------------------------------\n\n modifier isNotProcessed() {\n require(!processedList[msg.sender], \"Address cannot be processed twice\");\n _;\n }\n\n modifier isNotBlacklisted() {\n require(!blacklist[msg.sender], \"Address blacklisted\");\n _;\n }\n\n /**\n * @notice Get contract balance.\n * @return The token balance of the contract.\n * */\n function budget() external view returns (uint256) {\n uint256 SCBudget = address(this).balance;\n return SCBudget;\n }\n\n /**\n * @notice Deposit function to receiving value (rBTC).\n * */\n function deposit() public payable {}\n\n /**\n * @notice Send all contract balance to an account.\n * @param to The account address to send the balance to.\n * */\n function withdrawAll(address payable to) public onlyOwner {\n to.transfer(address(this).balance);\n }\n\n //--------------------------------------------------------------------------------------------------------------------------------------\n\n /**\n * @notice Sets vesting factory address. High level endpoint.\n * @param _vestingFactory The address of vesting factory contract.\n *\n * @dev Splitting code on two functions: high level and low level\n * is a pattern that makes easy to extend functionality in a readable way,\n * without accidentally breaking the actual action being performed.\n * For example, checks should be done on high level endpoint, while core\n * functionality should be coded on the low level function.\n * */\n function setVestingFactory(address _vestingFactory) public onlyOwner {\n _setVestingFactory(_vestingFactory);\n }\n\n /**\n * @notice Sets vesting factory address. Low level core function.\n * @param _vestingFactory The address of vesting factory contract.\n * */\n function _setVestingFactory(address _vestingFactory) internal {\n require(_vestingFactory != address(0), \"vestingFactory address invalid\");\n vestingFactory = IVestingFactory(_vestingFactory);\n }\n\n /**\n * @notice Sets cSOV tokens array. High level endpoint.\n * @param _CSOVtokens The array of cSOV tokens.\n * */\n function setCSOVtokens(address[] memory _CSOVtokens) public onlyOwner {\n _setCSOVtokens(_CSOVtokens);\n }\n\n /**\n * @notice Sets cSOV tokens array by looping through input. Low level function.\n * @param _CSOVtokens The array of cSOV tokens.\n * */\n function _setCSOVtokens(address[] memory _CSOVtokens) internal {\n for (uint256 i = 0; i < _CSOVtokens.length; i++) {\n require(_CSOVtokens[i] != address(0), \"CSOV address invalid\");\n }\n CSOVtokens = _CSOVtokens;\n }\n\n /**\n * @notice Set blacklist flag (true/false).\n * @param _account The address to be blacklisted.\n * @param _blacklisted The flag to add/remove to/from a blacklist.\n * */\n function setBlacklistFlag(address _account, bool _blacklisted) public onlyOwner {\n require(_account != address(0), \"account address invalid\");\n\n blacklist[_account] = _blacklisted;\n }\n\n /**\n * @notice Set amount to be subtracted from user token balance.\n * @param _account The address with locked amount.\n * @param _amount The amount to be locked.\n * */\n function setLockedAmount(address _account, uint256 _amount) public onlyOwner {\n require(_account != address(0), \"account address invalid\");\n require(_amount != 0, \"amount invalid\");\n\n lockedAmount[_account] = _amount;\n }\n\n /**\n * @notice Transfer SOV tokens to given address.\n *\n * @dev This is a wrapper for ERC-20 transfer function w/\n * additional checks and triggering an event.\n *\n * @param _receiver The address of the SOV receiver.\n * @param _amount The amount to be transferred.\n * */\n function transferSOV(address _receiver, uint256 _amount) public onlyOwner {\n require(_receiver != address(0), \"receiver address invalid\");\n require(_amount != 0, \"amount invalid\");\n\n IERC20(SOV).transfer(_receiver, _amount);\n emit SOVTransferred(_receiver, _amount);\n }\n\n /**\n * @notice cSOV tokens are moved and staked on Vesting contract.\n * @param _amount The amount of tokens to be vested.\n * */\n function _createVestingForCSOV(uint256 _amount) internal {\n address vesting =\n _getOrCreateVesting(msg.sender, CSOV_VESTING_CLIFF, CSOV_VESTING_DURATION);\n\n IERC20(SOV).approve(vesting, _amount);\n IVesting(vesting).stakeTokens(_amount);\n\n emit CSOVTokensExchanged(msg.sender, _amount);\n }\n\n /**\n * @notice Check a token address is among the cSOV token addresses.\n * @param _CSOV The cSOV token address.\n * */\n function _validateCSOV(address _CSOV) internal view {\n bool isValid = false;\n for (uint256 i = 0; i < CSOVtokens.length; i++) {\n if (_CSOV == CSOVtokens[i]) {\n isValid = true;\n break;\n }\n }\n require(isValid, \"wrong CSOV address\");\n }\n\n /**\n * @notice Create Vesting contract.\n * @param _tokenOwner The owner of the tokens.\n * @param _amount The amount to be staked.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * */\n function createVesting(\n address _tokenOwner,\n uint256 _amount,\n uint256 _cliff,\n uint256 _duration\n ) public onlyAuthorized {\n address vesting = _getOrCreateVesting(_tokenOwner, _cliff, _duration);\n emit VestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount);\n }\n\n /**\n * @notice Create Team Vesting contract.\n * @param _tokenOwner The owner of the tokens.\n * @param _amount The amount to be staked.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * */\n function createTeamVesting(\n address _tokenOwner,\n uint256 _amount,\n uint256 _cliff,\n uint256 _duration\n ) public onlyAuthorized {\n address vesting = _getOrCreateTeamVesting(_tokenOwner, _cliff, _duration);\n emit TeamVestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount);\n }\n\n /**\n * @notice Stake tokens according to the vesting schedule\n * @param _vesting the address of Vesting contract\n * @param _amount the amount of tokens to stake\n * */\n function stakeTokens(address _vesting, uint256 _amount) public onlyAuthorized {\n require(_vesting != address(0), \"vesting address invalid\");\n require(_amount > 0, \"amount invalid\");\n\n IERC20(SOV).approve(_vesting, _amount);\n IVesting(_vesting).stakeTokens(_amount);\n emit TokensStaked(_vesting, _amount);\n }\n\n /**\n * @notice Query the vesting contract for an account.\n * @param _tokenOwner The owner of the tokens.\n * @return The vesting contract address for the given token owner.\n * */\n function getVesting(address _tokenOwner) public view returns (address) {\n return vestingContracts[_tokenOwner][uint256(VestingType.Vesting)];\n }\n\n /**\n * @notice Query the team vesting contract for an account.\n * @param _tokenOwner The owner of the tokens.\n * @return The team vesting contract address for the given token owner.\n * */\n function getTeamVesting(address _tokenOwner) public view returns (address) {\n return vestingContracts[_tokenOwner][uint256(VestingType.TeamVesting)];\n }\n\n /**\n * @notice If not exists, deploy a vesting contract through factory.\n * @param _tokenOwner The owner of the tokens.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * @return The vesting contract address for the given token owner\n * whether it existed previously or not.\n * */\n function _getOrCreateVesting(\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration\n ) internal returns (address) {\n uint256 type_ = uint256(VestingType.Vesting);\n if (vestingContracts[_tokenOwner][type_] == address(0)) {\n //TODO Owner of OwnerVesting contracts - the same address as tokenOwner\n address vesting =\n vestingFactory.deployVesting(\n SOV,\n staking,\n _tokenOwner,\n _cliff,\n _duration,\n feeSharingCollector,\n _tokenOwner\n );\n vestingContracts[_tokenOwner][type_] = vesting;\n }\n return vestingContracts[_tokenOwner][type_];\n }\n\n /**\n * @notice If not exists, deploy a team vesting contract through factory.\n * @param _tokenOwner The owner of the tokens.\n * @param _cliff The time interval to the first withdraw in seconds.\n * @param _duration The total duration in seconds.\n * @return The team vesting contract address for the given token owner\n * whether it existed previously or not.\n * */\n function _getOrCreateTeamVesting(\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration\n ) internal returns (address) {\n uint256 type_ = uint256(VestingType.TeamVesting);\n if (vestingContracts[_tokenOwner][type_] == address(0)) {\n address vesting =\n vestingFactory.deployTeamVesting(\n SOV,\n staking,\n _tokenOwner,\n _cliff,\n _duration,\n feeSharingCollector,\n vestingOwner\n );\n vestingContracts[_tokenOwner][type_] = vesting;\n }\n return vestingContracts[_tokenOwner][type_];\n }\n}\n" + }, + "contracts/governance/Vesting/VestingRegistry3.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"../../interfaces/IERC20.sol\";\nimport \"../Staking/interfaces/IStaking.sol\";\nimport \"../IFeeSharingCollector.sol\";\nimport \"./IVestingFactory.sol\";\nimport \"./IVesting.sol\";\nimport \"./ITeamVesting.sol\";\nimport \"../../openzeppelin/SafeMath.sol\";\n\ncontract VestingRegistry3 is Ownable {\n using SafeMath for uint256;\n\n IVestingFactory public vestingFactory;\n\n ///@notice the SOV token contract\n address public SOV;\n\n ///@notice the staking contract address\n address public staking;\n //@notice fee sharing proxy\n address public feeSharingCollector;\n //@notice the vesting owner (e.g. governance timelock address)\n address public vestingOwner;\n\n //TODO add to the documentation: address can have only one vesting of each type\n //user => vesting type => vesting contract\n mapping(address => mapping(uint256 => address)) public vestingContracts;\n\n //user => flag whether user has admin role\n mapping(address => bool) public admins;\n\n enum VestingType {\n TeamVesting, //MultisigVesting\n Vesting //TokenHolderVesting\n }\n\n event SOVTransferred(address indexed receiver, uint256 amount);\n event VestingCreated(\n address indexed tokenOwner,\n address vesting,\n uint256 cliff,\n uint256 duration,\n uint256 amount\n );\n event TeamVestingCreated(\n address indexed tokenOwner,\n address vesting,\n uint256 cliff,\n uint256 duration,\n uint256 amount\n );\n event TokensStaked(address indexed vesting, uint256 amount);\n event AdminAdded(address admin);\n event AdminRemoved(address admin);\n\n constructor(\n address _vestingFactory,\n address _SOV,\n address _staking,\n address _feeSharingCollector,\n address _vestingOwner\n ) public {\n require(_SOV != address(0), \"SOV address invalid\");\n require(_staking != address(0), \"staking address invalid\");\n require(_feeSharingCollector != address(0), \"feeSharingCollector address invalid\");\n require(_vestingOwner != address(0), \"vestingOwner address invalid\");\n\n _setVestingFactory(_vestingFactory);\n\n SOV = _SOV;\n staking = _staking;\n feeSharingCollector = _feeSharingCollector;\n vestingOwner = _vestingOwner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner or admin.\n */\n modifier onlyAuthorized() {\n require(isOwner() || admins[msg.sender], \"unauthorized\");\n _;\n }\n\n function addAdmin(address _admin) public onlyOwner {\n admins[_admin] = true;\n emit AdminAdded(_admin);\n }\n\n function removeAdmin(address _admin) public onlyOwner {\n admins[_admin] = false;\n emit AdminRemoved(_admin);\n }\n\n /**\n * @notice sets vesting factory address\n * @param _vestingFactory the address of vesting factory contract\n */\n function setVestingFactory(address _vestingFactory) public onlyOwner {\n _setVestingFactory(_vestingFactory);\n }\n\n function _setVestingFactory(address _vestingFactory) internal {\n require(_vestingFactory != address(0), \"vestingFactory address invalid\");\n vestingFactory = IVestingFactory(_vestingFactory);\n }\n\n /**\n * @notice transfers SOV tokens to given address\n * @param _receiver the address of the SOV receiver\n * @param _amount the amount to be transferred\n */\n function transferSOV(address _receiver, uint256 _amount) public onlyOwner {\n require(_receiver != address(0), \"receiver address invalid\");\n require(_amount != 0, \"amount invalid\");\n\n IERC20(SOV).transfer(_receiver, _amount);\n emit SOVTransferred(_receiver, _amount);\n }\n\n /**\n * @notice creates Vesting contract\n * @param _tokenOwner the owner of the tokens\n * @param _amount the amount to be staked\n * @param _cliff the cliff in seconds\n * @param _duration the total duration in seconds\n */\n function createVesting(\n address _tokenOwner,\n uint256 _amount,\n uint256 _cliff,\n uint256 _duration\n ) public onlyAuthorized {\n address vesting = _getOrCreateVesting(_tokenOwner, _cliff, _duration);\n emit VestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount);\n }\n\n /**\n * @notice creates Team Vesting contract\n * @param _tokenOwner the owner of the tokens\n * @param _amount the amount to be staked\n * @param _cliff the cliff in seconds\n * @param _duration the total duration in seconds\n */\n function createTeamVesting(\n address _tokenOwner,\n uint256 _amount,\n uint256 _cliff,\n uint256 _duration\n ) public onlyAuthorized {\n address vesting = _getOrCreateTeamVesting(_tokenOwner, _cliff, _duration);\n emit TeamVestingCreated(_tokenOwner, vesting, _cliff, _duration, _amount);\n }\n\n /**\n * @notice stakes tokens according to the vesting schedule\n * @param _vesting the address of Vesting contract\n * @param _amount the amount of tokens to stake\n */\n function stakeTokens(address _vesting, uint256 _amount) public onlyAuthorized {\n require(_vesting != address(0), \"vesting address invalid\");\n require(_amount > 0, \"amount invalid\");\n\n IERC20(SOV).approve(_vesting, _amount);\n IVesting(_vesting).stakeTokens(_amount);\n emit TokensStaked(_vesting, _amount);\n }\n\n /**\n * @notice returns vesting contract address for the given token owner\n * @param _tokenOwner the owner of the tokens\n */\n function getVesting(address _tokenOwner) public view returns (address) {\n return vestingContracts[_tokenOwner][uint256(VestingType.Vesting)];\n }\n\n /**\n * @notice returns team vesting contract address for the given token owner\n * @param _tokenOwner the owner of the tokens\n */\n function getTeamVesting(address _tokenOwner) public view returns (address) {\n return vestingContracts[_tokenOwner][uint256(VestingType.TeamVesting)];\n }\n\n function _getOrCreateVesting(\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration\n ) internal returns (address) {\n uint256 type_ = uint256(VestingType.Vesting);\n if (vestingContracts[_tokenOwner][type_] == address(0)) {\n //TODO Owner of OwnerVesting contracts - the same address as tokenOwner\n address vesting =\n vestingFactory.deployVesting(\n SOV,\n staking,\n _tokenOwner,\n _cliff,\n _duration,\n feeSharingCollector,\n _tokenOwner\n );\n vestingContracts[_tokenOwner][type_] = vesting;\n }\n return vestingContracts[_tokenOwner][type_];\n }\n\n function _getOrCreateTeamVesting(\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration\n ) internal returns (address) {\n uint256 type_ = uint256(VestingType.TeamVesting);\n if (vestingContracts[_tokenOwner][type_] == address(0)) {\n address vesting =\n vestingFactory.deployTeamVesting(\n SOV,\n staking,\n _tokenOwner,\n _cliff,\n _duration,\n feeSharingCollector,\n vestingOwner\n );\n vestingContracts[_tokenOwner][type_] = vesting;\n }\n return vestingContracts[_tokenOwner][type_];\n }\n}\n" + }, + "contracts/governance/Vesting/VestingRegistryLogic.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../interfaces/IERC20.sol\";\nimport \"../IFeeSharingCollector.sol\";\nimport \"./IVesting.sol\";\nimport \"./ITeamVesting.sol\";\nimport \"./VestingRegistryStorage.sol\";\n\ncontract VestingRegistryLogic is VestingRegistryStorage {\n event SOVTransferred(address indexed receiver, uint256 amount);\n event VestingCreated(\n address indexed tokenOwner,\n address vesting,\n uint256 cliff,\n uint256 duration,\n uint256 amount,\n uint256 vestingCreationType\n );\n event TeamVestingCreated(\n address indexed tokenOwner,\n address vesting,\n uint256 cliff,\n uint256 duration,\n uint256 amount,\n uint256 vestingCreationType\n );\n event TokensStaked(address indexed vesting, uint256 amount);\n event VestingCreationAndTypesSet(\n address indexed vesting,\n VestingCreationAndTypeDetails vestingCreationAndType\n );\n\n /**\n * @notice Replace constructor with initialize function for Upgradable Contracts\n * This function will be called only once by the owner\n * */\n function initialize(\n address _vestingFactory,\n address _SOV,\n address _staking,\n address _feeSharingCollector,\n address _vestingOwner,\n address _lockedSOV,\n address[] calldata _vestingRegistries\n ) external onlyOwner initializer {\n require(_SOV != address(0), \"SOV address invalid\");\n require(_staking != address(0), \"staking address invalid\");\n require(_feeSharingCollector != address(0), \"feeSharingCollector address invalid\");\n require(_vestingOwner != address(0), \"vestingOwner address invalid\");\n require(_lockedSOV != address(0), \"LockedSOV address invalid\");\n\n _setVestingFactory(_vestingFactory);\n SOV = _SOV;\n staking = _staking;\n feeSharingCollector = _feeSharingCollector;\n vestingOwner = _vestingOwner;\n lockedSOV = LockedSOV(_lockedSOV);\n for (uint256 i = 0; i < _vestingRegistries.length; i++) {\n require(_vestingRegistries[i] != address(0), \"Vesting registry address invalid\");\n vestingRegistries.push(IVestingRegistry(_vestingRegistries[i]));\n }\n }\n\n /**\n * @notice sets vesting factory address\n * @param _vestingFactory the address of vesting factory contract\n */\n function setVestingFactory(address _vestingFactory) external onlyOwner {\n _setVestingFactory(_vestingFactory);\n }\n\n /**\n * @notice Internal function that sets vesting factory address\n * @param _vestingFactory the address of vesting factory contract\n */\n function _setVestingFactory(address _vestingFactory) internal {\n require(_vestingFactory != address(0), \"vestingFactory address invalid\");\n vestingFactory = IVestingFactory(_vestingFactory);\n }\n\n /**\n * @notice transfers SOV tokens to given address\n * @param _receiver the address of the SOV receiver\n * @param _amount the amount to be transferred\n */\n function transferSOV(address _receiver, uint256 _amount) external onlyOwner {\n require(_receiver != address(0), \"receiver address invalid\");\n require(_amount != 0, \"amount invalid\");\n require(IERC20(SOV).transfer(_receiver, _amount), \"transfer failed\");\n emit SOVTransferred(_receiver, _amount);\n }\n\n /**\n * @notice adds vestings that were deployed in previous vesting registries\n * @dev migration of data from previous vesting registy contracts\n */\n function addDeployedVestings(\n address[] calldata _tokenOwners,\n uint256[] calldata _vestingCreationTypes\n ) external onlyAuthorized {\n for (uint256 i = 0; i < _tokenOwners.length; i++) {\n require(_tokenOwners[i] != address(0), \"token owner cannot be 0 address\");\n require(_vestingCreationTypes[i] > 0, \"vesting creation type must be greater than 0\");\n _addDeployedVestings(_tokenOwners[i], _vestingCreationTypes[i]);\n }\n }\n\n /**\n * @notice adds four year vestings to vesting registry logic\n * @param _tokenOwners array of token owners\n * @param _vestingAddresses array of vesting addresses\n */\n function addFourYearVestings(\n address[] calldata _tokenOwners,\n address[] calldata _vestingAddresses\n ) external onlyAuthorized {\n require(_tokenOwners.length == _vestingAddresses.length, \"arrays mismatch\");\n uint256 vestingCreationType = 4;\n uint256 cliff = 4 weeks;\n uint256 duration = 156 weeks;\n for (uint256 i = 0; i < _tokenOwners.length; i++) {\n require(!isVesting[_vestingAddresses[i]], \"vesting exists\");\n require(_tokenOwners[i] != address(0), \"token owner cannot be 0 address\");\n require(_vestingAddresses[i] != address(0), \"vesting cannot be 0 address\");\n uint256 uid =\n uint256(\n keccak256(\n abi.encodePacked(\n _tokenOwners[i],\n uint256(VestingType.Vesting),\n cliff,\n duration,\n vestingCreationType\n )\n )\n );\n vestings[uid] = Vesting(\n uint256(VestingType.Vesting),\n vestingCreationType,\n _vestingAddresses[i]\n );\n vestingsOf[_tokenOwners[i]].push(uid);\n isVesting[_vestingAddresses[i]] = true;\n }\n }\n\n /**\n * @notice creates Vesting contract\n * @param _tokenOwner the owner of the tokens\n * @param _amount the amount to be staked\n * @param _cliff the cliff in seconds\n * @param _duration the total duration in seconds\n * @dev Calls a public createVestingAddr function with vestingCreationType. This is to accomodate the existing logic for LockedSOV\n * @dev vestingCreationType 0 = LockedSOV\n */\n function createVesting(\n address _tokenOwner,\n uint256 _amount,\n uint256 _cliff,\n uint256 _duration\n ) external onlyAuthorized {\n createVestingAddr(_tokenOwner, _amount, _cliff, _duration, 3);\n }\n\n /**\n * @notice creates Vesting contract\n * @param _tokenOwner the owner of the tokens\n * @param _amount the amount to be staked\n * @param _cliff the cliff in seconds\n * @param _duration the total duration in seconds\n * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.)\n */\n function createVestingAddr(\n address _tokenOwner,\n uint256 _amount,\n uint256 _cliff,\n uint256 _duration,\n uint256 _vestingCreationType\n ) public onlyAuthorized {\n address vesting =\n _getOrCreateVesting(\n _tokenOwner,\n _cliff,\n _duration,\n uint256(VestingType.Vesting),\n _vestingCreationType\n );\n\n emit VestingCreated(\n _tokenOwner,\n vesting,\n _cliff,\n _duration,\n _amount,\n _vestingCreationType\n );\n }\n\n /**\n * @notice creates Team Vesting contract\n * @param _tokenOwner the owner of the tokens\n * @param _amount the amount to be staked\n * @param _cliff the cliff in seconds\n * @param _duration the total duration in seconds\n * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.)\n */\n function createTeamVesting(\n address _tokenOwner,\n uint256 _amount,\n uint256 _cliff,\n uint256 _duration,\n uint256 _vestingCreationType\n ) external onlyAuthorized {\n address vesting =\n _getOrCreateVesting(\n _tokenOwner,\n _cliff,\n _duration,\n uint256(VestingType.TeamVesting),\n _vestingCreationType\n );\n\n emit TeamVestingCreated(\n _tokenOwner,\n vesting,\n _cliff,\n _duration,\n _amount,\n _vestingCreationType\n );\n }\n\n /**\n * @notice stakes tokens according to the vesting schedule\n * @param _vesting the address of Vesting contract\n * @param _amount the amount of tokens to stake\n */\n function stakeTokens(address _vesting, uint256 _amount) external onlyAuthorized {\n require(_vesting != address(0), \"vesting address invalid\");\n require(_amount > 0, \"amount invalid\");\n\n IERC20(SOV).approve(_vesting, _amount);\n IVesting(_vesting).stakeTokens(_amount);\n emit TokensStaked(_vesting, _amount);\n }\n\n /**\n * @notice returns vesting contract address for the given token owner\n * @param _tokenOwner the owner of the tokens\n * @dev Calls a public getVestingAddr function with cliff and duration. This is to accomodate the existing logic for LockedSOV\n * @dev We need to use LockedSOV.changeRegistryCliffAndDuration function very judiciously\n * @dev vestingCreationType 0 - LockedSOV\n */\n function getVesting(address _tokenOwner) public view returns (address) {\n return getVestingAddr(_tokenOwner, lockedSOV.cliff(), lockedSOV.duration(), 3);\n }\n\n /**\n * @notice public function that returns vesting contract address for the given token owner, cliff, duration\n * @dev Important: Please use this instead of getVesting function\n */\n function getVestingAddr(\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration,\n uint256 _vestingCreationType\n ) public view returns (address) {\n uint256 type_ = uint256(VestingType.Vesting);\n uint256 uid =\n uint256(\n keccak256(\n abi.encodePacked(_tokenOwner, type_, _cliff, _duration, _vestingCreationType)\n )\n );\n return vestings[uid].vestingAddress;\n }\n\n /**\n * @notice returns team vesting contract address for the given token owner, cliff, duration\n */\n function getTeamVesting(\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration,\n uint256 _vestingCreationType\n ) public view returns (address) {\n uint256 type_ = uint256(VestingType.TeamVesting);\n uint256 uid =\n uint256(\n keccak256(\n abi.encodePacked(_tokenOwner, type_, _cliff, _duration, _vestingCreationType)\n )\n );\n return vestings[uid].vestingAddress;\n }\n\n /**\n * @dev check if the specific vesting address is team vesting or not\n * @dev read the vestingType from vestingCreationAndTypes storage\n *\n * @param _vestingAddress address of vesting contract\n *\n * @return true for teamVesting, false for normal vesting\n */\n function isTeamVesting(address _vestingAddress) external view returns (bool) {\n return (vestingCreationAndTypes[_vestingAddress].isSet &&\n vestingCreationAndTypes[_vestingAddress].vestingType ==\n uint32(VestingType.TeamVesting));\n }\n\n /**\n * @dev setter function to register existing vesting contract to vestingCreationAndTypes storage\n * @dev need to set the function visilibty to public to support VestingCreationAndTypeDetails struct as parameter\n *\n * @param _vestingAddresses array of vesting address\n * @param _vestingCreationAndTypes array for VestingCreationAndTypeDetails struct\n */\n function registerVestingToVestingCreationAndTypes(\n address[] memory _vestingAddresses,\n VestingCreationAndTypeDetails[] memory _vestingCreationAndTypes\n ) public onlyAuthorized {\n require(_vestingAddresses.length == _vestingCreationAndTypes.length, \"Unmatched length\");\n for (uint256 i = 0; i < _vestingCreationAndTypes.length; i++) {\n VestingCreationAndTypeDetails memory _vestingCreationAndType =\n _vestingCreationAndTypes[i];\n address _vestingAddress = _vestingAddresses[i];\n\n vestingCreationAndTypes[_vestingAddress] = _vestingCreationAndType;\n\n emit VestingCreationAndTypesSet(\n _vestingAddress,\n vestingCreationAndTypes[_vestingAddress]\n );\n }\n }\n\n /**\n * @notice Internal function to deploy Vesting/Team Vesting contract\n * @param _tokenOwner the owner of the tokens\n * @param _cliff the cliff in seconds\n * @param _duration the total duration in seconds\n * @param _type the type of vesting\n * @param _vestingCreationType the type of vesting created(e.g. Origin, Bug Bounty etc.)\n */\n function _getOrCreateVesting(\n address _tokenOwner,\n uint256 _cliff,\n uint256 _duration,\n uint256 _type,\n uint256 _vestingCreationType\n ) internal returns (address) {\n address vesting;\n uint256 uid =\n uint256(\n keccak256(\n abi.encodePacked(_tokenOwner, _type, _cliff, _duration, _vestingCreationType)\n )\n );\n if (vestings[uid].vestingAddress == address(0)) {\n if (_type == 1) {\n vesting = vestingFactory.deployVesting(\n SOV,\n staking,\n _tokenOwner,\n _cliff,\n _duration,\n feeSharingCollector,\n _tokenOwner\n );\n } else {\n vesting = vestingFactory.deployTeamVesting(\n SOV,\n staking,\n _tokenOwner,\n _cliff,\n _duration,\n feeSharingCollector,\n vestingOwner\n );\n }\n vestings[uid] = Vesting(_type, _vestingCreationType, vesting);\n vestingsOf[_tokenOwner].push(uid);\n isVesting[vesting] = true;\n\n vestingCreationAndTypes[vesting] = VestingCreationAndTypeDetails({\n isSet: true,\n vestingType: uint32(_type),\n vestingCreationType: uint128(_vestingCreationType)\n });\n\n emit VestingCreationAndTypesSet(vesting, vestingCreationAndTypes[vesting]);\n }\n return vestings[uid].vestingAddress;\n }\n\n /**\n * @notice stores the addresses of Vesting contracts from all three previous versions of Vesting Registry\n */\n function _addDeployedVestings(address _tokenOwner, uint256 _vestingCreationType) internal {\n uint256 uid;\n uint256 i = _vestingCreationType - 1;\n\n address vestingAddress = vestingRegistries[i].getVesting(_tokenOwner);\n if (vestingAddress != address(0)) {\n VestingLogic vesting = VestingLogic(vestingAddress);\n uid = uint256(\n keccak256(\n abi.encodePacked(\n _tokenOwner,\n uint256(VestingType.Vesting),\n vesting.cliff(),\n vesting.duration(),\n _vestingCreationType\n )\n )\n );\n vestings[uid] = Vesting(\n uint256(VestingType.Vesting),\n _vestingCreationType,\n vestingAddress\n );\n vestingsOf[_tokenOwner].push(uid);\n isVesting[vestingAddress] = true;\n }\n\n address teamVestingAddress = vestingRegistries[i].getTeamVesting(_tokenOwner);\n if (teamVestingAddress != address(0)) {\n VestingLogic vesting = VestingLogic(teamVestingAddress);\n uid = uint256(\n keccak256(\n abi.encodePacked(\n _tokenOwner,\n uint256(VestingType.TeamVesting),\n vesting.cliff(),\n vesting.duration(),\n _vestingCreationType\n )\n )\n );\n vestings[uid] = Vesting(\n uint256(VestingType.TeamVesting),\n _vestingCreationType,\n teamVestingAddress\n );\n vestingsOf[_tokenOwner].push(uid);\n isVesting[teamVestingAddress] = true;\n }\n }\n\n /**\n * @notice returns all vesting details for the given token owner\n */\n function getVestingsOf(address _tokenOwner) external view returns (Vesting[] memory) {\n uint256[] memory vestingIds = vestingsOf[_tokenOwner];\n uint256 length = vestingIds.length;\n Vesting[] memory _vestings = new Vesting[](vestingIds.length);\n for (uint256 i = 0; i < length; i++) {\n _vestings[i] = vestings[vestingIds[i]];\n }\n return _vestings;\n }\n\n /**\n * @notice returns cliff and duration for Vesting & TeamVesting contracts\n */\n function getVestingDetails(address _vestingAddress)\n external\n view\n returns (uint256 cliff, uint256 duration)\n {\n VestingLogic vesting = VestingLogic(_vestingAddress);\n return (vesting.cliff(), vesting.duration());\n }\n\n /**\n * @notice returns if the address is a vesting address\n */\n function isVestingAddress(address _vestingAddress) external view returns (bool isVestingAddr) {\n return isVesting[_vestingAddress];\n }\n}\n" + }, + "contracts/governance/Vesting/VestingRegistryProxy.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./VestingRegistryStorage.sol\";\nimport \"../../proxy/UpgradableProxy.sol\";\n\n/**\n * @title Vesting Registry Proxy contract.\n * @dev Vesting Registry contract should be upgradable, use UpgradableProxy.\n * VestingRegistryStorage is deployed with the upgradable functionality\n * by using this contract instead, that inherits from UpgradableProxy\n * the possibility of being enhanced and re-deployed.\n * */\ncontract VestingRegistryProxy is VestingRegistryStorage, UpgradableProxy {\n\n}\n" + }, + "contracts/governance/Vesting/VestingRegistryStorage.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../openzeppelin/Initializable.sol\";\nimport \"../../utils/AdminRoleManaged.sol\";\nimport \"../../interfaces/IERC20.sol\";\nimport \"./IVestingFactory.sol\";\nimport \"../../locked/LockedSOV.sol\";\nimport \"./IVestingRegistry.sol\";\n\n/**\n * @title Vesting Registry Storage Contract.\n *\n * @notice This contract is just the storage required for vesting registry.\n * It is parent of VestingRegistryProxy and VestingRegistryLogic.\n *\n * @dev Use Ownable as a parent to align storage structure for Logic and Proxy contracts.\n * */\n\ncontract VestingRegistryStorage is Initializable, AdminRoleManaged {\n ///@notice the vesting factory contract\n IVestingFactory public vestingFactory;\n\n ///@notice the Locked SOV contract\n ///@dev NOTES: No need to update lockedSOV in this contract, since it might break the vestingRegistry if the new lockedSOV does not have the same value of cliff & duration.\n ILockedSOV public lockedSOV;\n\n ///@notice the list of vesting registries\n IVestingRegistry[] public vestingRegistries;\n\n ///@notice the SOV token contract\n address public SOV;\n\n ///@notice the staking contract address\n address public staking;\n\n ///@notice fee sharing proxy\n address public feeSharingCollector;\n\n ///@notice the vesting owner (e.g. governance timelock address)\n address public vestingOwner;\n\n enum VestingType {\n TeamVesting, //MultisigVesting\n Vesting //TokenHolderVesting\n }\n\n ///@notice Vesting details\n struct Vesting {\n uint256 vestingType;\n uint256 vestingCreationType;\n address vestingAddress;\n }\n\n ///@notice A record of vesting details for a unique id\n ///@dev vestings[uid] returns vesting data\n mapping(uint256 => Vesting) public vestings;\n\n ///@notice A record of all unique ids for a particular token owner\n ///@dev vestingsOf[tokenOwner] returns array of unique ids\n mapping(address => uint256[]) public vestingsOf;\n\n ///@notice A record of all vesting addresses\n ///@dev isVesting[address] returns if the address is a vesting address\n mapping(address => bool) public isVesting;\n\n /// @notice Store vesting creation type & vesting type information\n /// @dev it is packed into 1 single storage slot for cheaper gas usage\n struct VestingCreationAndTypeDetails {\n bool isSet;\n uint32 vestingType;\n uint128 vestingCreationType;\n }\n\n ///@notice A record of all vesting addresses with the detail\n ///@dev vestingDetail[vestingAddress] returns Vesting struct data\n ///@dev can be used to easily check the vesting type / creation type based on the vesting address itself\n mapping(address => VestingCreationAndTypeDetails) public vestingCreationAndTypes;\n}\n" + }, + "contracts/governance/Vesting/VestingStorage.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../openzeppelin/Ownable.sol\";\nimport \"../../interfaces/IERC20.sol\";\nimport \"../Staking/interfaces/IStaking.sol\";\nimport \"../IFeeSharingCollector.sol\";\n\n/**\n * @title Vesting Storage Contract.\n *\n * @notice This contract is just the storage required for vesting.\n * It is parent of VestingLogic and TeamVesting.\n *\n * @dev Use Ownable as a parent to align storage structure for Logic and Proxy contracts.\n * */\ncontract VestingStorage is Ownable {\n /// @notice The SOV token contract.\n IERC20 public SOV;\n\n /// @notice The staking contract address.\n IStaking public staking;\n\n /// @notice The owner of the vested tokens.\n address public tokenOwner;\n\n /// @notice Fee sharing Proxy.\n IFeeSharingCollector public feeSharingCollector;\n\n /// @notice The cliff. After this time period the tokens begin to unlock.\n uint256 public cliff;\n\n /// @notice The duration. After this period all tokens will have been unlocked.\n uint256 public duration;\n\n /// @notice The start date of the vesting.\n uint256 public startDate;\n\n /// @notice The end date of the vesting.\n uint256 public endDate;\n\n /// @notice Constant used for computing the vesting dates.\n uint256 constant FOUR_WEEKS = 4 weeks;\n}\n" + }, + "contracts/interfaces/IChai.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity >=0.5.0 <0.6.0;\n\nimport \"./IERC20.sol\";\n\ninterface IPot {\n function dsr() external view returns (uint256);\n\n function chi() external view returns (uint256);\n\n function rho() external view returns (uint256);\n}\n\ncontract IChai is IERC20 {\n function move(\n address src,\n address dst,\n uint256 wad\n ) external returns (bool);\n\n function join(address dst, uint256 wad) external;\n\n function draw(address src, uint256 wad) external;\n\n function exit(address src, uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IConverterAMM.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\ninterface IConverterAMM {\n function withdrawFees(address receiver) external returns (uint256);\n}\n" + }, + "contracts/interfaces/IERC20.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity >=0.5.0 <0.6.0;\n\ncontract IERC20 {\n string public name;\n uint8 public decimals;\n string public symbol;\n\n function totalSupply() external view returns (uint256);\n\n function balanceOf(address _who) external view returns (uint256);\n\n function allowance(address _owner, address _spender) external view returns (uint256);\n\n function approve(address _spender, uint256 _value) external returns (bool);\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "contracts/interfaces/IERC777.sol": { + "content": "pragma solidity ^0.5.0;\n\n/**\n * @dev Interface of the ERC777Token standard as defined in the EIP.\n *\n * This contract uses the\n * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let\n * token holders and recipients react to token movements by using setting implementers\n * for the associated interfaces in said registry. See {IERC1820Registry} and\n * {ERC1820Implementer}.\n */\ninterface IERC777 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the smallest part of the token that is not divisible. This\n * means all token operations (creation, movement and destruction) must have\n * amounts that are a multiple of this number.\n *\n * For most token contracts, this value will equal 1.\n */\n function granularity() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by an account (`owner`).\n */\n function balanceOf(address owner) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * If send or receive hooks are registered for the caller and `recipient`,\n * the corresponding functions will be called with `data` and empty\n * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.\n *\n * Emits a {Sent} event.\n *\n * Requirements\n *\n * - the caller must have at least `amount` tokens.\n * - `recipient` cannot be the zero address.\n * - if `recipient` is a contract, it must implement the {IERC777Recipient}\n * interface.\n */\n function send(\n address recipient,\n uint256 amount,\n bytes calldata data\n ) external;\n\n /**\n * @dev Destroys `amount` tokens from the caller's account, reducing the\n * total supply.\n *\n * If a send hook is registered for the caller, the corresponding function\n * will be called with `data` and empty `operatorData`. See {IERC777Sender}.\n *\n * Emits a {Burned} event.\n *\n * Requirements\n *\n * - the caller must have at least `amount` tokens.\n */\n function burn(uint256 amount, bytes calldata data) external;\n\n /**\n * @dev Returns true if an account is an operator of `tokenHolder`.\n * Operators can send and burn tokens on behalf of their owners. All\n * accounts are their own operator.\n *\n * See {operatorSend} and {operatorBurn}.\n */\n function isOperatorFor(address operator, address tokenHolder) external view returns (bool);\n\n /**\n * @dev Make an account an operator of the caller.\n *\n * See {isOperatorFor}.\n *\n * Emits an {AuthorizedOperator} event.\n *\n * Requirements\n *\n * - `operator` cannot be calling address.\n */\n function authorizeOperator(address operator) external;\n\n /**\n * @dev Make an account an operator of the caller.\n *\n * See {isOperatorFor} and {defaultOperators}.\n *\n * Emits a {RevokedOperator} event.\n *\n * Requirements\n *\n * - `operator` cannot be calling address.\n */\n function revokeOperator(address operator) external;\n\n /**\n * @dev Returns the list of default operators. These accounts are operators\n * for all token holders, even if {authorizeOperator} was never called on\n * them.\n *\n * This list is immutable, but individual holders may revoke these via\n * {revokeOperator}, in which case {isOperatorFor} will return false.\n */\n function defaultOperators() external view returns (address[] memory);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must\n * be an operator of `sender`.\n *\n * If send or receive hooks are registered for `sender` and `recipient`,\n * the corresponding functions will be called with `data` and\n * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.\n *\n * Emits a {Sent} event.\n *\n * Requirements\n *\n * - `sender` cannot be the zero address.\n * - `sender` must have at least `amount` tokens.\n * - the caller must be an operator for `sender`.\n * - `recipient` cannot be the zero address.\n * - if `recipient` is a contract, it must implement the {IERC777Recipient}\n * interface.\n */\n function operatorSend(\n address sender,\n address recipient,\n uint256 amount,\n bytes calldata data,\n bytes calldata operatorData\n ) external;\n\n /**\n * @dev Destoys `amount` tokens from `account`, reducing the total supply.\n * The caller must be an operator of `account`.\n *\n * If a send hook is registered for `account`, the corresponding function\n * will be called with `data` and `operatorData`. See {IERC777Sender}.\n *\n * Emits a {Burned} event.\n *\n * Requirements\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n * - the caller must be an operator for `account`.\n */\n function operatorBurn(\n address account,\n uint256 amount,\n bytes calldata data,\n bytes calldata operatorData\n ) external;\n\n event Sent(\n address indexed operator,\n address indexed from,\n address indexed to,\n uint256 amount,\n bytes data,\n bytes operatorData\n );\n\n event Minted(\n address indexed operator,\n address indexed to,\n uint256 amount,\n bytes data,\n bytes operatorData\n );\n\n event Burned(\n address indexed operator,\n address indexed from,\n uint256 amount,\n bytes data,\n bytes operatorData\n );\n\n event AuthorizedOperator(address indexed operator, address indexed tokenHolder);\n\n event RevokedOperator(address indexed operator, address indexed tokenHolder);\n}\n" + }, + "contracts/interfaces/IERC777Recipient.sol": { + "content": "pragma solidity ^0.5.0;\n\n/**\n * @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.\n *\n * Accounts can be notified of {IERC777} tokens being sent to them by having a\n * contract implement this interface (contract holders can be their own\n * implementer) and registering it on the\n * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].\n *\n * See {IERC1820Registry} and {ERC1820Implementer}.\n */\ninterface IERC777Recipient {\n /**\n * @dev Called by an {IERC777} token contract whenever tokens are being\n * moved or created into a registered account (`to`). The type of operation\n * is conveyed by `from` being the zero address or not.\n *\n * This call occurs _after_ the token contract's state is updated, so\n * {IERC777-balanceOf}, etc., can be used to query the post-operation state.\n *\n * This function may revert to prevent the operation from being executed.\n */\n function tokensReceived(\n address operator,\n address from,\n address to,\n uint256 amount,\n bytes calldata userData,\n bytes calldata operatorData\n ) external;\n}\n" + }, + "contracts/interfaces/IERC777Sender.sol": { + "content": "pragma solidity ^0.5.0;\n\n/**\n * @dev Interface of the ERC777TokensSender standard as defined in the EIP.\n *\n * {IERC777} Token holders can be notified of operations performed on their\n * tokens by having a contract implement this interface (contract holders can be\n * their own implementer) and registering it on the\n * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 global registry].\n *\n * See {IERC1820Registry} and {ERC1820Implementer}.\n */\ninterface IERC777Sender {\n /**\n * @dev Called by an {IERC777} token contract whenever a registered holder's\n * (`from`) tokens are about to be moved or destroyed. The type of operation\n * is conveyed by `to` being the zero address or not.\n *\n * This call occurs _before_ the token contract's state is updated, so\n * {IERC777-balanceOf}, etc., can be used to query the pre-operation state.\n *\n * This function may revert to prevent the operation from being executed.\n */\n function tokensToSend(\n address operator,\n address from,\n address to,\n uint256 amount,\n bytes calldata userData,\n bytes calldata operatorData\n ) external;\n}\n" + }, + "contracts/interfaces/ILoanPool.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity >=0.5.0 <0.6.0;\n\ninterface ILoanPool {\n function tokenPrice() external view returns (uint256 price);\n\n function borrowInterestRate() external view returns (uint256);\n\n function totalAssetSupply() external view returns (uint256);\n}\n" + }, + "contracts/interfaces/ILoanTokenModules.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\ninterface ILoanTokenModules {\n /** EVENT */\n /// topic: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /// topic: 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925\n event Approval(address indexed owner, address indexed spender, uint256 value);\n\n /// topic: 0x628e75c63c1873bcd3885f7aee9f58ee36f60dc789b2a6b3a978c4189bc548ba\n event AllowanceUpdate(\n address indexed owner,\n address indexed spender,\n uint256 valueBefore,\n uint256 valueAfter\n );\n\n /// topic: 0xb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb\n event Mint(address indexed minter, uint256 tokenAmount, uint256 assetAmount, uint256 price);\n\n /// topic: 0x743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b4644\n event Burn(address indexed burner, uint256 tokenAmount, uint256 assetAmount, uint256 price);\n\n /// topic: 0xc688ff9bd4a1c369dd44c5cf64efa9db6652fb6b280aa765cd43f17d256b816e\n event FlashBorrow(address borrower, address target, address loanToken, uint256 loanAmount);\n\n /// topic: 0x9bbd2de400810774339120e2f8a2b517ed748595e944529bba8ebabf314d0591\n event SetTransactionLimits(address[] addresses, uint256[] limits);\n\n event WithdrawRBTCTo(address indexed to, uint256 amount);\n\n event ToggledFunctionPaused(string functionId, bool prevFlag, bool newFlag);\n\n /** INTERFACE */\n\n /** START LOAN TOKEN SETTINGS LOWER ADMIN */\n struct LoanParams {\n /// @dev ID of loan params object.\n bytes32 id;\n /// @dev If false, this object has been disabled by the owner and can't\n /// be used for future loans.\n bool active;\n /// @dev Owner of this object.\n address owner;\n /// @dev The token being loaned.\n address loanToken;\n /// @dev The required collateral token.\n address collateralToken;\n /// @dev The minimum allowed initial margin.\n uint256 minInitialMargin;\n /// @dev An unhealthy loan when current margin is at or below this value.\n uint256 maintenanceMargin;\n /// @dev The maximum term for new loans (0 means there's no max term).\n uint256 maxLoanTerm;\n }\n\n function setAdmin(address _admin) external;\n\n function setPauser(address _pauser) external;\n\n function setupLoanParams(LoanParams[] calldata loanParamsList, bool areTorqueLoans) external;\n\n function disableLoanParams(address[] calldata collateralTokens, bool[] calldata isTorqueLoans)\n external;\n\n function setDemandCurve(\n uint256 _baseRate,\n uint256 _rateMultiplier,\n uint256 _lowUtilBaseRate,\n uint256 _lowUtilRateMultiplier,\n uint256 _targetLevel,\n uint256 _kinkLevel,\n uint256 _maxScaleRate\n ) external;\n\n function toggleFunctionPause(\n string calldata funcId, /// example: \"mint(uint256,uint256)\"\n bool isPaused\n ) external;\n\n function setTransactionLimits(address[] calldata addresses, uint256[] calldata limits)\n external;\n\n function changeLoanTokenNameAndSymbol(string calldata _name, string calldata _symbol) external;\n\n /** END LOAN TOKEN SETTINGS LOWER ADMIN */\n\n /** START LOAN TOKEN LOGIC STANDARD */\n function marginTrade(\n bytes32 loanId, /// 0 if new loan\n uint256 leverageAmount, /// Expected in x * 10**18 where x is the actual leverage (2, 3, 4, or 5).\n uint256 loanTokenSent,\n uint256 collateralTokenSent,\n address collateralTokenAddress,\n address trader,\n uint256 minEntryPrice, // Value of loan token in collateral.\n bytes calldata loanDataBytes /// Arbitrary order data.\n )\n external\n payable\n returns (\n uint256,\n uint256 /// Returns new principal and new collateral added to trade.\n );\n\n function marginTradeAffiliate(\n bytes32 loanId, // 0 if new loan\n uint256 leverageAmount, // expected in x * 10**18 where x is the actual leverage (2, 3, 4, or 5)\n uint256 loanTokenSent,\n uint256 collateralTokenSent,\n address collateralTokenAddress,\n address trader,\n uint256 minEntryPrice, // Value of loan token in collateral.\n address affiliateReferrer, // The user was brought by the affiliate (referrer).\n bytes calldata loanDataBytes // Arbitrary order data.\n )\n external\n payable\n returns (\n uint256,\n uint256 /// Returns new principal and new collateral added to trade.\n );\n\n function borrowInterestRate() external view returns (uint256);\n\n function mint(address receiver, uint256 depositAmount) external returns (uint256 mintAmount);\n\n function burn(address receiver, uint256 burnAmount) external returns (uint256 loanAmountPaid);\n\n function checkPause(string calldata funcId) external view returns (bool isPaused);\n\n function nextBorrowInterestRate(uint256 borrowAmount) external view returns (uint256);\n\n function totalAssetBorrow() external view returns (uint256);\n\n function totalAssetSupply() external view returns (uint256);\n\n function borrow(\n bytes32 loanId, /// 0 if new loan.\n uint256 withdrawAmount,\n uint256 initialLoanDuration, /// Duration in seconds.\n uint256 collateralTokenSent, /// If 0, loanId must be provided; any rBTC sent must equal this value.\n address collateralTokenAddress, /// If address(0), this means rBTC and rBTC must be sent with the call or loanId must be provided.\n address borrower,\n address receiver,\n bytes calldata /// loanDataBytes: arbitrary order data (for future use).\n )\n external\n payable\n returns (\n uint256,\n uint256 /// Returns new principal and new collateral added to loan.\n );\n\n function transfer(address _to, uint256 _value) external returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) external returns (bool);\n\n function setLiquidityMiningAddress(address LMAddress) external;\n\n function getLiquidityMiningAddress() external view returns (address);\n\n function setStakingContractAddress(address _stakingContractAddress) external;\n\n function getStakingContractAddress() external view returns (address);\n\n function getEstimatedMarginDetails(\n uint256 leverageAmount,\n uint256 loanTokenSent,\n uint256 collateralTokenSent,\n address collateralTokenAddress // address(0) means ETH\n )\n external\n view\n returns (\n uint256 principal,\n uint256 collateral,\n uint256 interestRate\n );\n\n function getDepositAmountForBorrow(\n uint256 borrowAmount,\n uint256 initialLoanDuration, /// Duration in seconds.\n address collateralTokenAddress /// address(0) means rBTC\n ) external view returns (uint256 depositAmount);\n\n function getBorrowAmountForDeposit(\n uint256 depositAmount,\n uint256 initialLoanDuration, /// Duration in seconds.\n address collateralTokenAddress /// address(0) means rBTC\n ) external view returns (uint256 borrowAmount);\n\n function checkPriceDivergence(\n uint256 loanTokenSent,\n address collateralTokenAddress,\n uint256 minEntryPrice\n ) external view;\n\n function getMaxEscrowAmount(uint256 leverageAmount)\n external\n view\n returns (uint256 maxEscrowAmount);\n\n function checkpointPrice(address _user) external view returns (uint256 price);\n\n function assetBalanceOf(address _owner) external view returns (uint256);\n\n function profitOf(address user) external view returns (int256);\n\n function tokenPrice() external view returns (uint256 price);\n\n function avgBorrowInterestRate() external view returns (uint256);\n\n function supplyInterestRate() external view returns (uint256);\n\n function nextSupplyInterestRate(uint256 supplyAmount) external view returns (uint256);\n\n function totalSupplyInterestRate(uint256 assetSupply) external view returns (uint256);\n\n function loanTokenAddress() external view returns (address);\n\n function getMarginBorrowAmountAndRate(uint256 leverageAmount, uint256 depositAmount)\n external\n view\n returns (uint256, uint256);\n\n function withdrawRBTCTo(address payable _receiverAddress, uint256 _amount) external;\n\n /** START LOAN TOKEN BASE */\n function initialPrice() external view returns (uint256);\n\n /** START LOAN TOKEN LOGIC LM */\n function mint(\n address receiver,\n uint256 depositAmount,\n bool useLM\n ) external returns (uint256 minted);\n\n function burn(\n address receiver,\n uint256 burnAmount,\n bool useLM\n ) external returns (uint256 redeemed);\n\n /** START LOAN TOKEN LOGIC WRBTC */\n function mintWithBTC(address receiver, bool useLM)\n external\n payable\n returns (uint256 mintAmount);\n\n function burnToBTC(\n address receiver,\n uint256 burnAmount,\n bool useLM\n ) external returns (uint256 loanAmountPaid);\n\n function marketLiquidity() external view returns (uint256);\n\n function calculateSupplyInterestRate(uint256 assetBorrow, uint256 assetSupply)\n external\n view\n returns (uint256);\n\n /** START LOAN TOKEN LOGIC STORAGE */\n function pauser() external view returns (address);\n\n function liquidityMiningAddress() external view returns (address);\n\n function name() external view returns (string memory);\n\n function symbol() external view returns (string memory);\n\n /** START ADVANCED TOKEN */\n function approve(address _spender, uint256 _value) external returns (bool);\n\n /** START ADVANCED TOKEN STORAGE */\n function allowance(address _owner, address _spender) external view returns (uint256);\n\n function balanceOf(address _owner) external view returns (uint256);\n\n function totalSupply() external view returns (uint256);\n\n function loanParamsIds(uint256) external view returns (bytes32);\n}\n" + }, + "contracts/interfaces/ISovryn.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity >=0.5.0 <0.6.0;\npragma experimental ABIEncoderV2;\n//TODO: stored in ./interfaces only while brownie isn't removed\n//TODO: move to contracts/interfaces after with brownie is removed\n\nimport \"../core/State.sol\";\nimport \"../events/ProtocolSettingsEvents.sol\";\nimport \"../events/LoanSettingsEvents.sol\";\nimport \"../events/LoanOpeningsEvents.sol\";\nimport \"../events/LoanMaintenanceEvents.sol\";\nimport \"../events/LoanClosingsEvents.sol\";\nimport \"../events/FeesEvents.sol\";\nimport \"../events/SwapsEvents.sol\";\nimport \"../events/AffiliatesEvents.sol\";\nimport \"../connectors/loantoken/lib/MarginTradeStructHelpers.sol\";\n\ncontract ISovryn is\n State,\n ProtocolSettingsEvents,\n LoanSettingsEvents,\n LoanOpeningsEvents,\n LoanMaintenanceEvents,\n LoanClosingsEvents,\n SwapsEvents,\n AffiliatesEvents,\n FeesEvents\n{\n /// Triggered whenever interest is paid to lender.\n event PayInterestTransfer(\n address indexed interestToken,\n address indexed lender,\n uint256 effectiveInterest\n );\n\n ////// Protocol //////\n\n function replaceContract(address target) external;\n\n function setTargets(string[] calldata sigsArr, address[] calldata targetsArr) external;\n\n function getTarget(string calldata sig) external view returns (address);\n\n ////// Protocol Settings //////\n\n function setSovrynProtocolAddress(address newProtocolAddress) external;\n\n function setSOVTokenAddress(address newSovTokenAddress) external;\n\n function setLockedSOVAddress(address newSOVLockedAddress) external;\n\n function setMinReferralsToPayoutAffiliates(uint256 newMinReferrals) external;\n\n function setPriceFeedContract(address newContract) external;\n\n function setSwapsImplContract(address newContract) external;\n\n function setLoanPool(address[] calldata pools, address[] calldata assets) external;\n\n function setSupportedTokens(address[] calldata addrs, bool[] calldata toggles) external;\n\n function setLendingFeePercent(uint256 newValue) external;\n\n function setTradingFeePercent(uint256 newValue) external;\n\n function setBorrowingFeePercent(uint256 newValue) external;\n\n function setSwapExternalFeePercent(uint256 newValue) external;\n\n function setAffiliateFeePercent(uint256 newValue) external;\n\n function setAffiliateTradingTokenFeePercent(uint256 newValue) external;\n\n function setLiquidationIncentivePercent(uint256 newAmount) external;\n\n function setMaxDisagreement(uint256 newAmount) external;\n\n function setSourceBuffer(uint256 newAmount) external;\n\n function setMaxSwapSize(uint256 newAmount) external;\n\n function setFeesController(address newController) external;\n\n function withdrawFees(address[] calldata tokens, address receiver)\n external\n returns (uint256 totalWRBTCWithdrawn);\n\n function withdrawLendingFees(\n address token,\n address receiver,\n uint256 amount\n ) external returns (bool);\n\n function withdrawTradingFees(\n address token,\n address receiver,\n uint256 amount\n ) external returns (bool);\n\n function withdrawBorrowingFees(\n address token,\n address receiver,\n uint256 amount\n ) external returns (bool);\n\n function withdrawProtocolToken(address receiver, uint256 amount)\n external\n returns (address, bool);\n\n function depositProtocolToken(uint256 amount) external;\n\n function getLoanPoolsList(uint256 start, uint256 count)\n external\n view\n returns (bytes32[] memory);\n\n function isLoanPool(address loanPool) external view returns (bool);\n\n function setWrbtcToken(address wrbtcTokenAddress) external;\n\n function setSovrynSwapContractRegistryAddress(address registryAddress) external;\n\n function setProtocolTokenAddress(address _protocolTokenAddress) external;\n\n function setRolloverBaseReward(uint256 transactionCost) external;\n\n function setRebatePercent(uint256 rebatePercent) external;\n\n function setSpecialRebates(\n address sourceToken,\n address destToken,\n uint256 specialRebatesPercent\n ) external;\n\n function getSpecialRebates(address sourceToken, address destToken)\n external\n view\n returns (uint256 specialRebatesPercent);\n\n function togglePaused(bool paused) external;\n\n function isProtocolPaused() external view returns (bool);\n\n ////// SwapsImplSovrynSwapModule //////\n function getSovrynSwapNetworkContract(address sovrynSwapRegistryAddress)\n public\n view\n returns (address);\n\n function getContractHexName(string calldata source) external pure returns (bytes32 result);\n\n function swapsImplExpectedRate(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 sourceTokenAmount\n ) external view returns (uint256);\n\n function swapsImplExpectedReturn(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 sourceTokenAmount\n ) external view returns (uint256 expectedReturn);\n\n ////// Loan Settings //////\n\n function setupLoanParams(LoanParams[] calldata loanParamsList)\n external\n returns (bytes32[] memory loanParamsIdList);\n\n // Deactivates LoanParams for future loans. Active loans using it are unaffected.\n function disableLoanParams(bytes32[] calldata loanParamsIdList) external;\n\n function getLoanParams(bytes32[] calldata loanParamsIdList)\n external\n view\n returns (LoanParams[] memory loanParamsList);\n\n function getLoanParamsList(\n address owner,\n uint256 start,\n uint256 count\n ) external view returns (bytes32[] memory loanParamsList);\n\n function getTotalPrincipal(address lender, address loanToken) external view returns (uint256);\n\n function minInitialMargin(bytes32 loanParamsId) external view returns (uint256);\n\n ////// Loan Openings //////\n\n function borrowOrTradeFromPool(\n bytes32 loanParamsId,\n bytes32 loanId, // if 0, start a new loan\n bool isTorqueLoan,\n uint256 initialMargin,\n MarginTradeStructHelpers.SentAddresses calldata sentAddresses,\n // lender: must match loan if loanId provided\n // borrower: must match loan if loanId provided\n // receiver: receiver of funds (address(0) assumes borrower address)\n // manager: delegated manager of loan unless address(0)\n MarginTradeStructHelpers.SentAmounts calldata sentValues,\n // newRate: new loan interest rate\n // newPrincipal: new loan size (borrowAmount + any borrowed interest)\n // torqueInterest: new amount of interest to escrow for Torque loan (determines initial loan length)\n // loanTokenReceived: total loanToken deposit (amount not sent to borrower in the case of Torque loans)\n // collateralTokenReceived: total collateralToken deposit\n bytes calldata loanDataBytes\n ) external payable returns (uint256 newPrincipal, uint256 newCollateral);\n\n function setDelegatedManager(\n bytes32 loanId,\n address delegated,\n bool toggle\n ) external;\n\n function getEstimatedMarginExposure(\n address loanToken,\n address collateralToken,\n uint256 loanTokenSent,\n uint256 collateralTokenSent,\n uint256 interestRate,\n uint256 newPrincipal\n ) external view returns (uint256);\n\n function getRequiredCollateral(\n address loanToken,\n address collateralToken,\n uint256 newPrincipal,\n uint256 marginAmount,\n bool isTorqueLoan\n ) external view returns (uint256 collateralAmountRequired);\n\n function getBorrowAmount(\n address loanToken,\n address collateralToken,\n uint256 collateralTokenAmount,\n uint256 marginAmount,\n bool isTorqueLoan\n ) external view returns (uint256 borrowAmount);\n\n ////// Loan Closings //////\n\n function liquidate(\n bytes32 loanId,\n address receiver,\n uint256 closeAmount // denominated in loanToken\n )\n external\n payable\n returns (\n uint256 loanCloseAmount,\n uint256 seizedAmount,\n address seizedToken\n );\n\n function rollover(bytes32 loanId, bytes calldata loanDataBytes) external;\n\n function closeWithDeposit(\n bytes32 loanId,\n address receiver,\n uint256 depositAmount // denominated in loanToken\n )\n external\n payable\n returns (\n uint256 loanCloseAmount,\n uint256 withdrawAmount,\n address withdrawToken\n );\n\n function closeWithSwap(\n bytes32 loanId,\n address receiver,\n uint256 swapAmount, // denominated in collateralToken\n bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken\n bytes calldata loanDataBytes\n )\n external\n returns (\n uint256 loanCloseAmount,\n uint256 withdrawAmount,\n address withdrawToken\n );\n\n ////// Loan Maintenance //////\n\n function depositCollateral(\n bytes32 loanId,\n uint256 depositAmount // must match msg.value if ether is sent\n ) external payable;\n\n function withdrawCollateral(\n bytes32 loanId,\n address receiver,\n uint256 withdrawAmount\n ) external returns (uint256 actualWithdrawAmount);\n\n function withdrawAccruedInterest(address loanToken) external;\n\n function getLenderInterestData(address lender, address loanToken)\n external\n view\n returns (\n uint256 interestPaid,\n uint256 interestPaidDate,\n uint256 interestOwedPerDay,\n uint256 interestUnPaid,\n uint256 interestFeePercent,\n uint256 principalTotal\n );\n\n function getLoanInterestData(bytes32 loanId)\n external\n view\n returns (\n address loanToken,\n uint256 interestOwedPerDay,\n uint256 interestDepositTotal,\n uint256 interestDepositRemaining\n );\n\n struct LoanReturnData {\n bytes32 loanId;\n address loanToken;\n address collateralToken;\n uint256 principal;\n uint256 collateral;\n uint256 interestOwedPerDay;\n uint256 interestDepositRemaining;\n uint256 startRate; // collateralToLoanRate\n uint256 startMargin;\n uint256 maintenanceMargin;\n uint256 currentMargin;\n uint256 maxLoanTerm;\n uint256 endTimestamp;\n uint256 maxLiquidatable;\n uint256 maxSeizable;\n }\n\n struct LoanReturnDataV2 {\n bytes32 loanId;\n address loanToken;\n address collateralToken;\n address borrower;\n uint256 principal;\n uint256 collateral;\n uint256 interestOwedPerDay;\n uint256 interestDepositRemaining;\n uint256 startRate; /// collateralToLoanRate\n uint256 startMargin;\n uint256 maintenanceMargin;\n uint256 currentMargin;\n uint256 maxLoanTerm;\n uint256 endTimestamp;\n uint256 maxLiquidatable;\n uint256 maxSeizable;\n uint256 creationTimestamp;\n }\n\n function getUserLoans(\n address user,\n uint256 start,\n uint256 count,\n uint256 loanType,\n bool isLender,\n bool unsafeOnly\n ) external view returns (LoanReturnData[] memory loansData);\n\n function getUserLoansV2(\n address user,\n uint256 start,\n uint256 count,\n uint256 loanType,\n bool isLender,\n bool unsafeOnly\n ) external view returns (LoanReturnDataV2[] memory loansDataV2);\n\n function getLoan(bytes32 loanId) external view returns (LoanReturnData memory loanData);\n\n function getLoanV2(bytes32 loanId) external view returns (LoanReturnDataV2 memory loanDataV2);\n\n function getActiveLoans(\n uint256 start,\n uint256 count,\n bool unsafeOnly\n ) external view returns (LoanReturnData[] memory loansData);\n\n function getActiveLoansV2(\n uint256 start,\n uint256 count,\n bool unsafeOnly\n ) external view returns (LoanReturnDataV2[] memory loansDataV2);\n\n function extendLoanDuration(\n bytes32 loanId,\n uint256 depositAmount,\n bool useCollateral,\n bytes calldata /// loanDataBytes, for future use.\n ) external returns (uint256 secondsExtended);\n\n function reduceLoanDuration(\n bytes32 loanId,\n address receiver,\n uint256 withdrawAmount\n ) external returns (uint256 secondsReduced);\n\n ////// Swaps External //////\n function swapExternal(\n address sourceToken,\n address destToken,\n address receiver,\n address returnToSender,\n uint256 sourceTokenAmount,\n uint256 requiredDestTokenAmount,\n uint256 minReturn,\n bytes calldata swapData\n ) external returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed);\n\n function getSwapExpectedReturn(\n address sourceToken,\n address destToken,\n uint256 sourceTokenAmount\n ) external view returns (uint256);\n\n function checkPriceDivergence(\n address sourceToken,\n address destToken,\n uint256 sourceTokenAmount,\n uint256 minReturn\n ) public view;\n\n ////// Affiliates Module //////\n\n function getUserNotFirstTradeFlag(address user) external view returns (bool);\n\n function setUserNotFirstTradeFlag(address user) external;\n\n function payTradingFeeToAffiliatesReferrer(\n address referrer,\n address trader,\n address token,\n uint256 tradingFeeTokenBaseAmount\n ) external returns (uint256 affiliatesBonusSOVAmount, uint256 affiliatesBonusTokenAmount);\n\n function setAffiliatesReferrer(address user, address referrer) external; //onlyCallableByLoanPools\n\n function getReferralsList(address referrer) external view returns (address[] memory refList);\n\n function getAffiliatesReferrerBalances(address referrer)\n external\n view\n returns (address[] memory referrerTokensList, uint256[] memory referrerTokensBalances);\n\n function getAffiliatesReferrerTokensList(address referrer)\n external\n view\n returns (address[] memory tokensList);\n\n function getAffiliatesReferrerTokenBalance(address referrer, address token)\n external\n view\n returns (uint256);\n\n function withdrawAffiliatesReferrerTokenFees(\n address token,\n address receiver,\n uint256 amount\n ) external;\n\n function withdrawAllAffiliatesReferrerTokenFees(address receiver) external;\n\n function getProtocolAddress() external view returns (address);\n\n function getSovTokenAddress() external view returns (address);\n\n function getLockedSOVAddress() external view returns (address);\n\n function getFeeRebatePercent() external view returns (uint256);\n\n function getMinReferralsToPayout() external view returns (uint256);\n\n function getAffiliatesUserReferrer(address user) external view returns (address referrer);\n\n function getAffiliateRewardsHeld(address referrer) external view returns (uint256);\n\n function getAffiliateTradingTokenFeePercent()\n external\n view\n returns (uint256 affiliateTradingTokenFeePercent);\n\n function getAffiliatesTokenRewardsValueInRbtc(address referrer)\n external\n view\n returns (uint256 rbtcTotalAmount);\n\n function getSwapExternalFeePercent() external view returns (uint256 swapExternalFeePercent);\n\n function setTradingRebateRewardsBasisPoint(uint256 newBasisPoint) external;\n\n function getTradingRebateRewardsBasisPoint() external view returns (uint256);\n\n function getDedicatedSOVRebate() external view returns (uint256);\n\n function setRolloverFlexFeePercent(uint256 newRolloverFlexFeePercent) external;\n\n function getDefaultPathConversion(address sourceTokenAddress, address destTokenAddress)\n external\n view\n returns (IERC20[] memory);\n\n function setDefaultPathConversion(IERC20[] calldata defaultPath) external;\n\n function removeDefaultPathConversion(address sourceTokenAddress, address destTokenAddress)\n external;\n\n function checkCloseWithDepositIsTinyPosition(bytes32 loanId, uint256 depositAmount)\n external\n view\n returns (bool isTinyPosition, uint256 tinyPositionAmount);\n\n function setAdmin(address newAdmin) external;\n\n function getAdmin() external view returns (address);\n\n function setPauser(address newPauser) external;\n\n function getPauser() external view returns (address);\n}\n" + }, + "contracts/interfaces/IWrbtc.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity >=0.5.0 <0.6.0;\n\ninterface IWrbtc {\n function deposit() external payable;\n\n function withdraw(uint256 wad) external;\n}\n" + }, + "contracts/interfaces/IWrbtcERC20.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity >=0.5.0 <0.6.0;\n\nimport \"./IWrbtc.sol\";\nimport \"./IERC20.sol\";\n\ncontract IWrbtcERC20 is IWrbtc, IERC20 {}\n" + }, + "contracts/locked/ILockedSOV.sol": { + "content": "pragma solidity ^0.5.17;\n\n/**\n * @title The Locked SOV Interface.\n * @author Franklin Richards - powerhousefrank@protonmail.com\n * @notice This interface is an incomplete yet useful for future migration of LockedSOV Contract.\n * @dev Only use it if you know what you are doing.\n */\ninterface ILockedSOV {\n /**\n * @notice Adds SOV to the user balance (Locked and Unlocked Balance based on `_basisPoint`).\n * @param _userAddress The user whose locked balance has to be updated with `_sovAmount`.\n * @param _sovAmount The amount of SOV to be added to the locked and/or unlocked balance.\n * @param _basisPoint The % (in Basis Point)which determines how much will be unlocked immediately.\n */\n function deposit(\n address _userAddress,\n uint256 _sovAmount,\n uint256 _basisPoint\n ) external;\n\n /**\n * @notice Adds SOV to the locked balance of a user.\n * @param _userAddress The user whose locked balance has to be updated with _sovAmount.\n * @param _sovAmount The amount of SOV to be added to the locked balance.\n */\n function depositSOV(address _userAddress, uint256 _sovAmount) external;\n\n /**\n * @notice Withdraws unlocked tokens and Stakes Locked tokens for a user who already have a vesting created.\n * @param _userAddress The address of user tokens will be withdrawn.\n */\n function withdrawAndStakeTokensFrom(address _userAddress) external;\n\n function cliff() external view returns (uint256);\n\n function duration() external view returns (uint256);\n\n function getLockedBalance(address _addr) external view returns (uint256 _balance);\n\n function getUnlockedBalance(address _addr) external view returns (uint256 _balance);\n}\n" + }, + "contracts/locked/LockedSOV.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"../interfaces/IERC20.sol\";\nimport \"../governance/Vesting/VestingRegistry.sol\";\nimport \"../governance/Vesting/VestingLogic.sol\";\nimport \"./ILockedSOV.sol\";\n\n/**\n * @title The Locked SOV Contract.\n * @author Franklin Richards - powerhousefrank@protonmail.com\n * @notice This contract is used to receive reward from other contracts, Create Vesting and Stake Tokens.\n */\ncontract LockedSOV is ILockedSOV {\n using SafeMath for uint256;\n\n uint256 public constant MAX_BASIS_POINT = 10000;\n uint256 public constant MAX_DURATION = 37;\n\n /* Storage */\n\n /// @notice True if the migration to a new Locked SOV Contract has started.\n bool public migration;\n\n /// @notice The cliff is the time period after which the tokens begin to unlock.\n uint256 public cliff;\n /// @notice The duration is the time period after all tokens will have been unlocked.\n uint256 public duration;\n\n /// @notice The SOV token contract.\n IERC20 public SOV;\n /// @notice The Vesting registry contract.\n VestingRegistry public vestingRegistry;\n /// @notice The New (Future) Locked SOV.\n ILockedSOV public newLockedSOV;\n\n /// @notice The locked user balances.\n mapping(address => uint256) private lockedBalances;\n /// @notice The unlocked user balances.\n mapping(address => uint256) private unlockedBalances;\n /// @notice The contracts/wallets with admin power.\n mapping(address => bool) private isAdmin;\n\n /* Events */\n\n /// @notice Emitted when a new Admin is added to the admin list.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _newAdmin The address of the new admin.\n event AdminAdded(address indexed _initiator, address indexed _newAdmin);\n\n /// @notice Emitted when an admin is removed from the admin list.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _removedAdmin The address of the removed admin.\n event AdminRemoved(address indexed _initiator, address indexed _removedAdmin);\n\n /// @notice Emitted when Vesting Registry, Duration and/or Cliff is updated.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _vestingRegistry The Vesting Registry Contract.\n /// @param _cliff The time period after which the tokens begin to unlock.\n /// @param _duration The time period after all tokens will have been unlocked.\n event RegistryCliffAndDurationUpdated(\n address indexed _initiator,\n address indexed _vestingRegistry,\n uint256 _cliff,\n uint256 _duration\n );\n\n /// @notice Emitted when a new deposit is made.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _userAddress The user to whose un/locked balance a new deposit was made.\n /// @param _sovAmount The amount of SOV to be added to the un/locked balance.\n /// @param _basisPoint The % (in Basis Point) which determines how much will be unlocked immediately.\n event Deposited(\n address indexed _initiator,\n address indexed _userAddress,\n uint256 _sovAmount,\n uint256 _basisPoint\n );\n\n /// @notice Emitted when a user withdraws the fund.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _userAddress The user whose unlocked balance has to be withdrawn.\n /// @param _sovAmount The amount of SOV withdrawn from the unlocked balance.\n event Withdrawn(address indexed _initiator, address indexed _userAddress, uint256 _sovAmount);\n\n /// @notice Emitted when a user creates a vesting for himself.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _userAddress The user whose unlocked balance has to be withdrawn.\n /// @param _vesting The Vesting Contract.\n event VestingCreated(\n address indexed _initiator,\n address indexed _userAddress,\n address indexed _vesting\n );\n\n /// @notice Emitted when a user stakes tokens.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _vesting The Vesting Contract.\n /// @param _amount The amount of locked tokens staked by the user.\n event TokenStaked(address indexed _initiator, address indexed _vesting, uint256 _amount);\n\n /// @notice Emitted when an admin initiates a migration to new Locked SOV Contract.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _newLockedSOV The address of the new Locked SOV Contract.\n event MigrationStarted(address indexed _initiator, address indexed _newLockedSOV);\n\n /// @notice Emitted when a user initiates the transfer to a new Locked SOV Contract.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _amount The amount of locked tokens to transfer from this contract to the new one.\n event UserTransfered(address indexed _initiator, uint256 _amount);\n\n /* Modifiers */\n\n modifier onlyAdmin {\n require(isAdmin[msg.sender], \"Only admin can call this.\");\n _;\n }\n\n modifier migrationAllowed {\n require(migration, \"Migration has not yet started.\");\n _;\n }\n\n /* Constructor */\n\n /**\n * @notice Setup the required parameters.\n * @param _SOV The SOV Token Address.\n * @param _vestingRegistry The Vesting Registry Address.\n * @param _cliff The time period after which the tokens begin to unlock.\n * @param _duration The time period after all tokens will have been unlocked.\n * @param _admins The list of Admins to be added.\n */\n constructor(\n address _SOV,\n address _vestingRegistry,\n uint256 _cliff,\n uint256 _duration,\n address[] memory _admins\n ) public {\n require(_SOV != address(0), \"Invalid SOV Address.\");\n require(_vestingRegistry != address(0), \"Vesting registry address is invalid.\");\n require(_duration < MAX_DURATION, \"Duration is too long.\");\n\n SOV = IERC20(_SOV);\n vestingRegistry = VestingRegistry(_vestingRegistry);\n cliff = _cliff * 4 weeks;\n duration = _duration * 4 weeks;\n\n for (uint256 index = 0; index < _admins.length; index++) {\n isAdmin[_admins[index]] = true;\n }\n }\n\n /* Public or External Functions */\n\n /**\n * @notice The function to add a new admin.\n * @param _newAdmin The address of the new admin.\n * @dev Only callable by an Admin.\n */\n function addAdmin(address _newAdmin) public onlyAdmin {\n require(_newAdmin != address(0), \"Invalid Address.\");\n require(!isAdmin[_newAdmin], \"Address is already admin.\");\n isAdmin[_newAdmin] = true;\n\n emit AdminAdded(msg.sender, _newAdmin);\n }\n\n /**\n * @notice The function to remove an admin.\n * @param _adminToRemove The address of the admin which should be removed.\n * @dev Only callable by an Admin.\n */\n function removeAdmin(address _adminToRemove) public onlyAdmin {\n require(isAdmin[_adminToRemove], \"Address is not an admin.\");\n isAdmin[_adminToRemove] = false;\n\n emit AdminRemoved(msg.sender, _adminToRemove);\n }\n\n /**\n * @notice The function to update the Vesting Registry, Duration and Cliff.\n * @param _vestingRegistry The Vesting Registry Address.\n * @param _cliff The time period after which the tokens begin to unlock.\n * @param _duration The time period after all tokens will have been unlocked.\n * @dev IMPORTANT 1: You have to change Vesting Registry if you want to change Duration and/or Cliff.\n * IMPORTANT 2: `_cliff` and `_duration` is multiplied by 4 weeks in this function.\n */\n function changeRegistryCliffAndDuration(\n address _vestingRegistry,\n uint256 _cliff,\n uint256 _duration\n ) external onlyAdmin {\n require(\n address(vestingRegistry) != _vestingRegistry,\n \"Vesting Registry has to be different for changing duration and cliff.\"\n );\n /// If duration is also zero, then it is similar to Unlocked SOV.\n require(_duration != 0, \"Duration cannot be zero.\");\n require(_duration < MAX_DURATION, \"Duration is too long.\");\n\n vestingRegistry = VestingRegistry(_vestingRegistry);\n\n cliff = _cliff * 4 weeks;\n duration = _duration * 4 weeks;\n\n emit RegistryCliffAndDurationUpdated(msg.sender, _vestingRegistry, _cliff, _duration);\n }\n\n /**\n * @notice Adds SOV to the user balance (Locked and Unlocked Balance based on `_basisPoint`).\n * @param _userAddress The user whose locked balance has to be updated with `_sovAmount`.\n * @param _sovAmount The amount of SOV to be added to the locked and/or unlocked balance.\n * @param _basisPoint The % (in Basis Point)which determines how much will be unlocked immediately.\n */\n function deposit(\n address _userAddress,\n uint256 _sovAmount,\n uint256 _basisPoint\n ) external {\n _deposit(_userAddress, _sovAmount, _basisPoint);\n }\n\n /**\n * @notice Adds SOV to the locked balance of a user.\n * @param _userAddress The user whose locked balance has to be updated with _sovAmount.\n * @param _sovAmount The amount of SOV to be added to the locked balance.\n * @dev This is here because there are dependency with other contracts.\n */\n function depositSOV(address _userAddress, uint256 _sovAmount) external {\n _deposit(_userAddress, _sovAmount, 0);\n }\n\n function _deposit(\n address _userAddress,\n uint256 _sovAmount,\n uint256 _basisPoint\n ) private {\n // MAX_BASIS_POINT is not included because if 100% is unlocked, then LockedSOV is not required to be used.\n require(_basisPoint < MAX_BASIS_POINT, \"Basis Point has to be less than 10000.\");\n bool txStatus = SOV.transferFrom(msg.sender, address(this), _sovAmount);\n require(txStatus, \"Token transfer was not successful. Check receiver address.\");\n\n uint256 unlockedBal = _sovAmount.mul(_basisPoint).div(MAX_BASIS_POINT);\n\n unlockedBalances[_userAddress] = unlockedBalances[_userAddress].add(unlockedBal);\n lockedBalances[_userAddress] = lockedBalances[_userAddress].add(_sovAmount).sub(\n unlockedBal\n );\n\n emit Deposited(msg.sender, _userAddress, _sovAmount, _basisPoint);\n }\n\n /**\n * @notice A function to withdraw the unlocked balance.\n * @param _receiverAddress If specified, the unlocked balance will go to this address, else to msg.sender.\n */\n function withdraw(address _receiverAddress) public {\n _withdraw(msg.sender, _receiverAddress);\n }\n\n function _withdraw(address _sender, address _receiverAddress) private {\n address userAddr = _receiverAddress;\n if (_receiverAddress == address(0)) {\n userAddr = _sender;\n }\n\n uint256 amount = unlockedBalances[_sender];\n unlockedBalances[_sender] = 0;\n\n bool txStatus = SOV.transfer(userAddr, amount);\n require(txStatus, \"Token transfer was not successful. Check receiver address.\");\n\n emit Withdrawn(_sender, userAddr, amount);\n }\n\n /**\n * @notice Creates vesting if not already created and Stakes tokens for a user.\n * @dev Only use this function if the `duration` is small.\n */\n function createVestingAndStake() public {\n _createVestingAndStake(msg.sender);\n }\n\n function _createVestingAndStake(address _sender) private {\n address vestingAddr = _getVesting(_sender);\n\n if (vestingAddr == address(0)) {\n vestingAddr = _createVesting(_sender);\n }\n\n _stakeTokens(_sender, vestingAddr);\n }\n\n /**\n * @notice Creates vesting contract (if it hasn't been created yet) for the calling user.\n * @return _vestingAddress The New Vesting Contract Created.\n */\n function createVesting() public returns (address _vestingAddress) {\n _vestingAddress = _createVesting(msg.sender);\n }\n\n /**\n * @notice Stakes tokens for a user who already have a vesting created.\n * @dev The user should already have a vesting created, else this function will throw error.\n */\n function stakeTokens() public {\n VestingLogic vesting = VestingLogic(_getVesting(msg.sender));\n\n require(\n cliff == vesting.cliff() && duration == vesting.duration(),\n \"Wrong Vesting Schedule.\"\n );\n\n _stakeTokens(msg.sender, address(vesting));\n }\n\n /**\n * @notice Withdraws unlocked tokens and Stakes Locked tokens for a user who already have a vesting created.\n * @param _receiverAddress If specified, the unlocked balance will go to this address, else to msg.sender.\n */\n function withdrawAndStakeTokens(address _receiverAddress) external {\n _withdraw(msg.sender, _receiverAddress);\n _createVestingAndStake(msg.sender);\n }\n\n /**\n * @notice Withdraws unlocked tokens and Stakes Locked tokens for a user who already have a vesting created.\n * @param _userAddress The address of user tokens will be withdrawn.\n */\n function withdrawAndStakeTokensFrom(address _userAddress) external {\n _withdraw(_userAddress, _userAddress);\n _createVestingAndStake(_userAddress);\n }\n\n /**\n * @notice Function to start the process of migration to new contract.\n * @param _newLockedSOV The new locked sov contract address.\n */\n function startMigration(address _newLockedSOV) external onlyAdmin {\n require(_newLockedSOV != address(0), \"New Locked SOV Address is Invalid.\");\n newLockedSOV = ILockedSOV(_newLockedSOV);\n SOV.approve(_newLockedSOV, SOV.balanceOf(address(this)));\n migration = true;\n\n emit MigrationStarted(msg.sender, _newLockedSOV);\n }\n\n /**\n * @notice Function to transfer the locked balance from this contract to new LockedSOV Contract.\n * @dev Address is not specified to discourage selling lockedSOV to other address.\n */\n function transfer() external migrationAllowed {\n uint256 amount = lockedBalances[msg.sender];\n lockedBalances[msg.sender] = 0;\n\n newLockedSOV.depositSOV(msg.sender, amount);\n\n emit UserTransfered(msg.sender, amount);\n }\n\n /* Internal Functions */\n\n /**\n * @notice Creates a Vesting Contract for a user.\n * @param _tokenOwner The owner of the vesting contract.\n * @return _vestingAddress The Vesting Contract Address.\n * @dev Does not do anything if Vesting Contract was already created.\n */\n function _createVesting(address _tokenOwner) internal returns (address _vestingAddress) {\n /// Here zero is given in place of amount, as amount is not really used in `vestingRegistry.createVesting()`.\n vestingRegistry.createVesting(_tokenOwner, 0, cliff, duration);\n _vestingAddress = _getVesting(_tokenOwner);\n emit VestingCreated(msg.sender, _tokenOwner, _vestingAddress);\n }\n\n /**\n * @notice Returns the Vesting Contract Address.\n * @param _tokenOwner The owner of the vesting contract.\n * @return _vestingAddress The Vesting Contract Address.\n */\n function _getVesting(address _tokenOwner) internal view returns (address _vestingAddress) {\n return vestingRegistry.getVesting(_tokenOwner);\n }\n\n /**\n * @notice Stakes the tokens in a particular vesting contract.\n * @param _vesting The Vesting Contract Address.\n */\n function _stakeTokens(address _sender, address _vesting) internal {\n uint256 amount = lockedBalances[_sender];\n lockedBalances[_sender] = 0;\n\n require(SOV.approve(_vesting, amount), \"Approve failed.\");\n VestingLogic(_vesting).stakeTokens(amount);\n\n emit TokenStaked(_sender, _vesting, amount);\n }\n\n /* Getter or Read Functions */\n\n /**\n * @notice The function to get the locked balance of a user.\n * @param _addr The address of the user to check the locked balance.\n * @return _balance The locked balance of the address `_addr`.\n */\n function getLockedBalance(address _addr) external view returns (uint256 _balance) {\n return lockedBalances[_addr];\n }\n\n /**\n * @notice The function to get the unlocked balance of a user.\n * @param _addr The address of the user to check the unlocked balance.\n * @return _balance The unlocked balance of the address `_addr`.\n */\n function getUnlockedBalance(address _addr) external view returns (uint256 _balance) {\n return unlockedBalances[_addr];\n }\n\n /**\n * @notice The function to check is an address is admin or not.\n * @param _addr The address of the user to check the admin status.\n * @return _status True if admin, False otherwise.\n */\n function adminStatus(address _addr) external view returns (bool _status) {\n return isAdmin[_addr];\n }\n}\n" + }, + "contracts/mixins/EnumerableAddressSet.sol": { + "content": "pragma solidity ^0.5.0;\n\n/**\n * @dev Based on Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * As of v2.5.0, only `address` sets are supported.\n *\n * Include with `using EnumerableSet for EnumerableSet.AddressSet;`.\n *\n * _Available since v2.5.0._\n */\nlibrary EnumerableAddressSet {\n struct AddressSet {\n // Position of the value in the `values` array, plus 1 because index 0\n // means a value is not in the set.\n mapping(address => uint256) index;\n address[] values;\n }\n\n /**\n * @dev Add a value to a set. O(1).\n * Returns false if the value was already in the set.\n */\n function add(AddressSet storage set, address value) internal returns (bool) {\n if (!contains(set, value)) {\n set.index[value] = set.values.push(value);\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Removes a value from a set. O(1).\n * Returns false if the value was not present in the set.\n */\n function remove(AddressSet storage set, address value) internal returns (bool) {\n if (contains(set, value)) {\n uint256 toDeleteIndex = set.index[value] - 1;\n uint256 lastIndex = set.values.length - 1;\n\n // If the element we're deleting is the last one, we can just remove it without doing a swap\n if (lastIndex != toDeleteIndex) {\n address lastValue = set.values[lastIndex];\n\n // Move the last value to the index where the deleted value is\n set.values[toDeleteIndex] = lastValue;\n // Update the index for the moved value\n set.index[lastValue] = toDeleteIndex + 1; // All indexes are 1-based\n }\n\n // Delete the index entry for the deleted value\n delete set.index[value];\n\n // Delete the old entry for the moved value\n set.values.pop();\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function contains(AddressSet storage set, address value) internal view returns (bool) {\n return set.index[value] != 0;\n }\n\n /**\n * @dev Returns an array with all values in the set. O(N).\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n\n * WARNING: This function may run out of gas on large sets: use {length} and\n * {get} instead in these cases.\n */\n function enumerate(AddressSet storage set) internal view returns (address[] memory) {\n address[] memory output = new address[](set.values.length);\n for (uint256 i; i < set.values.length; i++) {\n output[i] = set.values[i];\n }\n return output;\n }\n\n /**\n * @dev Returns a chunk of array as recommended in enumerate() to avoid running of gas.\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n\n * WARNING: This function may run out of gas on large sets: use {length} and\n * {get} instead in these cases.\n \n * @param start start index of chunk\n * @param count num of element to return; if count == 0 then returns all the elements from the @param start\n */\n function enumerateChunk(\n AddressSet storage set,\n uint256 start,\n uint256 count\n ) internal view returns (address[] memory output) {\n uint256 end = start + count;\n require(end >= start, \"addition overflow\");\n end = (set.values.length < end || count == 0) ? set.values.length : end;\n if (end == 0 || start >= end) {\n return output;\n }\n\n output = new address[](end - start);\n for (uint256 i; i < end - start; i++) {\n output[i] = set.values[i + start];\n }\n return output;\n }\n\n /**\n * @dev Returns the number of elements on the set. O(1).\n */\n function length(AddressSet storage set) internal view returns (uint256) {\n return set.values.length;\n }\n\n /** @dev Returns the element stored at position `index` in the set. O(1).\n * Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n */\n function get(AddressSet storage set, uint256 index) internal view returns (address) {\n return set.values[index];\n }\n}\n" + }, + "contracts/mixins/EnumerableBytes32Set.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\n/**\n * @title Library for managing loan sets.\n *\n * @notice Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * Include with `using EnumerableBytes32Set for EnumerableBytes32Set.Bytes32Set;`.\n * */\nlibrary EnumerableBytes32Set {\n struct Bytes32Set {\n /// Position of the value in the `values` array, plus 1 because index 0\n /// means a value is not in the set.\n mapping(bytes32 => uint256) index;\n bytes32[] values;\n }\n\n /**\n * @notice Add an address value to a set. O(1).\n *\n * @param set The set of values.\n * @param addrvalue The address to add.\n *\n * @return False if the value was already in the set.\n */\n function addAddress(Bytes32Set storage set, address addrvalue) internal returns (bool) {\n bytes32 value;\n assembly {\n value := addrvalue\n }\n return addBytes32(set, value);\n }\n\n /**\n * @notice Add a value to a set. O(1).\n *\n * @param set The set of values.\n * @param value The new value to add.\n *\n * @return False if the value was already in the set.\n */\n function addBytes32(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n if (!contains(set, value)) {\n set.index[value] = set.values.push(value);\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @notice Remove an address value from a set. O(1).\n *\n * @param set The set of values.\n * @param addrvalue The address to remove.\n *\n * @return False if the address was not present in the set.\n */\n function removeAddress(Bytes32Set storage set, address addrvalue) internal returns (bool) {\n bytes32 value;\n assembly {\n value := addrvalue\n }\n return removeBytes32(set, value);\n }\n\n /**\n * @notice Remove a value from a set. O(1).\n *\n * @param set The set of values.\n * @param value The value to remove.\n *\n * @return False if the value was not present in the set.\n */\n function removeBytes32(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n if (contains(set, value)) {\n uint256 toDeleteIndex = set.index[value] - 1;\n uint256 lastIndex = set.values.length - 1;\n\n /// If the element we're deleting is the last one,\n /// we can just remove it without doing a swap.\n if (lastIndex != toDeleteIndex) {\n bytes32 lastValue = set.values[lastIndex];\n\n /// Move the last value to the index where the deleted value is.\n set.values[toDeleteIndex] = lastValue;\n\n /// Update the index for the moved value.\n set.index[lastValue] = toDeleteIndex + 1; // All indexes are 1-based\n }\n\n /// Delete the index entry for the deleted value.\n delete set.index[value];\n\n /// Delete the old entry for the moved value.\n set.values.pop();\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @notice Find out whether a value exists in the set.\n *\n * @param set The set of values.\n * @param value The value to find.\n *\n * @return True if the value is in the set. O(1).\n */\n function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n return set.index[value] != 0;\n }\n\n /**\n * @dev Returns true if the value is in the set. O(1).\n */\n function containsAddress(Bytes32Set storage set, address addrvalue)\n internal\n view\n returns (bool)\n {\n bytes32 value;\n assembly {\n value := addrvalue\n }\n return set.index[value] != 0;\n }\n\n /**\n * @notice Get all set values.\n *\n * @param set The set of values.\n * @param start The offset of the returning set.\n * @param count The limit of number of values to return.\n *\n * @return An array with all values in the set. O(N).\n *\n * @dev Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * WARNING: This function may run out of gas on large sets: use {length} and\n * {get} instead in these cases.\n */\n function enumerate(\n Bytes32Set storage set,\n uint256 start,\n uint256 count\n ) internal view returns (bytes32[] memory output) {\n uint256 end = start + count;\n require(end >= start, \"addition overflow\");\n end = set.values.length < end ? set.values.length : end;\n if (end == 0 || start >= end) {\n return output;\n }\n\n output = new bytes32[](end - start);\n for (uint256 i; i < end - start; i++) {\n output[i] = set.values[i + start];\n }\n return output;\n }\n\n /**\n * @notice Get the legth of the set.\n *\n * @param set The set of values.\n *\n * @return the number of elements on the set. O(1).\n */\n function length(Bytes32Set storage set) internal view returns (uint256) {\n return set.values.length;\n }\n\n /**\n * @notice Get an item from the set by its index.\n *\n * @dev Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n *\n * @param set The set of values.\n * @param index The index of the value to return.\n *\n * @return the element stored at position `index` in the set. O(1).\n */\n function get(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n return set.values[index];\n }\n}\n" + }, + "contracts/mixins/EnumerableBytes4Set.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\n/**\n * @title Library for managing loan sets.\n *\n * @notice Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * Include with `using EnumerableBytes4Set for EnumerableBytes4Set.Bytes4Set;`.\n * */\nlibrary EnumerableBytes4Set {\n struct Bytes4Set {\n /// Position of the value in the `values` array, plus 1 because index 0\n /// means a value is not in the set.\n mapping(bytes4 => uint256) index;\n bytes4[] values;\n }\n\n /**\n * @notice Add a value to a set. O(1).\n *\n * @param set The set of values.\n * @param value The new value to add.\n *\n * @return False if the value was already in the set.\n */\n function addBytes4(Bytes4Set storage set, bytes4 value) internal returns (bool) {\n if (!contains(set, value)) {\n set.index[value] = set.values.push(value);\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @notice Remove a value from a set. O(1).\n *\n * @param set The set of values.\n * @param value The value to remove.\n *\n * @return False if the value was not present in the set.\n */\n function removeBytes4(Bytes4Set storage set, bytes4 value) internal returns (bool) {\n if (contains(set, value)) {\n uint256 toDeleteIndex = set.index[value] - 1;\n uint256 lastIndex = set.values.length - 1;\n\n /// If the element we're deleting is the last one,\n /// we can just remove it without doing a swap.\n if (lastIndex != toDeleteIndex) {\n bytes4 lastValue = set.values[lastIndex];\n\n /// Move the last value to the index where the deleted value is.\n set.values[toDeleteIndex] = lastValue;\n\n /// Update the index for the moved value.\n set.index[lastValue] = toDeleteIndex + 1; // All indexes are 1-based\n }\n\n /// Delete the index entry for the deleted value.\n delete set.index[value];\n\n /// Delete the old entry for the moved value.\n set.values.pop();\n\n return true;\n } else {\n return false;\n }\n }\n\n /**\n * @notice Find out whether a value exists in the set.\n *\n * @param set The set of values.\n * @param value The value to find.\n *\n * @return True if the value is in the set. O(1).\n */\n function contains(Bytes4Set storage set, bytes4 value) internal view returns (bool) {\n return set.index[value] != 0;\n }\n\n /**\n * @notice Get all set values.\n *\n * @param set The set of values.\n * @param start The offset of the returning set.\n * @param count The limit of number of values to return.\n *\n * @return An array with all values in the set. O(N).\n *\n * @dev Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * WARNING: This function may run out of gas on large sets: use {length} and\n * {get} instead in these cases.\n */\n function enumerate(\n Bytes4Set storage set,\n uint256 start,\n uint256 count\n ) internal view returns (bytes4[] memory output) {\n uint256 end = start + count;\n require(end >= start, \"addition overflow\");\n end = set.values.length < end ? set.values.length : end;\n if (end == 0 || start >= end) {\n return output;\n }\n\n output = new bytes4[](end - start);\n for (uint256 i; i < end - start; i++) {\n output[i] = set.values[i + start];\n }\n return output;\n }\n\n /**\n * @notice Get the legth of the set.\n *\n * @param set The set of values.\n *\n * @return the number of elements on the set. O(1).\n */\n function length(Bytes4Set storage set) internal view returns (uint256) {\n return set.values.length;\n }\n\n /**\n * @notice Get an item from the set by its index.\n *\n * @dev Note that there are no guarantees on the ordering of values inside the\n * array, and it may change when more values are added or removed.\n *\n * Requirements:\n *\n * - `index` must be strictly less than {length}.\n *\n * @param set The set of values.\n * @param index The index of the value to return.\n *\n * @return the element stored at position `index` in the set. O(1).\n */\n function get(Bytes4Set storage set, uint256 index) internal view returns (bytes4) {\n return set.values[index];\n }\n}\n" + }, + "contracts/mixins/FeesHelper.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../core/State.sol\";\nimport \"../openzeppelin/SafeERC20.sol\";\nimport \"../feeds/IPriceFeeds.sol\";\nimport \"../events/FeesEvents.sol\";\nimport \"../modules/interfaces/ProtocolAffiliatesInterface.sol\";\nimport \"../interfaces/ISovryn.sol\";\nimport \"../core/objects/LoanParamsStruct.sol\";\n\n/**\n * @title The Fees Helper contract.\n *\n * This contract calculates and pays lending/borrow fees and rewards.\n * */\ncontract FeesHelper is State, FeesEvents {\n using SafeERC20 for IERC20;\n\n /**\n * @notice Calculate trading fee.\n * @param feeTokenAmount The amount of tokens to trade.\n * @return The fee of the trade.\n * */\n function _getTradingFee(uint256 feeTokenAmount) internal view returns (uint256) {\n return feeTokenAmount.mul(tradingFeePercent).divCeil(10**20);\n }\n\n /**\n * @notice Calculate swap external fee.\n * @param feeTokenAmount The amount of token to swap.\n * @return The fee of the swap.\n */\n function _getSwapExternalFee(uint256 feeTokenAmount) internal view returns (uint256) {\n return feeTokenAmount.mul(swapExtrernalFeePercent).divCeil(10**20);\n }\n\n /*\n\t// p3.9 from bzx peckshield-audit-report-bZxV2-v1.0rc1.pdf\n\t// cannot be applied solely nor with LoanOpenings.sol as it drives to some other tests failure\n\tfunction _getTradingFee(uint256 feeTokenAmount) internal view returns (uint256) {\n\t\tuint256 collateralAmountRequired =\n\t\t\tfeeTokenAmount.mul(10**20).divCeil(\n\t\t\t\t10**20 - tradingFeePercent // never will overflow\n\t\t\t);\n\t\treturn collateralAmountRequired.sub(feeTokenAmount);\n\t}*/\n\n /**\n * @notice Calculate the loan origination fee.\n * @param feeTokenAmount The amount of tokens to borrow.\n * @return The fee of the loan.\n * */\n function _getBorrowingFee(uint256 feeTokenAmount) internal view returns (uint256) {\n return feeTokenAmount.mul(borrowingFeePercent).divCeil(10**20);\n /*\n\t\t// p3.9 from bzx peckshield-audit-report-bZxV2-v1.0rc1.pdf\n\t\t// cannot be applied solely nor with LoanOpenings.sol as it drives to some other tests failure\n\t\tuint256 collateralAmountRequired =\n\t\t\tfeeTokenAmount.mul(10**20).divCeil(\n\t\t\t\t10**20 - borrowingFeePercent // never will overflow\n\t\t\t);\n\t\treturn collateralAmountRequired.sub(feeTokenAmount);*/\n }\n\n /**\n * @notice Settle the trading fee and pay the token reward to the affiliates referrer.\n *\n * @param referrer The affiliate referrer address to send the reward to.\n * @param trader The account that performs this trade.\n * @param feeToken The address of the token in which the trading fee is paid.\n * @param tradingFee The amount of tokens accrued as fees on the trading.\n *\n * @return affiliatesBonusSOVAmount the total SOV amount that is distributed to the referrer\n * @return affiliatesBonusTokenAmount the total Token Base on the trading fee pairs that is distributed to the referrer\n * */\n function _payTradingFeeToAffiliate(\n address referrer,\n address trader,\n address feeToken,\n uint256 tradingFee\n ) internal returns (uint256 affiliatesBonusSOVAmount, uint256 affiliatesBonusTokenAmount) {\n (affiliatesBonusSOVAmount, affiliatesBonusTokenAmount) = ProtocolAffiliatesInterface(\n address(this)\n )\n .payTradingFeeToAffiliatesReferrer(referrer, trader, feeToken, tradingFee);\n }\n\n /**\n * @notice Settle the trading fee and pay the token reward to the user.\n * @param user The address to send the reward to.\n * @param loanId The Id of the associated loan - used for logging only.\n * @param feeToken The address of the token in which the trading fee is paid.\n * @param tradingFee The amount of tokens accrued as fees on the trading.\n * */\n function _payTradingFee(\n address user,\n bytes32 loanId,\n address feeToken,\n address feeTokenPair,\n uint256 tradingFee\n ) internal {\n uint256 protocolTradingFee = tradingFee; /// Trading fee paid to protocol.\n if (tradingFee != 0) {\n if (affiliatesUserReferrer[user] != address(0)) {\n _payTradingFeeToAffiliate(\n affiliatesUserReferrer[user],\n user,\n feeToken,\n protocolTradingFee\n );\n protocolTradingFee = (\n protocolTradingFee.sub(protocolTradingFee.mul(affiliateFeePercent).div(10**20))\n )\n .sub(protocolTradingFee.mul(affiliateTradingTokenFeePercent).div(10**20));\n }\n\n /// Increase the storage variable keeping track of the accumulated fees.\n tradingFeeTokensHeld[feeToken] = tradingFeeTokensHeld[feeToken].add(\n protocolTradingFee\n );\n\n emit PayTradingFee(user, feeToken, loanId, protocolTradingFee);\n\n /// Pay the token reward to the user.\n _payFeeReward(user, loanId, feeToken, feeTokenPair, tradingFee);\n }\n }\n\n /**\n * @notice Settle the borrowing fee and pay the token reward to the user.\n * @param user The address to send the reward to.\n * @param loanId The Id of the associated loan - used for logging only.\n * @param feeToken The address of the token in which the borrowig fee is paid.\n * @param borrowingFee The height of the fee.\n * */\n function _payBorrowingFee(\n address user,\n bytes32 loanId,\n address feeToken,\n address feeTokenPair,\n uint256 borrowingFee\n ) internal {\n if (borrowingFee != 0) {\n /// Increase the storage variable keeping track of the accumulated fees.\n borrowingFeeTokensHeld[feeToken] = borrowingFeeTokensHeld[feeToken].add(borrowingFee);\n\n emit PayBorrowingFee(user, feeToken, loanId, borrowingFee);\n\n /// Pay the token reward to the user.\n _payFeeReward(user, loanId, feeToken, feeTokenPair, borrowingFee);\n }\n }\n\n /**\n * @notice Settle the lending fee (based on the interest). Pay no token reward to the user.\n * @param user The address to send the reward to.\n * @param feeToken The address of the token in which the lending fee is paid.\n * @param lendingFee The height of the fee.\n * */\n function _payLendingFee(\n address user,\n address feeToken,\n uint256 lendingFee\n ) internal {\n if (lendingFee != 0) {\n /// Increase the storage variable keeping track of the accumulated fees.\n lendingFeeTokensHeld[feeToken] = lendingFeeTokensHeld[feeToken].add(lendingFee);\n\n emit PayLendingFee(user, feeToken, lendingFee);\n\n //// NOTE: Lenders do not receive a fee reward ////\n }\n }\n\n /// Settle and pay borrowers based on the fees generated by their interest payments.\n function _settleFeeRewardForInterestExpense(\n LoanInterest storage loanInterestLocal,\n bytes32 loanId,\n address feeToken,\n address feeTokenPair,\n address user,\n uint256 interestTime\n ) internal {\n /// This represents the fee generated by a borrower's interest payment.\n uint256 interestExpenseFee =\n interestTime\n .sub(loanInterestLocal.updatedTimestamp)\n .mul(loanInterestLocal.owedPerDay)\n .mul(lendingFeePercent)\n .div(1 days * 10**20);\n\n loanInterestLocal.updatedTimestamp = interestTime;\n\n if (interestExpenseFee != 0) {\n _payFeeReward(user, loanId, feeToken, feeTokenPair, interestExpenseFee);\n }\n }\n\n /**\n * @notice Pay the potocolToken reward to user. The reward is worth 50% of the trading/borrowing fee.\n * @param user The address to send the reward to.\n * @param loanId The Id of the associeated loan - used for logging only.\n * @param feeToken The address of the token in which the trading/borrowing fee was paid.\n * @param feeAmount The height of the fee.\n * */\n function _payFeeReward(\n address user,\n bytes32 loanId,\n address feeToken,\n address feeTokenPair,\n uint256 feeAmount\n ) internal {\n uint256 rewardAmount;\n uint256 _feeRebatePercent = feeRebatePercent;\n address _priceFeeds = priceFeeds;\n\n if (specialRebates[feeToken][feeTokenPair] > 0) {\n _feeRebatePercent = specialRebates[feeToken][feeTokenPair];\n }\n\n /// Note: this should be refactored.\n /// Calculate the reward amount, querying the price feed.\n (bool success, bytes memory data) =\n _priceFeeds.staticcall(\n abi.encodeWithSelector(\n IPriceFeeds(_priceFeeds).queryReturn.selector,\n feeToken,\n sovTokenAddress, /// Price rewards using BZRX price rather than vesting token price.\n feeAmount.mul(_feeRebatePercent).div(10**20)\n )\n );\n // solhint-disable-next-line no-inline-assembly\n assembly {\n if eq(success, 1) {\n rewardAmount := mload(add(data, 32))\n }\n }\n\n // Check the dedicated SOV that is used to pay trading rebate rewards\n uint256 dedicatedSOV = ISovryn(address(this)).getDedicatedSOVRebate();\n if (rewardAmount != 0 && dedicatedSOV >= rewardAmount) {\n IERC20(sovTokenAddress).approve(lockedSOVAddress, rewardAmount);\n\n (bool success, ) =\n lockedSOVAddress.call(\n abi.encodeWithSignature(\n \"deposit(address,uint256,uint256)\",\n user,\n rewardAmount,\n tradingRebateRewardsBasisPoint\n )\n );\n\n if (success) {\n protocolTokenPaid = protocolTokenPaid.add(rewardAmount);\n\n emit EarnReward(\n user,\n sovTokenAddress,\n loanId,\n _feeRebatePercent,\n rewardAmount,\n tradingRebateRewardsBasisPoint\n );\n } else {\n emit EarnRewardFail(\n user,\n sovTokenAddress,\n loanId,\n _feeRebatePercent,\n rewardAmount,\n tradingRebateRewardsBasisPoint\n );\n }\n } else if (rewardAmount != 0 && dedicatedSOV < rewardAmount) {\n emit EarnRewardFail(\n user,\n sovTokenAddress,\n loanId,\n _feeRebatePercent,\n rewardAmount,\n tradingRebateRewardsBasisPoint\n );\n }\n }\n}\n" + }, + "contracts/mixins/InterestUser.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../openzeppelin/SafeERC20.sol\";\nimport \"../core/State.sol\";\nimport \"../mixins/VaultController.sol\";\nimport \"./FeesHelper.sol\";\n\n/**\n * @title The Interest User contract.\n *\n * This contract pays loan interests.\n * */\ncontract InterestUser is VaultController, FeesHelper {\n using SafeERC20 for IERC20;\n\n /// Triggered whenever interest is paid to lender.\n event PayInterestTransfer(\n address indexed interestToken,\n address indexed lender,\n uint256 effectiveInterest\n );\n\n /**\n * @notice Internal function to pay interest of a loan.\n * @dev Calls _payInterestTransfer internal function to transfer tokens.\n * @param lender The account address of the lender.\n * @param interestToken The token address to pay interest with.\n * */\n function _payInterest(address lender, address interestToken) internal {\n LenderInterest storage lenderInterestLocal = lenderInterest[lender][interestToken];\n\n uint256 interestOwedNow = 0;\n if (lenderInterestLocal.owedPerDay != 0 && lenderInterestLocal.updatedTimestamp != 0) {\n interestOwedNow = block\n .timestamp\n .sub(lenderInterestLocal.updatedTimestamp)\n .mul(lenderInterestLocal.owedPerDay)\n .div(1 days);\n\n lenderInterestLocal.updatedTimestamp = block.timestamp;\n\n if (interestOwedNow > lenderInterestLocal.owedTotal)\n interestOwedNow = lenderInterestLocal.owedTotal;\n\n if (interestOwedNow != 0) {\n lenderInterestLocal.paidTotal = lenderInterestLocal.paidTotal.add(interestOwedNow);\n lenderInterestLocal.owedTotal = lenderInterestLocal.owedTotal.sub(interestOwedNow);\n\n _payInterestTransfer(lender, interestToken, interestOwedNow);\n }\n } else {\n lenderInterestLocal.updatedTimestamp = block.timestamp;\n }\n }\n\n /**\n * @notice Internal function to transfer tokens for the interest of a loan.\n * @param lender The account address of the lender.\n * @param interestToken The token address to pay interest with.\n * @param interestOwedNow The amount of interest to pay.\n * */\n function _payInterestTransfer(\n address lender,\n address interestToken,\n uint256 interestOwedNow\n ) internal {\n uint256 lendingFee = interestOwedNow.mul(lendingFeePercent).div(10**20);\n /// TODO: refactor: data incapsulation violation and DRY design principles\n /// uint256 lendingFee = interestOwedNow.mul(lendingFeePercent).divCeil(10**20); is better but produces errors in tests because of this\n\n _payLendingFee(lender, interestToken, lendingFee);\n\n /// Transfers the interest to the lender, less the interest fee.\n vaultWithdraw(interestToken, lender, interestOwedNow.sub(lendingFee));\n\n /// Event Log\n emit PayInterestTransfer(interestToken, lender, interestOwedNow.sub(lendingFee));\n }\n}\n" + }, + "contracts/mixins/LiquidationHelper.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../core/State.sol\";\n\n/**\n * @title The Liquidation Helper contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized margin\n * trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract computes the liquidation amount.\n * */\ncontract LiquidationHelper is State {\n /**\n * @notice Compute how much needs to be liquidated in order to restore the\n * desired margin (maintenance + 5%).\n *\n * @param principal The total borrowed amount (in loan tokens).\n * @param collateral The collateral (in collateral tokens).\n * @param currentMargin The current margin.\n * @param maintenanceMargin The maintenance (minimum) margin.\n * @param collateralToLoanRate The exchange rate from collateral to loan\n * tokens.\n *\n * @return maxLiquidatable The collateral you can get liquidating.\n * @return maxSeizable The loan you available for liquidation.\n * @return incentivePercent The discount on collateral.\n * */\n function _getLiquidationAmounts(\n uint256 principal,\n uint256 collateral,\n uint256 currentMargin,\n uint256 maintenanceMargin,\n uint256 collateralToLoanRate\n )\n internal\n view\n returns (\n uint256 maxLiquidatable,\n uint256 maxSeizable,\n uint256 incentivePercent\n )\n {\n incentivePercent = liquidationIncentivePercent;\n if (currentMargin > maintenanceMargin || collateralToLoanRate == 0) {\n return (maxLiquidatable, maxSeizable, incentivePercent);\n } else if (currentMargin <= incentivePercent) {\n return (principal, collateral, currentMargin);\n }\n\n /// 5 percentage points above maintenance.\n uint256 desiredMargin = maintenanceMargin.add(5 ether);\n\n /// maxLiquidatable = ((1 + desiredMargin)*principal - collateralToLoanRate*collateral) / (desiredMargin - 0.05)\n maxLiquidatable = desiredMargin.add(10**20).mul(principal).div(10**20);\n maxLiquidatable = maxLiquidatable.sub(collateral.mul(collateralToLoanRate).div(10**18));\n maxLiquidatable = maxLiquidatable.mul(10**20).div(desiredMargin.sub(incentivePercent));\n if (maxLiquidatable > principal) {\n maxLiquidatable = principal;\n }\n\n /// maxSeizable = maxLiquidatable * (1 + incentivePercent) / collateralToLoanRate\n maxSeizable = maxLiquidatable.mul(incentivePercent.add(10**20));\n maxSeizable = maxSeizable.div(collateralToLoanRate).div(100);\n if (maxSeizable > collateral) {\n maxSeizable = collateral;\n }\n\n return (maxLiquidatable, maxSeizable, incentivePercent);\n }\n}\n" + }, + "contracts/mixins/ModuleCommonFunctionalities.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../core/State.sol\";\n\ncontract ModuleCommonFunctionalities is State {\n modifier whenNotPaused() {\n require(!pause, \"Paused\");\n _;\n }\n}\n" + }, + "contracts/mixins/ProtocolTokenUser.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../core/State.sol\";\nimport \"../openzeppelin/SafeERC20.sol\";\n\n/**\n * @title The Protocol Token User contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized margin\n * trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract implements functionality to withdraw protocol tokens.\n * */\ncontract ProtocolTokenUser is State {\n using SafeERC20 for IERC20;\n\n /**\n * @notice Internal function to withdraw an amount of protocol tokens from this contract.\n *\n * @param receiver The address of the recipient.\n * @param amount The amount of tokens to withdraw.\n *\n * @return The protocol token address.\n * @return Withdrawal success (true/false).\n * */\n function _withdrawProtocolToken(address receiver, uint256 amount)\n internal\n returns (address, bool)\n {\n uint256 withdrawAmount = amount;\n\n uint256 tokenBalance = protocolTokenHeld;\n if (withdrawAmount > tokenBalance) {\n withdrawAmount = tokenBalance;\n }\n if (withdrawAmount == 0) {\n return (protocolTokenAddress, false);\n }\n\n protocolTokenHeld = tokenBalance.sub(withdrawAmount);\n\n IERC20(protocolTokenAddress).safeTransfer(receiver, withdrawAmount);\n\n return (protocolTokenAddress, true);\n }\n}\n" + }, + "contracts/mixins/RewardHelper.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../core/State.sol\";\nimport \"../feeds/IPriceFeeds.sol\";\n\n/**\n * @title The Reward Helper contract.\n * @notice This contract calculates the reward for rollover transactions.\n *\n * A rollover is a renewal of a deposit. Instead of liquidating a deposit\n * on maturity, you can roll it over into a new deposit. The outstanding\n * principal of the old deposit is rolled over with or without the interest\n * outstanding on it.\n * */\ncontract RewardHelper is State {\n using SafeMath for uint256;\n\n /**\n * @notice Calculate the reward of a rollover transaction.\n *\n * @param collateralToken The address of the collateral token.\n * @param loanToken The address of the loan token.\n * @param positionSize The amount of value of the position.\n *\n * @return The base fee + the flex fee.\n */\n function _getRolloverReward(\n address collateralToken,\n address loanToken,\n uint256 positionSize\n ) internal view returns (uint256 reward) {\n uint256 positionSizeInCollateralToken =\n IPriceFeeds(priceFeeds).queryReturn(loanToken, collateralToken, positionSize);\n uint256 rolloverBaseRewardInCollateralToken =\n IPriceFeeds(priceFeeds).queryReturn(\n address(wrbtcToken),\n collateralToken,\n rolloverBaseReward\n );\n\n return\n rolloverBaseRewardInCollateralToken\n .mul(2) /// baseFee\n .add(positionSizeInCollateralToken.mul(rolloverFlexFeePercent).div(10**20)); /// flexFee = 0.1% of position size\n }\n}\n" + }, + "contracts/mixins/VaultController.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../openzeppelin/SafeERC20.sol\";\nimport \"../core/State.sol\";\n\n/**\n * @title The Vault Controller contract.\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized margin\n * trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract implements functionality to deposit and withdraw wrBTC and\n * other tokens from the vault.\n * */\ncontract VaultController is State {\n using SafeERC20 for IERC20;\n\n event VaultDeposit(address indexed asset, address indexed from, uint256 amount);\n event VaultWithdraw(address indexed asset, address indexed to, uint256 amount);\n\n /**\n * @notice Deposit wrBTC into the vault.\n *\n * @param from The address of the account paying the deposit.\n * @param value The amount of wrBTC tokens to transfer.\n */\n function vaultEtherDeposit(address from, uint256 value) internal {\n IWrbtcERC20 _wrbtcToken = wrbtcToken;\n _wrbtcToken.deposit.value(value)();\n\n emit VaultDeposit(address(_wrbtcToken), from, value);\n }\n\n /**\n * @notice Withdraw wrBTC from the vault.\n *\n * @param to The address of the recipient.\n * @param value The amount of wrBTC tokens to transfer.\n */\n function vaultEtherWithdraw(address to, uint256 value) internal {\n if (value != 0) {\n IWrbtcERC20 _wrbtcToken = wrbtcToken;\n uint256 balance = address(this).balance;\n if (value > balance) {\n _wrbtcToken.withdraw(value - balance);\n }\n Address.sendValue(to, value);\n\n emit VaultWithdraw(address(_wrbtcToken), to, value);\n }\n }\n\n /**\n * @notice Deposit tokens into the vault.\n *\n * @param token The address of the token instance.\n * @param from The address of the account paying the deposit.\n * @param value The amount of tokens to transfer.\n */\n function vaultDeposit(\n address token,\n address from,\n uint256 value\n ) internal {\n if (value != 0) {\n IERC20(token).safeTransferFrom(from, address(this), value);\n\n emit VaultDeposit(token, from, value);\n }\n }\n\n /**\n * @notice Withdraw tokens from the vault.\n *\n * @param token The address of the token instance.\n * @param to The address of the recipient.\n * @param value The amount of tokens to transfer.\n */\n function vaultWithdraw(\n address token,\n address to,\n uint256 value\n ) internal {\n if (value != 0) {\n IERC20(token).safeTransfer(to, value);\n\n emit VaultWithdraw(token, to, value);\n }\n }\n\n /**\n * @notice Transfer tokens from an account into another one.\n *\n * @param token The address of the token instance.\n * @param from The address of the account paying.\n * @param to The address of the recipient.\n * @param value The amount of tokens to transfer.\n */\n function vaultTransfer(\n address token,\n address from,\n address to,\n uint256 value\n ) internal {\n if (value != 0) {\n if (from == address(this)) {\n IERC20(token).safeTransfer(to, value);\n } else {\n IERC20(token).safeTransferFrom(from, to, value);\n }\n }\n }\n\n /**\n * @notice Approve an allowance of tokens to be spent by an account.\n *\n * @param token The address of the token instance.\n * @param to The address of the spender.\n * @param value The amount of tokens to allow.\n */\n function vaultApprove(\n address token,\n address to,\n uint256 value\n ) internal {\n if (value != 0 && IERC20(token).allowance(address(this), to) != 0) {\n IERC20(token).safeApprove(to, 0);\n }\n IERC20(token).safeApprove(to, value);\n }\n}\n" + }, + "contracts/mockup/BlockMockUp.sol": { + "content": "pragma solidity 0.5.17;\n\n/**\n * @title Used to get and set mock block number.\n */\ncontract BlockMockUp {\n uint256 public blockNum;\n\n /**\n * @notice To get the `blockNum`.\n * @return _blockNum The block number.\n */\n function getBlockNum() public view returns (uint256 _blockNum) {\n return blockNum;\n }\n\n /**\n * @notice To set the `blockNum`.\n * @param _blockNum The block number.\n */\n function setBlockNum(uint256 _blockNum) public {\n blockNum = _blockNum;\n }\n}\n" + }, + "contracts/mockup/FeeSharingCollectorMockup.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../governance/FeeSharingCollector/FeeSharingCollector.sol\";\n\ncontract FeeSharingCollectorMockup is FeeSharingCollector {\n struct TestData {\n address loanPoolToken;\n uint32 maxCheckpoints;\n address receiver;\n }\n\n TestData public testData;\n\n constructor(IProtocol _protocol, IStaking _staking) public {\n protocol = _protocol;\n staking = _staking;\n }\n\n function withdraw(\n address _token,\n uint32 _maxCheckpoints,\n address _receiver\n ) public {\n testData = TestData(_token, _maxCheckpoints, _receiver);\n }\n\n function trueWithdraw(\n address _token,\n uint32 _maxCheckpoints,\n address _receiver\n ) public {\n super.withdraw(_token, _maxCheckpoints, _receiver);\n }\n\n function addCheckPoint(address loanPoolToken, uint256 poolTokenAmount) public {\n uint96 amount96 =\n safe96(\n poolTokenAmount,\n \"FeeSharingCollectorProxy::withdrawFees: pool token amount exceeds 96 bits\"\n );\n _addCheckpoint(loanPoolToken, amount96);\n }\n\n function setTotalTokenCheckpoints(address _token, uint256 qty) public {\n totalTokenCheckpoints[_token] = qty;\n }\n\n function setUserProcessedCheckpoints(\n address _user,\n address _token,\n uint256 num\n ) public {\n processedCheckpoints[_user][_token] = num;\n }\n\n function getFullAccumulatedFees(\n address _user,\n address _token,\n uint32 _maxCheckpoints\n ) public view returns (uint256 amount, uint256 end) {\n (amount, end) = _getAccumulatedFees(_user, _token, 0, _maxCheckpoints);\n }\n\n function endOfRangeWithZeroMaxCheckpoint(address _token) public view returns (uint256) {\n return _getEndOfRange(0, _token, 0);\n }\n\n function getRBTCBalance(\n address _token,\n address _user,\n uint32 _maxCheckpoints\n ) public view returns (uint256 _tokenAmount, uint256 _endToken) {\n return _getRBTCBalance(_token, _user, _maxCheckpoints);\n }\n\n function testWithdrawReentrancy(\n address _token,\n uint32 _maxCheckpoints,\n address _receiver\n ) public {\n reentrancyLock = REENTRANCY_GUARD_LOCKED;\n super.withdraw(_token, _maxCheckpoints, _receiver);\n }\n}\n" + }, + "contracts/mockup/GovernorAlphaMockup.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../governance/GovernorAlpha.sol\";\n\ncontract GovernorAlphaMockup is GovernorAlpha {\n constructor(\n address timelock_,\n address staking_,\n address guardian_,\n uint96 quorumVotes_,\n uint96 _minPercentageVotes\n ) public GovernorAlpha(timelock_, staking_, guardian_, quorumVotes_, _minPercentageVotes) {}\n\n function votingPeriod() public pure returns (uint256) {\n return 10;\n }\n\n function queueProposals(uint256[] calldata proposalIds) external {\n for (uint256 i = 0; i < proposalIds.length; i++) {\n queue(proposalIds[i]);\n }\n }\n}\n" + }, + "contracts/mockup/LiquidityMiningMockup.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../farm/LiquidityMining.sol\";\n\ncontract LiquidityMiningMockup is LiquidityMining {\n function getPassedBlocksWithBonusMultiplier(uint256 _from, uint256 _to)\n public\n view\n returns (uint256)\n {\n return _getPassedBlocksWithBonusMultiplier(_from, _to);\n }\n\n function getPoolAccumulatedReward(address _poolToken) public view returns (uint256, uint256) {\n uint256 poolId = _getPoolId(_poolToken);\n PoolInfo storage pool = poolInfoList[poolId];\n return _getPoolAccumulatedReward(pool);\n }\n}\n" + }, + "contracts/mockup/LiquidityPoolV1ConverterMockup.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../interfaces/IERC20.sol\";\n\ncontract LiquidityPoolV1ConverterMockup {\n IERC20[] public reserveTokens;\n IERC20 wrbtcToken;\n uint256 totalFeeMockupValue;\n address feesController;\n\n constructor(IERC20 _token0, IERC20 _token1) public {\n reserveTokens.push(_token0);\n reserveTokens.push(_token1);\n }\n\n function setFeesController(address _feesController) public {\n feesController = _feesController;\n }\n\n function setWrbtcToken(IERC20 _wrbtcToken) public {\n wrbtcToken = _wrbtcToken;\n }\n\n function setTotalFeeMockupValue(uint256 _totalFeeMockupValue) public {\n totalFeeMockupValue = _totalFeeMockupValue;\n }\n\n function withdrawFees(address _receiver) external returns (uint256) {\n require(msg.sender == feesController, \"unauthorized\");\n\n // transfer wrbtc\n wrbtcToken.transfer(_receiver, totalFeeMockupValue);\n return totalFeeMockupValue;\n }\n}\n" + }, + "contracts/mockup/LoanClosingsWithMockup.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../modules/LoanClosingsWith.sol\";\n\ncontract LoanClosingsWithMockup is LoanClosingsWith {\n function worthTheTransfer(address, uint256) internal returns (bool) {\n return true;\n }\n\n function initialize(address target) external onlyOwner {\n address prevModuleContractAddress = logicTargets[this.closeWithDeposit.selector];\n _setTarget(this.closeWithDeposit.selector, target);\n _setTarget(this.closeWithSwap.selector, target);\n _setTarget(this.checkCloseWithDepositIsTinyPosition.selector, target);\n emit ProtocolModuleContractReplaced(prevModuleContractAddress, target, \"LoanClosingsWith\");\n }\n}\n" + }, + "contracts/mockup/LoanClosingsWithoutInvariantCheck.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"./LoanClosingsWithMockup.sol\";\n\ncontract LoanClosingsWithoutInvariantCheck is LoanClosingsWithMockup {\n /** Override the modifier of invariant check so that we can test the shared reentrancy guard */\n modifier iTokenSupplyUnchanged(bytes32 loanId) {\n _;\n }\n\n function initialize(address target) external onlyOwner {\n address prevModuleContractAddress = logicTargets[this.closeWithDeposit.selector];\n _setTarget(this.closeWithDeposit.selector, target);\n _setTarget(this.closeWithSwap.selector, target);\n _setTarget(this.checkCloseWithDepositIsTinyPosition.selector, target);\n emit ProtocolModuleContractReplaced(prevModuleContractAddress, target, \"LoanClosingsWith\");\n }\n}\n" + }, + "contracts/mockup/LoanTokenLogicLMMockup.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../connectors/loantoken/modules/beaconLogicLM/LoanTokenLogicLM.sol\";\n\ncontract LoanTokenLogicLMMockup is LoanTokenLogicLM {\n function burn(address receiver, uint256 burnAmount)\n external\n nonReentrant\n returns (uint256 loanAmountPaid)\n {\n _callOptionalReturn(\n 0x2c34D66a5ca8686330e100372Eb3FDFB5aEECD0B, //Random EOA for testing\n abi.encodeWithSelector(IERC20(receiver).transfer.selector, receiver, burnAmount),\n \"error\"\n );\n }\n}\n" + }, + "contracts/mockup/LoanTokenLogicV2Mockup.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../connectors/loantoken/modules/beaconLogicLM/LoanTokenLogic.sol\";\n\ncontract LoanTokenLogicV1Mockup is LoanTokenLogicStandard {\n function getListFunctionSignatures()\n external\n pure\n returns (bytes4[] memory functionSignatures, bytes32 moduleName)\n {\n bytes4[] memory res = new bytes4[](27);\n\n // Loan Token Logic Standard\n res[0] = this.borrow.selector;\n res[1] = this.marginTrade.selector;\n res[2] = this.marginTradeAffiliate.selector;\n res[3] = this.transfer.selector;\n res[4] = this.transferFrom.selector;\n res[5] = this.profitOf.selector;\n res[6] = this.tokenPrice.selector;\n res[7] = this.checkpointPrice.selector;\n res[8] = this.marketLiquidity.selector;\n res[9] = this.avgBorrowInterestRate.selector;\n res[10] = this.borrowInterestRate.selector;\n res[11] = this.nextBorrowInterestRate.selector;\n res[12] = this.supplyInterestRate.selector;\n res[13] = this.nextSupplyInterestRate.selector;\n res[14] = this.totalSupplyInterestRate.selector;\n res[15] = this.totalAssetBorrow.selector;\n res[16] = this.totalAssetSupply.selector;\n res[17] = this.getMaxEscrowAmount.selector;\n res[18] = this.assetBalanceOf.selector;\n res[19] = this.getEstimatedMarginDetails.selector;\n res[20] = this.getDepositAmountForBorrow.selector;\n res[21] = this.getBorrowAmountForDeposit.selector;\n res[22] = this.checkPriceDivergence.selector;\n res[23] = this.calculateSupplyInterestRate.selector;\n\n // Advanced Token\n res[24] = this.approve.selector;\n\n // Advanced Token Storage\n // res[31] = this.totalSupply.selector;\n res[25] = this.balanceOf.selector;\n res[26] = this.allowance.selector;\n\n return (res, stringToBytes32(\"LoanTokenLogic\"));\n }\n}\n\ncontract LoanTokenLogicV2Mockup is LoanTokenLogicStandard {\n function testNewFunction() external pure returns (bool) {\n return true;\n }\n\n function getListFunctionSignatures()\n external\n pure\n returns (bytes4[] memory functionSignatures, bytes32 moduleName)\n {\n bytes4[] memory res = new bytes4[](29);\n\n // Loan Token Logic Standard\n res[0] = this.borrow.selector;\n res[1] = this.marginTrade.selector;\n res[2] = this.marginTradeAffiliate.selector;\n res[3] = this.transfer.selector;\n res[4] = this.transferFrom.selector;\n res[5] = this.profitOf.selector;\n res[6] = this.tokenPrice.selector;\n res[7] = this.checkpointPrice.selector;\n res[8] = this.marketLiquidity.selector;\n res[9] = this.avgBorrowInterestRate.selector;\n res[10] = this.borrowInterestRate.selector;\n res[11] = this.nextBorrowInterestRate.selector;\n res[12] = this.supplyInterestRate.selector;\n res[13] = this.nextSupplyInterestRate.selector;\n res[14] = this.totalSupplyInterestRate.selector;\n res[15] = this.totalAssetBorrow.selector;\n res[16] = this.totalAssetSupply.selector;\n res[17] = this.getMaxEscrowAmount.selector;\n res[18] = this.assetBalanceOf.selector;\n res[19] = this.getEstimatedMarginDetails.selector;\n res[20] = this.getDepositAmountForBorrow.selector;\n res[21] = this.getBorrowAmountForDeposit.selector;\n res[22] = this.checkPriceDivergence.selector;\n res[23] = this.calculateSupplyInterestRate.selector;\n\n // Advanced Token\n res[24] = this.approve.selector;\n\n // Advanced Token Storage\n res[25] = this.totalSupply.selector;\n res[26] = this.balanceOf.selector;\n res[27] = this.allowance.selector;\n\n // Mockup\n res[28] = this.testNewFunction.selector;\n\n return (res, stringToBytes32(\"LoanTokenLogic\"));\n }\n}\n" + }, + "contracts/mockup/lockedSOVFailedMockup.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"../interfaces/IERC20.sol\";\n\n/**\n * @title An interface for the Locked SOV Contract.\n * @author Franklin Richards - powerhousefrank@protonmail.com\n * @dev This is not a complete interface of the Locked SOV Contract.\n */\ncontract LockedSOVFailedMockup {\n using SafeMath for uint256;\n\n /* Storage */\n\n /// @notice The SOV token contract.\n IERC20 public SOV;\n\n /// @notice The user balances.\n mapping(address => uint256) lockedBalances;\n /// @notice The user balances.\n mapping(address => bool) isAdmin;\n\n /* Events */\n\n /// @notice Emitted when a new Admin is added to the admin list.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _newAdmin The address of the new admin.\n event AdminAdded(address indexed _initiator, address indexed _newAdmin);\n\n /// @notice Emitted when an admin is removed from the admin list.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _removedAdmin The address of the removed admin.\n event AdminRemoved(address indexed _initiator, address indexed _removedAdmin);\n\n /* Modifiers */\n\n modifier onlyAdmin {\n require(isAdmin[msg.sender], \"Only admin can call this.\");\n _;\n }\n\n /* Functions */\n\n /**\n * @notice Setup the required parameters.\n * @param _SOV The SOV token address.\n * @param _admins The list of admins to be added.\n */\n constructor(address _SOV, address[] memory _admins) public {\n require(_SOV != address(0), \"Invalid SOV Address.\");\n SOV = IERC20(_SOV);\n for (uint256 index = 0; index < _admins.length; index++) {\n isAdmin[_admins[index]] = true;\n }\n }\n\n /**\n * @notice The function to add a new admin.\n * @param _newAdmin The address of the new admin.\n */\n function addAdmin(address _newAdmin) public onlyAdmin {\n require(_newAdmin != address(0), \"Invalid Address\");\n require(!isAdmin[_newAdmin], \"Address is already admin\");\n isAdmin[_newAdmin] = true;\n\n emit AdminAdded(msg.sender, _newAdmin);\n }\n\n /**\n * @notice The function to remove an admin.\n * @param _adminToRemove The address of the admin which should be removed.\n */\n function removeAdmin(address _adminToRemove) public onlyAdmin {\n require(isAdmin[_adminToRemove], \"Address is not an admin\");\n isAdmin[_adminToRemove] = false;\n\n emit AdminRemoved(msg.sender, _adminToRemove);\n }\n\n /**\n * @notice Adds SOV to the locked balance of a user.\n * @param _userAddress The user whose locked balance has to be updated with _sovAmount.\n * @param _sovAmount The amount of SOV to be added to the locked balance.\n */\n function depositSOV(address _userAddress, uint256 _sovAmount) external {\n revert(\"For testing purposes\");\n bool txStatus = SOV.transferFrom(msg.sender, address(this), _sovAmount);\n require(txStatus, \"Token transfer was not successful. Check receiver address.\");\n\n lockedBalances[_userAddress] = lockedBalances[_userAddress].add(_sovAmount);\n }\n\n /**\n * @notice The function to get the locked balance of a user.\n * @param _addr The address of the user to check the locked balance.\n * @return _balance The locked balance of the address `_addr`.\n */\n function getLockedBalance(address _addr) public view returns (uint256 _balance) {\n return lockedBalances[_addr];\n }\n}\n" + }, + "contracts/mockup/LockedSOVMockup.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"../interfaces/IERC20.sol\";\n\n/**\n * @title An mockup for the Locked SOV Contract.\n * @author Franklin Richards - powerhousefrank@protonmail.com\n * @dev This is not a complete mockup of the Locked SOV Contract.\n */\ncontract LockedSOVMockup {\n using SafeMath for uint256;\n\n /* Storage */\n\n /// @notice The SOV token contract.\n IERC20 public SOV;\n\n /// @notice The locked user balances.\n mapping(address => uint256) lockedBalances;\n /// @notice The unlocked user balances.\n mapping(address => uint256) unlockedBalances;\n /// @notice The contracts/wallets with admin power.\n mapping(address => bool) isAdmin;\n\n /* Events */\n\n /// @notice Emitted when a new Admin is added to the admin list.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _newAdmin The address of the new admin.\n event AdminAdded(address indexed _initiator, address indexed _newAdmin);\n\n /// @notice Emitted when an admin is removed from the admin list.\n /// @param _initiator The address which initiated this event to be emitted.\n /// @param _removedAdmin The address of the removed admin.\n event AdminRemoved(address indexed _initiator, address indexed _removedAdmin);\n\n event Deposited(\n address indexed _initiator,\n address indexed _userAddress,\n uint256 _sovAmount,\n uint256 _basisPoint\n );\n\n event Withdrawn(address indexed _initiator, address indexed _userAddress, uint256 _sovAmount);\n\n event TokensStaked(address indexed _initiator, address indexed _vesting, uint256 _amount);\n\n /* Modifiers */\n\n modifier onlyAdmin {\n require(isAdmin[msg.sender], \"Only admin can call this.\");\n _;\n }\n\n /* Functions */\n\n /**\n * @notice Setup the required parameters.\n * @param _SOV The SOV token address.\n * @param _admins The list of admins to be added.\n */\n constructor(address _SOV, address[] memory _admins) public {\n require(_SOV != address(0), \"Invalid SOV Address.\");\n SOV = IERC20(_SOV);\n for (uint256 index = 0; index < _admins.length; index++) {\n isAdmin[_admins[index]] = true;\n }\n }\n\n /**\n * @notice The function to add a new admin.\n * @param _newAdmin The address of the new admin.\n */\n function addAdmin(address _newAdmin) public onlyAdmin {\n require(_newAdmin != address(0), \"Invalid Address\");\n require(!isAdmin[_newAdmin], \"Address is already admin\");\n isAdmin[_newAdmin] = true;\n\n emit AdminAdded(msg.sender, _newAdmin);\n }\n\n /**\n * @notice The function to remove an admin.\n * @param _adminToRemove The address of the admin which should be removed.\n */\n function removeAdmin(address _adminToRemove) public onlyAdmin {\n require(isAdmin[_adminToRemove], \"Address is not an admin\");\n isAdmin[_adminToRemove] = false;\n\n emit AdminRemoved(msg.sender, _adminToRemove);\n }\n\n /**\n * @notice Adds SOV to the user balance (Locked and Unlocked Balance based on `_basisPoint`).\n * @param _userAddress The user whose locked balance has to be updated with `_sovAmount`.\n * @param _sovAmount The amount of SOV to be added to the locked and/or unlocked balance.\n * @param _basisPoint The % (in Basis Point)which determines how much will be unlocked immediately.\n */\n function deposit(\n address _userAddress,\n uint256 _sovAmount,\n uint256 _basisPoint\n ) external {\n _deposit(_userAddress, _sovAmount, _basisPoint);\n }\n\n /**\n * @notice Adds SOV to the locked balance of a user.\n * @param _userAddress The user whose locked balance has to be updated with _sovAmount.\n * @param _sovAmount The amount of SOV to be added to the locked balance.\n * @dev This is here because there are dependency with other contracts.\n */\n function depositSOV(address _userAddress, uint256 _sovAmount) external {\n _deposit(_userAddress, _sovAmount, 0);\n }\n\n function _deposit(\n address _userAddress,\n uint256 _sovAmount,\n uint256 _basisPoint\n ) private {\n // 10000 is not included because if 100% is unlocked, then LockedSOV is not required to be used.\n require(_basisPoint < 10000, \"Basis Point has to be less than 10000.\");\n bool txStatus = SOV.transferFrom(msg.sender, address(this), _sovAmount);\n require(txStatus, \"Token transfer was not successful. Check receiver address.\");\n\n uint256 unlockedBal = _sovAmount.mul(_basisPoint).div(10000);\n\n unlockedBalances[_userAddress] = unlockedBalances[_userAddress].add(unlockedBal);\n lockedBalances[_userAddress] = lockedBalances[_userAddress].add(_sovAmount).sub(\n unlockedBal\n );\n\n emit Deposited(msg.sender, _userAddress, _sovAmount, _basisPoint);\n }\n\n /**\n * @notice Withdraws unlocked tokens and Stakes Locked tokens for a user who already have a vesting created.\n * @param _userAddress The address of user tokens will be withdrawn.\n */\n function withdrawAndStakeTokensFrom(address _userAddress) external {\n _withdraw(_userAddress, _userAddress);\n _createVestingAndStake(_userAddress);\n }\n\n function _withdraw(address _sender, address _receiverAddress) private {\n address userAddr = _receiverAddress;\n if (_receiverAddress == address(0)) {\n userAddr = _sender;\n }\n\n uint256 amount = unlockedBalances[_sender];\n unlockedBalances[_sender] = 0;\n\n bool txStatus = SOV.transfer(userAddr, amount);\n require(txStatus, \"Token transfer was not successful. Check receiver address.\");\n\n emit Withdrawn(_sender, userAddr, amount);\n }\n\n function _createVestingAndStake(address _sender) private {\n uint256 amount = lockedBalances[_sender];\n lockedBalances[_sender] = 0;\n\n emit TokensStaked(_sender, address(0), amount);\n }\n\n /**\n * @notice The function to get the locked balance of a user.\n * @param _addr The address of the user to check the locked balance.\n * @return _balance The locked balance of the address `_addr`.\n */\n function getLockedBalance(address _addr) public view returns (uint256 _balance) {\n return lockedBalances[_addr];\n }\n\n /**\n * @notice The function to get the unlocked balance of a user.\n * @param _addr The address of the user to check the unlocked balance.\n * @return _balance The unlocked balance of the address `_addr`.\n */\n function getUnlockedBalance(address _addr) external view returns (uint256 _balance) {\n return unlockedBalances[_addr];\n }\n}\n" + }, + "contracts/mockup/MockAffiliates.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../modules/Affiliates.sol\";\n\ncontract MockAffiliates is Affiliates {\n function getAffiliatesUserReferrer(address user) public view returns (address) {\n return affiliatesUserReferrer[user]; // REFACTOR: will be useful if affiliatesUserReferrer visibillity is not public\n }\n\n function initialize(address target) external onlyOwner {\n _setTarget(this.getAffiliatesUserReferrer.selector, target);\n }\n}\n" + }, + "contracts/mockup/MockFourYearVestingLogic.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../governance/Vesting/fouryear/FourYearVestingLogic.sol\";\n\ncontract MockFourYearVestingLogic is FourYearVestingLogic {\n /**\n * @notice gets duration left\n */\n function getDurationLeft() external view returns (uint256) {\n return durationLeft;\n }\n}\n" + }, + "contracts/mockup/MockLoanTokenLogic.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../modules/Affiliates.sol\";\nimport \"../connectors/loantoken/modules/beaconLogicLM/LoanTokenLogic.sol\";\nimport \"../modules/interfaces/ProtocolAffiliatesInterface.sol\";\nimport \"../interfaces/ILoanTokenModules.sol\";\n\ncontract MockLoanTokenLogic is LoanTokenLogic {\n /*function getAffiliatesUserReferrer(address user) public view returns (address) {\n\t\treturn affiliatesUserReferrer[user]; // REFACTOR: will be useful if affiliatesUserReferrer visibillity is not public\n\t}*/\n\n function getListFunctionSignatures()\n external\n pure\n returns (bytes4[] memory functionSignatures, bytes32 moduleName)\n {\n bytes4[] memory res = new bytes4[](31);\n\n // Loan Token Logic\n res[0] = this.borrow.selector;\n res[1] = this.marginTrade.selector;\n res[2] = this.marginTradeAffiliate.selector;\n res[3] = this.transfer.selector;\n res[4] = this.transferFrom.selector;\n res[5] = this.profitOf.selector;\n res[6] = this.tokenPrice.selector;\n res[7] = this.checkpointPrice.selector;\n res[8] = this.marketLiquidity.selector;\n res[9] = this.avgBorrowInterestRate.selector;\n res[10] = this.borrowInterestRate.selector;\n res[11] = this.nextBorrowInterestRate.selector;\n res[12] = this.supplyInterestRate.selector;\n res[13] = this.nextSupplyInterestRate.selector;\n res[14] = this.totalSupplyInterestRate.selector;\n res[15] = this.totalAssetBorrow.selector;\n res[16] = this.totalAssetSupply.selector;\n res[17] = this.getMaxEscrowAmount.selector;\n res[18] = this.assetBalanceOf.selector;\n res[19] = this.getEstimatedMarginDetails.selector;\n res[20] = this.getDepositAmountForBorrow.selector;\n res[21] = this.getBorrowAmountForDeposit.selector;\n res[22] = this.checkPriceDivergence.selector;\n res[23] = this.calculateSupplyInterestRate.selector;\n\n // Advanced Token\n res[24] = this.approve.selector;\n\n // Advanced Token Storage\n res[25] = this.totalSupply.selector;\n res[26] = this.balanceOf.selector;\n res[27] = this.allowance.selector;\n\n // Mock\n res[28] = this.setAffiliatesReferrer.selector;\n res[29] = this.setUserNotFirstTradeFlag.selector;\n res[30] = this.getMarginBorrowAmountAndRate.selector;\n\n return (res, stringToBytes32(\"MockLoanTokenLogic\"));\n }\n\n function setAffiliatesReferrer(address user, address referrer) public {\n ProtocolAffiliatesInterface(sovrynContractAddress).setAffiliatesReferrer(user, referrer);\n }\n\n function setUserNotFirstTradeFlag(address user) public {\n ProtocolAffiliatesInterface(sovrynContractAddress).setUserNotFirstTradeFlag(user);\n }\n\n function getMarginBorrowAmountAndRate(uint256 leverageAmount, uint256 depositAmount)\n public\n view\n returns (uint256, uint256)\n {\n return _getMarginBorrowAmountAndRate(leverageAmount, depositAmount);\n }\n\n /*function initialize(address target) external onlyOwner {\n\t\t_setTarget(this.setAffiliatesUserReferrer.selector, target);\n\t}*/\n}\n\ncontract ILoanTokenModulesMock is ILoanTokenModules {\n function setAffiliatesReferrer(address user, address referrer) external;\n\n function setUserNotFirstTradeFlag(address user) external;\n}\n" + }, + "contracts/mockup/MockLoanTokenLogicLM.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../modules/Affiliates.sol\";\nimport \"../connectors/loantoken/modules/beaconLogicLM/LoanTokenLogicLM.sol\";\nimport \"../modules/interfaces/ProtocolAffiliatesInterface.sol\";\nimport \"../interfaces/ILoanTokenModules.sol\";\n\ncontract MockLoanTokenLogicLM is LoanTokenLogicLM {\n /*function getAffiliatesUserReferrer(address user) public view returns (address) {\n\t\treturn affiliatesUserReferrer[user]; // REFACTOR: will be useful if affiliatesUserReferrer visibillity is not public\n\t}*/\n\n function getListFunctionSignatures()\n external\n pure\n returns (bytes4[] memory functionSignatures, bytes32 moduleName)\n {\n bytes4[] memory res = new bytes4[](4);\n\n /** LoanTokenLogicLM function signature */\n res[0] = bytes4(keccak256(\"mint(address,uint256)\"));\n res[1] = bytes4(keccak256(\"mint(address,uint256,bool)\"));\n res[2] = bytes4(keccak256(\"burn(address,uint256)\"));\n res[3] = bytes4(keccak256(\"burn(address,uint256,bool)\"));\n\n return (res, stringToBytes32(\"MockLoanTokenLogicLM\"));\n }\n}\n" + }, + "contracts/mockup/modules/IWeightedStakingModuleMockup.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../governance/Staking/modules/WeightedStakingModule.sol\";\n\ncontract IWeightedStakingModuleMockup {\n function MOCK_priorWeightedStake(uint96 _priorWeightedStake) external;\n\n function MOCK_priorWeightedStakeAtBlock(uint96 _priorWeightedStake, uint256 _block) external;\n\n function getPriorWeightedStake(\n address account,\n uint256 blockNumber,\n uint256 date\n ) external view returns (uint96);\n\n function calculatePriorWeightedStake(\n address account,\n uint256 blockNumber,\n uint256 date\n ) external;\n\n /**\n * @dev We need this function to simulate zero delegate checkpoint value.\n */\n function setDelegateStake(\n address delegatee,\n uint256 lockedTS,\n uint96 value\n ) external;\n\n /**\n * @notice Compute the voting power for a specific date.\n * Power = stake * weight\n * be internal instead of a public function.\n * @param account The user address.\n * @param date The staking date to compute the power for.\n * @param startDate The date for which we need to know the power of the stake.\n * @param blockNumber The block number, needed for checkpointing.\n * @return The stacking power.\n * */\n function weightedStakeByDate(\n address account,\n uint256 date,\n uint256 startDate,\n uint256 blockNumber\n ) external view returns (uint96 power);\n\n /**\n * @notice Compute the weight for a specific date.\n * @param date The unlocking date.\n * @param startDate We compute the weight for the tokens staked until 'date' on 'startDate'.\n * @return The weighted stake the account had as of the given block.\n * */\n function computeWeightByDate(uint256 date, uint256 startDate)\n external\n pure\n returns (uint96 weight);\n\n /**\n * @notice Receives approval from SOV token.\n * @param _data The data will be used for low level call.\n */\n function receiveApproval(\n address _sender,\n uint256 _amount,\n address _token,\n bytes calldata _data\n ) external;\n}\n" + }, + "contracts/mockup/modules/StakingModuleBlockMockup.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../governance/Staking/modules/StakingGovernanceModule.sol\";\nimport \"../../governance/Staking/modules/StakingStakeModule.sol\";\nimport \"../../governance/Staking/modules/StakingVestingModule.sol\";\nimport \"../../governance/Staking/modules/WeightedStakingModule.sol\";\nimport \"../../proxy/modules/interfaces/IFunctionsList.sol\";\nimport \"../BlockMockUp.sol\";\n\ncontract StakingModuleBlockMockup is\n IFunctionsList,\n StakingGovernanceModule,\n StakingStakeModule,\n StakingVestingModule,\n WeightedStakingModule\n{\n uint96 public priorWeightedStake;\n mapping(uint256 => uint96) public priorWeightedStakeAtBlock;\n ///@notice the block mock up contract\n BlockMockUp public blockMockUp;\n\n function balanceOf_MultipliedByTwo(address account) external view returns (uint256) {\n return this.balanceOf(account) * 2;\n }\n\n uint96 priorTotalVotingPower;\n\n function MOCK_priorTotalVotingPower(uint96 _priorTotalVotingPower) public {\n priorTotalVotingPower = _priorTotalVotingPower;\n }\n\n function getPriorTotalVotingPower(uint32 blockNumber, uint256 time)\n public\n view\n returns (uint96 totalVotingPower)\n {\n return\n priorTotalVotingPower != 0\n ? priorTotalVotingPower\n : super.getPriorTotalVotingPower(blockNumber, time);\n }\n\n function MOCK_priorWeightedStake(uint96 _priorWeightedStake) public {\n priorWeightedStake = _priorWeightedStake;\n }\n\n function MOCK_priorWeightedStakeAtBlock(uint96 _priorWeightedStake, uint256 _block) public {\n priorWeightedStakeAtBlock[_block] = _priorWeightedStake;\n }\n\n function getPriorWeightedStake(\n address account,\n uint256 blockNumber,\n uint256 date\n ) public view returns (uint96) {\n uint96 _priorWeightedStake;\n\n if (priorWeightedStakeAtBlock[blockNumber] != 0) {\n _priorWeightedStake = priorWeightedStakeAtBlock[blockNumber];\n } else {\n _priorWeightedStake = priorWeightedStake != 0\n ? priorWeightedStake\n : _getPriorWeightedStake(account, blockNumber, date);\n }\n\n return _priorWeightedStake;\n }\n\n function calculatePriorWeightedStake(\n address account,\n uint256 blockNumber,\n uint256 date\n ) public {\n getPriorWeightedStake(account, blockNumber, date);\n }\n\n /**\n * @dev We need this function to simulate zero delegate checkpoint value.\n */\n function setDelegateStake(\n address delegatee,\n uint256 lockedTS,\n uint96 value\n ) public {\n uint32 nCheckpoints = numDelegateStakingCheckpoints[delegatee][lockedTS];\n uint96 staked = delegateStakingCheckpoints[delegatee][lockedTS][nCheckpoints - 1].stake;\n _writeDelegateCheckpoint(delegatee, lockedTS, nCheckpoints, 0);\n }\n\n /**\n * @notice Add vesting contract's code hash to a map of code hashes.\n * @param vesting The address of Vesting contract.\n * @dev We need it to use _isVestingContract() function instead of isContract()\n */\n function addContractCodeHash(address vesting) public onlyAuthorized {\n bytes32 codeHash = _getCodeHash(vesting);\n vestingCodeHashes[codeHash] = true;\n emit ContractCodeHashAdded(codeHash);\n }\n\n /**\n * @notice Remove vesting contract's code hash to a map of code hashes.\n * @param vesting The address of Vesting contract.\n * @dev We need it to use _isVestingContract() function instead of isContract()\n */\n function removeContractCodeHash(address vesting) public onlyAuthorized {\n bytes32 codeHash = _getCodeHash(vesting);\n vestingCodeHashes[codeHash] = false;\n emit ContractCodeHashRemoved(codeHash);\n }\n\n /**\n * @notice Return hash of contract code\n */\n function _getCodeHash(address _contract) internal view returns (bytes32) {\n bytes32 codeHash;\n assembly {\n codeHash := extcodehash(_contract)\n }\n return codeHash;\n }\n\n /**\n * @notice Return flag whether the given address is a registered vesting contract.\n * @param stakerAddress the address to check\n */\n function isVestingContract(address stakerAddress) public view returns (bool) {\n bytes32 codeHash = _getCodeHash(stakerAddress);\n return vestingCodeHashes[codeHash];\n }\n\n function getPriorWeightedStakeAtBlock(uint256 blockNum) public view returns (uint256) {\n return uint256(priorWeightedStakeAtBlock[blockNum]);\n }\n\n /**\n * @notice gets block number from BlockMockUp\n * @param _blockMockUp the address of BlockMockUp\n */\n function setBlockMockUpAddr(address _blockMockUp) public onlyOwner {\n require(_blockMockUp != address(0), \"block mockup address invalid\");\n blockMockUp = BlockMockUp(_blockMockUp);\n }\n\n /**\n * @notice Determine the current Block Number from BlockMockUp\n * */\n function _getCurrentBlockNumber() internal view returns (uint256) {\n return blockMockUp.getBlockNum();\n }\n\n function getFunctionsList() external pure returns (bytes4[] memory) {\n // StakingGovernanceModule\n bytes4[] memory functionsList = new bytes4[](31);\n functionsList[0] = this.getPriorTotalVotingPower.selector;\n functionsList[1] = this.getCurrentVotes.selector;\n functionsList[2] = this.getPriorVotes.selector;\n functionsList[3] = this.getPriorStakeByDateForDelegatee.selector;\n functionsList[4] = this.getPriorTotalStakesForDate.selector;\n functionsList[5] = this.delegate.selector;\n\n // StakingStakeModule\n functionsList[6] = this.stake.selector;\n functionsList[7] = this.stakeWithApproval.selector;\n functionsList[8] = this.extendStakingDuration.selector;\n functionsList[9] = this.stakesBySchedule.selector;\n functionsList[10] = this.stakeBySchedule.selector;\n functionsList[11] = this.balanceOf.selector;\n functionsList[12] = this.getCurrentStakedUntil.selector;\n functionsList[13] = this.getStakes.selector;\n functionsList[14] = this.timestampToLockDate.selector;\n\n //StakingVestingModule\n functionsList[15] = this.setVestingRegistry.selector;\n functionsList[16] = this.setVestingStakes.selector;\n functionsList[17] = this.getPriorUserStakeByDate.selector;\n functionsList[18] = this.getPriorVestingWeightedStake.selector;\n functionsList[19] = this.getPriorVestingStakeByDate.selector;\n functionsList[20] = this.addContractCodeHash.selector;\n functionsList[21] = this.removeContractCodeHash.selector;\n functionsList[22] = this.isVestingContract.selector;\n\n //BlockMockup\n functionsList[23] = this.setBlockMockUpAddr.selector;\n functionsList[24] = this.MOCK_priorWeightedStake.selector;\n functionsList[25] = this.MOCK_priorWeightedStakeAtBlock.selector;\n\n //WeightedStakingModule\n functionsList[26] = this.getPriorWeightedStake.selector;\n functionsList[27] = this.weightedStakeByDate.selector;\n functionsList[28] = this.computeWeightByDate.selector;\n functionsList[29] = this.priorWeightedStakeAtBlock.selector;\n functionsList[30] = this.getPriorWeightedStakeAtBlock.selector;\n\n return functionsList;\n }\n}\n" + }, + "contracts/mockup/modules/StakingSharedModuleMock.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../governance/Staking/modules/shared/StakingShared.sol\";\nimport \"../BlockMockUp.sol\";\nimport \"../../proxy/modules/interfaces/IFunctionsList.sol\";\n\ncontract StakingModuleMock is IFunctionsList, StakingShared {\n ///@notice the block mock up contract\n BlockMockUp public blockMockUp;\n\n /**\n * @notice gets block number from BlockMockUp\n * @param _blockMockUp the address of BlockMockUp\n */\n function setBlockMockUpAddr(address _blockMockUp) public onlyOwner {\n require(_blockMockUp != address(0), \"block mockup address invalid\");\n blockMockUp = BlockMockUp(_blockMockUp);\n }\n\n /**\n * @notice Determine the current Block Number from BlockMockUp\n * */\n function _getCurrentBlockNumber() internal view returns (uint256) {\n return blockMockUp.getBlockNum();\n }\n\n function getFunctionsList() external pure returns (bytes4[] memory) {\n bytes4[] memory functionList = new bytes4[](1);\n functionList[0] = this.setBlockMockUpAddr.selector;\n }\n}\n" + }, + "contracts/mockup/modules/StakingWrapperMockup.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../governance/Staking/interfaces/IStaking.sol\";\nimport \"../../interfaces/IERC20.sol\";\n\ncontract StakingWrapperMockup {\n uint256 constant TWO_WEEKS = 1209600;\n\n IStaking staking;\n IERC20 token;\n\n constructor(IStaking _staking, IERC20 _token) public {\n staking = _staking;\n token = _token;\n }\n\n function stake2times(\n uint96 amount,\n uint256 until,\n address stakeFor,\n address delegatee\n ) external {\n require(token.transferFrom(msg.sender, address(this), amount * 2));\n token.approve(address(staking), amount * 2);\n\n staking.stake(amount, until, stakeFor, delegatee);\n staking.stake(amount, until, stakeFor, delegatee);\n }\n\n function stakeAndExtend(uint96 amount, uint256 until) external {\n require(token.transferFrom(msg.sender, address(this), amount));\n token.approve(address(staking), amount);\n\n staking.stake(amount, until, address(this), address(this));\n staking.extendStakingDuration(until, until + TWO_WEEKS);\n }\n\n function stakeAndStakeBySchedule(\n uint96 amount,\n uint256 until,\n uint256 cliff,\n uint256 duration,\n uint256 intervalLength,\n address stakeFor,\n address delegatee\n ) external {\n require(token.transferFrom(msg.sender, address(this), amount * 2));\n token.approve(address(staking), amount * 2);\n\n staking.stake(amount, until, stakeFor, delegatee);\n staking.stakeBySchedule(amount, cliff, duration, intervalLength, stakeFor, delegatee);\n }\n}\n" + }, + "contracts/mockup/modules/WeightedStakingModuleMockup.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../governance/Staking/modules/WeightedStakingModule.sol\";\n\ncontract WeightedStakingModuleMockup is WeightedStakingModule {\n uint96 priorWeightedStake;\n\n function MOCK_priorWeightedStake(uint96 _priorWeightedStake) public {\n priorWeightedStake = _priorWeightedStake;\n }\n\n mapping(uint256 => uint96) priorWeightedStakeAtBlock;\n\n function MOCK_priorWeightedStakeAtBlock(uint96 _priorWeightedStake, uint256 _block) public {\n priorWeightedStakeAtBlock[_block] = _priorWeightedStake;\n }\n\n function getPriorWeightedStake(\n address account,\n uint256 blockNumber,\n uint256 date\n ) public view returns (uint96) {\n uint96 _priorWeightedStake;\n\n if (priorWeightedStakeAtBlock[blockNumber] != 0) {\n _priorWeightedStake = priorWeightedStakeAtBlock[blockNumber];\n } else {\n _priorWeightedStake = priorWeightedStake != 0\n ? priorWeightedStake\n : _getPriorWeightedStake(account, blockNumber, date);\n }\n\n return _priorWeightedStake;\n }\n\n function calculatePriorWeightedStake(\n address account,\n uint256 blockNumber,\n uint256 date\n ) public {\n getPriorWeightedStake(account, blockNumber, date);\n }\n\n /**\n * @dev We need this function to simulate zero delegate checkpoint value.\n */\n function setDelegateStake(\n address delegatee,\n uint256 lockedTS,\n uint96 value\n ) public {\n uint32 nCheckpoints = numDelegateStakingCheckpoints[delegatee][lockedTS];\n uint96 staked = delegateStakingCheckpoints[delegatee][lockedTS][nCheckpoints - 1].stake;\n _writeDelegateCheckpoint(delegatee, lockedTS, nCheckpoints, 0);\n }\n\n function getFunctionsList() external pure returns (bytes4[] memory) {\n bytes4[] memory functionsList = new bytes4[](7);\n functionsList[0] = this.getPriorWeightedStake.selector;\n functionsList[1] = this.weightedStakeByDate.selector;\n functionsList[2] = this.computeWeightByDate.selector;\n functionsList[3] = this.MOCK_priorWeightedStake.selector;\n functionsList[4] = this.MOCK_priorWeightedStakeAtBlock.selector;\n functionsList[5] = this.calculatePriorWeightedStake.selector;\n functionsList[6] = this.setDelegateStake.selector;\n return functionsList;\n }\n}\n" + }, + "contracts/mockup/previousLoanToken/PreviousLoanToken.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../../connectors/loantoken/AdvancedTokenStorage.sol\";\n\n//@todo can I change this proxy to EIP-1822 proxy standard, please. https://eips.ethereum.org/EIPS/eip-1822. It's really hard to work with this.\ncontract PreviousLoanToken is AdvancedTokenStorage {\n // It is important to maintain the variables order so the delegate calls can access sovrynContractAddress and wrbtcTokenAddress\n address public sovrynContractAddress;\n address public wrbtcTokenAddress;\n address internal target_;\n\n constructor(\n address _newOwner,\n address _newTarget,\n address _sovrynContractAddress,\n address _wrbtcTokenAddress\n ) public {\n transferOwnership(_newOwner);\n _setTarget(_newTarget);\n _setSovrynContractAddress(_sovrynContractAddress);\n _setWrbtcTokenAddress(_wrbtcTokenAddress);\n }\n\n function() external payable {\n if (gasleft() <= 2300) {\n return;\n }\n\n address target = target_;\n bytes memory data = msg.data;\n assembly {\n let result := delegatecall(gas, target, add(data, 0x20), mload(data), 0, 0)\n let size := returndatasize\n let ptr := mload(0x40)\n returndatacopy(ptr, 0, size)\n switch result\n case 0 {\n revert(ptr, size)\n }\n default {\n return(ptr, size)\n }\n }\n }\n\n function setTarget(address _newTarget) public onlyOwner {\n _setTarget(_newTarget);\n }\n\n function _setTarget(address _newTarget) internal {\n require(Address.isContract(_newTarget), \"target not a contract\");\n target_ = _newTarget;\n }\n\n function _setSovrynContractAddress(address _sovrynContractAddress) internal {\n require(Address.isContract(_sovrynContractAddress), \"sovryn not a contract\");\n sovrynContractAddress = _sovrynContractAddress;\n }\n\n function _setWrbtcTokenAddress(address _wrbtcTokenAddress) internal {\n require(Address.isContract(_wrbtcTokenAddress), \"wrbtc not a contract\");\n wrbtcTokenAddress = _wrbtcTokenAddress;\n }\n\n //@todo add check for double init, idk but init usually can be called only once.\n function initialize(\n address _loanTokenAddress,\n string memory _name,\n string memory _symbol\n ) public onlyOwner {\n loanTokenAddress = _loanTokenAddress;\n\n name = _name;\n symbol = _symbol;\n decimals = IERC20(loanTokenAddress).decimals();\n\n initialPrice = 10**18; // starting price of 1\n }\n}\n" + }, + "contracts/mockup/previousLoanToken/PreviousLoanTokenSettingsLowerAdmin.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../connectors/loantoken/interfaces/ProtocolSettingsLike.sol\";\nimport \"../../connectors/loantoken/AdvancedTokenStorage.sol\";\n\n// It is a LoanToken implementation!\ncontract PreviousLoanTokenSettingsLowerAdmin is AdvancedTokenStorage {\n using SafeMath for uint256;\n\n // It is important to maintain the variables order so the delegate calls can access sovrynContractAddress\n\n // ------------- MUST BE THE SAME AS IN LoanToken CONTRACT -------------------\n address public sovrynContractAddress;\n address public wrbtcTokenAddress;\n address internal target_;\n // ------------- END MUST BE THE SAME AS IN LoanToken CONTRACT -------------------\n\n event SetTransactionLimits(address[] addresses, uint256[] limits);\n\n //@todo check for restrictions in this contract\n modifier onlyAdmin() {\n require(msg.sender == address(this) || msg.sender == owner(), \"unauthorized\");\n _;\n }\n\n //@todo add check for double init, idk but init usually can be called only once.\n function init(\n address _loanTokenAddress,\n string memory _name,\n string memory _symbol\n ) public onlyOwner {\n loanTokenAddress = _loanTokenAddress;\n\n name = _name;\n symbol = _symbol;\n decimals = IERC20(loanTokenAddress).decimals();\n\n initialPrice = 10**18; // starting price of 1\n }\n\n function() external {\n revert(\"LoanTokenSettingsLowerAdmin - fallback not allowed\");\n }\n\n function setupLoanParams(\n LoanParamsStruct.LoanParams[] memory loanParamsList,\n bool areTorqueLoans\n ) public onlyAdmin {\n bytes32[] memory loanParamsIdList;\n address _loanTokenAddress = loanTokenAddress;\n\n for (uint256 i = 0; i < loanParamsList.length; i++) {\n loanParamsList[i].loanToken = _loanTokenAddress;\n loanParamsList[i].maxLoanTerm = areTorqueLoans ? 0 : 28 days;\n }\n\n loanParamsIdList = ProtocolSettingsLike(sovrynContractAddress).setupLoanParams(\n loanParamsList\n );\n for (uint256 i = 0; i < loanParamsIdList.length; i++) {\n loanParamsIds[\n uint256(\n keccak256(\n abi.encodePacked(\n loanParamsList[i].collateralToken,\n areTorqueLoans // isTorqueLoan\n )\n )\n )\n ] = loanParamsIdList[i];\n }\n }\n\n function disableLoanParams(address[] calldata collateralTokens, bool[] calldata isTorqueLoans)\n external\n onlyAdmin\n {\n require(collateralTokens.length == isTorqueLoans.length, \"count mismatch\");\n\n bytes32[] memory loanParamsIdList = new bytes32[](collateralTokens.length);\n for (uint256 i = 0; i < collateralTokens.length; i++) {\n uint256 id =\n uint256(keccak256(abi.encodePacked(collateralTokens[i], isTorqueLoans[i])));\n loanParamsIdList[i] = loanParamsIds[id];\n delete loanParamsIds[id];\n }\n\n ProtocolSettingsLike(sovrynContractAddress).disableLoanParams(loanParamsIdList);\n }\n\n // These params should be percentages represented like so: 5% = 5000000000000000000\n // rateMultiplier + baseRate can't exceed 100%\n function setDemandCurve(\n uint256 _baseRate,\n uint256 _rateMultiplier,\n uint256 _lowUtilBaseRate,\n uint256 _lowUtilRateMultiplier,\n uint256 _targetLevel,\n uint256 _kinkLevel,\n uint256 _maxScaleRate\n ) public onlyAdmin {\n require(_rateMultiplier.add(_baseRate) <= WEI_PERCENT_PRECISION, \"curve params too high\");\n require(\n _lowUtilRateMultiplier.add(_lowUtilBaseRate) <= WEI_PERCENT_PRECISION,\n \"curve params too high\"\n );\n\n require(\n _targetLevel <= WEI_PERCENT_PRECISION && _kinkLevel <= WEI_PERCENT_PRECISION,\n \"levels too high\"\n );\n\n baseRate = _baseRate;\n rateMultiplier = _rateMultiplier;\n lowUtilBaseRate = _lowUtilBaseRate;\n lowUtilRateMultiplier = _lowUtilRateMultiplier;\n\n targetLevel = _targetLevel; // 80 ether\n kinkLevel = _kinkLevel; // 90 ether\n maxScaleRate = _maxScaleRate; // 100 ether\n }\n\n function toggleFunctionPause(\n string memory funcId, // example: \"mint(uint256,uint256)\"\n bool isPaused\n ) public onlyAdmin {\n // keccak256(\"iToken_FunctionPause\")\n bytes32 slot =\n keccak256(\n abi.encodePacked(\n bytes4(keccak256(abi.encodePacked(funcId))),\n uint256(0xd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f2)\n )\n );\n assembly {\n sstore(slot, isPaused)\n }\n }\n\n /**\n * sets the transaction limit per token address\n * @param addresses the token addresses\n * @param limits the limit denominated in the currency of the token address\n * */\n function setTransactionLimits(address[] memory addresses, uint256[] memory limits)\n public\n onlyOwner\n {\n require(addresses.length == limits.length, \"mismatched array lengths\");\n for (uint256 i = 0; i < addresses.length; i++) {\n transactionLimit[addresses[i]] = limits[i];\n }\n emit SetTransactionLimits(addresses, limits);\n }\n}\n" + }, + "contracts/mockup/PriceFeedsMoCMockup.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../feeds/testnet/PriceFeedsMoC.sol\";\n\n// This contract is only for test purposes\n// https://github.com/money-on-chain/Amphiraos-Oracle/blob/master/contracts/medianizer/medianizer.sol\ncontract PriceFeedsMoCMockup is Medianizer {\n uint256 public value;\n bool public has;\n\n function peek() external view returns (bytes32, bool) {\n return (bytes32(value), has);\n }\n\n function setValue(uint256 _value) public {\n value = _value;\n }\n\n function setHas(bool _has) public {\n has = _has;\n }\n}\n" + }, + "contracts/mockup/ProtocolSettingsMockup.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../modules/ProtocolSettings.sol\";\n\ncontract ProtocolSettingsMockup is ProtocolSettings {\n function setLendingFeeTokensHeld(address token, uint256 amout) public {\n lendingFeeTokensHeld[token] = amout;\n }\n\n function setTradingFeeTokensHeld(address token, uint256 amout) public {\n tradingFeeTokensHeld[token] = amout;\n }\n\n function setBorrowingFeeTokensHeld(address token, uint256 amout) public {\n borrowingFeeTokensHeld[token] = amout;\n }\n\n function initialize(address target) external onlyOwner {\n _setTarget(this.setPriceFeedContract.selector, target);\n _setTarget(this.setSwapsImplContract.selector, target);\n _setTarget(this.setLoanPool.selector, target);\n _setTarget(this.setSupportedTokens.selector, target);\n _setTarget(this.setLendingFeePercent.selector, target);\n _setTarget(this.setTradingFeePercent.selector, target);\n _setTarget(this.setBorrowingFeePercent.selector, target);\n _setTarget(this.setSwapExternalFeePercent.selector, target);\n _setTarget(this.setAffiliateFeePercent.selector, target);\n _setTarget(this.setAffiliateTradingTokenFeePercent.selector, target);\n _setTarget(this.setLiquidationIncentivePercent.selector, target);\n _setTarget(this.setMaxDisagreement.selector, target);\n _setTarget(this.setSourceBuffer.selector, target);\n _setTarget(this.setMaxSwapSize.selector, target);\n _setTarget(this.setFeesController.selector, target);\n _setTarget(this.withdrawFees.selector, target);\n _setTarget(this.withdrawLendingFees.selector, target);\n _setTarget(this.withdrawTradingFees.selector, target);\n _setTarget(this.withdrawBorrowingFees.selector, target);\n _setTarget(this.withdrawProtocolToken.selector, target);\n _setTarget(this.depositProtocolToken.selector, target);\n _setTarget(this.getLoanPoolsList.selector, target);\n _setTarget(this.isLoanPool.selector, target);\n _setTarget(this.setSovrynSwapContractRegistryAddress.selector, target);\n _setTarget(this.setWrbtcToken.selector, target);\n _setTarget(this.setSovrynProtocolAddress.selector, target);\n _setTarget(this.setProtocolTokenAddress.selector, target);\n _setTarget(this.setSOVTokenAddress.selector, target);\n _setTarget(this.setLockedSOVAddress.selector, target);\n _setTarget(this.setMinReferralsToPayoutAffiliates.selector, target);\n _setTarget(this.setRolloverBaseReward.selector, target);\n\n _setTarget(this.setLendingFeeTokensHeld.selector, target);\n _setTarget(this.setTradingFeeTokensHeld.selector, target);\n _setTarget(this.setBorrowingFeeTokensHeld.selector, target);\n _setTarget(this.getSpecialRebates.selector, target);\n\n _setTarget(this.getProtocolAddress.selector, target);\n _setTarget(this.getSovTokenAddress.selector, target);\n _setTarget(this.getLockedSOVAddress.selector, target);\n\n _setTarget(this.getFeeRebatePercent.selector, target);\n _setTarget(this.getSwapExternalFeePercent.selector, target);\n\n _setTarget(this.setTradingRebateRewardsBasisPoint.selector, target);\n _setTarget(this.getTradingRebateRewardsBasisPoint.selector, target);\n _setTarget(this.getDedicatedSOVRebate.selector, target);\n\n _setTarget(this.getDefaultPathConversion.selector, target);\n }\n}\n" + }, + "contracts/mockup/proxy/ImplementationMockup.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./StorageMockup.sol\";\n\ncontract ImplementationMockup is StorageMockup {\n function setValue(uint256 _value) public {\n value = _value;\n emit ValueChanged(_value);\n }\n\n function getValue() public view returns (uint256) {\n return value;\n }\n}\n" + }, + "contracts/mockup/proxy/ProxyMockup.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./StorageMockup.sol\";\nimport \"../../proxy/UpgradableProxy.sol\";\n\ncontract ProxyMockup is StorageMockup, UpgradableProxy {}\n" + }, + "contracts/mockup/proxy/StorageMockup.sol": { + "content": "pragma solidity ^0.5.17;\n\ncontract StorageMockup {\n uint256 value;\n\n event ValueChanged(uint256 value);\n}\n" + }, + "contracts/mockup/RBTCWrapperProxyMockup.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../farm/LiquidityMining.sol\";\n\ncontract RBTCWrapperProxyMockup {\n LiquidityMining public liquidityMining;\n\n constructor(LiquidityMining _liquidityMining) public {\n liquidityMining = _liquidityMining;\n }\n\n function claimReward(address _poolToken) public {\n liquidityMining.claimReward(_poolToken, msg.sender);\n }\n\n function claimRewardFromAllPools() public {\n liquidityMining.claimRewardFromAllPools(msg.sender);\n }\n\n function withdraw(address _poolToken, uint256 _amount) public {\n liquidityMining.withdraw(_poolToken, _amount, msg.sender);\n }\n}\n" + }, + "contracts/mockup/StakingRewardsMockUp.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../governance/StakingRewards/StakingRewards.sol\";\nimport \"./BlockMockUp.sol\";\n\n/**\n * @title Staking Rewards Contract MockUp\n * @notice This is used for Testing\n * */\ncontract StakingRewardsMockUp is StakingRewards {\n ///@notice the block mock up contract\n BlockMockUp public blockMockUp;\n\n using SafeMath for uint256;\n\n /**\n * @notice gets block number from BlockMockUp\n * @param _blockMockUp the address of BlockMockUp\n */\n function setBlockMockUpAddr(address _blockMockUp) public onlyOwner {\n require(_blockMockUp != address(0), \"block mockup address invalid\");\n blockMockUp = BlockMockUp(_blockMockUp);\n }\n\n /**\n * @notice Determine the current Block Number from BlockMockUp\n * */\n function _getCurrentBlockNumber() internal view returns (uint256) {\n return blockMockUp.getBlockNum();\n }\n}\n" + }, + "contracts/mockup/TimelockHarness.sol": { + "content": "pragma solidity ^0.5.16;\n\nimport \"../governance/Timelock.sol\";\n\ninterface Administered {\n function _acceptAdmin() external returns (uint256);\n}\n\ncontract TimelockHarness is Timelock {\n constructor(address admin_, uint256 delay_) public Timelock(admin_, delay_) {}\n\n function setDelayWithoutChecking(uint256 delay_) public {\n delay = delay_;\n\n emit NewDelay(delay);\n }\n\n function harnessSetPendingAdmin(address pendingAdmin_) public {\n pendingAdmin = pendingAdmin_;\n }\n\n function harnessSetAdmin(address admin_) public {\n admin = admin_;\n }\n}\n\ncontract TimelockTest is Timelock {\n constructor(address admin_, uint256 delay_) public Timelock(admin_, 2 days) {\n delay = delay_;\n }\n\n function harnessSetAdmin(address admin_) public {\n require(msg.sender == admin);\n admin = admin_;\n }\n\n function harnessAcceptAdmin(Administered administered) public {\n administered._acceptAdmin();\n }\n}\n" + }, + "contracts/mockup/VestingLogicMockup.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../governance/Vesting/VestingLogic.sol\";\n\ncontract VestingLogicMockup is VestingLogic {\n /**\n * @dev we had a bug in a loop: \"i < endDate\" instead of \"i <= endDate\"\n */\n function delegate(address _delegatee) public onlyTokenOwner {\n require(_delegatee != address(0), \"delegatee address invalid\");\n\n /// @dev Withdraw for each unlocked position.\n /// @dev Don't change FOUR_WEEKS to TWO_WEEKS, a lot of vestings already deployed with FOUR_WEEKS\n ///\t\tworkaround found, but it doesn't work with TWO_WEEKS\n for (uint256 i = startDate + cliff; i < endDate; i += FOUR_WEEKS) {\n staking.delegate(_delegatee, i);\n }\n emit VotesDelegated(msg.sender, _delegatee);\n }\n}\n" + }, + "contracts/mockup/VestingRegistryLogicMockUp.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\nimport \"../governance/Vesting/VestingRegistryLogic.sol\";\n\ncontract VestingRegistryLogicMockup is VestingRegistryLogic {\n function isVestingAddress(address _vestingAddress) external view returns (bool isVestingAddr) {\n return true;\n }\n\n function setTeamVesting(address _vesting, uint256 _vestingCreationType) external {\n vestingCreationAndTypes[_vesting] = VestingCreationAndTypeDetails({\n isSet: true,\n vestingType: uint32(VestingType.TeamVesting),\n vestingCreationType: uint128(_vestingCreationType)\n });\n }\n}\n" + }, + "contracts/modules/Affiliates.sol": { + "content": "/**\n * Copyright 2017-2020, Sovryn, All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../core/State.sol\";\nimport \"../mixins/EnumerableBytes32Set.sol\";\nimport \"../openzeppelin/SafeERC20.sol\";\nimport \"../events/AffiliatesEvents.sol\";\nimport \"../feeds/IPriceFeeds.sol\";\nimport \"../locked/ILockedSOV.sol\";\nimport \"../mixins/ModuleCommonFunctionalities.sol\";\n\n/**\n * @title Affiliates contract.\n * @notice Track referrals and reward referrers (affiliates) with tokens.\n * In-detail specifications are found at https://wiki.sovryn.app/en/community/Affiliates\n * @dev Module: Affiliates upgradable\n * Storage: from State, functions called from Protocol by delegatecall\n */\ncontract Affiliates is State, AffiliatesEvents, ModuleCommonFunctionalities {\n using SafeERC20 for IERC20;\n\n /**\n * @notice Void constructor.\n */\n // solhint-disable-next-line no-empty-blocks\n constructor() public {}\n\n /**\n * @notice Avoid calls to this contract except for those explicitly declared.\n */\n function() external {\n revert(\"Affiliates - fallback not allowed\");\n }\n\n /**\n * @notice Set delegate callable functions by proxy contract.\n * @dev This contract is designed as a module, this way logic can be\n * expanded and upgraded w/o losing storage that is kept in the protocol (State.sol)\n * initialize() is used to register in the proxy external (module) functions\n * to be called via the proxy.\n * @param target The address of a new logic implementation.\n */\n function initialize(address target) external onlyOwner {\n address prevModuleContractAddress = logicTargets[this.setAffiliatesReferrer.selector];\n _setTarget(this.setAffiliatesReferrer.selector, target);\n _setTarget(this.getUserNotFirstTradeFlag.selector, target);\n _setTarget(this.getReferralsList.selector, target);\n _setTarget(this.setUserNotFirstTradeFlag.selector, target);\n _setTarget(this.payTradingFeeToAffiliatesReferrer.selector, target);\n _setTarget(this.getAffiliatesReferrerBalances.selector, target);\n _setTarget(this.getAffiliatesReferrerTokenBalance.selector, target);\n _setTarget(this.getAffiliatesReferrerTokensList.selector, target);\n _setTarget(this.withdrawAffiliatesReferrerTokenFees.selector, target);\n _setTarget(this.withdrawAllAffiliatesReferrerTokenFees.selector, target);\n _setTarget(this.getMinReferralsToPayout.selector, target);\n _setTarget(this.getAffiliatesUserReferrer.selector, target);\n _setTarget(this.getAffiliateRewardsHeld.selector, target);\n _setTarget(this.getAffiliateTradingTokenFeePercent.selector, target);\n _setTarget(this.getAffiliatesTokenRewardsValueInRbtc.selector, target);\n emit ProtocolModuleContractReplaced(prevModuleContractAddress, target, \"Affiliates\");\n }\n\n /**\n * @notice Function modifier to avoid any other calls not coming from loan pools.\n */\n modifier onlyCallableByLoanPools() {\n require(loanPoolToUnderlying[msg.sender] != address(0), \"Affiliates: not authorized\");\n _;\n }\n\n /**\n * @notice Function modifier to avoid any other calls not coming from within protocol functions.\n */\n modifier onlyCallableInternal() {\n require(msg.sender == protocolAddress, \"Affiliates: not authorized\");\n _;\n }\n\n /**\n * @notice Data structure comprised of 3 flags to compute the result of setting a referrer.\n */\n struct SetAffiliatesReferrerResult {\n bool success;\n bool alreadySet;\n bool userNotFirstTradeFlag;\n }\n\n /**\n * @notice Loan pool calls this function to tell affiliates\n * a user coming from a referrer is trading and should be registered if not yet.\n * Taking into account some user status flags may lead to the user and referrer\n * become added or not to the affiliates record.\n *\n * @param user The address of the user that is trading on loan pools.\n * @param referrer The address of the referrer the user is coming from.\n */\n function setAffiliatesReferrer(address user, address referrer)\n external\n onlyCallableByLoanPools\n whenNotPaused\n {\n SetAffiliatesReferrerResult memory result;\n\n result.userNotFirstTradeFlag = getUserNotFirstTradeFlag(user);\n result.alreadySet = affiliatesUserReferrer[user] != address(0);\n result.success = !(result.userNotFirstTradeFlag || result.alreadySet || user == referrer);\n if (result.success) {\n affiliatesUserReferrer[user] = referrer;\n referralsList[referrer].add(user);\n emit SetAffiliatesReferrer(user, referrer);\n } else {\n emit SetAffiliatesReferrerFail(\n user,\n referrer,\n result.alreadySet,\n result.userNotFirstTradeFlag\n );\n }\n }\n\n /**\n * @notice Getter to query the referrals coming from a referrer.\n * @param referrer The address of a given referrer.\n * @return The referralsList mapping value by referrer.\n */\n function getReferralsList(address referrer) external view returns (address[] memory refList) {\n refList = referralsList[referrer].enumerate();\n return refList;\n }\n\n /**\n * @notice Getter to query the not-first-trade flag of a user.\n * @param user The address of a given user.\n * @return The userNotFirstTradeFlag mapping value by user.\n */\n function getUserNotFirstTradeFlag(address user) public view returns (bool) {\n return userNotFirstTradeFlag[user];\n }\n\n /**\n * @notice Setter to toggle on the not-first-trade flag of a user.\n * @param user The address of a given user.\n */\n function setUserNotFirstTradeFlag(address user)\n external\n onlyCallableByLoanPools\n whenNotPaused\n {\n if (!userNotFirstTradeFlag[user]) {\n userNotFirstTradeFlag[user] = true;\n emit SetUserNotFirstTradeFlag(user);\n }\n }\n\n /**\n * @notice Internal getter to query the fee share for affiliate program.\n * @dev It returns a value defined at protocol storage (State.sol)\n * @return The percentage of fee share w/ 18 decimals.\n */\n function _getAffiliatesTradingFeePercentForSOV() internal view returns (uint256) {\n return affiliateFeePercent;\n }\n\n /**\n * @notice Internal to calculate the affiliates trading token fee amount.\n * Affiliates program has 2 kind of rewards:\n * 1. x% based on the fee of the token that is traded (in form of the token itself).\n * 2. x% based on the fee of the token that is traded (in form of SOV).\n * This _getReferrerTradingFeeForToken calculates the first one\n * by applying a custom percentage multiplier.\n * @param feeTokenAmount The trading token fee amount.\n * @return The affiliates share of the trading token fee amount.\n */\n function _getReferrerTradingFeeForToken(uint256 feeTokenAmount)\n internal\n view\n returns (uint256)\n {\n return feeTokenAmount.mul(getAffiliateTradingTokenFeePercent()).div(10**20);\n }\n\n /**\n * @notice Getter to query the fee share of trading token fee for affiliate program.\n * @dev It returns a value defined at protocol storage (State.sol)\n * @return The percentage of fee share w/ 18 decimals.\n */\n function getAffiliateTradingTokenFeePercent() public view returns (uint256) {\n return affiliateTradingTokenFeePercent;\n }\n\n /**\n * @notice Getter to query referral threshold for paying out to the referrer.\n * @dev It returns a value defined at protocol storage (State.sol)\n * @return The minimum number of referrals set by Protocol.\n */\n function getMinReferralsToPayout() public view returns (uint256) {\n return minReferralsToPayout;\n }\n\n /**\n * @notice Get the sovToken reward of a trade.\n * @dev The reward is worth x% of the trading fee.\n * @param feeToken The address of the token in which the trading/borrowing fee was paid.\n * @param feeAmount The height of the fee.\n * @return The reward amount.\n * */\n function _getSovBonusAmount(address feeToken, uint256 feeAmount)\n internal\n view\n returns (uint256)\n {\n uint256 rewardAmount;\n address _priceFeeds = priceFeeds;\n\n /// @dev Calculate the reward amount, querying the price feed.\n (bool success, bytes memory data) =\n _priceFeeds.staticcall(\n abi.encodeWithSelector(\n IPriceFeeds(_priceFeeds).queryReturn.selector,\n feeToken,\n sovTokenAddress, /// dest token = SOV\n feeAmount.mul(_getAffiliatesTradingFeePercentForSOV()).div(1e20)\n )\n );\n // solhint-disable-next-line no-inline-assembly\n assembly {\n if eq(success, 1) {\n rewardAmount := mload(add(data, 32))\n }\n }\n\n return rewardAmount;\n }\n\n /**\n * @notice Protocol calls this function to pay the affiliates rewards to a user (referrer).\n *\n * @dev Affiliates program has 2 kind of rewards:\n * 1. x% based on the fee of the token that is traded (in form of the token itself).\n * 2. x% based on the fee of the token that is traded (in form of SOV).\n * Both are paid in this function.\n *\n * @dev Actually they are not paid, but just holded by protocol until user claims them by\n * actively calling withdrawAffiliatesReferrerTokenFees() function,\n * and/or when unvesting lockedSOV.\n *\n * @dev To be precise, what this function does is updating the registers of the rewards\n * for the referrer including the assignment of the SOV tokens as rewards to the\n * referrer's vesting contract.\n *\n * @param referrer The address of the referrer.\n * @param trader The address of the trader.\n * @param token The address of the token in which the trading/borrowing fee was paid.\n * @param tradingFeeTokenBaseAmount Total trading fee amount, the base for calculating referrer's fees.\n *\n * @return referrerBonusSovAmount The amount of SOV tokens paid to the referrer (through a vesting contract, lockedSOV).\n * @return referrerBonusTokenAmount The amount of trading tokens paid directly to the referrer.\n */\n function payTradingFeeToAffiliatesReferrer(\n address referrer,\n address trader,\n address token,\n uint256 tradingFeeTokenBaseAmount\n )\n external\n onlyCallableInternal\n whenNotPaused\n returns (uint256 referrerBonusSovAmount, uint256 referrerBonusTokenAmount)\n {\n bool isHeld = referralsList[referrer].length() < getMinReferralsToPayout();\n bool bonusPaymentIsSuccess = true;\n uint256 paidReferrerBonusSovAmount;\n\n /// Process token fee rewards first.\n referrerBonusTokenAmount = _getReferrerTradingFeeForToken(tradingFeeTokenBaseAmount);\n if (!affiliatesReferrerTokensList[referrer].contains(token))\n affiliatesReferrerTokensList[referrer].add(token);\n affiliatesReferrerBalances[referrer][token] = affiliatesReferrerBalances[referrer][token]\n .add(referrerBonusTokenAmount);\n\n /// Then process SOV rewards.\n referrerBonusSovAmount = _getSovBonusAmount(token, tradingFeeTokenBaseAmount);\n uint256 rewardsHeldByProtocol = affiliateRewardsHeld[referrer];\n\n if (isHeld) {\n /// If referrals less than minimum, temp the rewards SOV to the storage\n affiliateRewardsHeld[referrer] = rewardsHeldByProtocol.add(referrerBonusSovAmount);\n } else {\n /// If referrals >= minimum, directly send all of the remain rewards to locked sov\n /// Call depositSOV() in LockedSov contract\n /// Set the affiliaterewardsheld = 0\n if (affiliateRewardsHeld[referrer] > 0) {\n affiliateRewardsHeld[referrer] = 0;\n }\n\n paidReferrerBonusSovAmount = referrerBonusSovAmount.add(rewardsHeldByProtocol);\n IERC20(sovTokenAddress).approve(lockedSOVAddress, paidReferrerBonusSovAmount);\n\n (bool success, ) =\n lockedSOVAddress.call(\n abi.encodeWithSignature(\n \"depositSOV(address,uint256)\",\n referrer,\n paidReferrerBonusSovAmount\n )\n );\n\n if (!success) {\n bonusPaymentIsSuccess = false;\n }\n }\n\n if (bonusPaymentIsSuccess) {\n emit PayTradingFeeToAffiliate(\n referrer,\n trader, // trader\n token,\n isHeld,\n tradingFeeTokenBaseAmount,\n referrerBonusTokenAmount,\n referrerBonusSovAmount,\n paidReferrerBonusSovAmount\n );\n } else {\n emit PayTradingFeeToAffiliateFail(\n referrer,\n trader, // trader\n token,\n tradingFeeTokenBaseAmount,\n referrerBonusTokenAmount,\n referrerBonusSovAmount,\n paidReferrerBonusSovAmount\n );\n }\n\n return (referrerBonusSovAmount, referrerBonusTokenAmount);\n }\n\n /**\n * @notice Referrer calls this function to receive its reward in a given token.\n * It will send the other (non-SOV) reward tokens from trading protocol fees,\n * to the referrer’s wallet.\n * @dev Rewards are held by protocol in different tokens coming from trading fees.\n * Referrer has to claim them one by one for every token with accumulated balance.\n * @param token The address of the token to withdraw.\n * @param receiver The address of the withdrawal beneficiary.\n * @param amount The amount of tokens to claim. If greater than balance, just sends balance.\n */\n function withdrawAffiliatesReferrerTokenFees(\n address token,\n address receiver,\n uint256 amount\n ) public whenNotPaused {\n require(receiver != address(0), \"Affiliates: cannot withdraw to zero address\");\n address referrer = msg.sender;\n uint256 referrerTokenBalance = affiliatesReferrerBalances[referrer][token];\n uint256 withdrawAmount = referrerTokenBalance > amount ? amount : referrerTokenBalance;\n\n require(withdrawAmount > 0, \"Affiliates: cannot withdraw zero amount\");\n\n require(\n referralsList[referrer].length() >= getMinReferralsToPayout(),\n \"Your referrals has not reached the minimum request\"\n );\n\n uint256 newReferrerTokenBalance = referrerTokenBalance.sub(withdrawAmount);\n\n if (newReferrerTokenBalance == 0) {\n _removeAffiliatesReferrerToken(referrer, token);\n } else {\n affiliatesReferrerBalances[referrer][token] = newReferrerTokenBalance;\n }\n\n IERC20(token).safeTransfer(receiver, withdrawAmount);\n\n emit WithdrawAffiliatesReferrerTokenFees(referrer, receiver, token, withdrawAmount);\n }\n\n /**\n * @notice Withdraw to msg.sender all token fees for a referrer.\n * @dev It's done by looping through its available tokens.\n * @param receiver The address of the withdrawal beneficiary.\n */\n function withdrawAllAffiliatesReferrerTokenFees(address receiver) external whenNotPaused {\n require(receiver != address(0), \"Affiliates: cannot withdraw to zero address\");\n address referrer = msg.sender;\n\n require(\n referralsList[referrer].length() >= getMinReferralsToPayout(),\n \"Your referrals has not reached the minimum request\"\n );\n\n (address[] memory tokenAddresses, uint256[] memory tokenBalances) =\n getAffiliatesReferrerBalances(referrer);\n for (uint256 i; i < tokenAddresses.length; i++) {\n withdrawAffiliatesReferrerTokenFees(tokenAddresses[i], receiver, tokenBalances[i]);\n }\n }\n\n /**\n * @notice Internal function to delete a referrer's token balance.\n * @param referrer The address of the referrer.\n * @param token The address of the token specifying the balance to remove.\n */\n function _removeAffiliatesReferrerToken(address referrer, address token) internal {\n delete affiliatesReferrerBalances[referrer][token];\n affiliatesReferrerTokensList[referrer].remove(token);\n }\n\n /**\n * @notice Get all token balances of a referrer.\n * @param referrer The address of the referrer.\n * @return referrerTokensList The array of available tokens (keys).\n * @return referrerTokensBalances The array of token balances (values).\n */\n function getAffiliatesReferrerBalances(address referrer)\n public\n view\n returns (address[] memory referrerTokensList, uint256[] memory referrerTokensBalances)\n {\n referrerTokensList = getAffiliatesReferrerTokensList(referrer);\n referrerTokensBalances = new uint256[](referrerTokensList.length);\n for (uint256 i; i < referrerTokensList.length; i++) {\n referrerTokensBalances[i] = getAffiliatesReferrerTokenBalance(\n referrer,\n referrerTokensList[i]\n );\n }\n return (referrerTokensList, referrerTokensBalances);\n }\n\n /**\n * @dev Get all token rewards estimation value in rbtc.\n *\n * @param referrer Address of referrer.\n *\n * @return The value estimation in rbtc.\n */\n function getAffiliatesTokenRewardsValueInRbtc(address referrer)\n external\n view\n returns (uint256 rbtcTotalAmount)\n {\n address[] memory tokensList = getAffiliatesReferrerTokensList(referrer);\n address _priceFeeds = priceFeeds;\n\n for (uint256 i; i < tokensList.length; i++) {\n // Get the value of each token in rbtc\n\n (bool success, bytes memory data) =\n _priceFeeds.staticcall(\n abi.encodeWithSelector(\n IPriceFeeds(_priceFeeds).queryReturn.selector,\n tokensList[i], // source token\n address(wrbtcToken), // dest token = SOV\n affiliatesReferrerBalances[referrer][tokensList[i]] // total token rewards\n )\n );\n\n assembly {\n if eq(success, 1) {\n rbtcTotalAmount := add(rbtcTotalAmount, mload(add(data, 32)))\n }\n }\n }\n }\n\n /**\n * @notice Get all available tokens at the affiliates program for a given referrer.\n * @param referrer The address of a given referrer.\n * @return tokensList The list of available tokens.\n */\n function getAffiliatesReferrerTokensList(address referrer)\n public\n view\n returns (address[] memory tokensList)\n {\n tokensList = affiliatesReferrerTokensList[referrer].enumerate();\n return tokensList;\n }\n\n /**\n * @notice Getter to query the affiliate balance for a given referrer and token.\n * @param referrer The address of the referrer.\n * @param token The address of the token to get balance for.\n * @return The affiliatesReferrerBalances mapping value by referrer and token keys.\n */\n function getAffiliatesReferrerTokenBalance(address referrer, address token)\n public\n view\n returns (uint256)\n {\n return affiliatesReferrerBalances[referrer][token];\n }\n\n /**\n * @notice Getter to query the address of referrer for a given user.\n * @param user The address of the user.\n * @return The address on affiliatesUserReferrer mapping value by user key.\n */\n function getAffiliatesUserReferrer(address user) public view returns (address) {\n return affiliatesUserReferrer[user];\n }\n\n /**\n * @notice Getter to query the reward amount held for a given referrer.\n * @param referrer The address of the referrer.\n * @return The affiliateRewardsHeld mapping value by referrer key.\n */\n function getAffiliateRewardsHeld(address referrer) public view returns (uint256) {\n return affiliateRewardsHeld[referrer];\n }\n}\n" + }, + "contracts/modules/interfaces/ProtocolAffiliatesInterface.sol": { + "content": "/**\n * Copyright 2020, Denis Savelev. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\ninterface ProtocolAffiliatesInterface {\n function setAffiliatesReferrer(address user, address referrer) external;\n\n function setUserNotFirstTradeFlag(address user_) external;\n\n function getUserNotFirstTradeFlag(address user_) external returns (bool);\n\n function payTradingFeeToAffiliatesReferrer(\n address affiliate,\n address trader,\n address token,\n uint256 amount\n ) external returns (uint256 affiliatesBonusSOVAmount, uint256 affiliatesBonusTokenAmount);\n}\n" + }, + "contracts/modules/interfaces/ProtocolSwapExternalInterface.sol": { + "content": "/**\n * Copyright 2020, Denis Savelev. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\ninterface ProtocolSwapExternalInterface {\n function swapExternal(\n address sourceToken,\n address destToken,\n address receiver,\n address returnToSender,\n uint256 sourceTokenAmount,\n uint256 requiredDestTokenAmount,\n uint256 minReturn,\n bytes calldata swapData\n ) external returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed);\n}\n" + }, + "contracts/modules/LoanClosingsLiquidation.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../mixins/LiquidationHelper.sol\";\nimport \"../interfaces/ILoanPool.sol\";\nimport \"./LoanClosingsShared.sol\";\n\n/**\n * @title LoanClosingsLiquidation contract.\n * @notice Ways to close a loan: liquidation. Margin trade\n * positions are always closed with a swap.\n *\n * Loans are liquidated if the position goes below margin maintenance.\n * */\ncontract LoanClosingsLiquidation is LoanClosingsShared, LiquidationHelper {\n uint256 internal constant MONTH = 365 days / 12;\n\n constructor() public {}\n\n function() external {\n revert(\"fallback not allowed\");\n }\n\n function initialize(address target) external onlyOwner {\n address prevModuleContractAddress = logicTargets[this.liquidate.selector];\n _setTarget(this.liquidate.selector, target);\n emit ProtocolModuleContractReplaced(\n prevModuleContractAddress,\n target,\n \"LoanClosingsLiquidation\"\n );\n }\n\n /**\n * @notice Liquidate an unhealty loan.\n *\n * @dev Public wrapper for _liquidate internal function.\n *\n * The caller needs to approve the closeAmount prior to calling. Will\n * not liquidate more than is needed to restore the desired margin\n * (maintenance +5%).\n *\n * Whenever the current margin of a loan falls below maintenance margin,\n * it needs to be liquidated. Anybody can initiate a liquidation and buy\n * the collateral tokens at a discounted rate (5%).\n *\n * @param loanId The ID of the loan to liquidate.\n * loanId is the ID of the loan, which is created on loan opening.\n * It can be obtained either by parsing the Trade event or by reading\n * the open loans from the contract by calling getActiveLoans or getUserLoans.\n * @param receiver The receiver of the seized amount.\n * @param closeAmount The amount to close in loanTokens.\n *\n * @return loanCloseAmount The amount of the collateral token of the loan.\n * @return seizedAmount The seized amount in the collateral token.\n * @return seizedToken The loan token address.\n * */\n function liquidate(\n bytes32 loanId,\n address receiver,\n uint256 closeAmount // denominated in loanToken\n )\n external\n payable\n nonReentrant\n globallyNonReentrant\n iTokenSupplyUnchanged(loanId)\n whenNotPaused\n returns (\n uint256 loanCloseAmount,\n uint256 seizedAmount,\n address seizedToken\n )\n {\n return _liquidate(loanId, receiver, closeAmount);\n }\n\n /**\n * @notice Internal function for liquidating an unhealthy loan.\n *\n * The caller needs to approve the closeAmount prior to calling. Will\n * not liquidate more than is needed to restore the desired margin\n * (maintenance +5%).\n *\n * Whenever the current margin of a loan falls below maintenance margin,\n * it needs to be liquidated. Anybody can initiate a liquidation and buy\n * the collateral tokens at a discounted rate (5%).\n *\n * @param loanId The ID of the loan to liquidate.\n * @param receiver The receiver of the seized amount.\n * @param closeAmount The amount to close in loanTokens.\n *\n * @return loanCloseAmount The amount of the collateral token of the loan.\n * @return seizedAmount The seized amount in the collateral token.\n * @return seizedToken The loan token address.\n * */\n function _liquidate(\n bytes32 loanId,\n address receiver,\n uint256 closeAmount\n )\n internal\n returns (\n uint256 loanCloseAmount,\n uint256 seizedAmount,\n address seizedToken\n )\n {\n (Loan storage loanLocal, LoanParams storage loanParamsLocal) = _checkLoan(loanId);\n\n (uint256 currentMargin, uint256 collateralToLoanRate) =\n IPriceFeeds(priceFeeds).getCurrentMargin(\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanLocal.principal,\n loanLocal.collateral\n );\n require(currentMargin <= loanParamsLocal.maintenanceMargin, \"healthy position\");\n\n loanCloseAmount = closeAmount;\n\n //amounts to restore the desired margin (maintencance + 5%)\n (uint256 maxLiquidatable, uint256 maxSeizable, ) =\n _getLiquidationAmounts(\n loanLocal.principal,\n loanLocal.collateral,\n currentMargin,\n loanParamsLocal.maintenanceMargin,\n collateralToLoanRate\n );\n\n if (loanCloseAmount < maxLiquidatable) {\n //close maxLiquidatable if tiny position will remain\n uint256 remainingAmount = maxLiquidatable - loanCloseAmount;\n remainingAmount = _getAmountInRbtc(loanParamsLocal.loanToken, remainingAmount);\n if (remainingAmount <= TINY_AMOUNT) {\n loanCloseAmount = maxLiquidatable;\n seizedAmount = maxSeizable;\n } else {\n seizedAmount = maxSeizable.mul(loanCloseAmount).div(maxLiquidatable);\n }\n } else if (loanCloseAmount > maxLiquidatable) {\n // adjust down the close amount to the max\n loanCloseAmount = maxLiquidatable;\n seizedAmount = maxSeizable;\n } else {\n seizedAmount = maxSeizable;\n }\n\n require(loanCloseAmount != 0, \"nothing to liquidate\");\n\n // liquidator deposits the principal being closed\n _returnPrincipalWithDeposit(loanParamsLocal.loanToken, address(this), loanCloseAmount);\n\n // a portion of the principal is repaid to the lender out of interest refunded\n uint256 loanCloseAmountLessInterest =\n _settleInterestToPrincipal(\n loanLocal,\n loanParamsLocal,\n loanCloseAmount,\n loanLocal.borrower\n );\n\n if (loanCloseAmount > loanCloseAmountLessInterest) {\n // full interest refund goes to the borrower\n _withdrawAsset(\n loanParamsLocal.loanToken,\n loanLocal.borrower,\n loanCloseAmount - loanCloseAmountLessInterest\n );\n }\n\n if (loanCloseAmountLessInterest != 0) {\n // The lender always gets back an ERC20 (even wrbtc), so we call withdraw directly rather than\n // use the _withdrawAsset helper function\n vaultWithdraw(\n loanParamsLocal.loanToken,\n loanLocal.lender,\n loanCloseAmountLessInterest\n );\n }\n\n seizedToken = loanParamsLocal.collateralToken;\n\n if (seizedAmount != 0) {\n loanLocal.collateral = loanLocal.collateral.sub(seizedAmount);\n\n _withdrawAsset(seizedToken, receiver, seizedAmount);\n }\n\n _closeLoan(loanLocal, loanCloseAmount);\n\n _emitClosingEvents(\n loanParamsLocal,\n loanLocal,\n loanCloseAmount,\n seizedAmount,\n collateralToLoanRate,\n 0,\n currentMargin,\n CloseTypes.Liquidation\n );\n }\n\n /**\n * @notice Swap back excessive loan tokens to collateral tokens.\n *\n * @param loanLocal The loan object.\n * @param loanParamsLocal The loan parameters.\n * @param swapAmount The amount to be swapped.\n * @param loanDataBytes Additional loan data (not in use for token swaps).\n *\n * @return destTokenAmountReceived The amount of destiny tokens received.\n * @return sourceTokenAmountUsed The amount of source tokens used.\n * @return collateralToLoanSwapRate The swap rate of collateral.\n * */\n function _swapBackExcess(\n Loan memory loanLocal,\n LoanParams memory loanParamsLocal,\n uint256 swapAmount,\n bytes memory loanDataBytes\n )\n internal\n returns (\n uint256 destTokenAmountReceived,\n uint256 sourceTokenAmountUsed,\n uint256 collateralToLoanSwapRate\n )\n {\n (destTokenAmountReceived, sourceTokenAmountUsed, collateralToLoanSwapRate) = _loanSwap(\n loanLocal.id,\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanLocal.borrower,\n swapAmount, // minSourceTokenAmount\n swapAmount, // maxSourceTokenAmount\n 0, // requiredDestTokenAmount\n false, // bypassFee\n loanDataBytes\n );\n require(sourceTokenAmountUsed <= swapAmount, \"excessive source amount\");\n }\n}\n" + }, + "contracts/modules/LoanClosingsRollover.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../mixins/LiquidationHelper.sol\";\nimport \"../interfaces/ILoanPool.sol\";\nimport \"./LoanClosingsShared.sol\";\n\n/**\n * @title LoanClosingsRollover contract.\n * @notice Ways to close a loan: rollover. Margin trade\n * positions are always closed with a swap.\n *\n * */\ncontract LoanClosingsRollover is LoanClosingsShared, LiquidationHelper {\n uint256 internal constant MONTH = 365 days / 12;\n\n constructor() public {}\n\n function() external {\n revert(\"fallback not allowed\");\n }\n\n function initialize(address target) external onlyOwner {\n address prevModuleContractAddress = logicTargets[this.rollover.selector];\n _setTarget(this.rollover.selector, target);\n emit ProtocolModuleContractReplaced(\n prevModuleContractAddress,\n target,\n \"LoanClosingsRollover\"\n );\n }\n\n /**\n * @notice Roll over a loan.\n *\n * @dev Public wrapper for _rollover internal function.\n *\n * Each loan has a duration. In case of a margin trade it is set to 28\n * days, in case of borrowing, it can be set by the user. On loan\n * openning, the user pays the interest for this duration in advance.\n * If closing early, he gets the excess refunded. If it is not closed\n * before the end date, it needs to be rolled over. On rollover the\n * interest is paid for the next period. In case of margin trading\n * it's 28 days, in case of borrowing it's a month.\n *\n * The function rollover on the protocol contract extends the loan\n * duration by the maximum term (28 days for margin trades at the moment\n * of writing), pays the interest to the lender and refunds the caller\n * for the gas cost by sending 2 * the gas cost using the fast gas price\n * as base for the calculation.\n *\n * @param loanId The ID of the loan to roll over.\n * // param calldata The payload for the call. These loan DataBytes are additional loan data (not in use for token swaps).\n * */\n function rollover(\n bytes32 loanId,\n bytes calldata // for future use /*loanDataBytes*/\n ) external nonReentrant globallyNonReentrant iTokenSupplyUnchanged(loanId) whenNotPaused {\n // restrict to EOAs to prevent griefing attacks, during interest rate recalculation\n require(msg.sender == tx.origin, \"EOAs call\");\n\n return\n _rollover(\n loanId,\n \"\" // loanDataBytes\n );\n }\n\n /**\n * @notice Internal function for roll over a loan.\n *\n * Each loan has a duration. In case of a margin trade it is set to 28\n * days, in case of borrowing, it can be set by the user. On loan\n * openning, the user pays the interest for this duration in advance.\n * If closing early, he gets the excess refunded. If it is not closed\n * before the end date, it needs to be rolled over. On rollover the\n * interest is paid for the next period. In case of margin trading\n * it's 28 days, in case of borrowing it's a month.\n *\n * @param loanId The ID of the loan to roll over.\n * @param loanDataBytes The payload for the call. These loan DataBytes are\n * additional loan data (not in use for token swaps).\n * */\n function _rollover(bytes32 loanId, bytes memory loanDataBytes) internal {\n (Loan storage loanLocal, LoanParams storage loanParamsLocal) = _checkLoan(loanId);\n require(block.timestamp > loanLocal.endTimestamp.sub(3600), \"healthy position\");\n require(loanPoolToUnderlying[loanLocal.lender] != address(0), \"invalid lender\");\n\n // pay outstanding interest to lender\n _payInterest(loanLocal.lender, loanParamsLocal.loanToken);\n\n LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id];\n LenderInterest storage lenderInterestLocal =\n lenderInterest[loanLocal.lender][loanParamsLocal.loanToken];\n\n _settleFeeRewardForInterestExpense(\n loanInterestLocal,\n loanLocal.id,\n loanParamsLocal.loanToken, /// fee token\n loanParamsLocal.collateralToken, /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward\n loanLocal.borrower,\n block.timestamp\n );\n\n // Handle back interest: calculates interest owned since the loan endtime passed but the loan remained open\n uint256 backInterestTime;\n uint256 backInterestOwed;\n if (block.timestamp > loanLocal.endTimestamp) {\n backInterestTime = block.timestamp.sub(loanLocal.endTimestamp);\n backInterestOwed = backInterestTime.mul(loanInterestLocal.owedPerDay);\n backInterestOwed = backInterestOwed.div(1 days);\n }\n\n //note: to avoid code duplication, it would be nicer to store loanParamsLocal.maxLoanTerm in a local variable\n //however, we've got stack too deep issues if we do so.\n if (loanParamsLocal.maxLoanTerm != 0) {\n // fixed-term loan, so need to query iToken for latest variable rate\n uint256 owedPerDay =\n loanLocal.principal.mul(ILoanPool(loanLocal.lender).borrowInterestRate()).div(\n 365 * 10**20\n );\n\n lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.add(owedPerDay);\n lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.sub(\n loanInterestLocal.owedPerDay\n );\n\n loanInterestLocal.owedPerDay = owedPerDay;\n\n //if the loan has been open for longer than an additional period, add at least 1 additional day\n if (backInterestTime >= loanParamsLocal.maxLoanTerm) {\n loanLocal.endTimestamp = loanLocal.endTimestamp.add(backInterestTime).add(1 days);\n }\n //extend by the max loan term\n else {\n loanLocal.endTimestamp = loanLocal.endTimestamp.add(loanParamsLocal.maxLoanTerm);\n }\n } else {\n // loanInterestLocal.owedPerDay doesn't change\n if (backInterestTime >= MONTH) {\n loanLocal.endTimestamp = loanLocal.endTimestamp.add(backInterestTime).add(1 days);\n } else {\n loanLocal.endTimestamp = loanLocal.endTimestamp.add(MONTH);\n }\n }\n\n uint256 interestAmountRequired = loanLocal.endTimestamp.sub(block.timestamp);\n interestAmountRequired = interestAmountRequired.mul(loanInterestLocal.owedPerDay);\n interestAmountRequired = interestAmountRequired.div(1 days);\n\n loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.add(\n interestAmountRequired\n );\n\n lenderInterestLocal.owedTotal = lenderInterestLocal.owedTotal.add(interestAmountRequired);\n\n // add backInterestOwed\n interestAmountRequired = interestAmountRequired.add(backInterestOwed);\n\n // collect interest (needs to be converted from the collateral)\n (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed, ) =\n _doCollateralSwap(\n loanLocal,\n loanParamsLocal,\n 0, //min swap 0 -> swap connector estimates the amount of source tokens to use\n interestAmountRequired, //required destination tokens\n true, // returnTokenIsCollateral\n loanDataBytes\n );\n\n //received more tokens than needed to pay the interest\n if (destTokenAmountReceived > interestAmountRequired) {\n // swap rest back to collateral, if the amount is big enough to cover gas cost\n if (\n worthTheTransfer(\n loanParamsLocal.loanToken,\n destTokenAmountReceived - interestAmountRequired\n )\n ) {\n (destTokenAmountReceived, , ) = _swapBackExcess(\n loanLocal,\n loanParamsLocal,\n destTokenAmountReceived - interestAmountRequired, //amount to be swapped\n loanDataBytes\n );\n sourceTokenAmountUsed = sourceTokenAmountUsed.sub(destTokenAmountReceived);\n }\n //else give it to the protocol as a lending fee\n else {\n _payLendingFee(\n loanLocal.borrower,\n loanParamsLocal.loanToken,\n destTokenAmountReceived - interestAmountRequired\n );\n }\n }\n\n //subtract the interest from the collateral\n loanLocal.collateral = loanLocal.collateral.sub(sourceTokenAmountUsed);\n\n if (backInterestOwed != 0) {\n // pay out backInterestOwed\n\n _payInterestTransfer(loanLocal.lender, loanParamsLocal.loanToken, backInterestOwed);\n }\n\n uint256 rolloverReward =\n _getRolloverReward(\n loanParamsLocal.collateralToken,\n loanParamsLocal.loanToken,\n loanLocal.principal\n );\n\n if (rolloverReward != 0) {\n // if the reward > collateral:\n if (rolloverReward > loanLocal.collateral) {\n // 1. pay back the remaining loan to the lender\n // 2. pay the remaining collateral to msg.sender\n // 3. close the position & emit close event\n _closeWithSwap(\n loanLocal.id,\n msg.sender,\n loanLocal.collateral,\n false,\n \"\" // loanDataBytes\n );\n } else {\n // pay out reward to caller\n loanLocal.collateral = loanLocal.collateral.sub(rolloverReward);\n\n _withdrawAsset(loanParamsLocal.collateralToken, msg.sender, rolloverReward);\n }\n }\n\n if (loanLocal.collateral > 0) {\n //close whole loan if tiny position will remain\n if (_getAmountInRbtc(loanParamsLocal.loanToken, loanLocal.principal) <= TINY_AMOUNT) {\n _closeWithSwap(\n loanLocal.id,\n loanLocal.borrower,\n loanLocal.collateral, // swap all collaterals\n false,\n \"\" /// loanDataBytes\n );\n } else {\n (uint256 currentMargin, ) =\n IPriceFeeds(priceFeeds).getCurrentMargin(\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanLocal.principal,\n loanLocal.collateral\n );\n\n require(\n currentMargin > 3 ether, // ensure there's more than 3% margin remaining\n \"unhealthy position\"\n );\n }\n }\n\n if (loanLocal.active) {\n emit Rollover(\n loanLocal.borrower, // user (borrower)\n loanLocal.lender, // lender\n loanLocal.id, // loanId\n loanLocal.principal, // principal\n loanLocal.collateral, // collateral\n loanLocal.endTimestamp, // endTimestamp\n msg.sender, // rewardReceiver\n rolloverReward // reward\n );\n }\n }\n\n /**\n * @notice Swap back excessive loan tokens to collateral tokens.\n *\n * @param loanLocal The loan object.\n * @param loanParamsLocal The loan parameters.\n * @param swapAmount The amount to be swapped.\n * @param loanDataBytes Additional loan data (not in use for token swaps).\n *\n * @return destTokenAmountReceived The amount of destiny tokens received.\n * @return sourceTokenAmountUsed The amount of source tokens used.\n * @return collateralToLoanSwapRate The swap rate of collateral.\n * */\n function _swapBackExcess(\n Loan memory loanLocal,\n LoanParams memory loanParamsLocal,\n uint256 swapAmount,\n bytes memory loanDataBytes\n )\n internal\n returns (\n uint256 destTokenAmountReceived,\n uint256 sourceTokenAmountUsed,\n uint256 collateralToLoanSwapRate\n )\n {\n (destTokenAmountReceived, sourceTokenAmountUsed, collateralToLoanSwapRate) = _loanSwap(\n loanLocal.id,\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanLocal.borrower,\n swapAmount, // minSourceTokenAmount\n swapAmount, // maxSourceTokenAmount\n 0, // requiredDestTokenAmount\n false, // bypassFee\n loanDataBytes\n );\n require(sourceTokenAmountUsed <= swapAmount, \"excessive source amount\");\n }\n}\n" + }, + "contracts/modules/LoanClosingsShared.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../core/State.sol\";\nimport \"../events/LoanClosingsEvents.sol\";\nimport \"../mixins/VaultController.sol\";\nimport \"../mixins/InterestUser.sol\";\nimport \"../swaps/SwapsUser.sol\";\nimport \"../mixins/RewardHelper.sol\";\nimport \"../mixins/ModuleCommonFunctionalities.sol\";\nimport \"../interfaces/ILoanTokenModules.sol\";\n\n/**\n * @title LoanClosingsShared contract.\n * @notice This contract should only contains the internal function that is being used / utilized by\n * LoanClosingsLiquidation, LoanClosingsRollover & LoanClosingsWith contract\n *\n * */\ncontract LoanClosingsShared is\n LoanClosingsEvents,\n VaultController,\n InterestUser,\n SwapsUser,\n RewardHelper,\n ModuleCommonFunctionalities\n{\n uint256 internal constant MONTH = 365 days / 12;\n //0.00001 BTC, would be nicer in State.sol, but would require a redeploy of the complete protocol, so adding it here instead\n //because it's not shared state anyway and only used by this contract\n uint256 public constant paySwapExcessToBorrowerThreshold = 10000000000000;\n\n uint256 public constant TINY_AMOUNT = 25e13;\n\n enum CloseTypes { Deposit, Swap, Liquidation }\n\n /** modifier for invariant check */\n modifier iTokenSupplyUnchanged(bytes32 loanId) {\n Loan storage loanLocal = loans[loanId];\n\n require(loanLocal.lender != address(0), \"Invalid loan token pool address\");\n\n uint256 previousITokenSupply = ILoanTokenModules(loanLocal.lender).totalSupply();\n\n _;\n\n /// Validate iToken total supply\n require(\n previousITokenSupply == ILoanTokenModules(loanLocal.lender).totalSupply(),\n \"loan token supply invariant check failure\"\n );\n }\n\n /**\n * @dev computes the interest which needs to be refunded to the borrower based on the amount he's closing and either\n * subtracts it from the amount which still needs to be paid back (in case outstanding amount > interest) or withdraws the\n * excess to the borrower (in case interest > outstanding).\n * @param loanLocal the loan\n * @param loanParamsLocal the loan params\n * @param loanCloseAmount the amount to be closed (base for the computation)\n * @param receiver the address of the receiver (usually the borrower)\n * */\n function _settleInterestToPrincipal(\n Loan memory loanLocal,\n LoanParams memory loanParamsLocal,\n uint256 loanCloseAmount,\n address receiver\n ) internal returns (uint256) {\n uint256 loanCloseAmountLessInterest = loanCloseAmount;\n\n //compute the interest which neeeds to be refunded to the borrower (because full interest is paid on loan )\n uint256 interestRefundToBorrower =\n _settleInterest(loanParamsLocal, loanLocal, loanCloseAmountLessInterest);\n\n uint256 interestAppliedToPrincipal;\n //if the outstanding loan is bigger than the interest to be refunded, reduce the amount to be paid back / closed by the interest\n if (loanCloseAmountLessInterest >= interestRefundToBorrower) {\n // apply all of borrower interest refund torwards principal\n interestAppliedToPrincipal = interestRefundToBorrower;\n\n // principal needed is reduced by this amount\n loanCloseAmountLessInterest -= interestRefundToBorrower;\n\n // no interest refund remaining\n interestRefundToBorrower = 0;\n } else {\n //if the interest refund is bigger than the outstanding loan, the user needs to get back the interest\n // principal fully covered by excess interest\n interestAppliedToPrincipal = loanCloseAmountLessInterest;\n\n // amount refunded is reduced by this amount\n interestRefundToBorrower -= loanCloseAmountLessInterest;\n\n // principal fully covered by excess interest\n loanCloseAmountLessInterest = 0;\n\n if (interestRefundToBorrower != 0) {\n // refund overage\n _withdrawAsset(loanParamsLocal.loanToken, receiver, interestRefundToBorrower);\n }\n }\n\n //pay the interest to the lender\n //note: this is a waste of gas, because the loanCloseAmountLessInterest is withdrawn to the lender, too. It could be done at once.\n if (interestAppliedToPrincipal != 0) {\n // The lender always gets back an ERC20 (even wrbtc), so we call withdraw directly rather than\n // use the _withdrawAsset helper function\n vaultWithdraw(loanParamsLocal.loanToken, loanLocal.lender, interestAppliedToPrincipal);\n }\n\n return loanCloseAmountLessInterest;\n }\n\n // The receiver always gets back an ERC20 (even wrbtc)\n function _returnPrincipalWithDeposit(\n address loanToken,\n address receiver,\n uint256 principalNeeded\n ) internal {\n if (principalNeeded != 0) {\n if (msg.value == 0) {\n vaultTransfer(loanToken, msg.sender, receiver, principalNeeded);\n } else {\n require(loanToken == address(wrbtcToken), \"wrong asset sent\");\n require(msg.value >= principalNeeded, \"not enough ether\");\n wrbtcToken.deposit.value(principalNeeded)();\n if (receiver != address(this)) {\n vaultTransfer(loanToken, address(this), receiver, principalNeeded);\n }\n if (msg.value > principalNeeded) {\n // refund overage\n Address.sendValue(msg.sender, msg.value - principalNeeded);\n }\n }\n } else {\n require(msg.value == 0, \"wrong asset sent\");\n }\n }\n\n /**\n * @dev checks if the amount of the asset to be transfered is worth the transfer fee\n * @param asset the asset to be transfered\n * @param amount the amount to be transfered\n * @return True if the amount is bigger than the threshold\n * */\n function worthTheTransfer(address asset, uint256 amount) internal returns (bool) {\n uint256 amountInRbtc = _getAmountInRbtc(asset, amount);\n emit swapExcess(\n amountInRbtc > paySwapExcessToBorrowerThreshold,\n amount,\n amountInRbtc,\n paySwapExcessToBorrowerThreshold\n );\n\n return amountInRbtc > paySwapExcessToBorrowerThreshold;\n }\n\n /**\n * swaps collateral tokens for loan tokens\n * @param loanLocal the loan object\n * @param loanParamsLocal the loan parameters\n * @param swapAmount the amount to be swapped\n * @param principalNeeded the required destination token amount\n * @param returnTokenIsCollateral if true -> required destination token amount will be passed on, else not\n * note: quite dirty. should be refactored.\n * @param loanDataBytes additional loan data (not in use for token swaps)\n * */\n function _doCollateralSwap(\n Loan memory loanLocal,\n LoanParams memory loanParamsLocal,\n uint256 swapAmount,\n uint256 principalNeeded,\n bool returnTokenIsCollateral,\n bytes memory loanDataBytes\n )\n internal\n returns (\n uint256 destTokenAmountReceived,\n uint256 sourceTokenAmountUsed,\n uint256 collateralToLoanSwapRate\n )\n {\n (destTokenAmountReceived, sourceTokenAmountUsed, collateralToLoanSwapRate) = _loanSwap(\n loanLocal.id,\n loanParamsLocal.collateralToken,\n loanParamsLocal.loanToken,\n loanLocal.borrower,\n swapAmount, // minSourceTokenAmount\n loanLocal.collateral, // maxSourceTokenAmount\n returnTokenIsCollateral\n ? principalNeeded // requiredDestTokenAmount\n : 0,\n false, // bypassFee\n loanDataBytes\n );\n require(destTokenAmountReceived >= principalNeeded, \"insufficient dest amount\");\n require(sourceTokenAmountUsed <= loanLocal.collateral, \"excessive source amount\");\n }\n\n /**\n * @notice Withdraw asset to receiver.\n *\n * @param assetToken The loan token.\n * @param receiver The address of the receiver.\n * @param assetAmount The loan token amount.\n * */\n function _withdrawAsset(\n address assetToken,\n address receiver,\n uint256 assetAmount\n ) internal {\n if (assetAmount != 0) {\n if (assetToken == address(wrbtcToken)) {\n vaultEtherWithdraw(receiver, assetAmount);\n } else {\n vaultWithdraw(assetToken, receiver, assetAmount);\n }\n }\n }\n\n /**\n * @notice Internal function to close a loan.\n *\n * @param loanLocal The loan object.\n * @param loanCloseAmount The amount to close: principal or lower.\n *\n * */\n function _closeLoan(Loan storage loanLocal, uint256 loanCloseAmount) internal {\n require(loanCloseAmount != 0, \"nothing to close\");\n\n if (loanCloseAmount == loanLocal.principal) {\n loanLocal.principal = 0;\n loanLocal.active = false;\n loanLocal.endTimestamp = block.timestamp;\n loanLocal.pendingTradesId = 0;\n activeLoansSet.removeBytes32(loanLocal.id);\n lenderLoanSets[loanLocal.lender].removeBytes32(loanLocal.id);\n borrowerLoanSets[loanLocal.borrower].removeBytes32(loanLocal.id);\n } else {\n loanLocal.principal = loanLocal.principal.sub(loanCloseAmount);\n }\n }\n\n function _settleInterest(\n LoanParams memory loanParamsLocal,\n Loan memory loanLocal,\n uint256 closePrincipal\n ) internal returns (uint256) {\n // pay outstanding interest to lender\n _payInterest(loanLocal.lender, loanParamsLocal.loanToken);\n\n LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id];\n LenderInterest storage lenderInterestLocal =\n lenderInterest[loanLocal.lender][loanParamsLocal.loanToken];\n\n uint256 interestTime = block.timestamp;\n if (interestTime > loanLocal.endTimestamp) {\n interestTime = loanLocal.endTimestamp;\n }\n\n _settleFeeRewardForInterestExpense(\n loanInterestLocal,\n loanLocal.id,\n loanParamsLocal.loanToken, /// fee token\n loanParamsLocal.collateralToken, /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward\n loanLocal.borrower,\n interestTime\n );\n\n uint256 owedPerDayRefund;\n if (closePrincipal < loanLocal.principal) {\n owedPerDayRefund = loanInterestLocal.owedPerDay.mul(closePrincipal).div(\n loanLocal.principal\n );\n } else {\n owedPerDayRefund = loanInterestLocal.owedPerDay;\n }\n\n // update stored owedPerDay\n loanInterestLocal.owedPerDay = loanInterestLocal.owedPerDay.sub(owedPerDayRefund);\n lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.sub(owedPerDayRefund);\n\n // update borrower interest\n uint256 interestRefundToBorrower = loanLocal.endTimestamp.sub(interestTime);\n interestRefundToBorrower = interestRefundToBorrower.mul(owedPerDayRefund);\n interestRefundToBorrower = interestRefundToBorrower.div(1 days);\n\n if (closePrincipal < loanLocal.principal) {\n loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.sub(\n interestRefundToBorrower\n );\n } else {\n loanInterestLocal.depositTotal = 0;\n }\n\n // update remaining lender interest values\n lenderInterestLocal.principalTotal = lenderInterestLocal.principalTotal.sub(\n closePrincipal\n );\n\n uint256 owedTotal = lenderInterestLocal.owedTotal;\n lenderInterestLocal.owedTotal = owedTotal > interestRefundToBorrower\n ? owedTotal - interestRefundToBorrower\n : 0;\n\n return interestRefundToBorrower;\n }\n\n /**\n * @notice Check sender is borrower or delegatee and loan id exists.\n *\n * @param loanId byte32 of the loan id.\n * */\n function _checkAuthorized(bytes32 loanId) internal view {\n Loan storage loanLocal = loans[loanId];\n require(\n msg.sender == loanLocal.borrower || delegatedManagers[loanLocal.id][msg.sender],\n \"unauthorized\"\n );\n }\n\n /**\n * @notice Internal function for closing a position by swapping the\n * collateral back to loan tokens, paying the lender and withdrawing\n * the remainder.\n *\n * @param loanId The id of the loan.\n * @param receiver The receiver of the remainder (unused collatral + profit).\n * @param swapAmount Defines how much of the position should be closed and\n * is denominated in collateral tokens.\n * If swapAmount >= collateral, the complete position will be closed.\n * Else if returnTokenIsCollateral, (swapAmount/collateral) * principal will be swapped (partial closure).\n * Else coveredPrincipal\n * @param returnTokenIsCollateral Defines if the remainder should be paid\n * out in collateral tokens or underlying loan tokens.\n *\n * @return loanCloseAmount The amount of the collateral token of the loan.\n * @return withdrawAmount The withdraw amount in the collateral token.\n * @return withdrawToken The loan token address.\n * */\n function _closeWithSwap(\n bytes32 loanId,\n address receiver,\n uint256 swapAmount,\n bool returnTokenIsCollateral,\n bytes memory loanDataBytes\n )\n internal\n returns (\n uint256 loanCloseAmount,\n uint256 withdrawAmount,\n address withdrawToken\n )\n {\n require(swapAmount != 0, \"swapAmount == 0\");\n\n (Loan storage loanLocal, LoanParams storage loanParamsLocal) = _checkLoan(loanId);\n\n /// Can't swap more than collateral.\n swapAmount = swapAmount > loanLocal.collateral ? loanLocal.collateral : swapAmount;\n\n //close whole loan if tiny position will remain\n if (loanLocal.collateral - swapAmount > 0) {\n if (\n _getAmountInRbtc(\n loanParamsLocal.collateralToken,\n loanLocal.collateral - swapAmount\n ) <= TINY_AMOUNT\n ) {\n swapAmount = loanLocal.collateral;\n }\n }\n\n uint256 loanCloseAmountLessInterest;\n if (swapAmount == loanLocal.collateral || returnTokenIsCollateral) {\n /// loanCloseAmountLessInterest will be passed as required amount amount of destination tokens.\n /// this means, the actual swapAmount passed to the swap contract does not matter at all.\n /// the source token amount will be computed depending on the required amount amount of destination tokens.\n loanCloseAmount = swapAmount == loanLocal.collateral\n ? loanLocal.principal\n : loanLocal.principal.mul(swapAmount).div(loanLocal.collateral);\n require(loanCloseAmount != 0, \"loanCloseAmount == 0\");\n\n /// Computes the interest refund for the borrower and sends it to the lender to cover part of the principal.\n loanCloseAmountLessInterest = _settleInterestToPrincipal(\n loanLocal,\n loanParamsLocal,\n loanCloseAmount,\n receiver\n );\n } else {\n /// loanCloseAmount is calculated after swap; for this case we want to swap the entire source amount\n /// and determine the loanCloseAmount and withdraw amount based on that.\n loanCloseAmountLessInterest = 0;\n }\n\n uint256 coveredPrincipal;\n uint256 usedCollateral;\n\n /// swapAmount repurposed for collateralToLoanSwapRate to avoid stack too deep error.\n (coveredPrincipal, usedCollateral, withdrawAmount, swapAmount) = _coverPrincipalWithSwap(\n loanLocal,\n loanParamsLocal,\n swapAmount, /// The amount of source tokens to swap (only matters if !returnTokenIsCollateral or loanCloseAmountLessInterest = 0)\n loanCloseAmountLessInterest, /// This is the amount of destination tokens we want to receive (only matters if returnTokenIsCollateral)\n returnTokenIsCollateral,\n loanDataBytes\n );\n\n if (loanCloseAmountLessInterest == 0) {\n /// Condition prior to swap: swapAmount != loanLocal.collateral && !returnTokenIsCollateral\n\n /// Amounts that is closed.\n loanCloseAmount = coveredPrincipal;\n if (coveredPrincipal != loanLocal.principal) {\n loanCloseAmount = loanCloseAmount.mul(usedCollateral).div(loanLocal.collateral);\n }\n require(loanCloseAmount != 0, \"loanCloseAmount == 0\");\n\n /// Amount that is returned to the lender.\n loanCloseAmountLessInterest = _settleInterestToPrincipal(\n loanLocal,\n loanParamsLocal,\n loanCloseAmount,\n receiver\n );\n\n /// Remaining amount withdrawn to the receiver.\n withdrawAmount = withdrawAmount.add(coveredPrincipal).sub(loanCloseAmountLessInterest);\n } else {\n /// Pay back the amount which was covered by the swap.\n loanCloseAmountLessInterest = coveredPrincipal;\n }\n\n require(loanCloseAmountLessInterest != 0, \"closeAmount is 0 after swap\");\n\n /// Reduce the collateral by the amount which was swapped for the closure.\n if (usedCollateral != 0) {\n loanLocal.collateral = loanLocal.collateral.sub(usedCollateral);\n }\n\n /// Repays principal to lender.\n /// The lender always gets back an ERC20 (even wrbtc), so we call\n /// withdraw directly rather than use the _withdrawAsset helper function.\n vaultWithdraw(loanParamsLocal.loanToken, loanLocal.lender, loanCloseAmountLessInterest);\n\n withdrawToken = returnTokenIsCollateral\n ? loanParamsLocal.collateralToken\n : loanParamsLocal.loanToken;\n\n if (withdrawAmount != 0) {\n _withdrawAsset(withdrawToken, receiver, withdrawAmount);\n }\n\n _finalizeClose(\n loanLocal,\n loanParamsLocal,\n loanCloseAmount,\n usedCollateral,\n swapAmount, /// collateralToLoanSwapRate\n CloseTypes.Swap\n );\n }\n\n /**\n * @notice Close a loan.\n *\n * @dev Wrapper for _closeLoan internal function.\n *\n * @param loanLocal The loan object.\n * @param loanParamsLocal The loan params.\n * @param loanCloseAmount The amount to close: principal or lower.\n * @param collateralCloseAmount The amount of collateral to close.\n * @param collateralToLoanSwapRate The price rate collateral/loan token.\n * @param closeType The type of loan close.\n * */\n function _finalizeClose(\n Loan storage loanLocal,\n LoanParams storage loanParamsLocal,\n uint256 loanCloseAmount,\n uint256 collateralCloseAmount,\n uint256 collateralToLoanSwapRate,\n CloseTypes closeType\n ) internal {\n _closeLoan(loanLocal, loanCloseAmount);\n\n address _priceFeeds = priceFeeds;\n uint256 currentMargin;\n uint256 collateralToLoanRate;\n\n /// This is still called even with full loan close to return collateralToLoanRate\n (bool success, bytes memory data) =\n _priceFeeds.staticcall(\n abi.encodeWithSelector(\n IPriceFeeds(_priceFeeds).getCurrentMargin.selector,\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanLocal.principal,\n loanLocal.collateral\n )\n );\n assembly {\n if eq(success, 1) {\n currentMargin := mload(add(data, 32))\n collateralToLoanRate := mload(add(data, 64))\n }\n }\n /// Note: We can safely skip the margin check if closing\n /// via closeWithDeposit or if closing the loan in full by any method.\n require(\n closeType == CloseTypes.Deposit ||\n loanLocal.principal == 0 || /// loan fully closed\n currentMargin > loanParamsLocal.maintenanceMargin,\n \"unhealthy position\"\n );\n\n _emitClosingEvents(\n loanParamsLocal,\n loanLocal,\n loanCloseAmount,\n collateralCloseAmount,\n collateralToLoanRate,\n collateralToLoanSwapRate,\n currentMargin,\n closeType\n );\n }\n\n /**\n * swaps a share of a loan's collateral or the complete collateral in order to cover the principle.\n * @param loanLocal the loan\n * @param loanParamsLocal the loan parameters\n * @param swapAmount in case principalNeeded == 0 or !returnTokenIsCollateral, this is the amount which is going to be swapped.\n * Else, swapAmount doesn't matter, because the amount of source tokens needed for the swap is estimated by the connector.\n * @param principalNeeded the required amount of destination tokens in order to cover the principle (only used if returnTokenIsCollateral)\n * @param returnTokenIsCollateral tells if the user wants to withdraw his remaining collateral + profit in collateral tokens\n * @notice Swaps a share of a loan's collateral or the complete collateral\n * in order to cover the principle.\n *\n * @param loanLocal The loan object.\n * @param loanParamsLocal The loan parameters.\n * @param swapAmount In case principalNeeded == 0 or !returnTokenIsCollateral,\n * this is the amount which is going to be swapped.\n * Else, swapAmount doesn't matter, because the amount of source tokens\n * needed for the swap is estimated by the connector.\n * @param principalNeeded The required amount of destination tokens in order to\n * cover the principle (only used if returnTokenIsCollateral).\n * @param returnTokenIsCollateral Tells if the user wants to withdraw his\n * remaining collateral + profit in collateral tokens.\n *\n * @return coveredPrincipal The amount of principal that is covered.\n * @return usedCollateral The amount of collateral used.\n * @return withdrawAmount The withdraw amount in the collateral token.\n * @return collateralToLoanSwapRate The swap rate of collateral.\n * */\n function _coverPrincipalWithSwap(\n Loan memory loanLocal,\n LoanParams memory loanParamsLocal,\n uint256 swapAmount,\n uint256 principalNeeded,\n bool returnTokenIsCollateral,\n bytes memory loanDataBytes\n )\n internal\n returns (\n uint256 coveredPrincipal,\n uint256 usedCollateral,\n uint256 withdrawAmount,\n uint256 collateralToLoanSwapRate\n )\n {\n uint256 destTokenAmountReceived;\n uint256 sourceTokenAmountUsed;\n (\n destTokenAmountReceived,\n sourceTokenAmountUsed,\n collateralToLoanSwapRate\n ) = _doCollateralSwap(\n loanLocal,\n loanParamsLocal,\n swapAmount,\n principalNeeded,\n returnTokenIsCollateral,\n loanDataBytes\n );\n\n if (returnTokenIsCollateral) {\n coveredPrincipal = principalNeeded;\n\n /// Better fill than expected.\n if (destTokenAmountReceived > coveredPrincipal) {\n /// Send excess to borrower if the amount is big enough to be\n /// worth the gas fees.\n if (\n worthTheTransfer(\n loanParamsLocal.loanToken,\n destTokenAmountReceived - coveredPrincipal\n )\n ) {\n _withdrawAsset(\n loanParamsLocal.loanToken,\n loanLocal.borrower,\n destTokenAmountReceived - coveredPrincipal\n );\n }\n /// Else, give the excess to the lender (if it goes to the\n /// borrower, they're very confused. causes more trouble than it's worth)\n else {\n coveredPrincipal = destTokenAmountReceived;\n }\n }\n withdrawAmount = swapAmount > sourceTokenAmountUsed\n ? swapAmount - sourceTokenAmountUsed\n : 0;\n } else {\n require(sourceTokenAmountUsed == swapAmount, \"swap error\");\n\n if (swapAmount == loanLocal.collateral) {\n /// sourceTokenAmountUsed == swapAmount == loanLocal.collateral\n\n coveredPrincipal = principalNeeded;\n withdrawAmount = destTokenAmountReceived - principalNeeded;\n } else {\n /// sourceTokenAmountUsed == swapAmount < loanLocal.collateral\n\n if (destTokenAmountReceived >= loanLocal.principal) {\n /// Edge case where swap covers full principal.\n\n coveredPrincipal = loanLocal.principal;\n withdrawAmount = destTokenAmountReceived - loanLocal.principal;\n\n /// Excess collateral refunds to the borrower.\n _withdrawAsset(\n loanParamsLocal.collateralToken,\n loanLocal.borrower,\n loanLocal.collateral - sourceTokenAmountUsed\n );\n sourceTokenAmountUsed = loanLocal.collateral;\n } else {\n coveredPrincipal = destTokenAmountReceived;\n withdrawAmount = 0;\n }\n }\n }\n\n usedCollateral = sourceTokenAmountUsed > swapAmount ? sourceTokenAmountUsed : swapAmount;\n }\n\n function _emitClosingEvents(\n LoanParams memory loanParamsLocal,\n Loan memory loanLocal,\n uint256 loanCloseAmount,\n uint256 collateralCloseAmount,\n uint256 collateralToLoanRate,\n uint256 collateralToLoanSwapRate,\n uint256 currentMargin,\n CloseTypes closeType\n ) internal {\n if (closeType == CloseTypes.Deposit) {\n emit CloseWithDeposit(\n loanLocal.borrower, /// user (borrower)\n loanLocal.lender, /// lender\n loanLocal.id, /// loanId\n msg.sender, /// closer\n loanParamsLocal.loanToken, /// loanToken\n loanParamsLocal.collateralToken, /// collateralToken\n loanCloseAmount, /// loanCloseAmount\n collateralCloseAmount, /// collateralCloseAmount\n collateralToLoanRate, /// collateralToLoanRate\n currentMargin /// currentMargin\n );\n } else if (closeType == CloseTypes.Swap) {\n /// exitPrice = 1 / collateralToLoanSwapRate\n if (collateralToLoanSwapRate != 0) {\n collateralToLoanSwapRate = SafeMath.div(10**36, collateralToLoanSwapRate);\n }\n\n /// currentLeverage = 100 / currentMargin\n if (currentMargin != 0) {\n currentMargin = SafeMath.div(10**38, currentMargin);\n }\n\n emit CloseWithSwap(\n loanLocal.borrower, /// user (trader)\n loanLocal.lender, /// lender\n loanLocal.id, /// loanId\n loanParamsLocal.collateralToken, /// collateralToken\n loanParamsLocal.loanToken, /// loanToken\n msg.sender, /// closer\n collateralCloseAmount, /// positionCloseSize\n loanCloseAmount, /// loanCloseAmount\n collateralToLoanSwapRate, /// exitPrice (1 / collateralToLoanSwapRate)\n currentMargin /// currentLeverage\n );\n } else if (closeType == CloseTypes.Liquidation) {\n emit Liquidate(\n loanLocal.borrower, // user (borrower)\n msg.sender, // liquidator\n loanLocal.id, // loanId\n loanLocal.lender, // lender\n loanParamsLocal.loanToken, // loanToken\n loanParamsLocal.collateralToken, // collateralToken\n loanCloseAmount, // loanCloseAmount\n collateralCloseAmount, // collateralCloseAmount\n collateralToLoanRate, // collateralToLoanRate\n currentMargin // currentMargin\n );\n }\n }\n\n /**\n * @dev returns amount of the asset converted to RBTC\n * @param asset the asset to be transferred\n * @param amount the amount to be transferred\n * @return amount in RBTC\n * */\n function _getAmountInRbtc(address asset, uint256 amount) internal view returns (uint256) {\n (uint256 rbtcRate, uint256 rbtcPrecision) =\n IPriceFeeds(priceFeeds).queryRate(asset, address(wrbtcToken));\n return amount.mul(rbtcRate).div(rbtcPrecision);\n }\n\n /**\n * @dev private function which check the loanLocal & loanParamsLocal does exist\n *\n * @param loanId bytes32 of loanId\n *\n * @return Loan storage\n * @return LoanParams storage\n */\n function _checkLoan(bytes32 loanId) internal view returns (Loan storage, LoanParams storage) {\n Loan storage loanLocal = loans[loanId];\n LoanParams storage loanParamsLocal = loanParams[loanLocal.loanParamsId];\n\n require(loanLocal.active, \"loan is closed\");\n require(loanParamsLocal.id != 0, \"loanParams not exists\");\n\n return (loanLocal, loanParamsLocal);\n }\n}\n" + }, + "contracts/modules/LoanClosingsWith.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../interfaces/ILoanPool.sol\";\nimport \"./LoanClosingsShared.sol\";\n\n/**\n * @title LoanClosingsWith contract.\n * @notice Close a loan w/deposit, close w/swap. There are 2 functions for ending a loan on the\n * protocol contract: closeWithSwap and closeWithDeposit. Margin trade\n * positions are always closed with a swap.\n *\n * Loans are liquidated if the position goes below margin maintenance.\n * */\ncontract LoanClosingsWith is LoanClosingsShared {\n constructor() public {}\n\n function() external {\n revert(\"fallback not allowed\");\n }\n\n function initialize(address target) external onlyOwner {\n address prevModuleContractAddress = logicTargets[this.closeWithDeposit.selector];\n _setTarget(this.closeWithDeposit.selector, target);\n _setTarget(this.closeWithSwap.selector, target);\n _setTarget(this.checkCloseWithDepositIsTinyPosition.selector, target);\n emit ProtocolModuleContractReplaced(prevModuleContractAddress, target, \"LoanClosingsWith\");\n }\n\n /**\n * @notice Closes a loan by doing a deposit.\n *\n * @dev Public wrapper for _closeWithDeposit internal function.\n *\n * @param loanId The id of the loan.\n * @param receiver The receiver of the remainder.\n * @param depositAmount Defines how much of the position should be closed.\n * It is denominated in loan tokens. (e.g. rBTC on a iSUSD contract).\n * If depositAmount > principal, the complete loan will be closed\n * else deposit amount (partial closure).\n *\n * @return loanCloseAmount The amount of the collateral token of the loan.\n * @return withdrawAmount The withdraw amount in the collateral token.\n * @return withdrawToken The loan token address.\n * */\n function closeWithDeposit(\n bytes32 loanId,\n address receiver,\n uint256 depositAmount /// Denominated in loanToken.\n )\n public\n payable\n nonReentrant\n globallyNonReentrant\n iTokenSupplyUnchanged(loanId)\n whenNotPaused\n returns (\n uint256 loanCloseAmount,\n uint256 withdrawAmount,\n address withdrawToken\n )\n {\n _checkAuthorized(loanId);\n return _closeWithDeposit(loanId, receiver, depositAmount);\n }\n\n /**\n * @notice Close a position by swapping the collateral back to loan tokens\n * paying the lender and withdrawing the remainder.\n *\n * @dev Public wrapper for _closeWithSwap internal function.\n *\n * @param loanId The id of the loan.\n * @param receiver The receiver of the remainder (unused collateral + profit).\n * @param swapAmount Defines how much of the position should be closed and\n * is denominated in collateral tokens.\n * If swapAmount >= collateral, the complete position will be closed.\n * Else if returnTokenIsCollateral, (swapAmount/collateral) * principal will be swapped (partial closure).\n * Else coveredPrincipal\n * @param returnTokenIsCollateral Defines if the remainder should be paid out\n * in collateral tokens or underlying loan tokens.\n *\n * @return loanCloseAmount The amount of the collateral token of the loan.\n * @return withdrawAmount The withdraw amount in the collateral token.\n * @return withdrawToken The loan token address.\n * */\n function closeWithSwap(\n bytes32 loanId,\n address receiver,\n uint256 swapAmount, // denominated in collateralToken\n bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken\n bytes memory // for future use /*loanDataBytes*/\n )\n public\n nonReentrant\n globallyNonReentrant\n iTokenSupplyUnchanged(loanId)\n whenNotPaused\n returns (\n uint256 loanCloseAmount,\n uint256 withdrawAmount,\n address withdrawToken\n )\n {\n _checkAuthorized(loanId);\n return\n _closeWithSwap(\n loanId,\n receiver,\n swapAmount,\n returnTokenIsCollateral,\n \"\" /// loanDataBytes\n );\n }\n\n /**\n * @notice Internal function for closing a loan by doing a deposit.\n *\n * @param loanId The id of the loan.\n * @param receiver The receiver of the remainder.\n * @param depositAmount Defines how much of the position should be closed.\n * It is denominated in loan tokens.\n * If depositAmount > principal, the complete loan will be closed\n * else deposit amount (partial closure).\n *\n * @return loanCloseAmount The amount of the collateral token of the loan.\n * @return withdrawAmount The withdraw amount in the collateral token.\n * @return withdrawToken The loan token address.\n * */\n function _closeWithDeposit(\n bytes32 loanId,\n address receiver,\n uint256 depositAmount /// Denominated in loanToken.\n )\n internal\n returns (\n uint256 loanCloseAmount,\n uint256 withdrawAmount,\n address withdrawToken\n )\n {\n require(depositAmount != 0, \"depositAmount == 0\");\n\n //TODO should we skip this check if invoked from rollover ?\n (Loan storage loanLocal, LoanParams storage loanParamsLocal) = _checkLoan(loanId);\n\n /// Can't close more than the full principal.\n loanCloseAmount = depositAmount > loanLocal.principal\n ? loanLocal.principal\n : depositAmount;\n\n //revert if tiny position remains\n uint256 remainingAmount = loanLocal.principal - loanCloseAmount;\n if (remainingAmount > 0) {\n require(\n _getAmountInRbtc(loanParamsLocal.loanToken, remainingAmount) > TINY_AMOUNT,\n \"Tiny amount when closing with deposit\"\n );\n }\n\n uint256 loanCloseAmountLessInterest =\n _settleInterestToPrincipal(loanLocal, loanParamsLocal, loanCloseAmount, receiver);\n\n if (loanCloseAmountLessInterest != 0) {\n _returnPrincipalWithDeposit(\n loanParamsLocal.loanToken,\n loanLocal.lender,\n loanCloseAmountLessInterest\n );\n }\n\n if (loanCloseAmount == loanLocal.principal) {\n withdrawAmount = loanLocal.collateral;\n } else {\n withdrawAmount = loanLocal.collateral.mul(loanCloseAmount).div(loanLocal.principal);\n }\n\n withdrawToken = loanParamsLocal.collateralToken;\n\n if (withdrawAmount != 0) {\n loanLocal.collateral = loanLocal.collateral.sub(withdrawAmount);\n _withdrawAsset(withdrawToken, receiver, withdrawAmount);\n }\n\n _finalizeClose(\n loanLocal,\n loanParamsLocal,\n loanCloseAmount,\n withdrawAmount, /// collateralCloseAmount\n 0, /// collateralToLoanSwapRate\n CloseTypes.Deposit\n );\n }\n\n /**\n * @notice Function to check whether the given loanId & deposit amount when closing with deposit will cause the tiny position\n *\n * @param loanId The id of the loan.\n * @param depositAmount Defines how much the deposit amount to close the position.\n *\n * @return isTinyPosition true is indicating tiny position, false otherwise.\n * @return tinyPositionAmount will return 0 for non tiny position, and will return the amount of tiny position if true\n */\n function checkCloseWithDepositIsTinyPosition(bytes32 loanId, uint256 depositAmount)\n external\n view\n returns (bool isTinyPosition, uint256 tinyPositionAmount)\n {\n (Loan memory loanLocal, LoanParams memory loanParamsLocal) = _checkLoan(loanId);\n\n if (depositAmount < loanLocal.principal) {\n uint256 remainingAmount = loanLocal.principal - depositAmount;\n uint256 remainingRBTCAmount =\n _getAmountInRbtc(loanParamsLocal.loanToken, remainingAmount);\n if (remainingRBTCAmount < TINY_AMOUNT) {\n isTinyPosition = true;\n tinyPositionAmount = remainingRBTCAmount;\n }\n }\n\n return (isTinyPosition, tinyPositionAmount);\n }\n}\n" + }, + "contracts/modules/LoanMaintenance.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../core/State.sol\";\nimport \"../events/LoanOpeningsEvents.sol\";\nimport \"../events/LoanMaintenanceEvents.sol\";\nimport \"../mixins/VaultController.sol\";\nimport \"../mixins/InterestUser.sol\";\nimport \"../mixins/LiquidationHelper.sol\";\nimport \"../swaps/SwapsUser.sol\";\nimport \"../mixins/ModuleCommonFunctionalities.sol\";\n\n/**\n * @title Loan Maintenance contract.\n *\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains functions to query loan data and to modify its status\n * by withdrawing or depositing collateral.\n * */\ncontract LoanMaintenance is\n LoanOpeningsEvents,\n LoanMaintenanceEvents,\n VaultController,\n InterestUser,\n SwapsUser,\n LiquidationHelper,\n ModuleCommonFunctionalities\n{\n // Keep the old LoanReturnData for backward compatibility (especially for the watcher)\n struct LoanReturnData {\n bytes32 loanId;\n address loanToken;\n address collateralToken;\n uint256 principal;\n uint256 collateral;\n uint256 interestOwedPerDay;\n uint256 interestDepositRemaining;\n uint256 startRate; /// collateralToLoanRate\n uint256 startMargin;\n uint256 maintenanceMargin;\n uint256 currentMargin;\n uint256 maxLoanTerm;\n uint256 endTimestamp;\n uint256 maxLiquidatable;\n uint256 maxSeizable;\n }\n\n // The new struct which contained borrower & creation time of a loan\n struct LoanReturnDataV2 {\n bytes32 loanId;\n address loanToken;\n address collateralToken;\n address borrower;\n uint256 principal;\n uint256 collateral;\n uint256 interestOwedPerDay;\n uint256 interestDepositRemaining;\n uint256 startRate; /// collateralToLoanRate\n uint256 startMargin;\n uint256 maintenanceMargin;\n uint256 currentMargin;\n uint256 maxLoanTerm;\n uint256 endTimestamp;\n uint256 maxLiquidatable;\n uint256 maxSeizable;\n uint256 creationTimestamp;\n }\n\n /**\n * @notice Empty public constructor.\n * */\n constructor() public {}\n\n /**\n * @notice Fallback function is to react to receiving value (rBTC).\n * */\n function() external {\n revert(\"fallback not allowed\");\n }\n\n /**\n * @notice Set initial values of proxy targets.\n *\n * @param target The address of the logic contract instance.\n * */\n function initialize(address target) external onlyOwner {\n address prevModuleContractAddress = logicTargets[this.depositCollateral.selector];\n _setTarget(this.depositCollateral.selector, target);\n _setTarget(this.withdrawCollateral.selector, target);\n _setTarget(this.withdrawAccruedInterest.selector, target);\n _setTarget(this.extendLoanDuration.selector, target);\n _setTarget(this.reduceLoanDuration.selector, target);\n _setTarget(this.getLenderInterestData.selector, target);\n _setTarget(this.getLoanInterestData.selector, target);\n _setTarget(this.getUserLoans.selector, target);\n _setTarget(this.getUserLoansV2.selector, target);\n _setTarget(this.getLoan.selector, target);\n _setTarget(this.getLoanV2.selector, target);\n _setTarget(this.getActiveLoans.selector, target);\n _setTarget(this.getActiveLoansV2.selector, target);\n emit ProtocolModuleContractReplaced(prevModuleContractAddress, target, \"LoanMaintenance\");\n }\n\n /**\n * @notice Increase the margin of a position by depositing additional collateral.\n *\n * @param loanId A unique ID representing the loan.\n * @param depositAmount The amount to be deposited in collateral tokens.\n *\n * @return actualWithdrawAmount The amount withdrawn taking into account drawdowns.\n * */\n function depositCollateral(\n bytes32 loanId,\n uint256 depositAmount /// must match msg.value if ether is sent\n ) external payable nonReentrant whenNotPaused {\n require(depositAmount != 0, \"depositAmount is 0\");\n Loan storage loanLocal = loans[loanId];\n LoanParams storage loanParamsLocal = loanParams[loanLocal.loanParamsId];\n\n require(loanLocal.active, \"loan is closed\");\n require(\n msg.value == 0 || loanParamsLocal.collateralToken == address(wrbtcToken),\n \"wrong asset sent\"\n );\n\n loanLocal.collateral = loanLocal.collateral.add(depositAmount);\n\n if (msg.value == 0) {\n vaultDeposit(loanParamsLocal.collateralToken, msg.sender, depositAmount);\n } else {\n require(msg.value == depositAmount, \"ether deposit mismatch\");\n vaultEtherDeposit(msg.sender, msg.value);\n }\n\n (uint256 collateralToLoanRate, ) =\n IPriceFeeds(priceFeeds).queryRate(\n loanParamsLocal.collateralToken,\n loanParamsLocal.loanToken\n );\n\n emit DepositCollateral(loanId, depositAmount, collateralToLoanRate);\n }\n\n /**\n * @notice Withdraw from the collateral. This reduces the margin of a position.\n *\n * @param loanId A unique ID representing the loan.\n * @param receiver The account getting the withdrawal.\n * @param withdrawAmount The amount to be withdrawn in collateral tokens.\n *\n * @return actualWithdrawAmount The amount withdrawn taking into account drawdowns.\n * */\n function withdrawCollateral(\n bytes32 loanId,\n address receiver,\n uint256 withdrawAmount\n ) external nonReentrant whenNotPaused returns (uint256 actualWithdrawAmount) {\n require(withdrawAmount != 0, \"withdrawAmount is 0\");\n Loan storage loanLocal = loans[loanId];\n LoanParams storage loanParamsLocal = loanParams[loanLocal.loanParamsId];\n\n require(loanLocal.active, \"loan is closed\");\n require(\n msg.sender == loanLocal.borrower || delegatedManagers[loanLocal.id][msg.sender],\n \"unauthorized\"\n );\n\n uint256 maxDrawdown =\n IPriceFeeds(priceFeeds).getMaxDrawdown(\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanLocal.principal,\n loanLocal.collateral,\n loanParamsLocal.maintenanceMargin\n );\n\n if (withdrawAmount > maxDrawdown) {\n actualWithdrawAmount = maxDrawdown;\n } else {\n actualWithdrawAmount = withdrawAmount;\n }\n\n loanLocal.collateral = loanLocal.collateral.sub(actualWithdrawAmount);\n\n if (loanParamsLocal.collateralToken == address(wrbtcToken)) {\n vaultEtherWithdraw(receiver, actualWithdrawAmount);\n } else {\n vaultWithdraw(loanParamsLocal.collateralToken, receiver, actualWithdrawAmount);\n }\n }\n\n /**\n * @notice Withdraw accrued loan interest.\n *\n * @dev Wrapper for _payInterest internal function.\n *\n * @param loanToken The loan token address.\n * */\n function withdrawAccruedInterest(address loanToken) external whenNotPaused {\n /// Pay outstanding interest to lender.\n _payInterest(\n msg.sender, /// Lender.\n loanToken\n );\n }\n\n /**\n * @notice Extend the loan duration by as much time as depositAmount can buy.\n *\n * @param loanId A unique ID representing the loan.\n * @param depositAmount The amount to be deposited in loan tokens. Used to pay the interest for the new duration.\n * @param useCollateral Whether pay interests w/ the collateral. If true, depositAmount of loan tokens\n *\t\t\t\t\t\twill be purchased with the collateral.\n * // param calldata The payload for the call. These loan DataBytes are additional loan data (not in use for token swaps).\n *\n * @return secondsExtended The amount of time in seconds the loan is extended.\n * */\n function extendLoanDuration(\n bytes32 loanId,\n uint256 depositAmount,\n bool useCollateral,\n bytes calldata /// loanDataBytes, for future use.\n ) external payable nonReentrant whenNotPaused returns (uint256 secondsExtended) {\n require(depositAmount != 0, \"depositAmount is 0\");\n Loan storage loanLocal = loans[loanId];\n LoanParams storage loanParamsLocal = loanParams[loanLocal.loanParamsId];\n\n require(loanLocal.active, \"loan is closed\");\n require(\n !useCollateral ||\n msg.sender == loanLocal.borrower ||\n delegatedManagers[loanLocal.id][msg.sender],\n \"unauthorized\"\n );\n require(loanParamsLocal.maxLoanTerm == 0, \"indefinite-term only\");\n require(\n msg.value == 0 || (!useCollateral && loanParamsLocal.loanToken == address(wrbtcToken)),\n \"wrong asset sent\"\n );\n\n /// Pay outstanding interest to lender.\n _payInterest(loanLocal.lender, loanParamsLocal.loanToken);\n\n LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id];\n\n _settleFeeRewardForInterestExpense(\n loanInterestLocal,\n loanLocal.id,\n loanParamsLocal.loanToken, /// fee token\n loanParamsLocal.collateralToken, /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward\n loanLocal.borrower,\n block.timestamp\n );\n\n /// Handle back interest: calculates interest owned since the loan\n /// endtime passed but the loan remained open.\n uint256 backInterestOwed;\n if (block.timestamp > loanLocal.endTimestamp) {\n backInterestOwed = block.timestamp.sub(loanLocal.endTimestamp);\n backInterestOwed = backInterestOwed.mul(loanInterestLocal.owedPerDay);\n backInterestOwed = backInterestOwed.div(86400);\n\n require(depositAmount > backInterestOwed, \"deposit cannot cover back interest\");\n }\n\n /// Deposit interest.\n if (useCollateral) {\n /// Used the whole converted loanToken to extend the loan duration\n depositAmount = _doCollateralSwap(loanLocal, loanParamsLocal, depositAmount);\n } else {\n if (msg.value == 0) {\n vaultDeposit(loanParamsLocal.loanToken, msg.sender, depositAmount);\n } else {\n require(msg.value == depositAmount, \"ether deposit mismatch\");\n vaultEtherDeposit(msg.sender, msg.value);\n }\n }\n\n if (backInterestOwed != 0) {\n depositAmount = depositAmount.sub(backInterestOwed);\n\n /// Pay out backInterestOwed\n _payInterestTransfer(loanLocal.lender, loanParamsLocal.loanToken, backInterestOwed);\n }\n\n secondsExtended = depositAmount.mul(86400).div(loanInterestLocal.owedPerDay);\n\n loanLocal.endTimestamp = loanLocal.endTimestamp.add(secondsExtended);\n\n require(loanLocal.endTimestamp > block.timestamp, \"loan too short\");\n\n uint256 maxDuration = loanLocal.endTimestamp.sub(block.timestamp);\n\n /// Loan term has to at least be greater than one hour.\n require(maxDuration > 3600, \"loan too short\");\n\n loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.add(depositAmount);\n\n lenderInterest[loanLocal.lender][loanParamsLocal.loanToken].owedTotal = lenderInterest[\n loanLocal.lender\n ][loanParamsLocal.loanToken]\n .owedTotal\n .add(depositAmount);\n }\n\n /**\n * @notice Reduce the loan duration by withdrawing from the deposited interest.\n *\n * @param loanId A unique ID representing the loan.\n * @param receiver The account getting the withdrawal.\n * @param withdrawAmount The amount to be withdrawn in loan tokens.\n *\n * @return secondsReduced The amount of time in seconds the loan is reduced.\n * */\n function reduceLoanDuration(\n bytes32 loanId,\n address receiver,\n uint256 withdrawAmount\n ) external nonReentrant whenNotPaused returns (uint256 secondsReduced) {\n require(withdrawAmount != 0, \"withdrawAmount is 0\");\n Loan storage loanLocal = loans[loanId];\n LoanParams storage loanParamsLocal = loanParams[loanLocal.loanParamsId];\n\n require(loanLocal.active, \"loan is closed\");\n require(\n msg.sender == loanLocal.borrower || delegatedManagers[loanLocal.id][msg.sender],\n \"unauthorized\"\n );\n require(loanParamsLocal.maxLoanTerm == 0, \"indefinite-term only\");\n require(loanLocal.endTimestamp > block.timestamp, \"loan term has ended\");\n\n /// Pay outstanding interest to lender.\n _payInterest(loanLocal.lender, loanParamsLocal.loanToken);\n\n LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id];\n\n _settleFeeRewardForInterestExpense(\n loanInterestLocal,\n loanLocal.id,\n loanParamsLocal.loanToken, /// fee token\n loanParamsLocal.collateralToken, /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward\n loanLocal.borrower,\n block.timestamp\n );\n\n uint256 interestDepositRemaining =\n loanLocal.endTimestamp.sub(block.timestamp).mul(loanInterestLocal.owedPerDay).div(\n 86400\n );\n require(withdrawAmount < interestDepositRemaining, \"withdraw amount too high\");\n\n /// Withdraw interest.\n if (loanParamsLocal.loanToken == address(wrbtcToken)) {\n vaultEtherWithdraw(receiver, withdrawAmount);\n } else {\n vaultWithdraw(loanParamsLocal.loanToken, receiver, withdrawAmount);\n }\n\n secondsReduced = withdrawAmount.mul(86400).div(loanInterestLocal.owedPerDay);\n\n require(loanLocal.endTimestamp > secondsReduced, \"loan too short\");\n\n loanLocal.endTimestamp = loanLocal.endTimestamp.sub(secondsReduced);\n\n require(loanLocal.endTimestamp > block.timestamp, \"loan too short\");\n\n uint256 maxDuration = loanLocal.endTimestamp.sub(block.timestamp);\n\n /// Loan term has to at least be greater than one hour.\n require(maxDuration > 3600, \"loan too short\");\n\n loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.sub(withdrawAmount);\n\n lenderInterest[loanLocal.lender][loanParamsLocal.loanToken].owedTotal = lenderInterest[\n loanLocal.lender\n ][loanParamsLocal.loanToken]\n .owedTotal\n .sub(withdrawAmount);\n }\n\n /**\n * @notice Get current lender interest data totals for all loans\n * with a specific oracle and interest token.\n *\n * @param lender The lender address.\n * @param loanToken The loan token address.\n *\n * @return interestPaid The total amount of interest that has been paid to a lender so far.\n * @return interestPaidDate The date of the last interest pay out, or 0 if no interest has been withdrawn yet.\n * @return interestOwedPerDay The amount of interest the lender is earning per day.\n * @return interestUnPaid The total amount of interest the lender is owned and not yet withdrawn.\n * @return interestFeePercent The fee retained by the protocol before interest is paid to the lender.\n * @return principalTotal The total amount of outstanding principal the lender has loaned.\n * */\n function getLenderInterestData(address lender, address loanToken)\n external\n view\n returns (\n uint256 interestPaid,\n uint256 interestPaidDate,\n uint256 interestOwedPerDay,\n uint256 interestUnPaid,\n uint256 interestFeePercent,\n uint256 principalTotal\n )\n {\n LenderInterest memory lenderInterestLocal = lenderInterest[lender][loanToken];\n\n interestUnPaid = block\n .timestamp\n .sub(lenderInterestLocal.updatedTimestamp)\n .mul(lenderInterestLocal.owedPerDay)\n .div(86400);\n if (interestUnPaid > lenderInterestLocal.owedTotal)\n interestUnPaid = lenderInterestLocal.owedTotal;\n\n return (\n lenderInterestLocal.paidTotal,\n lenderInterestLocal.paidTotal != 0 ? lenderInterestLocal.updatedTimestamp : 0,\n lenderInterestLocal.owedPerDay,\n lenderInterestLocal.updatedTimestamp != 0 ? interestUnPaid : 0,\n lendingFeePercent,\n lenderInterestLocal.principalTotal\n );\n }\n\n /**\n * @notice Get current interest data for a loan.\n *\n * @param loanId A unique ID representing the loan.\n *\n * @return loanToken The loan token that interest is paid in.\n * @return interestOwedPerDay The amount of interest the borrower is paying per day.\n * @return interestDepositTotal The total amount of interest the borrower has deposited.\n * @return interestDepositRemaining The amount of deposited interest that is not yet owed to a lender.\n * */\n function getLoanInterestData(bytes32 loanId)\n external\n view\n returns (\n address loanToken,\n uint256 interestOwedPerDay,\n uint256 interestDepositTotal,\n uint256 interestDepositRemaining\n )\n {\n loanToken = loanParams[loans[loanId].loanParamsId].loanToken;\n interestOwedPerDay = loanInterest[loanId].owedPerDay;\n interestDepositTotal = loanInterest[loanId].depositTotal;\n\n uint256 endTimestamp = loans[loanId].endTimestamp;\n uint256 interestTime = block.timestamp > endTimestamp ? endTimestamp : block.timestamp;\n interestDepositRemaining = endTimestamp > interestTime\n ? endTimestamp.sub(interestTime).mul(interestOwedPerDay).div(86400)\n : 0;\n }\n\n /**\n * @notice Get all user loans.\n *\n * Only returns data for loans that are active.\n *\n * @param user The user address.\n * @param start The lower loan ID to start with.\n * @param count The maximum number of results.\n * @param loanType The type of loan.\n * loanType 0: all loans.\n * loanType 1: margin trade loans.\n * loanType 2: non-margin trade loans.\n * @param isLender Whether the user is lender or borrower.\n * @param unsafeOnly The safe filter (True/False).\n *\n * @return loansData The array of loans as query result.\n * */\n function getUserLoans(\n address user,\n uint256 start,\n uint256 count,\n uint256 loanType,\n bool isLender,\n bool unsafeOnly\n ) external view returns (LoanReturnData[] memory loansData) {\n EnumerableBytes32Set.Bytes32Set storage set =\n isLender ? lenderLoanSets[user] : borrowerLoanSets[user];\n\n uint256 end = start.add(count).min256(set.length());\n if (start >= end) {\n return loansData;\n }\n\n loansData = new LoanReturnData[](count);\n uint256 itemCount;\n for (uint256 i = end - start; i > 0; i--) {\n if (itemCount == count) {\n break;\n }\n LoanReturnData memory loanData =\n _getLoan(\n set.get(i + start - 1), /// loanId\n loanType,\n unsafeOnly\n );\n if (loanData.loanId == 0) continue;\n\n loansData[itemCount] = loanData;\n itemCount++;\n }\n\n if (itemCount < count) {\n assembly {\n mstore(loansData, itemCount)\n }\n }\n }\n\n /**\n * @notice Get all user loans.\n *\n * Only returns data for loans that are active.\n *\n * @param user The user address.\n * @param start The lower loan ID to start with.\n * @param count The maximum number of results.\n * @param loanType The type of loan.\n * loanType 0: all loans.\n * loanType 1: margin trade loans.\n * loanType 2: non-margin trade loans.\n * @param isLender Whether the user is lender or borrower.\n * @param unsafeOnly The safe filter (True/False).\n *\n * @return loansData The array of loans as query result.\n * */\n function getUserLoansV2(\n address user,\n uint256 start,\n uint256 count,\n uint256 loanType,\n bool isLender,\n bool unsafeOnly\n ) external view returns (LoanReturnDataV2[] memory loansDataV2) {\n EnumerableBytes32Set.Bytes32Set storage set =\n isLender ? lenderLoanSets[user] : borrowerLoanSets[user];\n\n uint256 end = start.add(count).min256(set.length());\n if (start >= end) {\n return loansDataV2;\n }\n\n loansDataV2 = new LoanReturnDataV2[](count);\n uint256 itemCount;\n for (uint256 i = end - start; i > 0; i--) {\n if (itemCount == count) {\n break;\n }\n LoanReturnDataV2 memory loanDataV2 =\n _getLoanV2(\n set.get(i + start - 1), /// loanId\n loanType,\n unsafeOnly\n );\n if (loanDataV2.loanId == 0) continue;\n\n loansDataV2[itemCount] = loanDataV2;\n itemCount++;\n }\n\n if (itemCount < count) {\n assembly {\n mstore(loansDataV2, itemCount)\n }\n }\n }\n\n /**\n * @notice Get one loan data structure by matching ID.\n *\n * Wrapper to internal _getLoan call.\n *\n * @param loanId A unique ID representing the loan.\n *\n * @return loansData The data structure w/ loan information.\n * */\n function getLoan(bytes32 loanId) external view returns (LoanReturnData memory loanData) {\n return\n _getLoan(\n loanId,\n 0, /// loanType\n false /// unsafeOnly\n );\n }\n\n /**\n * @notice Get one loan data structure by matching ID.\n *\n * Wrapper to internal _getLoan call.\n *\n * @param loanId A unique ID representing the loan.\n *\n * @return loansData The data structure w/ loan information.\n * */\n function getLoanV2(bytes32 loanId) external view returns (LoanReturnDataV2 memory loanDataV2) {\n return\n _getLoanV2(\n loanId,\n 0, /// loanType\n false /// unsafeOnly\n );\n }\n\n /**\n * @notice Get all active loans.\n *\n * @param start The lower loan ID to start with.\n * @param count The maximum number of results.\n * @param unsafeOnly The safe filter (True/False).\n *\n * @return loansData The data structure w/ loan information.\n * */\n function getActiveLoans(\n uint256 start,\n uint256 count,\n bool unsafeOnly\n ) external view returns (LoanReturnData[] memory loansData) {\n uint256 end = start.add(count).min256(activeLoansSet.length());\n if (start >= end) {\n return loansData;\n }\n\n loansData = new LoanReturnData[](count);\n uint256 itemCount;\n for (uint256 i = end - start; i > 0; i--) {\n if (itemCount == count) {\n break;\n }\n LoanReturnData memory loanData =\n _getLoan(\n activeLoansSet.get(i + start - 1), /// loanId\n 0, /// loanType\n unsafeOnly\n );\n if (loanData.loanId == 0) continue;\n\n loansData[itemCount] = loanData;\n itemCount++;\n }\n\n if (itemCount < count) {\n assembly {\n mstore(loansData, itemCount)\n }\n }\n }\n\n /**\n * @dev New view function which will return the loan data.\n * @dev This function was created to support backward compatibility\n * @dev As in we the old getActiveLoans function is not expected to be changed by the wathcers.\n *\n * @param start The lower loan ID to start with.\n * @param count The maximum number of results.\n * @param unsafeOnly The safe filter (True/False).\n *\n * @return loanData The data structure\n * @return extendedLoanData The data structure which contained (borrower & creation time)\n */\n function getActiveLoansV2(\n uint256 start,\n uint256 count,\n bool unsafeOnly\n ) external view returns (LoanReturnDataV2[] memory loansDataV2) {\n uint256 end = start.add(count).min256(activeLoansSet.length());\n if (start >= end) {\n return loansDataV2;\n }\n\n loansDataV2 = new LoanReturnDataV2[](count);\n uint256 itemCount;\n for (uint256 i = end - start; i > 0; i--) {\n if (itemCount == count) {\n break;\n }\n LoanReturnDataV2 memory loanDataV2 =\n _getLoanV2(\n activeLoansSet.get(i + start - 1), /// loanId\n 0, /// loanType\n unsafeOnly\n );\n if (loanDataV2.loanId == 0) continue;\n\n loansDataV2[itemCount] = loanDataV2;\n itemCount++;\n }\n\n if (itemCount < count) {\n assembly {\n mstore(loansDataV2, itemCount)\n }\n }\n }\n\n /**\n * @notice Internal function to get one loan data structure.\n *\n * @param loanId A unique ID representing the loan.\n * @param loanType The type of loan.\n * loanType 0: all loans.\n * loanType 1: margin trade loans.\n * loanType 2: non-margin trade loans.\n * @param unsafeOnly The safe filter (True/False).\n *\n * @return loansData The data structure w/ the loan information.\n * */\n function _getLoan(\n bytes32 loanId,\n uint256 loanType,\n bool unsafeOnly\n ) internal view returns (LoanReturnData memory loanData) {\n Loan memory loanLocal = loans[loanId];\n LoanParams memory loanParamsLocal = loanParams[loanLocal.loanParamsId];\n\n if (loanType != 0) {\n if (\n !((loanType == 1 && loanParamsLocal.maxLoanTerm != 0) ||\n (loanType == 2 && loanParamsLocal.maxLoanTerm == 0))\n ) {\n return loanData;\n }\n }\n\n LoanInterest memory loanInterestLocal = loanInterest[loanId];\n\n (uint256 currentMargin, uint256 collateralToLoanRate) =\n IPriceFeeds(priceFeeds).getCurrentMargin(\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanLocal.principal,\n loanLocal.collateral\n );\n\n uint256 maxLiquidatable;\n uint256 maxSeizable;\n if (currentMargin <= loanParamsLocal.maintenanceMargin) {\n (maxLiquidatable, maxSeizable, ) = _getLiquidationAmounts(\n loanLocal.principal,\n loanLocal.collateral,\n currentMargin,\n loanParamsLocal.maintenanceMargin,\n collateralToLoanRate\n );\n } else if (unsafeOnly) {\n return loanData;\n }\n\n return\n LoanReturnData({\n loanId: loanId,\n loanToken: loanParamsLocal.loanToken,\n collateralToken: loanParamsLocal.collateralToken,\n principal: loanLocal.principal,\n collateral: loanLocal.collateral,\n interestOwedPerDay: loanInterestLocal.owedPerDay,\n interestDepositRemaining: loanLocal.endTimestamp >= block.timestamp\n ? loanLocal\n .endTimestamp\n .sub(block.timestamp)\n .mul(loanInterestLocal.owedPerDay)\n .div(86400)\n : 0,\n startRate: loanLocal.startRate,\n startMargin: loanLocal.startMargin,\n maintenanceMargin: loanParamsLocal.maintenanceMargin,\n currentMargin: currentMargin,\n maxLoanTerm: loanParamsLocal.maxLoanTerm,\n endTimestamp: loanLocal.endTimestamp,\n maxLiquidatable: maxLiquidatable,\n maxSeizable: maxSeizable\n });\n }\n\n /**\n * @notice Internal function to get one loan data structure v2.\n *\n * @param loanId A unique ID representing the loan.\n * @param loanType The type of loan.\n * loanType 0: all loans.\n * loanType 1: margin trade loans.\n * loanType 2: non-margin trade loans.\n * @param unsafeOnly The safe filter (True/False).\n *\n * @return loansData The data v2 structure w/ the loan information.\n * */\n function _getLoanV2(\n bytes32 loanId,\n uint256 loanType,\n bool unsafeOnly\n ) internal view returns (LoanReturnDataV2 memory loanDataV2) {\n Loan memory loanLocal = loans[loanId];\n LoanParams memory loanParamsLocal = loanParams[loanLocal.loanParamsId];\n\n if (loanType != 0) {\n if (\n !((loanType == 1 && loanParamsLocal.maxLoanTerm != 0) ||\n (loanType == 2 && loanParamsLocal.maxLoanTerm == 0))\n ) {\n return loanDataV2;\n }\n }\n\n LoanInterest memory loanInterestLocal = loanInterest[loanId];\n\n (uint256 currentMargin, uint256 collateralToLoanRate) =\n IPriceFeeds(priceFeeds).getCurrentMargin(\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanLocal.principal,\n loanLocal.collateral\n );\n\n uint256 maxLiquidatable;\n uint256 maxSeizable;\n if (currentMargin <= loanParamsLocal.maintenanceMargin) {\n (maxLiquidatable, maxSeizable, ) = _getLiquidationAmounts(\n loanLocal.principal,\n loanLocal.collateral,\n currentMargin,\n loanParamsLocal.maintenanceMargin,\n collateralToLoanRate\n );\n } else if (unsafeOnly) {\n return loanDataV2;\n }\n\n return\n LoanReturnDataV2({\n loanId: loanId,\n loanToken: loanParamsLocal.loanToken,\n collateralToken: loanParamsLocal.collateralToken,\n borrower: loanLocal.borrower,\n principal: loanLocal.principal,\n collateral: loanLocal.collateral,\n interestOwedPerDay: loanInterestLocal.owedPerDay,\n interestDepositRemaining: loanLocal.endTimestamp >= block.timestamp\n ? loanLocal\n .endTimestamp\n .sub(block.timestamp)\n .mul(loanInterestLocal.owedPerDay)\n .div(86400)\n : 0,\n startRate: loanLocal.startRate,\n startMargin: loanLocal.startMargin,\n maintenanceMargin: loanParamsLocal.maintenanceMargin,\n currentMargin: currentMargin,\n maxLoanTerm: loanParamsLocal.maxLoanTerm,\n endTimestamp: loanLocal.endTimestamp,\n maxLiquidatable: maxLiquidatable,\n maxSeizable: maxSeizable,\n creationTimestamp: loanLocal.startTimestamp\n });\n }\n\n /**\n * @notice Internal function to collect interest from the collateral.\n *\n * @param loanLocal The loan object.\n * @param loanParamsLocal The loan parameters.\n * @param depositAmount The amount of underlying tokens provided on the loan.\n * */\n function _doCollateralSwap(\n Loan storage loanLocal,\n LoanParams memory loanParamsLocal,\n uint256 depositAmount\n ) internal returns (uint256 purchasedLoanToken) {\n /// Reverts in _loanSwap if amountNeeded can't be bought.\n (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed, ) =\n _loanSwap(\n loanLocal.id,\n loanParamsLocal.collateralToken,\n loanParamsLocal.loanToken,\n loanLocal.borrower,\n loanLocal.collateral, /// minSourceTokenAmount\n 0, /// maxSourceTokenAmount (0 means minSourceTokenAmount)\n depositAmount, /// requiredDestTokenAmount (partial spend of loanLocal.collateral to fill this amount)\n true, /// bypassFee\n \"\" /// loanDataBytes\n );\n loanLocal.collateral = loanLocal.collateral.sub(sourceTokenAmountUsed);\n\n /// Ensure the loan is still healthy.\n (uint256 currentMargin, ) =\n IPriceFeeds(priceFeeds).getCurrentMargin(\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanLocal.principal,\n loanLocal.collateral\n );\n require(currentMargin > loanParamsLocal.maintenanceMargin, \"unhealthy position\");\n\n return destTokenAmountReceived;\n }\n}\n" + }, + "contracts/modules/LoanOpenings.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../core/State.sol\";\nimport \"../events/LoanOpeningsEvents.sol\";\nimport \"../mixins/VaultController.sol\";\nimport \"../mixins/InterestUser.sol\";\nimport \"../swaps/SwapsUser.sol\";\nimport \"../mixins/ModuleCommonFunctionalities.sol\";\nimport \"../connectors/loantoken/lib/MarginTradeStructHelpers.sol\";\n\n/**\n * @title Loan Openings contract.\n *\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains functions to borrow and trade.\n * */\ncontract LoanOpenings is\n LoanOpeningsEvents,\n VaultController,\n InterestUser,\n SwapsUser,\n ModuleCommonFunctionalities\n{\n constructor() public {}\n\n /**\n * @notice Fallback function is to react to receiving value (rBTC).\n * */\n function() external {\n revert(\"fallback not allowed\");\n }\n\n /**\n * @notice Set function selectors on target contract.\n *\n * @param target The address of the target contract.\n * */\n function initialize(address target) external onlyOwner {\n address prevModuleContractAddress = logicTargets[this.borrowOrTradeFromPool.selector];\n _setTarget(this.borrowOrTradeFromPool.selector, target);\n _setTarget(this.setDelegatedManager.selector, target);\n _setTarget(this.getEstimatedMarginExposure.selector, target);\n _setTarget(this.getRequiredCollateral.selector, target);\n _setTarget(this.getBorrowAmount.selector, target);\n emit ProtocolModuleContractReplaced(prevModuleContractAddress, target, \"LoanOpenings\");\n }\n\n /**\n * @notice Borrow or trade from pool.\n *\n * @dev Note: Only callable by loan pools (iTokens).\n * Wrapper to _borrowOrTrade internal function.\n *\n * @param loanParamsId The ID of the loan parameters.\n * @param loanId The ID of the loan. If 0, start a new loan.\n * @param isTorqueLoan Whether the loan is a Torque loan.\n * @param initialMargin The initial amount of margin.\n * @param sentAddresses The addresses to send tokens: lender, borrower,\n * receiver and manager:\n * lender: must match loan if loanId provided.\n * borrower: must match loan if loanId provided.\n * receiver: receiver of funds (address(0) assumes borrower address).\n * manager: delegated manager of loan unless address(0).\n * @param sentValues The values to send:\n * interestRate: New loan interest rate.\n * newPrincipal: New loan size (borrowAmount + any borrowed interest).\n * interestInitialAmount: New amount of interest to escrow for Torque loan (determines initial loan length).\n * loanTokenReceived: Total loanToken deposit (amount not sent to borrower in the case of Torque loans).\n * collateralTokenSent: Total collateralToken deposit.\n * minEntryPrice: Minimum entry price for checking price divergence (Value of loan token in collateral).\n * @param loanDataBytes The payload for the call. These loan DataBytes are\n * additional loan data (not in use for token swaps).\n *\n * @return newPrincipal The new loan size.\n * @return newCollateral The new collateral amount.\n * */\n function borrowOrTradeFromPool(\n bytes32 loanParamsId,\n bytes32 loanId,\n bool isTorqueLoan,\n uint256 initialMargin,\n MarginTradeStructHelpers.SentAddresses calldata sentAddresses,\n MarginTradeStructHelpers.SentAmounts calldata sentValues,\n bytes calldata loanDataBytes\n )\n external\n payable\n nonReentrant\n whenNotPaused\n returns (uint256 newPrincipal, uint256 newCollateral)\n {\n require(msg.value == 0 || loanDataBytes.length != 0, \"loanDataBytes required with ether\");\n\n /// Only callable by loan pools.\n require(loanPoolToUnderlying[msg.sender] != address(0), \"not authorized\");\n\n LoanParams memory loanParamsLocal = loanParams[loanParamsId];\n require(loanParamsLocal.id != 0, \"loanParams not exists\");\n\n /// Get required collateral.\n uint256 collateralAmountRequired =\n _getRequiredCollateral(\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n sentValues.newPrincipal,\n initialMargin,\n isTorqueLoan\n );\n require(collateralAmountRequired != 0, \"collateral is 0\");\n\n return\n _borrowOrTrade(\n loanParamsLocal,\n loanId,\n isTorqueLoan,\n collateralAmountRequired,\n initialMargin,\n sentAddresses,\n sentValues,\n loanDataBytes\n );\n }\n\n /**\n * @notice Set the delegated manager.\n *\n * @dev Wrapper for _setDelegatedManager internal function.\n *\n * @param loanId The ID of the loan. If 0, start a new loan.\n * @param delegated The address of the delegated manager.\n * @param toggle The flag true/false for the delegated manager.\n * */\n function setDelegatedManager(\n bytes32 loanId,\n address delegated,\n bool toggle\n ) external whenNotPaused {\n require(loans[loanId].borrower == msg.sender, \"unauthorized\");\n\n _setDelegatedManager(loanId, msg.sender, delegated, toggle);\n }\n\n /**\n * @notice Get the estimated margin exposure.\n *\n * Margin is the money borrowed from a broker to purchase an investment\n * and is the difference between the total value of investment and the\n * loan amount. Margin trading refers to the practice of using borrowed\n * funds from a broker to trade a financial asset, which forms the\n * collateral for the loan from the broker.\n *\n * @param loanToken The loan token instance address.\n * @param collateralToken The collateral token instance address.\n * @param loanTokenSent The amount of loan tokens sent.\n * @param collateralTokenSent The amount of collateral tokens sent.\n * @param interestRate The interest rate. Percentage w/ 18 decimals.\n * @param newPrincipal The updated amount of principal (current debt).\n *\n * @return The margin exposure.\n * */\n function getEstimatedMarginExposure(\n address loanToken,\n address collateralToken,\n uint256 loanTokenSent,\n uint256 collateralTokenSent,\n uint256 interestRate,\n uint256 newPrincipal\n ) external view returns (uint256) {\n uint256 maxLoanTerm = 2419200; // 28 days\n\n uint256 owedPerDay = newPrincipal.mul(interestRate).div(365 * 10**20);\n\n uint256 interestAmountRequired = maxLoanTerm.mul(owedPerDay).div(86400);\n\n uint256 swapAmount = loanTokenSent.sub(interestAmountRequired);\n uint256 tradingFee = _getTradingFee(swapAmount);\n if (tradingFee != 0) {\n swapAmount = swapAmount.sub(tradingFee);\n }\n\n uint256 receivedAmount = _swapsExpectedReturn(loanToken, collateralToken, swapAmount);\n if (receivedAmount == 0) {\n return 0;\n } else {\n return collateralTokenSent.add(receivedAmount);\n }\n }\n\n /**\n * @notice Get the required collateral.\n *\n * @dev Calls internal _getRequiredCollateral and add fees.\n *\n * @param loanToken The loan token instance address.\n * @param collateralToken The collateral token instance address.\n * @param newPrincipal The updated amount of principal (current debt).\n * @param marginAmount The amount of margin of the trade.\n * @param isTorqueLoan Whether the loan is a Torque loan.\n *\n * @return collateralAmountRequired The required collateral.\n * */\n function getRequiredCollateral(\n address loanToken,\n address collateralToken,\n uint256 newPrincipal,\n uint256 marginAmount,\n bool isTorqueLoan\n ) public view returns (uint256 collateralAmountRequired) {\n if (marginAmount != 0) {\n collateralAmountRequired = _getRequiredCollateral(\n loanToken,\n collateralToken,\n newPrincipal,\n marginAmount,\n isTorqueLoan\n );\n\n // p3.9 from bzx peckshield-audit-report-bZxV2-v1.0rc1.pdf\n // cannot be applied solely as it drives to some other tests failure\n /*\n\t\t\tuint256 feePercent = isTorqueLoan ? borrowingFeePercent : tradingFeePercent;\n\t\t\tif (collateralAmountRequired != 0 && feePercent != 0) {\n\t\t\t\tcollateralAmountRequired = collateralAmountRequired.mul(10**20).divCeil(\n\t\t\t\t\t10**20 - feePercent // never will overflow\n\t\t\t\t);\n\t\t\t}*/\n\n uint256 fee =\n isTorqueLoan\n ? _getBorrowingFee(collateralAmountRequired)\n : _getTradingFee(collateralAmountRequired);\n if (fee != 0) {\n collateralAmountRequired = collateralAmountRequired.add(fee);\n }\n }\n }\n\n /**\n * @notice Get the borrow amount of a trade loan.\n *\n * @dev Basically borrowAmount = collateral / marginAmount\n *\n * Collateral is something that helps secure a loan. When you borrow money,\n * you agree that your lender can take something and sell it to get their\n * money back if you fail to repay the loan. That's the collateral.\n *\n * @param loanToken The loan token instance address.\n * @param collateralToken The collateral token instance address.\n * @param collateralTokenAmount The amount of collateral.\n * @param marginAmount The amount of margin of the trade.\n * @param isTorqueLoan Whether the loan is a Torque loan.\n *\n * @return borrowAmount The borrow amount.\n * */\n function getBorrowAmount(\n address loanToken,\n address collateralToken,\n uint256 collateralTokenAmount,\n uint256 marginAmount,\n bool isTorqueLoan\n ) public view returns (uint256 borrowAmount) {\n if (marginAmount != 0) {\n if (isTorqueLoan) {\n marginAmount = marginAmount.add(10**20); /// Adjust for over-collateralized loan.\n }\n uint256 collateral = collateralTokenAmount;\n uint256 fee = isTorqueLoan ? _getBorrowingFee(collateral) : _getTradingFee(collateral);\n if (fee != 0) {\n collateral = collateral.sub(fee);\n }\n if (loanToken == collateralToken) {\n borrowAmount = collateral.mul(10**20).div(marginAmount);\n } else {\n (uint256 sourceToDestRate, uint256 sourceToDestPrecision) =\n IPriceFeeds(priceFeeds).queryRate(collateralToken, loanToken);\n if (sourceToDestPrecision != 0) {\n borrowAmount = collateral\n .mul(10**20)\n .mul(sourceToDestRate)\n .div(marginAmount)\n .div(sourceToDestPrecision);\n }\n }\n /*\n\t\t\t// p3.9 from bzx peckshield-audit-report-bZxV2-v1.0rc1.pdf\n\t\t\t// cannot be applied solely as it drives to some other tests failure\n\t\t\tuint256 feePercent = isTorqueLoan ? borrowingFeePercent : tradingFeePercent;\n\t\t\tif (borrowAmount != 0 && feePercent != 0) {\n\t\t\t\tborrowAmount = borrowAmount\n\t\t\t\t\t.mul(\n\t\t\t\t\t10**20 - feePercent // never will overflow\n\t\t\t\t)\n\t\t\t\t\t.divCeil(10**20);\n\t\t\t}*/\n }\n }\n\n /**\n * @notice Borrow or trade.\n *\n * @param loanParamsLocal The loan parameters.\n * @param loanId The ID of the loan. If 0, start a new loan.\n * @param isTorqueLoan Whether the loan is a Torque loan.\n * @param collateralAmountRequired The required amount of collateral.\n * @param initialMargin The initial amount of margin.\n * @param sentAddresses The addresses to send tokens: lender, borrower,\n * receiver and manager:\n * lender: must match loan if loanId provided.\n * borrower: must match loan if loanId provided.\n * receiver: receiver of funds (address(0) assumes borrower address).\n * manager: delegated manager of loan unless address(0).\n * @param sentValues The values to send:\n * interestRate: New loan interest rate.\n * newPrincipal: New loan size (borrowAmount + any borrowed interest).\n * interestInitialAmount: New amount of interest to escrow for Torque loan (determines initial loan length).\n * loanTokenReceived: Total loanToken deposit (amount not sent to borrower in the case of Torque loans).\n * collateralTokenSent: Total collateralToken deposit.\n * minEntryPrice: Minimum entry price for checking price divergence (Value of loan token in collateral).\n * @param loanDataBytes The payload for the call. These loan DataBytes are\n * additional loan data (not in use for token swaps).\n *\n * @return The new loan size.\n * @return The new collateral amount.\n * */\n function _borrowOrTrade(\n LoanParams memory loanParamsLocal,\n bytes32 loanId,\n bool isTorqueLoan,\n uint256 collateralAmountRequired,\n uint256 initialMargin,\n MarginTradeStructHelpers.SentAddresses memory sentAddresses,\n MarginTradeStructHelpers.SentAmounts memory sentValues,\n bytes memory loanDataBytes\n ) internal returns (uint256, uint256) {\n require(\n loanParamsLocal.collateralToken != loanParamsLocal.loanToken,\n \"collateral/loan match\"\n );\n require(initialMargin >= loanParamsLocal.minInitialMargin, \"initialMargin too low\");\n\n /// maxLoanTerm == 0 indicates a Torque loan and requires that torqueInterest != 0\n require(\n loanParamsLocal.maxLoanTerm != 0 || sentValues.interestInitialAmount != 0, /// torqueInterest\n \"invalid interest\"\n );\n\n /// Initialize loan.\n Loan storage loanLocal =\n loans[\n _initializeLoan(\n loanParamsLocal,\n loanId,\n initialMargin,\n sentAddresses,\n sentValues.newPrincipal\n )\n ];\n\n // Get required interest.\n uint256 amount =\n _initializeInterest(\n loanParamsLocal,\n loanLocal,\n sentValues.interestRate, /// newRate\n sentValues.newPrincipal, /// newPrincipal,\n sentValues.interestInitialAmount /// torqueInterest\n );\n\n /// substract out interest from usable loanToken sent.\n sentValues.loanTokenSent = sentValues.loanTokenSent.sub(amount);\n\n if (isTorqueLoan) {\n require(sentValues.loanTokenSent == 0, \"surplus loan token\");\n\n uint256 borrowingFee = _getBorrowingFee(sentValues.collateralTokenSent);\n // need to temp into local state to avoid\n address _collateralToken = loanParamsLocal.collateralToken;\n address _loanToken = loanParamsLocal.loanToken;\n if (borrowingFee != 0) {\n _payBorrowingFee(\n sentAddresses.borrower, /// borrower\n loanLocal.id,\n _collateralToken, /// fee token\n _loanToken, /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward\n borrowingFee\n );\n\n sentValues.collateralTokenSent = sentValues.collateralTokenSent.sub(borrowingFee);\n }\n } else {\n /// Update collateral after trade.\n uint256 receivedAmount;\n (receivedAmount, , sentValues.loanToCollateralSwapRate) = _loanSwap(\n loanId,\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n sentAddresses.borrower, /// borrower\n sentValues.loanTokenSent, /// loanTokenUsable (minSourceTokenAmount)\n 0, /// maxSourceTokenAmount (0 means minSourceTokenAmount)\n 0, /// requiredDestTokenAmount (enforces that all of loanTokenUsable is swapped)\n false, /// bypassFee\n loanDataBytes\n );\n sentValues.collateralTokenSent = sentValues.collateralTokenSent.add(receivedAmount);\n\n /// Check the minEntryPrice with the rate\n require(\n sentValues.loanToCollateralSwapRate >= sentValues.minEntryPrice,\n \"entry price above the minimum\"\n );\n }\n\n /// Settle collateral.\n require(\n _isCollateralSatisfied(\n loanParamsLocal,\n loanLocal,\n initialMargin,\n sentValues.collateralTokenSent,\n collateralAmountRequired\n ),\n \"collateral insufficient\"\n );\n\n loanLocal.collateral = loanLocal.collateral.add(sentValues.collateralTokenSent);\n\n if (isTorqueLoan) {\n /// reclaiming variable -> interestDuration\n sentValues.interestDuration = loanLocal.endTimestamp.sub(block.timestamp);\n } else {\n /// reclaiming variable -> entryLeverage = 100 / initialMargin\n sentValues.entryLeverage = SafeMath.div(10**38, initialMargin);\n }\n\n _finalizeOpen(loanParamsLocal, loanLocal, sentAddresses, sentValues, isTorqueLoan);\n\n return (sentValues.newPrincipal, sentValues.collateralTokenSent); /// newPrincipal, newCollateral\n }\n\n /**\n * @notice Finalize an open loan.\n *\n * @dev Finalize it by updating local parameters of the loan.\n *\n * @param loanParamsLocal The loan parameters.\n * @param loanLocal The loan object.\n * @param sentAddresses The addresses to send tokens: lender, borrower,\n * receiver and manager:\n * lender: must match loan if loanId provided.\n * borrower: must match loan if loanId provided.\n * receiver: receiver of funds (address(0) assumes borrower address).\n * manager: delegated manager of loan unless address(0).\n * @param sentValues The values to send:\n * interestRate: New loan interest rate.\n * newPrincipal: New loan size (borrowAmount + any borrowed interest).\n * interestInitialAmount: New amount of interest to escrow for Torque loan (determines initial loan length).\n * loanTokenReceived: Total loanToken deposit (amount not sent to borrower in the case of Torque loans).\n * collateralTokenSent: Total collateralToken deposit.\n * minEntryPrice: Minimum entry price for checking price divergence (Value of loan token in collateral).\n * @param isTorqueLoan Whether the loan is a Torque loan.\n * */\n function _finalizeOpen(\n LoanParams memory loanParamsLocal,\n Loan storage loanLocal,\n MarginTradeStructHelpers.SentAddresses memory sentAddresses,\n MarginTradeStructHelpers.SentAmounts memory sentValues,\n bool isTorqueLoan\n ) internal {\n /// @dev TODO: here the actual used rate and margin should go.\n (uint256 initialMargin, uint256 collateralToLoanRate) =\n IPriceFeeds(priceFeeds).getCurrentMargin(\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanLocal.principal,\n loanLocal.collateral\n );\n require(initialMargin > loanParamsLocal.maintenanceMargin, \"unhealthy position\");\n\n if (loanLocal.startTimestamp == block.timestamp) {\n uint256 loanToCollateralPrecision =\n IPriceFeeds(priceFeeds).queryPrecision(\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken\n );\n uint256 collateralToLoanPrecision =\n IPriceFeeds(priceFeeds).queryPrecision(\n loanParamsLocal.collateralToken,\n loanParamsLocal.loanToken\n );\n uint256 totalSwapRate = loanToCollateralPrecision.mul(collateralToLoanPrecision);\n loanLocal.startRate = isTorqueLoan\n ? collateralToLoanRate\n : totalSwapRate.div(sentValues.loanToCollateralSwapRate);\n }\n\n _emitOpeningEvents(\n loanParamsLocal,\n loanLocal,\n sentAddresses,\n sentValues,\n collateralToLoanRate,\n initialMargin,\n isTorqueLoan\n );\n }\n\n /**\n * @notice Emit the opening events.\n *\n * @param loanParamsLocal The loan parameters.\n * @param loanLocal The loan object.\n * @param sentAddresses The addresses to send tokens: lender, borrower,\n * receiver and manager:\n * lender: must match loan if loanId provided.\n * borrower: must match loan if loanId provided.\n * receiver: receiver of funds (address(0) assumes borrower address).\n * manager: delegated manager of loan unless address(0).\n * @param sentValues The values to send:\n * interestRate: New loan interest rate.\n * newPrincipal: New loan size (borrowAmount + any borrowed interest).\n * interestInitialAmount: New amount of interest to escrow for Torque loan (determines initial loan length).\n * loanTokenReceived: Total loanToken deposit (amount not sent to borrower in the case of Torque loans).\n * collateralTokenSent: Total collateralToken deposit.\n * minEntryPrice: Minimum entry price for checking price divergence (Value of loan token in collateral).\n * @param collateralToLoanRate The exchange rate from collateral to loan\n * tokens.\n * @param margin The amount of margin of the trade.\n * @param isTorqueLoan Whether the loan is a Torque loan.\n * */\n function _emitOpeningEvents(\n LoanParams memory loanParamsLocal,\n Loan memory loanLocal,\n MarginTradeStructHelpers.SentAddresses memory sentAddresses,\n MarginTradeStructHelpers.SentAmounts memory sentValues,\n uint256 collateralToLoanRate,\n uint256 margin,\n bool isTorqueLoan\n ) internal {\n if (isTorqueLoan) {\n emit Borrow(\n sentAddresses.borrower, /// user (borrower)\n sentAddresses.lender, /// lender\n loanLocal.id, /// loanId\n loanParamsLocal.loanToken, /// loanToken\n loanParamsLocal.collateralToken, /// collateralToken\n sentValues.newPrincipal, /// newPrincipal\n sentValues.collateralTokenSent, /// newCollateral\n sentValues.interestRate, /// interestRate\n sentValues.interestDuration, /// interestDuration\n collateralToLoanRate, /// collateralToLoanRate,\n margin /// currentMargin\n );\n } else {\n /// currentLeverage = 100 / currentMargin\n margin = SafeMath.div(10**38, margin);\n\n emit Trade(\n sentAddresses.borrower, /// user (trader)\n sentAddresses.lender, /// lender\n loanLocal.id, /// loanId\n loanParamsLocal.collateralToken, /// collateralToken\n loanParamsLocal.loanToken, /// loanToken\n sentValues.collateralTokenSent, /// positionSize\n sentValues.newPrincipal, /// borrowedAmount\n sentValues.interestRate, /// interestRate,\n loanLocal.endTimestamp, /// settlementDate\n sentValues.loanToCollateralSwapRate, /// entryPrice (loanToCollateralSwapRate)\n sentValues.entryLeverage, /// entryLeverage\n margin /// currentLeverage\n );\n }\n }\n\n /**\n * @notice Set the delegated manager.\n *\n * @param loanId The ID of the loan. If 0, start a new loan.\n * @param delegator The address of previous manager.\n * @param delegated The address of the delegated manager.\n * @param toggle The flag true/false for the delegated manager.\n * */\n function _setDelegatedManager(\n bytes32 loanId,\n address delegator,\n address delegated,\n bool toggle\n ) internal {\n delegatedManagers[loanId][delegated] = toggle;\n\n emit DelegatedManagerSet(loanId, delegator, delegated, toggle);\n }\n\n /**\n * @notice Calculate whether the collateral is satisfied.\n *\n * @dev Basically check collateral + drawdown >= 98% of required.\n *\n * @param loanParamsLocal The loan parameters.\n * @param loanLocal The loan object.\n * @param initialMargin The initial amount of margin.\n * @param newCollateral The amount of new collateral.\n * @param collateralAmountRequired The amount of required collateral.\n *\n * @return Whether the collateral is satisfied.\n * */\n function _isCollateralSatisfied(\n LoanParams memory loanParamsLocal,\n Loan memory loanLocal,\n uint256 initialMargin,\n uint256 newCollateral,\n uint256 collateralAmountRequired\n ) internal view returns (bool) {\n /// Allow at most 2% under-collateralized.\n collateralAmountRequired = collateralAmountRequired.mul(98 ether).div(100 ether);\n\n if (newCollateral < collateralAmountRequired) {\n /// Check that existing collateral is sufficient coverage.\n if (loanLocal.collateral != 0) {\n uint256 maxDrawdown =\n IPriceFeeds(priceFeeds).getMaxDrawdown(\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanLocal.principal,\n loanLocal.collateral,\n initialMargin\n );\n return newCollateral.add(maxDrawdown) >= collateralAmountRequired;\n } else {\n return false;\n }\n }\n return true;\n }\n\n /**\n * @notice Initialize a loan.\n *\n * @param loanParamsLocal The loan parameters.\n * @param loanId The ID of the loan.\n * @param initialMargin The amount of margin of the trade.\n * @param sentAddresses The addresses to send tokens: lender, borrower,\n * receiver and manager:\n * lender: must match loan if loanId provided.\n * borrower: must match loan if loanId provided.\n * receiver: receiver of funds (address(0) assumes borrower address).\n * manager: delegated manager of loan unless address(0).\n * @param newPrincipal New loan size (borrowAmount + any borrowed interest).\n * @return The loanId.\n * */\n function _initializeLoan(\n LoanParams memory loanParamsLocal,\n bytes32 loanId,\n uint256 initialMargin,\n MarginTradeStructHelpers.SentAddresses memory sentAddresses,\n uint256 newPrincipal\n ) internal returns (bytes32) {\n require(loanParamsLocal.active, \"loanParams disabled\");\n\n address lender = sentAddresses.lender;\n address borrower = sentAddresses.borrower;\n address manager = sentAddresses.manager;\n\n Loan memory loanLocal;\n\n if (loanId == 0) {\n borrowerNonce[borrower]++;\n loanId = keccak256(\n abi.encodePacked(loanParamsLocal.id, lender, borrower, borrowerNonce[borrower])\n );\n require(loans[loanId].id == 0, \"loan exists\");\n\n loanLocal = Loan({\n id: loanId,\n loanParamsId: loanParamsLocal.id,\n pendingTradesId: 0,\n active: true,\n principal: newPrincipal,\n collateral: 0, /// calculated later\n startTimestamp: block.timestamp,\n endTimestamp: 0, /// calculated later\n startMargin: initialMargin,\n startRate: 0, /// queried later\n borrower: borrower,\n lender: lender\n });\n\n activeLoansSet.addBytes32(loanId);\n lenderLoanSets[lender].addBytes32(loanId);\n borrowerLoanSets[borrower].addBytes32(loanId);\n } else {\n loanLocal = loans[loanId];\n require(\n loanLocal.active && block.timestamp < loanLocal.endTimestamp,\n \"loan has ended\"\n );\n require(loanLocal.borrower == borrower, \"borrower mismatch\");\n require(loanLocal.lender == lender, \"lender mismatch\");\n require(loanLocal.loanParamsId == loanParamsLocal.id, \"loanParams mismatch\");\n\n loanLocal.principal = loanLocal.principal.add(newPrincipal);\n }\n\n if (manager != address(0)) {\n _setDelegatedManager(loanId, borrower, manager, true);\n }\n\n loans[loanId] = loanLocal;\n\n return loanId;\n }\n\n /**\n * @notice Initialize a loan interest.\n *\n * @dev A Torque loan is an indefinite-term loan.\n *\n * @param loanParamsLocal The loan parameters.\n * @param loanLocal The loan object.\n * @param newRate The new interest rate of the loan.\n * @param newPrincipal The new principal amount of the loan.\n * @param torqueInterest The interest rate of the Torque loan.\n *\n * @return interestAmountRequired The interest amount required.\n * */\n function _initializeInterest(\n LoanParams memory loanParamsLocal,\n Loan storage loanLocal,\n uint256 newRate,\n uint256 newPrincipal,\n uint256 torqueInterest /// ignored for fixed-term loans\n ) internal returns (uint256 interestAmountRequired) {\n /// Pay outstanding interest to lender.\n _payInterest(loanLocal.lender, loanParamsLocal.loanToken);\n\n LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id];\n LenderInterest storage lenderInterestLocal =\n lenderInterest[loanLocal.lender][loanParamsLocal.loanToken];\n\n uint256 maxLoanTerm = loanParamsLocal.maxLoanTerm;\n\n _settleFeeRewardForInterestExpense(\n loanInterestLocal,\n loanLocal.id,\n loanParamsLocal.loanToken, /// fee token\n loanParamsLocal.collateralToken, /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward\n loanLocal.borrower,\n block.timestamp\n );\n\n uint256 previousDepositRemaining;\n if (maxLoanTerm == 0 && loanLocal.endTimestamp != 0) {\n previousDepositRemaining = loanLocal\n .endTimestamp\n .sub(block.timestamp) /// block.timestamp < endTimestamp was confirmed earlier.\n .mul(loanInterestLocal.owedPerDay)\n .div(86400);\n }\n\n uint256 owedPerDay = newPrincipal.mul(newRate).div(365 * 10**20);\n\n /// Update stored owedPerDay\n loanInterestLocal.owedPerDay = loanInterestLocal.owedPerDay.add(owedPerDay);\n lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.add(owedPerDay);\n\n if (maxLoanTerm == 0) {\n /// Indefinite-term (Torque) loan.\n\n /// torqueInterest != 0 was confirmed earlier.\n loanLocal.endTimestamp = torqueInterest\n .add(previousDepositRemaining)\n .mul(86400)\n .div(loanInterestLocal.owedPerDay)\n .add(block.timestamp);\n\n maxLoanTerm = loanLocal.endTimestamp.sub(block.timestamp);\n\n /// Loan term has to at least be greater than one hour.\n require(maxLoanTerm > 3600, \"loan too short\");\n\n interestAmountRequired = torqueInterest;\n } else {\n /// Fixed-term loan.\n\n if (loanLocal.endTimestamp == 0) {\n loanLocal.endTimestamp = block.timestamp.add(maxLoanTerm);\n }\n\n interestAmountRequired = loanLocal\n .endTimestamp\n .sub(block.timestamp)\n .mul(owedPerDay)\n .div(86400);\n }\n\n loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.add(\n interestAmountRequired\n );\n\n /// Update remaining lender interest values.\n lenderInterestLocal.principalTotal = lenderInterestLocal.principalTotal.add(newPrincipal);\n lenderInterestLocal.owedTotal = lenderInterestLocal.owedTotal.add(interestAmountRequired);\n }\n\n /**\n * @notice Get the required collateral.\n *\n * @dev Basically collateral = newPrincipal * marginAmount\n *\n * @param loanToken The loan token instance address.\n * @param collateralToken The collateral token instance address.\n * @param newPrincipal The updated amount of principal (current debt).\n * @param marginAmount The amount of margin of the trade.\n * @param isTorqueLoan Whether the loan is a Torque loan.\n *\n * @return collateralTokenAmount The required collateral.\n * */\n function _getRequiredCollateral(\n address loanToken,\n address collateralToken,\n uint256 newPrincipal,\n uint256 marginAmount,\n bool isTorqueLoan\n ) internal view returns (uint256 collateralTokenAmount) {\n if (loanToken == collateralToken) {\n collateralTokenAmount = newPrincipal.mul(marginAmount).div(10**20);\n } else {\n /// Using the price feed instead of the swap expected return\n /// because we need the rate in the inverse direction\n /// so the swap is probably farther off than the price feed.\n (uint256 sourceToDestRate, uint256 sourceToDestPrecision) =\n IPriceFeeds(priceFeeds).queryRate(collateralToken, loanToken);\n if (sourceToDestRate != 0) {\n collateralTokenAmount = newPrincipal\n .mul(sourceToDestPrecision)\n .div(sourceToDestRate)\n .mul(marginAmount)\n .div(10**20);\n /*TODO: review\n\t\t\t\tcollateralTokenAmount = newPrincipal.mul(sourceToDestPrecision).mul(marginAmount).div(sourceToDestRate).div(10**20);*/\n }\n }\n // ./tests/loan-token/TradingTestToken.test.js\n if (isTorqueLoan && collateralTokenAmount != 0) {\n collateralTokenAmount = collateralTokenAmount.mul(10**20).div(marginAmount).add(\n collateralTokenAmount\n );\n }\n }\n}\n" + }, + "contracts/modules/LoanSettings.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../core/State.sol\";\nimport \"../events/LoanSettingsEvents.sol\";\nimport \"../mixins/ModuleCommonFunctionalities.sol\";\n\n/**\n * @title Loan Settings contract.\n *\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains functions to get and set loan parameters.\n * */\ncontract LoanSettings is State, LoanSettingsEvents, ModuleCommonFunctionalities {\n /**\n * @notice Empty public constructor.\n * */\n constructor() public {}\n\n /**\n * @notice Fallback function is to react to receiving value (rBTC).\n * */\n function() external {\n revert(\"LoanSettings - fallback not allowed\");\n }\n\n /**\n * @notice Set function selectors on target contract.\n *\n * @param target The address of the target contract.\n * */\n function initialize(address target) external onlyOwner {\n address prevModuleContractAddress = logicTargets[this.setupLoanParams.selector];\n _setTarget(this.setupLoanParams.selector, target);\n _setTarget(this.disableLoanParams.selector, target);\n _setTarget(this.getLoanParams.selector, target);\n _setTarget(this.getLoanParamsList.selector, target);\n _setTarget(this.getTotalPrincipal.selector, target);\n _setTarget(this.minInitialMargin.selector, target);\n emit ProtocolModuleContractReplaced(prevModuleContractAddress, target, \"LoanSettings\");\n }\n\n /**\n * @notice Setup loan parameters, by looping every loan\n * and populating its parameters.\n *\n * @dev For each loan calls _setupLoanParams internal function.\n *\n * @param loanParamsList The array of loan parameters.\n *\n * @return loanParamsIdList The array of loan parameters IDs.\n * */\n function setupLoanParams(LoanParams[] calldata loanParamsList)\n external\n whenNotPaused\n returns (bytes32[] memory loanParamsIdList)\n {\n loanParamsIdList = new bytes32[](loanParamsList.length);\n for (uint256 i = 0; i < loanParamsList.length; i++) {\n loanParamsIdList[i] = _setupLoanParams(loanParamsList[i]);\n }\n }\n\n /**\n * @notice Deactivate LoanParams for future loans. Active loans\n * using it are unaffected.\n *\n * @param loanParamsIdList The array of loan parameters IDs to deactivate.\n * */\n function disableLoanParams(bytes32[] calldata loanParamsIdList) external whenNotPaused {\n for (uint256 i = 0; i < loanParamsIdList.length; i++) {\n require(msg.sender == loanParams[loanParamsIdList[i]].owner, \"unauthorized owner\");\n loanParams[loanParamsIdList[i]].active = false;\n\n LoanParams memory loanParamsLocal = loanParams[loanParamsIdList[i]];\n emit LoanParamsDisabled(\n loanParamsLocal.id,\n loanParamsLocal.owner,\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanParamsLocal.minInitialMargin,\n loanParamsLocal.maintenanceMargin,\n loanParamsLocal.maxLoanTerm\n );\n emit LoanParamsIdDisabled(loanParamsLocal.id, loanParamsLocal.owner);\n }\n }\n\n /**\n * @notice Get loan parameters for every matching IDs.\n *\n * @param loanParamsIdList The array of loan parameters IDs to match.\n *\n * @return loanParamsList The result array of loan parameters.\n * */\n function getLoanParams(bytes32[] memory loanParamsIdList)\n public\n view\n returns (LoanParams[] memory loanParamsList)\n {\n loanParamsList = new LoanParams[](loanParamsIdList.length);\n uint256 itemCount;\n\n for (uint256 i = 0; i < loanParamsIdList.length; i++) {\n LoanParams memory loanParamsLocal = loanParams[loanParamsIdList[i]];\n if (loanParamsLocal.id == 0) {\n continue;\n }\n loanParamsList[itemCount] = loanParamsLocal;\n itemCount++;\n }\n\n if (itemCount < loanParamsList.length) {\n assembly {\n mstore(loanParamsList, itemCount)\n }\n }\n }\n\n /**\n * @notice Get loan parameters for an owner and a given page\n * defined by an offset and a limit.\n *\n * @param owner The address of the loan owner.\n * @param start The page offset.\n * @param count The page limit.\n *\n * @return loanParamsList The result array of loan parameters.\n * */\n function getLoanParamsList(\n address owner,\n uint256 start,\n uint256 count\n ) external view returns (bytes32[] memory loanParamsList) {\n EnumerableBytes32Set.Bytes32Set storage set = userLoanParamSets[owner];\n uint256 end = start.add(count).min256(set.length());\n if (start >= end) {\n return loanParamsList;\n }\n\n loanParamsList = new bytes32[](count);\n uint256 itemCount;\n for (uint256 i = end - start; i > 0; i--) {\n if (itemCount == count) {\n break;\n }\n loanParamsList[itemCount] = set.get(i + start - 1);\n itemCount++;\n }\n\n if (itemCount < count) {\n assembly {\n mstore(loanParamsList, itemCount)\n }\n }\n }\n\n /**\n * @notice Get the total principal of the loans by a lender.\n *\n * @param lender The address of the lender.\n * @param loanToken The address of the token instance.\n *\n * @return The total principal of the loans.\n * */\n function getTotalPrincipal(address lender, address loanToken) external view returns (uint256) {\n return lenderInterest[lender][loanToken].principalTotal;\n }\n\n /**\n * @notice Setup a loan parameters.\n *\n * @param loanParamsLocal The loan parameters.\n *\n * @return loanParamsId The loan parameters ID.\n * */\n function _setupLoanParams(LoanParams memory loanParamsLocal) internal returns (bytes32) {\n bytes32 loanParamsId =\n keccak256(\n abi.encodePacked(\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanParamsLocal.minInitialMargin,\n loanParamsLocal.maintenanceMargin,\n loanParamsLocal.maxLoanTerm,\n block.timestamp\n )\n );\n require(loanParams[loanParamsId].id == 0, \"loanParams exists\");\n\n require(\n loanParamsLocal.loanToken != address(0) &&\n loanParamsLocal.collateralToken != address(0) &&\n loanParamsLocal.minInitialMargin > loanParamsLocal.maintenanceMargin &&\n (loanParamsLocal.maxLoanTerm == 0 || loanParamsLocal.maxLoanTerm > 3600), /// A defined maxLoanTerm has to be greater than one hour.\n \"invalid params\"\n );\n\n loanParamsLocal.id = loanParamsId;\n loanParamsLocal.active = true;\n loanParamsLocal.owner = msg.sender;\n\n loanParams[loanParamsId] = loanParamsLocal;\n userLoanParamSets[msg.sender].addBytes32(loanParamsId);\n\n emit LoanParamsSetup(\n loanParamsId,\n loanParamsLocal.owner,\n loanParamsLocal.loanToken,\n loanParamsLocal.collateralToken,\n loanParamsLocal.minInitialMargin,\n loanParamsLocal.maintenanceMargin,\n loanParamsLocal.maxLoanTerm\n );\n emit LoanParamsIdSetup(loanParamsId, loanParamsLocal.owner);\n\n return loanParamsId;\n }\n\n function minInitialMargin(bytes32 loanParamsId) external view returns (uint256) {\n return loanParams[loanParamsId].minInitialMargin;\n }\n}\n" + }, + "contracts/modules/ProtocolSettings.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../core/State.sol\";\nimport \"../events/ProtocolSettingsEvents.sol\";\nimport \"../openzeppelin/SafeERC20.sol\";\nimport \"../mixins/ProtocolTokenUser.sol\";\nimport \"../modules/interfaces/ProtocolSwapExternalInterface.sol\";\nimport \"../mixins/ModuleCommonFunctionalities.sol\";\nimport \"../governance/IFeeSharingCollector.sol\";\nimport \"../feeds/IPriceFeeds.sol\";\n\n/**\n * @title Protocol Settings contract.\n *\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains functions to customize protocol settings.\n * */\ncontract ProtocolSettings is\n State,\n ProtocolTokenUser,\n ProtocolSettingsEvents,\n ModuleCommonFunctionalities\n{\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n\n /**\n * @notice Empty public constructor.\n * */\n constructor() public {}\n\n /**\n * @notice Fallback function is to react to receiving value (rBTC).\n * */\n function() external {\n revert(\"fallback not allowed\");\n }\n\n /**\n * @notice Set function selectors on target contract.\n *\n * @param target The address of the target contract.\n * */\n function initialize(address target) external onlyAdminOrOwner {\n address prevModuleContractAddress = logicTargets[this.setPriceFeedContract.selector];\n _setTarget(this.setPriceFeedContract.selector, target);\n _setTarget(this.setSwapsImplContract.selector, target);\n _setTarget(this.setLoanPool.selector, target);\n _setTarget(this.setSupportedTokens.selector, target);\n _setTarget(this.setLendingFeePercent.selector, target);\n _setTarget(this.setTradingFeePercent.selector, target);\n _setTarget(this.setBorrowingFeePercent.selector, target);\n _setTarget(this.setSwapExternalFeePercent.selector, target);\n _setTarget(this.setAffiliateFeePercent.selector, target);\n _setTarget(this.setAffiliateTradingTokenFeePercent.selector, target);\n _setTarget(this.setLiquidationIncentivePercent.selector, target);\n _setTarget(this.setMaxDisagreement.selector, target);\n _setTarget(this.setSourceBuffer.selector, target);\n _setTarget(this.setMaxSwapSize.selector, target);\n _setTarget(this.setFeesController.selector, target);\n _setTarget(this.withdrawFees.selector, target);\n _setTarget(this.withdrawLendingFees.selector, target);\n _setTarget(this.withdrawTradingFees.selector, target);\n _setTarget(this.withdrawBorrowingFees.selector, target);\n _setTarget(this.withdrawProtocolToken.selector, target);\n _setTarget(this.depositProtocolToken.selector, target);\n _setTarget(this.getLoanPoolsList.selector, target);\n _setTarget(this.isLoanPool.selector, target);\n _setTarget(this.setSovrynSwapContractRegistryAddress.selector, target);\n _setTarget(this.setWrbtcToken.selector, target);\n _setTarget(this.setProtocolTokenAddress.selector, target);\n _setTarget(this.setRolloverBaseReward.selector, target);\n _setTarget(this.setRebatePercent.selector, target);\n _setTarget(this.setSpecialRebates.selector, target);\n _setTarget(this.setSovrynProtocolAddress.selector, target);\n _setTarget(this.setSOVTokenAddress.selector, target);\n _setTarget(this.setLockedSOVAddress.selector, target);\n _setTarget(this.setMinReferralsToPayoutAffiliates.selector, target);\n _setTarget(this.getSpecialRebates.selector, target);\n _setTarget(this.getProtocolAddress.selector, target);\n _setTarget(this.getSovTokenAddress.selector, target);\n _setTarget(this.getLockedSOVAddress.selector, target);\n _setTarget(this.getFeeRebatePercent.selector, target);\n _setTarget(this.togglePaused.selector, target);\n _setTarget(this.isProtocolPaused.selector, target);\n _setTarget(this.getSwapExternalFeePercent.selector, target);\n _setTarget(this.setTradingRebateRewardsBasisPoint.selector, target);\n _setTarget(this.getTradingRebateRewardsBasisPoint.selector, target);\n _setTarget(this.getDedicatedSOVRebate.selector, target);\n _setTarget(this.setRolloverFlexFeePercent.selector, target);\n _setTarget(this.getDefaultPathConversion.selector, target);\n _setTarget(this.setDefaultPathConversion.selector, target);\n _setTarget(this.removeDefaultPathConversion.selector, target);\n _setTarget(this.setAdmin.selector, target);\n _setTarget(this.getAdmin.selector, target);\n _setTarget(this.setPauser.selector, target);\n _setTarget(this.getPauser.selector, target);\n emit ProtocolModuleContractReplaced(prevModuleContractAddress, target, \"ProtocolSettings\");\n }\n\n /**\n * setting wrong address will break inter module functions calling\n * should be set once\n */\n function setSovrynProtocolAddress(address newProtocolAddress)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n address oldProtocolAddress = protocolAddress;\n protocolAddress = newProtocolAddress;\n\n emit SetProtocolAddress(msg.sender, oldProtocolAddress, newProtocolAddress);\n }\n\n function setSOVTokenAddress(address newSovTokenAddress)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(Address.isContract(newSovTokenAddress), \"newSovTokenAddress not a contract\");\n\n address oldTokenAddress = sovTokenAddress;\n sovTokenAddress = newSovTokenAddress;\n\n emit SetSOVTokenAddress(msg.sender, oldTokenAddress, newSovTokenAddress);\n }\n\n function setLockedSOVAddress(address newLockedSOVAddress)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(Address.isContract(newLockedSOVAddress), \"newLockSOVAddress not a contract\");\n\n address oldLockedSOVAddress = lockedSOVAddress;\n lockedSOVAddress = newLockedSOVAddress;\n\n emit SetLockedSOVAddress(msg.sender, oldLockedSOVAddress, newLockedSOVAddress);\n }\n\n /**\n * @notice Set the basis point of trading rebate rewards (SOV), max value is 9999 (99.99% liquid, 0.01% vested).\n *\n * @param newBasisPoint Basis point value.\n */\n function setTradingRebateRewardsBasisPoint(uint256 newBasisPoint)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(newBasisPoint <= 9999, \"value too high\");\n\n uint256 oldBasisPoint = tradingRebateRewardsBasisPoint;\n tradingRebateRewardsBasisPoint = newBasisPoint;\n\n emit SetTradingRebateRewardsBasisPoint(msg.sender, oldBasisPoint, newBasisPoint);\n }\n\n /**\n * @notice Update the minimum number of referrals to get affiliates rewards.\n *\n * @param newMinReferrals The new minimum number of referrals.\n * */\n function setMinReferralsToPayoutAffiliates(uint256 newMinReferrals)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n uint256 oldMinReferrals = minReferralsToPayout;\n minReferralsToPayout = newMinReferrals;\n\n emit SetMinReferralsToPayoutAffiliates(msg.sender, oldMinReferrals, newMinReferrals);\n }\n\n /**\n * @notice Set the address of the Price Feed instance.\n *\n * @param newContract The address of the Price Feed new instance.\n * */\n function setPriceFeedContract(address newContract) external onlyAdminOrOwner whenNotPaused {\n address oldContract = priceFeeds;\n priceFeeds = newContract;\n\n emit SetPriceFeedContract(msg.sender, oldContract, newContract);\n }\n\n /**\n * @notice Set the address of the asset swapper instance.\n *\n * @param newContract The address of the asset swapper new instance.\n * */\n function setSwapsImplContract(address newContract) external onlyAdminOrOwner whenNotPaused {\n address oldContract = swapsImpl;\n swapsImpl = newContract;\n\n emit SetSwapsImplContract(msg.sender, oldContract, newContract);\n }\n\n /**\n * @notice Set a list of loan pools and its tokens.\n *\n * @param pools The array of addresses of new loan pool instances.\n * @param assets The array of addresses of the corresponding underlying tokens.\n * */\n function setLoanPool(address[] calldata pools, address[] calldata assets)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(pools.length == assets.length, \"count mismatch\");\n\n for (uint256 i = 0; i < pools.length; i++) {\n require(pools[i] != assets[i], \"pool == asset\");\n require(pools[i] != address(0), \"pool == 0\");\n require(\n assets[i] != address(0) || loanPoolToUnderlying[pools[i]] != address(0),\n \"pool not exists\"\n );\n if (assets[i] == address(0)) {\n underlyingToLoanPool[loanPoolToUnderlying[pools[i]]] = address(0);\n loanPoolToUnderlying[pools[i]] = address(0);\n loanPoolsSet.removeAddress(pools[i]);\n } else {\n loanPoolToUnderlying[pools[i]] = assets[i];\n underlyingToLoanPool[assets[i]] = pools[i];\n loanPoolsSet.addAddress(pools[i]);\n }\n\n emit SetLoanPool(msg.sender, pools[i], assets[i]);\n }\n }\n\n /**\n * @notice Set a list of supported tokens by populating the\n * storage supportedTokens mapping.\n *\n * @param addrs The array of addresses of the tokens.\n * @param toggles The array of flags indicating whether\n * the corresponding token is supported or not.\n * */\n function setSupportedTokens(address[] calldata addrs, bool[] calldata toggles)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(addrs.length == toggles.length, \"count mismatch\");\n\n for (uint256 i = 0; i < addrs.length; i++) {\n supportedTokens[addrs[i]] = toggles[i];\n\n emit SetSupportedTokens(msg.sender, addrs[i], toggles[i]);\n }\n }\n\n /**\n * @notice Set the value of lendingFeePercent storage variable.\n *\n * @param newValue The new value for lendingFeePercent.\n * */\n function setLendingFeePercent(uint256 newValue) external onlyAdminOrOwner whenNotPaused {\n require(newValue <= 10**20, \"value too high\");\n uint256 oldValue = lendingFeePercent;\n lendingFeePercent = newValue;\n\n emit SetLendingFeePercent(msg.sender, oldValue, newValue);\n }\n\n /**\n * @notice Set the value of tradingFeePercent storage variable.\n *\n * @param newValue The new value for tradingFeePercent.\n * */\n function setTradingFeePercent(uint256 newValue) external onlyAdminOrOwner whenNotPaused {\n require(newValue <= 10**20, \"value too high\");\n uint256 oldValue = tradingFeePercent;\n tradingFeePercent = newValue;\n\n emit SetTradingFeePercent(msg.sender, oldValue, newValue);\n }\n\n /**\n * @notice Set the value of borrowingFeePercent storage variable.\n *\n * @param newValue The new value for borrowingFeePercent.\n * */\n function setBorrowingFeePercent(uint256 newValue) external onlyAdminOrOwner whenNotPaused {\n require(newValue <= 10**20, \"value too high\");\n uint256 oldValue = borrowingFeePercent;\n borrowingFeePercent = newValue;\n\n emit SetBorrowingFeePercent(msg.sender, oldValue, newValue);\n }\n\n /**\n * @notice Set the value of swapExtrernalFeePercent storage variable\n *\n * @param newValue the new value for swapExternalFeePercent\n */\n function setSwapExternalFeePercent(uint256 newValue) external onlyAdminOrOwner whenNotPaused {\n require(newValue <= 10**20, \"value too high\");\n uint256 oldValue = swapExtrernalFeePercent;\n swapExtrernalFeePercent = newValue;\n\n emit SetSwapExternalFeePercent(msg.sender, oldValue, newValue);\n }\n\n /**\n * @notice Set the value of affiliateFeePercent storage variable.\n *\n * @param newValue The new value for affiliateFeePercent.\n * */\n function setAffiliateFeePercent(uint256 newValue) external onlyAdminOrOwner whenNotPaused {\n require(newValue <= 10**20, \"value too high\");\n uint256 oldValue = affiliateFeePercent;\n affiliateFeePercent = newValue;\n\n emit SetAffiliateFeePercent(msg.sender, oldValue, newValue);\n }\n\n /**\n * @notice Set the value of affiliateTradingTokenFeePercent storage variable.\n *\n * @param newValue The new value for affiliateTradingTokenFeePercent.\n * */\n function setAffiliateTradingTokenFeePercent(uint256 newValue)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(newValue <= 10**20, \"value too high\");\n uint256 oldValue = affiliateTradingTokenFeePercent;\n affiliateTradingTokenFeePercent = newValue;\n\n emit SetAffiliateTradingTokenFeePercent(msg.sender, oldValue, newValue);\n }\n\n /**\n * @notice Set the value of liquidationIncentivePercent storage variable.\n *\n * @param newValue The new value for liquidationIncentivePercent.\n * */\n function setLiquidationIncentivePercent(uint256 newValue)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(newValue <= 10**20, \"value too high\");\n uint256 oldValue = liquidationIncentivePercent;\n liquidationIncentivePercent = newValue;\n\n emit SetLiquidationIncentivePercent(msg.sender, oldValue, newValue);\n }\n\n /**\n * @notice Set the value of the maximum swap spread.\n *\n * @param newValue The new value for maxDisagreement.\n * */\n function setMaxDisagreement(uint256 newValue) external onlyAdminOrOwner whenNotPaused {\n maxDisagreement = newValue;\n }\n\n /**\n * @notice Set the value of the maximum source buffer.\n *\n * @dev To avoid rounding issues on the swap rate a small buffer is implemented.\n *\n * @param newValue The new value for the maximum source buffer.\n * */\n function setSourceBuffer(uint256 newValue) external onlyAdminOrOwner whenNotPaused {\n sourceBuffer = newValue;\n }\n\n /**\n * @notice Set the value of the swap size limit.\n *\n * @param newValue The new value for the maximum swap size.\n * */\n function setMaxSwapSize(uint256 newValue) external onlyAdminOrOwner whenNotPaused {\n uint256 oldValue = maxSwapSize;\n maxSwapSize = newValue;\n\n emit SetMaxSwapSize(msg.sender, oldValue, newValue);\n }\n\n /**\n * @notice Set the address of the feesController instance.\n *\n * @dev The fee sharing proxy must be the feesController of the\n * protocol contract. This allows the fee sharing proxy\n * to withdraw the fees.\n *\n * @param newController The new address of the feesController.\n * */\n function setFeesController(address newController) external onlyAdminOrOwner whenNotPaused {\n address oldController = feesController;\n feesController = newController;\n\n emit SetFeesController(msg.sender, oldController, newController);\n }\n\n /**\n * @notice Set the pauser address of sovryn protocol.\n *\n * only pauser or owner can perform this action.\n *\n * @param newPauser The new address of the pauser.\n * */\n function setPauser(address newPauser) external onlyOwner {\n address oldPauser = pauser;\n pauser = newPauser;\n\n emit SetPauser(msg.sender, oldPauser, newPauser);\n }\n\n /**\n * @dev Get pauser address.\n *\n *\n * @return pauser address.\n */\n function getPauser() external view returns (address) {\n return pauser;\n }\n\n /*\n * @notice Set the admin address of sovryn protocol.\n *\n * only owner can perform this action.\n *\n * @param newAdmin The new address of the admin.\n * */\n function setAdmin(address newAdmin) external onlyOwner {\n emit SetAdmin(msg.sender, admin, newAdmin);\n admin = newAdmin;\n }\n\n /**\n * @dev Get admin address.\n *\n *\n * @return admin address.\n */\n function getAdmin() external view returns (address) {\n return admin;\n }\n\n /**\n * @notice The feesController calls this function to withdraw fees\n * from three sources: lending, trading and borrowing.\n * The fees (except SOV) will be converted to wRBTC.\n * For SOV, it will be deposited directly to feeSharingCollector from the protocol.\n *\n * @param tokens The array of address of the token instance.\n * @param receiver The address of the withdrawal recipient.\n *\n * @return The withdrawn total amount in wRBTC\n * */\n function withdrawFees(address[] calldata tokens, address receiver)\n external\n whenNotPaused\n returns (uint256 totalWRBTCWithdrawn)\n {\n require(msg.sender == feesController, \"unauthorized\");\n\n for (uint256 i = 0; i < tokens.length; i++) {\n uint256 lendingBalance = lendingFeeTokensHeld[tokens[i]];\n if (lendingBalance > 0) {\n lendingFeeTokensHeld[tokens[i]] = 0;\n lendingFeeTokensPaid[tokens[i]] = lendingFeeTokensPaid[tokens[i]].add(\n lendingBalance\n );\n }\n\n uint256 tradingBalance = tradingFeeTokensHeld[tokens[i]];\n if (tradingBalance > 0) {\n tradingFeeTokensHeld[tokens[i]] = 0;\n tradingFeeTokensPaid[tokens[i]] = tradingFeeTokensPaid[tokens[i]].add(\n tradingBalance\n );\n }\n\n uint256 borrowingBalance = borrowingFeeTokensHeld[tokens[i]];\n if (borrowingBalance > 0) {\n borrowingFeeTokensHeld[tokens[i]] = 0;\n borrowingFeeTokensPaid[tokens[i]] = borrowingFeeTokensPaid[tokens[i]].add(\n borrowingBalance\n );\n }\n\n uint256 tempAmount = lendingBalance.add(tradingBalance).add(borrowingBalance);\n\n if (tempAmount == 0) {\n continue;\n }\n\n uint256 amountConvertedToWRBTC;\n if (tokens[i] == address(sovTokenAddress)) {\n IERC20(tokens[i]).approve(feesController, tempAmount);\n IFeeSharingCollector(feesController).transferTokens(\n address(sovTokenAddress),\n uint96(tempAmount)\n );\n amountConvertedToWRBTC = 0;\n } else {\n if (tokens[i] == address(wrbtcToken)) {\n amountConvertedToWRBTC = tempAmount;\n\n IERC20(address(wrbtcToken)).safeTransfer(receiver, amountConvertedToWRBTC);\n } else {\n IERC20(tokens[i]).approve(protocolAddress, tempAmount);\n\n (amountConvertedToWRBTC, ) = ProtocolSwapExternalInterface(protocolAddress)\n .swapExternal(\n tokens[i], // source token address\n address(wrbtcToken), // dest token address\n feesController, // set feeSharingCollector as receiver\n protocolAddress, // protocol as the sender\n tempAmount, // source token amount\n 0, // reqDestToken\n 0, // minReturn\n \"\" // loan data bytes\n );\n\n /// Will revert if disagreement found.\n IPriceFeeds(priceFeeds).checkPriceDisagreement(\n tokens[i],\n address(wrbtcToken),\n tempAmount,\n amountConvertedToWRBTC,\n maxDisagreement\n );\n }\n\n totalWRBTCWithdrawn = totalWRBTCWithdrawn.add(amountConvertedToWRBTC);\n }\n\n emit WithdrawFees(\n msg.sender,\n tokens[i],\n receiver,\n lendingBalance,\n tradingBalance,\n borrowingBalance,\n amountConvertedToWRBTC\n );\n }\n\n return totalWRBTCWithdrawn;\n }\n\n /**\n * @notice The feesController calls this function to withdraw fees\n * accrued from lending operations.\n *\n * @param token The address of the token instance.\n * @param receiver The address of the withdrawal recipient.\n * @param amount The amount of fees to get, ignored if greater than balance.\n *\n * @return Whether withdrawal was successful.\n * */\n function withdrawLendingFees(\n address token,\n address receiver,\n uint256 amount\n ) external whenNotPaused returns (bool) {\n require(msg.sender == feesController, \"unauthorized\");\n\n uint256 withdrawAmount = amount;\n\n uint256 balance = lendingFeeTokensHeld[token];\n if (withdrawAmount > balance) {\n withdrawAmount = balance;\n }\n if (withdrawAmount == 0) {\n return false;\n }\n\n lendingFeeTokensHeld[token] = balance.sub(withdrawAmount);\n lendingFeeTokensPaid[token] = lendingFeeTokensPaid[token].add(withdrawAmount);\n\n IERC20(token).safeTransfer(receiver, withdrawAmount);\n\n emit WithdrawLendingFees(msg.sender, token, receiver, withdrawAmount);\n\n return true;\n }\n\n /**\n * @notice The feesController calls this function to withdraw fees\n * accrued from trading operations.\n *\n * @param token The address of the token instance.\n * @param receiver The address of the withdrawal recipient.\n * @param amount The amount of fees to get, ignored if greater than balance.\n *\n * @return Whether withdrawal was successful.\n * */\n function withdrawTradingFees(\n address token,\n address receiver,\n uint256 amount\n ) external whenNotPaused returns (bool) {\n require(msg.sender == feesController, \"unauthorized\");\n\n uint256 withdrawAmount = amount;\n\n uint256 balance = tradingFeeTokensHeld[token];\n if (withdrawAmount > balance) {\n withdrawAmount = balance;\n }\n if (withdrawAmount == 0) {\n return false;\n }\n\n tradingFeeTokensHeld[token] = balance.sub(withdrawAmount);\n tradingFeeTokensPaid[token] = tradingFeeTokensPaid[token].add(withdrawAmount);\n\n IERC20(token).safeTransfer(receiver, withdrawAmount);\n\n emit WithdrawTradingFees(msg.sender, token, receiver, withdrawAmount);\n\n return true;\n }\n\n /**\n * @notice The feesController calls this function to withdraw fees\n * accrued from borrowing operations.\n *\n * @param token The address of the token instance.\n * @param receiver The address of the withdrawal recipient.\n * @param amount The amount of fees to get, ignored if greater than balance.\n *\n * @return Whether withdrawal was successful.\n * */\n function withdrawBorrowingFees(\n address token,\n address receiver,\n uint256 amount\n ) external whenNotPaused returns (bool) {\n require(msg.sender == feesController, \"unauthorized\");\n\n uint256 withdrawAmount = amount;\n\n uint256 balance = borrowingFeeTokensHeld[token];\n if (withdrawAmount > balance) {\n withdrawAmount = balance;\n }\n if (withdrawAmount == 0) {\n return false;\n }\n\n borrowingFeeTokensHeld[token] = balance.sub(withdrawAmount);\n borrowingFeeTokensPaid[token] = borrowingFeeTokensPaid[token].add(withdrawAmount);\n\n IERC20(token).safeTransfer(receiver, withdrawAmount);\n\n emit WithdrawBorrowingFees(msg.sender, token, receiver, withdrawAmount);\n\n return true;\n }\n\n /**\n * @notice The owner calls this function to withdraw protocol tokens.\n *\n * @dev Wrapper for ProtocolTokenUser::_withdrawProtocolToken internal function.\n *\n * @param receiver The address of the withdrawal recipient.\n * @param amount The amount of tokens to get.\n *\n * @return The protocol token address.\n * @return Withdrawal success (true/false).\n * */\n function withdrawProtocolToken(address receiver, uint256 amount)\n external\n onlyAdminOrOwner\n whenNotPaused\n returns (address, bool)\n {\n return _withdrawProtocolToken(receiver, amount);\n }\n\n /**\n * @notice The owner calls this function to deposit protocol tokens.\n *\n * @param amount The tokens of fees to send.\n * */\n function depositProtocolToken(uint256 amount) external onlyAdminOrOwner whenNotPaused {\n /// @dev Update local balance\n protocolTokenHeld = protocolTokenHeld.add(amount);\n\n /// @dev Send the tokens\n IERC20(protocolTokenAddress).safeTransferFrom(msg.sender, address(this), amount);\n }\n\n /**\n * @notice Get a list of loan pools.\n *\n * @param start The offset.\n * @param count The limit.\n *\n * @return The array of loan pools.\n * */\n function getLoanPoolsList(uint256 start, uint256 count)\n external\n view\n returns (bytes32[] memory)\n {\n return loanPoolsSet.enumerate(start, count);\n }\n\n /**\n * @notice Check whether a token is a pool token.\n *\n * @dev By querying its underlying token.\n *\n * @param loanPool The token address to check.\n * */\n function isLoanPool(address loanPool) external view returns (bool) {\n return loanPoolToUnderlying[loanPool] != address(0);\n }\n\n /**\n * @notice Set the contract registry address of the SovrynSwap network.\n *\n * @param registryAddress the address of the registry contract.\n * */\n function setSovrynSwapContractRegistryAddress(address registryAddress)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(Address.isContract(registryAddress), \"registryAddress not a contract\");\n\n address oldSovrynSwapContractRegistryAddress = sovrynSwapContractRegistryAddress;\n sovrynSwapContractRegistryAddress = registryAddress;\n\n emit SetSovrynSwapContractRegistryAddress(\n msg.sender,\n oldSovrynSwapContractRegistryAddress,\n sovrynSwapContractRegistryAddress\n );\n }\n\n /**\n * @notice Set the wrBTC contract address.\n *\n * @param wrbtcTokenAddress The address of the wrBTC contract.\n * */\n function setWrbtcToken(address wrbtcTokenAddress) external onlyAdminOrOwner whenNotPaused {\n require(Address.isContract(wrbtcTokenAddress), \"wrbtcTokenAddress not a contract\");\n\n address oldwrbtcToken = address(wrbtcToken);\n wrbtcToken = IWrbtcERC20(wrbtcTokenAddress);\n\n emit SetWrbtcToken(msg.sender, oldwrbtcToken, wrbtcTokenAddress);\n }\n\n /**\n * @notice Set the protocol token contract address.\n *\n * @param _protocolTokenAddress The address of the protocol token contract.\n * */\n function setProtocolTokenAddress(address _protocolTokenAddress)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(Address.isContract(_protocolTokenAddress), \"_protocolTokenAddress not a contract\");\n\n address oldProtocolTokenAddress = protocolTokenAddress;\n protocolTokenAddress = _protocolTokenAddress;\n\n emit SetProtocolTokenAddress(msg.sender, oldProtocolTokenAddress, _protocolTokenAddress);\n }\n\n /**\n * @notice Set rollover base reward. It should be denominated in wrBTC.\n *\n * @param baseRewardValue The base reward.\n * */\n function setRolloverBaseReward(uint256 baseRewardValue)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(baseRewardValue > 0, \"Base reward is zero\");\n\n uint256 oldValue = rolloverBaseReward;\n rolloverBaseReward = baseRewardValue;\n\n emit SetRolloverBaseReward(msg.sender, oldValue, rolloverBaseReward);\n }\n\n /**\n * @notice Set the fee rebate percent.\n *\n * @param rebatePercent The fee rebate percent.\n * */\n function setRebatePercent(uint256 rebatePercent) external onlyAdminOrOwner whenNotPaused {\n require(rebatePercent <= 10**20, \"Fee rebate is too high\");\n\n uint256 oldRebatePercent = feeRebatePercent;\n feeRebatePercent = rebatePercent;\n\n emit SetRebatePercent(msg.sender, oldRebatePercent, rebatePercent);\n }\n\n /**\n * @notice Set the special fee rebate percent for specific pair\n *\n * @param specialRebatesPercent The new special fee rebate percent.\n * */\n function setSpecialRebates(\n address sourceToken,\n address destToken,\n uint256 specialRebatesPercent\n ) external onlyAdminOrOwner whenNotPaused {\n // Set max special rebates to 1000%\n require(specialRebatesPercent <= 1000e18, \"Special fee rebate is too high\");\n\n uint256 oldSpecialRebatesPercent = specialRebates[sourceToken][destToken];\n specialRebates[sourceToken][destToken] = specialRebatesPercent;\n\n emit SetSpecialRebates(\n msg.sender,\n sourceToken,\n destToken,\n oldSpecialRebatesPercent,\n specialRebatesPercent\n );\n }\n\n /**\n * @notice Get a rebate percent of specific pairs.\n *\n * @param sourceTokenAddress The source of pairs.\n * @param destTokenAddress The dest of pairs.\n *\n * @return The percent rebates of the pairs.\n * */\n function getSpecialRebates(address sourceTokenAddress, address destTokenAddress)\n external\n view\n returns (uint256 specialRebatesPercent)\n {\n return specialRebates[sourceTokenAddress][destTokenAddress];\n }\n\n function getProtocolAddress() external view returns (address) {\n return protocolAddress;\n }\n\n function getSovTokenAddress() external view returns (address) {\n return sovTokenAddress;\n }\n\n function getLockedSOVAddress() external view returns (address) {\n return lockedSOVAddress;\n }\n\n function getFeeRebatePercent() external view returns (uint256) {\n return feeRebatePercent;\n }\n\n function togglePaused(bool paused) external onlyPauserOrOwner {\n require(paused != pause, \"Can't toggle\");\n pause = paused;\n emit TogglePaused(msg.sender, !paused, paused);\n }\n\n function isProtocolPaused() external view returns (bool) {\n return pause;\n }\n\n function getSwapExternalFeePercent() external view returns (uint256) {\n return swapExtrernalFeePercent;\n }\n\n /**\n * @notice Get the basis point of trading rebate rewards.\n *\n * @return The basis point value.\n */\n function getTradingRebateRewardsBasisPoint() external view returns (uint256) {\n return tradingRebateRewardsBasisPoint;\n }\n\n /**\n * @dev Get how much SOV that is dedicated to pay the trading rebate rewards.\n * @notice If SOV balance is less than the fees held, it will return 0.\n *\n * @return total dedicated SOV.\n */\n function getDedicatedSOVRebate() public view returns (uint256) {\n uint256 sovProtocolBalance = IERC20(sovTokenAddress).balanceOf(address(this));\n uint256 sovFees =\n lendingFeeTokensHeld[sovTokenAddress].add(tradingFeeTokensHeld[sovTokenAddress]).add(\n borrowingFeeTokensHeld[sovTokenAddress]\n );\n\n return sovProtocolBalance >= sovFees ? sovProtocolBalance.sub(sovFees) : 0;\n }\n\n /**\n * @notice Set rolloverFlexFeePercent (max value is 1%)\n *\n * @param newRolloverFlexFeePercent uint256 value of new rollover flex fee percentage (0.1 ether = 0.1%)\n */\n function setRolloverFlexFeePercent(uint256 newRolloverFlexFeePercent)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(newRolloverFlexFeePercent <= 1e18, \"value too high\");\n uint256 oldRolloverFlexFeePercent = rolloverFlexFeePercent;\n rolloverFlexFeePercent = newRolloverFlexFeePercent;\n\n emit SetRolloverFlexFeePercent(\n msg.sender,\n oldRolloverFlexFeePercent,\n newRolloverFlexFeePercent\n );\n }\n\n /**\n * @dev Get default path conversion for pairs.\n *\n * @param sourceTokenAddress source token address.\n * @param destTokenAddress destination token address.\n *\n * @return default path of the conversion.\n */\n function getDefaultPathConversion(address sourceTokenAddress, address destTokenAddress)\n external\n view\n returns (IERC20[] memory)\n {\n return defaultPathConversion[sourceTokenAddress][destTokenAddress];\n }\n\n /**\n * @dev Set default path conversion for pairs.\n *\n * @param defaultPath array of addresses for the default path.\n *\n */\n function setDefaultPathConversion(IERC20[] calldata defaultPath)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n address sourceTokenAddress = address(defaultPath[0]);\n address destTokenAddress = address(defaultPath[defaultPath.length - 1]);\n\n uint256 defaultPathLength = defaultPath.length;\n require(defaultPathLength >= 3, \"ERR_PATH_LENGTH\");\n\n for (uint256 i = 0; i < defaultPathLength; i++) {\n require(Address.isContract(address(defaultPath[i])), \"ERR_PATH_NON_CONTRACT_ADDR\");\n }\n\n defaultPathConversion[sourceTokenAddress][destTokenAddress] = defaultPath;\n\n emit SetDefaultPathConversion(\n msg.sender,\n sourceTokenAddress,\n destTokenAddress,\n defaultPath\n );\n }\n\n /**\n * @dev Remove the default path conversion for pairs\n *\n * @param sourceTokenAddress source token address.\n * @param destTokenAddress destination token address\n */\n function removeDefaultPathConversion(address sourceTokenAddress, address destTokenAddress)\n external\n onlyAdminOrOwner\n whenNotPaused\n {\n require(\n defaultPathConversion[sourceTokenAddress][destTokenAddress].length > 0,\n \"DEFAULT_PATH_EMPTY\"\n );\n\n IERC20[] memory defaultPathValue =\n defaultPathConversion[sourceTokenAddress][destTokenAddress];\n delete defaultPathConversion[sourceTokenAddress][destTokenAddress];\n\n emit RemoveDefaultPathConversion(\n msg.sender,\n sourceTokenAddress,\n destTokenAddress,\n defaultPathValue\n );\n }\n}\n" + }, + "contracts/modules/SwapsExternal.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../core/State.sol\";\nimport \"../mixins/VaultController.sol\";\nimport \"../swaps/SwapsUser.sol\";\nimport \"../mixins/ModuleCommonFunctionalities.sol\";\n\n/**\n * @title Swaps External contract.\n *\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains functions to calculate and execute swaps.\n * */\ncontract SwapsExternal is VaultController, SwapsUser, ModuleCommonFunctionalities {\n /**\n * @notice Empty public constructor.\n * */\n constructor() public {}\n\n /**\n * @notice Fallback function is to react to receiving value (rBTC).\n * */\n function() external {\n revert(\"fallback not allowed\");\n }\n\n /**\n * @notice Set function selectors on target contract.\n *\n * @param target The address of the target contract.\n * */\n function initialize(address target) external onlyOwner {\n address prevModuleContractAddress = logicTargets[this.swapExternal.selector];\n _setTarget(this.swapExternal.selector, target);\n _setTarget(this.getSwapExpectedReturn.selector, target);\n _setTarget(this.checkPriceDivergence.selector, target);\n emit ProtocolModuleContractReplaced(prevModuleContractAddress, target, \"SwapsExternal\");\n }\n\n /**\n * @notice Perform a swap w/ tokens or rBTC as source currency.\n *\n * @dev External wrapper that calls SwapsUser::_swapsCall\n * after turning potential incoming rBTC into wrBTC tokens.\n *\n * @param sourceToken The address of the source token instance.\n * @param destToken The address of the destiny token instance.\n * @param receiver The address of the recipient account.\n * @param returnToSender The address of the sender account.\n * @param sourceTokenAmount The amount of source tokens.\n * @param requiredDestTokenAmount The amount of required destiny tokens.\n * @param minReturn Minimum amount (position size) in the collateral tokens.\n * @param swapData Additional swap data (not in use yet).\n *\n * @return destTokenAmountReceived The amount of destiny tokens sent.\n * @return sourceTokenAmountUsed The amount of source tokens spent.\n * */\n function swapExternal(\n address sourceToken,\n address destToken,\n address receiver,\n address returnToSender,\n uint256 sourceTokenAmount,\n uint256 requiredDestTokenAmount,\n uint256 minReturn,\n bytes memory swapData\n )\n public\n payable\n nonReentrant\n whenNotPaused\n returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed)\n {\n require(sourceTokenAmount != 0, \"sourceTokenAmount == 0\");\n checkPriceDivergence(sourceToken, destToken, sourceTokenAmount, minReturn);\n\n /// @dev Get payed value, be it rBTC or tokenized.\n if (msg.value != 0) {\n if (sourceToken == address(0)) {\n sourceToken = address(wrbtcToken);\n }\n require(sourceToken == address(wrbtcToken), \"sourceToken mismatch\");\n require(msg.value == sourceTokenAmount, \"sourceTokenAmount mismatch\");\n\n /// @dev Update wrBTC balance for this contract.\n wrbtcToken.deposit.value(sourceTokenAmount)();\n } else {\n if (address(this) != msg.sender) {\n IERC20(sourceToken).safeTransferFrom(msg.sender, address(this), sourceTokenAmount);\n }\n }\n\n /// @dev Perform the swap w/ tokens.\n (destTokenAmountReceived, sourceTokenAmountUsed) = _swapsCall(\n [\n sourceToken,\n destToken,\n receiver,\n returnToSender,\n msg.sender /// user\n ],\n [\n sourceTokenAmount, /// minSourceTokenAmount\n sourceTokenAmount, /// maxSourceTokenAmount\n requiredDestTokenAmount\n ],\n 0, /// loanId (not tied to a specific loan)\n false, /// bypassFee\n swapData,\n true // the flag for swapExternal (so that it will use the swapExternalFeePercent)\n );\n\n emit ExternalSwap(\n msg.sender, /// user\n sourceToken,\n destToken,\n sourceTokenAmountUsed,\n destTokenAmountReceived\n );\n }\n\n /**\n * @notice Get the swap expected return value.\n *\n * @dev External wrapper that calls SwapsUser::_swapsExpectedReturn\n *\n * @param sourceToken The address of the source token instance.\n * @param destToken The address of the destiny token instance.\n * @param sourceTokenAmount The amount of source tokens.\n *\n * @return The expected return value.\n * */\n function getSwapExpectedReturn(\n address sourceToken,\n address destToken,\n uint256 sourceTokenAmount\n ) external view returns (uint256) {\n return _swapsExpectedReturn(sourceToken, destToken, sourceTokenAmount);\n }\n\n /**\n * @notice Check the slippage based on the swapExpectedReturn.\n *\n * @param sourceToken The address of the source token instance.\n * @param destToken The address of the destiny token instance.\n * @param sourceTokenAmount The amount of source tokens.\n * @param minReturn The amount (max slippage) that will be compared to the swapsExpectedReturn.\n *\n */\n function checkPriceDivergence(\n address sourceToken,\n address destToken,\n uint256 sourceTokenAmount,\n uint256 minReturn\n ) public view {\n uint256 destTokenAmount = _swapsExpectedReturn(sourceToken, destToken, sourceTokenAmount);\n require(destTokenAmount >= minReturn, \"destTokenAmountReceived too low\");\n }\n}\n" + }, + "contracts/modules/SwapsImplSovrynSwapModule.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../swaps/connectors/SwapsImplSovrynSwapLib.sol\";\nimport \"../events/ModulesCommonEvents.sol\";\n\ncontract SwapsImplSovrynSwapModule is State, ModulesCommonEvents {\n /**\n * @notice Empty public constructor.\n * */\n constructor() public {}\n\n /**\n * @notice Fallback function is to react to receiving value (rBTC).\n * */\n function() external {\n revert(\"fallback not allowed\");\n }\n\n /**\n * @notice Set function selectors on target contract.\n *\n * @param target The address of the target contract.\n * */\n function initialize(address target) external onlyOwner {\n address prevModuleContractAddress =\n logicTargets[this.getSovrynSwapNetworkContract.selector];\n _setTarget(this.getSovrynSwapNetworkContract.selector, target);\n _setTarget(this.getContractHexName.selector, target);\n _setTarget(this.swapsImplExpectedRate.selector, target);\n _setTarget(this.swapsImplExpectedReturn.selector, target);\n emit ProtocolModuleContractReplaced(\n prevModuleContractAddress,\n target,\n \"SwapsImplSovrynSwapModule\"\n );\n }\n\n /**\n * Get the hex name of a contract.\n * @param source The name of the contract.\n * */\n function getContractHexName(string memory source) public pure returns (bytes32 result) {\n return SwapsImplSovrynSwapLib.getContractHexName(source);\n }\n\n /**\n * Look up the Sovryn swap network contract registered at the given address.\n * @param sovrynSwapRegistryAddress The address of the registry.\n * */\n function getSovrynSwapNetworkContract(address sovrynSwapRegistryAddress)\n public\n view\n returns (ISovrynSwapNetwork)\n {\n return SwapsImplSovrynSwapLib.getSovrynSwapNetworkContract(sovrynSwapRegistryAddress);\n }\n\n /**\n * @notice Get the expected rate for 1 source token when exchanging the\n * given amount of source tokens.\n *\n * @param sourceTokenAddress The address of the source token contract.\n * @param destTokenAddress The address of the destination token contract.\n * @param sourceTokenAmount The amount of source tokens to get the rate for.\n * */\n function swapsImplExpectedRate(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 sourceTokenAmount\n ) external view returns (uint256) {\n return\n SwapsImplSovrynSwapLib.getExpectedRate(\n sourceTokenAddress,\n destTokenAddress,\n sourceTokenAmount\n );\n }\n\n /**\n * @notice Get the expected return amount when exchanging the given\n * amount of source tokens.\n *\n * @notice Right now, this function is being called directly by _swapsExpectedReturn from the protocol\n * So, this function is not using _getConversionPath function since it will try to read the defaultPath storage which is stored in the protocol's slot, and it will cause an issue for direct call.\n * Instead, this function is accepting additional parameters called defaultPath which value can be declared by the caller (protocol in this case).\n *\n * @param sourceTokenAddress The address of the source token contract.\n * @param destTokenAddress The address of the destination token contract.\n * @param sourceTokenAmount The amount of source tokens to get the return for.\n * */\n function swapsImplExpectedReturn(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 sourceTokenAmount\n ) external view returns (uint256 expectedReturn) {\n return\n SwapsImplSovrynSwapLib.getExpectedReturn(\n sourceTokenAddress,\n destTokenAddress,\n sourceTokenAmount\n );\n }\n}\n" + }, + "contracts/multisig/MultiSigKeyHolders.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../openzeppelin/Ownable.sol\";\n\n/**\n * @title Multi Signature Key Holders contract.\n *\n * This contract contains the implementation of functions to add and remove\n * key holders w/ rBTC and BTC addresses.\n * */\ncontract MultiSigKeyHolders is Ownable {\n /* Storage */\n\n uint256 public constant MAX_OWNER_COUNT = 50;\n\n string private constant ERROR_INVALID_ADDRESS = \"Invalid address\";\n string private constant ERROR_INVALID_REQUIRED = \"Invalid required\";\n\n /// Flag and index for Ethereum address.\n mapping(address => Data) private isEthereumAddressAdded;\n\n /// List of Ethereum addresses.\n address[] private ethereumAddresses;\n\n /// Required number of signatures for the Ethereum multisig.\n uint256 public ethereumRequired = 2;\n\n /// Flag and index for Bitcoin address.\n mapping(string => Data) private isBitcoinAddressAdded;\n\n /// List of Bitcoin addresses.\n string[] private bitcoinAddresses;\n\n /// Required number of signatures for the Bitcoin multisig.\n uint256 public bitcoinRequired = 2;\n\n /// Helps removing items from array.\n struct Data {\n bool added;\n uint248 index;\n }\n\n /* Events */\n\n event EthereumAddressAdded(address indexed account);\n event EthereumAddressRemoved(address indexed account);\n event EthereumRequirementChanged(uint256 required);\n event BitcoinAddressAdded(string account);\n event BitcoinAddressRemoved(string account);\n event BitcoinRequirementChanged(uint256 required);\n\n /* Modifiers */\n\n modifier validRequirement(uint256 ownerCount, uint256 _required) {\n require(\n ownerCount <= MAX_OWNER_COUNT &&\n _required <= ownerCount &&\n _required != 0 &&\n ownerCount != 0,\n ERROR_INVALID_REQUIRED\n );\n _;\n }\n\n /* Functions */\n\n /**\n * @notice Add rBTC address to the key holders.\n * @param _address The address to be added.\n * */\n function addEthereumAddress(address _address) public onlyOwner {\n _addEthereumAddress(_address);\n }\n\n /**\n * @notice Add rBTC addresses to the key holders.\n * @param _address The addresses to be added.\n * */\n function addEthereumAddresses(address[] memory _address) public onlyOwner {\n for (uint256 i = 0; i < _address.length; i++) {\n _addEthereumAddress(_address[i]);\n }\n }\n\n /**\n * @notice Internal function to add rBTC address to the key holders.\n * @param _address The address to be added.\n * */\n function _addEthereumAddress(address _address) internal {\n require(_address != address(0), ERROR_INVALID_ADDRESS);\n\n if (!isEthereumAddressAdded[_address].added) {\n isEthereumAddressAdded[_address] = Data({\n added: true,\n index: uint248(ethereumAddresses.length)\n });\n ethereumAddresses.push(_address);\n }\n\n emit EthereumAddressAdded(_address);\n }\n\n /**\n * @notice Remove rBTC address to the key holders.\n * @param _address The address to be removed.\n * */\n function removeEthereumAddress(address _address) public onlyOwner {\n _removeEthereumAddress(_address);\n }\n\n /**\n * @notice Remove rBTC addresses to the key holders.\n * @param _address The addresses to be removed.\n * */\n function removeEthereumAddresses(address[] memory _address) public onlyOwner {\n for (uint256 i = 0; i < _address.length; i++) {\n _removeEthereumAddress(_address[i]);\n }\n }\n\n /**\n * @notice Internal function to remove rBTC address to the key holders.\n * @param _address The address to be removed.\n * */\n function _removeEthereumAddress(address _address) internal {\n require(_address != address(0), ERROR_INVALID_ADDRESS);\n\n if (isEthereumAddressAdded[_address].added) {\n uint248 index = isEthereumAddressAdded[_address].index;\n if (index != ethereumAddresses.length - 1) {\n ethereumAddresses[index] = ethereumAddresses[ethereumAddresses.length - 1];\n isEthereumAddressAdded[ethereumAddresses[index]].index = index;\n }\n ethereumAddresses.length--;\n delete isEthereumAddressAdded[_address];\n }\n\n emit EthereumAddressRemoved(_address);\n }\n\n /**\n * @notice Get whether rBTC address is a key holder.\n * @param _address The rBTC address to be checked.\n * */\n function isEthereumAddressOwner(address _address) public view returns (bool) {\n return isEthereumAddressAdded[_address].added;\n }\n\n /**\n * @notice Get array of rBTC key holders.\n * */\n function getEthereumAddresses() public view returns (address[] memory) {\n return ethereumAddresses;\n }\n\n /**\n * @notice Set flag ethereumRequired to true/false.\n * @param _required The new value of the ethereumRequired flag.\n * */\n function changeEthereumRequirement(uint256 _required)\n public\n onlyOwner\n validRequirement(ethereumAddresses.length, _required)\n {\n ethereumRequired = _required;\n emit EthereumRequirementChanged(_required);\n }\n\n /**\n * @notice Add bitcoin address to the key holders.\n * @param _address The address to be added.\n * */\n function addBitcoinAddress(string memory _address) public onlyOwner {\n _addBitcoinAddress(_address);\n }\n\n /**\n * @notice Add bitcoin addresses to the key holders.\n * @param _address The addresses to be added.\n * */\n function addBitcoinAddresses(string[] memory _address) public onlyOwner {\n for (uint256 i = 0; i < _address.length; i++) {\n _addBitcoinAddress(_address[i]);\n }\n }\n\n /**\n * @notice Internal function to add bitcoin address to the key holders.\n * @param _address The address to be added.\n * */\n function _addBitcoinAddress(string memory _address) internal {\n require(bytes(_address).length != 0, ERROR_INVALID_ADDRESS);\n\n if (!isBitcoinAddressAdded[_address].added) {\n isBitcoinAddressAdded[_address] = Data({\n added: true,\n index: uint248(bitcoinAddresses.length)\n });\n bitcoinAddresses.push(_address);\n }\n\n emit BitcoinAddressAdded(_address);\n }\n\n /**\n * @notice Remove bitcoin address to the key holders.\n * @param _address The address to be removed.\n * */\n function removeBitcoinAddress(string memory _address) public onlyOwner {\n _removeBitcoinAddress(_address);\n }\n\n /**\n * @notice Remove bitcoin addresses to the key holders.\n * @param _address The addresses to be removed.\n * */\n function removeBitcoinAddresses(string[] memory _address) public onlyOwner {\n for (uint256 i = 0; i < _address.length; i++) {\n _removeBitcoinAddress(_address[i]);\n }\n }\n\n /**\n * @notice Internal function to remove bitcoin address to the key holders.\n * @param _address The address to be removed.\n * */\n function _removeBitcoinAddress(string memory _address) internal {\n require(bytes(_address).length != 0, ERROR_INVALID_ADDRESS);\n\n if (isBitcoinAddressAdded[_address].added) {\n uint248 index = isBitcoinAddressAdded[_address].index;\n if (index != bitcoinAddresses.length - 1) {\n bitcoinAddresses[index] = bitcoinAddresses[bitcoinAddresses.length - 1];\n isBitcoinAddressAdded[bitcoinAddresses[index]].index = index;\n }\n bitcoinAddresses.length--;\n delete isBitcoinAddressAdded[_address];\n }\n\n emit BitcoinAddressRemoved(_address);\n }\n\n /**\n * @notice Get whether bitcoin address is a key holder.\n * @param _address The bitcoin address to be checked.\n * */\n function isBitcoinAddressOwner(string memory _address) public view returns (bool) {\n return isBitcoinAddressAdded[_address].added;\n }\n\n /**\n * @notice Get array of bitcoin key holders.\n * */\n function getBitcoinAddresses() public view returns (string[] memory) {\n return bitcoinAddresses;\n }\n\n /**\n * @notice Set flag bitcoinRequired to true/false.\n * @param _required The new value of the bitcoinRequired flag.\n * */\n function changeBitcoinRequirement(uint256 _required)\n public\n onlyOwner\n validRequirement(bitcoinAddresses.length, _required)\n {\n bitcoinRequired = _required;\n emit BitcoinRequirementChanged(_required);\n }\n\n /**\n * @notice Add rBTC and bitcoin addresses to the key holders.\n * @param _ethereumAddress the rBTC addresses to be added.\n * @param _bitcoinAddress the bitcoin addresses to be added.\n * */\n function addEthereumAndBitcoinAddresses(\n address[] memory _ethereumAddress,\n string[] memory _bitcoinAddress\n ) public onlyOwner {\n for (uint256 i = 0; i < _ethereumAddress.length; i++) {\n _addEthereumAddress(_ethereumAddress[i]);\n }\n for (uint256 i = 0; i < _bitcoinAddress.length; i++) {\n _addBitcoinAddress(_bitcoinAddress[i]);\n }\n }\n\n /**\n * @notice Remove rBTC and bitcoin addresses to the key holders.\n * @param _ethereumAddress The rBTC addresses to be removed.\n * @param _bitcoinAddress The bitcoin addresses to be removed.\n * */\n function removeEthereumAndBitcoinAddresses(\n address[] memory _ethereumAddress,\n string[] memory _bitcoinAddress\n ) public onlyOwner {\n for (uint256 i = 0; i < _ethereumAddress.length; i++) {\n _removeEthereumAddress(_ethereumAddress[i]);\n }\n for (uint256 i = 0; i < _bitcoinAddress.length; i++) {\n _removeBitcoinAddress(_bitcoinAddress[i]);\n }\n }\n}\n" + }, + "contracts/openzeppelin/Address.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // According to EIP-1052, 0x0 is the value returned for not-yet created accounts\n // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned\n // for accounts without code, i.e. `keccak256('')`\n bytes32 codehash;\n bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;\n // solhint-disable-next-line no-inline-assembly\n assembly {\n codehash := extcodehash(account)\n }\n return (codehash != accountHash && codehash != 0x0);\n }\n\n /**\n * @dev Converts an `address` into `address payable`. Note that this is\n * simply a type cast: the actual underlying value is not changed.\n *\n * _Available since v2.4.0._\n */\n function toPayable(address account) internal pure returns (address payable) {\n return address(uint160(account));\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html\n * #use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n *\n * _Available since v2.4.0._\n */\n function sendValue(address recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n // solhint-disable-next-line avoid-call-value\n (bool success, ) = recipient.call.value(amount)(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n}\n" + }, + "contracts/openzeppelin/Context.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\n/*\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with GSN meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\ncontract Context {\n // Empty internal constructor, to prevent people from mistakenly deploying\n // an instance of this contract, which should be used via inheritance.\n constructor() internal {}\n\n // solhint-disable-previous-line no-empty-blocks\n\n function _msgSender() internal view returns (address payable) {\n return msg.sender;\n }\n\n function _msgData() internal view returns (bytes memory) {\n this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691\n return msg.data;\n }\n}\n" + }, + "contracts/openzeppelin/ERC20.sol": { + "content": "pragma solidity ^0.5.0;\n\nimport \"./Context.sol\";\nimport \"./IERC20_.sol\";\nimport \"./SafeMath.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20Mintable}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20_ {\n using SafeMath for uint256;\n\n mapping(address => uint256) private _balances;\n\n mapping(address => mapping(address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20};\n *\n * Requirements:\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for `sender`'s tokens of at least\n * `amount`.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) public returns (bool) {\n _transfer(sender, recipient, amount);\n _approve(\n sender,\n _msgSender(),\n _allowances[sender][_msgSender()].sub(\n amount,\n \"ERC20: transfer amount exceeds allowance\"\n )\n );\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {\n _approve(\n _msgSender(),\n spender,\n _allowances[_msgSender()][spender].sub(\n subtractedValue,\n \"ERC20: decreased allowance below zero\"\n )\n );\n return true;\n }\n\n /**\n * @dev Moves tokens `amount` from `sender` to `recipient`.\n *\n * This is internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(\n address sender,\n address recipient,\n uint256 amount\n ) internal {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _balances[sender] = _balances[sender].sub(\n amount,\n \"ERC20: transfer amount exceeds balance\"\n );\n _balances[recipient] = _balances[recipient].add(amount);\n emit Transfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements\n *\n * - `to` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _totalSupply = _totalSupply.add(amount);\n _balances[account] = _balances[account].add(amount);\n emit Transfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _balances[account] = _balances[account].sub(amount, \"ERC20: burn amount exceeds balance\");\n _totalSupply = _totalSupply.sub(amount);\n emit Transfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.\n *\n * This is internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) internal {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`.`amount` is then deducted\n * from the caller's allowance.\n *\n * See {_burn} and {_approve}.\n */\n function _burnFrom(address account, uint256 amount) internal {\n _burn(account, amount);\n _approve(\n account,\n _msgSender(),\n _allowances[account][_msgSender()].sub(amount, \"ERC20: burn amount exceeds allowance\")\n );\n }\n}\n" + }, + "contracts/openzeppelin/ERC20Detailed.sol": { + "content": "pragma solidity ^0.5.0;\n\nimport \"./IERC20_.sol\";\n\n/**\n * @dev Optional functions from the ERC20 standard.\n */\ncontract ERC20Detailed is IERC20_ {\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of\n * these values are immutable: they can only be set once during\n * construction.\n */\n constructor(\n string memory name,\n string memory symbol,\n uint8 decimals\n ) public {\n _name = name;\n _symbol = symbol;\n _decimals = decimals;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n}\n" + }, + "contracts/openzeppelin/IERC20_.sol": { + "content": "pragma solidity ^0.5.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP. Does not include\n * the optional functions; to access them see {ERC20Detailed}.\n */\ninterface IERC20_ {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller's account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender's allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller's\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(\n address sender,\n address recipient,\n uint256 amount\n ) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n" + }, + "contracts/openzeppelin/Initializable.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n */\ncontract Initializable {\n /**\n * @dev Indicates that the contract has been initialized.\n */\n bool private _initialized;\n\n /**\n * @dev Indicates that the contract is in the process of being initialized.\n */\n bool private _initializing;\n\n /**\n * @dev Modifier to protect an initializer function from being invoked twice.\n */\n modifier initializer() {\n require(_initializing || !_initialized, \"Initializable: contract is already initialized\");\n\n bool isTopLevelCall = !_initializing;\n if (isTopLevelCall) {\n _initializing = true;\n _initialized = true;\n }\n\n _;\n\n if (isTopLevelCall) {\n _initializing = false;\n }\n }\n}\n" + }, + "contracts/openzeppelin/Ownable.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\nimport \"./Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\ncontract Ownable is Context {\n address private _owner;\n\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() internal {\n address msgSender = _msgSender();\n _owner = msgSender;\n emit OwnershipTransferred(address(0), msgSender);\n }\n\n /**\n * @dev Returns the address of the current owner.\n */\n function owner() public view returns (address) {\n return _owner;\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyOwner() {\n require(isOwner(), \"unauthorized\");\n _;\n }\n\n /**\n * @dev Returns true if the caller is the current owner.\n */\n function isOwner() public view returns (bool) {\n return _msgSender() == _owner;\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n * Can only be called by the current owner.\n */\n function transferOwnership(address newOwner) public onlyOwner {\n _transferOwnership(newOwner);\n }\n\n /**\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\n */\n function _transferOwnership(address newOwner) internal {\n require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n emit OwnershipTransferred(_owner, newOwner);\n _owner = newOwner;\n }\n}\n" + }, + "contracts/openzeppelin/PausableOz.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"./Ownable.sol\";\n\ncontract PausableOz is Ownable {\n /**\n * @dev Emitted when the pause is triggered by the owner (`account`).\n */\n event Paused(address account);\n\n /**\n * @dev Emitted when the pause is lifted by the owner (`account`).\n */\n event Unpaused(address account);\n\n bool internal _paused;\n\n constructor() internal {}\n\n /**\n * @dev Returns true if the contract is paused, and false otherwise.\n */\n function paused() public view returns (bool) {\n return _paused;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is not paused.\n */\n modifier whenNotPaused() {\n require(!_paused, \"Pausable: paused\");\n _;\n }\n\n /**\n * @dev Modifier to make a function callable only when the contract is paused.\n */\n modifier whenPaused() {\n require(_paused, \"Pausable: not paused\");\n _;\n }\n\n /**\n * @dev Called by the owner to pause, triggers stopped state.\n */\n function pause() public onlyOwner whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Called by the owner to unpause, returns to normal state.\n */\n function unpause() public onlyOwner whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "contracts/openzeppelin/ReentrancyGuard.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\n/**\n * @title Helps contracts guard against reentrancy attacks.\n * @author Remco Bloemen , Eenae \n * @dev If you mark a function `nonReentrant`, you should also\n * mark it `external`.\n */\ncontract ReentrancyGuard {\n /// @dev Constant for unlocked guard state - non-zero to prevent extra gas costs.\n /// See: https://github.com/OpenZeppelin/openzeppelin-solidity/issues/1056\n uint256 internal constant REENTRANCY_GUARD_FREE = 1;\n\n /// @dev Constant for locked guard state\n uint256 internal constant REENTRANCY_GUARD_LOCKED = 2;\n\n /**\n * @dev We use a single lock for the whole contract.\n */\n uint256 internal reentrancyLock = REENTRANCY_GUARD_FREE;\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * If you mark a function `nonReentrant`, you should also\n * mark it `external`. Calling one `nonReentrant` function from\n * another is not supported. Instead, you can implement a\n * `private` function doing the actual work, and an `external`\n * wrapper marked as `nonReentrant`.\n */\n modifier nonReentrant() {\n require(reentrancyLock == REENTRANCY_GUARD_FREE, \"nonReentrant\");\n reentrancyLock = REENTRANCY_GUARD_LOCKED;\n _;\n reentrancyLock = REENTRANCY_GUARD_FREE;\n }\n}\n" + }, + "contracts/openzeppelin/SafeERC20.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\nimport \"./SafeMath.sol\";\nimport \"./Address.sol\";\nimport \"../interfaces/IERC20.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n using SafeMath for uint256;\n using Address for address;\n\n function safeTransfer(\n IERC20 token,\n address to,\n uint256 value\n ) internal {\n callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n }\n\n function safeTransferFrom(\n IERC20 token,\n address from,\n address to,\n uint256 value\n ) internal {\n callOptionalReturn(\n token,\n abi.encodeWithSelector(token.transferFrom.selector, from, to, value)\n );\n }\n\n function safeApprove(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n // safeApprove should only be called when setting an initial allowance,\n // or when resetting it to zero. To increase and decrease it, use\n // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'\n // solhint-disable-next-line max-line-length\n require(\n (value == 0) || (token.allowance(address(this), spender) == 0),\n \"SafeERC20: approve from non-zero to non-zero allowance\"\n );\n callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n }\n\n function safeIncreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance = token.allowance(address(this), spender).add(value);\n callOptionalReturn(\n token,\n abi.encodeWithSelector(token.approve.selector, spender, newAllowance)\n );\n }\n\n function safeDecreaseAllowance(\n IERC20 token,\n address spender,\n uint256 value\n ) internal {\n uint256 newAllowance =\n token.allowance(address(this), spender).sub(\n value,\n \"SafeERC20: decreased allowance below zero\"\n );\n callOptionalReturn(\n token,\n abi.encodeWithSelector(token.approve.selector, spender, newAllowance)\n );\n }\n\n /**\n * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n * on the return value: the return value is optional (but if data is returned, it must not be false).\n * @param token The token targeted by the call.\n * @param data The call data (encoded using abi.encode or one of its variants).\n */\n function callOptionalReturn(IERC20 token, bytes memory data) private {\n // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n // we're implementing it ourselves.\n\n // A Solidity high level call has three parts:\n // 1. The target address is checked to verify it contains contract code\n // 2. The call itself is made, and success asserted\n // 3. The return value is decoded, which in turn checks the size of the returned data.\n // solhint-disable-next-line max-line-length\n require(address(token).isContract(), \"SafeERC20: call to non-contract\");\n\n // solhint-disable-next-line avoid-low-level-calls\n (bool success, bytes memory returndata) = address(token).call(data);\n require(success, \"SafeERC20: low-level call failed\");\n\n if (returndata.length > 0) {\n // Return data is optional\n // solhint-disable-next-line max-line-length\n require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n }\n }\n}\n" + }, + "contracts/openzeppelin/SafeMath.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations with added overflow\n * checks.\n *\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\n * in bugs, because programmers usually assume that an overflow raises an\n * error, which is the standard behavior in high level programming languages.\n * `SafeMath` restores this intuition by reverting the transaction when an\n * operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeMath {\n /**\n * @dev Returns the addition of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n * - Addition cannot overflow.\n */\n function add(uint256 a, uint256 b) internal pure returns (uint256) {\n uint256 c = a + b;\n require(c >= a, \"SafeMath: addition overflow\");\n\n return c;\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n * - Subtraction cannot overflow.\n */\n function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n return sub(a, b, \"SafeMath: subtraction overflow\");\n }\n\n /**\n * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n * overflow (when the result is negative).\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n * - Subtraction cannot overflow.\n *\n * _Available since v2.4.0._\n */\n function sub(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n require(b <= a, errorMessage);\n uint256 c = a - b;\n\n return c;\n }\n\n /**\n * @dev Returns the multiplication of two unsigned integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n * - Multiplication cannot overflow.\n */\n function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) {\n return 0;\n }\n\n uint256 c = a * b;\n require(c / a == b, \"SafeMath: multiplication overflow\");\n\n return c;\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers. Reverts on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n * - The divisor cannot be zero.\n */\n function div(uint256 a, uint256 b) internal pure returns (uint256) {\n return div(a, b, \"SafeMath: division by zero\");\n }\n\n /**\n * @dev Returns the integer division of two unsigned integers. Reverts with custom message on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n * - The divisor cannot be zero.\n *\n * _Available since v2.4.0._\n */\n function div(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n // Solidity only automatically asserts when dividing by 0\n require(b != 0, errorMessage);\n uint256 c = a / b;\n // assert(a == b * c + a % b); // There is no case in which this doesn't hold\n\n return c;\n }\n\n /**\n * @dev Integer division of two numbers, rounding up and truncating the quotient\n */\n function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {\n return divCeil(a, b, \"SafeMath: division by zero\");\n }\n\n /**\n * @dev Integer division of two numbers, rounding up and truncating the quotient\n */\n function divCeil(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n // Solidity only automatically asserts when dividing by 0\n require(b != 0, errorMessage);\n\n if (a == 0) {\n return 0;\n }\n uint256 c = ((a - 1) / b) + 1;\n\n return c;\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * Reverts when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n * - The divisor cannot be zero.\n */\n function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n return mod(a, b, \"SafeMath: modulo by zero\");\n }\n\n /**\n * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n * Reverts with custom message when dividing by zero.\n *\n * Counterpart to Solidity's `%` operator. This function uses a `revert`\n * opcode (which leaves remaining gas untouched) while Solidity uses an\n * invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n * - The divisor cannot be zero.\n *\n * _Available since v2.4.0._\n */\n function mod(\n uint256 a,\n uint256 b,\n string memory errorMessage\n ) internal pure returns (uint256) {\n require(b != 0, errorMessage);\n return a % b;\n }\n\n function min256(uint256 _a, uint256 _b) internal pure returns (uint256) {\n return _a < _b ? _a : _b;\n }\n}\n" + }, + "contracts/openzeppelin/SignedSafeMath.sol": { + "content": "pragma solidity >=0.5.0 <0.6.0;\n\n/**\n * @title SignedSafeMath\n * @dev Signed math operations with safety checks that revert on error.\n */\nlibrary SignedSafeMath {\n int256 private constant _INT256_MIN = -2**255;\n\n /**\n * @dev Returns the multiplication of two signed integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `*` operator.\n *\n * Requirements:\n *\n * - Multiplication cannot overflow.\n */\n function mul(int256 a, int256 b) internal pure returns (int256) {\n // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n // benefit is lost if 'b' is also tested.\n // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n if (a == 0) {\n return 0;\n }\n\n require(!(a == -1 && b == _INT256_MIN), \"SignedSafeMath: multiplication overflow\");\n\n int256 c = a * b;\n require(c / a == b, \"SignedSafeMath: multiplication overflow\");\n\n return c;\n }\n\n /**\n * @dev Returns the integer division of two signed integers. Reverts on\n * division by zero. The result is rounded towards zero.\n *\n * Counterpart to Solidity's `/` operator. Note: this function uses a\n * `revert` opcode (which leaves remaining gas untouched) while Solidity\n * uses an invalid opcode to revert (consuming all remaining gas).\n *\n * Requirements:\n *\n * - The divisor cannot be zero.\n */\n function div(int256 a, int256 b) internal pure returns (int256) {\n require(b != 0, \"SignedSafeMath: division by zero\");\n require(!(b == -1 && a == _INT256_MIN), \"SignedSafeMath: division overflow\");\n\n int256 c = a / b;\n\n return c;\n }\n\n /**\n * @dev Returns the subtraction of two signed integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `-` operator.\n *\n * Requirements:\n *\n * - Subtraction cannot overflow.\n */\n function sub(int256 a, int256 b) internal pure returns (int256) {\n int256 c = a - b;\n require((b >= 0 && c <= a) || (b < 0 && c > a), \"SignedSafeMath: subtraction overflow\");\n\n return c;\n }\n\n /**\n * @dev Returns the addition of two signed integers, reverting on\n * overflow.\n *\n * Counterpart to Solidity's `+` operator.\n *\n * Requirements:\n *\n * - Addition cannot overflow.\n */\n function add(int256 a, int256 b) internal pure returns (int256) {\n int256 c = a + b;\n require((b >= 0 && c >= a) || (b < 0 && c < a), \"SignedSafeMath: addition overflow\");\n\n return c;\n }\n}\n" + }, + "contracts/proxy/modules/interfaces/IFunctionsList.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.5.17;\n\ninterface IFunctionsList {\n function getFunctionsList() external pure returns (bytes4[] memory functionSignatures);\n}\n" + }, + "contracts/proxy/modules/interfaces/IModulesProxyRegistry.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.5.17;\n\n/**\n * ModulesProxyRegistry Interface\n */\n\ncontract IModulesProxyRegistry {\n event AddModule(address indexed moduleAddress);\n event ReplaceModule(address indexed oldAddress, address indexed newAddress);\n event RemoveModule(address indexed moduleAddress);\n event SetModuleFuncImplementation(\n bytes4 indexed _funcSig,\n address indexed _oldImplementation,\n address indexed _newImplementation\n );\n\n /// @notice Add module functions.\n /// Overriding functions is not allowed. To replace modules use ReplaceModule function.\n /// @param _impl Module implementation address\n function addModule(address _impl) external;\n\n /// @notice Add modules functions.\n /// @param _implementations Modules implementation addresses\n function addModules(address[] calldata _implementations) external;\n\n /// @notice Replace module - remove the previous, add the new one\n /// @param _oldModuleImpl Module implementation address to remove\n /// @param _newModuleImpl Module implementation address to add\n function replaceModule(address _oldModuleImpl, address _newModuleImpl) external;\n\n /// @notice Add modules functions.\n /// @param _implementationsFrom Modules to replace\n /// @param _implementationsTo Replacing modules\n function replaceModules(\n address[] calldata _implementationsFrom,\n address[] calldata _implementationsTo\n ) external;\n\n /// @notice to disable module - set all its functions implementation to address(0)\n /// @param _impl implementation address\n function removeModule(address _impl) external;\n\n /// @notice Add modules functions.\n /// @param _implementations Modules implementation addresses\n function removeModules(address[] calldata _implementations) external;\n\n /// @param _sig function signature to get impmementation address for\n /// @return function's contract implelementation address\n function getFuncImplementation(bytes4 _sig) external view returns (address);\n\n /// @notice verifies if no functions from the module deployed already registered\n /// @param _impl module implementation address to verify\n /// @return true if module can be added\n function canAddModule(address _impl) external view returns (bool);\n\n /// @notice Multiple modules verification if no functions from the modules already registered\n /// @param _implementations modules implementation addresses to verify\n /// @return True if all modules can be added, false otherwise\n function canNotAddModules(address[] calldata _implementations)\n external\n view\n returns (address[] memory modules);\n\n /// @notice used externally to verify module being added for clashing\n /// @param _newModule module implementation which functions to verify\n /// @return clashing functions signatures and corresponding modules (contracts) addresses\n function checkClashingFuncSelectors(address _newModule)\n external\n view\n returns (\n address[] memory clashingModules,\n bytes4[] memory clashingModulesFuncSelectors,\n bytes4[] memory clashingProxyRegistryFuncSelectors\n );\n}\n" + }, + "contracts/proxy/modules/ModulesProxy.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.5.17;\n\nimport \"./ModulesProxyRegistry.sol\";\n\n/**\n * ModulesProxy serves as a storage processed by a set of logic contracts - modules\n * Modules functions are registered in the contract's slots generated per func sig\n * All the function calls except for own Proxy functions are delegated to\n * the registered functions\n * The ModulesProxy is designed as a universal solution for refactorig contracts\n * reaching a 24K size limit (EIP-170)\n *\n * Upgradability is implemented at a module level to provide consistency\n * It does not allow to replace separate functions - only the whole module\n * meaning that if a module being registered contains other modules function signatures\n * then these modulea should be replaced completely - all the functions should be removed\n * to avoid leftovers or accidental replacements and therefore functional inconsistency.\n *\n * A module is either a new non-overlapping with registered modules\n * or a complete replacement of another registered module\n * in which case all the old module functions are unregistered and then\n * the new module functions are registered\n * There is also a separate function to unregister a module which unregisters all the functions\n * There is no option to unregister a subset of module functions - one should use pausable functionality\n * to achieve this\n */\n\ncontract ModulesProxy is ModulesProxyRegistry {\n // Uncomment for using beforeFallback() hook\n /*\n bytes private constant BEFORE_FALLBACK_SIG = abi.encodeWithSignature(\"beforeFallback()\");\n bytes4 private constant BEFORE_FALLBACK_SIG_BYTES4 = bytes4(keccak256(abi.encodePacked(\"beforeFallback()\")));\n */\n\n /**\n * @notice Fallback function delegates calls to modules.\n * Returns whatever the implementation call returns.\n * Has a hook to execute before delegating calls\n * To activate register a module with beforeFallback() function\n */\n function() external payable {\n /*\n // Commented to safe gas by default\n // Uncomment for using beforeFallback() hook \n // Implement and register beforeFallback() function in a module\n address beforeFallback = _getFuncImplementation(BEFORE_FALLBACK_SIG_BYTES4);\n if (beforeFallback != address(0)) {\n (bool success, ) = beforeFallback.delegatecall(bytes(0x39b0111a)); // abi.encodeWithSignature(\"beforeFallback()\")\n require(success, \"ModulesProxy::fallback: beforeFallback() fail\"); //MP02\n }\n */\n\n address target = _getFuncImplementation(msg.sig);\n require(target != address(0), \"ModulesProxy:target module not registered\"); // MP03\n\n bytes memory data = msg.data;\n assembly {\n let result := delegatecall(gas, target, add(data, 0x20), mload(data), 0, 0)\n let size := returndatasize\n let ptr := mload(0x40)\n returndatacopy(ptr, 0, size)\n switch result\n case 0 {\n revert(ptr, size)\n }\n default {\n return(ptr, size)\n }\n }\n }\n}\n" + }, + "contracts/proxy/modules/ModulesProxyRegistry.sol": { + "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.5.17;\n\nimport \"../../utils/Utils.sol\";\nimport \"../../utils/ProxyOwnable.sol\";\nimport \"../modules/interfaces/IFunctionsList.sol\";\nimport \"../modules/interfaces/IModulesProxyRegistry.sol\";\nimport \"../../openzeppelin/Address.sol\";\n\n/**\n * ModulesProxyRegistry provides modules registration/removing/replacing functionality to ModulesProxy\n * Designed to be inherited\n */\n\ncontract ModulesProxyRegistry is IModulesProxyRegistry, ProxyOwnable {\n using Address for address;\n\n bytes32 internal constant KEY_IMPLEMENTATION = keccak256(\"key.implementation\");\n\n ///@notice Constructor is internal to make contract abstract\n constructor() internal {\n // abstract\n }\n\n /// @notice Add module functions.\n /// Overriding functions is not allowed. To replace modules use replaceModule function.\n /// @param _impl Module implementation address\n function addModule(address _impl) external onlyProxyOwner {\n _addModule(_impl);\n }\n\n /// @notice Add modules functions.\n /// @param _implementations Modules implementation addresses\n function addModules(address[] calldata _implementations) external onlyProxyOwner {\n _addModules(_implementations);\n }\n\n /// @notice Replace module - remove the previous, add the new one\n /// @param _oldModuleImpl Module implementation address to remove\n /// @param _newModuleImpl Module implementation address to add\n function replaceModule(address _oldModuleImpl, address _newModuleImpl)\n external\n onlyProxyOwner\n {\n _replaceModule(_oldModuleImpl, _newModuleImpl);\n }\n\n /// @notice Add modules functions.\n /// @param _implementationsFrom Modules to replace\n /// @param _implementationsTo Replacing modules\n function replaceModules(\n address[] calldata _implementationsFrom,\n address[] calldata _implementationsTo\n ) external onlyProxyOwner {\n require(\n _implementationsFrom.length == _implementationsTo.length,\n \"ModulesProxyRegistry::replaceModules: arrays sizes must be equal\"\n ); //MR10\n\n // because the order of addresses is arbitrary, all modules are removed first to avoid collisions\n _removeModules(_implementationsFrom);\n _addModules(_implementationsTo);\n }\n\n /// @notice To disable module - set all its functions implementation to address(0)\n /// @param _impl implementation address\n function removeModule(address _impl) external onlyProxyOwner {\n _removeModule(_impl);\n }\n\n /// @notice Add modules functions.\n /// @param _implementations Modules implementation addresses\n function removeModules(address[] calldata _implementations) external onlyProxyOwner {\n _removeModules(_implementations);\n }\n\n /// @param _sig Function signature to get impmementation address for\n /// @return Function's contract implelementation address\n function getFuncImplementation(bytes4 _sig) external view returns (address) {\n return _getFuncImplementation(_sig);\n }\n\n /// @notice Verifies if no functions from the module already registered\n /// @param _impl Module implementation address to verify\n /// @return True if module can be added\n function canAddModule(address _impl) external view returns (bool) {\n return _canAddModule(_impl);\n }\n\n /// @notice Multiple modules verification if there are functions from the modules already registered\n /// @param _implementations modules implementation addresses to verify\n /// @return addresses of registered modules\n function canNotAddModules(address[] memory _implementations)\n public\n view\n returns (address[] memory)\n {\n for (uint256 i = 0; i < _implementations.length; i++) {\n if (_canAddModule(_implementations[i])) {\n delete _implementations[i];\n }\n }\n return _implementations;\n }\n\n /// @notice Used externally to verify module being added for clashing\n /// @param _newModule module implementation which functions to verify\n /// @return Clashing functions signatures and corresponding modules (contracts) addresses\n function checkClashingFuncSelectors(address _newModule)\n external\n view\n returns (\n address[] memory clashingModules,\n bytes4[] memory clashingModulesFuncSelectors,\n bytes4[] memory clashingProxyRegistryFuncSelectors\n )\n {\n require(\n _newModule.isContract(),\n \"ModulesProxyRegistry::checkClashingFuncSelectors: address is not a contract\"\n ); //MR06\n bytes4[] memory newModuleFunctions = IFunctionsList(_newModule).getFunctionsList();\n bytes4[] memory proxyRegistryFunctions = _getFunctionsList(); //registry functions list\n uint256 clashingProxyRegistryFuncsSize;\n uint256 clashingArraySize;\n uint256 clashingArrayIndex;\n uint256 clashingRegistryArrayIndex;\n\n for (uint256 i = 0; i < newModuleFunctions.length; i++) {\n address funcImpl = _getFuncImplementation(newModuleFunctions[i]);\n if (funcImpl != address(0) && funcImpl != _newModule) {\n clashingArraySize++;\n } else if (_isFuncClashingWithProxyFunctions(newModuleFunctions[i]))\n clashingProxyRegistryFuncsSize++;\n }\n clashingModules = new address[](clashingArraySize);\n clashingModulesFuncSelectors = new bytes4[](clashingArraySize);\n clashingProxyRegistryFuncSelectors = new bytes4[](clashingProxyRegistryFuncsSize);\n\n if (clashingArraySize == 0 && clashingProxyRegistryFuncsSize == 0)\n //return empty arrays\n return (\n clashingModules,\n clashingModulesFuncSelectors,\n clashingProxyRegistryFuncSelectors\n );\n for (uint256 i = 0; i < newModuleFunctions.length; i++) {\n address funcImpl = _getFuncImplementation(newModuleFunctions[i]);\n if (funcImpl != address(0)) {\n clashingModules[clashingArrayIndex] = funcImpl;\n clashingModulesFuncSelectors[clashingArrayIndex] = newModuleFunctions[i];\n clashingArrayIndex++;\n }\n for (uint256 j = 0; j < proxyRegistryFunctions.length; j++) {\n //ModulesProxyRegistry has a clashing function selector\n if (proxyRegistryFunctions[j] == newModuleFunctions[i]) {\n clashingProxyRegistryFuncSelectors[\n clashingRegistryArrayIndex\n ] = proxyRegistryFunctions[j];\n clashingRegistryArrayIndex++;\n }\n }\n }\n }\n\n /// Verifies the deployed contract address is a registered module contract\n /// @param _impl deployment address to verify\n /// @return true if _impl address is a registered module\n function isModuleRegistered(address _impl) external view returns (bool) {\n return _getFirstRegisteredModuleAddress(_impl) == _impl;\n }\n\n /****************** INTERNAL FUNCTIONS ******************/\n\n function _getFirstRegisteredModuleAddress(address _impl) internal view returns (address) {\n require(\n _impl.isContract(),\n \"ModulesProxyRegistry::_getRegisteredModuleAddress: address is not a contract\"\n );\n bytes4[] memory functions = IFunctionsList(_impl).getFunctionsList();\n for (uint256 i = 0; i < functions.length; i++) {\n address _moduleImpl = _getFuncImplementation(functions[i]);\n if (_moduleImpl != address(0)) {\n return (_moduleImpl);\n }\n }\n return address(0);\n }\n\n function _getFuncImplementation(bytes4 _sig) internal view returns (address) {\n //TODO: add querying Registry for logic address and then delegate call to it OR use proxy memory slots like this:\n bytes32 key = keccak256(abi.encode(_sig, KEY_IMPLEMENTATION));\n address implementation;\n assembly {\n implementation := sload(key)\n }\n return implementation;\n }\n\n function _addModule(address _impl) internal {\n require(_impl.isContract(), \"ModulesProxyRegistry::_addModule: address is not a contract\"); //MR01\n bytes4[] memory functions = IFunctionsList(_impl).getFunctionsList();\n for (uint256 i = 0; i < functions.length; i++) {\n require(\n _getFuncImplementation(functions[i]) == address(0),\n \"ModulesProxyRegistry::_addModule: function already registered - use replaceModule function\"\n ); //MR02\n require(functions[i] != bytes4(0), \"does not allow empty function id\"); // MR03\n require(\n !_isFuncClashingWithProxyFunctions(functions[i]),\n \"ModulesProxyRegistry::_addModule: has a function with the same signature\"\n ); //MR09\n _setModuleFuncImplementation(functions[i], _impl);\n }\n emit AddModule(_impl);\n }\n\n function _addModules(address[] memory _implementations) internal {\n for (uint256 i = 0; i < _implementations.length; i++) {\n _addModule(_implementations[i]);\n }\n }\n\n function _removeModule(address _impl) internal onlyProxyOwner {\n require(\n _impl.isContract(),\n \"ModulesProxyRegistry::_removeModule: address is not a contract\"\n ); //MR07\n bytes4[] memory functions = IFunctionsList(_impl).getFunctionsList();\n for (uint256 i = 0; i < functions.length; i++)\n _setModuleFuncImplementation(functions[i], address(0));\n\n emit RemoveModule(_impl);\n }\n\n function _removeModules(address[] memory _implementations) internal {\n for (uint256 i = 0; i < _implementations.length; i++) {\n _removeModule(_implementations[i]);\n }\n }\n\n function _replaceModule(address _oldModuleImpl, address _newModuleImpl) internal {\n if (_oldModuleImpl != _newModuleImpl) {\n require(\n _newModuleImpl.isContract(),\n \"ModulesProxyRegistry::_replaceModule - _newModuleImpl is not a contract\"\n ); //MR03\n require(\n _oldModuleImpl.isContract(),\n \"ModulesProxyRegistry::_replaceModule - _oldModuleImpl is not a contract\"\n ); //MR04\n _removeModule(_oldModuleImpl);\n _addModule(_newModuleImpl);\n\n emit ReplaceModule(_oldModuleImpl, _newModuleImpl);\n }\n }\n\n function _setModuleFuncImplementation(bytes4 _sig, address _impl) internal {\n emit SetModuleFuncImplementation(_sig, _getFuncImplementation(_sig), _impl);\n\n bytes32 key = keccak256(abi.encode(_sig, KEY_IMPLEMENTATION));\n assembly {\n sstore(key, _impl)\n }\n }\n\n function _isFuncClashingWithProxyFunctions(bytes4 _sig) internal pure returns (bool) {\n bytes4[] memory functionList = _getFunctionsList();\n for (uint256 i = 0; i < functionList.length; i++) {\n if (_sig == functionList[i])\n //ModulesProxyRegistry has function with the same id\n return true;\n }\n return false;\n }\n\n function _canAddModule(address _impl) internal view returns (bool) {\n require(\n _impl.isContract(),\n \"ModulesProxyRegistry::_canAddModule: address is not a contract\"\n ); //MR06\n bytes4[] memory functions = IFunctionsList(_impl).getFunctionsList();\n for (uint256 i = 0; i < functions.length; i++)\n if (_getFuncImplementation(functions[i]) != address(0)) return (false);\n return true;\n }\n\n function _getFunctionsList() internal pure returns (bytes4[] memory) {\n bytes4[] memory functionList = new bytes4[](13);\n functionList[0] = this.getFuncImplementation.selector;\n functionList[1] = this.addModule.selector;\n functionList[2] = this.addModules.selector;\n functionList[3] = this.removeModule.selector;\n functionList[4] = this.removeModules.selector;\n functionList[5] = this.replaceModule.selector;\n functionList[6] = this.replaceModules.selector;\n functionList[7] = this.canAddModule.selector;\n functionList[8] = this.canNotAddModules.selector;\n functionList[9] = this.setProxyOwner.selector;\n functionList[10] = this.getProxyOwner.selector;\n functionList[11] = this.checkClashingFuncSelectors.selector;\n functionList[12] = this.isModuleRegistered.selector;\n return functionList;\n }\n}\n" + }, + "contracts/proxy/Proxy.sol": { + "content": "pragma solidity ^0.5.17;\n\n/**\n * @title Base Proxy contract.\n * @notice The proxy performs delegated calls to the contract implementation\n * it is pointing to. This way upgradable contracts are possible on blockchain.\n *\n * Delegating proxy contracts are widely used for both upgradeability and gas\n * savings. These proxies rely on a logic contract (also known as implementation\n * contract or master copy) that is called using delegatecall. This allows\n * proxies to keep a persistent state (storage and balance) while the code is\n * delegated to the logic contract.\n *\n * Proxy contract is meant to be inherited and its internal functions\n * _setImplementation and _setProxyOwner to be called when upgrades become\n * neccessary.\n *\n * The loan token (iToken) contract as well as the protocol contract act as\n * proxies, delegating all calls to underlying contracts. Therefore, if you\n * want to interact with them using web3, you need to use the ABIs from the\n * contracts containing the actual logic or the interface contract.\n * ABI for LoanToken contracts: LoanTokenLogicStandard\n * ABI for Protocol contract: ISovryn\n *\n * @dev UpgradableProxy is the contract that inherits Proxy and wraps these\n * functions.\n * */\ncontract Proxy {\n bytes32 private constant KEY_IMPLEMENTATION = keccak256(\"key.implementation\");\n bytes32 private constant KEY_OWNER = keccak256(\"key.proxy.owner\");\n\n event OwnershipTransferred(address indexed _oldOwner, address indexed _newOwner);\n event ImplementationChanged(\n address indexed _oldImplementation,\n address indexed _newImplementation\n );\n\n /**\n * @notice Set sender as an owner.\n * */\n constructor() public {\n _setProxyOwner(msg.sender);\n }\n\n /**\n * @notice Throw error if called not by an owner.\n * */\n modifier onlyProxyOwner() {\n require(msg.sender == getProxyOwner(), \"Proxy:: access denied\");\n _;\n }\n\n /**\n * @notice Set address of the implementation.\n * @param _implementation Address of the implementation.\n * */\n function _setImplementation(address _implementation) internal {\n require(_implementation != address(0), \"Proxy::setImplementation: invalid address\");\n emit ImplementationChanged(getImplementation(), _implementation);\n\n bytes32 key = KEY_IMPLEMENTATION;\n assembly {\n sstore(key, _implementation)\n }\n }\n\n /**\n * @notice Return address of the implementation.\n * @return Address of the implementation.\n * */\n function getImplementation() public view returns (address _implementation) {\n bytes32 key = KEY_IMPLEMENTATION;\n assembly {\n _implementation := sload(key)\n }\n }\n\n /**\n * @notice Set address of the owner.\n * @param _owner Address of the owner.\n * */\n function _setProxyOwner(address _owner) internal {\n require(_owner != address(0), \"Proxy::setProxyOwner: invalid address\");\n emit OwnershipTransferred(getProxyOwner(), _owner);\n\n bytes32 key = KEY_OWNER;\n assembly {\n sstore(key, _owner)\n }\n }\n\n /**\n * @notice Return address of the owner.\n * @return Address of the owner.\n * */\n function getProxyOwner() public view returns (address _owner) {\n bytes32 key = KEY_OWNER;\n assembly {\n _owner := sload(key)\n }\n }\n\n /**\n * @notice Fallback function performs a delegate call\n * to the actual implementation address is pointing this proxy.\n * Returns whatever the implementation call returns.\n * */\n function() external payable {\n address implementation = getImplementation();\n require(implementation != address(0), \"Proxy::(): implementation not found\");\n\n assembly {\n let pointer := mload(0x40)\n calldatacopy(pointer, 0, calldatasize)\n let result := delegatecall(gas, implementation, pointer, calldatasize, 0, 0)\n let size := returndatasize\n returndatacopy(pointer, 0, size)\n\n switch result\n case 0 {\n revert(pointer, size)\n }\n default {\n return(pointer, size)\n }\n }\n }\n}\n" + }, + "contracts/proxy/UpgradableProxy.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./Proxy.sol\";\n\n/**\n * @title Upgradable Proxy contract.\n * @notice A disadvantage of the immutable ledger is that nobody can change the\n * source code of a smart contract after it’s been deployed. In order to fix\n * bugs or introduce new features, smart contracts need to be upgradable somehow.\n *\n * Although it is not possible to upgrade the code of an already deployed smart\n * contract, it is possible to set-up a proxy contract architecture that will\n * allow to use new deployed contracts as if the main logic had been upgraded.\n *\n * A proxy architecture pattern is such that all message calls go through a\n * Proxy contract that will redirect them to the latest deployed contract logic.\n * To upgrade, a new version of the contract is deployed, and the Proxy is\n * updated to reference the new contract address.\n * */\ncontract UpgradableProxy is Proxy {\n /**\n * @notice Set address of the implementation.\n * @dev Wrapper for _setImplementation that exposes the function\n * as public for owner to be able to set a new version of the\n * contract as current pointing implementation.\n * @param _implementation Address of the implementation.\n * */\n function setImplementation(address _implementation) public onlyProxyOwner {\n _setImplementation(_implementation);\n }\n\n /**\n * @notice Set address of the owner.\n * @param _owner Address of the owner.\n * */\n function setProxyOwner(address _owner) public onlyProxyOwner {\n _setProxyOwner(_owner);\n }\n}\n" + }, + "contracts/reentrancy/Mutex.sol": { + "content": "pragma solidity ^0.5.17;\n\n/*\n * @title Global Mutex contract\n *\n * @notice A mutex contract that allows only one function to be called at a time out\n * of a large set of functions. *Anyone* in the network can freely use any instance\n * of this contract to add a universal mutex to any function in any contract.\n */\ncontract Mutex {\n /*\n * We use an uint to store the mutex state.\n */\n uint256 public value;\n\n /*\n * @notice Increment the mutex state and return the new value.\n *\n * @dev This is the function that will be called by anyone to change the mutex\n * state. It is purposely not protected by any access control\n */\n function incrementAndGetValue() external returns (uint256) {\n /*\n * increment value using unsafe math. This is safe because we are\n * pretty certain no one will ever increment the value 2^256 times\n * in a single transaction.\n */\n return ++value;\n }\n}\n" + }, + "contracts/reentrancy/SharedReentrancyGuard.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"./Mutex.sol\";\n\n/*\n * @title Abstract contract for shared reentrancy guards\n *\n * @notice Exposes a single modifier `globallyNonReentrant` that can be used to ensure\n * that there's no reentrancy between *any* functions marked with the modifier.\n *\n * @dev The Mutex contract address is hardcoded because the address is deployed using a\n * special deployment method (similar to ERC1820Registry). This contract therefore has no\n * state and is thus safe to add to the inheritance chain of upgradeable contracts.\n */\ncontract SharedReentrancyGuard {\n /*\n * This is the address of the mutex contract that will be used as the\n * reentrancy guard.\n *\n * The address is hardcoded to avoid changing the memory layout of\n * derived contracts (possibly upgradable). Hardcoding the address is possible,\n * because the Mutex contract is always deployed to the same address, with the\n * same method used in the deployment of ERC1820Registry.\n */\n Mutex private constant MUTEX = Mutex(0xba10edD6ABC7696Eae685839217BdcC42139612b);\n\n /*\n * This is the modifier that will be used to protect functions from\n * reentrancy. It will call the mutex contract to increment the mutex\n * state and then revert if the mutex state was changed by another\n * nested call.\n */\n modifier globallyNonReentrant() {\n uint256 previous = MUTEX.incrementAndGetValue();\n\n _;\n\n /*\n * If the mutex state was changed by a nested function call, then\n * the value of the state variable will be different from the previous value.\n */\n require(previous == MUTEX.value(), \"reentrancy violation\");\n }\n}\n" + }, + "contracts/rsk/RSKAddrValidator.sol": { + "content": "// SPDX-License-Identifier:MIT\npragma solidity ^0.5.17;\n\nlibrary RSKAddrValidator {\n /*\n * @param addr it is an address to check that it does not originates from\n * signing with PK = ZERO. RSK has a small difference in which @ZERO_PK_ADDR is\n * also an address from PK = ZERO. So we check for both of them.\n * */\n function checkPKNotZero(address addr) internal pure returns (bool) {\n return (addr != 0xdcc703c0E500B653Ca82273B7BFAd8045D85a470 && addr != address(0));\n }\n\n /*\n * Safely compares two addresses, checking they do not originate from\n * a zero private key.\n * */\n function safeEquals(address addr1, address addr2) internal pure returns (bool) {\n return (addr1 == addr2 &&\n addr1 != 0xdcc703c0E500B653Ca82273B7BFAd8045D85a470 &&\n addr1 != address(0));\n }\n}\n" + }, + "contracts/swaps/connectors/interfaces/IContractRegistry.sol": { + "content": "pragma solidity 0.5.17;\n\ncontract IContractRegistry {\n function addressOf(bytes32 contractName) public view returns (address);\n}\n" + }, + "contracts/swaps/connectors/interfaces/ISovrynSwapNetwork.sol": { + "content": "pragma solidity >=0.5.8 <=0.5.17;\n\nimport \"../../../interfaces/IERC20.sol\";\n\ncontract ISovrynSwapNetwork {\n function convertByPath(\n IERC20[] calldata _path,\n uint256 _amount,\n uint256 _minReturn,\n address _beneficiary,\n address _affiliateAccount,\n uint256 _affiliateFee\n ) external payable returns (uint256);\n\n function rateByPath(IERC20[] calldata _path, uint256 _amount) external view returns (uint256);\n\n function conversionPath(IERC20 _sourceToken, IERC20 _targetToken)\n external\n view\n returns (IERC20[] memory);\n}\n" + }, + "contracts/swaps/connectors/SwapsImplSovrynSwap.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../../core/State.sol\";\nimport \"../../feeds/IPriceFeeds.sol\";\nimport \"../../openzeppelin/SafeERC20.sol\";\nimport \"./interfaces/ISovrynSwapNetwork.sol\";\nimport \"./interfaces/IContractRegistry.sol\";\n\n/**\n * @dev WARNING: This contract is deprecated, all public functions are moved to the protocol modules.\n * @title Swaps Implementation Sovryn contract.\n *\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the implementation of swap process and rate\n * calculations for Sovryn network.\n * */\ncontract SwapsImplSovrynSwap is State {\n using SafeERC20 for IERC20;\n\n /// bytes32 contractName = hex\"42616e636f724e6574776f726b\"; /// \"SovrynSwapNetwork\"\n\n constructor() internal {\n // abstract\n }\n\n /**\n * Get the hex name of a contract.\n * @param source The name of the contract.\n * */\n function getContractHexName(string memory source) public pure returns (bytes32 result) {\n assembly {\n result := mload(add(source, 32))\n }\n }\n\n /**\n * Look up the Sovryn swap network contract registered at the given address.\n * @param sovrynSwapRegistryAddress The address of the registry.\n * */\n function getSovrynSwapNetworkContract(address sovrynSwapRegistryAddress)\n public\n view\n returns (ISovrynSwapNetwork)\n {\n /// State variable sovrynSwapContractRegistryAddress is part of\n /// State.sol and set in ProtocolSettings.sol and this function\n /// needs to work without delegate call as well -> therefore pass it.\n IContractRegistry contractRegistry = IContractRegistry(sovrynSwapRegistryAddress);\n return\n ISovrynSwapNetwork(\n contractRegistry.addressOf(getContractHexName(\"SovrynSwapNetwork\"))\n );\n }\n\n /**\n * Swap the source token for the destination token on the oracle based AMM.\n * On loan opening: minSourceTokenAmount = maxSourceTokenAmount and requiredDestTokenAmount = 0\n * -> swap the minSourceTokenAmount\n * On loan rollover: (swap interest) minSourceTokenAmount = 0, maxSourceTokenAmount = complete collateral and requiredDestTokenAmount > 0\n * -> amount of required source tokens to swap is estimated (want to fill requiredDestTokenAmount, not more). maxSourceTokenAMount is not exceeded.\n * On loan closure: minSourceTokenAmount <= maxSourceTokenAmount and requiredDestTokenAmount >= 0\n * -> same as on rollover. minimum amount is not considered at all.\n *\n * @param sourceTokenAddress The address of the source tokens.\n * @param destTokenAddress The address of the destination tokens.\n * @param receiverAddress The address who will received the swap token results\n * @param returnToSenderAddress The address to return unspent tokens to (when called by the protocol, it's always the protocol contract).\n * @param minSourceTokenAmount The minimum amount of source tokens to swapped (only considered if requiredDestTokens == 0).\n * @param maxSourceTokenAmount The maximum amount of source tokens to swapped.\n * @param requiredDestTokenAmount The required amount of destination tokens.\n * */\n function internalSwap(\n address sourceTokenAddress,\n address destTokenAddress,\n address receiverAddress,\n address returnToSenderAddress,\n uint256 minSourceTokenAmount,\n uint256 maxSourceTokenAmount,\n uint256 requiredDestTokenAmount\n ) public payable returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) {\n require(sourceTokenAddress != destTokenAddress, \"source == dest\");\n require(\n supportedTokens[sourceTokenAddress] && supportedTokens[destTokenAddress],\n \"invalid tokens\"\n );\n\n ISovrynSwapNetwork sovrynSwapNetwork =\n getSovrynSwapNetworkContract(sovrynSwapContractRegistryAddress);\n\n IERC20[] memory path =\n getConversionPath(sourceTokenAddress, destTokenAddress, sovrynSwapNetwork);\n\n uint256 minReturn = 1;\n sourceTokenAmountUsed = minSourceTokenAmount;\n\n /// If the required amount of destination tokens is passed, we need to\n /// calculate the estimated amount of source tokens regardless of the\n /// minimum source token amount (name is misleading).\n if (requiredDestTokenAmount > 0) {\n sourceTokenAmountUsed = estimateSourceTokenAmount(\n sourceTokenAddress,\n destTokenAddress,\n requiredDestTokenAmount,\n maxSourceTokenAmount\n );\n /// sovrynSwapNetwork.rateByPath does not return a rate, but instead the amount of destination tokens returned.\n require(\n sovrynSwapNetwork.rateByPath(path, sourceTokenAmountUsed) >=\n requiredDestTokenAmount,\n \"insufficient source tokens provided.\"\n );\n minReturn = requiredDestTokenAmount;\n }\n\n require(sourceTokenAmountUsed > 0, \"cannot swap 0 tokens\");\n\n allowTransfer(sourceTokenAmountUsed, sourceTokenAddress, address(sovrynSwapNetwork));\n\n /// @dev Note: the kyber connector uses .call() to interact with kyber\n /// to avoid bubbling up. here we allow bubbling up.\n destTokenAmountReceived = sovrynSwapNetwork.convertByPath(\n path,\n sourceTokenAmountUsed,\n minReturn,\n receiverAddress,\n address(0),\n 0\n );\n\n /// If the sender is not the protocol (calling with delegatecall),\n /// return the remainder to the specified address.\n /// @dev Note: for the case that the swap is used without the\n /// protocol. Not sure if it should, though. needs to be discussed.\n if (returnToSenderAddress != address(this)) {\n if (sourceTokenAmountUsed < maxSourceTokenAmount) {\n /// Send unused source token back.\n IERC20(sourceTokenAddress).safeTransfer(\n returnToSenderAddress,\n maxSourceTokenAmount - sourceTokenAmountUsed\n );\n }\n }\n }\n\n /**\n * @notice Check whether the existing allowance suffices to transfer\n * the needed amount of tokens.\n * If not, allows the transfer of an arbitrary amount of tokens.\n *\n * @param tokenAmount The amount to transfer.\n * @param tokenAddress The address of the token to transfer.\n * @param sovrynSwapNetwork The address of the sovrynSwap network contract.\n * */\n function allowTransfer(\n uint256 tokenAmount,\n address tokenAddress,\n address sovrynSwapNetwork\n ) internal {\n uint256 tempAllowance = IERC20(tokenAddress).allowance(address(this), sovrynSwapNetwork);\n if (tempAllowance < tokenAmount) {\n IERC20(tokenAddress).safeApprove(sovrynSwapNetwork, uint256(-1));\n }\n }\n\n /**\n * @notice Calculate the number of source tokens to provide in order to\n * obtain the required destination amount.\n *\n * @param sourceTokenAddress The address of the source token address.\n * @param destTokenAddress The address of the destination token address.\n * @param requiredDestTokenAmount The number of destination tokens needed.\n * @param maxSourceTokenAmount The maximum number of source tokens to spend.\n *\n * @return The estimated amount of source tokens needed.\n * Minimum: minSourceTokenAmount, maximum: maxSourceTokenAmount\n * */\n function estimateSourceTokenAmount(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 requiredDestTokenAmount,\n uint256 maxSourceTokenAmount\n ) internal view returns (uint256 estimatedSourceAmount) {\n uint256 sourceToDestPrecision =\n IPriceFeeds(priceFeeds).queryPrecision(sourceTokenAddress, destTokenAddress);\n if (sourceToDestPrecision == 0) return maxSourceTokenAmount;\n\n /// Compute the expected rate for the maxSourceTokenAmount -> if spending less, we can't get a worse rate.\n uint256 expectedRate =\n internalExpectedRate(\n sourceTokenAddress,\n destTokenAddress,\n maxSourceTokenAmount,\n sovrynSwapContractRegistryAddress\n );\n\n /// Compute the source tokens needed to get the required amount with the worst case rate.\n estimatedSourceAmount = requiredDestTokenAmount.mul(sourceToDestPrecision).div(\n expectedRate\n );\n\n /// If the actual rate is exactly the same as the worst case rate, we get rounding issues. So, add a small buffer.\n /// buffer = min(estimatedSourceAmount/1000 , sourceBuffer) with sourceBuffer = 10000\n uint256 buffer = estimatedSourceAmount.div(1000);\n if (buffer > sourceBuffer) buffer = sourceBuffer;\n estimatedSourceAmount = estimatedSourceAmount.add(buffer);\n\n /// Never spend more than the maximum.\n if (estimatedSourceAmount == 0 || estimatedSourceAmount > maxSourceTokenAmount)\n return maxSourceTokenAmount;\n }\n\n /**\n * @notice Get the expected rate for 1 source token when exchanging the\n * given amount of source tokens.\n *\n * @param sourceTokenAddress The address of the source token contract.\n * @param destTokenAddress The address of the destination token contract.\n * @param sourceTokenAmount The amount of source tokens to get the rate for.\n * */\n function internalExpectedRate(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 sourceTokenAmount,\n address sovrynSwapContractRegistryAddress\n ) public view returns (uint256) {\n ISovrynSwapNetwork sovrynSwapNetwork =\n getSovrynSwapNetworkContract(sovrynSwapContractRegistryAddress);\n\n IERC20[] memory path =\n getConversionPath(sourceTokenAddress, destTokenAddress, sovrynSwapNetwork);\n\n /// Is returning the total amount of destination tokens.\n uint256 expectedReturn = sovrynSwapNetwork.rateByPath(path, sourceTokenAmount);\n\n /// Return the rate for 1 token with 18 decimals.\n return expectedReturn.mul(10**18).div(sourceTokenAmount);\n }\n\n /**\n * @notice Get the expected return amount when exchanging the given\n * amount of source tokens.\n *\n * @notice Right now, this function is being called directly by _swapsExpectedReturn from the protocol\n * So, this function is not using getConversionPath function since it will try to read the defaultPath storage which is stored in the protocol's slot, and it will cause an issue for direct call.\n * Instead, this function is accepting additional parameters called defaultPath which value can be declared by the caller (protocol in this case).\n *\n * @param sourceTokenAddress The address of the source token contract.\n * @param destTokenAddress The address of the destination token contract.\n * @param sourceTokenAmount The amount of source tokens to get the return for.\n * @param sovrynSwapContractRegistry The sovryn swap contract reigstry address.\n * @param defaultPath The default path for specific pairs.\n * */\n function internalExpectedReturn(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 sourceTokenAmount,\n address sovrynSwapContractRegistry,\n IERC20[] memory defaultPath\n ) public view returns (uint256 expectedReturn) {\n ISovrynSwapNetwork sovrynSwapNetwork =\n getSovrynSwapNetworkContract(sovrynSwapContractRegistry);\n\n IERC20[] memory path =\n defaultPath.length >= 3\n ? defaultPath\n : sovrynSwapNetwork.conversionPath(\n IERC20(sourceTokenAddress),\n IERC20(destTokenAddress)\n );\n\n /// Is returning the total amount of destination tokens.\n expectedReturn = sovrynSwapNetwork.rateByPath(path, sourceTokenAmount);\n }\n\n function getConversionPath(\n address sourceTokenAddress,\n address destTokenAddress,\n ISovrynSwapNetwork sovrynSwapNetwork\n ) private view returns (IERC20[] memory path) {\n IERC20[] memory _defaultPathConversion =\n defaultPathConversion[sourceTokenAddress][destTokenAddress];\n\n /// will use the defaultPath if it's set, otherwise query from the SovrynSwapNetwork.\n path = _defaultPathConversion.length >= 3\n ? _defaultPathConversion\n : sovrynSwapNetwork.conversionPath(\n IERC20(sourceTokenAddress),\n IERC20(destTokenAddress)\n );\n }\n}\n" + }, + "contracts/swaps/connectors/SwapsImplSovrynSwapLib.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../../feeds/IPriceFeeds.sol\";\nimport \"../../openzeppelin/SafeERC20.sol\";\nimport \"./interfaces/ISovrynSwapNetwork.sol\";\nimport \"./interfaces/IContractRegistry.sol\";\nimport \"../../interfaces/ISovryn.sol\";\n\n/**\n * @title Swaps Implementation Sovryn contract.\n *\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the implementation of swap process and rate\n * calculations for Sovryn network.\n * */\nlibrary SwapsImplSovrynSwapLib {\n using SafeMath for uint256;\n using SafeERC20 for IERC20;\n\n struct SwapParams {\n address sourceTokenAddress;\n address destTokenAddress;\n address receiverAddress;\n address returnToSenderAddress;\n uint256 minSourceTokenAmount;\n uint256 maxSourceTokenAmount;\n uint256 requiredDestTokenAmount;\n }\n\n /// bytes32 contractName = hex\"42616e636f724e6574776f726b\"; /// \"SovrynSwapNetwork\"\n\n /**\n * Get the hex name of a contract.\n * @param source The name of the contract.\n * */\n function getContractHexName(string memory source) public pure returns (bytes32 result) {\n assembly {\n result := mload(add(source, 32))\n }\n }\n\n /**\n * Look up the Sovryn swap network contract registered at the given address.\n * @param sovrynSwapRegistryAddress The address of the registry.\n * */\n function getSovrynSwapNetworkContract(address sovrynSwapRegistryAddress)\n public\n view\n returns (ISovrynSwapNetwork)\n {\n /// State variable sovrynSwapContractRegistryAddress is part of\n /// State.sol and set in ProtocolSettings.sol and this function\n /// needs to work without delegate call as well -> therefore pass it.\n IContractRegistry contractRegistry = IContractRegistry(sovrynSwapRegistryAddress);\n return\n ISovrynSwapNetwork(\n contractRegistry.addressOf(getContractHexName(\"SovrynSwapNetwork\"))\n );\n }\n\n /**\n * Swap the source token for the destination token on the oracle based AMM.\n * On loan opening: minSourceTokenAmount = maxSourceTokenAmount and requiredDestTokenAmount = 0\n * -> swap the minSourceTokenAmount\n * On loan rollover: (swap interest) minSourceTokenAmount = 0, maxSourceTokenAmount = complete collateral and requiredDestTokenAmount > 0\n * -> amount of required source tokens to swap is estimated (want to fill requiredDestTokenAmount, not more). maxSourceTokenAMount is not exceeded.\n * On loan closure: minSourceTokenAmount <= maxSourceTokenAmount and requiredDestTokenAmount >= 0\n * -> same as on rollover. minimum amount is not considered at all.\n *\n * @param params SwapParams struct\n * sourceTokenAddress The address of the source tokens.\n * destTokenAddress The address of the destination tokens.\n * receiverAddress The address who will received the swap token results\n * returnToSenderAddress The address to return unspent tokens to (when called by the protocol, it's always the protocol contract).\n * minSourceTokenAmount The minimum amount of source tokens to swapped (only considered if requiredDestTokens == 0).\n * maxSourceTokenAmount The maximum amount of source tokens to swapped.\n * requiredDestTokenAmount The required amount of destination tokens.\n * */\n function swap(SwapParams memory params)\n public\n returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed)\n {\n require(params.sourceTokenAddress != params.destTokenAddress, \"source == dest\");\n\n ISovryn iSovryn = ISovryn(address(this));\n require(\n iSovryn.supportedTokens(params.sourceTokenAddress) &&\n iSovryn.supportedTokens(params.destTokenAddress),\n \"invalid tokens\"\n );\n\n ISovrynSwapNetwork sovrynSwapNetwork =\n getSovrynSwapNetworkContract(iSovryn.sovrynSwapContractRegistryAddress());\n\n IERC20[] memory path =\n _getConversionPath(\n params.sourceTokenAddress,\n params.destTokenAddress,\n sovrynSwapNetwork\n );\n\n uint256 minReturn = 1;\n sourceTokenAmountUsed = params.minSourceTokenAmount;\n\n /// If the required amount of destination tokens is passed, we need to\n /// calculate the estimated amount of source tokens regardless of the\n /// minimum source token amount (name is misleading).\n if (params.requiredDestTokenAmount > 0) {\n sourceTokenAmountUsed = _estimateSourceTokenAmount(\n params.sourceTokenAddress,\n params.destTokenAddress,\n params.requiredDestTokenAmount,\n params.maxSourceTokenAmount\n );\n /// sovrynSwapNetwork.rateByPath does not return a rate, but instead the amount of destination tokens returned.\n require(\n sovrynSwapNetwork.rateByPath(path, sourceTokenAmountUsed) >=\n params.requiredDestTokenAmount,\n \"insufficient source tokens provided.\"\n );\n minReturn = params.requiredDestTokenAmount;\n }\n\n require(sourceTokenAmountUsed > 0, \"cannot swap 0 tokens\");\n\n _allowTransfer(\n sourceTokenAmountUsed,\n params.sourceTokenAddress,\n address(sovrynSwapNetwork)\n );\n\n /// @dev Note: the kyber connector uses .call() to interact with kyber\n /// to avoid bubbling up. here we allow bubbling up.\n destTokenAmountReceived = sovrynSwapNetwork.convertByPath(\n path,\n sourceTokenAmountUsed,\n minReturn,\n params.receiverAddress,\n address(0),\n 0\n );\n\n /// If the sender is not the protocol (calling with delegatecall),\n /// return the remainder to the specified address.\n /// @dev Note: for the case that the swap is used without the\n /// protocol. Not sure if it should, though. needs to be discussed.\n if (params.returnToSenderAddress != address(this)) {\n if (sourceTokenAmountUsed < params.maxSourceTokenAmount) {\n /// Send unused source token back.\n IERC20(params.sourceTokenAddress).safeTransfer(\n params.returnToSenderAddress,\n params.maxSourceTokenAmount - sourceTokenAmountUsed\n );\n }\n }\n }\n\n /**\n * @notice Check whether the existing allowance suffices to transfer\n * the needed amount of tokens.\n * If not, allows the transfer of an arbitrary amount of tokens.\n *\n * @param tokenAmount The amount to transfer.\n * @param tokenAddress The address of the token to transfer.\n * @param sovrynSwapNetwork The address of the sovrynSwap network contract.\n * */\n function _allowTransfer(\n uint256 tokenAmount,\n address tokenAddress,\n address sovrynSwapNetwork\n ) internal {\n uint256 tempAllowance = IERC20(tokenAddress).allowance(address(this), sovrynSwapNetwork);\n if (tempAllowance < tokenAmount) {\n IERC20(tokenAddress).safeApprove(sovrynSwapNetwork, uint256(-1));\n }\n }\n\n /**\n * @notice Calculate the number of source tokens to provide in order to\n * obtain the required destination amount.\n *\n * @param sourceTokenAddress The address of the source token address.\n * @param destTokenAddress The address of the destination token address.\n * @param requiredDestTokenAmount The number of destination tokens needed.\n * @param maxSourceTokenAmount The maximum number of source tokens to spend.\n *\n * @return The estimated amount of source tokens needed.\n * Minimum: minSourceTokenAmount, maximum: maxSourceTokenAmount\n * */\n function _estimateSourceTokenAmount(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 requiredDestTokenAmount,\n uint256 maxSourceTokenAmount\n ) internal view returns (uint256 estimatedSourceAmount) {\n ISovryn iSovryn = ISovryn(address(this));\n uint256 sourceToDestPrecision =\n IPriceFeeds(iSovryn.priceFeeds()).queryPrecision(sourceTokenAddress, destTokenAddress);\n if (sourceToDestPrecision == 0) return maxSourceTokenAmount;\n\n /// Compute the expected rate for the maxSourceTokenAmount -> if spending less, we can't get a worse rate.\n uint256 expectedRate =\n getExpectedRate(sourceTokenAddress, destTokenAddress, maxSourceTokenAmount);\n\n /// Compute the source tokens needed to get the required amount with the worst case rate.\n estimatedSourceAmount = requiredDestTokenAmount.mul(sourceToDestPrecision).div(\n expectedRate\n );\n\n /// If the actual rate is exactly the same as the worst case rate, we get rounding issues. So, add a small buffer.\n /// buffer = min(estimatedSourceAmount/1000 , sourceBuffer) with sourceBuffer = 10000\n uint256 buffer = estimatedSourceAmount.div(1000);\n if (buffer > iSovryn.sourceBuffer()) buffer = iSovryn.sourceBuffer();\n estimatedSourceAmount = estimatedSourceAmount.add(buffer);\n\n /// Never spend more than the maximum.\n if (estimatedSourceAmount == 0 || estimatedSourceAmount > maxSourceTokenAmount)\n return maxSourceTokenAmount;\n }\n\n /**\n * @notice Get the expected rate for 1 source token when exchanging the\n * given amount of source tokens.\n *\n * @param sourceTokenAddress The address of the source token contract.\n * @param destTokenAddress The address of the destination token contract.\n * @param sourceTokenAmount The amount of source tokens to get the rate for.\n * */\n function getExpectedRate(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 sourceTokenAmount\n ) public view returns (uint256) {\n ISovrynSwapNetwork sovrynSwapNetwork =\n getSovrynSwapNetworkContract(\n ISovryn(address(this)).sovrynSwapContractRegistryAddress()\n );\n\n IERC20[] memory path =\n _getConversionPath(sourceTokenAddress, destTokenAddress, sovrynSwapNetwork);\n\n /// Is returning the total amount of destination tokens.\n uint256 expectedReturn = sovrynSwapNetwork.rateByPath(path, sourceTokenAmount);\n\n /// Return the rate for 1 token with 18 decimals.\n return expectedReturn.mul(10**18).div(sourceTokenAmount);\n }\n\n /**\n * @notice Get the expected return amount when exchanging the given\n * amount of source tokens.\n *\n * @notice Right now, this function is being called directly by _swapsExpectedReturn from the protocol\n * So, this function is not using _getConversionPath function since it will try to read the defaultPath storage which is stored in the protocol's slot, and it will cause an issue for direct call.\n * Instead, this function is accepting additional parameters called defaultPath which value can be declared by the caller (protocol in this case).\n *\n * @param sourceTokenAddress The address of the source token contract.\n * @param destTokenAddress The address of the destination token contract.\n * @param sourceTokenAmount The amount of source tokens to get the return for.\n * */\n function getExpectedReturn(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 sourceTokenAmount\n ) public view returns (uint256 expectedReturn) {\n ISovrynSwapNetwork sovrynSwapNetwork =\n getSovrynSwapNetworkContract(\n ISovryn(address(this)).sovrynSwapContractRegistryAddress()\n );\n\n IERC20[] memory path =\n _getConversionPath(sourceTokenAddress, destTokenAddress, sovrynSwapNetwork);\n\n /// Is returning the total amount of destination tokens.\n expectedReturn = sovrynSwapNetwork.rateByPath(path, sourceTokenAmount);\n }\n\n function _getConversionPath(\n address sourceTokenAddress,\n address destTokenAddress,\n ISovrynSwapNetwork sovrynSwapNetwork\n ) private view returns (IERC20[] memory path) {\n IERC20[] memory _defaultPathConversion =\n ISovryn(address(this)).getDefaultPathConversion(sourceTokenAddress, destTokenAddress);\n\n /// will use the defaultPath if it's set, otherwise query from the SovrynSwapNetwork.\n path = _defaultPathConversion.length >= 3\n ? _defaultPathConversion\n : sovrynSwapNetwork.conversionPath(\n IERC20(sourceTokenAddress),\n IERC20(destTokenAddress)\n );\n }\n}\n" + }, + "contracts/swaps/connectors/testnet/SwapsImplLocal.sol": { + "content": "/**\n * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../../../core/State.sol\";\nimport \"../../../openzeppelin/SafeERC20.sol\";\nimport \"../../../feeds/IPriceFeeds.sol\";\nimport \"../../../testhelpers/TestToken.sol\";\n\n/**\n * @title Swaps Implementation Local contract.\n *\n * @notice This contract code comes from bZx. bZx is a protocol for tokenized\n * margin trading and lending https://bzx.network similar to the dYdX protocol.\n *\n * This contract contains the implementation of swap process and rate calculations.\n * */\ncontract SwapsImplLocal is State {\n using SafeERC20 for IERC20;\n\n /**\n * @notice Swap two tokens.\n *\n * @param sourceTokenAddress The address of the source tokens.\n * @param destTokenAddress The address of the destiny tokens.\n *\n * @return destTokenAmountReceived The amount of destiny tokens sent.\n * @return sourceTokenAmountUsed The amount of source tokens spent.\n * */\n function internalSwap(\n address sourceTokenAddress,\n address destTokenAddress,\n address, /*receiverAddress*/\n address returnToSenderAddress,\n uint256 minSourceTokenAmount,\n uint256 maxSourceTokenAmount,\n uint256 requiredDestTokenAmount\n ) public payable returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) {\n require(sourceTokenAddress != destTokenAddress, \"source == dest\");\n\n (uint256 tradeRate, uint256 precision) =\n IPriceFeeds(priceFeeds).queryRate(sourceTokenAddress, destTokenAddress);\n\n if (requiredDestTokenAmount == 0) {\n sourceTokenAmountUsed = minSourceTokenAmount;\n destTokenAmountReceived = minSourceTokenAmount.mul(tradeRate).div(precision);\n } else {\n destTokenAmountReceived = requiredDestTokenAmount;\n sourceTokenAmountUsed = requiredDestTokenAmount.mul(precision).div(tradeRate);\n require(sourceTokenAmountUsed <= minSourceTokenAmount, \"destAmount too great\");\n }\n\n TestToken(sourceTokenAddress).burn(address(this), sourceTokenAmountUsed);\n TestToken(destTokenAddress).mint(address(this), destTokenAmountReceived);\n\n if (returnToSenderAddress != address(this)) {\n if (sourceTokenAmountUsed < maxSourceTokenAmount) {\n /// Send unused source token back.\n IERC20(sourceTokenAddress).safeTransfer(\n returnToSenderAddress,\n maxSourceTokenAmount - sourceTokenAmountUsed\n );\n }\n }\n }\n\n /**\n * @notice Calculate the expected price rate of swapping a given amount\n * of tokens.\n *\n * @param sourceTokenAddress The address of the source tokens.\n * @param destTokenAddress The address of the destiny tokens.\n * @param sourceTokenAmount The amount of source tokens.\n * @param unused Fourth parameter ignored.\n *\n * @return precision The expected price rate.\n * */\n function internalExpectedRate(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 sourceTokenAmount,\n address unused\n ) public view returns (uint256) {\n (uint256 sourceToDestRate, uint256 sourceToDestPrecision) =\n IPriceFeeds(priceFeeds).queryRate(sourceTokenAddress, destTokenAddress);\n\n return sourceTokenAmount.mul(sourceToDestRate).div(sourceToDestPrecision);\n }\n\n /**\n * @notice Calculate the expected return of swapping a given amount\n * of tokens.\n *\n * @param sourceTokenAddress The address of the source tokens.\n * @param destTokenAddress The address of the destiny tokens.\n * @param sourceTokenAmount The amount of source tokens.\n * @param unused Fourth parameter ignored.\n * @param defaultPath defaultPath for swap.\n *\n * @return precision The expected return.\n * */\n function internalExpectedReturn(\n address sourceTokenAddress,\n address destTokenAddress,\n uint256 sourceTokenAmount,\n address unused,\n IERC20[] memory defaultPath\n ) public view returns (uint256) {\n (uint256 sourceToDestRate, uint256 sourceToDestPrecision) =\n IPriceFeeds(priceFeeds).queryRate(sourceTokenAddress, destTokenAddress);\n\n return sourceTokenAmount.mul(sourceToDestRate).div(sourceToDestPrecision);\n }\n}\n" + }, + "contracts/swaps/SwapsUser.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC . All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../core/State.sol\";\nimport \"../feeds/IPriceFeeds.sol\";\nimport \"../events/SwapsEvents.sol\";\nimport \"../mixins/FeesHelper.sol\";\nimport \"./connectors/SwapsImplSovrynSwapLib.sol\";\n\n/**\n * @title Perform token swaps for loans and trades.\n * */\ncontract SwapsUser is State, SwapsEvents, FeesHelper {\n /**\n * @notice Internal loan swap.\n *\n * @param loanId The ID of the loan.\n * @param sourceToken The address of the source tokens.\n * @param destToken The address of destination tokens.\n * @param user The user address.\n * @param minSourceTokenAmount The minimum amount of source tokens to swap.\n * @param maxSourceTokenAmount The maximum amount of source tokens to swap.\n * @param requiredDestTokenAmount The required amount of destination tokens.\n * @param bypassFee To bypass or not the fee.\n * @param loanDataBytes The payload for the call. These loan DataBytes are\n * additional loan data (not in use for token swaps).\n *\n * @return destTokenAmountReceived\n * @return sourceTokenAmountUsed\n * @return sourceToDestSwapRate\n * */\n function _loanSwap(\n bytes32 loanId,\n address sourceToken,\n address destToken,\n address user,\n uint256 minSourceTokenAmount,\n uint256 maxSourceTokenAmount,\n uint256 requiredDestTokenAmount,\n bool bypassFee,\n bytes memory loanDataBytes\n )\n internal\n returns (\n uint256 destTokenAmountReceived,\n uint256 sourceTokenAmountUsed,\n uint256 sourceToDestSwapRate\n )\n {\n (destTokenAmountReceived, sourceTokenAmountUsed) = _swapsCall(\n [\n sourceToken,\n destToken,\n address(this), // receiver\n address(this), // returnToSender\n user\n ],\n [minSourceTokenAmount, maxSourceTokenAmount, requiredDestTokenAmount],\n loanId,\n bypassFee,\n loanDataBytes,\n false // swap external flag, set to false so that it will use the tradingFeePercent\n );\n\n /// Will revert if swap size too large.\n _checkSwapSize(sourceToken, sourceTokenAmountUsed);\n\n /// Will revert if disagreement found.\n sourceToDestSwapRate = IPriceFeeds(priceFeeds).checkPriceDisagreement(\n sourceToken,\n destToken,\n sourceTokenAmountUsed,\n destTokenAmountReceived,\n maxDisagreement\n );\n\n emit LoanSwap(\n loanId,\n sourceToken,\n destToken,\n user,\n sourceTokenAmountUsed,\n destTokenAmountReceived\n );\n }\n\n /**\n * @notice Calculate amount of source and destination tokens.\n *\n * @dev Wrapper for _swapsCall_internal function.\n *\n * @param addrs The array of addresses.\n * @param vals The array of values.\n * @param loanId The Id of the associated loan.\n * @param miscBool True/false to bypassFee.\n * @param loanDataBytes Additional loan data (not in use yet).\n *\n * @return destTokenAmountReceived The amount of destination tokens received.\n * @return sourceTokenAmountUsed The amount of source tokens used.\n * */\n function _swapsCall(\n address[5] memory addrs,\n uint256[3] memory vals,\n bytes32 loanId,\n bool miscBool, /// bypassFee\n bytes memory loanDataBytes,\n bool isSwapExternal\n ) internal returns (uint256, uint256) {\n /// addrs[0]: sourceToken\n /// addrs[1]: destToken\n /// addrs[2]: receiver\n /// addrs[3]: returnToSender\n /// addrs[4]: user\n /// vals[0]: minSourceTokenAmount\n /// vals[1]: maxSourceTokenAmount\n /// vals[2]: requiredDestTokenAmount\n\n require(vals[0] != 0 || vals[1] != 0, \"min or max source token amount needs to be set\");\n\n if (vals[1] == 0) {\n vals[1] = vals[0];\n }\n require(vals[0] <= vals[1], \"sourceAmount larger than max\");\n\n uint256 destTokenAmountReceived;\n uint256 sourceTokenAmountUsed;\n\n uint256 tradingFee;\n if (!miscBool) {\n /// bypassFee\n if (vals[2] == 0) {\n /// condition: vals[0] will always be used as sourceAmount\n\n if (isSwapExternal) {\n tradingFee = _getSwapExternalFee(vals[0]);\n } else {\n tradingFee = _getTradingFee(vals[0]);\n }\n\n if (tradingFee != 0) {\n _payTradingFee(\n addrs[4], /// user\n loanId,\n addrs[0], /// sourceToken (feeToken)\n addrs[1], /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward\n tradingFee\n );\n\n vals[0] = vals[0].sub(tradingFee);\n }\n } else {\n /// Condition: unknown sourceAmount will be used.\n\n if (isSwapExternal) {\n tradingFee = _getSwapExternalFee(vals[2]);\n } else {\n tradingFee = _getTradingFee(vals[2]);\n }\n\n if (tradingFee != 0) {\n vals[2] = vals[2].add(tradingFee);\n }\n }\n }\n\n require(loanDataBytes.length == 0, \"invalid state\");\n\n (destTokenAmountReceived, sourceTokenAmountUsed) = _swapsCall_internal(addrs, vals);\n\n if (vals[2] == 0) {\n /// There's no minimum destTokenAmount, but all of vals[0]\n /// (minSourceTokenAmount) must be spent.\n require(sourceTokenAmountUsed == vals[0], \"swap too large to fill\");\n\n if (tradingFee != 0) {\n sourceTokenAmountUsed = sourceTokenAmountUsed.add(tradingFee);\n }\n } else {\n /// There's a minimum destTokenAmount required, but\n /// sourceTokenAmountUsed won't be greater\n /// than vals[1] (maxSourceTokenAmount)\n require(sourceTokenAmountUsed <= vals[1], \"swap fill too large\");\n require(destTokenAmountReceived >= vals[2], \"insufficient swap liquidity\");\n\n if (tradingFee != 0) {\n _payTradingFee(\n addrs[4], /// user\n loanId, /// loanId,\n addrs[1], /// destToken (feeToken)\n addrs[0], /// pairToken (used to check if there is any special rebates or not) -- to pay fee reward\n tradingFee\n );\n\n destTokenAmountReceived = destTokenAmountReceived.sub(tradingFee);\n }\n }\n\n return (destTokenAmountReceived, sourceTokenAmountUsed);\n }\n\n /**\n * @notice Calculate amount of source and destination tokens.\n *\n * @dev Calls swapsImpl::internalSwap\n *\n * @param addrs The array of addresses.\n * @param vals The array of values.\n *\n * @return destTokenAmountReceived The amount of destination tokens received.\n * @return sourceTokenAmountUsed The amount of source tokens used.\n * */\n function _swapsCall_internal(address[5] memory addrs, uint256[3] memory vals)\n internal\n returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed)\n {\n SwapsImplSovrynSwapLib.SwapParams memory swapParams;\n\n swapParams.sourceTokenAddress = addrs[0];\n swapParams.destTokenAddress = addrs[1];\n swapParams.receiverAddress = addrs[2];\n swapParams.returnToSenderAddress = addrs[3];\n swapParams.minSourceTokenAmount = vals[0];\n swapParams.maxSourceTokenAmount = vals[1];\n swapParams.requiredDestTokenAmount = vals[2];\n\n (destTokenAmountReceived, sourceTokenAmountUsed) = SwapsImplSovrynSwapLib.swap(swapParams);\n }\n\n /**\n * @notice Calculate expected amount of destination tokens.\n *\n * @dev Calls swapsImpl::internalExpectedReturn\n *\n * @param sourceToken The address of the source tokens.\n * @param destToken The address of the destination tokens.\n * @param sourceTokenAmount The amount of the source tokens.\n *\n * @param destTokenAmount The amount of destination tokens.\n * */\n function _swapsExpectedReturn(\n address sourceToken,\n address destToken,\n uint256 sourceTokenAmount\n ) internal view returns (uint256 destTokenAmount) {\n destTokenAmount = SwapsImplSovrynSwapLib.getExpectedReturn(\n sourceToken,\n destToken,\n sourceTokenAmount\n );\n }\n\n /**\n * @notice Verify that the amount of tokens are under the swap limit.\n *\n * @dev Calls priceFeeds::amountInEth\n *\n * @param tokenAddress The address of the token to calculate price.\n * @param amount The amount of tokens to calculate price.\n * */\n function _checkSwapSize(address tokenAddress, uint256 amount) internal view {\n uint256 _maxSwapSize = maxSwapSize;\n if (_maxSwapSize != 0) {\n uint256 amountInEth;\n if (tokenAddress == address(wrbtcToken)) {\n amountInEth = amount;\n } else {\n amountInEth = IPriceFeeds(priceFeeds).amountInEth(tokenAddress, amount);\n }\n require(amountInEth <= _maxSwapSize, \"swap too large\");\n }\n }\n}\n" + }, + "contracts/testhelpers/FlashLoanerTest.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n// \"SPDX-License-Identifier: Apache-2.0\"\n\nimport \"../interfaces/IERC20.sol\";\nimport \"../openzeppelin/Ownable.sol\";\nimport \"./ITokenFlashLoanTest.sol\";\n\ncontract FlashLoanerTest is Ownable {\n function initiateFlashLoanTest(\n address loanToken,\n address iToken,\n uint256 flashLoanAmount\n ) internal returns (bytes memory success) {\n ITokenFlashLoanTest iTokenContract = ITokenFlashLoanTest(iToken);\n return\n iTokenContract.flashBorrow(\n flashLoanAmount,\n address(this),\n address(this),\n \"\",\n abi.encodeWithSignature(\n \"executeOperation(address,address,uint256)\",\n loanToken,\n iToken,\n flashLoanAmount\n )\n );\n }\n\n function repayFlashLoan(\n address loanToken,\n address iToken,\n uint256 loanAmount\n ) internal {\n IERC20(loanToken).transfer(iToken, loanAmount);\n }\n\n function executeOperation(\n address loanToken,\n address iToken,\n uint256 loanAmount\n ) external returns (bytes memory success) {\n emit BalanceOf(IERC20(loanToken).balanceOf(address(this)));\n emit ExecuteOperation(loanToken, iToken, loanAmount);\n repayFlashLoan(loanToken, iToken, loanAmount);\n return bytes(\"1\");\n }\n\n function doStuffWithFlashLoan(\n address token,\n address iToken,\n uint256 amount\n ) external onlyOwner {\n bytes memory result;\n emit BalanceOf(IERC20(token).balanceOf(address(this)));\n\n result = initiateFlashLoanTest(token, iToken, amount);\n\n emit BalanceOf(IERC20(token).balanceOf(address(this)));\n\n // after loan checks and what not.\n if (hashCompareWithLengthCheck(bytes(\"1\"), result)) {\n revert(\"failed executeOperation\");\n }\n }\n\n function hashCompareWithLengthCheck(bytes memory a, bytes memory b)\n internal\n pure\n returns (bool)\n {\n if (a.length != b.length) {\n return false;\n } else {\n return keccak256(a) == keccak256(b);\n }\n }\n\n event ExecuteOperation(address loanToken, address iToken, uint256 loanAmount);\n\n event BalanceOf(uint256 balance);\n}\n" + }, + "contracts/testhelpers/interfaces/IERC1820Registry.sol": { + "content": "pragma solidity ^0.5.0;\n\n/**\n * @dev Interface of the global ERC1820 Registry, as defined in the\n * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register\n * implementers for interfaces in this registry, as well as query support.\n *\n * Implementers may be shared by multiple accounts, and can also implement more\n * than a single interface for each account. Contracts can implement interfaces\n * for themselves, but externally-owned accounts (EOA) must delegate this to a\n * contract.\n *\n * {IERC165} interfaces can also be queried via the registry.\n *\n * For an in-depth explanation and source code analysis, see the EIP text.\n */\ninterface IERC1820Registry {\n /**\n * @dev Sets `newManager` as the manager for `account`. A manager of an\n * account is able to set interface implementers for it.\n *\n * By default, each account is its own manager. Passing a value of `0x0` in\n * `newManager` will reset the manager to this initial state.\n *\n * Emits a {ManagerChanged} event.\n *\n * Requirements:\n *\n * - the caller must be the current manager for `account`.\n */\n function setManager(address account, address newManager) external;\n\n /**\n * @dev Returns the manager for `account`.\n *\n * See {setManager}.\n */\n function getManager(address account) external view returns (address);\n\n /**\n * @dev Sets the `implementer` contract as `account`'s implementer for\n * `interfaceHash`.\n *\n * `account` being the zero address is an alias for the caller's address.\n * The zero address can also be used in `implementer` to remove an old one.\n *\n * See {interfaceHash} to learn how these are created.\n *\n * Emits an {InterfaceImplementerSet} event.\n *\n * Requirements:\n *\n * - the caller must be the current manager for `account`.\n * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not\n * end in 28 zeroes).\n * - `implementer` must implement {IERC1820Implementer} and return true when\n * queried for support, unless `implementer` is the caller. See\n * {IERC1820Implementer-canImplementInterfaceForAddress}.\n */\n function setInterfaceImplementer(\n address account,\n bytes32 interfaceHash,\n address implementer\n ) external;\n\n /**\n * @dev Returns the implementer of `interfaceHash` for `account`. If no such\n * implementer is registered, returns the zero address.\n *\n * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28\n * zeroes), `account` will be queried for support of it.\n *\n * `account` being the zero address is an alias for the caller's address.\n */\n function getInterfaceImplementer(address account, bytes32 interfaceHash)\n external\n view\n returns (address);\n\n /**\n * @dev Returns the interface hash for an `interfaceName`, as defined in the\n * corresponding\n * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].\n */\n function interfaceHash(string calldata interfaceName) external pure returns (bytes32);\n\n /**\n * @notice Updates the cache with whether the contract implements an ERC165 interface or not.\n * @param account Address of the contract for which to update the cache.\n * @param interfaceId ERC165 interface for which to update the cache.\n */\n function updateERC165Cache(address account, bytes4 interfaceId) external;\n\n /**\n * @notice Checks whether a contract implements an ERC165 interface or not.\n * If the result is not cached a direct lookup on the contract address is performed.\n * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling\n * {updateERC165Cache} with the contract address.\n * @param account Address of the contract to check.\n * @param interfaceId ERC165 interface to check.\n * @return True if `account` implements `interfaceId`, false otherwise.\n */\n function implementsERC165Interface(address account, bytes4 interfaceId)\n external\n view\n returns (bool);\n\n /**\n * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.\n * @param account Address of the contract to check.\n * @param interfaceId ERC165 interface to check.\n * @return True if `account` implements `interfaceId`, false otherwise.\n */\n function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId)\n external\n view\n returns (bool);\n\n event InterfaceImplementerSet(\n address indexed account,\n bytes32 indexed interfaceHash,\n address indexed implementer\n );\n\n event ManagerChanged(address indexed account, address indexed newManager);\n}\n" + }, + "contracts/testhelpers/ITokenFlashLoanTest.sol": { + "content": "pragma solidity ^0.5.17;\npragma experimental ABIEncoderV2;\n\n// \"SPDX-License-Identifier: Apache-2.0\"\n\ninterface ITokenFlashLoanTest {\n function flashBorrow(\n uint256 borrowAmount,\n address borrower,\n address target,\n string calldata signature,\n bytes calldata data\n ) external payable returns (bytes memory);\n}\n" + }, + "contracts/testhelpers/LoanTokenLogicTest.sol": { + "content": "pragma solidity 0.5.17;\npragma experimental ABIEncoderV2;\n\nimport \"../connectors/loantoken/modules/beaconLogicLM/LoanTokenLogic.sol\";\n\ncontract LoanTokenLogicTest is LoanTokenLogic {\n function getMarginBorrowAmountAndRate(uint256 leverageAmount, uint256 depositAmount)\n public\n view\n returns (uint256, uint256)\n {\n return _getMarginBorrowAmountAndRate(leverageAmount, depositAmount);\n }\n}\n" + }, + "contracts/testhelpers/reentrancy/TestNonReentrantValueSetter.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../reentrancy/SharedReentrancyGuard.sol\";\n\ncontract TestNonReentrantValueSetter is SharedReentrancyGuard {\n uint256 public value;\n\n // This will fail if another globallyNonReentrant function has already been entered\n function setValue(uint256 newValue) public globallyNonReentrant {\n value = newValue;\n }\n\n // this will always fail if `other.setValue` is globallyNonReentrant\n function setOtherContractValueNonReentrant(address other, uint256 newValue)\n external\n globallyNonReentrant\n {\n TestNonReentrantValueSetter(other).setValue(newValue);\n }\n\n // this is intentionally not globallyNonReentrant and should work even if both contracts are non-reentrant\n function setThisAndOtherContractValue(address other, uint256 newValue) external {\n setValue(newValue);\n TestNonReentrantValueSetter(other).setValue(newValue);\n }\n}\n" + }, + "contracts/testhelpers/reentrancy/TestValueSetterProxy.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../proxy/UpgradableProxy.sol\";\n\ncontract TestValueSetterProxy is UpgradableProxy {\n // This is here for the memory layout\n uint256 public value;\n}\n" + }, + "contracts/testhelpers/staking/StakingTester.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../../governance/Staking/interfaces/IStaking.sol\";\nimport \"../TestToken.sol\";\n\ncontract StakingTester {\n IStaking public staking;\n TestToken public token;\n\n constructor(address _staking, address _token) public {\n staking = IStaking(_staking);\n token = TestToken(_token);\n }\n\n function stakeAndWithdraw(uint96 _amount, uint256 _until) public {\n token.mint(address(this), _amount);\n token.approve(address(staking), _amount);\n staking.stake(_amount, _until, address(this), address(this));\n staking.withdraw(_amount, _until, address(this));\n }\n\n function stakeAndDelegate(\n uint96 _amount,\n address _delegatee,\n uint256 _lockDate\n ) public {\n token.mint(address(this), _amount);\n token.approve(address(staking), _amount);\n staking.stake(_amount, _lockDate, address(this), address(this));\n staking.delegate(_delegatee, _lockDate);\n }\n}\n" + }, + "contracts/testhelpers/TestCoverage.sol": { + "content": "/**\n * In order to test some functionalities like Pausable::pausable() modifier,\n * it is required to add a contract to invoke them and get a full coverage on tests.\n */\n\npragma solidity 0.5.17;\n\nimport \"../connectors/loantoken/Pausable.sol\";\nimport \"../governance/Staking/SafeMath96.sol\";\nimport \"../mixins/EnumerableBytes32Set.sol\";\nimport \"../mixins/VaultController.sol\";\nimport \"../connectors/loantoken/AdvancedToken.sol\";\nimport \"../connectors/loantoken/LoanTokenLogicStorage.sol\";\n\ncontract TestCoverage is\n Pausable,\n SafeMath96,\n VaultController,\n AdvancedToken,\n LoanTokenLogicStorage\n{\n /// @dev Pausable is currently an unused contract that still is operative\n /// because margin trade flashloan functionality has been commented out.\n /// In case it were restored, contract would become used again, so for a\n /// complete test coverage it is required to test it.\n\n function dummyPausableFunction() external pausable(msg.sig) {\n /// @dev do nothing, just to check if modifier is working\n }\n\n /// @dev This function should be located on Pausable contract in the case\n /// it has to be used again by flashloan restoration.\n function togglePause(\n string memory funcId, // example: \"mint(uint256,uint256)\"\n bool isPaused\n ) public {\n /// keccak256(\"Pausable_FunctionPause\")\n bytes32 slot =\n keccak256(\n abi.encodePacked(\n bytes4(keccak256(abi.encodePacked(funcId))),\n uint256(0xa7143c84d793a15503da6f19bf9119a2dac94448ca45d77c8bf08f57b2e91047)\n )\n );\n\n // solhint-disable-next-line no-inline-assembly\n assembly {\n sstore(slot, isPaused)\n }\n }\n\n /// @dev Testing internal functions of governance/Staking/SafeMath96.sol\n function testSafeMath96_safe32(uint256 n) public pure returns (uint32) {\n // Public wrapper for SafeMath96 internal function\n return safe32(n, \"overflow\");\n }\n\n function testSafeMath96_safe64(uint256 n) public pure returns (uint64) {\n // Public wrapper for SafeMath96 internal function\n return safe64(n, \"overflow\");\n }\n\n function testSafeMath96_safe96(uint256 n) public pure returns (uint96) {\n // Public wrapper for SafeMath96 internal function\n return safe96(n, \"overflow\");\n }\n\n function testSafeMath96_sub96(uint96 a, uint96 b) public pure returns (uint96) {\n // Public wrapper for SafeMath96 internal function\n return sub96(a, b, \"underflow\");\n }\n\n function testSafeMath96_mul96(uint96 a, uint96 b) public pure returns (uint96) {\n // Public wrapper for SafeMath96 internal function\n return mul96(a, b, \"overflow\");\n }\n\n function testSafeMath96_div96(uint96 a, uint96 b) public pure returns (uint96) {\n // Public wrapper for SafeMath96 internal function\n return div96(a, b, \"division by 0\");\n }\n\n using EnumerableBytes32Set for EnumerableBytes32Set.Bytes32Set;\n EnumerableBytes32Set.Bytes32Set internal aSet;\n\n function testEnum_AddRemove(bytes32 a, bytes32 b) public returns (bool) {\n aSet.addBytes32(a);\n return aSet.removeBytes32(b);\n }\n\n function testEnum_AddAddress(address a, address b) public returns (bool) {\n aSet.addAddress(a);\n return aSet.containsAddress(b);\n }\n\n function testEnum_AddAddressesAndEnumerate(\n address a,\n address b,\n uint256 start,\n uint256 count\n ) public returns (bytes32[] memory) {\n aSet.addAddress(a);\n aSet.addAddress(b);\n return aSet.enumerate(start, count);\n }\n\n /// @dev Wrapper to test internal function never called along current codebase\n function testVaultController_vaultApprove(\n address token,\n address to,\n uint256 value\n ) public {\n vaultApprove(token, to, value);\n }\n\n /// @dev mint wrapper w/o previous checks\n function testMint(\n address _to,\n uint256 _tokenAmount,\n uint256 _assetAmount,\n uint256 _price\n ) public {\n _mint(_to, _tokenAmount, _assetAmount, _price);\n }\n\n /// @dev wrapper for a function unreachable to tests\n function testStringToBytes32(string memory source) public pure returns (bytes32 result) {\n return stringToBytes32(source);\n }\n}\n" + }, + "contracts/testhelpers/TestCrossReentrancyERC777.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../interfaces/ILoanTokenModules.sol\";\nimport \"../interfaces/IERC20.sol\";\nimport \"../connectors/loantoken/interfaces/ProtocolLike.sol\";\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"../interfaces/IWrbtcERC20.sol\";\nimport \"./interfaces/IERC1820Registry.sol\";\nimport \"../mockup/MockLoanTokenLogic.sol\";\n\n/**\n * @dev This is the smart contract wrapper that is designed to test the cross-reentrancy attack between the protocol & loan token contract.\n * The cross-reentrancy can be triggered from the closeWithSwap, closeWithDeposit, liquidate, rollover since it might send the RBTC / ERC777 back to the receiver for refunding the excess of the swap.\n * This wrapper function will try to:\n * 1. Borrow some ERC777 from the lending pool.\n * 2. Close the loan with closeWithDeposit function in the protocol.\n * 3. Burn all iERC777.\n *\n * The cross-reentrancy happened in step#3. It might happened through a hook function (tokensToSend) that is implemented in this contract to support the ERC777 transfer.\n * Inside the hook function, it will try to mint the iERC777.\n * The details about the hook functions can be found here: https://eips.ethereum.org/EIPS/eip-777#hooks\n *\n * This function should never been passed in the unit testing since we have:\n * 1. invariant check for the loan token (iToken) total supply for closeWithDeposit function.\n * 2. global reentrancy guard between the protocol & the loan token.\n */\n\ncontract TestCrossReentrancyERC777 {\n address public loanToken;\n address public WRBTC;\n address public SUSD; /// ERC777\n ProtocolLike public sovrynProtocol;\n\n IERC1820Registry internal constant ERC1820_REGISTRY =\n IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);\n\n using SafeMath for uint256;\n\n struct balanceState {\n uint256 rbtcBalance;\n uint256 wrbtcBalance;\n uint256 susdBalance;\n uint256 iUSDTBalance;\n }\n\n function() external payable {}\n\n constructor(\n address _loanToken,\n address _WRBTC,\n address _SUSD,\n address _sovrynProtocol\n ) public {\n loanToken = _loanToken;\n WRBTC = _WRBTC;\n SUSD = _SUSD;\n sovrynProtocol = ProtocolLike(_sovrynProtocol);\n\n ERC1820_REGISTRY.setInterfaceImplementer(\n address(this),\n keccak256(\"ERC777TokensSender\"),\n address(this)\n );\n ERC1820_REGISTRY.setInterfaceImplementer(\n address(this),\n keccak256(\"ERC20Token\"),\n address(this)\n );\n }\n\n function testCrossReentrancy(uint256 withdrawAmount, uint256 collateralTokenSent) public {\n address _receiver = address(this);\n address _borrower = address(this);\n\n // step 1, borrow\n // prerequisite: WRBTC has been transferred to this contract\n balanceState memory initial =\n balanceState({\n rbtcBalance: address(this).balance,\n wrbtcBalance: IERC20(WRBTC).balanceOf(address(this)),\n susdBalance: IERC20(SUSD).balanceOf(address(this)),\n iUSDTBalance: ILoanTokenModules(loanToken).balanceOf(_borrower)\n });\n\n IERC20(WRBTC).approve(loanToken, initial.susdBalance);\n\n ILoanTokenModules(loanToken).borrow(\n bytes32(0),\n withdrawAmount,\n 10000,\n collateralTokenSent,\n WRBTC,\n _borrower,\n _receiver,\n \"\"\n );\n\n uint256 _borrowerNonce = sovrynProtocol.borrowerNonce(_borrower);\n bytes32 loanParamsLocalId =\n ILoanTokenModules(loanToken).loanParamsIds(\n uint256(keccak256(abi.encodePacked(WRBTC, true)))\n );\n bytes32 loan_id =\n keccak256(abi.encodePacked(loanParamsLocalId, loanToken, _borrower, _borrowerNonce));\n\n // STEP 3 close the borrowed position with a deposit\n uint256 _SUSDBalance = IERC20(SUSD).balanceOf(address(this));\n IERC20(SUSD).approve(address(sovrynProtocol), _SUSDBalance);\n sovrynProtocol.closeWithDeposit(\n loan_id,\n address(this),\n collateralTokenSent.mul(20).div(100) // make it 20% higher from initial borrow amount\n );\n\n /** Rest of code Should not be executed as in there will be reverted in step #3 because of invariant check.\n if it's got executed, means that there is an cross-reentrancy vulnerability */\n // STEP 4 Burn all iSUSD\n uint256 _iSUSDBalance = ILoanTokenModules(loanToken).balanceOf(_borrower);\n ILoanTokenModules(loanToken).burn(_receiver, _iSUSDBalance);\n\n /** Used for debugging */\n // balanceState memory finalBalance =\n // balanceState({\n // rbtcBalance: address(this).balance,\n // wrbtcBalance: IERC20(WRBTC).balanceOf(address(this)),\n // susdBalance: IERC20(SUSD).balanceOf(address(this)),\n // iUSDTBalance: ILoanTokenModules(loanToken).balanceOf(_borrower)\n // });\n }\n\n function tokensToSend(\n address operator,\n address from,\n address to,\n uint256,\n bytes calldata,\n bytes calldata\n ) external {\n if (operator == address(sovrynProtocol) && to == loanToken && from == address(this)) {\n uint256 _SUSDBalance = IERC20(SUSD).balanceOf(address(this));\n IERC20(SUSD).approve(loanToken, _SUSDBalance);\n\n ILoanTokenModules(loanToken).mint(address(this), 1000000 ether); // unable to reentrant mint here since mint function have reentrancy guard in place\n }\n }\n}\n" + }, + "contracts/testhelpers/TestCrossReentrancyRBTC.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../interfaces/ILoanTokenModules.sol\";\nimport \"../interfaces/IERC20.sol\";\nimport \"../connectors/loantoken/interfaces/ProtocolLike.sol\";\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"../interfaces/IWrbtcERC20.sol\";\nimport \"../mockup/MockLoanTokenLogic.sol\";\n\n/**\n * @dev This is the smart contract wrapper that is designed to test the cross-reentrancy attack between the protocol & loan token contract.\n * The cross-reentrancy can be triggered from the closeWithSwap, closeWithDeposit, liquidate, rollover since it might send the RBTC / ERC777 back to the receiver for refunding the excess of the swap.\n * This wrapper function will try to:\n * 1. Borrow some WRBTC from the lending pool.\n * 2. Close the loan with closeWithSwap function in the protocol.\n * 3. Burn all iWRBTC.\n *\n * The refund happened in step #3, which will send back the RBTC back to this contract.\n * Then, this contract will try to do another iWRBTC minting to the loan token --> this is where the cross-reentrancy happened between the protocol & the loan token contract.\n *\n * This function should never been passed in the unit testing since we have:\n * 1. invariant check for the loan token (iToken) total supply for closeWithSwap function.\n * 2. global reentrancy guard between the protocol & the loan token.\n */\n\ncontract TestCrossReentrancyRBTC {\n address public loanTokenWRBTC;\n address public WRBTC;\n address public SUSD;\n ProtocolLike public sovrynProtocol;\n\n using SafeMath for uint256;\n\n struct balanceState {\n uint256 rbtcBalance;\n uint256 wrbtcBalance;\n uint256 susdBalance;\n uint256 iWRBTCBalance;\n }\n\n function() external payable {\n if (msg.sender == address(sovrynProtocol)) {\n uint256 latestRBTCBalance = address(this).balance;\n IWrbtcERC20(WRBTC).deposit.value(14 ether)();\n uint256 _WRBTCBalance = IERC20(WRBTC).balanceOf(address(this));\n IERC20(WRBTC).approve(loanTokenWRBTC, _WRBTCBalance);\n\n ILoanTokenModules(loanTokenWRBTC).mint(address(this), 14 ether); // unable to reentrant mint here since mint function have reentrancy guard in place\n }\n }\n\n constructor(\n address _loanTokenWRBTC,\n address _WRBTC,\n address _SUSD,\n address _sovrynProtocol\n ) public {\n loanTokenWRBTC = _loanTokenWRBTC;\n WRBTC = _WRBTC;\n SUSD = _SUSD;\n sovrynProtocol = ProtocolLike(_sovrynProtocol);\n }\n\n function testCrossReentrancy(uint256 withdrawAmount, uint256 collateralTokenSent) public {\n address _receiver = address(this);\n address _borrower = address(this);\n\n // step 1, borrow\n // prerequisite: SUSD has been transferred to this contract\n balanceState memory initial =\n balanceState({\n rbtcBalance: address(this).balance,\n wrbtcBalance: IERC20(WRBTC).balanceOf(address(this)),\n susdBalance: IERC20(SUSD).balanceOf(address(this)),\n iWRBTCBalance: ILoanTokenModules(loanTokenWRBTC).balanceOf(_borrower)\n });\n\n IERC20(SUSD).approve(loanTokenWRBTC, initial.susdBalance);\n\n ILoanTokenModules(loanTokenWRBTC).borrow(\n bytes32(0),\n withdrawAmount,\n 10000,\n collateralTokenSent,\n SUSD,\n _borrower,\n _receiver,\n \"\"\n );\n\n uint256 _borrowerNonce = sovrynProtocol.borrowerNonce(_borrower);\n bytes32 loanParamsLocalId =\n ILoanTokenModules(loanTokenWRBTC).loanParamsIds(\n uint256(keccak256(abi.encodePacked(SUSD, true)))\n );\n bytes32 loan_id =\n keccak256(\n abi.encodePacked(loanParamsLocalId, loanTokenWRBTC, _borrower, _borrowerNonce)\n );\n\n // STEP 3 close the borrowed position with a swap (probably works just as well with deposit)\n sovrynProtocol.closeWithSwap(\n loan_id,\n msg.sender,\n collateralTokenSent.mul(200).div(100), // make it 20% higher from initial collateral sent to make sure whole position is closed\n true,\n \"\"\n );\n\n /** Rest of code Should not be executed as in there will be reverted in step #3 because of invariant check.\n if it's got executed, means that there is an cross-reentrancy vulnerability */\n // STEP 4 Burn all iRBTC\n uint256 _iWRBTCBalance = ILoanTokenModules(loanTokenWRBTC).balanceOf(_borrower);\n ILoanTokenModules(loanTokenWRBTC).burn(_receiver, _iWRBTCBalance);\n\n /** Used for debugging */\n // balanceState memory finalBalance =\n // balanceState({\n // rbtcBalance: address(this).balance,\n // wrbtcBalance: IERC20(WRBTC).balanceOf(address(this)),\n // susdBalance: IERC20(SUSD).balanceOf(address(this)),\n // iWRBTCBalance: ILoanTokenModules(loanTokenWRBTC).balanceOf(_borrower)\n // });\n }\n}\n" + }, + "contracts/testhelpers/TestLibraries.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../rsk/RSKAddrValidator.sol\";\n\n// contract for testing libraries\ncontract TestLibraries {\n /*\n * @param addr it is an address to check that it does not originates from\n * signing with PK = ZERO. RSK has a small difference in which @ZERO_PK_ADDR is\n * also an address from PK = ZERO. So we check for both of them.\n */\n function RSKAddrValidator_checkPKNotZero(address addr) public pure returns (bool) {\n return (RSKAddrValidator.checkPKNotZero(addr));\n }\n\n /*\n * Safely compares two addresses, checking they do not originate from\n * a zero private key\n */\n function RSKAddrValidator_safeEquals(address addr1, address addr2) public pure returns (bool) {\n return (RSKAddrValidator.safeEquals(addr1, addr2));\n }\n}\n" + }, + "contracts/testhelpers/TestSovrynSwap.sol": { + "content": "/**\n * Test file simulating the SovrynSwap network\n * */\n\npragma solidity 0.5.17;\n\nimport \"../openzeppelin/SafeERC20.sol\";\nimport \"../feeds/IPriceFeeds.sol\";\nimport \"./TestToken.sol\";\nimport \"../openzeppelin/SafeMath.sol\";\n\ncontract TestSovrynSwap {\n using SafeERC20 for IERC20;\n using SafeMath for uint256;\n\n address public priceFeeds;\n\n constructor(address feed) public {\n priceFeeds = feed;\n }\n\n /**\n * simulating the contract registry. always returns the address of this contract\n * */\n function addressOf(bytes32 contractName) public view returns (address) {\n return address(this);\n }\n\n /**\n * calculates the return tokens when swapping _amount, makes sure the return is bigger than _minReturn,\n * mints and burns the test tokens accordingly.\n * */\n function convertByPath(\n IERC20[] calldata _path,\n uint256 _amount,\n uint256 _minReturn,\n address _beneficiary,\n address _affiliateAccount,\n uint256 _affiliateFee\n ) external payable returns (uint256) {\n //compute the return for the amount of tokens provided\n (uint256 sourceToDestRate, uint256 sourceToDestPrecision) =\n IPriceFeeds(priceFeeds).queryRate(address(_path[0]), address(_path[1]));\n uint256 actualReturn = _amount.mul(sourceToDestRate).div(sourceToDestPrecision);\n\n require(actualReturn >= _minReturn, \"insufficient source tokens provided\");\n\n TestToken(address(_path[0])).burn(address(msg.sender), _amount);\n TestToken(address(_path[1])).mint(address(_beneficiary), actualReturn);\n return actualReturn;\n }\n\n /**\n * queries the rate from the Price Feed contract and computes the expected return amount based on the\n * amout of source tokens to be swapped.\n * */\n function rateByPath(IERC20[] calldata _path, uint256 _amount) external view returns (uint256) {\n (uint256 sourceToDestRate, uint256 sourceToDestPrecision) =\n IPriceFeeds(priceFeeds).queryRate(address(_path[0]), address(_path[1]));\n\n return _amount.mul(sourceToDestRate).div(sourceToDestPrecision);\n }\n\n /**\n * returns the conversion path -> always a direct path\n * */\n function conversionPath(IERC20 _sourceToken, IERC20 _targetToken)\n external\n view\n returns (IERC20[] memory)\n {\n IERC20[] memory path = new IERC20[](2);\n path[0] = _sourceToken;\n path[1] = _targetToken;\n return path;\n }\n}\n" + }, + "contracts/testhelpers/TestToken.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../openzeppelin/SafeMath.sol\";\n\ncontract TestToken {\n using SafeMath for uint256;\n\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(address indexed owner, address indexed spender, uint256 value);\n event AllowanceUpdate(\n address indexed owner,\n address indexed spender,\n uint256 valueBefore,\n uint256 valueAfter\n );\n event Mint(address indexed minter, uint256 value);\n event Burn(address indexed burner, uint256 value);\n\n string public name;\n string public symbol;\n uint8 public decimals;\n\n mapping(address => uint256) internal balances;\n mapping(address => mapping(address => uint256)) internal allowed;\n uint256 internal totalSupply_;\n\n constructor(\n string memory _name,\n string memory _symbol,\n uint8 _decimals,\n uint256 _initialAmount\n ) public {\n name = _name;\n symbol = _symbol;\n decimals = _decimals;\n\n if (_initialAmount != 0) {\n mint(msg.sender, _initialAmount);\n }\n }\n\n function approve(address _spender, uint256 _value) public returns (bool) {\n allowed[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n function transfer(address _to, uint256 _value) public returns (bool) {\n require(_value <= balances[msg.sender] && _to != address(0), \"invalid transfer\");\n\n balances[msg.sender] = balances[msg.sender].sub(_value);\n balances[_to] = balances[_to].add(_value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public returns (bool) {\n uint256 allowanceAmount = allowed[_from][msg.sender];\n require(\n _value <= balances[_from] && _value <= allowanceAmount && _to != address(0),\n \"invalid transfer\"\n );\n\n balances[_from] = balances[_from].sub(_value);\n balances[_to] = balances[_to].add(_value);\n if (allowanceAmount < uint256(-1)) {\n allowed[_from][msg.sender] = allowanceAmount.sub(_value);\n /// @dev Allowance mapping update requires an event log\n emit AllowanceUpdate(_from, msg.sender, allowanceAmount, allowed[_from][msg.sender]);\n }\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function mint(address _to, uint256 _value) public {\n require(_to != address(0), \"no burn allowed\");\n totalSupply_ = totalSupply_.add(_value);\n balances[_to] = balances[_to].add(_value);\n\n emit Mint(_to, _value);\n emit Transfer(address(0), _to, _value);\n }\n\n function burn(address _who, uint256 _value) public {\n require(_value <= balances[_who], \"balance too low\");\n // no need to require _value <= totalSupply, since that would imply the\n // sender's balance is greater than the totalSupply, which *should* be an assertion failure\n\n balances[_who] = balances[_who].sub(_value);\n totalSupply_ = totalSupply_.sub(_value);\n\n emit Burn(_who, _value);\n emit Transfer(_who, address(0), _value);\n }\n\n function totalSupply() public view returns (uint256) {\n return totalSupply_;\n }\n\n function balanceOf(address _owner) public view returns (uint256) {\n return balances[_owner];\n }\n\n function allowance(address _owner, address _spender) public view returns (uint256) {\n return allowed[_owner][_spender];\n }\n}\n" + }, + "contracts/testhelpers/TestTokenERC777.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../openzeppelin/Context.sol\";\nimport \"../openzeppelin/SafeMath.sol\";\nimport \"../openzeppelin/Address.sol\";\nimport \"../interfaces/IERC777.sol\";\nimport \"../interfaces/IERC777Recipient.sol\";\nimport \"../interfaces/IERC777Sender.sol\";\nimport \"../interfaces/IERC20.sol\";\nimport \"./interfaces/IERC1820Registry.sol\";\n\ncontract TestTokenERC777 is Context, IERC777, IERC20 {\n using SafeMath for uint256;\n using Address for address;\n\n IERC1820Registry internal constant ERC1820_REGISTRY =\n IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);\n\n mapping(address => uint256) private _balances;\n\n uint256 private _totalSupply;\n\n // We inline the result of the following hashes because Solidity doesn't resolve them at compile time.\n // See https://github.com/ethereum/solidity/issues/4024.\n\n // keccak256(\"ERC777TokensSender\")\n bytes32 private constant TOKENS_SENDER_INTERFACE_HASH =\n 0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895;\n\n // keccak256(\"ERC777TokensRecipient\")\n bytes32 private constant TOKENS_RECIPIENT_INTERFACE_HASH =\n 0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;\n\n // This isn't ever read from - it's only used to respond to the defaultOperators query.\n address[] private _defaultOperatorsArray;\n\n // Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators).\n mapping(address => bool) private _defaultOperators;\n\n // For each account, a mapping of its operators and revoked default operators.\n mapping(address => mapping(address => bool)) private _operators;\n mapping(address => mapping(address => bool)) private _revokedDefaultOperators;\n\n // ERC20-allowances\n mapping(address => mapping(address => uint256)) private _allowances;\n\n /**\n * @dev `defaultOperators` may be an empty array.\n */\n constructor(\n string memory _name,\n string memory _symbol,\n uint256 _initialSupply,\n uint8 _decimals,\n address[] memory defaultOperators\n ) public {\n name = _name;\n symbol = _symbol;\n decimals = _decimals;\n\n _defaultOperatorsArray = defaultOperators;\n for (uint256 i = 0; i < _defaultOperatorsArray.length; i++) {\n _defaultOperators[_defaultOperatorsArray[i]] = true;\n }\n\n _mint(msg.sender, msg.sender, _initialSupply, \"\", \"\");\n\n // register interfaces\n ERC1820_REGISTRY.setInterfaceImplementer(\n address(this),\n keccak256(\"ERC777Token\"),\n address(this)\n );\n ERC1820_REGISTRY.setInterfaceImplementer(\n address(this),\n keccak256(\"ERC20Token\"),\n address(this)\n );\n }\n\n /**\n * @dev See {IERC777-granularity}.\n *\n * This implementation always returns `1`.\n */\n function granularity() public view returns (uint256) {\n return 1;\n }\n\n /**\n * @dev See {IERC777-totalSupply}.\n */\n function totalSupply() public view returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev Returns the amount of tokens owned by an account (`tokenHolder`).\n */\n function balanceOf(address tokenHolder) public view returns (uint256) {\n return _balances[tokenHolder];\n }\n\n /**\n * @dev See {IERC777-send}.\n *\n * Also emits a {IERC20-Transfer} event for ERC20 compatibility.\n */\n function send(\n address recipient,\n uint256 amount,\n bytes memory data\n ) public {\n _send(_msgSender(), _msgSender(), recipient, amount, data, \"\", true);\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Unlike `send`, `recipient` is _not_ required to implement the {IERC777Recipient}\n * interface if it is a contract.\n *\n * Also emits a {Sent} event.\n */\n function transfer(address recipient, uint256 amount) public returns (bool) {\n require(recipient != address(0), \"ERC777: transfer to the zero address\");\n\n address from = _msgSender();\n\n _callTokensToSend(from, from, recipient, amount, \"\", \"\");\n\n _move(from, from, recipient, amount, \"\", \"\");\n\n _callTokensReceived(from, from, recipient, amount, \"\", \"\", false);\n\n return true;\n }\n\n /**\n * @dev See {IERC777-burn}.\n *\n * Also emits a {IERC20-Transfer} event for ERC20 compatibility.\n */\n function burn(uint256 amount, bytes memory data) public {\n _burn(_msgSender(), _msgSender(), amount, data, \"\");\n }\n\n /**\n * @dev See {IERC777-isOperatorFor}.\n */\n function isOperatorFor(address operator, address tokenHolder) public view returns (bool) {\n return\n operator == tokenHolder ||\n (_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) ||\n _operators[tokenHolder][operator];\n }\n\n /**\n * @dev See {IERC777-authorizeOperator}.\n */\n function authorizeOperator(address operator) public {\n require(_msgSender() != operator, \"ERC777: authorizing self as operator\");\n\n if (_defaultOperators[operator]) {\n delete _revokedDefaultOperators[_msgSender()][operator];\n } else {\n _operators[_msgSender()][operator] = true;\n }\n\n emit AuthorizedOperator(operator, _msgSender());\n }\n\n /**\n * @dev See {IERC777-revokeOperator}.\n */\n function revokeOperator(address operator) public {\n require(operator != _msgSender(), \"ERC777: revoking self as operator\");\n\n if (_defaultOperators[operator]) {\n _revokedDefaultOperators[_msgSender()][operator] = true;\n } else {\n delete _operators[_msgSender()][operator];\n }\n\n emit RevokedOperator(operator, _msgSender());\n }\n\n /**\n * @dev See {IERC777-defaultOperators}.\n */\n function defaultOperators() public view returns (address[] memory) {\n return _defaultOperatorsArray;\n }\n\n /**\n * @dev See {IERC777-operatorSend}.\n *\n * Emits {Sent} and {IERC20-Transfer} events.\n */\n function operatorSend(\n address sender,\n address recipient,\n uint256 amount,\n bytes memory data,\n bytes memory operatorData\n ) public {\n require(\n isOperatorFor(_msgSender(), sender),\n \"ERC777: caller is not an operator for holder\"\n );\n _send(_msgSender(), sender, recipient, amount, data, operatorData, true);\n }\n\n /**\n * @dev See {IERC777-operatorBurn}.\n *\n * Emits {Burned} and {IERC20-Transfer} events.\n */\n function operatorBurn(\n address account,\n uint256 amount,\n bytes memory data,\n bytes memory operatorData\n ) public {\n require(\n isOperatorFor(_msgSender(), account),\n \"ERC777: caller is not an operator for holder\"\n );\n _burn(_msgSender(), account, amount, data, operatorData);\n }\n\n /**\n * @dev See {IERC20-allowance}.\n *\n * Note that operator and allowance concepts are orthogonal: operators may\n * not have allowance, and accounts with allowance may not be operators\n * themselves.\n */\n function allowance(address holder, address spender) public view returns (uint256) {\n return _allowances[holder][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Note that accounts cannot have allowance issued by their operators.\n */\n function approve(address spender, uint256 value) public returns (bool) {\n address holder = _msgSender();\n _approve(holder, spender, value);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Note that operator and allowance concepts are orthogonal: operators cannot\n * call `transferFrom` (unless they have allowance), and accounts with\n * allowance cannot call `operatorSend` (unless they are operators).\n *\n * Emits {Sent}, {IERC20-Transfer} and {IERC20-Approval} events.\n */\n function transferFrom(\n address holder,\n address recipient,\n uint256 amount\n ) public returns (bool) {\n require(recipient != address(0), \"ERC777: transfer to the zero address\");\n require(holder != address(0), \"ERC777: transfer from the zero address\");\n\n address spender = _msgSender();\n\n _callTokensToSend(spender, holder, recipient, amount, \"\", \"\");\n\n _move(spender, holder, recipient, amount, \"\", \"\");\n\n _approve(\n holder,\n spender,\n _allowances[holder][spender].sub(amount, \"ERC777: transfer amount exceeds allowance\")\n );\n\n _callTokensReceived(spender, holder, recipient, amount, \"\", \"\", false);\n\n return true;\n }\n\n /**\n * @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * If a send hook is registered for `account`, the corresponding function\n * will be called with `operator`, `data` and `operatorData`.\n *\n * See {IERC777Sender} and {IERC777Recipient}.\n *\n * Emits {Minted} and {IERC20-Transfer} events.\n *\n * Requirements\n *\n * - `account` cannot be the zero address.\n * - if `account` is a contract, it must implement the {IERC777Recipient}\n * interface.\n */\n function _mint(\n address operator,\n address account,\n uint256 amount,\n bytes memory userData,\n bytes memory operatorData\n ) internal {\n require(account != address(0), \"ERC777: mint to the zero address\");\n\n // Update state variables\n _totalSupply = _totalSupply.add(amount);\n _balances[account] = _balances[account].add(amount);\n\n _callTokensReceived(operator, address(0), account, amount, userData, operatorData, true);\n\n emit Minted(operator, account, amount, userData, operatorData);\n emit Transfer(address(0), account, amount);\n }\n\n /**\n * @dev Send tokens\n * @param operator address operator requesting the transfer\n * @param from address token holder address\n * @param to address recipient address\n * @param amount uint256 amount of tokens to transfer\n * @param userData bytes extra information provided by the token holder (if any)\n * @param operatorData bytes extra information provided by the operator (if any)\n * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient\n */\n function _send(\n address operator,\n address from,\n address to,\n uint256 amount,\n bytes memory userData,\n bytes memory operatorData,\n bool requireReceptionAck\n ) internal {\n require(from != address(0), \"ERC777: send from the zero address\");\n require(to != address(0), \"ERC777: send to the zero address\");\n\n _callTokensToSend(operator, from, to, amount, userData, operatorData);\n\n _move(operator, from, to, amount, userData, operatorData);\n\n _callTokensReceived(\n operator,\n from,\n to,\n amount,\n userData,\n operatorData,\n requireReceptionAck\n );\n }\n\n /**\n * @dev Burn tokens\n * @param operator address operator requesting the operation\n * @param from address token holder address\n * @param amount uint256 amount of tokens to burn\n * @param data bytes extra information provided by the token holder\n * @param operatorData bytes extra information provided by the operator (if any)\n */\n function _burn(\n address operator,\n address from,\n uint256 amount,\n bytes memory data,\n bytes memory operatorData\n ) internal {\n require(from != address(0), \"ERC777: burn from the zero address\");\n\n _callTokensToSend(operator, from, address(0), amount, data, operatorData);\n\n // Update state variables\n _balances[from] = _balances[from].sub(amount, \"ERC777: burn amount exceeds balance\");\n _totalSupply = _totalSupply.sub(amount);\n\n emit Burned(operator, from, amount, data, operatorData);\n emit Transfer(from, address(0), amount);\n }\n\n function _move(\n address operator,\n address from,\n address to,\n uint256 amount,\n bytes memory userData,\n bytes memory operatorData\n ) private {\n _balances[from] = _balances[from].sub(amount, \"ERC777: transfer amount exceeds balance\");\n _balances[to] = _balances[to].add(amount);\n\n emit Sent(operator, from, to, amount, userData, operatorData);\n emit Transfer(from, to, amount);\n }\n\n function _approve(\n address holder,\n address spender,\n uint256 value\n ) internal {\n // TODO: restore this require statement if this function becomes internal, or is called at a new callsite. It is\n // currently unnecessary.\n //require(holder != address(0), \"ERC777: approve from the zero address\");\n require(spender != address(0), \"ERC777: approve to the zero address\");\n\n _allowances[holder][spender] = value;\n emit Approval(holder, spender, value);\n }\n\n /**\n * @dev Call from.tokensToSend() if the interface is registered\n * @param operator address operator requesting the transfer\n * @param from address token holder address\n * @param to address recipient address\n * @param amount uint256 amount of tokens to transfer\n * @param userData bytes extra information provided by the token holder (if any)\n * @param operatorData bytes extra information provided by the operator (if any)\n */\n function _callTokensToSend(\n address operator,\n address from,\n address to,\n uint256 amount,\n bytes memory userData,\n bytes memory operatorData\n ) internal {\n address implementer =\n ERC1820_REGISTRY.getInterfaceImplementer(from, TOKENS_SENDER_INTERFACE_HASH);\n if (implementer != address(0)) {\n IERC777Sender(implementer).tokensToSend(\n operator,\n from,\n to,\n amount,\n userData,\n operatorData\n );\n }\n }\n\n /**\n * @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but\n * tokensReceived() was not registered for the recipient\n * @param operator address operator requesting the transfer\n * @param from address token holder address\n * @param to address recipient address\n * @param amount uint256 amount of tokens to transfer\n * @param userData bytes extra information provided by the token holder (if any)\n * @param operatorData bytes extra information provided by the operator (if any)\n * @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient\n */\n function _callTokensReceived(\n address operator,\n address from,\n address to,\n uint256 amount,\n bytes memory userData,\n bytes memory operatorData,\n bool requireReceptionAck\n ) internal {\n address implementer =\n ERC1820_REGISTRY.getInterfaceImplementer(to, TOKENS_RECIPIENT_INTERFACE_HASH);\n if (implementer != address(0)) {\n IERC777Recipient(implementer).tokensReceived(\n operator,\n from,\n to,\n amount,\n userData,\n operatorData\n );\n } else if (requireReceptionAck) {\n require(\n !to.isContract(),\n \"ERC777: token recipient contract has no implementer for ERC777TokensRecipient\"\n );\n }\n }\n\n function mint(address _to, uint256 _value) public {\n // Update state variables\n _totalSupply = _totalSupply.add(_value);\n _balances[_to] = _balances[_to].add(_value);\n\n emit Minted(msg.sender, _to, _value, \"\", \"\");\n }\n\n function burn(address _who, uint256 _value) public {\n require(_value <= balanceOf(_who), \"balance too low\");\n\n _burn(msg.sender, _who, _value, \"\", \"\");\n }\n}\n" + }, + "contracts/testhelpers/TestTokenLimited.sol": { + "content": "/**\n * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved.\n * Licensed under the Apache License, Version 2.0.\n */\n\npragma solidity 0.5.17;\n\nimport \"../openzeppelin/SafeMath.sol\";\n\ncontract TestTokenLimited {\n using SafeMath for uint256;\n\n event Transfer(address indexed from, address indexed to, uint256 value);\n event Approval(address indexed owner, address indexed spender, uint256 value);\n event AllowanceUpdate(\n address indexed owner,\n address indexed spender,\n uint256 valueBefore,\n uint256 valueAfter\n );\n event Mint(address indexed minter, uint256 value);\n event Burn(address indexed burner, uint256 value);\n\n string public name;\n string public symbol;\n uint8 public decimals;\n\n mapping(address => uint256) internal balances;\n mapping(address => mapping(address => uint256)) internal allowed;\n uint256 internal totalSupply_;\n\n constructor(\n string memory _name,\n string memory _symbol,\n uint8 _decimals,\n uint256 _initialAmount\n ) public {\n name = _name;\n symbol = _symbol;\n decimals = _decimals;\n\n if (_initialAmount != 0) {\n mint(msg.sender, _initialAmount);\n }\n }\n\n function approve(address _spender, uint256 _value) public returns (bool) {\n allowed[msg.sender][_spender] = _value;\n emit Approval(msg.sender, _spender, _value);\n return true;\n }\n\n function transfer(address _to, uint256 _value) public returns (bool) {\n require(_value <= balances[msg.sender] && _to != address(0), \"invalid transfer\");\n\n balances[msg.sender] = balances[msg.sender].sub(_value);\n balances[_to] = balances[_to].add(_value);\n\n emit Transfer(msg.sender, _to, _value);\n return true;\n }\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n ) public returns (bool) {\n uint256 allowanceAmount = allowed[_from][msg.sender];\n require(\n _value <= balances[_from] && _value <= allowanceAmount && _to != address(0),\n \"invalid transfer\"\n );\n\n balances[_from] = balances[_from].sub(_value);\n balances[_to] = balances[_to].add(_value);\n if (allowanceAmount < uint256(-1)) {\n allowed[_from][msg.sender] = allowanceAmount.sub(_value);\n /// @dev Allowance mapping update requires an event log\n emit AllowanceUpdate(_from, msg.sender, allowanceAmount, allowed[_from][msg.sender]);\n }\n\n emit Transfer(_from, _to, _value);\n return true;\n }\n\n function mint(address _to, uint256 _value) public {\n require(_to != address(0), \"no burn allowed\");\n require(_value <= 100000 ether, \"max mint amount exceeded\");\n totalSupply_ = totalSupply_.add(_value);\n balances[_to] = balances[_to].add(_value);\n\n emit Mint(_to, _value);\n emit Transfer(address(0), _to, _value);\n }\n\n function burn(uint256 _value) public {\n require(_value <= balances[msg.sender], \"balance too low\");\n // no need to require _value <= totalSupply, since that would imply the\n // sender's balance is greater than the totalSupply, which *should* be an assertion failure\n\n balances[msg.sender] = balances[msg.sender].sub(_value);\n totalSupply_ = totalSupply_.sub(_value);\n\n emit Burn(msg.sender, _value);\n emit Transfer(msg.sender, address(0), _value);\n }\n\n function totalSupply() public view returns (uint256) {\n return totalSupply_;\n }\n\n function balanceOf(address _owner) public view returns (uint256) {\n return balances[_owner];\n }\n\n function allowance(address _owner, address _spender) public view returns (uint256) {\n return allowed[_owner][_spender];\n }\n}\n" + }, + "contracts/token/IApproveAndCall.sol": { + "content": "pragma solidity ^0.5.17;\n\n/**\n * @title Interface for contract governance/ApprovalReceiver.sol\n * @dev Interfaces are used to cast a contract address into a callable instance.\n */\ninterface IApproveAndCall {\n /**\n * @notice Receives approval from SOV token.\n * @param _sender The sender of SOV.approveAndCall function.\n * @param _amount The amount was approved.\n * @param _token The address of token.\n * @param _data The data will be used for low level call.\n * */\n function receiveApproval(\n address _sender,\n uint256 _amount,\n address _token,\n bytes calldata _data\n ) external;\n}\n" + }, + "contracts/token/SOV.sol": { + "content": "pragma solidity ^0.5.17;\n\nimport \"../openzeppelin/ERC20Detailed.sol\";\nimport \"../openzeppelin/ERC20.sol\";\nimport \"../openzeppelin/Ownable.sol\";\nimport \"./IApproveAndCall.sol\";\n\n/**\n * @title Sovryn Token: SOV is an ERC-20 token contract for Sovryn governance.\n *\n * @notice This contract accounts for all holders' balances.\n *\n * @dev This contract represents a token with dynamic supply.\n * The owner of the token contract can mint/burn tokens to/from any account\n * based upon previous governance voting and approval.\n * */\ncontract SOV is ERC20, ERC20Detailed, Ownable {\n string constant NAME = \"Sovryn Token\";\n string constant SYMBOL = \"SOV\";\n uint8 constant DECIMALS = 18;\n\n /**\n * @notice Constructor called on deployment, initiates the contract.\n * @dev On deployment, some amount of tokens will be minted for the owner.\n * @param _initialAmount The amount of tokens to be minted on contract creation.\n * */\n constructor(uint256 _initialAmount) public ERC20Detailed(NAME, SYMBOL, DECIMALS) {\n if (_initialAmount != 0) {\n _mint(msg.sender, _initialAmount);\n }\n }\n\n /**\n * @notice Creates new tokens and sends them to the recipient.\n * @dev Don't create more than 2^96/10 tokens before updating the governance first.\n * @param _account The recipient address to get the minted tokens.\n * @param _amount The amount of tokens to be minted.\n * */\n function mint(address _account, uint256 _amount) public onlyOwner {\n _mint(_account, _amount);\n }\n\n /**\n * @notice Approves and then calls the receiving contract.\n * Useful to encapsulate sending tokens to a contract in one call.\n * Solidity has no native way to send tokens to contracts.\n * ERC-20 tokens require approval to be spent by third parties, such as a contract in this case.\n * @param _spender The contract address to spend the tokens.\n * @param _amount The amount of tokens to be sent.\n * @param _data Parameters for the contract call, such as endpoint signature.\n * */\n function approveAndCall(\n address _spender,\n uint256 _amount,\n bytes memory _data\n ) public {\n approve(_spender, _amount);\n IApproveAndCall(_spender).receiveApproval(msg.sender, _amount, address(this), _data);\n }\n}\n" + }, + "contracts/utils/AdminManagerRole.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../openzeppelin/Ownable.sol\";\n\ncontract AdminManagerRole is Ownable {\n /// @dev user => flag whether user has adminManager role.\n bytes32 private constant KEY_ADMIN_MANAGER_ROLE = keccak256(\"key.admin.manager.role\");\n\n event AdminManagerChanged(\n address indexed sender,\n address indexed oldAdminManager,\n address indexed newAdminManager\n );\n event AdminManagerRemoved(address indexed sender, address indexed removedAdminManager);\n\n /**\n * @dev Throws if called by any account other than the owner or adminManager.\n * or on our own overriding sovrynOwnable.\n */\n modifier onlyOwnerOrAdminManager() {\n require(isOwner() || msg.sender == getAdminManager(), \"unauthorized\");\n _;\n }\n\n /**\n * @notice Set new admin manager.\n * @param _newAdminManager The addresses of the account to grant permissions.\n * */\n function setAdminManager(address _newAdminManager) public onlyOwner {\n require(_newAdminManager != address(0), \"invalid admin manager\");\n emit AdminManagerChanged(msg.sender, getAdminManager(), _newAdminManager);\n\n bytes32 key = KEY_ADMIN_MANAGER_ROLE;\n assembly {\n sstore(key, _newAdminManager)\n }\n }\n\n /**\n * @notice Set admin manager to 0 address.\n * */\n function removeAdminManager() public onlyOwner {\n require(getAdminManager() != address(0), \"Admin manager is not set\");\n emit AdminManagerRemoved(msg.sender, getAdminManager());\n address _newAdminManager = address(0);\n bytes32 key = KEY_ADMIN_MANAGER_ROLE;\n assembly {\n sstore(key, _newAdminManager)\n }\n }\n\n /**\n * @notice Return address of the admin manager.\n * @return Address of admin manager.\n * */\n function getAdminManager() public view returns (address _adminManager) {\n bytes32 key = KEY_ADMIN_MANAGER_ROLE;\n assembly {\n _adminManager := sload(key)\n }\n }\n}\n" + }, + "contracts/utils/AdminRole.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../openzeppelin/Ownable.sol\";\n\ncontract AdminRole is Ownable {\n /// @dev user => flag whether user has admin role.\n mapping(address => bool) public admins;\n\n event AdminAdded(address admin);\n event AdminRemoved(address admin);\n\n /**\n * @dev Throws if called by any account other than the owner or admin.\n * or on our own overriding sovrynOwnable.\n */\n modifier onlyAuthorized() {\n require(isOwner() || admins[msg.sender], \"unauthorized\");\n _;\n }\n\n /**\n * @notice Add account to ACL.\n * @param _admin The addresses of the account to grant permissions.\n * */\n function addAdmin(address _admin) public onlyOwner {\n admins[_admin] = true;\n emit AdminAdded(_admin);\n }\n\n /**\n * @notice Remove account from ACL.\n * @param _admin The addresses of the account to revoke permissions.\n * */\n function removeAdmin(address _admin) public onlyOwner {\n admins[_admin] = false;\n emit AdminRemoved(_admin);\n }\n}\n" + }, + "contracts/utils/AdminRoleManaged.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../openzeppelin/Ownable.sol\";\nimport \"./AdminManagerRole.sol\";\n\ncontract AdminRoleManaged is Ownable, AdminManagerRole {\n /// @dev user => flag whether user has admin role.\n mapping(address => bool) public admins;\n\n event AdminAdded(address admin);\n event AdminRemoved(address admin);\n\n /**\n * @dev Throws if called by any account other than the owner or admin.\n * or on our own overriding sovrynOwnable.\n */\n modifier onlyAuthorized() {\n require(isOwner() || admins[msg.sender], \"unauthorized\");\n _;\n }\n\n /**\n * @notice Add account to ACL.\n * @param _admin The addresses of the account to grant permissions.\n * */\n function addAdmin(address _admin) public onlyOwnerOrAdminManager {\n admins[_admin] = true;\n emit AdminAdded(_admin);\n }\n\n /**\n * @notice Remove account from ACL.\n * @param _admin The addresses of the account to revoke permissions.\n * */\n function removeAdmin(address _admin) public onlyOwnerOrAdminManager {\n admins[_admin] = false;\n emit AdminRemoved(_admin);\n }\n}\n" + }, + "contracts/utils/PausableRole.sol": { + "content": "pragma solidity 0.5.17;\n\nimport \"../openzeppelin/PausableOz.sol\";\n\ncontract PausableRole is PausableOz {\n address public pauser;\n\n event SetPauser(address indexed sender, address indexed oldPauser, address indexed newPauser);\n\n /**\n * @dev Modifier to make a function callable only when the caller is pauser or owner\n */\n modifier onlyPauserOrOwner() {\n require(isOwner() || msg.sender == pauser, \"Pausable: unauthorized\"); // SS02\n _;\n }\n\n /**\n * @notice Set the pauser address.\n *\n * only pauser can perform this action.\n *\n * @param newPauser The new address of the pauser.\n * */\n function setPauser(address newPauser) external onlyOwner {\n address oldPauser = pauser;\n pauser = newPauser;\n\n emit SetPauser(msg.sender, oldPauser, newPauser);\n }\n\n /**\n * @dev Called by the owner to pause, triggers stopped state.\n */\n function pause() public onlyPauserOrOwner whenNotPaused {\n _paused = true;\n emit Paused(_msgSender());\n }\n\n /**\n * @dev Called by the owner to unpause, returns to normal state.\n */\n function unpause() public onlyPauserOrOwner whenPaused {\n _paused = false;\n emit Unpaused(_msgSender());\n }\n}\n" + }, + "contracts/utils/ProxyOwnable.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.5.17;\n\n/**\n * Based on OpenZeppelin's Ownable contract:\n * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol\n *\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\ncontract ProxyOwnable {\n bytes32 private constant KEY_OWNER = keccak256(\"key.proxy.owner\");\n\n event ProxyOwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n /**\n * @dev Initializes the contract setting the deployer as the initial owner.\n */\n constructor() internal {\n _setProxyOwner(msg.sender);\n }\n\n /**\n * @dev Throws if called by any account other than the owner.\n */\n modifier onlyProxyOwner() {\n require(msg.sender == getProxyOwner(), \"Ownable:: access denied\");\n _;\n }\n\n /**\n * @notice Set address of the owner.\n * @param _owner Address of the owner.\n * */\n function _setProxyOwner(address _owner) internal {\n require(_owner != address(0), \"ProxyOwnable::setProxyOwner: invalid address\");\n emit ProxyOwnershipTransferred(getProxyOwner(), _owner);\n\n bytes32 key = KEY_OWNER;\n assembly {\n sstore(key, _owner)\n }\n }\n\n /**\n * @notice Set address of the owner (only owner can call this function)\n * @param _owner Address of the owner.\n * */\n function setProxyOwner(address _owner) public onlyProxyOwner {\n _setProxyOwner(_owner);\n }\n\n /**\n * @notice Return address of the owner.\n * @return _owner Address of the owner.\n * */\n function getProxyOwner() public view returns (address _owner) {\n bytes32 key = KEY_OWNER;\n assembly {\n _owner := sload(key)\n }\n }\n}\n" + }, + "contracts/utils/Utils.sol": { + "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.5.17;\n\nlibrary Utils {\n function stringToBytes32(string memory source) internal pure returns (bytes32 result) {\n bytes memory tempEmptyStringTest = bytes(source);\n if (tempEmptyStringTest.length == 0) {\n return 0x0;\n }\n\n assembly {\n result := mload(add(source, 32))\n }\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "*": [ + "storageLayout", + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "evm.gasEstimates" + ], + "": [ + "ast" + ] + } + }, + "metadata": { + "useLiteralContent": true + }, + "remappings": [] + } +} \ No newline at end of file diff --git a/hardhat/tasks/adminManagerRole.js b/hardhat/tasks/adminManagerRole.js new file mode 100644 index 000000000..ead479d2c --- /dev/null +++ b/hardhat/tasks/adminManagerRole.js @@ -0,0 +1,49 @@ +const { task } = require("hardhat/config"); +const Logs = require("node-logs"); +const logger = new Logs().showInConsole(true); +const { sendWithMultisig } = require("../../deployment/helpers/helpers"); + +task("setAdminManager", "SetAdminManager to the contract that implement adminManagerRole") + .addOptionalParam("signer", "Signer name: 'signer' or 'deployer'", "deployer") + .addParam("adminManager", "New admin manager: 'MultiSigWallet'") + .addParam( + "contractTarget", + "Contract name or address to set admin manager: e.g: 'VestingRegistry'" + ) + .setAction(async ({ signer, adminManagerTarget, contractTarget }, hre) => { + const { + deployments: { get }, + ethers, + } = hre; + + const signerAcc = (await hre.getNamedAccounts())[signer]; + + const multisigDeployment = await get("MultiSigWallet"); + + let contractTargetAddress = ethers.constants.AddressZero; + if ( + ethers.utils.isAddress(contractTarget) && + (await ethers.provider.getCode(contractTarget)) !== "0x" + ) { + contractTargetAddress = contractTarget; + } + + const contractTargetDeployment = + contractTargetAddress === ethers.constants.AddressZero + ? await get(contractTarget) + : await ethers.getContractAt(contractTarget, contractTargetAddress); + + const adminManagerTargetDeployment = await get(adminManagerTarget); + + const targetInterface = new ethers.utils.Interface(contractTargetDeployment.abi); + const data = targetInterface.encodeFunctionData("setAdminManager", [ + adminManagerTargetDeployment.address, + ]); + + await sendWithMultisig( + multisigDeployment.address, + contractTargetDeployment.address, + data, + signerAcc + ); + }); diff --git a/hardhat/tasks/index.js b/hardhat/tasks/index.js index 990971f9e..0c660b399 100644 --- a/hardhat/tasks/index.js +++ b/hardhat/tasks/index.js @@ -6,5 +6,6 @@ require("./utils"); require("./misc"); require("./governance"); require("./feeSharingCollector"); +require("./adminManagerRole"); require("./uniswap"); require("./bridge"); diff --git a/hardhat/tasks/sips/args/sipArgs.js b/hardhat/tasks/sips/args/sipArgs.js index aaa30376c..bb4defff8 100644 --- a/hardhat/tasks/sips/args/sipArgs.js +++ b/hardhat/tasks/sips/args/sipArgs.js @@ -1098,6 +1098,41 @@ const getArgsSIP0077 = async (hre) => { return { args, governor: "GovernorAdmin" }; }; +const getArgsSov3686 = async (hre) => { + const { + ethers, + deployments: { get }, + } = hre; + const abiCoder = new ethers.utils.AbiCoder(); + const vestingRegistryDeployment = await get("VestingRegistry"); + + const newVestingRegistryImplDeployment = await get("VestingRegistry_Implementation"); + const multisigDeployment = await get("MultiSigWallet"); + + const vestingRegistry = await ethers.getContract("VestingRegistry"); + + if ( + (await vestingRegistry.getImplementation()) == + newVestingRegistryImplDeployment.implementation + ) { + throw new Error(`New VestingRegistry impl is the same with the current one`); + } + + const args = { + targets: [vestingRegistryDeployment.address, vestingRegistryDeployment.address], + values: [0, 0], + signatures: ["setImplementation(address)", "setAdminManager(address)"], + data: [ + abiCoder.encode(["address"], [newVestingRegistryImplDeployment.address]), + abiCoder.encode(["address"], [multisigDeployment.address]), + ], + /** @todo change SIP description */ + description: "SIP-Sov3686: xxx", + }; + + return { args, governor: "GovernorOwner" }; +}; + module.exports = { sampleGovernorAdminSIP, sampleGovernorOwnerSIP, @@ -1118,4 +1153,5 @@ module.exports = { getArgsSip0076, getArgsSip0078, getArgsSip0079, + getArgsSov3686, }; diff --git a/scripts/contractInteraction/staking_vesting.py b/scripts/contractInteraction/staking_vesting.py index 692a00a91..de630d607 100644 --- a/scripts/contractInteraction/staking_vesting.py +++ b/scripts/contractInteraction/staking_vesting.py @@ -25,7 +25,7 @@ def vestingRegistryAddAdmin(admin, vestingRegistryAddress): sendWithMultisig(conf.contracts['multisig'], vestingRegistry.address, data, conf.acct) def vestingRegistryProxyAddAdmin(admin): - vestingRegistry = Contract.from_abi("VestingRegistryLogic", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) + vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) vestingRegistryAddAdmin(admin, vestingRegistry.address) def vestingRegistryRemoveAdmin(admin, vestingRegistryAddress): @@ -35,7 +35,7 @@ def vestingRegistryRemoveAdmin(admin, vestingRegistryAddress): sendWithMultisig(conf.contracts['multisig'], vestingRegistry.address, data, conf.acct) def vestingRegistryProxyRemoveAdmin(admin): - vestingRegistry = Contract.from_abi("VestingRegistryLogic", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) + vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) vestingRegistryRemoveAdmin(admin, vestingRegistry.address) def isVestingRegistryAdmin(admin, vestingRegistryAddress): @@ -64,7 +64,7 @@ def readTeamVestingContractForAddress(userAddress): def cancelTeamVestingsOfAccount(userAddress, startFrom): staking = Contract.from_abi("Staking", address=conf.contracts['Staking'], abi=interface.IStaking.abi, owner=conf.acct) - vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistryLogic.abi, owner=conf.acct) + vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) vestings = vestingRegistry.getVestingsOf(userAddress) for vesting in vestings: vestingContract = Contract.from_abi("VestingLogic", address=vesting[2], abi=VestingLogic.abi, owner=conf.acct) @@ -82,13 +82,13 @@ def readLMVestingContractForAddress(userAddress): # vesting type 0 -> team vesting # vesting type 1 -> owner vesting def readAllVestingContractsForAddress(userAddress): - vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistryLogic.abi, owner=conf.acct) + vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) addresses = vestingRegistry.getVestingsOf(userAddress) print(addresses) def addVestingAdmin(admin): multisig = Contract.from_abi("MultiSig", address=conf.contracts['multisig'], abi=MultiSigWallet.abi, owner=conf.acct) - vestingRegistry = Contract.from_abi("VestingRegistryLogic", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistryLogic.abi, owner=conf.acct) + vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) data = vestingRegistry.addAdmin.encode_input(admin) sendWithMultisig(conf.contracts['multisig'], vestingRegistry.address, data, conf.acct) @@ -100,12 +100,12 @@ def setMaxVestingWithdrawIterations(num): def removeVestingAdmin(admin): multisig = Contract.from_abi("MultiSig", address=conf.contracts['multisig'], abi=MultiSigWallet.abi, owner=conf.acct) - vestingRegistry = Contract.from_abi("VestingRegistryLogic", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistryLogic.abi, owner=conf.acct) + vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) data = vestingRegistry.removeAdmin.encode_input(admin) sendWithMultisig(conf.contracts['multisig'], vestingRegistry.address, data, conf.acct) def isVestingAdmin(admin): - vestingRegistry = Contract.from_abi("VestingRegistryLogic", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistryLogic.abi, owner=conf.acct) + vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) print(vestingRegistry.admins(admin)) def readStakingKickOff(): @@ -366,14 +366,14 @@ def upgradeVestingRegistry(): print("Upgrading vesting registry") # Deploy the staking logic contracts - vestingRegistryLogic = conf.acct.deploy(VestingRegistryLogic) - print("New vesting registry logic address:", vestingRegistryLogic.address) + vestingRegistry = conf.acct.deploy(VestingRegistry) + print("New vesting registry logic address:", vestingRegistry.address) # Get the proxy contract instance vestingRegistryProxy = Contract.from_abi("VestingRegistryProxy", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistryProxy.abi, owner=conf.acct) # Register logic in Proxy - data = vestingRegistryProxy.setImplementation.encode_input(vestingRegistryLogic.address) + data = vestingRegistryProxy.setImplementation.encode_input(vestingRegistry.address) sendWithMultisig(conf.contracts['multisig'], conf.contracts['VestingRegistryProxy'], data, conf.acct) # Set Vesting Registry Address for Staking @@ -608,12 +608,12 @@ def registerVestingToVestingCreationAndTypes(addresses, vestingCreationAndTypeDe if len(addresses) != len(vestingCreationAndTypeDetails): raise Exception("umatched length of array between addresses & vestingCreationAndTypeDetails") - vestingRegistry = Contract.from_abi("VestingRegistryLogic", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistryLogic.abi, owner=conf.acct) + vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) data = vestingRegistry.registerVestingToVestingCreationAndTypes.encode_input(addresses, vestingCreationAndTypeDetails) sendWithMultisig(conf.contracts['multisig'], vestingRegistry.address, data, conf.acct) def getVestingCreationAndTypes(vestingAddress): - vestingRegistry = Contract.from_abi("VestingRegistryLogic", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistryLogic.abi, owner=conf.acct) + vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) print(vestingRegistry.vestingCreationAndTypes(vestingAddress)) def readTokenOwner(vestingAddress): diff --git a/scripts/contractInteraction/tasks/airdrop_distribution/functions/create_vestings.py b/scripts/contractInteraction/tasks/airdrop_distribution/functions/create_vestings.py index 821e7743f..afa04d14c 100644 --- a/scripts/contractInteraction/tasks/airdrop_distribution/functions/create_vestings.py +++ b/scripts/contractInteraction/tasks/airdrop_distribution/functions/create_vestings.py @@ -11,7 +11,7 @@ def createVestings(path, dryRun, multiplier): multiplier - usually 10**16 considering the amount format should have 2 decimals ''' - vestingRegistry = Contract.from_abi("VestingRegistryLogic", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistryLogic.abi, owner=conf.acct) + vestingRegistry = Contract.from_abi("VestingRegistry", address=conf.contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=conf.acct) staking = Contract.from_abi("Staking", address=conf.contracts['Staking'], abi=interface.IStaking.abi, owner=conf.acct) SOVtoken = Contract.from_abi("SOV", address=conf.contracts['SOV'], abi=SOV.abi, owner=conf.acct) diff --git a/scripts/deployment/deploy_sov_staking_rewards.py b/scripts/deployment/deploy_sov_staking_rewards.py index b9252272f..852d368b9 100644 --- a/scripts/deployment/deploy_sov_staking_rewards.py +++ b/scripts/deployment/deploy_sov_staking_rewards.py @@ -38,7 +38,7 @@ def main(): # ================================ SOV Staking Rewards - SIP-0024 =================================== - # deploy VestingRegistryLogic + # deploy VestingRegistry stakingRewardsLogic = acct.deploy(StakingRewards) stakingRewardsProxy = acct.deploy(StakingRewardsProxy) stakingRewardsProxy.setImplementation(stakingRewardsLogic.address) diff --git a/scripts/deployment/distribution/create_vesting_migrations.py b/scripts/deployment/distribution/create_vesting_migrations.py index b1f73f434..ec48f9fb0 100644 --- a/scripts/deployment/distribution/create_vesting_migrations.py +++ b/scripts/deployment/distribution/create_vesting_migrations.py @@ -27,10 +27,10 @@ def main(): # load deployed contracts addresses contracts = json.load(configFile) - vestingRegistryLogic = Contract.from_abi( - "VestingRegistryLogic", + vestingRegistry = Contract.from_abi( + "VestingRegistry", address=contracts['VestingRegistryProxy'], - abi=VestingRegistryLogic.abi, + abi=VestingRegistry.abi, owner=acct) # open the file in universal line ending mode @@ -51,13 +51,13 @@ def main(): print(tokenOwners) print(vestingCreationTypes) - vestingRegistryLogic.addDeployedVestings(tokenOwners, vestingCreationTypes) - # data = vestingRegistryLogic.addDeployedVestings.encode_input(tokenOwners, vestingCreationTypes) + vestingRegistry.addDeployedVestings(tokenOwners, vestingCreationTypes) + # data = vestingRegistry.addDeployedVestings.encode_input(tokenOwners, vestingCreationTypes) # print(data) # multisig = Contract.from_abi("MultiSig", address=contracts['multisig'], abi=MultiSigWallet.abi, owner=acct) # print(multisig) - # tx = multisig.submitTransaction(vestingRegistryLogic.address, 0, data, {'allow_revert':True}) + # tx = multisig.submitTransaction(vestingRegistry.address, 0, data, {'allow_revert':True}) # print(tx.revert_msg) # txId = tx.events["Submission"]["transactionId"] # print(txId) \ No newline at end of file diff --git a/scripts/fouryearvesting/add_to_registry.py b/scripts/fouryearvesting/add_to_registry.py index 011ba5b52..429afe5d9 100644 --- a/scripts/fouryearvesting/add_to_registry.py +++ b/scripts/fouryearvesting/add_to_registry.py @@ -29,10 +29,10 @@ def main(): balanceBefore = acct.balance() - vestingRegistryLogic = Contract.from_abi( - "VestingRegistryLogic", + vestingRegistry = Contract.from_abi( + "VestingRegistry", address=contracts['VestingRegistryProxy'], - abi=VestingRegistryLogic.abi, + abi=VestingRegistry.abi, owner=acct) # open the file in universal line ending mode @@ -53,17 +53,17 @@ def main(): print(tokenOwners) print(vestingAddresses) - vestingRegistryLogic.addFourYearVestings(tokenOwners, vestingAddresses) + vestingRegistry.addFourYearVestings(tokenOwners, vestingAddresses) print("deployment cost:") print((balanceBefore - acct.balance()) / 10**18) - # data = vestingRegistryLogic.addFourYearVestings.encode_input(tokenOwners, vestingAddresses) + # data = vestingRegistry.addFourYearVestings.encode_input(tokenOwners, vestingAddresses) # print(data) # multisig = Contract.from_abi("MultiSig", address=contracts['multisig'], abi=MultiSigWallet.abi, owner=acct) # print(multisig) - # tx = multisig.submitTransaction(vestingRegistryLogic.address, 0, data, {'allow_revert':True}) + # tx = multisig.submitTransaction(vestingRegistry.address, 0, data, {'allow_revert':True}) # print(tx.revert_msg) # txId = tx.events["Submission"]["transactionId"] # print(txId) \ No newline at end of file diff --git a/scripts/staking/check_user_vestings.py b/scripts/staking/check_user_vestings.py index 0522ab97f..a21e0caf3 100644 --- a/scripts/staking/check_user_vestings.py +++ b/scripts/staking/check_user_vestings.py @@ -30,7 +30,7 @@ def main(): registries.append(Contract.from_abi("VestingRegistry", address=contracts['VestingRegistry3'], abi=VestingRegistry.abi, owner=acct)) ''' - vestingRegistry = Contract.from_abi("VestingRegistryLogic", address=contracts['VestingRegistryProxy'], abi=VestingRegistryLogic.abi, owner=acct) + vestingRegistry = Contract.from_abi("VestingRegistry", address=contracts['VestingRegistryProxy'], abi=VestingRegistry.abi, owner=acct) INPUT_FILE = "./scripts/staking/users.csv" diff --git a/tests-foundry/staking/interfaces/IStaking.sol b/tests-foundry/staking/interfaces/IStaking.sol index c50cb749b..aafcf3666 100644 --- a/tests-foundry/staking/interfaces/IStaking.sol +++ b/tests-foundry/staking/interfaces/IStaking.sol @@ -417,7 +417,7 @@ interface IStaking { function numVestingCheckpoints(uint256 date) external view returns (uint32 checkpointsQty); ///@notice vesting registry contract PROXY address - function vestingRegistryLogic() external view returns (address); + function vestingRegistry() external view returns (address); /// @dev user => flag whether user has pauser role. function pausers(address isPauser) external view returns (bool); diff --git a/tests-onchain/sipSov3686.test.js b/tests-onchain/sipSov3686.test.js new file mode 100644 index 000000000..5c14aa01c --- /dev/null +++ b/tests-onchain/sipSov3686.test.js @@ -0,0 +1,165 @@ +// first run a local forked mainnet node in a separate terminal window: +// npx hardhat node --fork https://mainnet-dev.sovryn.app/rpc --no-deploy +// now run the test: +// npx hardhat test tests-onchain/sipSov3497.test.js --network rskForkedMainnet + +const { + impersonateAccount, + mine, + time, + setBalance, +} = require("@nomicfoundation/hardhat-network-helpers"); +const hre = require("hardhat"); +const { getProtocolModules } = require("../deployment/helpers/helpers"); + +const { + ethers, + deployments: { createFixture, get }, +} = hre; + +const MAX_DURATION = ethers.BigNumber.from(24 * 60 * 60).mul(1092); + +const ONE_RBTC = ethers.utils.parseEther("1.0"); + +const getImpersonatedSigner = async (addressToImpersonate) => { + await impersonateAccount(addressToImpersonate); + return await ethers.getSigner(addressToImpersonate); +}; + +describe("Protocol Modules Deployments and Upgrades via Governance", () => { + const getImpersonatedSignerFromJsonRpcProvider = async (addressToImpersonate) => { + const provider = new ethers.providers.JsonRpcProvider("http://localhost:8545"); + await provider.send("hardhat_impersonateAccount", [addressToImpersonate]); + return provider.getSigner(addressToImpersonate); + }; + + const setupTest = createFixture(async ({ deployments }) => { + const deployer = (await ethers.getSigners())[0].address; + const deployerSigner = await ethers.getSigner(deployer); + + const multisigAddress = (await get("MultiSigWallet")).address; + const multisigSigner = await getImpersonatedSignerFromJsonRpcProvider(multisigAddress); + + await setBalance(deployer, ONE_RBTC.mul(10)); + await deployments.fixture(["ProtocolModules"], { + keepExistingDeployments: true, + }); // start from a fresh deployments + + const staking = await ethers.getContract("Staking", deployerSigner); + const sovrynProtocol = await ethers.getContract("SovrynProtocol", deployerSigner); + + const god = await deployments.get("GovernorOwner"); + const governorOwner = await ethers.getContractAt( + "GovernorAlpha", + god.address, + deployerSigner + ); + const governorOwnerSigner = await getImpersonatedSigner(god.address); + + await setBalance(governorOwnerSigner.address, ONE_RBTC); + const timelockOwner = await ethers.getContract("TimelockOwner", governorOwnerSigner); + + const timelockOwnerSigner = await getImpersonatedSignerFromJsonRpcProvider( + timelockOwner.address + ); + await setBalance(timelockOwnerSigner._address, ONE_RBTC); + + // + return { + deployer, + deployerSigner, + staking, + sovrynProtocol, + governorOwner, + governorOwnerSigner, + timelockOwner, + timelockOwnerSigner, + multisigAddress, + multisigSigner, + }; + }); + + /// @todo change the SIP name + describe("SIP-Sov3686 Test creation and execution", () => { + it("SIP-Sov3686 is executable and valid", async () => { + if (!hre.network.tags["forked"]) { + console.error("ERROR: Must run on a forked net"); + return; + } + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + jsonRpcUrl: "https://mainnet-dev.sovryn.app/rpc", + blockNumber: 5937831, // block num at the time no new modules deployed yet + }, + }, + ], + }); + + const { + deployer, + deployerSigner, + staking, + sovrynProtocol, + governorOwner, + timelockOwnerSigner, + multisigAddress, + multisigSigner, + } = await setupTest(); + + // CREATE PROPOSAL + const sov = await ethers.getContract("SOV", timelockOwnerSigner); + const whaleAmount = (await sov.totalSupply()).mul(ethers.BigNumber.from(5)); + await sov.mint(deployer, whaleAmount); + + await sov.connect(deployerSigner).approve(staking.address, whaleAmount); + + if (await staking.paused()) await staking.connect(multisigSigner).pauseUnpause(false); + const kickoffTS = await staking.kickoffTS(); + await staking.stake(whaleAmount, kickoffTS.add(MAX_DURATION), deployer, deployer); + await mine(); + + // CREATE PROPOSAL AND VERIFY + const proposalIdBeforeSIP = await governorOwner.latestProposalIds(deployer); + await hre.run("sips:create", { argsFunc: "getArgsSov3686" }); + const proposalId = await governorOwner.latestProposalIds(deployer); + expect( + proposalId, + "Proposal was not created. Check the SIP creation is not commented out." + ).is.gt(proposalIdBeforeSIP); + + // VOTE FOR PROPOSAL + + await mine(); + await governorOwner.connect(deployerSigner).castVote(proposalId, true); + + // QUEUE PROPOSAL + let proposal = await governorOwner.proposals(proposalId); + await mine(proposal.endBlock); + await governorOwner.queue(proposalId); + + // EXECUTE PROPOSAL + proposal = await governorOwner.proposals(proposalId); + await time.increaseTo(proposal.eta); + await expect(governorOwner.execute(proposalId)) + .to.emit(governorOwner, "ProposalExecuted") + .withArgs(proposalId); + + // VERIFY execution + expect((await governorOwner.proposals(proposalId)).executed).to.be.true; + + // VERIFY VestingRegistry has been upgraded + const vestingRegistry = await ethers.getContract("VestingRegistry"); + const newVestingRegistryImplDeployment = await get("VestingRegistry_Implementation"); + expect(await vestingRegistry.getImplementation()).to.equal( + newVestingRegistryImplDeployment.address + ); + + // VERIFY Exchequer multisig has been registered as AdminManager in VestingRegistry + const multisigDeployment = await get("MultiSigWallet"); + expect(await vestingRegistry.getAdminManager()).to.equal(multisigDeployment.address); + }); + }); +}); diff --git a/tests/EscrowReward/anyone.test.js b/tests/EscrowReward/anyone.test.js index 1d522761d..b488a2fce 100644 --- a/tests/EscrowReward/anyone.test.js +++ b/tests/EscrowReward/anyone.test.js @@ -11,7 +11,7 @@ const SOV = artifacts.require("TestToken"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); const { BN, // Big Number support. diff --git a/tests/EscrowReward/creator.test.js b/tests/EscrowReward/creator.test.js index ad830a93e..70ac85152 100644 --- a/tests/EscrowReward/creator.test.js +++ b/tests/EscrowReward/creator.test.js @@ -23,7 +23,7 @@ const EscrowReward = artifacts.require("EscrowReward"); const LockedSOV = artifacts.require("LockedSOV"); // Ideally should be using actual LockedSOV for testing. const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); const StakingProxy = artifacts.require("StakingProxy"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const SOV = artifacts.require("TestToken"); diff --git a/tests/EscrowReward/event.test.js b/tests/EscrowReward/event.test.js index 92bb97744..9210434a6 100644 --- a/tests/EscrowReward/event.test.js +++ b/tests/EscrowReward/event.test.js @@ -26,7 +26,7 @@ const LockedSOV = artifacts.require("LockedSOV"); // Ideally should be using act const SOV = artifacts.require("TestToken"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); const StakingProxy = artifacts.require("StakingProxy"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); diff --git a/tests/EscrowReward/multisig.test.js b/tests/EscrowReward/multisig.test.js index c2af18fab..fcc6bb85f 100644 --- a/tests/EscrowReward/multisig.test.js +++ b/tests/EscrowReward/multisig.test.js @@ -19,7 +19,7 @@ const EscrowReward = artifacts.require("EscrowReward"); const LockedSOV = artifacts.require("LockedSOV"); // Ideally should be using actual LockedSOV for testing. const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); const StakingProxy = artifacts.require("StakingProxy"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const SOV = artifacts.require("TestToken"); diff --git a/tests/EscrowReward/state.test.js b/tests/EscrowReward/state.test.js index 62095560c..13aa0cd10 100644 --- a/tests/EscrowReward/state.test.js +++ b/tests/EscrowReward/state.test.js @@ -21,7 +21,7 @@ const EscrowReward = artifacts.require("EscrowReward"); const LockedSOV = artifacts.require("LockedSOV"); // Ideally should be using actual LockedSOV for testing. const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); const StakingProxy = artifacts.require("StakingProxy"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const SOV = artifacts.require("TestToken"); diff --git a/tests/FeeSharingCollectorTest.js b/tests/FeeSharingCollectorTest.js index a511b11f2..c0303669e 100644 --- a/tests/FeeSharingCollectorTest.js +++ b/tests/FeeSharingCollectorTest.js @@ -68,7 +68,7 @@ const FeeSharingCollectorMockup = artifacts.require("FeeSharingCollectorMockup") const PriceFeedsLocal = artifacts.require("PriceFeedsLocal"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); const LiquidityPoolV1Converter = artifacts.require("LiquidityPoolV1ConverterMockup"); diff --git a/tests/Governance/GovernorAlpha/ProposeTest.js b/tests/Governance/GovernorAlpha/ProposeTest.js index e71629181..34da1b3d8 100644 --- a/tests/Governance/GovernorAlpha/ProposeTest.js +++ b/tests/Governance/GovernorAlpha/ProposeTest.js @@ -19,7 +19,7 @@ const GovernorAlpha = artifacts.require("GovernorAlpha"); const StakingProxy = artifacts.require("StakingProxy"); const TestToken = artifacts.require("TestToken"); //Upgradable Vesting Registry -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistry = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const QUORUM_VOTES = etherMantissa(4000000); @@ -44,10 +44,10 @@ contract("GovernorAlpha#propose/5", (accounts) => { gov = await GovernorAlpha.new(address(0), staking.address, address(0), 4, 0); // Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistry = await VestingRegistry.new(); vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + await vesting.setImplementation(vestingRegistry.address); + vesting = await VestingRegistry.at(vesting.address); await staking.setVestingRegistry(vesting.address); diff --git a/tests/Governance/GovernorAlpha/QueueTest.js b/tests/Governance/GovernorAlpha/QueueTest.js index 72a54f326..8704b2104 100644 --- a/tests/Governance/GovernorAlpha/QueueTest.js +++ b/tests/Governance/GovernorAlpha/QueueTest.js @@ -24,7 +24,7 @@ const Timelock = artifacts.require("TimelockHarness"); const StakingProxy = artifacts.require("StakingProxy"); const TestToken = artifacts.require("TestToken"); //Upgradable Vesting Registry -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistry = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const DELAY = 86400 * 14; @@ -38,10 +38,10 @@ async function enfranchise(token, staking, actor, amount) { let kickoffTS = await staking.kickoffTS.call(); let stakingDate = kickoffTS.add(new BN(DELAY)); //Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistry = await VestingRegistry.new(); vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + await vesting.setImplementation(vestingRegistry.address); + vesting = await VestingRegistry.at(vesting.address); await staking.setVestingRegistry(vesting.address); await staking.stake(amount, stakingDate, actor, actor, { from: actor }); diff --git a/tests/Locked/admin.test.js b/tests/Locked/admin.test.js index ca5f0337d..77cf3ce10 100644 --- a/tests/Locked/admin.test.js +++ b/tests/Locked/admin.test.js @@ -14,7 +14,7 @@ const StakingProxy = artifacts.require("StakingProxy"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); const { BN, // Big Number support. diff --git a/tests/Locked/anyone.test.js b/tests/Locked/anyone.test.js index ffce71cd7..003cd8b86 100644 --- a/tests/Locked/anyone.test.js +++ b/tests/Locked/anyone.test.js @@ -16,7 +16,8 @@ const StakingProxy = artifacts.require("StakingProxy"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); +const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const { BN, // Big Number support. @@ -77,13 +78,11 @@ contract("Locked SOV (Any User Functions)", (accounts) => { // Creating the Vesting Instance. vestingLogic = await VestingLogic.new(); vestingFactory = await VestingFactory.new(vestingLogic.address); - vestingRegistry = await VestingRegistry.new( - vestingFactory.address, - sov.address, - staking.address, - feeSharingCollectorProxy.address, - creator // This should be Governance Timelock Contract. - ); + + vestingRegistry = await VestingRegistry.new(); + vestingRegistryProxy = await VestingRegistryProxy.new(); + await vestingRegistryProxy.setImplementation(vestingRegistry.address); + vestingRegistry = await VestingRegistry.at(vestingRegistryProxy.address); await vestingFactory.transferOwnership(vestingRegistry.address); // Creating the instance of newLockedSOV Contract. @@ -97,6 +96,16 @@ contract("Locked SOV (Any User Functions)", (accounts) => { admin, ]); + await vestingRegistry.initialize( + vestingFactory.address, + sov.address, + staking.address, + feeSharingCollectorProxy.address, + creator, // This should be Governance Timelock Contract. + lockedSOV.address, + [vestingRegistry.address] + ); + // Adding lockedSOV as an admin in the Vesting Registry. await vestingRegistry.addAdmin(lockedSOV.address); diff --git a/tests/Locked/creator.test.js b/tests/Locked/creator.test.js index 1afc45083..a5661655a 100644 --- a/tests/Locked/creator.test.js +++ b/tests/Locked/creator.test.js @@ -12,7 +12,7 @@ const StakingProxy = artifacts.require("StakingProxy"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); const { BN, // Big Number support. diff --git a/tests/Locked/event.test.js b/tests/Locked/event.test.js index 7b09d574e..4effa0ff6 100644 --- a/tests/Locked/event.test.js +++ b/tests/Locked/event.test.js @@ -17,7 +17,8 @@ const StakingProxy = artifacts.require("StakingProxy"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); +const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const { BN, // Big Number support. @@ -78,13 +79,10 @@ contract("Locked SOV (Events)", (accounts) => { // Creating the Vesting Instance. vestingLogic = await VestingLogic.new(); vestingFactory = await VestingFactory.new(vestingLogic.address); - vestingRegistry = await VestingRegistry.new( - vestingFactory.address, - sov.address, - staking.address, - feeSharingCollectorProxy.address, - creator // This should be Governance Timelock Contract. - ); + vestingRegistry = await VestingRegistry.new(); + vestingRegistryProxy = await VestingRegistryProxy.new(); + await vestingRegistryProxy.setImplementation(vestingRegistry.address); + vestingRegistry = await VestingRegistry.at(vestingRegistryProxy.address); await vestingFactory.transferOwnership(vestingRegistry.address); // Creating the instance of newLockedSOV Contract. @@ -98,6 +96,16 @@ contract("Locked SOV (Events)", (accounts) => { admin, ]); + await vestingRegistry.initialize( + vestingFactory.address, + sov.address, + staking.address, + feeSharingCollectorProxy.address, + creator, // This should be Governance Timelock Contract. + lockedSOV.address, + [vestingRegistry.address] + ); + // Adding lockedSOV as an admin in the Vesting Registry. await vestingRegistry.addAdmin(lockedSOV.address); diff --git a/tests/Locked/state.test.js b/tests/Locked/state.test.js index 2a38cbbef..b75746325 100644 --- a/tests/Locked/state.test.js +++ b/tests/Locked/state.test.js @@ -14,7 +14,8 @@ const StakingProxy = artifacts.require("StakingProxy"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); +const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const { BN, // Big Number support. @@ -180,13 +181,11 @@ contract("Locked SOV (State)", (accounts) => { // Creating the Vesting Instance. vestingLogic = await VestingLogic.new(); vestingFactory = await VestingFactory.new(vestingLogic.address); - vestingRegistry = await VestingRegistry.new( - vestingFactory.address, - sov.address, - staking.address, - feeSharingCollectorProxy.address, - creator // This should be Governance Timelock Contract. - ); + + vestingRegistry = await VestingRegistry.new(); + vestingRegistryProxy = await VestingRegistryProxy.new(); + await vestingRegistryProxy.setImplementation(vestingRegistry.address); + vestingRegistry = await VestingRegistry.at(vestingRegistryProxy.address); await vestingFactory.transferOwnership(vestingRegistry.address); // Creating the instance of newLockedSOV Contract. @@ -199,6 +198,16 @@ contract("Locked SOV (State)", (accounts) => { admin, ]); + await vestingRegistry.initialize( + vestingFactory.address, + sov.address, + staking.address, + feeSharingCollectorProxy.address, + creator, // This should be Governance Timelock Contract. + lockedSOV.address, + [vestingRegistry.address] + ); + // Adding lockedSOV as an admin in the Vesting Registry. await vestingRegistry.addAdmin(lockedSOV.address); }); diff --git a/tests/OriginInvestorsClaim.test.js b/tests/OriginInvestorsClaim.test.js index 87052178d..010508eb4 100644 --- a/tests/OriginInvestorsClaim.test.js +++ b/tests/OriginInvestorsClaim.test.js @@ -30,7 +30,9 @@ const TestWrbtc = artifacts.require("TestWrbtc"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry2"); // removed some methods from VestingRegistry to prevent double spendings +const VestingRegistry = artifacts.require("VestingRegistry"); // removed some methods from VestingRegistry to prevent double spendings +const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); +const LockedSOV = artifacts.require("LockedSOV"); const OriginInvestorsClaim = artifacts.require("OriginInvestorsClaim"); const { @@ -67,13 +69,19 @@ contract("OriginInvestorsClaim", (accounts) => { return kickoffTS.add(new BN(offset)); } - async function checkVestingContractCreatedAndStaked(txHash, receiver, cliff, amount) { + async function checkVestingContractCreatedAndStaked( + txHash, + receiver, + cliff, + duration, + amount + ) { const vestingAddress = await vestingRegistry.getVesting(receiver); await expectEvent.inTransaction(txHash, vestingRegistry, "VestingCreated", { tokenOwner: receiver, vesting: vestingAddress, cliff: cliff, - duration: cliff, + duration: duration, amount: amount, }); @@ -172,15 +180,27 @@ contract("OriginInvestorsClaim", (accounts) => { vestingLogic = await VestingLogic.new(); vestingFactory = await VestingFactory.new(vestingLogic.address); - vestingRegistry = await VestingRegistry.new( + vestingRegistry = await VestingRegistry.new(); + vestingRegistryProxy = await VestingRegistryProxy.new(); + await vestingRegistryProxy.setImplementation(vestingRegistry.address); + vestingRegistry = await VestingRegistry.at(vestingRegistryProxy.address); + + let cliff = 1; + let duration = 20; + lockedSOV = await LockedSOV.new(SOV.address, vestingRegistry.address, cliff, duration, [ + account1, + ]); + + await vestingRegistry.initialize( vestingFactory.address, SOV.address, - [cSOV1.address, cSOV2.address], - priceSats, staking.address, feeSharingCollectorProxy.address, - account1 + accounts[2], + lockedSOV.address, + [vestingRegistry.address] ); + await vestingFactory.transferOwnership(vestingRegistry.address); kickoffTS = await staking.kickoffTS.call(); @@ -362,7 +382,8 @@ contract("OriginInvestorsClaim", (accounts) => { await checkVestingContractCreatedAndStaked( txHash, investor1, - SIX_WEEKS.sub(ONE_WEEK), + FOUR_WEEKS, + await lockedSOV.duration(), amount1 ); @@ -380,7 +401,13 @@ contract("OriginInvestorsClaim", (accounts) => { txHash = tx.receipt.transactionHash; - await checkVestingContractCreatedAndStaked(txHash, investor2, new BN(1), amount2); + await checkVestingContractCreatedAndStaked( + txHash, + investor2, + FOUR_WEEKS, + await lockedSOV.duration(), + amount2 + ); expect(await investorsClaim.investorsAmountsList(investor2)).to.be.bignumber.equal( new BN(0) diff --git a/tests/affiliates/affiliates.test.js b/tests/affiliates/affiliates.test.js index fc5cd9995..97b1f7d93 100644 --- a/tests/affiliates/affiliates.test.js +++ b/tests/affiliates/affiliates.test.js @@ -38,7 +38,7 @@ const StakingProxy = artifacts.require("StakingProxy"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); const TestWrbtc = artifacts.require("TestWrbtc"); const TestToken = artifacts.require("TestToken"); diff --git a/tests/affiliates/affiliates_integration.test.js b/tests/affiliates/affiliates_integration.test.js index fdf8aa800..cb4a90cab 100644 --- a/tests/affiliates/affiliates_integration.test.js +++ b/tests/affiliates/affiliates_integration.test.js @@ -31,7 +31,7 @@ const StakingProxy = artifacts.require("StakingProxy"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); const TestWrbtc = artifacts.require("TestWrbtc"); const TestToken = artifacts.require("TestToken"); diff --git a/tests/staking/ExtendedStakingTest.js b/tests/staking/ExtendedStakingTest.js index 5512bf781..da061ec02 100644 --- a/tests/staking/ExtendedStakingTest.js +++ b/tests/staking/ExtendedStakingTest.js @@ -70,7 +70,7 @@ const FeeSharingCollector = artifacts.require("FeeSharingCollector"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorProxy"); // Upgradable Vesting Registry -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistry = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const StakingStakeModule = artifacts.require("StakingStakeModule"); @@ -137,10 +137,10 @@ contract("Staking", (accounts) => { await staking.setMaxVestingWithdrawIterations(maxWithdrawIterations); // Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); - vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + vestingRegistry = await VestingRegistry.new(); + vestingRegistryProxy = await VestingRegistryProxy.new(); + await vestingRegistryProxy.setImplementation(vestingRegistry.address); + vesting = await VestingRegistry.at(vestingRegistryProxy.address); await staking.setVestingRegistry(vesting.address); diff --git a/tests/staking/PauseStaking.test.js b/tests/staking/PauseStaking.test.js index 7139bfdba..dd735f029 100644 --- a/tests/staking/PauseStaking.test.js +++ b/tests/staking/PauseStaking.test.js @@ -38,8 +38,8 @@ const FeeSharingCollector = artifacts.require("FeeSharingCollector"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorProxy"); // Upgradable Vesting Registry -// const VestingRegistryLogic = artifacts.require("VestingRegistryLogicMockup"); -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +// const VestingRegistry = artifacts.require("VestingRegistryMockup"); +const VestingRegistry = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const Vesting = artifacts.require("TeamVesting"); @@ -103,10 +103,10 @@ contract("Staking", (accounts) => { iWeightedStakingModuleMockup = await IWeightedStakingModuleMockup.at(staking.address); // Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); - vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + vestingRegistry = await VestingRegistry.new(); + vestingRegistryProxy = await VestingRegistryProxy.new(); + await vestingRegistryProxy.setImplementation(vestingRegistry.address); + vesting = await VestingRegistry.at(vestingRegistryProxy.address); await staking.setVestingRegistry(vesting.address); await staking.setMaxVestingWithdrawIterations(maxWithdrawIterations); @@ -361,10 +361,10 @@ contract("Staking", (accounts) => { let toStake = ONE_MILLON; // Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistryLogic = await VestingRegistry.new(); vestingRegistry = await VestingRegistryProxy.new(); await vestingRegistry.setImplementation(vestingRegistryLogic.address); - vestingRegistry = await VestingRegistryLogic.at(vestingRegistry.address); + vestingRegistry = await VestingRegistry.at(vestingRegistry.address); await staking.setVestingRegistry(vestingRegistry.address); @@ -492,10 +492,10 @@ contract("Staking", (accounts) => { let toStake = ONE_MILLON; // Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistryLogic = await VestingRegistry.new(); vestingRegistry = await VestingRegistryProxy.new(); await vestingRegistry.setImplementation(vestingRegistryLogic.address); - vestingRegistry = await VestingRegistryLogic.at(vestingRegistry.address); + vestingRegistry = await VestingRegistry.at(vestingRegistry.address); await staking.setVestingRegistry(vestingRegistry.address); @@ -581,10 +581,10 @@ contract("Staking", (accounts) => { let toStake = ONE_MILLON; // Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistryLogic = await VestingRegistry.new(); vestingRegistry = await VestingRegistryProxy.new(); await vestingRegistry.setImplementation(vestingRegistryLogic.address); - vestingRegistry = await VestingRegistryLogic.at(vestingRegistry.address); + vestingRegistry = await VestingRegistry.at(vestingRegistry.address); await staking.setVestingRegistry(vestingRegistry.address); @@ -662,10 +662,10 @@ contract("Staking", (accounts) => { /** Try to withdraw by using cancelTeamVesting function */ // Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistryLogic = await VestingRegistry.new(); vestingRegistry = await VestingRegistryProxy.new(); await vestingRegistry.setImplementation(vestingRegistryLogic.address); - vestingRegistry = await VestingRegistryLogic.at(vestingRegistry.address); + vestingRegistry = await VestingRegistry.at(vestingRegistry.address); await staking.setVestingRegistry(vestingRegistry.address); @@ -721,10 +721,10 @@ contract("Staking", (accounts) => { let toStake = ONE_MILLON; // Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistryLogic = await VestingRegistry.new(); vestingRegistry = await VestingRegistryProxy.new(); await vestingRegistry.setImplementation(vestingRegistryLogic.address); - vestingRegistry = await VestingRegistryLogic.at(vestingRegistry.address); + vestingRegistry = await VestingRegistry.at(vestingRegistry.address); await staking.setVestingRegistry(vestingRegistry.address); diff --git a/tests/staking/StakingTest.js b/tests/staking/StakingTest.js index 7f851ff30..07e370961 100644 --- a/tests/staking/StakingTest.js +++ b/tests/staking/StakingTest.js @@ -41,7 +41,7 @@ const Vesting = artifacts.require("Vesting"); const VestingLogic = artifacts.require("VestingLogic"); const StakingTester = artifacts.require("StakingTester"); //Upgradable Vesting Registry -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistry = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const StakingAdminModule = artifacts.require("StakingAdminModule"); const StakingVestingModule = artifacts.require("StakingVestingModule"); @@ -101,10 +101,10 @@ contract("Staking", (accounts) => { stakingWrapperMockup = await StakingWrapperMockup.new(stakingProxy.address, token.address); //Upgradable Vesting Registry - const vestingRegistryLogic = await VestingRegistryLogic.new(); + const vestingRegistry = await VestingRegistry.new(); vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + await vesting.setImplementation(vestingRegistry.address); + vesting = await VestingRegistry.at(vesting.address); //FeeSharingCollectorProxy let feeSharingCollector = await FeeSharingCollector.new(); @@ -1093,7 +1093,7 @@ contract("Staking", (accounts) => { //expect(await staking.frozen()).to.be.false; // sanity check const newAddress = address(1337); await staking.setVestingRegistry(newAddress); - expect(await staking.vestingRegistryLogic()).to.be.equal(newAddress); + expect(await staking.vestingRegistry()).to.be.equal(newAddress); }); it("the owner may not set the vesting registry if the contract is frozen", async () => { @@ -1105,7 +1105,7 @@ contract("Staking", (accounts) => { await staking.pauseUnpause(true); const newAddress = address(1337); await staking.setVestingRegistry(newAddress); - expect(await staking.vestingRegistryLogic()).to.be.equal(newAddress); + expect(await staking.vestingRegistry()).to.be.equal(newAddress); }); it("any other address may not set the vesting registry", async () => { @@ -1124,10 +1124,10 @@ contract("Staking", (accounts) => { it("it is allowed to set the vesting registry to the 0 address", async () => { await staking.setVestingRegistry(address(0)); - expect(await staking.vestingRegistryLogic()).to.be.equal(address(0)); + expect(await staking.vestingRegistry()).to.be.equal(address(0)); }); - // "calling vestingRegistryLogic returns _vestingRegistryProxy" is tested implicitly in the above scenarios + // "calling vestingRegistry returns _vestingRegistryProxy" is tested implicitly in the above scenarios }); describe("setVestingStakes", () => { diff --git a/tests/staking/VoteExploitSameBlock.test.js b/tests/staking/VoteExploitSameBlock.test.js index 8868a3949..920a9f307 100644 --- a/tests/staking/VoteExploitSameBlock.test.js +++ b/tests/staking/VoteExploitSameBlock.test.js @@ -16,7 +16,7 @@ const StakingProxy = artifacts.require("StakingProxy"); const TestToken = artifacts.require("TestToken"); const VestingLogic = artifacts.require("VestingLogic"); //Upgradable Vesting Registry -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistry = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const TOTAL_SUPPLY = "10000000000000000000000000"; @@ -73,10 +73,10 @@ contract("Staking", (accounts) => { staking = await deployAndGetIStaking(stakingProxy.address); //Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistry = await VestingRegistry.new(); vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + await vesting.setImplementation(vestingRegistry.address); + vesting = await VestingRegistry.at(vesting.address); await staking.setVestingRegistry(vesting.address); diff --git a/tests/stakingRewards/Rewards.js b/tests/stakingRewards/Rewards.js index 961850c63..e0e08e117 100644 --- a/tests/stakingRewards/Rewards.js +++ b/tests/stakingRewards/Rewards.js @@ -26,7 +26,7 @@ const StakingRewardsProxy = artifacts.require("StakingRewardsProxy"); const IStakingModuleBlockMockup = artifacts.require("IStakingModuleBlockMockup"); // Upgradable Vesting Registry -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistry = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const BlockMockUp = artifacts.require("BlockMockUp"); @@ -59,10 +59,10 @@ contract("StakingRewards - First Period", (accounts) => { staking = await IStakingModuleBlockMockup.at(iStaking.address); // applying extended mockup interface //Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistry = await VestingRegistry.new(); vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + await vesting.setImplementation(vestingRegistry.address); + vesting = await VestingRegistry.at(vesting.address); await staking.setVestingRegistry(vesting.address); kickoffTS = await staking.kickoffTS.call(); diff --git a/tests/stakingRewards/RewardsOs.js b/tests/stakingRewards/RewardsOs.js index 82444e21d..c1cad8716 100644 --- a/tests/stakingRewards/RewardsOs.js +++ b/tests/stakingRewards/RewardsOs.js @@ -27,7 +27,7 @@ const StakingRewardsProxy = artifacts.require("StakingRewardsOsProxy"); const IStakingModuleBlockMockup = artifacts.require("IStakingModuleBlockMockup"); // Upgradable Vesting Registry -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistryLogic = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const BlockMockUp = artifacts.require("BlockMockUp"); @@ -74,9 +74,9 @@ contract("StakingRewardsOs - First Period", (accounts) => { //Upgradable Vesting Registry vestingRegistryLogic = await VestingRegistryLogic.new(); - vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + vestingRegistryProxy = await VestingRegistryProxy.new(); + await vestingRegistryProxy.setImplementation(vestingRegistryLogic.address); + vesting = await VestingRegistryLogic.at(vestingRegistryProxy.address); await staking.setVestingRegistry(vesting.address); kickoffTS = await staking.kickoffTS.call(); diff --git a/tests/stakingRewards/StakingRewards.js b/tests/stakingRewards/StakingRewards.js index 9bcbe23a2..5999dd288 100644 --- a/tests/stakingRewards/StakingRewards.js +++ b/tests/stakingRewards/StakingRewards.js @@ -23,7 +23,7 @@ const FeeSharingCollector = artifacts.require("FeeSharingCollector"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorProxy"); // Upgradable Vesting Registry -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistry = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const BlockMockUp = artifacts.require("BlockMockUp"); const IStakingModuleBlockMockup = artifacts.require("IStakingModuleBlockMockup"); @@ -83,10 +83,10 @@ contract("StakingRewards", (accounts) => { await staking.setFeeSharing(feeSharingCollectorProxy.address); //Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistry = await VestingRegistry.new(); vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + await vesting.setImplementation(vestingRegistry.address); + vesting = await VestingRegistry.at(vesting.address); await staking.setVestingRegistry(vesting.address); diff --git a/tests/stakingRewards/StakingRewardsOs.js b/tests/stakingRewards/StakingRewardsOs.js index 609113d82..f172a76cb 100644 --- a/tests/stakingRewards/StakingRewardsOs.js +++ b/tests/stakingRewards/StakingRewardsOs.js @@ -29,7 +29,7 @@ const FeeSharingCollector = artifacts.require("FeeSharingCollector"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorProxy"); // Upgradable Vesting Registry -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistryLogic = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const BlockMockUp = artifacts.require("BlockMockUp"); const IStakingModuleBlockMockup = artifacts.require("IStakingModuleBlockMockup"); @@ -91,9 +91,9 @@ contract("StakingRewardsOs", (accounts) => { //Upgradable Vesting Registry vestingRegistryLogic = await VestingRegistryLogic.new(); - vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + vestingRegistryProxy = await VestingRegistryProxy.new(); + await vestingRegistryProxy.setImplementation(vestingRegistryLogic.address); + vesting = await VestingRegistryLogic.at(vestingRegistryProxy.address); await staking.setVestingRegistry(vesting.address); diff --git a/tests/swaps/SwapsExternal.js b/tests/swaps/SwapsExternal.js index 51a1ef97d..3549bcfb6 100644 --- a/tests/swaps/SwapsExternal.js +++ b/tests/swaps/SwapsExternal.js @@ -39,7 +39,7 @@ const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorProxy"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry3"); +const VestingRegistry = artifacts.require("VestingRegistry"); const { getSUSD, getRBTC, diff --git a/tests/vesting/FourYearVesting.js b/tests/vesting/FourYearVesting.js index b7065623c..93269f7ae 100644 --- a/tests/vesting/FourYearVesting.js +++ b/tests/vesting/FourYearVesting.js @@ -14,7 +14,7 @@ const VestingLogic = artifacts.require("FourYearVestingLogic"); const Vesting = artifacts.require("FourYearVesting"); const VestingFactory = artifacts.require("FourYearVestingFactory"); //Upgradable Vesting Registry -const VestingRegistryLogic = artifacts.require("VestingRegistryLogicMockup"); +const VestingRegistry = artifacts.require("VestingRegistryMockup"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const MAX_DURATION = new BN(24 * 60 * 60).mul(new BN(1092)); @@ -58,10 +58,10 @@ contract("FourYearVesting", (accounts) => { staking = await deployAndGetIStaking(stakingProxy.address); //Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistry = await VestingRegistry.new(); vestingReg = await VestingRegistryProxy.new(); - await vestingReg.setImplementation(vestingRegistryLogic.address); - vestingReg = await VestingRegistryLogic.at(vestingReg.address); + await vestingReg.setImplementation(vestingRegistry.address); + vestingReg = await VestingRegistry.at(vestingReg.address); console.log(`staking owner: ${await staking.owner()}`); await staking.setVestingRegistry(vestingReg.address); diff --git a/tests/vesting/PostCSOV_dev.js b/tests/vesting/PostCSOV_dev.js deleted file mode 100644 index 04633da24..000000000 --- a/tests/vesting/PostCSOV_dev.js +++ /dev/null @@ -1,226 +0,0 @@ -/** Speed optimized on branch hardhatTestRefactor, 2021-10-05 - * Bottleneck found at beforeEach hook, redeploying tokens on every test. - * - * Total time elapsed: 4.5s - * After optimization: 4.2s - * - * Notes: Applied fixture to use snapshot beforeEach test. - */ - -const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); -const { expectEvent, expectRevert } = require("@openzeppelin/test-helpers"); -const PostCSOV = artifacts.require("VestingRegistry.sol"); -const TestToken = artifacts.require("TestToken.sol"); - -const TOTAL_SUPPLY = "100000000000000000000000000"; - -contract("PostCSOV", (accounts) => { - let postcsov; - let token1; - let token2; - let postcsovAddr; - - const dummyAddress = accounts[9]; - const owner = accounts[5]; - const csovAdmin = accounts[0]; - const amountUser = web3.utils.toWei("3"); - // console.log("Owner: " + owner); - - const pricsSats = "2500"; - - async function deploymentAndInitFixture(_wallets, _provider) { - // deploy CSOVToken1 - token1 = await TestToken.new("cSOV1", "cSOV1", 18, TOTAL_SUPPLY); - tokenAddr1 = await token1.address; - - await token1.transfer(accounts[2], amountUser, { from: csovAdmin }); - - let CSOVAmountWei = await token1.balanceOf(accounts[2]); - console.log("CSOVAmountWei: " + CSOVAmountWei); - - // deploy CSOVToken2 - token2 = await TestToken.new("cSOV2", "cSOV2", 18, TOTAL_SUPPLY); - tokenAddr2 = await token2.address; - - await token2.transfer(accounts[2], amountUser, { from: csovAdmin }); - - CSOVAmountWei = await token2.balanceOf(accounts[2]); - console.log("CSOVAmountWei: " + CSOVAmountWei); - - // deploy PostCSOV - postcsov = await PostCSOV.new( - dummyAddress, - dummyAddress, - [tokenAddr1, tokenAddr2], - pricsSats, - dummyAddress, - dummyAddress, - dummyAddress, - { from: owner } - ); - console.log(tokenAddr1 + " " + tokenAddr2 + " " + pricsSats); - postcsovAddr = await postcsov.address; - } - - beforeEach(async () => { - await loadFixture(deploymentAndInitFixture); - }); - - describe("deposit funds", () => { - it("should deposit", async () => { - const amount = web3.utils.toWei("3"); - let postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - - await postcsov.deposit({ from: accounts[1], value: amount }); - - postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - }); - }); - - describe("reImburse", () => { - it("should reImburse", async () => { - const amount = web3.utils.toWei("3"); - let postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - - await postcsov.deposit({ from: accounts[1], value: amount }); - - postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - - let CSOVAmountWei1 = await token1.balanceOf(accounts[2]); - console.log("CSOVAmountWei1: " + CSOVAmountWei1); - - let CSOVAmountWei2 = await token2.balanceOf(accounts[2]); - console.log("CSOVAmountWei2: " + CSOVAmountWei2); - - let tx = await postcsov.reImburse({ from: accounts[2] }); - - // Found and fixed the SIP-0007 bug on VestingRegistry::reImburse formula. - // More details at Documenting Code issues at point 11 in - // https://docs.google.com/document/d/10idTD1K6JvoBmtPKGuJ2Ub_mMh6qTLLlTP693GQKMyU/ - // Bug: let rbtcAmount = ((CSOVAmountWei1 + CSOVAmountWei2) * pricsSats) / 10 ** 10; - let rbtcAmount = ((CSOVAmountWei1 + CSOVAmountWei2) * pricsSats) / 10 ** 8; - console.log("rbtcAmount: " + rbtcAmount); - - expectEvent(tx, "CSOVReImburse", { - from: accounts[2], - CSOVamount: "6000000000000000000", - reImburseAmount: "150000000000000", - }); - }); - - it("should reImburse partially", async () => { - const amount = web3.utils.toWei("3"); - let postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - - await postcsov.deposit({ from: accounts[1], value: amount }); - - postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - - let CSOVAmountWei1 = await token1.balanceOf(accounts[2]); - console.log("CSOVAmountWei1: " + CSOVAmountWei1); - - let CSOVAmountWei2 = await token2.balanceOf(accounts[2]); - console.log("CSOVAmountWei2: " + CSOVAmountWei2); - - await postcsov.setLockedAmount(accounts[2], "2000000000000000000", { from: owner }); - let tx = await postcsov.reImburse({ from: accounts[2] }); - - // Found and fixed the SIP-0007 bug on VestingRegistry::reImburse formula. - // More details at Documenting Code issues at point 11 in - // https://docs.google.com/document/d/10idTD1K6JvoBmtPKGuJ2Ub_mMh6qTLLlTP693GQKMyU/ - let rbtcAmount = ((CSOVAmountWei1 + CSOVAmountWei2) * pricsSats) / 10 ** 8; - console.log("rbtcAmount: " + rbtcAmount); - - expectEvent(tx, "CSOVReImburse", { - from: accounts[2], - CSOVamount: "4000000000000000000", - reImburseAmount: "100000000000000", - }); - }); - - it("should NOT reImburse twice", async () => { - const amount = web3.utils.toWei("3"); - let postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - - await postcsov.deposit({ from: accounts[1], value: amount }); - - postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - - let CSOVAmountWei1 = await token1.balanceOf(accounts[2]); - console.log("CSOVAmountWei1: " + CSOVAmountWei1); - - let CSOVAmountWei2 = await token2.balanceOf(accounts[2]); - console.log("CSOVAmountWei2: " + CSOVAmountWei2); - - let tx = await postcsov.reImburse({ from: accounts[2] }); - - // Found and fixed the SIP-0007 bug on VestingRegistry::reImburse formula. - // More details at Documenting Code issues at point 11 in - // https://docs.google.com/document/d/10idTD1K6JvoBmtPKGuJ2Ub_mMh6qTLLlTP693GQKMyU/ - let rbtcAmount = ((CSOVAmountWei1 + CSOVAmountWei2) * pricsSats) / 10 ** 8; - console.log("rbtcAmount: " + rbtcAmount); - - await expectRevert(postcsov.reImburse({ from: accounts[3] }), "holder has no CSOV"); - - expectEvent(tx, "CSOVReImburse", { - from: accounts[2], - CSOVamount: "6000000000000000000", - reImburseAmount: "150000000000000", - }); - - await expectRevert( - postcsov.reImburse({ from: accounts[2] }), - "Address cannot be processed twice" - ); - }); - - it("should not reImburse if user has no CSOV", async () => { - let postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - - let CSOVAmountWei1 = await token1.balanceOf(accounts[3]); - console.log("CSOVAmountWei1: " + CSOVAmountWei1); - - let CSOVAmountWei2 = await token2.balanceOf(accounts[3]); - console.log("CSOVAmountWei2: " + CSOVAmountWei2); - - postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - console.log("CSOVAmountWei1: " + CSOVAmountWei1); - console.log("CSOVAmountWei2: " + CSOVAmountWei2); - - await expectRevert(postcsov.reImburse({ from: accounts[3] }), "holder has no CSOV"); - }); - - it("should not reImburse if user blacklisted", async () => { - await postcsov.setBlacklistFlag(accounts[3], true, { from: owner }); - - await expectRevert(postcsov.reImburse({ from: accounts[3] }), "Address blacklisted"); - }); - }); - - describe("withdraw funds", () => { - it("should withdraw", async () => { - await expectRevert( - postcsov.withdrawAll(accounts[4], { from: accounts[4] }), - "unauthorized" - ); - - let postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - - await postcsov.withdrawAll(accounts[4], { from: owner }); - - postBudget = await postcsov.budget(); - console.log("postBudget: " + postBudget); - }); - }); -}); diff --git a/tests/vesting/Vesting.js b/tests/vesting/Vesting.js index a47b8f04e..7c970c593 100644 --- a/tests/vesting/Vesting.js +++ b/tests/vesting/Vesting.js @@ -31,7 +31,7 @@ const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogicMockup"); const Vesting = artifacts.require("TeamVesting"); //Upgradable Vesting Registry -const VestingRegistryLogic = artifacts.require("VestingRegistryLogicMockup"); +const VestingRegistry = artifacts.require("VestingRegistryMockup"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const MAX_DURATION = new BN(24 * 60 * 60).mul(new BN(1092)); @@ -79,10 +79,10 @@ contract("Vesting", (accounts) => { staking = await deployAndGetIStaking(stakingProxy.address); //Upgradable Vesting Registry - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistry = await VestingRegistry.new(); vestingReg = await VestingRegistryProxy.new(); - await vestingReg.setImplementation(vestingRegistryLogic.address); - vestingReg = await VestingRegistryLogic.at(vestingReg.address); + await vestingReg.setImplementation(vestingRegistry.address); + vestingReg = await VestingRegistry.at(vestingReg.address); await staking.setVestingRegistry(vestingReg.address); await staking.setMaxVestingWithdrawIterations(maxWithdrawIterations); diff --git a/tests/vesting/VestingCreator.js b/tests/vesting/VestingCreator.js index 7c299157e..3033021fa 100644 --- a/tests/vesting/VestingCreator.js +++ b/tests/vesting/VestingCreator.js @@ -21,13 +21,10 @@ const SOV_ABI = artifacts.require("SOV"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistry = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const VestingCreator = artifacts.require("VestingCreator"); const LockedSOV = artifacts.require("LockedSOV"); -const VestingRegistry = artifacts.require("VestingRegistry"); -const VestingRegistry2 = artifacts.require("VestingRegistry2"); -const VestingRegistry3 = artifacts.require("VestingRegistry3"); const TestToken = artifacts.require("TestToken"); const TeamVesting = artifacts.require("TeamVesting"); @@ -42,9 +39,8 @@ const pricsSats = "2500"; contract("VestingCreator", (accounts) => { let root, account1, account2, account3, account4; let SOV, lockedSOV; - let vesting, vestingLogic, vestingRegistryLogic; + let vesting, vestingLogic, vestingRegistry; let vestingCreator; - let vestingRegistry, vestingRegistry2, vestingRegistry3; let cliff = 1; // This is in 4 weeks. i.e. 1 * 4 weeks. let duration = 11; // This is in 4 weeks. i.e. 11 * 4 weeks. @@ -67,44 +63,16 @@ contract("VestingCreator", (accounts) => { vestingLogic = await VestingLogic.new(); vestingFactory = await VestingFactory.new(vestingLogic.address); - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistry = await VestingRegistry.new(); vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + await vesting.setImplementation(vestingRegistry.address); + vesting = await VestingRegistry.at(vesting.address); vestingFactory.transferOwnership(vesting.address); vestingCreator = await VestingCreator.new(SOV.address, vesting.address); lockedSOV = await LockedSOV.new(SOV.address, vesting.address, cliff, duration, [root]); - vestingRegistry = await VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ); - - vestingRegistry2 = await VestingRegistry2.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ); - - vestingRegistry3 = await VestingRegistry3.new( - vestingFactory.address, - SOV.address, - staking.address, - feeSharingCollectorProxy.address, - account1 - ); - await vesting.initialize( vestingFactory.address, SOV.address, @@ -112,7 +80,7 @@ contract("VestingCreator", (accounts) => { feeSharingCollectorProxy.address, account4, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address] ); await vesting.addAdmin(vestingCreator.address); @@ -140,10 +108,10 @@ contract("VestingCreator", (accounts) => { describe("constructor", () => { it("sets the expected values", async () => { let _sov = await vestingCreator.SOV(); - let _vestingRegistryLogic = await vestingCreator.vestingRegistryLogic(); + let _vestingRegistry = await vestingCreator.vestingRegistry(); expect(_sov).equal(SOV.address); - expect(_vestingRegistryLogic).equal(vesting.address); + expect(_vestingRegistry).equal(vesting.address); }); it("fails if the 0 address is passed as SOV Address", async () => { @@ -893,127 +861,6 @@ contract("VestingCreator", (accounts) => { }); }); - describe("vestingRegistry3", () => { - it("check transferSOV", async () => { - let amount = new BN(1000); - - // Send funds to vestingRegistry3 - await SOV.transfer(vestingRegistry3.address, amount); - - // Get recipient's balance before transfer - let balance2before = await SOV.balanceOf(account1); - // console.log("balance2before: ", balance2before.toString()); - - // Call transferSOV - await vestingRegistry3.transferSOV(account1, amount); - - // Get recipient's balance after transfer - let balance2after = await SOV.balanceOf(account1); - // console.log("balance2after: ", balance2after.toString()); - - // Check account1 received the transfer - expect(balance2after.sub(balance2before)).to.be.bignumber.equal(amount); - - // Fail to transfer to address(0) - await expectRevert( - vestingRegistry3.transferSOV(ZERO_ADDRESS, amount), - "receiver address invalid" - ); - - // Fail to transfer 0 amount - await expectRevert(vestingRegistry3.transferSOV(account1, 0), "amount invalid"); - }); - - /// @dev vestingRegistry3 has its own methods for adding and removing admins - it("add and remove admin", async () => { - // Add account1 as admin - let tx = await vestingRegistry3.addAdmin(account1); - expectEvent(tx, "AdminAdded", { - admin: account1, - }); - - // Check account1 is admin - let isAdmin = await vestingRegistry3.admins(account1); - expect(isAdmin).equal(true); - - // Remove account1 as admin - tx = await vestingRegistry3.removeAdmin(account1); - expectEvent(tx, "AdminRemoved", { - admin: account1, - }); - - // Check account1 is not admin anymore - isAdmin = await vestingRegistry3.admins(account1); - expect(isAdmin).equal(false); - }); - - /// @dev vestingRegistry3 has its own method for setting vesting factory - it("set vesting factory", async () => { - await vestingRegistry3.setVestingFactory(account2); - - let vestingFactory = await vestingRegistry3.vestingFactory(); - expect(vestingFactory).equal(account2); - }); - - /// @dev Checks all require statements for test coverage - it("constructor", async () => { - await expectRevert( - VestingRegistry3.new( - vestingFactory.address, - ZERO_ADDRESS, - staking.address, - feeSharingCollectorProxy.address, - account1 - ), - "SOV address invalid" - ); - - await expectRevert( - VestingRegistry3.new( - vestingFactory.address, - SOV.address, - ZERO_ADDRESS, - feeSharingCollectorProxy.address, - account1 - ), - "staking address invalid" - ); - - await expectRevert( - VestingRegistry3.new( - vestingFactory.address, - SOV.address, - staking.address, - ZERO_ADDRESS, - account1 - ), - "feeSharingCollector address invalid" - ); - - await expectRevert( - VestingRegistry3.new( - vestingFactory.address, - SOV.address, - staking.address, - feeSharingCollectorProxy.address, - ZERO_ADDRESS - ), - "vestingOwner address invalid" - ); - }); - - /// @dev Check require statements for stakeTokens method - it("should fail stakeTokens when parameters are address(0), 0", async () => { - let amount = new BN(1000); - await SOV.transfer(vestingRegistry3.address, amount); - - await expectRevert( - vestingRegistry3.stakeTokens(ZERO_ADDRESS, amount), - "vesting address invalid" - ); - }); - }); - /// @dev Test coverage /// Vesting.js also checks delegate, but on a mockup contract describe("VestingLogic", () => { diff --git a/tests/vesting/VestingRegistry.js b/tests/vesting/VestingRegistry.js deleted file mode 100644 index b6b4286ca..000000000 --- a/tests/vesting/VestingRegistry.js +++ /dev/null @@ -1,736 +0,0 @@ -/** Speed optimized on branch hardhatTestRefactor, 2021-10-05 - * Bottlenecks found: - * + beforeEach hook, redeploying DevelopmentFund and tokens, staking and vesting. - * + should be able to create vesting (725ms) - * + should be able to exchange CSOV partially (457ms) - * - * Total time elapsed: 12.3s - * After optimization: 8.0s - * - * Notes: - * Reloading the fixture snapshot is not working for all tests. So, only - * some of them are requesting to redeploy when needed. - * - * + Added new coverage tests - */ - -const { expect } = require("chai"); - -const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); - -const { expectRevert, expectEvent, constants, BN } = require("@openzeppelin/test-helpers"); - -const { mineBlock } = require("../Utils/Ethereum"); -const { deployAndGetIStaking } = require("../Utils/initializer"); - -const StakingProxy = artifacts.require("StakingProxy"); -const SOV_ABI = artifacts.require("SOV"); -const TestWrbtc = artifacts.require("TestWrbtc"); -const TestToken = artifacts.require("TestToken"); -const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); -const VestingLogic = artifacts.require("VestingLogic"); -const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry"); -const UpgradableProxy = artifacts.require("UpgradableProxy"); -const OrigingVestingCreator = artifacts.require("OrigingVestingCreator"); - -const FOUR_WEEKS = new BN(4 * 7 * 24 * 60 * 60); - -const TEAM_VESTING_CLIFF = FOUR_WEEKS.mul(new BN(6)); -const TEAM_VESTING_DURATION = FOUR_WEEKS.mul(new BN(36)); - -const TOTAL_SUPPLY = "100000000000000000000000000"; -const ZERO_ADDRESS = constants.ZERO_ADDRESS; - -const pricsSats = "2500"; - -contract("VestingRegistry", (accounts) => { - let root, account1, account2, account3; - let SOV, cSOV1, cSOV2; - let staking, feeSharingCollectorProxy; - let vestingFactory, vestingLogic, vestingRegistry; - - async function deploymentAndInitFixture(_wallets, _provider) { - SOV = await SOV_ABI.new(TOTAL_SUPPLY); - cSOV1 = await TestToken.new("cSOV1", "cSOV1", 18, TOTAL_SUPPLY); - cSOV2 = await TestToken.new("cSOV2", "cSOV2", 18, TOTAL_SUPPLY); - - /// Staking Modules - // Creating the Staking Instance (Staking Modules Interface). - const stakingProxy = await StakingProxy.new(SOV.address); - staking = await deployAndGetIStaking(stakingProxy.address); - - feeSharingCollectorProxy = await FeeSharingCollectorProxy.new( - ZERO_ADDRESS, - staking.address - ); - - vestingLogic = await VestingLogic.new(); - vestingFactory = await VestingFactory.new(vestingLogic.address); - vestingRegistry = await VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ); - vestingFactory.transferOwnership(vestingRegistry.address); - } - - before(async () => { - [root, account1, account2, account3, ...accounts] = accounts; - }); - - beforeEach(async () => { - /// @dev Only some tests really require an initial redeployment - // await loadFixture(deploymentAndInitFixture); - }); - - describe("constructor", () => { - it("sets the expected values", async () => { - await loadFixture(deploymentAndInitFixture); - - let _sov = await vestingRegistry.SOV(); - let _CSOV1 = await vestingRegistry.CSOVtokens(0); - let _CSOV2 = await vestingRegistry.CSOVtokens(1); - let _stacking = await vestingRegistry.staking(); - let _feeSharingCollectorProxy = await vestingRegistry.feeSharingCollector(); - let _vestingOwner = await vestingRegistry.vestingOwner(); - - expect(_sov).equal(SOV.address); - expect(_CSOV1).equal(cSOV1.address); - expect(_CSOV2).equal(cSOV2.address); - expect(_stacking).equal(staking.address); - expect(_feeSharingCollectorProxy).equal(feeSharingCollectorProxy.address); - expect(_vestingOwner).equal(account1); - }); - - it("fails if the 0 address is passed as vestingFactory address", async () => { - await expectRevert( - VestingRegistry.new( - ZERO_ADDRESS, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ), - "vestingFactory address invalid" - ); - }); - - it("fails if the 0 address is passed as SOV address", async () => { - await expectRevert( - VestingRegistry.new( - vestingFactory.address, - ZERO_ADDRESS, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ), - "SOV address invalid" - ); - }); - - it("fails if the 0 address is passed as cSOV address", async () => { - await expectRevert( - VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address, ZERO_ADDRESS], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ), - "CSOV address invalid" - ); - }); - - it("fails if the 0 address is passed as staking address", async () => { - await expectRevert( - VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - ZERO_ADDRESS, - feeSharingCollectorProxy.address, - account1 - ), - "staking address invalid" - ); - }); - - it("fails if the 0 address is passed as feeSharingCollectorProxy address", async () => { - await expectRevert( - VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - ZERO_ADDRESS, - account1 - ), - "feeSharingCollector address invalid" - ); - }); - - it("fails if the 0 address is passed as vestingOwner address", async () => { - await expectRevert( - VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - ZERO_ADDRESS - ), - "vestingOwner address invalid" - ); - }); - }); - - describe("setVestingFactory", () => { - it("sets vesting factory", async () => { - await vestingRegistry.setVestingFactory(account2); - - let vestingFactory = await vestingRegistry.vestingFactory(); - expect(vestingFactory).equal(account2); - }); - - it("fails if the 0 address is passed", async () => { - await expectRevert( - vestingRegistry.setVestingFactory(ZERO_ADDRESS), - "vestingFactory address invalid" - ); - }); - - it("fails if sender isn't an owner", async () => { - await expectRevert( - vestingRegistry.setVestingFactory(account2, { from: account2 }), - "unauthorized" - ); - }); - }); - - describe("addAdmin", () => { - it("adds admin", async () => { - let tx = await vestingRegistry.addAdmin(account1); - - expectEvent(tx, "AdminAdded", { - admin: account1, - }); - - let isAdmin = await vestingRegistry.admins(account1); - expect(isAdmin).equal(true); - }); - - it("fails sender isn't an owner", async () => { - await expectRevert( - vestingRegistry.addAdmin(account1, { from: account1 }), - "unauthorized" - ); - }); - }); - - describe("removeAdmin", () => { - it("adds admin", async () => { - await vestingRegistry.addAdmin(account1); - let tx = await vestingRegistry.removeAdmin(account1); - - expectEvent(tx, "AdminRemoved", { - admin: account1, - }); - - let isAdmin = await vestingRegistry.admins(account1); - expect(isAdmin).equal(false); - }); - - it("fails sender isn't an owner", async () => { - await expectRevert( - vestingRegistry.removeAdmin(account1, { from: account1 }), - "unauthorized" - ); - }); - }); - - describe("setCSOVtokens", () => { - it("sets the expected values", async () => { - await vestingRegistry.setCSOVtokens([account2, account3]); - - let _CSOV1 = await vestingRegistry.CSOVtokens(0); - let _CSOV2 = await vestingRegistry.CSOVtokens(1); - - expect(_CSOV1).equal(account2); - expect(_CSOV2).equal(account3); - }); - - it("fails if the 0 address is passed as cSOV address", async () => { - await expectRevert( - vestingRegistry.setCSOVtokens([cSOV1.address, cSOV2.address, ZERO_ADDRESS]), - "CSOV address invalid" - ); - }); - - it("fails if sender isn't an owner", async () => { - await expectRevert( - vestingRegistry.setCSOVtokens([cSOV1.address, cSOV2.address], { from: account2 }), - "unauthorized" - ); - }); - }); - - describe("transferSOV", () => { - it("should be able to transfer SOV", async () => { - let amount = new BN(1000); - await SOV.transfer(vestingRegistry.address, amount); - - let balanceBefore = await SOV.balanceOf(account1); - await vestingRegistry.transferSOV(account1, amount); - let balanceAfter = await SOV.balanceOf(account1); - - expect(amount).to.be.bignumber.equal(balanceAfter.sub(balanceBefore)); - }); - - it("only owner should be able to transfer", async () => { - await expectRevert( - vestingRegistry.transferSOV(account1, 1000, { from: account1 }), - "unauthorized" - ); - }); - - it("fails if the 0 address is passed as receiver address", async () => { - await expectRevert( - vestingRegistry.transferSOV(ZERO_ADDRESS, 1000), - "receiver address invalid" - ); - }); - - it("fails if the 0 is passed as an amount", async () => { - await expectRevert(vestingRegistry.transferSOV(account1, 0), "amount invalid"); - }); - }); - - describe("setBlacklistFlag", () => { - it("should be able to add user to blacklist", async () => { - await vestingRegistry.setBlacklistFlag(account2, true); - - let blacklisted = await vestingRegistry.blacklist(account2); - expect(blacklisted).equal(true); - }); - - it("fails if the 0 address is passed", async () => { - await expectRevert( - vestingRegistry.setBlacklistFlag(ZERO_ADDRESS, true), - "account address invalid" - ); - }); - }); - - describe("setLockedAmount", () => { - it("should be able to set locked amount", async () => { - let amount = new BN(123); - await vestingRegistry.setLockedAmount(account2, amount); - - let lockedAmount = await vestingRegistry.lockedAmount(account2); - expect(lockedAmount).to.be.bignumber.equal(amount); - }); - - it("fails if the 0 address is passed", async () => { - await expectRevert( - vestingRegistry.setLockedAmount(ZERO_ADDRESS, 111), - "account address invalid" - ); - }); - - it("fails if the 0 amount is passed", async () => { - await expectRevert(vestingRegistry.setLockedAmount(account2, 0), "amount invalid"); - }); - }); - - describe("exchangeAllCSOV", () => { - it("should be able to exchange CSOV", async () => { - /// @dev This test requires a hard reset of init fixture - await deploymentAndInitFixture(); - - let amount1 = new BN(1000); - let amount2 = new BN(2000); - await cSOV1.transfer(account2, amount1); - await cSOV2.transfer(account2, amount2); - let amount = amount1.add(amount2); - await SOV.transfer(vestingRegistry.address, amount); - - let tx = await vestingRegistry.exchangeAllCSOV({ from: account2 }); - - expectEvent(tx, "CSOVTokensExchanged", { - caller: account2, - amount: amount, - }); - - let processedList = await vestingRegistry.processedList(account2); - expect(processedList).equal(true); - - let balance = await SOV.balanceOf(vestingRegistry.address); - expect(balance.toString()).equal("0"); - - let cliff = await vestingRegistry.CSOV_VESTING_CLIFF(); - let duration = await vestingRegistry.CSOV_VESTING_DURATION(); - - let vestingAddress = await vestingRegistry.getVesting(account2); - let vesting = await VestingLogic.at(vestingAddress); - await checkVesting(vesting, account2, cliff, duration, amount); - }); - - it("should be able to exchange CSOV partially", async () => { - /// @dev This test requires a hard reset of init fixture - await deploymentAndInitFixture(); - - let amount1 = new BN(1000); - let amount2 = new BN(2000); - let lockedAmount = new BN(700); - await cSOV1.transfer(account2, amount1); - await cSOV2.transfer(account2, amount2); - - let amount = amount1.add(amount2).sub(lockedAmount); - await SOV.transfer(vestingRegistry.address, amount); - - await vestingRegistry.setLockedAmount(account2, lockedAmount); - let tx = await vestingRegistry.exchangeAllCSOV({ from: account2 }); - - expectEvent(tx, "CSOVTokensExchanged", { - caller: account2, - amount: amount, - }); - - let processedList = await vestingRegistry.processedList(account2); - expect(processedList).equal(true); - - let balance = await SOV.balanceOf(vestingRegistry.address); - expect(balance.toString()).equal("0"); - - let cliff = await vestingRegistry.CSOV_VESTING_CLIFF(); - let duration = await vestingRegistry.CSOV_VESTING_DURATION(); - - let vestingAddress = await vestingRegistry.getVesting(account2); - let vesting = await VestingLogic.at(vestingAddress); - await checkVesting(vesting, account2, cliff, duration, amount); - }); - - it("fails if trying to withdraw second time", async () => { - /// @dev This test requires a hard reset of init fixture - await deploymentAndInitFixture(); - - let amount = new BN(1000); - await cSOV1.transfer(account2, amount); - await SOV.transfer(vestingRegistry.address, amount); - - await vestingRegistry.exchangeAllCSOV({ from: account2 }); - await expectRevert( - vestingRegistry.exchangeAllCSOV({ from: account2 }), - "Address cannot be processed twice" - ); - }); - - it("fails if account blacklisted", async () => { - /// @dev This test requires a hard reset of init fixture - await deploymentAndInitFixture(); - - let amount = new BN(1000); - await cSOV1.transfer(account2, amount); - await SOV.transfer(vestingRegistry.address, amount); - - await vestingRegistry.setBlacklistFlag(account2, true); - await expectRevert( - vestingRegistry.exchangeAllCSOV({ from: account2 }), - "Address blacklisted" - ); - }); - - it("fails if the 0 is cSOV amount", async () => { - /// @dev This test requires a hard reset of init fixture - await deploymentAndInitFixture(); - - await expectRevert( - vestingRegistry.exchangeAllCSOV({ from: account2 }), - "amount invalid" - ); - }); - - it("fails if vestingRegistry doesn't have enough SOV", async () => { - /// @dev This test requires a hard reset of init fixture - await deploymentAndInitFixture(); - - let amount = new BN(1000); - await cSOV1.transfer(account2, amount); - - await expectRevert( - vestingRegistry.exchangeAllCSOV({ from: account2 }), - "ERC20: transfer amount exceeds balance" - ); - }); - }); - - describe("createVesting", () => { - it("should be able to create vesting", async () => { - let amount = new BN(1000000); - await SOV.transfer(vestingRegistry.address, amount); - - let cliff = FOUR_WEEKS; - let duration = FOUR_WEEKS.mul(new BN(20)); - let tx = await vestingRegistry.createVesting(account2, amount, cliff, duration); - let vestingAddress = await vestingRegistry.getVesting(account2); - await vestingRegistry.stakeTokens(vestingAddress, amount); - - expectEvent(tx, "VestingCreated", { - tokenOwner: account2, - vesting: vestingAddress, - cliff: cliff, - duration: duration, - amount: amount, - }); - - let balance = await SOV.balanceOf(vestingRegistry.address); - expect(balance.toString()).equal("0"); - - let vesting = await VestingLogic.at(vestingAddress); - await checkVesting(vesting, account2, cliff, duration, amount); - - let proxy = await UpgradableProxy.at(vestingAddress); - await expectRevert(proxy.setImplementation(account2), "revert"); - }); - - it("fails if vestingRegistry doesn't have enough SOV", async () => { - let amount = new BN(1000000); - let cliff = FOUR_WEEKS; - let duration = FOUR_WEEKS.mul(new BN(20)); - - await vestingRegistry.createVesting(account2, amount, cliff, duration); - let vestingAddress = await vestingRegistry.getVesting(account2); - - await expectRevert( - vestingRegistry.stakeTokens(vestingAddress, amount), - "ERC20: transfer amount exceeds balance" - ); - }); - - it("fails if sender is not an owner or admin", async () => { - let amount = new BN(1000000); - let cliff = TEAM_VESTING_CLIFF; - let duration = TEAM_VESTING_DURATION; - - await expectRevert( - vestingRegistry.createVesting(account2, amount, cliff, duration, { - from: account1, - }), - "unauthorized" - ); - - await vestingRegistry.addAdmin(account1); - await vestingRegistry.createVesting(account2, amount, cliff, duration, { - from: account1, - }); - }); - }); - - describe("createTeamVesting", () => { - it("should be able to create vesting", async () => { - /// @dev This test requires a hard reset of init fixture - await deploymentAndInitFixture(); - - let amount = new BN(1000000); - await SOV.transfer(vestingRegistry.address, amount); - - let cliff = TEAM_VESTING_CLIFF; - let duration = TEAM_VESTING_DURATION; - let tx = await vestingRegistry.createTeamVesting(account2, amount, cliff, duration); - let vestingAddress = await vestingRegistry.getTeamVesting(account2); - let tx2 = await vestingRegistry.stakeTokens(vestingAddress, amount); - - console.log("\ngasUsed = " + tx.receipt.gasUsed); - console.log("gasUsed = " + tx2.receipt.gasUsed); - - expectEvent(tx, "TeamVestingCreated", { - tokenOwner: account2, - vesting: vestingAddress, - cliff: cliff, - duration: duration, - amount: amount, - }); - - let balance = await SOV.balanceOf(vestingRegistry.address); - expect(balance.toString()).equal("0"); - - let vesting = await VestingLogic.at(vestingAddress); - await checkVesting(vesting, account2, cliff, duration, amount); - - let proxy = await UpgradableProxy.at(vestingAddress); - await expectRevert(proxy.setImplementation(account2), "revert"); - }); - - it("fails if vestingRegistry doesn't have enough SOV", async () => { - let amount = new BN(1000000); - let cliff = TEAM_VESTING_CLIFF; - let duration = TEAM_VESTING_DURATION; - - await vestingRegistry.createTeamVesting(account2, amount, cliff, duration); - let vestingAddress = await vestingRegistry.getTeamVesting(account2); - - await expectRevert( - vestingRegistry.stakeTokens(vestingAddress, amount), - "ERC20: transfer amount exceeds balance" - ); - }); - - it("fails if sender is not an owner or admin", async () => { - let amount = new BN(1000000); - let cliff = TEAM_VESTING_CLIFF; - let duration = TEAM_VESTING_DURATION; - - await expectRevert( - vestingRegistry.createTeamVesting(account2, amount, cliff, duration, { - from: account1, - }), - "unauthorized" - ); - - await vestingRegistry.addAdmin(account1); - await vestingRegistry.createTeamVesting(account2, amount, cliff, duration, { - from: account1, - }); - }); - }); - - describe("stakeTokens", () => { - it("fails if the 0 address is passed as vesting address", async () => { - await expectRevert( - vestingRegistry.stakeTokens(ZERO_ADDRESS, new BN(1000000)), - "vesting address invalid" - ); - }); - - it("fails if the 0 address is passed as an amount", async () => { - await expectRevert(vestingRegistry.stakeTokens(account1, 0), "amount invalid"); - }); - - it("only owner or admin should be able to stake tokens", async () => { - /// @dev This test requires a hard reset of init fixture - await deploymentAndInitFixture(); - - let amount = new BN(1000000); - await SOV.transfer(vestingRegistry.address, amount); - - let cliff = TEAM_VESTING_CLIFF; - let duration = TEAM_VESTING_DURATION; - await vestingRegistry.createTeamVesting(account2, amount, cliff, duration); - let vestingAddress = await vestingRegistry.getTeamVesting(account2); - - await expectRevert( - vestingRegistry.stakeTokens(vestingAddress, new BN(1000000), { from: account1 }), - "unauthorized" - ); - - await vestingRegistry.addAdmin(account1); - await vestingRegistry.stakeTokens(vestingAddress, new BN(1000000), { from: account1 }); - }); - }); - - describe("OrigingVestingCreator", () => { - it("should be able to create vesting", async () => { - await deploymentAndInitFixture(); - - let origingVestingCreator = await OrigingVestingCreator.new(vestingRegistry.address); - - let amount = new BN(1000000); - await SOV.transfer(vestingRegistry.address, amount); - - let cliff = FOUR_WEEKS; - let duration = FOUR_WEEKS.mul(new BN(20)); - - // Adding origingVestingCreator as an admin in the Vesting Registry. - await vestingRegistry.addAdmin(origingVestingCreator.address); - - // Try to use address(0) - await expectRevert( - origingVestingCreator.createVesting(ZERO_ADDRESS, amount, cliff, duration), - "Invalid address" - ); - - // Create the vesting contract - await origingVestingCreator.createVesting(account3, amount, cliff, duration); - - // Try to create it again w/ the same tokenOwner - await expectRevert( - origingVestingCreator.createVesting(account3, amount, cliff, duration), - "Already processed" - ); - }); - }); - - async function checkVesting(vesting, account, cliff, duration, amount) { - await mineBlock(); - - let vestingBalance = await staking.balanceOf(vesting.address); - expect(vestingBalance).to.be.bignumber.equal(amount); - - let accountVotes = await staking.getCurrentVotes(account); - expect(accountVotes).to.be.not.equal(new BN(0)); - let vestingVotes = await staking.getCurrentVotes(vesting.address); - expect(vestingVotes).to.be.bignumber.equal(new BN(0)); - - let startDate = await vesting.startDate(); - let start = startDate.toNumber() + cliff.toNumber(); - let end = startDate.toNumber() + duration.toNumber(); - - let numIntervals = Math.floor((end - start) / FOUR_WEEKS) + 1; - let stakedPerInterval = Math.floor(amount / numIntervals); - - let stakeForFirstInterval = amount - stakedPerInterval * (numIntervals - 1); - - expect(await vesting.cliff()).to.be.bignumber.equal(cliff); - expect(await vesting.duration()).to.be.bignumber.equal(duration); - - for (let i = start; i <= end; i += FOUR_WEEKS) { - let lockedTS = await staking.timestampToLockDate(i); - - let numUserStakingCheckpoints = await staking.numUserStakingCheckpoints( - vesting.address, - lockedTS - ); - let userStakingCheckpoints = await staking.userStakingCheckpoints( - vesting.address, - lockedTS, - numUserStakingCheckpoints - 1 - ); - assert.equal(numUserStakingCheckpoints.toString(), "1"); - if (i === start) { - assert.equal(userStakingCheckpoints.stake.toString(), stakeForFirstInterval); - } else { - assert.equal(userStakingCheckpoints.stake.toString(), stakedPerInterval); - } - - let numDelegateStakingCheckpoints = await staking.numDelegateStakingCheckpoints( - account, - lockedTS - ); - let delegateStakingCheckpoints = await staking.delegateStakingCheckpoints( - account, - lockedTS, - numUserStakingCheckpoints - 1 - ); - assert.equal(parseInt(numDelegateStakingCheckpoints), 1); - if (i === start) { - assert.equal(delegateStakingCheckpoints.stake.toString(), stakeForFirstInterval); - } else { - assert.equal(delegateStakingCheckpoints.stake.toString(), stakedPerInterval); - } - } - } -}); diff --git a/tests/vesting/VestingRegistry2.js b/tests/vesting/VestingRegistry2.js deleted file mode 100644 index d76aac44c..000000000 --- a/tests/vesting/VestingRegistry2.js +++ /dev/null @@ -1,618 +0,0 @@ -/** Speed optimized on branch hardhatTestRefactor, 2021-10-05 - * Bottlenecks found: - * + beforeEach hook, redeploying DevelopmentFund and tokens, staking and vesting. - * + createVesting: should be able to create vesting (552ms) - * + createTeamVesting: should be able to create vesting (656ms) - * - * Total time elapsed: 10.3s - * After optimization: 6.4s - * - * Notes: - * Reloading the fixture snapshot is not working for all tests. So, only - * some of them are requesting to redeploy when needed. - * - * Added coverage tests. - */ - -const { expect, assert } = require("chai"); -const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); -const provider = ethers.provider; - -const { expectRevert, expectEvent, constants, BN } = require("@openzeppelin/test-helpers"); - -const { mineBlock } = require("../Utils/Ethereum"); -const { deployAndGetIStaking } = require("../Utils/initializer"); - -const StakingProxy = artifacts.require("StakingProxy"); -const SOV_ABI = artifacts.require("SOV"); -const TestToken = artifacts.require("TestToken"); -const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); -const VestingLogic = artifacts.require("VestingLogic"); -const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistry = artifacts.require("VestingRegistry2"); -const UpgradableProxy = artifacts.require("UpgradableProxy"); - -const FOUR_WEEKS = new BN(4 * 7 * 24 * 60 * 60); - -const TEAM_VESTING_CLIFF = FOUR_WEEKS.mul(new BN(6)); -const TEAM_VESTING_DURATION = FOUR_WEEKS.mul(new BN(36)); - -const TOTAL_SUPPLY = "100000000000000000000000000"; -const ZERO_ADDRESS = constants.ZERO_ADDRESS; - -const pricsSats = "2500"; - -contract("VestingRegistry", (accounts) => { - let root, account1, account2, account3; - let SOV, cSOV1, cSOV2; - let staking, feeSharingCollectorProxy; - let vestingFactory, vestingLogic, vestingRegistry; - - async function deploymentAndInitFixture(_wallets, _provider) { - SOV = await SOV_ABI.new(TOTAL_SUPPLY); - cSOV1 = await TestToken.new("cSOV1", "cSOV1", 18, TOTAL_SUPPLY); - cSOV2 = await TestToken.new("cSOV2", "cSOV2", 18, TOTAL_SUPPLY); - - /// Staking Modules - // Creating the Staking Instance (Staking Modules Interface). - const stakingProxy = await StakingProxy.new(SOV.address); - staking = await deployAndGetIStaking(stakingProxy.address); - - feeSharingCollectorProxy = await FeeSharingCollectorProxy.new( - ZERO_ADDRESS, - staking.address - ); - - vestingLogic = await VestingLogic.new(); - vestingFactory = await VestingFactory.new(vestingLogic.address); - vestingRegistry = await VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ); - vestingFactory.transferOwnership(vestingRegistry.address); - } - - before(async () => { - [root, account1, account2, account3, ...accounts] = accounts; - }); - - beforeEach(async () => { - /// @dev Only some tests really require an initial redeployment - // await loadFixture(deploymentAndInitFixture); - }); - - describe("constructor", () => { - it("sets the expected values", async () => { - await loadFixture(deploymentAndInitFixture); - - let _sov = await vestingRegistry.SOV(); - let _CSOV1 = await vestingRegistry.CSOVtokens(0); - let _CSOV2 = await vestingRegistry.CSOVtokens(1); - let _stacking = await vestingRegistry.staking(); - let _feeSharingCollectorProxy = await vestingRegistry.feeSharingCollector(); - let _vestingOwner = await vestingRegistry.vestingOwner(); - - expect(_sov).equal(SOV.address); - expect(_CSOV1).equal(cSOV1.address); - expect(_CSOV2).equal(cSOV2.address); - expect(_stacking).equal(staking.address); - expect(_feeSharingCollectorProxy).equal(feeSharingCollectorProxy.address); - expect(_vestingOwner).equal(account1); - }); - - it("fails if the 0 address is passed as vestingFactory address", async () => { - await expectRevert( - VestingRegistry.new( - ZERO_ADDRESS, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ), - "vestingFactory address invalid" - ); - }); - - it("fails if the 0 address is passed as SOV address", async () => { - await expectRevert( - VestingRegistry.new( - vestingFactory.address, - ZERO_ADDRESS, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ), - "SOV address invalid" - ); - }); - - it("fails if the 0 address is passed as cSOV address", async () => { - await expectRevert( - VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address, ZERO_ADDRESS], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ), - "CSOV address invalid" - ); - }); - - it("fails if the 0 address is passed as staking address", async () => { - await expectRevert( - VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - ZERO_ADDRESS, - feeSharingCollectorProxy.address, - account1 - ), - "staking address invalid" - ); - }); - - it("fails if the 0 address is passed as feeSharingCollectorProxy address", async () => { - await expectRevert( - VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - ZERO_ADDRESS, - account1 - ), - "feeSharingCollector address invalid" - ); - }); - - it("fails if the 0 address is passed as vestingOwner address", async () => { - await expectRevert( - VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - ZERO_ADDRESS - ), - "vestingOwner address invalid" - ); - }); - }); - - describe("setVestingFactory", () => { - it("sets vesting factory", async () => { - await vestingRegistry.setVestingFactory(account2); - - let vestingFactory = await vestingRegistry.vestingFactory(); - expect(vestingFactory).equal(account2); - }); - - it("fails if the 0 address is passed", async () => { - await expectRevert( - vestingRegistry.setVestingFactory(ZERO_ADDRESS), - "vestingFactory address invalid" - ); - }); - - it("fails if sender isn't an owner", async () => { - await expectRevert( - vestingRegistry.setVestingFactory(account2, { from: account2 }), - "unauthorized" - ); - }); - }); - - describe("addAdmin", () => { - it("adds admin", async () => { - let tx = await vestingRegistry.addAdmin(account1); - - expectEvent(tx, "AdminAdded", { - admin: account1, - }); - - let isAdmin = await vestingRegistry.admins(account1); - expect(isAdmin).equal(true); - }); - - it("fails sender isn't an owner", async () => { - await expectRevert( - vestingRegistry.addAdmin(account1, { from: account1 }), - "unauthorized" - ); - }); - }); - - describe("removeAdmin", () => { - it("adds admin", async () => { - await vestingRegistry.addAdmin(account1); - let tx = await vestingRegistry.removeAdmin(account1); - - expectEvent(tx, "AdminRemoved", { - admin: account1, - }); - - let isAdmin = await vestingRegistry.admins(account1); - expect(isAdmin).equal(false); - }); - - it("fails sender isn't an owner", async () => { - await expectRevert( - vestingRegistry.removeAdmin(account1, { from: account1 }), - "unauthorized" - ); - }); - }); - - describe("setCSOVtokens", () => { - it("sets the expected values", async () => { - await vestingRegistry.setCSOVtokens([account2, account3]); - - let _CSOV1 = await vestingRegistry.CSOVtokens(0); - let _CSOV2 = await vestingRegistry.CSOVtokens(1); - - expect(_CSOV1).equal(account2); - expect(_CSOV2).equal(account3); - }); - - it("fails if the 0 address is passed as cSOV address", async () => { - await expectRevert( - vestingRegistry.setCSOVtokens([cSOV1.address, cSOV2.address, ZERO_ADDRESS]), - "CSOV address invalid" - ); - }); - - it("fails if sender isn't an owner", async () => { - await expectRevert( - vestingRegistry.setCSOVtokens([cSOV1.address, cSOV2.address], { from: account2 }), - "unauthorized" - ); - }); - }); - - describe("transferSOV", () => { - it("should be able to transfer SOV", async () => { - let amount = new BN(1000); - await SOV.transfer(vestingRegistry.address, amount); - - let balanceBefore = await SOV.balanceOf(account1); - await vestingRegistry.transferSOV(account1, amount); - let balanceAfter = await SOV.balanceOf(account1); - - expect(amount).to.be.bignumber.equal(balanceAfter.sub(balanceBefore)); - }); - - it("only owner should be able to transfer", async () => { - await expectRevert( - vestingRegistry.transferSOV(account1, 1000, { from: account1 }), - "unauthorized" - ); - }); - - it("fails if the 0 address is passed as receiver address", async () => { - await expectRevert( - vestingRegistry.transferSOV(ZERO_ADDRESS, 1000), - "receiver address invalid" - ); - }); - - it("fails if the 0 is passed as an amount", async () => { - await expectRevert(vestingRegistry.transferSOV(account1, 0), "amount invalid"); - }); - }); - - describe("setBlacklistFlag", () => { - it("should be able to add user to blacklist", async () => { - await vestingRegistry.setBlacklistFlag(account2, true); - - let blacklisted = await vestingRegistry.blacklist(account2); - expect(blacklisted).equal(true); - }); - - it("fails if the 0 address is passed", async () => { - await expectRevert( - vestingRegistry.setBlacklistFlag(ZERO_ADDRESS, true), - "account address invalid" - ); - }); - }); - - describe("setLockedAmount", () => { - it("should be able to set locked amount", async () => { - let amount = new BN(123); - await vestingRegistry.setLockedAmount(account2, amount); - - let lockedAmount = await vestingRegistry.lockedAmount(account2); - expect(lockedAmount).to.be.bignumber.equal(amount); - }); - - it("fails if the 0 address is passed", async () => { - await expectRevert( - vestingRegistry.setLockedAmount(ZERO_ADDRESS, 111), - "account address invalid" - ); - }); - - it("fails if the 0 amount is passed", async () => { - await expectRevert(vestingRegistry.setLockedAmount(account2, 0), "amount invalid"); - }); - }); - - describe("createVesting", () => { - it("should be able to create vesting", async () => { - /// @dev This test requires a hard reset of init fixture - await deploymentAndInitFixture(); - - let amount = new BN(1000000); - await SOV.transfer(vestingRegistry.address, amount); - - let cliff = FOUR_WEEKS; - let duration = FOUR_WEEKS.mul(new BN(20)); - let tx = await vestingRegistry.createVesting(account2, amount, cliff, duration); - let vestingAddress = await vestingRegistry.getVesting(account2); - await vestingRegistry.stakeTokens(vestingAddress, amount); - - expectEvent(tx, "VestingCreated", { - tokenOwner: account2, - vesting: vestingAddress, - cliff: cliff, - duration: duration, - amount: amount, - }); - - let balance = await SOV.balanceOf(vestingRegistry.address); - expect(balance.toString()).equal("0"); - - let vesting = await VestingLogic.at(vestingAddress); - await checkVesting(vesting, account2, cliff, duration, amount); - - let proxy = await UpgradableProxy.at(vestingAddress); - await expectRevert(proxy.setImplementation(account2), "revert"); - }); - - it("fails if vestingRegistry doesn't have enough SOV", async () => { - let amount = new BN(1000000); - let cliff = FOUR_WEEKS; - let duration = FOUR_WEEKS.mul(new BN(20)); - - await vestingRegistry.createVesting(account2, amount, cliff, duration); - let vestingAddress = await vestingRegistry.getVesting(account2); - - await expectRevert( - vestingRegistry.stakeTokens(vestingAddress, amount), - "ERC20: transfer amount exceeds balance" - ); - }); - - it("fails if sender is not an owner or admin", async () => { - let amount = new BN(1000000); - let cliff = TEAM_VESTING_CLIFF; - let duration = TEAM_VESTING_DURATION; - - await expectRevert( - vestingRegistry.createVesting(account2, amount, cliff, duration, { - from: account1, - }), - "unauthorized" - ); - - await vestingRegistry.addAdmin(account1); - await vestingRegistry.createVesting(account2, amount, cliff, duration, { - from: account1, - }); - }); - }); - - describe("createTeamVesting", () => { - it("should be able to create vesting", async () => { - /// @dev This test requires a hard reset of init fixture - await deploymentAndInitFixture(); - - let amount = new BN(1000000); - await SOV.transfer(vestingRegistry.address, amount); - - let cliff = TEAM_VESTING_CLIFF; - let duration = TEAM_VESTING_DURATION; - let tx = await vestingRegistry.createTeamVesting(account2, amount, cliff, duration); - let vestingAddress = await vestingRegistry.getTeamVesting(account2); - let tx2 = await vestingRegistry.stakeTokens(vestingAddress, amount); - - console.log("\ngasUsed = " + tx.receipt.gasUsed); - console.log("gasUsed = " + tx2.receipt.gasUsed); - - expectEvent(tx, "TeamVestingCreated", { - tokenOwner: account2, - vesting: vestingAddress, - cliff: cliff, - duration: duration, - amount: amount, - }); - - let balance = await SOV.balanceOf(vestingRegistry.address); - expect(balance.toString()).equal("0"); - - let vesting = await VestingLogic.at(vestingAddress); - await checkVesting(vesting, account2, cliff, duration, amount); - - let proxy = await UpgradableProxy.at(vestingAddress); - await expectRevert(proxy.setImplementation(account2), "revert"); - }); - - it("fails if vestingRegistry doesn't have enough SOV", async () => { - let amount = new BN(1000000); - let cliff = TEAM_VESTING_CLIFF; - let duration = TEAM_VESTING_DURATION; - - await vestingRegistry.createTeamVesting(account2, amount, cliff, duration); - let vestingAddress = await vestingRegistry.getTeamVesting(account2); - - await expectRevert( - vestingRegistry.stakeTokens(vestingAddress, amount), - "ERC20: transfer amount exceeds balance" - ); - }); - - it("fails if sender is not an owner or admin", async () => { - let amount = new BN(1000000); - let cliff = TEAM_VESTING_CLIFF; - let duration = TEAM_VESTING_DURATION; - - await expectRevert( - vestingRegistry.createTeamVesting(account2, amount, cliff, duration, { - from: account1, - }), - "unauthorized" - ); - - await vestingRegistry.addAdmin(account1); - await vestingRegistry.createTeamVesting(account2, amount, cliff, duration, { - from: account1, - }); - }); - }); - - describe("stakeTokens", () => { - it("fails if the 0 address is passed as vesting address", async () => { - await expectRevert( - vestingRegistry.stakeTokens(ZERO_ADDRESS, new BN(1000000)), - "vesting address invalid" - ); - }); - - it("fails if the 0 address is passed as an amount", async () => { - await expectRevert(vestingRegistry.stakeTokens(account1, 0), "amount invalid"); - }); - - it("only owner or admin should be able to stake tokens", async () => { - /// @dev This test requires a hard reset of init fixture - await deploymentAndInitFixture(); - - let amount = new BN(1000000); - await SOV.transfer(vestingRegistry.address, amount); - - let cliff = TEAM_VESTING_CLIFF; - let duration = TEAM_VESTING_DURATION; - await vestingRegistry.createTeamVesting(account2, amount, cliff, duration); - let vestingAddress = await vestingRegistry.getTeamVesting(account2); - - await expectRevert( - vestingRegistry.stakeTokens(vestingAddress, new BN(1000000), { from: account1 }), - "unauthorized" - ); - - await vestingRegistry.addAdmin(account1); - await vestingRegistry.stakeTokens(vestingAddress, new BN(1000000), { from: account1 }); - }); - }); - - describe("deposit funds", () => { - it("should get budget and deposit", async () => { - await deploymentAndInitFixture(); - - // Check budget is 0 - let budget = await vestingRegistry.budget(); - // console.log("budget: " + budget); - expect(budget).to.be.bignumber.equal(new BN(0)); - - // Deposit funds - const amount = web3.utils.toWei("3"); - await vestingRegistry.deposit({ from: accounts[1], value: amount }); - - // Check budget is not 0 - budget = await vestingRegistry.budget(); - // console.log("budget: " + budget); - expect(budget).to.be.bignumber.equal(amount); - - // Get recipient's balance before withdrawal - let balance2before = await provider.getBalance(accounts[2]); - // console.log("balance2before: " + balance2before); - - // Check budget after withdrawal - await vestingRegistry.withdrawAll(accounts[2]); - budget = await vestingRegistry.budget(); - // console.log("budget: " + budget); - expect(budget).to.be.bignumber.equal(new BN(0)); - - // Get recipient's balance after withdrawal - let balance2after = await provider.getBalance(accounts[2]); - // console.log("balance2after: " + balance2after); - expect(balance2after.sub(balance2before)).to.be.equal(amount); - }); - }); - - async function checkVesting(vesting, account, cliff, duration, amount) { - await mineBlock(); - - let vestingBalance = await staking.balanceOf(vesting.address); - expect(vestingBalance).to.be.bignumber.equal(amount); - - let accountVotes = await staking.getCurrentVotes(account); - expect(accountVotes).to.be.not.equal(new BN(0)); - let vestingVotes = await staking.getCurrentVotes(vesting.address); - expect(vestingVotes).to.be.bignumber.equal(new BN(0)); - - let startDate = await vesting.startDate(); - let start = startDate.toNumber() + cliff.toNumber(); - let end = startDate.toNumber() + duration.toNumber(); - - let numIntervals = Math.floor((end - start) / FOUR_WEEKS) + 1; - let stakedPerInterval = Math.floor(amount / numIntervals); - - let stakeForFirstInterval = amount - stakedPerInterval * (numIntervals - 1); - - expect(await vesting.cliff()).to.be.bignumber.equal(cliff); - expect(await vesting.duration()).to.be.bignumber.equal(duration); - - for (let i = start; i <= end; i += FOUR_WEEKS) { - let lockedTS = await staking.timestampToLockDate(i); - - let numUserStakingCheckpoints = await staking.numUserStakingCheckpoints( - vesting.address, - lockedTS - ); - let userStakingCheckpoints = await staking.userStakingCheckpoints( - vesting.address, - lockedTS, - numUserStakingCheckpoints - 1 - ); - assert.equal(numUserStakingCheckpoints.toString(), "1"); - if (i === start) { - assert.equal(userStakingCheckpoints.stake.toString(), stakeForFirstInterval); - } else { - assert.equal(userStakingCheckpoints.stake.toString(), stakedPerInterval); - } - - let numDelegateStakingCheckpoints = await staking.numDelegateStakingCheckpoints( - account, - lockedTS - ); - let delegateStakingCheckpoints = await staking.delegateStakingCheckpoints( - account, - lockedTS, - numUserStakingCheckpoints - 1 - ); - assert.equal(parseInt(numDelegateStakingCheckpoints), 1); - if (i === start) { - assert.equal(delegateStakingCheckpoints.stake.toString(), stakeForFirstInterval); - } else { - assert.equal(delegateStakingCheckpoints.stake.toString(), stakedPerInterval); - } - } - } -}); diff --git a/tests/vesting/VestingRegistryLogic.js b/tests/vesting/VestingRegistryLogic.js index 2a08783b5..4aeeefb27 100644 --- a/tests/vesting/VestingRegistryLogic.js +++ b/tests/vesting/VestingRegistryLogic.js @@ -9,13 +9,10 @@ const SOV_ABI = artifacts.require("SOV"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistry = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const UpgradableProxy = artifacts.require("UpgradableProxy"); const LockedSOV = artifacts.require("LockedSOV"); -const VestingRegistry = artifacts.require("VestingRegistry"); -const VestingRegistry2 = artifacts.require("VestingRegistry2"); -const VestingRegistry3 = artifacts.require("VestingRegistry3"); const TestToken = artifacts.require("TestToken"); const FOUR_WEEKS = new BN(4 * 7 * 24 * 60 * 60); @@ -25,12 +22,11 @@ const TOTAL_SUPPLY = "100000000000000000000000000"; const ZERO_ADDRESS = constants.ZERO_ADDRESS; const pricsSats = "2500"; -contract("VestingRegistryLogic", (accounts) => { +contract("VestingRegistry", (accounts) => { let root, account1, account2, account3, account4; let SOV, lockedSOV; let staking, feeSharingCollectorProxy; - let vesting, vestingFactory, vestingLogic, vestingRegistryLogic; - let vestingRegistry, vestingRegistry2, vestingRegistry3; + let vesting, vestingFactory, vestingLogic, vestingRegistry; let cliff = 1; // This is in 4 weeks. i.e. 1 * 4 weeks. let duration = 11; // This is in 4 weeks. i.e. 11 * 4 weeks. @@ -57,10 +53,10 @@ contract("VestingRegistryLogic", (accounts) => { vestingLogic = await VestingLogic.new(); vestingFactory = await VestingFactory.new(vestingLogic.address); - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistry = await VestingRegistry.new(); vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + await vesting.setImplementation(vestingRegistry.address); + vesting = await VestingRegistry.at(vesting.address); vestingFactory.transferOwnership(vesting.address); lockedSOV = await LockedSOV.new(SOV.address, vesting.address, cliff, duration, [root]); @@ -75,24 +71,6 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1 ); - - vestingRegistry2 = await VestingRegistry2.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ); - - vestingRegistry3 = await VestingRegistry3.new( - vestingFactory.address, - SOV.address, - staking.address, - feeSharingCollectorProxy.address, - account1 - ); }); describe("initialize", () => { @@ -105,7 +83,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ), "vestingFactory address invalid" ); @@ -120,7 +98,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ), "SOV address invalid" ); @@ -135,7 +113,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ), "staking address invalid" ); @@ -150,7 +128,7 @@ contract("VestingRegistryLogic", (accounts) => { ZERO_ADDRESS, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ), "feeSharingCollector address invalid" ); @@ -165,7 +143,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, ZERO_ADDRESS, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ), "vestingOwner address invalid" ); @@ -180,7 +158,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, ZERO_ADDRESS, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ), "LockedSOV address invalid" ); @@ -195,37 +173,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [ZERO_ADDRESS, vestingRegistry2.address, vestingRegistry3.address] - ), - "Vesting registry address invalid" - ); - }); - - it("fails if the 0 address is passed as VestingRegistry2 address", async () => { - await expectRevert( - vesting.initialize( - vestingFactory.address, - SOV.address, - staking.address, - feeSharingCollectorProxy.address, - account1, - lockedSOV.address, - [vestingRegistry.address, ZERO_ADDRESS, vestingRegistry3.address] - ), - "Vesting registry address invalid" - ); - }); - - it("fails if the 0 address is passed as VestingRegistry3 address", async () => { - await expectRevert( - vesting.initialize( - vestingFactory.address, - SOV.address, - staking.address, - feeSharingCollectorProxy.address, - account1, - lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, ZERO_ADDRESS] + [ZERO_ADDRESS, vestingRegistry.address, vestingRegistry.address] ), "Vesting registry address invalid" ); @@ -239,7 +187,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let _sov = await vesting.SOV(); @@ -261,7 +209,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); await expectRevert( vesting.initialize( @@ -271,7 +219,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ), "contract is already initialized" ); @@ -301,8 +249,84 @@ contract("VestingRegistryLogic", (accounts) => { }); }); + describe("addAdminManager", () => { + it("add adminManager", async () => { + const owner = root; + const newAdminManager = accounts[5]; + expect(await vesting.getAdminManager()).to.equal(ZERO_ADDRESS); + let tx = await vesting.setAdminManager(newAdminManager, { from: owner }); + + expectEvent(tx, "AdminManagerChanged", { + sender: owner, + oldAdminManager: ZERO_ADDRESS, + newAdminManager: newAdminManager, + }); + + expect(await vesting.getAdminManager()).to.equal(newAdminManager); + }); + + it("add adminManager should revert if try to set 0 address", async () => { + const owner = root; + expect(await vesting.getAdminManager()).to.equal(ZERO_ADDRESS); + await expectRevert( + vesting.setAdminManager(ZERO_ADDRESS, { from: owner }), + "Use removeAdminManager function to set adminManager to 0" + ); + expect(await vesting.getAdminManager()).to.equal(ZERO_ADDRESS); + }); + + it("fails sender isn't an owner", async () => { + await expectRevert( + vesting.setAdminManager(account1, { from: account1 }), + "unauthorized" + ); + }); + }); + + describe("removeAdminManager", () => { + it("remove adminManager", async () => { + const owner = root; + const newAdminManager = accounts[5]; + expect(await vesting.getAdminManager()).to.equal(ZERO_ADDRESS); + let tx = await vesting.setAdminManager(newAdminManager, { from: owner }); + expectEvent(tx, "AdminManagerChanged", { + sender: owner, + oldAdminManager: ZERO_ADDRESS, + newAdminManager: newAdminManager, + }); + + /** remove admin manager */ + tx = await vesting.removeAdminManager({ from: owner }); + + expectEvent(tx, "AdminManagerRemoved", { + sender: owner, + removedAdminManager: newAdminManager, + }); + + expect(await vesting.getAdminManager()).to.equal(ZERO_ADDRESS); + }); + + it("remove adminManager should revert if admin manager is not set yet", async () => { + const owner = root; + expect(await vesting.getAdminManager()).to.equal(ZERO_ADDRESS); + + /** remove admin manager */ + await expectRevert( + vesting.removeAdminManager({ from: owner }), + "Admin manager is not set" + ); + + expect(await vesting.getAdminManager()).to.equal(ZERO_ADDRESS); + }); + + it("fails sender isn't an owner", async () => { + const newAdminManager = accounts[5]; + await expectRevert(vesting.removeAdminManager({ from: account1 }), "unauthorized"); + }); + }); + describe("addAdmin", () => { - it("adds admin", async () => { + it("adds admin by owner", async () => { let tx = await vesting.addAdmin(account1); expectEvent(tx, "AdminAdded", { @@ -313,13 +337,36 @@ contract("VestingRegistryLogic", (accounts) => { expect(isAdmin).equal(true); }); - it("fails sender isn't an owner", async () => { + it("adds admin by adminManager", async () => { + const owner = root; + const newAdminManager = accounts[5]; + expect(await vesting.getAdminManager()).to.equal(ZERO_ADDRESS); + let tx = await vesting.setAdminManager(newAdminManager, { from: owner }); + + expectEvent(tx, "AdminManagerChanged", { + sender: owner, + oldAdminManager: ZERO_ADDRESS, + newAdminManager: newAdminManager, + }); + + expect(await vesting.getAdminManager()).to.equal(newAdminManager); + tx = await vesting.addAdmin(account1, { from: newAdminManager }); + + expectEvent(tx, "AdminAdded", { + admin: account1, + }); + + let isAdmin = await vesting.admins(account1); + expect(isAdmin).equal(true); + }); + + it("fails sender is non-authorized address (owner or adminManager)", async () => { await expectRevert(vesting.addAdmin(account1, { from: account1 }), "unauthorized"); }); }); describe("removeAdmin", () => { - it("removes admin", async () => { + it("removes admin by owner", async () => { await vesting.addAdmin(account1); let tx = await vesting.removeAdmin(account1); @@ -331,7 +378,32 @@ contract("VestingRegistryLogic", (accounts) => { expect(isAdmin).equal(false); }); - it("fails sender isn't an owner", async () => { + it("removes admin by adminManager", async () => { + const owner = root; + const newAdminManager = accounts[5]; + expect(await vesting.getAdminManager()).to.equal(ZERO_ADDRESS); + let tx = await vesting.setAdminManager(newAdminManager, { from: owner }); + + expectEvent(tx, "AdminManagerChanged", { + sender: owner, + oldAdminManager: ZERO_ADDRESS, + newAdminManager: newAdminManager, + }); + + expect(await vesting.getAdminManager()).to.equal(newAdminManager); + + await vesting.addAdmin(account1, { from: owner }); + tx = await vesting.removeAdmin(account1, { from: owner }); + + expectEvent(tx, "AdminRemoved", { + admin: account1, + }); + + let isAdmin = await vesting.admins(account1); + expect(isAdmin).equal(false); + }); + + it("fails sender is non-authorized address (owner or adminManager)", async () => { await expectRevert(vesting.removeAdmin(account1, { from: account1 }), "unauthorized"); }); }); @@ -345,7 +417,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000); @@ -389,7 +461,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); @@ -453,7 +525,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); @@ -497,7 +569,7 @@ contract("VestingRegistryLogic", (accounts) => { await expectRevert(proxy.setImplementation(account2), "revert"); }); - it("fails if vestingRegistryLogic doesn't have enough SOV", async () => { + it("fails if vestingRegistry doesn't have enough SOV", async () => { await vesting.initialize( vestingFactory.address, SOV.address, @@ -505,7 +577,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); @@ -535,7 +607,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); @@ -555,6 +627,39 @@ contract("VestingRegistryLogic", (accounts) => { from: account1, }); }); + + it("fails if sender is not an owner or admin (added the admin by adminManager", async () => { + await vesting.initialize( + vestingFactory.address, + SOV.address, + staking.address, + feeSharingCollectorProxy.address, + account1, + lockedSOV.address, + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] + ); + + let amount = new BN(1000000); + let cliff = TEAM_VESTING_CLIFF; + let duration = TEAM_VESTING_DURATION; + let vestingType = new BN(3); //Team Salary + + await expectRevert( + vesting.createVestingAddr(account2, amount, cliff, duration, vestingType, { + from: account1, + }), + "unauthorized" + ); + + const owner = root; + const newAdminManager = accounts[5]; + expect(await vesting.getAdminManager()).to.equal(ZERO_ADDRESS); + await vesting.setAdminManager(newAdminManager, { from: owner }); + await vesting.addAdmin(account1, { from: newAdminManager }); + await vesting.createVestingAddr(account2, amount, cliff, duration, vestingType, { + from: account1, + }); + }); }); describe("createVesting and getVesting - LockedSOV", () => { @@ -566,7 +671,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); @@ -600,7 +705,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); @@ -646,7 +751,7 @@ contract("VestingRegistryLogic", (accounts) => { vesting: vestingAddress, amount: amount, }); - let balance = await SOV.balanceOf(vestingRegistryLogic.address); + let balance = await SOV.balanceOf(vestingRegistry.address); expect(balance.toString()).equal("0"); let vestingAddr = await VestingLogic.at(vestingAddress); @@ -656,7 +761,7 @@ contract("VestingRegistryLogic", (accounts) => { await expectRevert(proxy.setImplementation(account2), "revert"); }); - it("fails if vestingRegistryLogic doesn't have enough SOV", async () => { + it("fails if vestingRegistry doesn't have enough SOV", async () => { await vesting.initialize( vestingFactory.address, SOV.address, @@ -664,7 +769,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); @@ -694,7 +799,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); @@ -736,7 +841,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); @@ -772,7 +877,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); @@ -812,7 +917,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); @@ -837,7 +942,7 @@ contract("VestingRegistryLogic", (accounts) => { feeSharingCollectorProxy.address, account1, lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] + [vestingRegistry.address, vestingRegistry.address, vestingRegistry.address] ); let amount = new BN(1000000); diff --git a/tests/vesting/VestingRegistryMigrations.js b/tests/vesting/VestingRegistryMigrations.js index 92aacfcc0..8f1a99ee2 100644 --- a/tests/vesting/VestingRegistryMigrations.js +++ b/tests/vesting/VestingRegistryMigrations.js @@ -9,12 +9,9 @@ const SOV_ABI = artifacts.require("SOV"); const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorMockup"); const VestingLogic = artifacts.require("VestingLogic"); const VestingFactory = artifacts.require("VestingFactory"); -const VestingRegistryLogic = artifacts.require("VestingRegistryLogic"); +const VestingRegistry = artifacts.require("VestingRegistry"); const VestingRegistryProxy = artifacts.require("VestingRegistryProxy"); const LockedSOV = artifacts.require("LockedSOV"); -const VestingRegistry = artifacts.require("VestingRegistry"); -const VestingRegistry2 = artifacts.require("VestingRegistry2"); -const VestingRegistry3 = artifacts.require("VestingRegistry3"); const TestToken = artifacts.require("TestToken"); const FourYearVesting = artifacts.require("FourYearVesting"); const FourYearVestingLogic = artifacts.require("FourYearVestingLogic"); @@ -31,8 +28,7 @@ contract("VestingRegistryMigrations", (accounts) => { let root, account1, account2, account3, account4; let SOV, lockedSOV; let staking, feeSharingCollectorProxy; - let vesting, vestingFactory, vestingLogic, vestingRegistryLogic; - let vestingRegistry, vestingRegistry2, vestingRegistry3; + let vesting, vestingFactory, vestingLogic, vestingRegistry; let vestingAddress, vestingAddress2, vestingAddress3; let vestingTeamAddress, vestingTeamAddress2, vestingTeamAddress3; let newVestingAddress, newVestingAddress2, newVestingAddress3; @@ -64,10 +60,10 @@ contract("VestingRegistryMigrations", (accounts) => { vestingLogic = await VestingLogic.new(); vestingFactory = await VestingFactory.new(vestingLogic.address); - vestingRegistryLogic = await VestingRegistryLogic.new(); + vestingRegistry = await VestingRegistry.new(); vesting = await VestingRegistryProxy.new(); - await vesting.setImplementation(vestingRegistryLogic.address); - vesting = await VestingRegistryLogic.at(vesting.address); + await vesting.setImplementation(vestingRegistry.address); + vesting = await VestingRegistry.at(vesting.address); await staking.setVestingRegistry(vesting.address); lockedSOV = await LockedSOV.new(SOV.address, vesting.address, cliff, duration, [root]); @@ -87,183 +83,6 @@ contract("VestingRegistryMigrations", (accounts) => { }); describe("addDeployedVestings", () => { - it("adds deployed vestings from VestingRegistry ", async () => { - vestingRegistry = await VestingRegistry.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ); - - let amt = new BN(2000000); - let amount = new BN(200000); - await SOV.transfer(vestingRegistry.address, amt); - - let cliff = FOUR_WEEKS; - let duration = FOUR_WEEKS.mul(new BN(20)); - let teamCliff = TEAM_VESTING_CLIFF; - let teamDuration = TEAM_VESTING_DURATION; - - await vestingFactory.transferOwnership(vestingRegistry.address); - await vestingRegistry.createVesting(account1, amount, cliff, duration); - vestingAddress = await vestingRegistry.getVesting(account1); - await vestingRegistry.stakeTokens(vestingAddress, amount); - await vestingRegistry.createTeamVesting(account1, amount, teamCliff, teamDuration); - vestingTeamAddress = await vestingRegistry.getTeamVesting(account1); - await vestingRegistry.stakeTokens(vestingTeamAddress, amount); - assert.notEqual(vestingAddress, ZERO_ADDRESS, "Vesting Address should not be zero."); - assert.notEqual( - vestingTeamAddress, - ZERO_ADDRESS, - "Vesting Team Address should not be zero." - ); - }); - - it("adds deployed vestings from VestingRegistry2 ", async () => { - vestingRegistry2 = await VestingRegistry2.new( - vestingFactory.address, - SOV.address, - [cSOV1.address, cSOV2.address], - pricsSats, - staking.address, - feeSharingCollectorProxy.address, - account1 - ); - - let amt = new BN(2000000); - let amount = new BN(200000); - await SOV.transfer(vestingRegistry2.address, amt); - - let cliff = FOUR_WEEKS; - let duration = FOUR_WEEKS.mul(new BN(20)); - let teamCliff = TEAM_VESTING_CLIFF; - let teamDuration = TEAM_VESTING_DURATION; - - await vestingFactory.transferOwnership(vestingRegistry2.address); - await vestingRegistry2.createVesting(account2, amount, cliff, duration); - vestingAddress2 = await vestingRegistry2.getVesting(account2); - let tx = await vestingRegistry2.stakeTokens(vestingAddress2, amount); - expectEvent(tx, "TokensStaked", { - vesting: vestingAddress2, - amount: amount, - }); - await staking.balanceOf(vestingAddress2); - await vestingRegistry2.createTeamVesting(account2, amount, teamCliff, teamDuration); - vestingTeamAddress2 = await vestingRegistry2.getTeamVesting(account2); - let tx2 = await vestingRegistry2.stakeTokens(vestingTeamAddress2, amount); - expectEvent(tx2, "TokensStaked", { - vesting: vestingTeamAddress2, - amount: amount, - }); - assert.notEqual(vestingAddress2, ZERO_ADDRESS, "Vesting Address should not be zero."); - assert.notEqual( - vestingTeamAddress2, - ZERO_ADDRESS, - "Vesting Team Address should not be zero." - ); - }); - - it("adds deployed vestings from VestingRegistry3 ", async () => { - vestingRegistry3 = await VestingRegistry3.new( - vestingFactory.address, - SOV.address, - staking.address, - feeSharingCollectorProxy.address, - account1 - ); - - let amt = new BN(2000000); - let amount = new BN(200000); - await SOV.transfer(vestingRegistry3.address, amt); - - let cliff = FOUR_WEEKS; - let duration = FOUR_WEEKS.mul(new BN(20)); - let teamCliff = TEAM_VESTING_CLIFF; - let teamDuration = TEAM_VESTING_DURATION; - - await vestingFactory.transferOwnership(vestingRegistry3.address); - await vestingRegistry3.createVesting(account3, amount, cliff, duration); - vestingAddress3 = await vestingRegistry3.getVesting(account3); - await vestingRegistry3.stakeTokens(vestingAddress3, amount); - await vestingRegistry3.createTeamVesting(account3, amount, teamCliff, teamDuration); - vestingTeamAddress3 = await vestingRegistry3.getTeamVesting(account3); - await vestingRegistry3.stakeTokens(vestingTeamAddress3, amount); - assert.notEqual(vestingAddress3, ZERO_ADDRESS, "Vesting Address should not be zero."); - assert.notEqual( - vestingTeamAddress3, - ZERO_ADDRESS, - "Vesting Team Address should not be zero." - ); - }); - - it("adds deployed vestings to new Vesting Registry ", async () => { - await vesting.initialize( - vestingFactory.address, - SOV.address, - staking.address, - feeSharingCollectorProxy.address, - account1, - lockedSOV.address, - [vestingRegistry.address, vestingRegistry2.address, vestingRegistry3.address] - ); - - let cliff = FOUR_WEEKS; - let duration = FOUR_WEEKS.mul(new BN(20)); - let teamCliff = TEAM_VESTING_CLIFF; - let teamDuration = TEAM_VESTING_DURATION; - - vestingFactory.transferOwnership(vesting.address); - let tx = await vesting.addDeployedVestings([account1, account2, account3], [1, 2, 3]); - console.log("gasUsed = " + tx.receipt.gasUsed); - - newVestingAddress = await vesting.getVestingAddr(account1, cliff, duration, 1); - expect(await vesting.isVestingAddress(newVestingAddress)).equal(true); - newVestingAddress2 = await vesting.getVestingAddr(account2, cliff, duration, 2); - expect(await vesting.isVestingAddress(newVestingAddress2)).equal(true); - newVestingAddress3 = await vesting.getVestingAddr(account3, cliff, duration, 3); - expect(await vesting.isVestingAddress(newVestingAddress3)).equal(true); - newTeamVestingAddress = await vesting.getTeamVesting( - account1, - teamCliff, - teamDuration, - 1 - ); - expect(await vesting.isVestingAddress(newTeamVestingAddress)).equal(true); - newTeamVestingAddress2 = await vesting.getTeamVesting( - account2, - teamCliff, - teamDuration, - 2 - ); - expect(await vesting.isVestingAddress(newTeamVestingAddress2)).equal(true); - newTeamVestingAddress3 = await vesting.getTeamVesting( - account3, - teamCliff, - teamDuration, - 3 - ); - expect(await vesting.isVestingAddress(newTeamVestingAddress3)).equal(true); - - expect(vestingAddress).equal(newVestingAddress); - expect(vestingAddress2).equal(newVestingAddress2); - expect(vestingAddress3).equal(newVestingAddress3); - expect(vestingTeamAddress).equal(newTeamVestingAddress); - expect(vestingTeamAddress2).equal(newTeamVestingAddress2); - expect(vestingTeamAddress3).equal(newTeamVestingAddress3); - - let vestingAddresses = await vesting.getVestingsOf(account2); - assert.equal(vestingAddresses.length.toString(), "2"); - assert.equal(vestingAddresses[0].vestingType, 1); - assert.equal(vestingAddresses[0].vestingCreationType, 2); - assert.equal(vestingAddresses[0].vestingAddress, newVestingAddress2); - assert.equal(vestingAddresses[1].vestingType, 0); - assert.equal(vestingAddresses[1].vestingCreationType, 2); - assert.equal(vestingAddresses[1].vestingAddress, newTeamVestingAddress2); - }); - it("fails if the 0 address is passed", async () => { await expectRevert( vesting.addDeployedVestings([ZERO_ADDRESS], [1]),