> For the complete documentation index, see [llms.txt](https://tech.usual.money/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tech.usual.money/smart-contracts/protocol-contracts/usual/usualx-lockup-contract.md).

# USUALx Lockup Contract

## High-Level Overview

The UsualXLockup contract is an upgradeable vault that enables users to lock their UsualX tokens for predefined durations. It implements a flexible locking mechanism with features such as position creation, top-ups within the same UTC calendar day, and position releases after lock expiration. The contract leverages OpenZeppelin's upgradeable contracts for enhanced security and flexibility, including pausability and reentrancy protection.\
\
The primary objective of UsualXLockup is to provide a secure mechanism for users to lock their UsualX tokens, potentially for governance or reward-earning purposes, while maintaining strict control over lock durations and position management.

### Inherited Contracts

* **PausableUpgradeable**: Emergency halt capabilities
* **ReentrancyGuardUpgradeable**: Reentrancy attack prevention

### Reward Boost Mechanism

The contract enables users to lock their UsualX tokens for various durations to receive\
boosted rewards. Key points:

* Boost calculations are performed off-chain
* Contract provides only necessary on-chain data through events and state tracking

## Contract Summary

### Main Functions

* `initialize`: Contract setup with registry contract address
* `lock`: New lock position creation
* `intraDayTopUp`: Same-day position top-ups
* `release`: Token release after lock expiration
* `releaseAndLock`: Combined release and lock operations
* `unlockUserPosition`: Forced position unlock by privileged roles
* `unlockUsersPositionsBatch`: Forced batch position unlocking by privileged roles

Storage:

* Uses UsualXLockupStorageV0 structure for state variables

### Functionality Breakdown

A. Access Control and Security

* Registry contract for role-based access
* Position unlocking controls through dedicated roles

B. Position Management

* Multiple lock positions per user support
* Same-day position top-ups
* Post-expiration release functionality
* Privileged forced unlock capabilities

C. Lock Duration Management

* Predefined durations (1, 3, 6, and 12 months)
* Lock duration validation enforcement

### Method Analysis

#### Public/External Functions

#### 1. Initialize

```solidity
function initialize(address registryContract) external initializer {
    if (registryContract == address(0)) {
        revert NullAddress();
    }
    UsualXLockupStorageV0 storage $ = _usualXLockupStorageV0();
    $.registryContract = IRegistryContract(registryContract);
    $.registryAccess = IRegistryAccess($.registryContract.getContract(CONTRACT_REGISTRY_ACCESS));
    $.usualX = IERC20($.registryContract.getContract(CONTRACT_USUALX));
    $.lockDurations[ONE_MONTH] = true;
    $.lockDurations[THREE_MONTHS] = true;
    $.lockDurations[SIX_MONTHS] = true;
    $.lockDurations[ONE_YEAR] = true;
}
```

**Purpose**: Contract initialization\
**Key Features:**

* Registry contract validation
* Access control setup
* Default lock duration enablement\
  Security:
* Null address checks
* Single initialization enforcement

2. **Lock**

```solidity
function lock(address receiver, uint256 amount, uint256 lockDuration)
    external
    nonReentrant
    whenNotPaused
{
    if (receiver == address(0)) {
        revert NullAddress();
    }
    if (amount == 0) {
        revert AmountIsZero();
    }
    UsualXLockupStorageV0 storage $ = _usualXLockupStorageV0();
    _lock($, msg.sender, receiver, amount, lockDuration);
}
```

**Purpose**: New position creation\
**Key Features:**

* Amount and receiver validation
* Lock duration verification
* Position creation and event emission
* Reentrancy protection
* Pause mechanism
* Null address checks

3. **IntraDayTopUp**

```solidity
function intraDayTopUp(address receiver, uint256 amount, uint256 positionIndex)
    external
    nonReentrant
    whenNotPaused
{
    if (receiver == address(0)) {
        revert NullAddress();
    }
    if (amount == 0) {
        revert AmountIsZero();
    }
    UsualXLockupStorageV0 storage $ = _usualXLockupStorageV0();
    _topUp($, msg.sender, receiver, amount, positionIndex);
}
```

**Purpose:** Same-day position enhancement\
**Key Features:**

* Same-day validation
* Position amount update
* Event emission
* Reentrancy protection
* Position existence checks
* Active status verification

4. **Release**

```solidity
function release(address user, uint256 positionIndex) external nonReentrant whenNotPaused {
    if (user == address(0)) {
        revert NullAddress();
    }
    UsualXLockupStorageV0 storage $ = _usualXLockupStorageV0();
    _release($, user, positionIndex);
}
```

**Purpose**: Lock expiration token release\
**Key Features:**

* Lock expiration verification
* Token transfer
* Position status update
* Reentrancy protection
* Position validation
* Active status checks

#### Internal Functions

1. **\_lock**

```solidity
function _lock(
    UsualXLockupStorageV0 storage $,
    address sender,
    address receiver,
    uint256 amount,
    uint256 lockDuration
) internal {
    if ($.lockDurations[lockDuration] == false) {
        revert LockDurationIsNotEnabled();
    }

    $.usualX.safeTransferFrom(sender, address(this), amount);

    LockPosition memory lockPosition = LockPosition({
        amount: amount,
        startTime: block.timestamp,
        endTime: block.timestamp + lockDuration,
        isActive: true
    });
    $.userLockPositions[receiver].push(lockPosition);
    $.userLockedAmount[receiver] += amount;

    emit PositionCreated(
        receiver,
        $.userLockPositions[receiver].length - 1,
        lockPosition.amount,
        lockPosition.startTime,
        lockPosition.endTime
    );
}
```

**Purpose**: Core lock position creation\
**Implementation**:

* Lock duration validation
* Token transfer handling
* Position creation
* Total amount tracking
* Event emission

2. **\_topUp**

```solidity
function _topUp(
    UsualXLockupStorageV0 storage $,
    address sender,
    address receiver,
    uint256 amount,
    uint256 positionIndex
) internal {
    if (positionIndex >= $.userLockPositions[receiver].length) {
        revert PositionDoesNotExist();
    }
    LockPosition storage lockPosition = $.userLockPositions[receiver][positionIndex];
    if (!lockPosition.isActive) {
        revert PositionIsNotActive();
    }

    // Check if current time is within the same UTC calendar day as start time
    uint256 startDay = lockPosition.startTime / ONE_DAY;
    uint256 currentDay = block.timestamp / ONE_DAY;
    if (currentDay > startDay) {
        revert TopUpDelayHasPassed();
    }

    $.usualX.safeTransferFrom(sender, address(this), amount);
    lockPosition.amount += amount;
    $.userLockedAmount[receiver] += amount;
    emit PositionToppedUp(
        receiver,
        positionIndex,
        amount,
        lockPosition.amount,
        lockPosition.startTime,
        lockPosition.endTime
    );
}
```

**Purpose**: Position top-up logic\
**Implementation**:

* Position validation
* Same-day verification
* Amount updates
* Event emission

3. **\_release**

```solidity
function _release(UsualXLockupStorageV0 storage $, address user, uint256 positionIndex)
    internal
{
    if (positionIndex >= $.userLockPositions[user].length) {
        revert PositionDoesNotExist();
    }
    LockPosition storage lockPosition = $.userLockPositions[user][positionIndex];
    if (!lockPosition.isActive) {
        revert PositionIsNotActive();
    }
    if (block.timestamp < lockPosition.endTime) {
        revert PositionIsStillLocked();
    }
    uint256 positionAmount = lockPosition.amount;

    lockPosition.isActive = false;
    $.userLockedAmount[user] -= positionAmount;
    $.usualX.safeTransfer(user, positionAmount);
    emit PositionReleased(user, positionIndex, positionAmount);
}
```

**Purpose**: Token release implementation\
**Implementation**:

* Position validation
* Lock expiration check
* Token transfer
* State updates

4. **\_unlockBatchPositionsForUser**

```solidity
function _unlockBatchPositionsForUser(
    UsualXLockupStorageV0 storage $,
    address user,
    uint256 startIndex,
    uint256 endIndex
) private returns (uint256[] memory) {
    if (user == address(0)) {
        revert NullAddress();
    }
    if (startIndex > endIndex) {
        revert StartIndexGreaterThanEndIndex();
    }
    if (endIndex > $.userLockPositions[user].length) {
        revert PositionDoesNotExist();
    }
    uint256 totalAmount = 0;
    uint256[] memory unlockedIndexes = new uint256[](endIndex - startIndex + 1);
    uint256 unlockedCount = 0;
    for (uint256 j = startIndex; j <= endIndex; j++) {
        LockPosition storage lockPosition = $.userLockPositions[user][j];
        if (!lockPosition.isActive) {
            continue;
        }
        lockPosition.isActive = false;
        totalAmount += lockPosition.amount;
        unlockedIndexes[unlockedCount] = j;
        unlockedCount++;
    }
    // Optimize array length using assembly
    // solhint-disable-next-line no-inline-assembly
    assembly {
        mstore(unlockedIndexes, unlockedCount)
    }

    // Update the user's locked amount and transfer the assets
    $.userLockedAmount[user] -= totalAmount;
    $.usualX.safeTransfer(user, totalAmount);

    return unlockedIndexes;
}Purpose: Batch position unlocking
Implementation:
```

* Range validation
* Position processing
* Amount accumulation
* State updates
* Token transfers

## Flow Diagram

<figure><img src="/files/JzQiGqGdj9gify2g5z7Y" alt=""><figcaption></figcaption></figure>

## Security Considerations

**1. Access Control**

* Role-based permissions
* Registry contract integration
* Function-level access restrictions

**2. Input Validation**

* Address validation
* Amount verification
* Index range checks

**3. Protection Mechanisms**

* Reentrancy guards
* Pause functionality
* Duration controls


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://tech.usual.money/smart-contracts/protocol-contracts/usual/usualx-lockup-contract.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
