> 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
