Skip to content

Latest commit

 

History

History
1101 lines (871 loc) · 31.1 KB

VestingRegistry.md

File metadata and controls

1101 lines (871 loc) · 31.1 KB

Vesting Registry contract.

  • (VestingRegistry.sol)

View Source: contracts/governance/Vesting/VestingRegistry.sol

↗ Extends: Ownable

VestingRegistry contract

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.

Enums

VestingType

enum VestingType {
 TeamVesting,
 Vesting
}

Contract Members

Constants & Variables

uint256 public constant FOUR_WEEKS;
uint256 public constant CSOV_VESTING_CLIFF;
uint256 public constant CSOV_VESTING_DURATION;
contract IVestingFactory public vestingFactory;
address public SOV;
address[] public CSOVtokens;
uint256 public priceSats;
address public staking;
address public feeSharingCollector;
address public vestingOwner;
mapping(address => mapping(uint256 => address)) public vestingContracts;
mapping(address => bool) public processedList;
mapping(address => bool) public blacklist;
mapping(address => uint256) public lockedAmount;
mapping(address => bool) public admins;

Events

event CSOVReImburse(address  from, uint256  CSOVamount, uint256  reImburseAmount);
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);

Modifiers

onlyAuthorized

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() internal

isNotProcessed

modifier isNotProcessed() internal

isNotBlacklisted

modifier isNotBlacklisted() internal

Functions


constructor

Contract deployment settings.

function (address _vestingFactory, address _SOV, address[] _CSOVtokens, uint256 _priceSats, address _staking, address _feeSharingCollector, address _vestingOwner) public nonpayable

Arguments

Name Type Description
_vestingFactory address The address of vesting factory contract.
_SOV address The SOV token address.
_CSOVtokens address[] The array of cSOV tokens.
_priceSats uint256 The price of cSOV tokens in satoshis.
_staking address The address of staking contract.
_feeSharingCollector address The address of fee sharing collector proxy contract.
_vestingOwner address The address of an owner of vesting contract.
Source Code
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;
    }

addAdmin

Add account to ACL.

function addAdmin(address _admin) public nonpayable onlyOwner 

Arguments

Name Type Description
_admin address The addresses of the account to grant permissions.
Source Code
function addAdmin(address _admin) public onlyOwner {
        admins[_admin] = true;
        emit AdminAdded(_admin);
    }

removeAdmin

Remove account from ACL.

function removeAdmin(address _admin) public nonpayable onlyOwner 

Arguments

Name Type Description
_admin address The addresses of the account to revoke permissions.
Source Code
function removeAdmin(address _admin) public onlyOwner {
        admins[_admin] = false;
        emit AdminRemoved(_admin);
    }

reImburse

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 nonpayable isNotProcessed isNotBlacklisted 
Source Code
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);
        }

        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);
    }

budget

Get contract balance.

function budget() external view
returns(uint256)
Source Code
function budget() external view returns (uint256) {
        uint256 SCBudget = address(this).balance;
        return SCBudget;
    }

deposit

Deposit function to receiving value (rBTC).

function deposit() public payable
Source Code
function deposit() public payable {}

withdrawAll

Send all contract balance to an account.

function withdrawAll(address payable to) public nonpayable onlyOwner 

Arguments

Name Type Description
to address payable The account address to send the balance to.
Source Code
function withdrawAll(address payable to) public onlyOwner {
        to.transfer(address(this).balance);
    }

setVestingFactory

Sets vesting factory address. High level endpoint.

function setVestingFactory(address _vestingFactory) public nonpayable onlyOwner 

Arguments

Name Type Description
_vestingFactory address The address of vesting factory contract. *
Source Code
function setVestingFactory(address _vestingFactory) public onlyOwner {
        _setVestingFactory(_vestingFactory);
    }

_setVestingFactory

Sets vesting factory address. Low level core function.

function _setVestingFactory(address _vestingFactory) internal nonpayable

Arguments

Name Type Description
_vestingFactory address The address of vesting factory contract.
Source Code
function _setVestingFactory(address _vestingFactory) internal {
        require(_vestingFactory != address(0), "vestingFactory address invalid");
        vestingFactory = IVestingFactory(_vestingFactory);
    }

setCSOVtokens

Sets cSOV tokens array. High level endpoint.

function setCSOVtokens(address[] _CSOVtokens) public nonpayable onlyOwner 

Arguments

Name Type Description
_CSOVtokens address[] The array of cSOV tokens.
Source Code
function setCSOVtokens(address[] memory _CSOVtokens) public onlyOwner {
        _setCSOVtokens(_CSOVtokens);
    }

_setCSOVtokens

Sets cSOV tokens array by looping through input. Low level function.

function _setCSOVtokens(address[] _CSOVtokens) internal nonpayable

Arguments

Name Type Description
_CSOVtokens address[] The array of cSOV tokens.
Source Code
function _setCSOVtokens(address[] memory _CSOVtokens) internal {
        for (uint256 i = 0; i < _CSOVtokens.length; i++) {
            require(_CSOVtokens[i] != address(0), "CSOV address invalid");
        }
        CSOVtokens = _CSOVtokens;
    }

setBlacklistFlag

Set blacklist flag (true/false).

