编译器
0.8.19+commit.7dd6d404
文件 1 的 4:AllocationVesting.sol
pragma solidity 0.8.19;
import { IERC20 } from "IERC20.sol";
import { DelegatedOps } from "DelegatedOps.sol";
import { ITokenLocker } from "ITokenLocker.sol";
contract AllocationVesting is DelegatedOps {
error NothingToClaim();
error CannotLock();
error WrongMaxTotalPreclaimPct();
error PreclaimTooLarge();
error AllocationsMismatch();
error ZeroTotalAllocation();
error ZeroAllocation();
error ZeroNumberOfWeeks();
error DuplicateAllocation();
error InsufficientPoints();
error LockedAllocation();
error SelfTransfer();
error IncompatibleVestingPeriod(uint256 numberOfWeeksFrom, uint256 numberOfWeeksTo);
struct AllocationSplit {
address recipient;
uint24 points;
uint8 numberOfWeeks;
}
struct AllocationState {
uint24 points;
uint8 numberOfWeeks;
uint128 claimed;
uint96 preclaimed;
}
uint256 private immutable totalPoints;
mapping(address => AllocationState) public allocations;
uint256 public immutable maxTotalPreclaimPct;
uint256 public immutable totalAllocation;
IERC20 public immutable vestingToken;
address public immutable vault;
ITokenLocker public immutable tokenLocker;
uint256 public immutable lockToTokenRatio;
uint256 public immutable vestingStart;
constructor(
IERC20 vestingToken_,
ITokenLocker tokenLocker_,
uint256 totalAllocation_,
address vault_,
uint256 maxTotalPreclaimPct_,
uint256 vestingStart_,
AllocationSplit[] memory allocationSplits
) {
if (totalAllocation_ == 0) revert ZeroTotalAllocation();
if (maxTotalPreclaimPct_ > 20) revert WrongMaxTotalPreclaimPct();
vault = vault_;
tokenLocker = tokenLocker_;
vestingToken = vestingToken_;
totalAllocation = totalAllocation_;
lockToTokenRatio = tokenLocker_.lockToTokenRatio();
maxTotalPreclaimPct = maxTotalPreclaimPct_;
vestingStart = vestingStart_;
uint256 loopEnd = allocationSplits.length;
uint256 total;
for (uint256 i; i < loopEnd; ) {
address recipient = allocationSplits[i].recipient;
uint8 numberOfWeeks = allocationSplits[i].numberOfWeeks;
uint256 points = allocationSplits[i].points;
if (points == 0) revert ZeroAllocation();
if (numberOfWeeks == 0) revert ZeroNumberOfWeeks();
if (allocations[recipient].numberOfWeeks > 0) revert DuplicateAllocation();
total += points;
allocations[recipient].points = uint24(points);
allocations[recipient].numberOfWeeks = numberOfWeeks;
unchecked {
++i;
}
}
totalPoints = total;
}
function transferPoints(address from, address to, uint256 points) external callerOrDelegated(from) {
if (from == to) revert SelfTransfer();
AllocationState memory fromAllocation = allocations[from];
AllocationState memory toAllocation = allocations[to];
uint8 numberOfWeeksFrom = fromAllocation.numberOfWeeks;
uint8 numberOfWeeksTo = toAllocation.numberOfWeeks;
uint256 pointsFrom = fromAllocation.points;
if (numberOfWeeksTo != 0 && numberOfWeeksTo != numberOfWeeksFrom)
revert IncompatibleVestingPeriod(numberOfWeeksFrom, numberOfWeeksTo);
uint256 totalVested = _vestedAt(block.timestamp, pointsFrom, numberOfWeeksFrom);
if (totalVested < fromAllocation.claimed) revert LockedAllocation();
if (points == 0) revert ZeroAllocation();
if (pointsFrom < points) revert InsufficientPoints();
uint256 claimed = _claim(from, pointsFrom, fromAllocation.claimed, numberOfWeeksFrom);
uint128 claimedAdjustment = uint128((claimed * points) / fromAllocation.points);
allocations[from].points = uint24(pointsFrom - points);
allocations[from].claimed = allocations[from].claimed - claimedAdjustment;
allocations[to].points = toAllocation.points + uint24(points);
allocations[to].claimed = toAllocation.claimed + claimedAdjustment;
uint256 preclaimedToTransfer = (fromAllocation.preclaimed * points) / pointsFrom;
allocations[to].preclaimed = uint96(toAllocation.preclaimed + preclaimedToTransfer);
allocations[from].preclaimed = uint96(fromAllocation.preclaimed - preclaimedToTransfer);
if (numberOfWeeksTo == 0) {
allocations[to].numberOfWeeks = numberOfWeeksFrom;
}
}
function lockFutureClaims(address account, uint256 amount) external callerOrDelegated(account) {
lockFutureClaimsWithReceiver(account, account, amount);
}
function lockFutureClaimsWithReceiver(
address account,
address receiver,
uint256 amount
) public callerOrDelegated(account) {
AllocationState memory allocation = allocations[account];
if (allocation.points == 0 || vestingStart == 0) revert CannotLock();
uint256 claimedUpdated = allocation.claimed;
if (_claimableAt(block.timestamp, allocation.points, allocation.claimed, allocation.numberOfWeeks) > 0) {
claimedUpdated = _claim(account, allocation.points, allocation.claimed, allocation.numberOfWeeks);
}
uint256 userAllocation = (allocation.points * totalAllocation) / totalPoints;
uint256 _unclaimed = userAllocation - claimedUpdated;
uint256 preclaimed = allocation.preclaimed;
uint256 maxTotalPreclaim = (maxTotalPreclaimPct * userAllocation) / 100;
uint256 leftToPreclaim = maxTotalPreclaim - preclaimed;
if (amount == 0) amount = leftToPreclaim > _unclaimed ? _unclaimed : leftToPreclaim;
else if (preclaimed + amount > maxTotalPreclaim || amount > _unclaimed) revert PreclaimTooLarge();
amount = (amount / lockToTokenRatio) * lockToTokenRatio;
allocations[account].claimed = uint128(claimedUpdated + amount);
allocations[account].preclaimed = uint96(preclaimed + amount);
vestingToken.transferFrom(vault, address(this), amount);
tokenLocker.lock(receiver, amount / lockToTokenRatio, 52);
}
function claim(address account) external callerOrDelegated(account) {
AllocationState memory allocation = allocations[account];
_claim(account, allocation.points, allocation.claimed, allocation.numberOfWeeks);
}
function _claim(
address account,
uint256 points,
uint256 claimed,
uint256 numberOfWeeks
) private returns (uint256 claimedUpdated) {
if (points == 0) revert NothingToClaim();
uint256 claimable = _claimableAt(block.timestamp, points, claimed, numberOfWeeks);
if (claimable == 0) revert NothingToClaim();
claimedUpdated = claimed + claimable;
allocations[account].claimed = uint128(claimedUpdated);
vestingToken.transferFrom(vault, msg.sender, claimable);
}
function claimableNow(address account) external view returns (uint256 claimable) {
AllocationState memory allocation = allocations[account];
claimable = _claimableAt(block.timestamp, allocation.points, allocation.claimed, allocation.numberOfWeeks);
}
function _claimableAt(
uint256 when,
uint256 points,
uint256 claimed,
uint256 numberOfWeeks
) private view returns (uint256) {
uint256 totalVested = _vestedAt(when, points, numberOfWeeks);
return totalVested > claimed ? totalVested - claimed : 0;
}
function _vestedAt(uint256 when, uint256 points, uint256 numberOfWeeks) private view returns (uint256 vested) {
if (vestingStart == 0 || numberOfWeeks == 0) return 0;
uint256 vestingWeeks = numberOfWeeks * 1 weeks;
uint256 vestingEnd = vestingStart + vestingWeeks;
uint256 endTime = when >= vestingEnd ? vestingEnd : when;
uint256 timeSinceStart = endTime - vestingStart;
vested = (totalAllocation * timeSinceStart * points) / (totalPoints * vestingWeeks);
}
function unclaimed(address account) external view returns (uint256) {
AllocationState memory allocation = allocations[account];
uint256 accountAllocation = (totalAllocation * allocation.points) / totalPoints;
return accountAllocation - allocation.claimed;
}
function preclaimable(address account) external view returns (uint256) {
AllocationState memory allocation = allocations[account];
if (allocation.points == 0 || vestingStart == 0) return 0;
uint256 userAllocation = (allocation.points * totalAllocation) / totalPoints;
uint256 preclaimed = allocation.preclaimed;
uint256 maxTotalPreclaim = (maxTotalPreclaimPct * userAllocation) / 100;
return maxTotalPreclaim - preclaimed;
}
}
文件 2 的 4:DelegatedOps.sol
pragma solidity 0.8.19;
contract DelegatedOps {
mapping(address owner => mapping(address caller => bool isApproved)) public isApprovedDelegate;
modifier callerOrDelegated(address _account) {
require(msg.sender == _account || isApprovedDelegate[_account][msg.sender], "Delegate not approved");
_;
}
function setDelegateApproval(address _delegate, bool _isApproved) external {
isApprovedDelegate[msg.sender][_delegate] = _isApproved;
}
}
文件 3 的 4:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
文件 4 的 4:ITokenLocker.sol
pragma solidity ^0.8.0;
interface ITokenLocker {
struct LockData {
uint256 amount;
uint256 weeksToUnlock;
}
struct ExtendLockData {
uint256 amount;
uint256 currentWeeks;
uint256 newWeeks;
}
event LockCreated(address indexed account, uint256 amount, uint256 _weeks);
event LockExtended(address indexed account, uint256 amount, uint256 _weeks, uint256 newWeeks);
event LocksCreated(address indexed account, LockData[] newLocks);
event LocksExtended(address indexed account, ExtendLockData[] locks);
event LocksFrozen(address indexed account, uint256 amount);
event LocksUnfrozen(address indexed account, uint256 amount);
event LocksWithdrawn(address indexed account, uint256 withdrawn, uint256 penalty);
function extendLock(uint256 _amount, uint256 _weeks, uint256 _newWeeks) external returns (bool);
function extendMany(ExtendLockData[] calldata newExtendLocks) external returns (bool);
function freeze() external;
function getAccountWeightWrite(address account) external returns (uint256);
function getTotalWeightWrite() external returns (uint256);
function lock(address _account, uint256 _amount, uint256 _weeks) external returns (bool);
function lockMany(address _account, LockData[] calldata newLocks) external returns (bool);
function setPenaltyWithdrawalsEnabled(bool _enabled) external returns (bool);
function unfreeze(bool keepIncentivesVote) external;
function withdrawExpiredLocks(uint256 _weeks) external returns (bool);
function withdrawWithPenalty(uint256 amountToWithdraw) external returns (uint256);
function MAX_LOCK_WEEKS() external view returns (uint256);
function PRISMA_CORE() external view returns (address);
function getAccountActiveLocks(
address account,
uint256 minWeeks
) external view returns (LockData[] memory lockData, uint256 frozenAmount);
function getAccountBalances(address account) external view returns (uint256 locked, uint256 unlocked);
function getAccountWeight(address account) external view returns (uint256);
function getAccountWeightAt(address account, uint256 week) external view returns (uint256);
function getTotalWeight() external view returns (uint256);
function getTotalWeightAt(uint256 week) external view returns (uint256);
function getWeek() external view returns (uint256 week);
function getWithdrawWithPenaltyAmounts(
address account,
uint256 amountToWithdraw
) external view returns (uint256 amountWithdrawn, uint256 penaltyAmountPaid);
function guardian() external view returns (address);
function incentiveVoter() external view returns (address);
function lockToTokenRatio() external view returns (uint256);
function lockToken() external view returns (address);
function owner() external view returns (address);
function penaltyWithdrawalsEnabled() external view returns (bool);
function prismaCore() external view returns (address);
function totalDecayRate() external view returns (uint32);
function totalUpdatedWeek() external view returns (uint16);
}
{
"compilationTarget": {
"AllocationVesting.sol": "AllocationVesting"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IERC20","name":"vestingToken_","type":"address"},{"internalType":"contract ITokenLocker","name":"tokenLocker_","type":"address"},{"internalType":"uint256","name":"totalAllocation_","type":"uint256"},{"internalType":"address","name":"vault_","type":"address"},{"internalType":"uint256","name":"maxTotalPreclaimPct_","type":"uint256"},{"internalType":"uint256","name":"vestingStart_","type":"uint256"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint24","name":"points","type":"uint24"},{"internalType":"uint8","name":"numberOfWeeks","type":"uint8"}],"internalType":"struct AllocationVesting.AllocationSplit[]","name":"allocationSplits","type":"tuple[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AllocationsMismatch","type":"error"},{"inputs":[],"name":"CannotLock","type":"error"},{"inputs":[],"name":"DuplicateAllocation","type":"error"},{"inputs":[{"internalType":"uint256","name":"numberOfWeeksFrom","type":"uint256"},{"internalType":"uint256","name":"numberOfWeeksTo","type":"uint256"}],"name":"IncompatibleVestingPeriod","type":"error"},{"inputs":[],"name":"InsufficientPoints","type":"error"},{"inputs":[],"name":"LockedAllocation","type":"error"},{"inputs":[],"name":"NothingToClaim","type":"error"},{"inputs":[],"name":"PreclaimTooLarge","type":"error"},{"inputs":[],"name":"SelfTransfer","type":"error"},{"inputs":[],"name":"WrongMaxTotalPreclaimPct","type":"error"},{"inputs":[],"name":"ZeroAllocation","type":"error"},{"inputs":[],"name":"ZeroNumberOfWeeks","type":"error"},{"inputs":[],"name":"ZeroTotalAllocation","type":"error"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allocations","outputs":[{"internalType":"uint24","name":"points","type":"uint24"},{"internalType":"uint8","name":"numberOfWeeks","type":"uint8"},{"internalType":"uint128","name":"claimed","type":"uint128"},{"internalType":"uint96","name":"preclaimed","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"claimableNow","outputs":[{"internalType":"uint256","name":"claimable","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"caller","type":"address"}],"name":"isApprovedDelegate","outputs":[{"internalType":"bool","name":"isApproved","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"lockFutureClaims","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"lockFutureClaimsWithReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockToTokenRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxTotalPreclaimPct","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"preclaimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"bool","name":"_isApproved","type":"bool"}],"name":"setDelegateApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenLocker","outputs":[{"internalType":"contract ITokenLocker","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"points","type":"uint256"}],"name":"transferPoints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"unclaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"}]