Skip to main content

Shared Libraries

Core libraries providing foundational functionality, data structures, and utilities used across all TokenOps vesting contracts. These libraries ensure consistency, efficiency, and maintainability throughout the system.

📚 Available Libraries

Core Computational Libraries

TokenVestingLib

library TokenVestingLib {
struct Vesting { /* ... */ }
function calculateVestedAmount(Vesting memory, uint32) pure returns (uint256);
}

Central library containing vesting calculations and data structures. Provides gas-optimized algorithms for all vesting computations across the system.

Access Control Libraries

AccessProtected

abstract contract AccessProtected {
enum AdminRole { None, Standard, Super }
modifier onlyAdmin() { /* ... */ }
}

Multi-admin access control system with role-based permissions and emergency controls for secure administrative operations.

Error Management Libraries

Errors

library Errors {
error InvalidRecipient();
error VestingNotFound();
error InsufficientFunding();
}

Centralized error definitions providing standardized, gas-efficient error handling across all contracts.

Fee Management Libraries

FactoryFeeManager

abstract contract FactoryFeeManager {
enum FeeType { Gas, DistributionToken }
function getFeeConfig(address) returns (FeeType, uint256);
}

Sophisticated fee management for factory contracts with support for custom fee structures and flexible collection mechanisms.

🎯 Library Overview

TokenVestingLib - Core Computations

Purpose: Mathematical foundation for all vesting operations Key Features:

  • Gas-optimized vesting calculations
  • Packed struct design for storage efficiency
  • Support for complex vesting schedules (cliff, linear, initial unlock)
  • Consistent calculation logic across all manager types

Usage Example:

using TokenVestingLib for TokenVestingLib.Vesting;

function getVestedAmount(bytes32 vestingId) public view returns (uint256) {
return vestings[vestingId].calculateVestedAmount(uint32(block.timestamp));
}

AccessProtected - Security Framework

Purpose: Role-based access control and security management Key Features:

  • Multi-admin support with hierarchical roles
  • Emergency pause/unpause capabilities
  • Secure admin role management
  • Event-driven transparency

Usage Example:

contract MyVestingManager is AccessProtected {
constructor() AccessProtected(msg.sender) {}

function createVesting() external onlyAdmin whenNotPaused {
// Admin-only function with pause protection
}
}

Errors - Standardized Error Handling

Purpose: Centralized, gas-efficient error management Key Features:

  • Custom errors instead of string-based reverts
  • Categorized by functional domain
  • Parameterized errors for better debugging
  • Significant gas savings on deployment and execution

Usage Example:

function claimVesting(bytes32 vestingId) external {
if (vestings[vestingId].recipient != msg.sender) {
revert Errors.NotAuthorized();
}

if (claimableAmount == 0) {
revert Errors.NoTokensAvailable();
}
}

FactoryFeeManager - Fee Collection System

Purpose: Flexible fee management for factory-deployed contracts Key Features:

  • Dual fee types (gas-based and token-based)
  • Custom fee configurations per deployer
  • Automatic fee collection and distribution
  • Volume-based discounts and tier systems

Usage Example:

contract VestingFactory is FactoryFeeManager {
function deployManager(address token) external payable {
collectGasFee(msg.sender);
// Deploy with fee configuration
}
}

🔧 Integration Patterns

Combined Usage

Most vesting contracts use multiple libraries together:

contract TokenVestingManager is AccessProtected {
using TokenVestingLib for TokenVestingLib.Vesting;

mapping(bytes32 => TokenVestingLib.Vesting) public vestings;

function createVesting(
address recipient,
uint256 amount,
uint32 duration
) external onlyAdmin returns (bytes32) {
if (recipient == address(0)) {
revert Errors.InvalidRecipient();
}

bytes32 vestingId = keccak256(abi.encodePacked(recipient, amount, block.timestamp));

vestings[vestingId] = TokenVestingLib.Vesting({
recipient: recipient,
startTimestamp: uint32(block.timestamp),
endTimestamp: uint32(block.timestamp + duration),
// ... other fields
});

return vestingId;
}

function claim(bytes32 vestingId) external {
TokenVestingLib.Vesting storage vesting = vestings[vestingId];

if (vesting.recipient != msg.sender) {
revert Errors.NotAuthorized();
}

uint256 vested = vesting.calculateVestedAmount(uint32(block.timestamp));
uint256 claimable = vested - vesting.claimedAmount;

if (claimable == 0) {
revert Errors.NoTokensAvailable();
}

vesting.claimedAmount += claimable;
// Transfer tokens...
}
}

