LogoLogo
  • Welcome to Stobox
  • Stobox Company
    • Overview
      • Key Tokenization Trends
      • Tokenization Market Challenges in 2025
      • Stobox’s Mission and Business Objectives
      • Business Model Framework
      • Market Demand & Business Cases
      • Compliance & Security
      • Advantages
      • Values
      • Social Impact
    • Stobox Assets
      • Stobox Token (STBU)
      • Stobox Security Token (STBX)
    • Investor Relations
  • PRODUCTS
    • Stobox 4
      • Introduction
      • Stobox 4 Platform Roadmap 2025
      • Key Features
        • Wallet Management
          • Understanding MPC-CMP
          • Multi-Device Security
          • Multi-Blockchain Support
          • Full Private Key Takeover 🔥
          • Supported Assets
        • Blockchain dApps Connectivity
        • Compliance and Regulatory Framework
        • Asset Tokenization Module
        • Tokenization AI Framework
        • Roles and User Management System
        • Financial Operations, Integration, and Settlement Mechanics
        • Trust, Transparency, and Audits
      • Integrations
        • Blockchains
        • Protocols
        • Assets
    • Stobox V3
      • DS Dashboard V3
      • DS Swap
      • STV2 Stobox Protocol
        • Roles
        • Limits
        • Mint, Burn and Treasury Management
        • Lock-Ups
        • Contract Governance
    • STV3 Stobox Protocol
    • Stobox DID
    • Stobox Oracle
  • ENTERPRISE
    • Stobox API
    • Stobox 4 Whitelabel
  • TURN-KEY SERVICES
    • Stobox 3 Tokenization Suite
      • FAQ
  • CONCEPTS
    • Tokenization of Time
      • Introduction
      • Exploring the Benefits of Time Tokenization
      • Mechanism of Time Tokenization
      • Liquidity in Professional Services
      • Global Impact and Solutions to Systemic Issues
      • Time-Backed Securities and Investment Funds
    • The Power of Single Ledger Settlement
      • Chapter 1: Introduction to Single Ledger Settlement: Understanding the Basics
      • Chapter 2: Tokenization: The Digital Transformation of Assets
      • Chapter 3: How Single Ledger Settlement Works
      • Chapter 4: Revolutionizing the Auto Dealership Industry
      • Chapter 5: Supply Chain and Logistics – Enhancing Transparency and Efficiency
      • Chapter 6: Tokenization in Real Estate – Simplifying Transactions and Ownership
      • Chapter 7: Healthcare – Streamlining Patient Data and Payments
      • Chapter 8: Smart Contracts – Automating and Simplifying Business Processes
      • Chapter 9: Digital Payments – The New Era of Instant, Transparent Transactions
      • Chapter 10: Tokenization and Payroll – A New Frontier in Employee Compensation
      • Chapter 11: Reducing Costs with Single Ledger Settlement
      • Chapter 12: Legal Implications and Compliance
      • Chapter 13: Overcoming Challenges in Adopting Single Ledger Settlement
      • Chapter 14: Case Studies – Real-World Applications of Single Ledger Settlement
      • Chapter 15: The Future of Business Operations – A Unified Ledger for the Global Economy
  • DeFi
    • Staking Program
Powered by GitBook
On this page
  • The Process of Locking Tokens
  • Information About Locked Tokens of an Address
  • How Do Tokens Unlock?

Was this helpful?

  1. PRODUCTS
  2. Stobox V3
  3. STV2 Stobox Protocol

Lock-Ups

For smart contracts that represent securities, it is beneficial to provide an option for an "early-sale" or "pre-sale" of security tokens. This means that tokens can be purchased by investors at a lower price, but cannot be sold or transferred for a specified period of time. To facilitate this, lock-ups have been added to the smart contract.

  • Tokens can be locked for a predetermined period.

  • Investors can see the locked tokens in their balance but cannot transfer them until the lock-up period ends.

  • Lock-ups are an optional feature that can be enabled or disabled. The authority to toggle Lock-ups is assigned to the Super Admin role.

/// @notice Flag to indicate whether Lock-ups are enabled (true) or disabled (false)
    bool public _isEnabledLockUps;

When Lock-ups are disabled, the information regarding locked tokens remains intact, but the contract no longer checks this parameter during token transfers.

