Limits

STV2 embeds two types of limits

  • Secondary Trading Limit (limiting the amount of tokens that the address can transfer)

  • Transaction Count Limit (limiting the number of transactions that the address can send)

Using these limits is optional. They can be turned on and turned off independently of each other (Secondary Trading Limit and Transaction Count Limit are not related in any way).

The right to toggle both kinds of limits belongs to SuperAdmin Role.

    /// @notice flag true - Secondary limit turned on, otherwise - turn off
    bool public _isEnabledSecondaryTradingLimit;

    /// @notice flag true - Transaction count limit turned on, otherwise - turn off
    bool public _isEnabledTransactionCountLimit;

The smart contract has parameters for the limits, which are used for all addresses by default.

    /// @notice secondary trading limit which is used for every address without individual limit
    /// (when flag `_isEnabledSecondaryTradingLimit` is true)
    uint256 internal _defaultSecondaryTradingLimit;

    /// @notice transaction count limit which is used for every address without individual limit
    /// (when flag `_isEnabledTransactionCountLimit` is true)
    uint256 internal _defaultTransactionCountLimit;

Also, addresses can have individual Secondary or/and Transaction limits. This information is kept in the structure with personal info about the address:

/// @notice Struct that contains all information about address of user.
    struct PersonalInfo {
        //user's wallet, appointed at the moment when user is whitelisted.
        //default value - address(0)
        address userAddress;
        //true - if address is whitelisted, otherwise - false.
        bool whitelisted;
        **//true - if address has individual Secondary limit, otherwise - false.
        bool hasOwnSecondaryLimit;
        //true - if address has individual Transaction Count limit, otherwise - false.
        bool hasOwnTransactionCountLimit;
        //value of individual Secondary limit(if exists), default - 0
        uint256 individualSecondaryTradingLimit;
        //value of individual Transaction Count limit(if exists), default - 0
        uint256 individualTransactionCountLimit;**
        //the total amount of all tokens sent by the uder(address)
        uint256 outputAmount;
        //the total number of all transfers done by user(address)
        uint256 transactionCount;
        //dynamic array of arrays of 2 elements([0]:timestamp, [1]:blocked amount)
        uint256[2][] personalLockUps;
    }

In other words, the default limits are applied for all addresses except those, that have set individual limits. All values of limits (default ones and individual for a certain address) can be changed! This functionality is available for the Compliance Manager Role.


How technically these limits are tracked?

First, we need to know about some more parameters kept in the structure with personal information about certain addresses:

outputAmount - keeps the whole amount of tokens, which address has ever sent;

transactionCount - keeps the quantity of all transfers of security tokens ever made from this address.

/// @notice Struct that contains all information about address of user.
    struct PersonalInfo {
        //user's wallet, appointed at the moment when user is whitelisted.
        //default value - address(0)
        address userAddress;
        //true - if address is whitelisted, otherwise - false.
        bool whitelisted;
        //true - if address has individual Secondary limit, otherwise - false.
        bool hasOwnSecondaryLimit;
        //true - if address has individual Transaction Count limit, otherwise - false.
        bool hasOwnTransactionCountLimit;
        //value of individual Secondary limit(if exists), default - 0
        uint256 individualSecondaryTradingLimit;
        //value of individual Transaction Count limit(if exists), default - 0
        uint256 individualTransactionCountLimit;
        **//the total amount of all tokens ever sent by the user(address)
        uint256 outputAmount;
        //the total number of all transfers ever made by user(address)
        uint256 transactionCount;**
        //dynamic array of arrays of 2 elements([0]:timestamp, [1]:blocked amount)
        uint256[2][] personalLockUps;
    }

In order to find out information about available limits, we have to compare the amount of already sent tokens(or quantity of sent transactions) with the value of the Secondary (Transaction) limit that is valid at this moment for the specified address. ****And limit has to be greater than amount of tokens or number of sent transactions from the PersonalInfo of address.

This logic allows us not to save all set limits in the smart contract, but only to have some markers to compare them with data of the certain address we need. This will be very useful when the quantity of users will be huge.


Transaction verification

  • If the Secondary Trading Limit is enabled (if not - the contract will miss its further checks and as the value of limit for _account will be used MAX_UINT), and determines what limit (and its value) is used for the _account : individualSecondaryTradingLimit or defaultSecondaryTradingLimit

    function secondaryTradingLimitOf(address _account)
        public
        view
        returns (uint256)
    {
        if (_isEnabledSecondaryTradingLimit) {
            if (userData[_account].hasOwnSecondaryLimit) {
                return userData[_account].individualSecondaryTradingLimit;
            } else {
                return _defaultSecondaryTradingLimit;
            }
        }
        return MAX_UINT;
    }
  • If the Transaction Count Limit is enabled (if not - the contract will miss its further checks and as the value of limit for _account will be used MAX_UINT), and determines what limit (and its value) is used for the _account : individualTransactionCountLimit or defaultTransactionCountLimit

    function transactionCountLimitOf(address _account)
        public
        view
        returns (uint256)
    {
        if (_isEnabledTransactionCountLimit) {
            if (userData[_account].hasOwnTransactionCountLimit) {
                return userData[_account].individualTransactionCountLimit;
            } else {
                return _defaultTransactionCountLimit;
            }
        }
        return MAX_UINT;
    }
  • If this address has available limits and whether it will not exceed them after the transaction:

function _beforeTokenTransfer(address _from, uint256 _amount) internal {
        require(
            getLeftTransactionCountLimit(_from) > 0,
            "STokenV1: Available limit of transactions exceeded."
        );

        require(
            _availableLimit(_from) >= _amount,
            "STokenV1: Amount you want to transfer exceeds your Secondary Trading limit."
        );
...

Last updated