Factory Integration Pattern

contract VestingManagerFactory is AccessProtected, FactoryFeeManager {
constructor(
address feeCollector,
uint256 defaultGasFee,
uint256 defaultTokenFee
)
AccessProtected(msg.sender)
FactoryFeeManager(feeCollector, defaultGasFee, defaultTokenFee)
{}

function deployVestingManager(
address token
) external payable onlyAdmin returns (address) {
// Collect deployment fee
collectGasFee(msg.sender);

// Get fee configuration for the deployed manager
(ITypes.FeeType feeType, uint256 fee) = getFeeConfig(msg.sender);

// Deploy with proper configuration
TokenVestingManager manager = new TokenVestingManager(
token,
feeType,
fee,
feeCollector
);

// Transfer admin privileges
manager.transferAdmin(msg.sender);

return address(manager);
}
}

📊 Library Comparison

LibraryPurposeGas ImpactComplexityDependencies
TokenVestingLibCore calculationsHigh savingsMediumNone
AccessProtectedAccess controlMedium costLowNone
ErrorsError handlingHigh savingsLowNone
FactoryFeeManagerFee managementMedium costHighAccessProtected

🚀 Quick Start

Basic Manager Implementation

pragma solidity ^0.8.19;

import {AccessProtected} from "./libraries/AccessProtected.sol";
import {TokenVestingLib} from "./libraries/TokenVestingLib.sol";
import {Errors} from "./libraries/Errors.sol";

contract BasicVestingManager is AccessProtected {
using TokenVestingLib for TokenVestingLib.Vesting;

mapping(bytes32 => TokenVestingLib.Vesting) public vestings;

constructor(address initialAdmin) AccessProtected(initialAdmin) {}

function createVesting(
TokenVestingLib.VestingParams memory params
) external onlyAdmin returns (bytes32) {
if (params._recipient == address(0)) {
revert Errors.InvalidRecipient();
}

vestings[params._vestingId] = TokenVestingLib.Vesting({
recipient: params._recipient,
startTimestamp: params._startTimestamp,
endTimestamp: params._endTimestamp,
// ... other fields from params
});

return params._vestingId;
}

function getVestedAmount(bytes32 vestingId) external view returns (uint256) {
return vestings[vestingId].calculateVestedAmount(uint32(block.timestamp));
}
}

Factory Implementation

contract VestingFactory is AccessProtected, FactoryFeeManager {
constructor()
AccessProtected(msg.sender)
FactoryFeeManager(
msg.sender, // Fee collector
0.001 ether, // Default gas fee
100 // Default token fee (1%)
)
{}

function deployBasicManager(
address token
) external payable returns (BasicVestingManager) {
collectGasFee(msg.sender);

BasicVestingManager manager = new BasicVestingManager(msg.sender);

emit ManagerDeployed(address(manager), msg.sender);
return manager;
}

event ManagerDeployed(address indexed manager, address indexed deployer);
}

🛡️ Security Considerations

Library Security

TokenVestingLib

  • Pure functions - No state modifications, safe for all contexts
  • Input validation - Calling contracts must validate inputs
  • Overflow protection - Uses Solidity 0.8+ built-in protection
  • Precision handling - Conservative calculations prevent over-vesting

AccessProtected

  • Admin lockout prevention - Cannot remove last admin
  • Role validation - Proper role checking in all functions
  • Emergency controls - Pause mechanisms for security incidents
  • Event logging - All admin changes logged for transparency

Errors

  • Information leakage - No sensitive data in error messages
  • Gas efficiency - Custom errors reduce deployment and runtime costs
  • Consistency - Standardized error types across system