function getAmountOfLockedTokens(
        address _account
    ) public view returns (uint256) {
        uint256 result = 0;

        **if (!(_isEnabledLockUps)) {
            return result;**
        } else {...

Information about locked tokens is stored in the PersonalInfo struct for each address. This is a dynamic array consisting of pairs of data: 1 - the timestamp when tokens can be unlocked, and 2 - the amount of tokens locked until that timestamp.

/// @notice Struct that contains all information about a user's address.
    struct PersonalInfo {
        // User's wallet address, assigned when the user is whitelisted.
        // Default value - address(0)
        address userAddress;
        // True if the address is whitelisted, otherwise false.
        bool whitelisted;
        // True if the address has an individual secondary limit, otherwise false.
        bool hasOwnSecondaryLimit;
        // True if the address has an individual transaction count limit, otherwise false.
        bool hasOwnTransactionCountLimit;
        // Value of the individual secondary trading limit (if exists), default - 0
        uint256 individualSecondaryTradingLimit;
        // Value of the individual transaction count limit (if exists), default - 0
        uint256 individualTransactionCountLimit;
        // The total amount of all tokens ever sent by the user.
        uint256 outputAmount;
        // The total number of all transfers ever made by the user.
        uint256 transactionCount;
        **// Dynamic array containing pairs of [0]: timestamp, [1]: blocked amount**
        uint256[2][] personalLockUps;
    }

Tokens can only be locked by the Financial Manager role and will only be unlocked after the period specified during the locking process. There is no option to unlock them earlier.


The Process of Locking Tokens

Technically, locking tokens involves writing a pair (timestamp, amount) to the PersonalInfo of the account. The smart contract checks this array whenever the address attempts to transfer tokens, and if tokens are locked, they cannot be moved.

  • Tokens can be locked at the time of their distribution. When the Financial Manager sends tokens to the investor’s address from the Corporate Treasury using the transferFromTreasuryLockedTokens() function, the specified amount of tokens will be sent and locked simultaneously. (This function cannot be called if Lock-ups are disabled)

  • Tokens can also be locked when they are already in the user’s balance. In this case, the Financial Manager uses the lockUpTokensOnAddress() function. (This function cannot be called if Lock-ups are disabled)

    This function has a special feature! It checks the available balance of the address (total token balance minus already locked tokens), compares it with the amount to be locked, and if the available balance is less than the amount to lock, only the available tokens will be locked.

/// @dev Checks the amount to lock on `_account`:
    ///      compares the available balance of `_account` and `_amountToLock`
    ///      and returns the lesser value.
    ///      This function does not allow locking tokens that `_account` does not have
    ///      in its balance, preventing a "negative balance" for `_account`.
    function _checkAmountToLock(address _account, uint256 _amountToLock)
        internal
        view
        returns (uint256 resultedAmount)
    {
        _availableBalance(_account) > _amountToLock
            ? resultedAmount = _amountToLock
            : resultedAmount = _availableBalance(_account);
    }

Information About Locked Tokens of an Address

Using the standard balanceOf() function will not show if there are locked tokens in an account. It will only display the total token balance.

To find out information about locked tokens, you can use:

  • getListOfLockUps(address _account) to see the complete list of (timestamp, amount) pairs.

  • getAmountOfLockedTokens(address _account) to see the total amount of locked tokens (this information is also included in getUserData(address _account)).


How Do Tokens Unlock?

Any function that includes the internal _transfer() function (such as transfer, transferFrom, transferFromTreasuryLockedTokens, replacementOfCorporateTreasury) uses the _beforeTokenTransfer(_from, _amount) hook to check the amount of locked tokens associated with an address. If locked tokens exist, the transfer will be prevented.

function _beforeTokenTransfer(address _from, uint256 _amount) internal {
...        
        require(
            _availableBalance(_from) >= _amount,
            "STokenV1: transfer amount exceeds balance or you are trying to transfer locked tokens."
        );
...
    }

After the transfer, the _afterTokenTransfer(_from) hook updates the array of locked tokens.

/// @dev This hook is called after any token transfer (except minting & burning).
    ///      It updates the PersonalInfo.personalLockUps of the `_from` account.
    ///      See {updateDataOfLockedTokensOf}.
    function _afterTokenTransfer(address _from) internal {
        updateDataOfLockedTokensOf(_from);
    }

The updateDataOfLockedTokensOf() function removes the lock-up entry if the unlocking timestamp has passed, thus freeing the locked tokens and allowing them to be transferred.

/// @notice Updates the lock-ups of the `_account` based on timestamps.
    ///         If the unlock time for certain tokens has arrived,
    ///         those tokens will be released.
    /// @dev This function iterates through the array of locked token pairs:
    ///      {PersonalInfo.personalLockUps}.
    ///      If the [0] indexed parameter (unlock timestamp) is less than the current
    ///      timestamp - {block.timestamp} => it will be deleted from the array
    ///      and the [1] indexed amount of tokens will be unlocked, triggering the
    ///      UnlockTokens event.
    ///      Otherwise, the pair is retained, and the tokens remain locked.
    /// @param _account The address for which to update Lock-ups.
    /// @return true if the function executes successfully.
    function updateDataOfLockedTokensOf(
        address _account
    ) public returns (bool) {
        if (userData[_account].personalLockUps.length == 0) {
            return true;
        } else {
            uint count = 0;
            uint256[2][] memory memoryArray = new uint256[2][](
                userData[_account].personalLockUps.length
            );
            for (uint256 i = 0; i < memoryArray.length; i++) {
                if (
                    userData[_account].personalLockUps[i][0] <= block.timestamp
                ) {
                    emit UnlockTokens(
                        _account,
                        block.timestamp,
                        userData[_account].personalLockUps[i][1]
                    );
                } else {
                    memoryArray[i] = userData[_account].personalLockUps[i];
                    count++;
                }
            }

            uint256[2][] memory finalArray = new uint256[2][](count);
            uint k = 0;

            for (uint256 i = 0; i < memoryArray.length; i++) {
                if (memoryArray[i][0] > 0) {
                    finalArray[k] = memoryArray[i];
                    k++;
                }
            }

            userData[_account].personalLockUps = finalArray;
            return true;
        }
    }

Additionally, note that the updateDataOfLockedTokensOf() function is public and can be called at any time by any user without restrictions. This will update the list of locked pairs and unlock tokens as their respective timestamps are reached.


PreviousMint, Burn and Treasury ManagementNextContract Governance

Last updated 8 months ago

Was this helpful?