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 addresslock
: New lock position creationintraDayTopUp
: Same-day position top-upsrelease
: Token release after lock expirationreleaseAndLock
: Combined release and lock operationsunlockUserPosition
: Forced position unlock by privileged rolesunlockUsersPositionsBatch
: 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
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
Lock
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
IntraDayTopUp
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
Release
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
_lock
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
_topUp
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
_release
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
_unlockBatchPositionsForUser
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

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
Last updated
Was this helpful?