编译器
0.8.19+commit.7dd6d404
文件 1 的 6:IIncentiveVoting.sol
pragma solidity ^0.8.0;
interface IIncentiveVoting {
struct Vote {
uint256 id;
uint256 points;
}
struct LockData {
uint256 amount;
uint256 weeksToUnlock;
}
event AccountWeightRegistered(
address indexed account,
uint256 indexed week,
uint256 frozenBalance,
LockData[] registeredLockData
);
event ClearedVotes(address indexed account, uint256 indexed week);
event NewVotes(address indexed account, uint256 indexed week, Vote[] newVotes, uint256 totalPointsUsed);
function clearRegisteredWeight(address account) external returns (bool);
function clearVote(address account) external;
function getReceiverVotePct(uint256 id, uint256 week) external returns (uint256);
function getReceiverWeightWrite(uint256 idx) external returns (uint256);
function getTotalWeightWrite() external returns (uint256);
function registerAccountWeight(address account, uint256 minWeeks) external;
function registerAccountWeightAndVote(address account, uint256 minWeeks, Vote[] calldata votes) external;
function registerNewReceiver() external returns (uint256);
function setDelegateApproval(address _delegate, bool _isApproved) external;
function unfreeze(address account, bool keepVote) external returns (bool);
function vote(address account, Vote[] calldata votes, bool clearPrevious) external;
function MAX_LOCK_WEEKS() external view returns (uint256);
function MAX_POINTS() external view returns (uint256);
function getAccountCurrentVotes(address account) external view returns (Vote[] memory votes);
function getAccountRegisteredLocks(
address account
) external view returns (uint256 frozenWeight, LockData[] memory lockData);
function getReceiverWeight(uint256 idx) external view returns (uint256);
function getReceiverWeightAt(uint256 idx, 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 isApprovedDelegate(address owner, address caller) external view returns (bool isApproved);
function receiverCount() external view returns (uint256);
function receiverDecayRate(uint256) external view returns (uint32);
function receiverUpdatedWeek(uint256) external view returns (uint16);
function receiverWeeklyUnlocks(uint256, uint256) external view returns (uint32);
function tokenLocker() external view returns (address);
function totalDecayRate() external view returns (uint32);
function totalUpdatedWeek() external view returns (uint16);
function totalWeeklyUnlocks(uint256) external view returns (uint32);
function vault() external view returns (address);
}
文件 2 的 6:IPrismaCore.sol
pragma solidity ^0.8.0;
interface IPrismaCore {
event FeeReceiverSet(address feeReceiver);
event GuardianSet(address guardian);
event NewOwnerAccepted(address oldOwner, address owner);
event NewOwnerCommitted(address owner, address pendingOwner, uint256 deadline);
event NewOwnerRevoked(address owner, address revokedOwner);
event Paused();
event PriceFeedSet(address priceFeed);
event Unpaused();
function acceptTransferOwnership() external;
function commitTransferOwnership(address newOwner) external;
function revokeTransferOwnership() external;
function setFeeReceiver(address _feeReceiver) external;
function setGuardian(address _guardian) external;
function setPaused(bool _paused) external;
function setPriceFeed(address _priceFeed) external;
function OWNERSHIP_TRANSFER_DELAY() external view returns (uint256);
function feeReceiver() external view returns (address);
function guardian() external view returns (address);
function owner() external view returns (address);
function ownershipTransferDeadline() external view returns (uint256);
function paused() external view returns (bool);
function pendingOwner() external view returns (address);
function priceFeed() external view returns (address);
function startTime() external view returns (uint256);
}
文件 3 的 6:IPrismaToken.sol
pragma solidity ^0.8.0;
interface IPrismaToken {
event Approval(address indexed owner, address indexed spender, uint256 value);
event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, bytes _reason);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event ReceiveFromChain(uint16 indexed _srcChainId, address indexed _to, uint256 _amount);
event RetryMessageSuccess(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes32 _payloadHash);
event SendToChain(uint16 indexed _dstChainId, address indexed _from, bytes _toAddress, uint256 _amount);
event SetMinDstGas(uint16 _dstChainId, uint16 _type, uint256 _minDstGas);
event SetPrecrime(address precrime);
event SetTrustedRemote(uint16 _remoteChainId, bytes _path);
event SetTrustedRemoteAddress(uint16 _remoteChainId, bytes _remoteAddress);
event SetUseCustomAdapterParams(bool _useCustomAdapterParams);
event Transfer(address indexed from, address indexed to, uint256 value);
function approve(address spender, uint256 amount) external returns (bool);
function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external;
function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) external;
function mintToVault(uint256 _totalSupply) external returns (bool);
function nonblockingLzReceive(
uint16 _srcChainId,
bytes calldata _srcAddress,
uint64 _nonce,
bytes calldata _payload
) external;
function permit(
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function renounceOwnership() external;
function setConfig(uint16 _version, uint16 _chainId, uint256 _configType, bytes calldata _config) external;
function setMinDstGas(uint16 _dstChainId, uint16 _packetType, uint256 _minGas) external;
function setPayloadSizeLimit(uint16 _dstChainId, uint256 _size) external;
function setPrecrime(address _precrime) external;
function setReceiveVersion(uint16 _version) external;
function setSendVersion(uint16 _version) external;
function setTrustedRemote(uint16 _srcChainId, bytes calldata _path) external;
function setTrustedRemoteAddress(uint16 _remoteChainId, bytes calldata _remoteAddress) external;
function setUseCustomAdapterParams(bool _useCustomAdapterParams) external;
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function transferOwnership(address newOwner) external;
function transferToLocker(address sender, uint256 amount) external returns (bool);
function retryMessage(
uint16 _srcChainId,
bytes calldata _srcAddress,
uint64 _nonce,
bytes calldata _payload
) external payable;
function sendFrom(
address _from,
uint16 _dstChainId,
bytes calldata _toAddress,
uint256 _amount,
address _refundAddress,
address _zroPaymentAddress,
bytes calldata _adapterParams
) external payable;
function DEFAULT_PAYLOAD_SIZE_LIMIT() external view returns (uint256);
function NO_EXTRA_GAS() external view returns (uint256);
function PT_SEND() external view returns (uint16);
function allowance(address owner, address spender) external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function circulatingSupply() external view returns (uint256);
function decimals() external view returns (uint8);
function domainSeparator() external view returns (bytes32);
function estimateSendFee(
uint16 _dstChainId,
bytes calldata _toAddress,
uint256 _amount,
bool _useZro,
bytes calldata _adapterParams
) external view returns (uint256 nativeFee, uint256 zroFee);
function failedMessages(uint16, bytes calldata, uint64) external view returns (bytes32);
function getConfig(
uint16 _version,
uint16 _chainId,
address,
uint256 _configType
) external view returns (bytes memory);
function getTrustedRemoteAddress(uint16 _remoteChainId) external view returns (bytes memory);
function isTrustedRemote(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool);
function locker() external view returns (address);
function lzEndpoint() external view returns (address);
function maxTotalSupply() external view returns (uint256);
function minDstGasLookup(uint16, uint16) external view returns (uint256);
function name() external view returns (string memory);
function nonces(address owner) external view returns (uint256);
function owner() external view returns (address);
function payloadSizeLimitLookup(uint16) external view returns (uint256);
function permitTypeHash() external view returns (bytes32);
function precrime() external view returns (address);
function supportsInterface(bytes4 interfaceId) external view returns (bool);
function symbol() external view returns (string memory);
function token() external view returns (address);
function totalSupply() external view returns (uint256);
function trustedRemoteLookup(uint16) external view returns (bytes memory);
function useCustomAdapterParams() external view returns (bool);
function vault() external view returns (address);
function version() external view returns (string memory);
}
文件 4 的 6:PrismaOwnable.sol
pragma solidity 0.8.19;
import "IPrismaCore.sol";
contract PrismaOwnable {
IPrismaCore public immutable PRISMA_CORE;
constructor(address _prismaCore) {
PRISMA_CORE = IPrismaCore(_prismaCore);
}
modifier onlyOwner() {
require(msg.sender == PRISMA_CORE.owner(), "Only owner");
_;
}
function owner() public view returns (address) {
return PRISMA_CORE.owner();
}
function guardian() public view returns (address) {
return PRISMA_CORE.guardian();
}
}
文件 5 的 6:SystemStart.sol
pragma solidity 0.8.19;
import "IPrismaCore.sol";
contract SystemStart {
uint256 immutable startTime;
constructor(address prismaCore) {
startTime = IPrismaCore(prismaCore).startTime();
}
function getWeek() public view returns (uint256 week) {
return (block.timestamp - startTime) / 1 weeks;
}
}
文件 6 的 6:TokenLocker.sol
pragma solidity 0.8.19;
import "PrismaOwnable.sol";
import "SystemStart.sol";
import "IPrismaCore.sol";
import "IIncentiveVoting.sol";
import "IPrismaToken.sol";
contract TokenLocker is PrismaOwnable, SystemStart {
uint256 public constant MAX_LOCK_WEEKS = 52;
uint256 public immutable lockToTokenRatio;
IPrismaToken public immutable lockToken;
IIncentiveVoting public immutable incentiveVoter;
IPrismaCore public immutable prismaCore;
address public immutable deploymentManager;
bool public penaltyWithdrawalsEnabled;
uint256 public allowPenaltyWithdrawAfter;
struct AccountData {
uint32 locked;
uint32 unlocked;
uint32 frozen;
uint16 week;
uint256[256] updateWeeks;
}
struct LockData {
uint256 amount;
uint256 weeksToUnlock;
}
struct ExtendLockData {
uint256 amount;
uint256 currentWeeks;
uint256 newWeeks;
}
uint32 public totalDecayRate;
uint16 public totalUpdatedWeek;
uint40[65535] totalWeeklyWeights;
uint32[65535] totalWeeklyUnlocks;
mapping(address => uint40[65535]) accountWeeklyWeights;
mapping(address => uint32[65535]) accountWeeklyUnlocks;
mapping(address => AccountData) accountLockData;
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);
constructor(
address _prismaCore,
IPrismaToken _token,
IIncentiveVoting _voter,
address _manager,
uint256 _lockToTokenRatio
) SystemStart(_prismaCore) PrismaOwnable(_prismaCore) {
lockToken = _token;
incentiveVoter = _voter;
prismaCore = IPrismaCore(_prismaCore);
deploymentManager = _manager;
lockToTokenRatio = _lockToTokenRatio;
}
modifier notFrozen(address account) {
require(accountLockData[account].frozen == 0, "Lock is frozen");
_;
}
function setAllowPenaltyWithdrawAfter(uint256 _timestamp) external returns (bool) {
require(msg.sender == deploymentManager, "!deploymentManager");
require(allowPenaltyWithdrawAfter == 0, "Already set");
require(_timestamp > block.timestamp && _timestamp < block.timestamp + 13 weeks, "Invalid timestamp");
allowPenaltyWithdrawAfter = _timestamp;
return true;
}
function setPenaltyWithdrawalsEnabled(bool _enabled) external onlyOwner returns (bool) {
uint256 start = allowPenaltyWithdrawAfter;
require(start != 0 && block.timestamp > start, "Not yet!");
penaltyWithdrawalsEnabled = _enabled;
return true;
}
function getAccountBalances(address account) external view returns (uint256 locked, uint256 unlocked) {
AccountData storage accountData = accountLockData[account];
uint256 frozen = accountData.frozen;
unlocked = accountData.unlocked;
if (frozen > 0) {
return (frozen, unlocked);
}
locked = accountData.locked;
if (locked > 0) {
uint32[65535] storage weeklyUnlocks = accountWeeklyUnlocks[account];
uint256 accountWeek = accountData.week;
uint256 systemWeek = getWeek();
uint256 bitfield = accountData.updateWeeks[accountWeek / 256] >> (accountWeek % 256);
while (accountWeek < systemWeek) {
accountWeek++;
if (accountWeek % 256 == 0) {
bitfield = accountData.updateWeeks[accountWeek / 256];
} else {
bitfield = bitfield >> 1;
}
if (bitfield & uint256(1) == 1) {
uint256 u = weeklyUnlocks[accountWeek];
locked -= u;
unlocked += u;
if (locked == 0) break;
}
}
}
return (locked, unlocked);
}
function getAccountWeight(address account) external view returns (uint256) {
return getAccountWeightAt(account, getWeek());
}
function getAccountWeightAt(address account, uint256 week) public view returns (uint256) {
if (week > getWeek()) return 0;
uint32[65535] storage weeklyUnlocks = accountWeeklyUnlocks[account];
uint40[65535] storage weeklyWeights = accountWeeklyWeights[account];
AccountData storage accountData = accountLockData[account];
uint256 accountWeek = accountData.week;
if (accountWeek >= week) return weeklyWeights[week];
uint256 locked = accountData.locked;
uint256 weight = weeklyWeights[accountWeek];
if (locked == 0 || accountData.frozen > 0) {
return weight;
}
uint256 bitfield = accountData.updateWeeks[accountWeek / 256] >> (accountWeek % 256);
while (accountWeek < week) {
accountWeek++;
weight -= locked;
if (accountWeek % 256 == 0) {
bitfield = accountData.updateWeeks[accountWeek / 256];
} else {
bitfield = bitfield >> 1;
}
if (bitfield & uint256(1) == 1) {
uint256 amount = weeklyUnlocks[accountWeek];
locked -= amount;
if (locked == 0) break;
}
}
return weight;
}
function getAccountActiveLocks(
address account,
uint256 minWeeks
) external view returns (LockData[] memory lockData, uint256 frozenAmount) {
AccountData storage accountData = accountLockData[account];
frozenAmount = accountData.frozen;
if (frozenAmount == 0) {
if (minWeeks == 0) minWeeks = 1;
uint32[65535] storage unlocks = accountWeeklyUnlocks[account];
uint256 systemWeek = getWeek();
uint256 currentWeek = systemWeek + minWeeks;
uint256 maxLockWeek = systemWeek + MAX_LOCK_WEEKS;
uint256[] memory unlockWeeks = new uint256[](MAX_LOCK_WEEKS);
uint256 bitfield = accountData.updateWeeks[currentWeek / 256] >> (currentWeek % 256);
uint256 length;
while (currentWeek <= maxLockWeek) {
if (bitfield & uint256(1) == 1) {
unlockWeeks[length] = currentWeek;
length++;
}
currentWeek++;
if (currentWeek % 256 == 0) {
bitfield = accountData.updateWeeks[currentWeek / 256];
} else {
bitfield = bitfield >> 1;
}
}
lockData = new LockData[](length);
uint256 x = length;
for (uint256 i = 0; x != 0; i++) {
x--;
uint256 idx = unlockWeeks[x];
lockData[i] = LockData({ weeksToUnlock: idx - systemWeek, amount: unlocks[idx] });
}
}
return (lockData, frozenAmount);
}
function getWithdrawWithPenaltyAmounts(
address account,
uint256 amountToWithdraw
) external view returns (uint256 amountWithdrawn, uint256 penaltyAmountPaid) {
AccountData storage accountData = accountLockData[account];
uint32[65535] storage unlocks = accountWeeklyUnlocks[account];
if (amountToWithdraw != type(uint256).max) amountToWithdraw *= lockToTokenRatio;
uint256 unlocked = accountData.unlocked * lockToTokenRatio;
if (unlocked >= amountToWithdraw) {
return (amountToWithdraw, 0);
}
uint256 remaining = amountToWithdraw - unlocked;
uint256 penaltyTotal;
uint256 accountWeek = accountData.week;
uint256 systemWeek = getWeek();
uint256 offset = systemWeek - accountWeek;
uint256 bitfield = accountData.updateWeeks[accountWeek / 256];
for (uint256 weeksToUnlock = 1; weeksToUnlock < MAX_LOCK_WEEKS; weeksToUnlock++) {
accountWeek++;
if (accountWeek % 256 == 0) {
bitfield = accountData.updateWeeks[accountWeek / 256];
}
if ((bitfield >> (accountWeek % 256)) & uint256(1) == 1) {
uint256 lockAmount = unlocks[accountWeek] * lockToTokenRatio;
uint256 penaltyOnAmount = 0;
if (accountWeek > systemWeek) {
penaltyOnAmount = (lockAmount * (weeksToUnlock - offset)) / MAX_LOCK_WEEKS;
}
if (lockAmount - penaltyOnAmount > remaining) {
penaltyOnAmount =
(remaining * MAX_LOCK_WEEKS) /
(MAX_LOCK_WEEKS - (weeksToUnlock - offset)) -
remaining;
uint256 dust = ((penaltyOnAmount + remaining) % lockToTokenRatio);
if (dust > 0) penaltyOnAmount += lockToTokenRatio - dust;
penaltyTotal += penaltyOnAmount;
remaining = 0;
} else {
penaltyTotal += penaltyOnAmount;
remaining -= lockAmount - penaltyOnAmount;
}
if (remaining == 0) {
break;
}
}
}
amountToWithdraw -= remaining;
return (amountToWithdraw, penaltyTotal);
}
function getTotalWeight() external view returns (uint256) {
return getTotalWeightAt(getWeek());
}
function getTotalWeightAt(uint256 week) public view returns (uint256) {
uint256 systemWeek = getWeek();
if (week > systemWeek) return 0;
uint32 updatedWeek = totalUpdatedWeek;
if (week <= updatedWeek) return totalWeeklyWeights[week];
uint32 rate = totalDecayRate;
uint40 weight = totalWeeklyWeights[updatedWeek];
if (rate == 0 || updatedWeek >= systemWeek) {
return weight;
}
while (updatedWeek < systemWeek) {
updatedWeek++;
weight -= rate;
rate -= totalWeeklyUnlocks[updatedWeek];
}
return weight;
}
function getAccountWeightWrite(address account) external returns (uint256) {
return _weeklyWeightWrite(account);
}
function getTotalWeightWrite() public returns (uint256) {
uint256 week = getWeek();
uint32 rate = totalDecayRate;
uint32 updatedWeek = totalUpdatedWeek;
uint40 weight = totalWeeklyWeights[updatedWeek];
if (weight == 0) {
totalUpdatedWeek = uint16(week);
return 0;
}
while (updatedWeek < week) {
updatedWeek++;
weight -= rate;
totalWeeklyWeights[updatedWeek] = weight;
rate -= totalWeeklyUnlocks[updatedWeek];
}
totalDecayRate = rate;
totalUpdatedWeek = uint16(week);
return weight;
}
function lock(address _account, uint256 _amount, uint256 _weeks) external returns (bool) {
require(_weeks > 0, "Min 1 week");
require(_amount > 0, "Amount must be nonzero");
_lock(_account, _amount, _weeks);
lockToken.transferToLocker(msg.sender, _amount * lockToTokenRatio);
return true;
}
function _lock(address _account, uint256 _amount, uint256 _weeks) internal {
require(_weeks <= MAX_LOCK_WEEKS, "Exceeds MAX_LOCK_WEEKS");
AccountData storage accountData = accountLockData[_account];
uint256 accountWeight = _weeklyWeightWrite(_account);
uint256 totalWeight = getTotalWeightWrite();
uint256 systemWeek = getWeek();
uint256 frozen = accountData.frozen;
if (frozen > 0) {
accountData.frozen = uint32(frozen + _amount);
_weeks = MAX_LOCK_WEEKS;
} else {
if (_weeks == 1 && block.timestamp % 1 weeks > 4 days) _weeks = 2;
accountData.locked = uint32(accountData.locked + _amount);
totalDecayRate = uint32(totalDecayRate + _amount);
uint32[65535] storage unlocks = accountWeeklyUnlocks[_account];
uint256 unlockWeek = systemWeek + _weeks;
uint256 previous = unlocks[unlockWeek];
unlocks[unlockWeek] = uint32(previous + _amount);
totalWeeklyUnlocks[unlockWeek] += uint32(_amount);
if (previous == 0) {
uint256 idx = unlockWeek / 256;
uint256 bitfield = accountData.updateWeeks[idx] | (uint256(1) << (unlockWeek % 256));
accountData.updateWeeks[idx] = bitfield;
}
}
accountWeeklyWeights[_account][systemWeek] = uint40(accountWeight + _amount * _weeks);
totalWeeklyWeights[systemWeek] = uint40(totalWeight + _amount * _weeks);
emit LockCreated(_account, _amount, _weeks);
}
function extendLock(
uint256 _amount,
uint256 _weeks,
uint256 _newWeeks
) external notFrozen(msg.sender) returns (bool) {
require(_weeks > 0, "Min 1 week");
require(_newWeeks <= MAX_LOCK_WEEKS, "Exceeds MAX_LOCK_WEEKS");
require(_weeks < _newWeeks, "newWeeks must be greater than weeks");
require(_amount > 0, "Amount must be nonzero");
AccountData storage accountData = accountLockData[msg.sender];
uint256 systemWeek = getWeek();
uint256 increase = (_newWeeks - _weeks) * _amount;
uint32[65535] storage unlocks = accountWeeklyUnlocks[msg.sender];
uint256 weight = _weeklyWeightWrite(msg.sender);
accountWeeklyWeights[msg.sender][systemWeek] = uint40(weight + increase);
uint256 changedWeek = systemWeek + _weeks;
uint256 previous = unlocks[changedWeek];
unlocks[changedWeek] = uint32(previous - _amount);
totalWeeklyUnlocks[changedWeek] -= uint32(_amount);
if (previous == _amount) {
uint256 idx = changedWeek / 256;
uint256 bitfield = accountData.updateWeeks[idx] & ~(uint256(1) << (changedWeek % 256));
accountData.updateWeeks[idx] = bitfield;
}
changedWeek = systemWeek + _newWeeks;
previous = unlocks[changedWeek];
unlocks[changedWeek] = uint32(previous + _amount);
totalWeeklyUnlocks[changedWeek] += uint32(_amount);
if (previous == 0) {
uint256 idx = changedWeek / 256;
uint256 bitfield = accountData.updateWeeks[idx] | (uint256(1) << (changedWeek % 256));
accountData.updateWeeks[idx] = bitfield;
}
totalWeeklyWeights[systemWeek] = uint40(getTotalWeightWrite() + increase);
emit LockExtended(msg.sender, _amount, _weeks, _newWeeks);
return true;
}
function lockMany(address _account, LockData[] calldata newLocks) external notFrozen(_account) returns (bool) {
AccountData storage accountData = accountLockData[_account];
uint32[65535] storage unlocks = accountWeeklyUnlocks[_account];
uint256 accountWeight = _weeklyWeightWrite(_account);
uint256 systemWeek = getWeek();
uint256[2] memory bitfield = [
accountData.updateWeeks[systemWeek / 256],
accountData.updateWeeks[(systemWeek / 256) + 1]
];
uint256 increasedAmount;
uint256 increasedWeight;
uint256 length = newLocks.length;
for (uint256 i = 0; i < length; i++) {
uint256 amount = newLocks[i].amount;
uint256 week = newLocks[i].weeksToUnlock;
require(amount > 0, "Amount must be nonzero");
require(week > 0, "Min 1 week");
require(week <= MAX_LOCK_WEEKS, "Exceeds MAX_LOCK_WEEKS");
if (week == 1 && block.timestamp % 1 weeks > 4 days) week = 2;
increasedAmount += amount;
increasedWeight += amount * week;
uint256 unlockWeek = systemWeek + week;
uint256 previous = unlocks[unlockWeek];
unlocks[unlockWeek] = uint32(previous + amount);
totalWeeklyUnlocks[unlockWeek] += uint32(amount);
if (previous == 0) {
uint256 idx = (unlockWeek / 256) - (systemWeek / 256);
bitfield[idx] = bitfield[idx] | (uint256(1) << (unlockWeek % 256));
}
}
accountData.updateWeeks[systemWeek / 256] = bitfield[0];
accountData.updateWeeks[(systemWeek / 256) + 1] = bitfield[1];
lockToken.transferToLocker(msg.sender, increasedAmount * lockToTokenRatio);
accountWeeklyWeights[_account][systemWeek] = uint40(accountWeight + increasedWeight);
totalWeeklyWeights[systemWeek] = uint40(getTotalWeightWrite() + increasedWeight);
accountData.locked = uint32(accountData.locked + increasedAmount);
totalDecayRate = uint32(totalDecayRate + increasedAmount);
emit LocksCreated(_account, newLocks);
return true;
}
function extendMany(ExtendLockData[] calldata newExtendLocks) external notFrozen(msg.sender) returns (bool) {
AccountData storage accountData = accountLockData[msg.sender];
uint32[65535] storage unlocks = accountWeeklyUnlocks[msg.sender];
uint256 accountWeight = _weeklyWeightWrite(msg.sender);
uint256 systemWeek = getWeek();
uint256[2] memory bitfield = [
accountData.updateWeeks[systemWeek / 256],
accountData.updateWeeks[(systemWeek / 256) + 1]
];
uint256 increasedWeight;
uint256 length = newExtendLocks.length;
for (uint256 i = 0; i < length; i++) {
uint256 amount = newExtendLocks[i].amount;
uint256 oldWeeks = newExtendLocks[i].currentWeeks;
uint256 newWeeks = newExtendLocks[i].newWeeks;
require(oldWeeks > 0, "Min 1 week");
require(newWeeks <= MAX_LOCK_WEEKS, "Exceeds MAX_LOCK_WEEKS");
require(oldWeeks < newWeeks, "newWeeks must be greater than weeks");
require(amount > 0, "Amount must be nonzero");
increasedWeight += (newWeeks - oldWeeks) * amount;
oldWeeks += systemWeek;
uint256 previous = unlocks[oldWeeks];
unlocks[oldWeeks] = uint32(previous - amount);
totalWeeklyUnlocks[oldWeeks] -= uint32(amount);
if (previous == amount) {
uint256 idx = (oldWeeks / 256) - (systemWeek / 256);
bitfield[idx] = bitfield[idx] & ~(uint256(1) << (oldWeeks % 256));
}
newWeeks += systemWeek;
previous = unlocks[newWeeks];
unlocks[newWeeks] = uint32(previous + amount);
totalWeeklyUnlocks[newWeeks] += uint32(amount);
if (previous == 0) {
uint256 idx = (newWeeks / 256) - (systemWeek / 256);
bitfield[idx] = bitfield[idx] | (uint256(1) << (newWeeks % 256));
}
}
accountData.updateWeeks[systemWeek / 256] = bitfield[0];
accountData.updateWeeks[(systemWeek / 256) + 1] = bitfield[1];
accountWeeklyWeights[msg.sender][systemWeek] = uint40(accountWeight + increasedWeight);
totalWeeklyWeights[systemWeek] = uint40(getTotalWeightWrite() + increasedWeight);
emit LocksExtended(msg.sender, newExtendLocks);
return true;
}
function freeze() external notFrozen(msg.sender) {
AccountData storage accountData = accountLockData[msg.sender];
uint32[65535] storage unlocks = accountWeeklyUnlocks[msg.sender];
uint256 accountWeight = _weeklyWeightWrite(msg.sender);
uint256 totalWeight = getTotalWeightWrite();
uint256 locked = accountData.locked;
require(locked > 0, "No locked balance");
totalDecayRate = uint32(totalDecayRate - locked);
accountData.frozen = uint32(locked);
accountData.locked = 0;
uint256 systemWeek = getWeek();
accountWeeklyWeights[msg.sender][systemWeek] = uint40(locked * MAX_LOCK_WEEKS);
totalWeeklyWeights[systemWeek] = uint40(totalWeight - accountWeight + locked * MAX_LOCK_WEEKS);
uint256 bitfield = accountData.updateWeeks[systemWeek / 256] >> (systemWeek % 256);
while (locked > 0) {
systemWeek++;
if (systemWeek % 256 == 0) {
bitfield = accountData.updateWeeks[systemWeek / 256];
accountData.updateWeeks[(systemWeek / 256) - 1] = 0;
} else {
bitfield = bitfield >> 1;
}
if (bitfield & uint256(1) == 1) {
uint32 amount = unlocks[systemWeek];
unlocks[systemWeek] = 0;
totalWeeklyUnlocks[systemWeek] -= amount;
locked -= amount;
}
}
accountData.updateWeeks[systemWeek / 256] = 0;
emit LocksFrozen(msg.sender, locked);
}
function unfreeze(bool keepIncentivesVote) external {
AccountData storage accountData = accountLockData[msg.sender];
uint32[65535] storage unlocks = accountWeeklyUnlocks[msg.sender];
uint256 frozen = accountData.frozen;
require(frozen > 0, "Locks already unfrozen");
incentiveVoter.unfreeze(msg.sender, keepIncentivesVote);
_weeklyWeightWrite(msg.sender);
getTotalWeightWrite();
totalDecayRate = uint32(totalDecayRate + frozen);
accountData.locked = uint32(frozen);
accountData.frozen = 0;
uint256 systemWeek = getWeek();
uint256 unlockWeek = systemWeek + MAX_LOCK_WEEKS;
unlocks[unlockWeek] = uint32(frozen);
totalWeeklyUnlocks[unlockWeek] += uint32(frozen);
uint256 idx = unlockWeek / 256;
uint256 bitfield = accountData.updateWeeks[idx] | (uint256(1) << (unlockWeek % 256));
accountData.updateWeeks[idx] = bitfield;
emit LocksUnfrozen(msg.sender, frozen);
}
function withdrawExpiredLocks(uint256 _weeks) external returns (bool) {
_weeklyWeightWrite(msg.sender);
getTotalWeightWrite();
AccountData storage accountData = accountLockData[msg.sender];
uint256 unlocked = accountData.unlocked;
require(unlocked > 0, "No unlocked tokens");
accountData.unlocked = 0;
if (_weeks > 0) {
_lock(msg.sender, unlocked, _weeks);
} else {
lockToken.transfer(msg.sender, unlocked * lockToTokenRatio);
emit LocksWithdrawn(msg.sender, unlocked, 0);
}
return true;
}
function withdrawWithPenalty(uint256 amountToWithdraw) external notFrozen(msg.sender) returns (uint256) {
require(penaltyWithdrawalsEnabled, "Penalty withdrawals are disabled");
AccountData storage accountData = accountLockData[msg.sender];
uint32[65535] storage unlocks = accountWeeklyUnlocks[msg.sender];
uint256 weight = _weeklyWeightWrite(msg.sender);
if (amountToWithdraw != type(uint256).max) amountToWithdraw *= lockToTokenRatio;
uint256 unlocked = accountData.unlocked * lockToTokenRatio;
if (unlocked >= amountToWithdraw) {
accountData.unlocked = uint32((unlocked - amountToWithdraw) / lockToTokenRatio);
lockToken.transfer(msg.sender, amountToWithdraw);
return amountToWithdraw;
}
incentiveVoter.clearRegisteredWeight(msg.sender);
uint256 remaining = amountToWithdraw;
if (unlocked > 0) {
remaining -= unlocked;
accountData.unlocked = 0;
}
uint256 systemWeek = getWeek();
uint256 bitfield = accountData.updateWeeks[systemWeek / 256];
uint256 penaltyTotal;
uint256 decreasedWeight;
for (uint256 weeksToUnlock = 1; weeksToUnlock < MAX_LOCK_WEEKS; weeksToUnlock++) {
systemWeek++;
if (systemWeek % 256 == 0) {
accountData.updateWeeks[systemWeek / 256 - 1] = 0;
bitfield = accountData.updateWeeks[systemWeek / 256];
}
if ((bitfield >> (systemWeek % 256)) & uint256(1) == 1) {
uint256 lockAmount = unlocks[systemWeek] * lockToTokenRatio;
uint256 penaltyOnAmount = (lockAmount * weeksToUnlock) / MAX_LOCK_WEEKS;
if (lockAmount - penaltyOnAmount > remaining) {
penaltyOnAmount = (remaining * MAX_LOCK_WEEKS) / (MAX_LOCK_WEEKS - weeksToUnlock) - remaining;
uint256 dust = ((penaltyOnAmount + remaining) % lockToTokenRatio);
if (dust > 0) penaltyOnAmount += lockToTokenRatio - dust;
penaltyTotal += penaltyOnAmount;
uint256 lockReduceAmount = (penaltyOnAmount + remaining) / lockToTokenRatio;
decreasedWeight += lockReduceAmount * weeksToUnlock;
unlocks[systemWeek] -= uint32(lockReduceAmount);
totalWeeklyUnlocks[systemWeek] -= uint32(lockReduceAmount);
remaining = 0;
} else {
penaltyTotal += penaltyOnAmount;
decreasedWeight += (lockAmount / lockToTokenRatio) * weeksToUnlock;
bitfield = bitfield & ~(uint256(1) << (systemWeek % 256));
unlocks[systemWeek] = 0;
totalWeeklyUnlocks[systemWeek] -= uint32(lockAmount / lockToTokenRatio);
remaining -= lockAmount - penaltyOnAmount;
}
if (remaining == 0) {
break;
}
}
}
accountData.updateWeeks[systemWeek / 256] = bitfield;
if (amountToWithdraw == type(uint256).max) {
amountToWithdraw -= remaining;
} else {
require(remaining == 0, "Insufficient balance after fees");
}
accountData.locked -= uint32((amountToWithdraw + penaltyTotal - unlocked) / lockToTokenRatio);
totalDecayRate -= uint32((amountToWithdraw + penaltyTotal - unlocked) / lockToTokenRatio);
systemWeek = getWeek();
accountWeeklyWeights[msg.sender][systemWeek] = uint40(weight - decreasedWeight);
totalWeeklyWeights[systemWeek] = uint40(getTotalWeightWrite() - decreasedWeight);
lockToken.transfer(msg.sender, amountToWithdraw);
lockToken.transfer(prismaCore.feeReceiver(), penaltyTotal);
emit LocksWithdrawn(msg.sender, amountToWithdraw, penaltyTotal);
return amountToWithdraw;
}
function _weeklyWeightWrite(address account) internal returns (uint256 weight) {
AccountData storage accountData = accountLockData[account];
uint32[65535] storage weeklyUnlocks = accountWeeklyUnlocks[account];
uint40[65535] storage weeklyWeights = accountWeeklyWeights[account];
uint256 systemWeek = getWeek();
uint256 accountWeek = accountData.week;
weight = weeklyWeights[accountWeek];
if (accountWeek == systemWeek) return weight;
if (accountData.frozen > 0) {
while (systemWeek > accountWeek) {
accountWeek++;
weeklyWeights[accountWeek] = uint40(weight);
}
accountData.week = uint16(systemWeek);
return weight;
}
uint256 locked = accountData.locked;
if (locked == 0) {
if (accountWeek < systemWeek) {
accountData.week = uint16(systemWeek);
}
return 0;
}
uint256 unlocked;
uint256 bitfield = accountData.updateWeeks[accountWeek / 256] >> (accountWeek % 256);
while (accountWeek < systemWeek) {
accountWeek++;
weight -= locked;
weeklyWeights[accountWeek] = uint40(weight);
if (accountWeek % 256 == 0) {
bitfield = accountData.updateWeeks[accountWeek / 256];
} else {
bitfield = bitfield >> 1;
}
if (bitfield & uint256(1) == 1) {
uint32 amount = weeklyUnlocks[accountWeek];
locked -= amount;
unlocked += amount;
if (locked == 0) {
accountWeek = systemWeek;
break;
}
}
}
accountData.unlocked = uint32(accountData.unlocked + unlocked);
accountData.locked = uint32(locked);
accountData.week = uint16(accountWeek);
return weight;
}
}
{
"compilationTarget": {
"TokenLocker.sol": "TokenLocker"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_prismaCore","type":"address"},{"internalType":"contract IPrismaToken","name":"_token","type":"address"},{"internalType":"contract IIncentiveVoting","name":"_voter","type":"address"},{"internalType":"address","name":"_manager","type":"address"},{"internalType":"uint256","name":"_lockToTokenRatio","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_weeks","type":"uint256"}],"name":"LockCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_weeks","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newWeeks","type":"uint256"}],"name":"LockExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"weeksToUnlock","type":"uint256"}],"indexed":false,"internalType":"struct TokenLocker.LockData[]","name":"newLocks","type":"tuple[]"}],"name":"LocksCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"currentWeeks","type":"uint256"},{"internalType":"uint256","name":"newWeeks","type":"uint256"}],"indexed":false,"internalType":"struct TokenLocker.ExtendLockData[]","name":"locks","type":"tuple[]"}],"name":"LocksExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LocksFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LocksUnfrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"}],"name":"LocksWithdrawn","type":"event"},{"inputs":[],"name":"MAX_LOCK_WEEKS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRISMA_CORE","outputs":[{"internalType":"contract IPrismaCore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowPenaltyWithdrawAfter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deploymentManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_weeks","type":"uint256"},{"internalType":"uint256","name":"_newWeeks","type":"uint256"}],"name":"extendLock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"currentWeeks","type":"uint256"},{"internalType":"uint256","name":"newWeeks","type":"uint256"}],"internalType":"struct TokenLocker.ExtendLockData[]","name":"newExtendLocks","type":"tuple[]"}],"name":"extendMany","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"freeze","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"minWeeks","type":"uint256"}],"name":"getAccountActiveLocks","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"weeksToUnlock","type":"uint256"}],"internalType":"struct TokenLocker.LockData[]","name":"lockData","type":"tuple[]"},{"internalType":"uint256","name":"frozenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountBalances","outputs":[{"internalType":"uint256","name":"locked","type":"uint256"},{"internalType":"uint256","name":"unlocked","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"week","type":"uint256"}],"name":"getAccountWeightAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountWeightWrite","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getTotalWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"week","type":"uint256"}],"name":"getTotalWeightAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalWeightWrite","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getWeek","outputs":[{"internalType":"uint256","name":"week","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amountToWithdraw","type":"uint256"}],"name":"getWithdrawWithPenaltyAmounts","outputs":[{"internalType":"uint256","name":"amountWithdrawn","type":"uint256"},{"internalType":"uint256","name":"penaltyAmountPaid","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"incentiveVoter","outputs":[{"internalType":"contract IIncentiveVoting","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_weeks","type":"uint256"}],"name":"lock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"weeksToUnlock","type":"uint256"}],"internalType":"struct TokenLocker.LockData[]","name":"newLocks","type":"tuple[]"}],"name":"lockMany","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockToTokenRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockToken","outputs":[{"internalType":"contract IPrismaToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"penaltyWithdrawalsEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"prismaCore","outputs":[{"internalType":"contract IPrismaCore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"setAllowPenaltyWithdrawAfter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"setPenaltyWithdrawalsEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalDecayRate","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalUpdatedWeek","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"keepIncentivesVote","type":"bool"}],"name":"unfreeze","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_weeks","type":"uint256"}],"name":"withdrawExpiredLocks","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToWithdraw","type":"uint256"}],"name":"withdrawWithPenalty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]