Token Linear Release System
The token linear release system is used to implement smooth and continuous token release, typically used in scenarios such as team incentives and investor allocations. This tutorial will explain how to implement a secure linear release system.
Features
- Linear release mechanism
- Flexible release parameters
- Multi-beneficiary support
- Release pause functionality
- Emergency withdrawal mechanism
Contract Implementation
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
/**
* @title LinearTokenRelease
* @dev Token linear release contract implementation
*/
contract LinearTokenRelease is Ownable, ReentrancyGuard {
using SafeMath for uint256;
// Release plan structure
struct ReleasePlan {
address token; // Token address
address beneficiary; // Beneficiary address
uint256 totalAmount; // Total release amount
uint256 releasedAmount; // Released amount
uint256 startTime; // Start time
uint256 duration; // Release period
uint256 cliffDuration; // Lock period
bool revocable; // Whether revocable
bool revoked; // Whether revoked
bool paused; // Whether paused
}
// Release plan mapping
mapping(bytes32 => ReleasePlan) public plans;
// User plan mapping
mapping(address => bytes32[]) public userPlans;
// Events
event PlanCreated(bytes32 indexed planId, address indexed token, address indexed beneficiary);
event TokensReleased(bytes32 indexed planId, uint256 amount);
event PlanRevoked(bytes32 indexed planId);
event PlanPaused(bytes32 indexed planId);
event PlanUnpaused(bytes32 indexed planId);
event BeneficiaryUpdated(bytes32 indexed planId, address indexed oldBeneficiary, address indexed newBeneficiary);
/**
* @dev Create release plan
*/
function createPlan(
address _token,
address _beneficiary,
uint256 _totalAmount,
uint256 _startTime,
uint256 _duration,
uint256 _cliffDuration,
bool _revocable
) external returns (bytes32) {
require(_token != address(0), "Invalid token address");
require(_beneficiary != address(0), "Invalid beneficiary address");
require(_totalAmount > 0, "Invalid amount");
require(_duration > 0, "Invalid duration");
require(_startTime >= block.timestamp, "Start time must be future");
require(_cliffDuration <= _duration, "Cliff longer than duration");
// Generate plan ID
bytes32 planId = keccak256(
abi.encodePacked(
_token,
_beneficiary,
_totalAmount,
_startTime,
block.timestamp
)
);
// Transfer tokens
IERC20(_token).transferFrom(msg.sender, address(this), _totalAmount);
// Create release plan
plans[planId] = ReleasePlan({
token: _token,
beneficiary: _beneficiary,
totalAmount: _totalAmount,
releasedAmount: 0,
startTime: _startTime,
duration: _duration,
cliffDuration: _cliffDuration,
revocable: _revocable,
revoked: false,
paused: false
});
userPlans[_beneficiary].push(planId);
emit PlanCreated(planId, _token, _beneficiary);
return planId;
}
/**
* @dev Release tokens
*/
function release(bytes32 _planId) external nonReentrant {
ReleasePlan storage plan = plans[_planId];
require(!plan.revoked, "Plan revoked");
require(!plan.paused, "Plan paused");
require(
msg.sender == plan.beneficiary,
"Only beneficiary can release"
);
uint256 releasableAmount = getReleasableAmount(_planId);
require(releasableAmount > 0, "No tokens to release");
plan.releasedAmount = plan.releasedAmount.add(releasableAmount);
IERC20(plan.token).transfer(plan.beneficiary, releasableAmount);
emit TokensReleased(_planId, releasableAmount);
}
/**
* @dev Calculate releasable amount
*/
function getReleasableAmount(bytes32 _planId) public view returns (uint256) {
ReleasePlan storage plan = plans[_planId];
if (plan.revoked || plan.paused ||
block.timestamp < plan.startTime.add(plan.cliffDuration)) {
return 0;
}
if (block.timestamp >= plan.startTime.add(plan.duration)) {
return plan.totalAmount.sub(plan.releasedAmount);
}
uint256 timeFromStart = block.timestamp.sub(plan.startTime);
uint256 vestedAmount = plan.totalAmount.mul(timeFromStart).div(plan.duration);
return vestedAmount.sub(plan.releasedAmount);
}
/**
* @dev Revoke plan (admin only)
*/
function revoke(bytes32 _planId) external onlyOwner {
ReleasePlan storage plan = plans[_planId];
require(plan.revocable, "Plan not revocable");
require(!plan.revoked, "Plan already revoked");
plan.revoked = true;
uint256 remainingAmount = plan.totalAmount.sub(plan.releasedAmount);
if (remainingAmount > 0) {
IERC20(plan.token).transfer(owner(), remainingAmount);
}
emit PlanRevoked(_planId);
}
/**
* @dev Pause plan (admin only)
*/
function pausePlan(bytes32 _planId) external onlyOwner {
ReleasePlan storage plan = plans[_planId];
require(!plan.paused, "Plan already paused");
plan.paused = true;
emit PlanPaused(_planId);
}
/**
* @dev Unpause plan (admin only)
*/
function unpausePlan(bytes32 _planId) external onlyOwner {
ReleasePlan storage plan = plans[_planId];
require(plan.paused, "Plan not paused");
plan.paused = false;
emit PlanUnpaused(_planId);
}
/**
* @dev Update beneficiary
*/
function updateBeneficiary(bytes32 _planId, address _newBeneficiary) external {
require(_newBeneficiary != address(0), "Invalid beneficiary address");
ReleasePlan storage plan = plans[_planId];
require(
msg.sender == plan.beneficiary,
"Only current beneficiary can update"
);
address oldBeneficiary = plan.beneficiary;
plan.beneficiary = _newBeneficiary;
// Update user plan mapping
bytes32[] storage oldUserPlanIds = userPlans[oldBeneficiary];
for (uint256 i = 0; i < oldUserPlanIds.length; i++) {
if (oldUserPlanIds[i] == _planId) {
oldUserPlanIds[i] = oldUserPlanIds[oldUserPlanIds.length - 1];
oldUserPlanIds.pop();
break;
}
}
userPlans[_newBeneficiary].push(_planId);
emit BeneficiaryUpdated(_planId, oldBeneficiary, _newBeneficiary);
}
/**
* @dev Get all plans for a user
*/
function getUserPlans(address _user) external view returns (bytes32[] memory) {
return userPlans[_user];
}
/**
* @dev Get plan details
*/
function getPlanInfo(bytes32 _planId)
external
view
returns (
address token,
address beneficiary,
uint256 totalAmount,
uint256 releasedAmount,
uint256 startTime,
uint256 duration,
uint256 cliffDuration,
bool revocable,
bool revoked,
bool paused
)
{
ReleasePlan storage plan = plans[_planId];
return (
plan.token,
plan.beneficiary,
plan.totalAmount,
plan.releasedAmount,
plan.startTime,
plan.duration,
plan.cliffDuration,
plan.revocable,
plan.revoked,
plan.paused
);
}
}
Key Concepts
Release Mechanism
Linear release supports:
- Continuous release
- Lock period setting
- Release rate
- Pause functionality
Calculation Methods
Release calculation includes:
- Time calculation
- Proportion calculation
- Lock verification
- Balance check
Permission Control
Permission management:
- Beneficiary permission
- Admin permission
- Pause permission
- Revoke permission
Security Considerations
Time control
- Start time verification
- Lock period check
- Duration verification
- Pause mechanism
Amount verification
- Balance check
- Release calculation
- Minimum amount
- Precision handling
Permission management
- Role verification
- Operation restriction
- State protection
- Event recording
Emergency handling
- Pause mechanism
- Revoke function
- Balance return
- State recovery
Best Practices
Release design
- Reasonable release cycle
- Appropriate lock period
- Flexible parameter setting
- Comprehensive pause mechanism
Data management
- State tracking
- Plan management
- User mapping
- Event recording
User experience
- Simple operation process
- Clear state display
- Timely feedback mechanism
- Complete query function
Exception handling
- Input verification
- State check
- Error prompt
- Rollback mechanism
Extended Features
- Multi-token support
- Dynamic release rate
- Segmented release
- Conditional trigger
- Voting right management
Application Scenarios
Team incentives
- Employee options
- Consultant rewards
- Contribution incentives
- Long-term binding
Investment unlocking
- Private placement allocation
- Institutional investment
- Strategic cooperation
- Risk control
Project development
- Ecological construction
- Community incentives
- Market marketing
- Liquidity management
Summary
The token linear release system is an important tool for token distribution. Through this tutorial, you can:
- Implement smooth token release
- Protect the rights of all parties
- Flexibly control release
- Ensure system security
Frequently Asked Questions (FAQ)
Basic Concepts
Q: What is linear release?
A: Linear release is a token distribution mechanism, with the main features including:
- Uniform release
- Time control
- Release rate
- Lock period
- Release plan
Q: What types of linear release are there?
A: The main types include:
- Fixed rate release
- Adjustable rate release
- Segmented linear release
- Mixed release
- Conditional release
Operation Related
Q: How to design release strategies?
A: Design points include:
- Determine the total amount
- Set the cycle
- Configure the rate
- Add conditions
- Set checkpoints
Q: How to manage release processes?
A: Management methods include:
- Monitor progress
- Adjust parameters
- Handle exceptions
- Verify conditions
- Execute release
Security Related
Q: What are the risks of linear release?
A: The main risks include:
- Incorrect parameter setting
- Calculation precision issues
- Time control failure
- Contract vulnerabilities
- Operational errors
Q: How to ensure release security?
A: Security measures include:
- Multiple verification
- Parameter check
- State monitoring
- Emergency pause
- Complete logging
## Implementation Details
### Release Mechanism
The linear release mechanism includes:
1. Time-based release
- Start time
- Lock period
- Release period
- Release rate
2. Amount calculation
- Total amount
- Released amount
- Releasable amount
- Remaining amount
3. State management
- Plan creation
- Release execution
- Plan revocation
- Plan pause/unpause
### Security Features
1. Access control
- Owner permissions
- Beneficiary permissions
- Function restrictions
- State validations
2. Fund security
- Safe transfer
- Balance verification
- Reentrancy protection
- Emergency handling
3. Parameter validation
- Address validation
- Amount validation
- Time validation
- State validation
## Usage Guide
### Creating a Release Plan
To create a linear release plan:
1. Prepare parameters:
- Token address
- Beneficiary address
- Total amount
- Start time
- Duration
- Cliff duration
- Revocable flag
2. Approve token transfer:
```solidity
IERC20(tokenAddress).approve(releaseContractAddress, amount);
- Create plan:
bytes32 planId = linearRelease.createPlan(
tokenAddress,
beneficiary,
amount,
startTime,
duration,
cliffDuration,
revocable
);
Managing Release Plans
- Release tokens:
linearRelease.release(planId);
- Pause plan (admin only):
linearRelease.pausePlan(planId);
- Unpause plan (admin only):
linearRelease.unpausePlan(planId);
- Revoke plan (admin only):
linearRelease.revoke(planId);
Querying Plan Information
- Get plan details:
linearRelease.getPlanInfo(planId);
- Get user plans:
linearRelease.getUserPlans(userAddress);
- Get releasable amount:
uint256 amount = linearRelease.getReleasableAmount(planId);
Integration Examples
Basic Integration
// Deploy contract
LinearTokenRelease release = new LinearTokenRelease();
// Create plan
bytes32 planId = release.createPlan(
tokenAddress,
beneficiary,
1000000 * 10**18, // 1 million tokens
block.timestamp + 1 days, // starts tomorrow
365 days, // 1 year duration
30 days, // 1 month cliff
true // revocable
);
// Release tokens
release.release(planId);
Advanced Integration
// Custom release contract
contract CustomRelease is LinearTokenRelease {
// Add custom logic
function customRelease(bytes32 planId) external {
// Custom validation
require(condition, "Custom condition failed");
// Release tokens
release(planId);
// Post-release actions
emit CustomEvent(planId);
}
}
Testing Guide
Test Cases
- Basic functionality:
function testCreatePlan() public {
// Test plan creation
}
function testRelease() public {
// Test token release
}
- Security features:
function testAccessControl() public {
// Test permissions
}
function testEmergencyHandling() public {
// Test emergency functions
}
Test Environment
- Setup:
// Deploy test token
TestToken token = new TestToken();
// Deploy release contract
LinearTokenRelease release = new LinearTokenRelease();
// Setup test data
token.mint(address(this), 1000000);
token.approve(address(release), 1000000);
- Execution:
// Create test plan
bytes32 planId = release.createPlan(...);
// Advance time
vm.warp(block.timestamp + 30 days);
// Test release
release.release(planId);
Troubleshooting
Common Issues
Transaction failures
- Insufficient balance
- Insufficient allowance
- Invalid state
- Permission denied
Calculation issues
- Rounding errors
- Time calculation
- Amount precision
- State inconsistency
Solutions
Balance issues:
- Check token balance
- Verify allowance
- Confirm decimals
- Review calculations
Permission issues:
- Verify caller
- Check role
- Review permissions
- Check state
Time issues:
- Verify timestamps
- Check durations
- Review periods
- Validate states