FactoryFeeManager

  • Fee validation - Maximum fee limits prevent abuse
  • Access control - Admin-only fee configuration
  • Payment verification - Proper fee collection before services
  • Refund mechanisms - Excess payments automatically refunded

Integration Security

Common Patterns

// Good: Proper input validation
function createVesting(address recipient, uint256 amount) external onlyAdmin {
if (recipient == address(0)) revert Errors.InvalidRecipient();
if (amount == 0) revert Errors.ZeroAmount();
// Proceed with creation
}

// Bad: Missing validation
function createVesting(address recipient, uint256 amount) external onlyAdmin {
// Direct creation without validation
}

Error Handling

// Good: Specific errors with context
if (amount > maxAllowed) {
revert Errors.AmountExceedsMaximum(amount, maxAllowed);
}

// Bad: Generic errors
if (amount > maxAllowed) {
revert Errors.InvalidInput();
}

📈 Performance Optimization

Gas Optimization Strategies

Storage Layout

// Optimized: Pack related fields together
struct Vesting {
address recipient; // 20 bytes
uint32 startTimestamp; // 4 bytes | Same slot
uint32 endTimestamp; // 4 bytes | Same slot
uint32 cliffTimestamp; // 4 bytes | Same slot
uint256 amount; // 32 bytes | New slot
}

Calculation Efficiency

// Optimized: Single calculation with memory parameter
uint256 vested = vesting.calculateVestedAmount(uint32(block.timestamp));

// Less optimal: Multiple storage reads
uint256 vested = calculateVested(vestingId, block.timestamp);

Error Handling

// Optimized: Custom errors
if (invalid) revert Errors.InvalidInput();

// Less optimal: String errors
require(!invalid, "Invalid input parameter");

🔄 Upgrade Considerations

Library Versioning

Libraries should be versioned for compatibility:

// contracts/libraries/v1/TokenVestingLib.sol
library TokenVestingLibV1 {
// Version 1 implementation
}

// contracts/libraries/v2/TokenVestingLib.sol
library TokenVestingLibV2 {
// Version 2 with new features
}

Migration Strategies

Data Structure Evolution

// V1 Vesting struct
struct VestingV1 {
address recipient;
uint256 amount;
uint32 startTime;
}

// V2 Vesting struct with additional fields
struct VestingV2 {
address recipient;
uint256 amount;
uint32 startTime;
uint32 cliffTime; // New field
bool isRevocable; // New field
}

// Migration function
function migrateVesting(bytes32 vestingId) external {
VestingV1 memory oldVesting = oldVestings[vestingId];

newVestings[vestingId] = VestingV2({
recipient: oldVesting.recipient,
amount: oldVesting.amount,
startTime: oldVesting.startTime,
cliffTime: 0, // Default value
isRevocable: true // Default value
});
}

📋 Best Practices

Development Guidelines

  1. Import specific items instead of entire libraries
  2. Use library functions consistently across similar operations
  3. Validate inputs before calling library functions
  4. Handle errors appropriately with specific error types
  5. Test edge cases thoroughly, especially for calculations
  6. Document library usage in your contracts
  7. Monitor gas costs when using complex library functions

Code Organization

// Good: Clear imports and usage
import {TokenVestingLib} from "./libraries/TokenVestingLib.sol";
import {Errors} from "./libraries/Errors.sol";

contract VestingManager {
using TokenVestingLib for TokenVestingLib.Vesting;

function processVesting(bytes32 id) external {
if (vestings[id].recipient == address(0)) {
revert Errors.InvalidVestingId();
}

uint256 amount = vestings[id].calculateVestedAmount(uint32(block.timestamp));
// Process amount...
}
}

Testing Approach

contract LibraryTest {
function testVestingCalculation() public {
TokenVestingLib.Vesting memory vesting = createTestVesting();

uint256 vested = vesting.calculateVestedAmount(uint32(block.timestamp));

assertEq(vested, expectedAmount);
}

function testErrorHandling() public {
vm.expectRevert(Errors.InvalidRecipient.selector);
manager.createVesting(address(0), 1000);
}
}

The shared libraries form the backbone of the TokenOps vesting system, providing consistent, efficient, and secure functionality across all contracts while maintaining flexibility for different use cases and deployment patterns.