function setBlacklistFlag(address _account, bool _blacklisted) public nonpayable onlyOwner 

Arguments

Name Type Description
_account address The address to be blacklisted.
_blacklisted bool The flag to add/remove to/from a blacklist.
Source Code
function setBlacklistFlag(address _account, bool _blacklisted) public onlyOwner {
        require(_account != address(0), "account address invalid");

        blacklist[_account] = _blacklisted;
    }

setLockedAmount

Set amount to be subtracted from user token balance.

function setLockedAmount(address _account, uint256 _amount) public nonpayable onlyOwner 

Arguments

Name Type Description
_account address The address with locked amount.
_amount uint256 The amount to be locked.
Source Code
function setLockedAmount(address _account, uint256 _amount) public onlyOwner {
        require(_account != address(0), "account address invalid");
        require(_amount != 0, "amount invalid");

        lockedAmount[_account] = _amount;
    }

transferSOV

Transfer SOV tokens to given address. *

function transferSOV(address _receiver, uint256 _amount) public nonpayable onlyOwner 

Arguments

Name Type Description
_receiver address The address of the SOV receiver.
_amount uint256 The amount to be transferred.
Source Code
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);
    }

exchangeAllCSOV

Exchange cSOV to SOV with 1:1 rate

function exchangeAllCSOV() public nonpayable isNotProcessed isNotBlacklisted 
Source Code
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;
        }

        require(amount > lockedAmount[msg.sender], "amount invalid");
        amount -= lockedAmount[msg.sender];

        _createVestingForCSOV(amount);
    }

_createVestingForCSOV

cSOV tokens are moved and staked on Vesting contract.

function _createVestingForCSOV(uint256 _amount) internal nonpayable

Arguments

Name Type Description
_amount uint256 The amount of tokens to be vested.
Source Code
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);
    }

_validateCSOV

Check a token address is among the cSOV token addresses.

function _validateCSOV(address _CSOV) internal view

Arguments

Name Type Description
_CSOV address The cSOV token address.
Source Code
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");
    }

createVesting

Create Vesting contract.

function createVesting(address _tokenOwner, uint256 _amount, uint256 _cliff, uint256 _duration) public nonpayable onlyAuthorized 

Arguments

Name Type Description
_tokenOwner address The owner of the tokens.
_amount uint256 The amount to be staked.
_cliff uint256 The time interval to the first withdraw in seconds.
_duration uint256 The total duration in seconds.
Source Code
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);
    }

createTeamVesting

Create Team Vesting contract.

function createTeamVesting(address _tokenOwner, uint256 _amount, uint256 _cliff, uint256 _duration) public nonpayable onlyAuthorized 

Arguments

Name Type Description
_tokenOwner address The owner of the tokens.
_amount uint256 The amount to be staked.
_cliff uint256 The time interval to the first withdraw in seconds.
_duration uint256 The total duration in seconds.
Source Code
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);
    }

stakeTokens

Stake tokens according to the vesting schedule.

function stakeTokens(address _vesting, uint256 _amount) public nonpayable onlyAuthorized 

Arguments

Name Type Description
_vesting address The address of Vesting contract.
_amount uint256 The amount of tokens to stake.
Source Code
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);
    }

getVesting

Query the vesting contract for an account.

function getVesting(address _tokenOwner) public view
returns(address)

Arguments

Name Type Description
_tokenOwner address The owner of the tokens.

Returns

The vesting contract address for the given token owner.

Source Code
function getVesting(address _tokenOwner) public view returns (address) {
        return vestingContracts[_tokenOwner][uint256(VestingType.Vesting)];
    }

getTeamVesting

Query the team vesting contract for an account.

function getTeamVesting(address _tokenOwner) public view
returns(address)

Arguments

Name Type Description
_tokenOwner address The owner of the tokens.

Returns

The team vesting contract address for the given token owner.

Source Code
function getTeamVesting(address _tokenOwner) public view returns (address) {
        return vestingContracts[_tokenOwner][uint256(VestingType.TeamVesting)];
    }

_getOrCreateVesting

If not exists, deploy a vesting contract through factory.

function _getOrCreateVesting(address _tokenOwner, uint256 _cliff, uint256 _duration) internal nonpayable
returns(address)

Arguments

Name Type Description
_tokenOwner address The owner of the tokens.
_cliff uint256 The time interval to the first withdraw in seconds.
_duration uint256 The total duration in seconds.

Returns

The vesting contract address for the given token owner whether it existed previously or not.

Source Code
function _getOrCreateVesting(
        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
                );
            vestingContracts[_tokenOwner][type_] = vesting;
        }
        return vestingContracts[_tokenOwner][type_];
    }

_getOrCreateTeamVesting

If not exists, deploy a team vesting contract through factory.

function _getOrCreateTeamVesting(address _tokenOwner, uint256 _cliff, uint256 _duration) internal nonpayable
returns(address)

Arguments

Name Type Description
_tokenOwner address The owner of the tokens.
_cliff uint256 The time interval to the first withdraw in seconds.
_duration uint256 The total duration in seconds.

Returns

The team vesting contract address for the given token owner whether it existed previously or not.

Source Code
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_];
    }

Contracts