编译器
0.8.16+commit.07a7930e
文件 1 的 9:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 9:IBlocklist.sol
pragma solidity ^0.8.16;
interface IBlocklist {
function isBlocked(address addr) external view returns (bool);
}
文件 3 的 9: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 的 9:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 5 的 9:IVotingEscrow.sol
pragma solidity ^0.8.16;
interface IVotingEscrow {
function createLock(uint256 _value, uint256 _unlockTime) external;
function lockFor(address account_, uint256 value_, uint256 unlockTime_) external;
function increaseAmount(uint256 _value) external;
function increaseAmountFor(address account, uint256 _value) external;
function increaseUnlockTime(uint256 _unlockTime) external;
function withdraw() external;
function delegate(address _addr) external;
function quitLock() external;
function balanceOf(address _owner) external view returns (uint256);
function balanceOfAt(address _owner, uint256 _blockNumber) external view returns (uint256);
function totalSupply() external view returns (uint256);
function totalSupplyAt(uint256 _blockNumber) external view returns (uint256);
function forceUndelegate(address _addr) external;
function lockEnd(address _addr) external view returns (uint256);
}
文件 6 的 9:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
}
文件 7 的 9:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 8 的 9:VotingEscrow.sol
pragma solidity 0.8.16;
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IVotingEscrow } from "./interfaces/IVotingEscrow.sol";
import { IBlocklist } from "./interfaces/IBlocklist.sol";
contract VotingEscrow is IVotingEscrow, ReentrancyGuard {
using SafeERC20 for IERC20;
event Deposit(
address indexed provider,
uint256 value,
uint256 locktime,
LockAction indexed action,
uint256 ts
);
event Withdraw(address indexed provider, uint256 value, LockAction indexed action, uint256 ts);
event TransferOwnership(address indexed owner);
event UpdateBlocklist(address indexed blocklist);
event UpdatePenaltyRecipient(address indexed recipient);
event CollectPenalty(uint256 amount, address indexed recipient);
event Unlock();
event QuitEnabled(bool quitEnabled);
event UpdateLockerWhitelist(address indexed addr, bool allowed);
IERC20 public immutable token;
uint256 public constant WEEK = 7 days;
uint256 public constant MAXTIME = 730 days;
uint256 public constant MULTIPLIER = 1e18;
address public owner;
address public penaltyRecipient;
uint256 public maxPenalty = 1e18;
uint256 public penaltyAccumulated;
address public blocklist;
uint256 public supply;
mapping(address => bool) public lockerWhitelist;
uint256 public globalEpoch;
Point[1000000000000000000] public pointHistory;
mapping(address => Point[1000000000]) public userPointHistory;
mapping(address => uint256) public userPointEpoch;
mapping(uint256 => int128) public slopeChanges;
mapping(address => LockedBalance) public locked;
bool public quitEnabled = false;
string public name;
string public symbol;
uint256 public immutable decimals;
struct Point {
int128 bias;
int128 slope;
uint256 ts;
uint256 blk;
}
struct LockedBalance {
int128 amount;
int128 delegated;
uint96 end;
address delegatee;
}
enum LockAction {
CREATE,
INCREASE_AMOUNT,
INCREASE_AMOUNT_AND_DELEGATION,
INCREASE_TIME,
WITHDRAW,
QUIT,
DELEGATE,
UNDELEGATE
}
constructor(
address _owner,
address _penaltyRecipient,
address _token,
string memory _name,
string memory _symbol
) {
token = IERC20(_token);
pointHistory[0] = Point({
bias: int128(0),
slope: int128(0),
ts: block.timestamp,
blk: block.number
});
decimals = IERC20Metadata(_token).decimals();
require(decimals <= 18, "Exceeds max decimals");
name = _name;
symbol = _symbol;
owner = _owner;
penaltyRecipient = _penaltyRecipient;
}
modifier checkBlocklist() {
if (blocklist != address(0))
require(!IBlocklist(blocklist).isBlocked(msg.sender), "Blocked contract");
_;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only owner");
_;
}
modifier onlyLocker() {
require(lockerWhitelist[msg.sender], "Only Whitelisted Locker");
_;
}
modifier canQuit() {
require(quitEnabled, "Quit disabled");
_;
}
function transferOwnership(address _addr) external onlyOwner {
owner = _addr;
emit TransferOwnership(_addr);
}
function updateBlocklist(address _addr) external onlyOwner {
blocklist = _addr;
emit UpdateBlocklist(_addr);
}
function updatePenaltyRecipient(address _addr) external onlyOwner {
penaltyRecipient = _addr;
emit UpdatePenaltyRecipient(_addr);
}
function setQuitEnabled(bool _quitEnabled) external onlyOwner {
quitEnabled = _quitEnabled;
emit QuitEnabled(_quitEnabled);
}
function unlock() external onlyOwner {
maxPenalty = 0;
emit Unlock();
}
function forceUndelegate(address _addr) external override {
require(msg.sender == blocklist, "Only Blocklist");
LockedBalance memory locked_ = locked[_addr];
address delegatee = locked_.delegatee;
int128 value = locked_.amount;
if (delegatee != _addr && value > 0) {
LockedBalance memory fromLocked;
locked_.delegatee = _addr;
fromLocked = locked[delegatee];
locked_.end = fromLocked.end;
_delegate(delegatee, fromLocked, value, LockAction.UNDELEGATE);
_delegate(_addr, locked_, value, LockAction.DELEGATE);
}
}
function updateLockerWhitelist(address _addr, bool allowed) external onlyOwner {
lockerWhitelist[_addr] = allowed;
emit UpdateLockerWhitelist(_addr, true);
}
function lockEnd(address _addr) external view returns (uint256) {
return locked[_addr].end;
}
function getLastUserPoint(address _addr)
external
view
returns (
int128 bias,
int128 slope,
uint256 ts
)
{
uint256 uepoch = userPointEpoch[_addr];
if (uepoch == 0) {
return (0, 0, 0);
}
Point memory point = userPointHistory[_addr][uepoch];
return (point.bias, point.slope, point.ts);
}
function _checkpoint(
address _addr,
LockedBalance memory _oldLocked,
LockedBalance memory _newLocked
) internal {
Point memory userOldPoint;
Point memory userNewPoint;
int128 oldSlopeDelta = 0;
int128 newSlopeDelta = 0;
uint256 epoch = globalEpoch;
if (_addr != address(0)) {
if (_oldLocked.end > block.timestamp && _oldLocked.delegated > 0) {
userOldPoint.slope = _oldLocked.delegated / int128(int256(MAXTIME));
userOldPoint.bias =
userOldPoint.slope *
int128(int256(_oldLocked.end - block.timestamp));
}
if (_newLocked.end > block.timestamp && _newLocked.delegated > 0) {
userNewPoint.slope = _newLocked.delegated / int128(int256(MAXTIME));
userNewPoint.bias =
userNewPoint.slope *
int128(int256(_newLocked.end - block.timestamp));
}
uint256 uEpoch = userPointEpoch[_addr];
userPointEpoch[_addr] = uEpoch + 1;
userNewPoint.ts = block.timestamp;
userNewPoint.blk = block.number;
userPointHistory[_addr][uEpoch + 1] = userNewPoint;
oldSlopeDelta = slopeChanges[_oldLocked.end];
if (_newLocked.end != 0) {
if (_newLocked.end == _oldLocked.end) {
newSlopeDelta = oldSlopeDelta;
} else {
newSlopeDelta = slopeChanges[_newLocked.end];
}
}
}
Point memory lastPoint = Point({
bias: 0,
slope: 0,
ts: block.timestamp,
blk: block.number
});
if (epoch > 0) {
lastPoint = pointHistory[epoch];
}
uint256 lastCheckpoint = lastPoint.ts;
Point memory initialLastPoint = Point({
bias: 0,
slope: 0,
ts: lastPoint.ts,
blk: lastPoint.blk
});
uint256 blockSlope = 0;
if (block.timestamp > lastPoint.ts) {
blockSlope =
(MULTIPLIER * (block.number - lastPoint.blk)) /
(block.timestamp - lastPoint.ts);
}
uint256 iterativeTime = _floorToWeek(lastCheckpoint);
for (uint256 i; i < 255; ) {
iterativeTime = iterativeTime + WEEK;
int128 dSlope = 0;
if (iterativeTime > block.timestamp) {
iterativeTime = block.timestamp;
} else {
dSlope = slopeChanges[iterativeTime];
}
int128 biasDelta = lastPoint.slope * int128(int256((iterativeTime - lastCheckpoint)));
lastPoint.bias = lastPoint.bias - biasDelta;
lastPoint.slope = lastPoint.slope + dSlope;
if (lastPoint.bias < 0) {
lastPoint.bias = 0;
}
if (lastPoint.slope < 0) {
lastPoint.slope = 0;
}
lastCheckpoint = iterativeTime;
lastPoint.ts = iterativeTime;
lastPoint.blk =
initialLastPoint.blk +
(blockSlope * (iterativeTime - initialLastPoint.ts)) /
MULTIPLIER;
epoch = epoch + 1;
if (iterativeTime == block.timestamp) {
lastPoint.blk = block.number;
break;
} else {
pointHistory[epoch] = lastPoint;
}
unchecked {
++i;
}
}
globalEpoch = epoch;
if (_addr != address(0)) {
lastPoint.slope = lastPoint.slope + userNewPoint.slope - userOldPoint.slope;
lastPoint.bias = lastPoint.bias + userNewPoint.bias - userOldPoint.bias;
if (lastPoint.slope < 0) {
lastPoint.slope = 0;
}
if (lastPoint.bias < 0) {
lastPoint.bias = 0;
}
}
pointHistory[epoch] = lastPoint;
if (_addr != address(0)) {
if (_oldLocked.end > block.timestamp) {
oldSlopeDelta = oldSlopeDelta + userOldPoint.slope;
if (_newLocked.end == _oldLocked.end) {
oldSlopeDelta = oldSlopeDelta - userNewPoint.slope;
}
slopeChanges[_oldLocked.end] = oldSlopeDelta;
}
if (_newLocked.end > block.timestamp) {
if (_newLocked.end > _oldLocked.end) {
newSlopeDelta = newSlopeDelta - userNewPoint.slope;
slopeChanges[_newLocked.end] = newSlopeDelta;
}
}
}
}
function checkpoint() external {
LockedBalance memory empty;
_checkpoint(address(0), empty, empty);
}
function createLock(uint256 _value, uint256 _unlockTime)
external
override
nonReentrant
checkBlocklist
{
_lockFor(msg.sender, _value, _unlockTime);
}
function lockFor(
address _account,
uint256 _value,
uint256 _unlockTime
) external override nonReentrant checkBlocklist onlyLocker {
_lockFor(_account, _value, _unlockTime);
}
function _lockFor(
address account,
uint256 _value,
uint256 _unlockTime
) internal {
uint256 unlock_time = _floorToWeek(_unlockTime);
LockedBalance memory locked_ = locked[account];
LockedBalance memory oldLock = _copyLock(locked_);
require(_value != 0, "Only non zero amount");
require(locked_.delegatee == account || locked_.amount == 0, "Delegated lock");
require(unlock_time >= locked_.end, "Only increase lock end");
require(unlock_time > block.timestamp, "Only future lock end");
require(unlock_time <= block.timestamp + MAXTIME, "Exceeds maxtime");
supply = supply + _value;
locked_.amount = locked_.amount + int128(int256(_value));
locked_.end = uint96(unlock_time);
locked_.delegated = locked_.delegated + int128(int256(_value));
locked_.delegatee = account;
locked[account] = locked_;
_checkpoint(account, oldLock, locked_);
token.safeTransferFrom(msg.sender, address(this), _value);
emit Deposit(account, _value, unlock_time, LockAction.CREATE, block.timestamp);
}
function increaseAmountFor(address account, uint256 _value)
external
override
nonReentrant
checkBlocklist
{
_increaseAmount(account, _value);
}
function increaseAmount(uint256 _value) external override nonReentrant checkBlocklist {
_increaseAmount(msg.sender, _value);
}
function _increaseAmount(address account, uint256 _value) internal {
LockedBalance memory locked_ = locked[account];
require(_value != 0, "Only non zero amount");
require(locked_.amount > 0, "No lock");
require(locked_.end > block.timestamp, "Lock expired");
supply = supply + _value;
address delegatee = locked_.delegatee;
uint256 unlockTime = locked_.end;
LockAction action = LockAction.INCREASE_AMOUNT;
LockedBalance memory newLocked;
if (delegatee == account) {
action = LockAction.INCREASE_AMOUNT_AND_DELEGATION;
newLocked = _copyLock(locked_);
newLocked.amount = newLocked.amount + int128(int256(_value));
newLocked.delegated = newLocked.delegated + int128(int256(_value));
locked[account] = newLocked;
} else {
locked_.amount = locked_.amount + int128(int256(_value));
locked[account] = locked_;
locked_ = locked[delegatee];
require(locked_.amount > 0, "Delegatee has no lock");
require(locked_.end > block.timestamp, "Delegatee lock expired");
newLocked = _copyLock(locked_);
newLocked.delegated = newLocked.delegated + int128(int256(_value));
locked[delegatee] = newLocked;
emit Deposit(delegatee, _value, newLocked.end, LockAction.DELEGATE, block.timestamp);
}
_checkpoint(delegatee, locked_, newLocked);
token.safeTransferFrom(msg.sender, address(this), _value);
emit Deposit(account, _value, unlockTime, action, block.timestamp);
}
function increaseUnlockTime(uint256 _unlockTime) external override nonReentrant checkBlocklist {
LockedBalance memory locked_ = locked[msg.sender];
uint256 unlock_time = _floorToWeek(_unlockTime);
require(locked_.amount > 0, "No lock");
require(locked_.end > block.timestamp, "Lock expired");
require(unlock_time > locked_.end, "Only increase lock end");
require(unlock_time <= block.timestamp + MAXTIME, "Exceeds maxtime");
uint256 oldUnlockTime = locked_.end;
locked_.end = uint96(unlock_time);
locked[msg.sender] = locked_;
if (locked_.delegated > 0) {
LockedBalance memory oldLocked = _copyLock(locked_);
oldLocked.end = uint96(oldUnlockTime);
_checkpoint(msg.sender, oldLocked, locked_);
}
emit Deposit(msg.sender, 0, unlock_time, LockAction.INCREASE_TIME, block.timestamp);
}
function withdraw() external override nonReentrant {
LockedBalance memory locked_ = locked[msg.sender];
require(locked_.amount > 0, "No lock");
require(locked_.end <= block.timestamp, "Lock not expired");
require(locked_.delegatee == msg.sender, "Lock delegated");
uint256 value = uint256(uint128(locked_.amount));
supply = supply - value;
LockedBalance memory newLocked = _copyLock(locked_);
newLocked.amount = 0;
newLocked.end = 0;
newLocked.delegated = newLocked.delegated - locked_.amount;
newLocked.delegatee = address(0);
locked[msg.sender] = newLocked;
newLocked.delegated = 0;
_checkpoint(msg.sender, locked_, newLocked);
token.safeTransfer(msg.sender, value);
emit Withdraw(msg.sender, value, LockAction.WITHDRAW, block.timestamp);
}
function delegate(address _addr) external override nonReentrant checkBlocklist {
if (_addr == msg.sender) {
_undelegate();
return;
}
LockedBalance memory locked_ = locked[msg.sender];
if (blocklist != address(0))
require(!IBlocklist(blocklist).isBlocked(_addr), "Blocked contract");
require(locked_.amount > 0, "No lock");
require(locked_.end > block.timestamp, "Lock expired");
require(locked_.delegatee != _addr, "Already delegated");
int128 value = locked_.amount;
address delegatee = locked_.delegatee;
LockedBalance memory toLocked = locked[_addr];
locked_.delegatee = _addr;
if (delegatee != msg.sender) {
locked[msg.sender] = locked_;
locked_ = locked[delegatee];
}
require(toLocked.amount > 0, "Delegatee has no lock");
require(toLocked.end > block.timestamp, "Delegatee lock expired");
require(toLocked.end >= locked_.end, "Only delegate to longer lock");
_delegate(delegatee, locked_, value, LockAction.UNDELEGATE);
_delegate(_addr, toLocked, value, LockAction.DELEGATE);
}
function _undelegate() internal {
LockedBalance memory locked_ = locked[msg.sender];
require(locked_.amount > 0, "No lock");
require(locked_.delegatee != msg.sender, "Already undelegated");
int128 value = locked_.amount;
address delegatee = locked_.delegatee;
LockedBalance memory fromLocked = locked[delegatee];
locked_.delegatee = msg.sender;
if (locked_.end < fromLocked.end) {
locked_.end = fromLocked.end;
}
_delegate(delegatee, fromLocked, value, LockAction.UNDELEGATE);
_delegate(msg.sender, locked_, value, LockAction.DELEGATE);
}
function _delegate(
address addr,
LockedBalance memory _locked,
int128 value,
LockAction action
) internal {
LockedBalance memory newLocked = _copyLock(_locked);
if (action == LockAction.DELEGATE) {
newLocked.delegated = newLocked.delegated + value;
emit Deposit(addr, uint256(int256(value)), newLocked.end, action, block.timestamp);
} else {
newLocked.delegated = newLocked.delegated - value;
emit Withdraw(addr, uint256(int256(value)), action, block.timestamp);
}
locked[addr] = newLocked;
if (newLocked.amount > 0) {
_checkpoint(addr, _locked, newLocked);
}
}
function quitLock() external override nonReentrant canQuit {
LockedBalance memory locked_ = locked[msg.sender];
require(locked_.amount > 0, "No lock");
require(locked_.end > block.timestamp, "Lock expired");
require(locked_.delegatee == msg.sender, "Lock delegated");
uint256 value = uint256(uint128(locked_.amount));
supply = supply - value;
LockedBalance memory newLocked = _copyLock(locked_);
newLocked.amount = 0;
newLocked.delegated = newLocked.delegated - locked_.amount;
newLocked.delegatee = address(0);
locked[msg.sender] = newLocked;
newLocked.end = 0;
newLocked.delegated = 0;
_checkpoint(msg.sender, locked_, newLocked);
uint256 penaltyRate = _calculatePenaltyRate(locked_.end);
uint256 penaltyAmount = (value * penaltyRate) / 1e18;
penaltyAccumulated = penaltyAccumulated + penaltyAmount;
uint256 remainingAmount = value - penaltyAmount;
token.safeTransfer(msg.sender, remainingAmount);
emit Withdraw(msg.sender, value, LockAction.QUIT, block.timestamp);
}
function getPenaltyRate(uint256 end) external view returns (uint256) {
return _calculatePenaltyRate(end);
}
function _calculatePenaltyRate(uint256 end) internal view returns (uint256) {
return ((end - block.timestamp) * maxPenalty) / MAXTIME;
}
function collectPenalty() external {
uint256 amount = penaltyAccumulated;
penaltyAccumulated = 0;
address recipient = penaltyRecipient;
token.safeTransfer(recipient, amount);
emit CollectPenalty(amount, recipient);
}
function _copyLock(LockedBalance memory _locked) internal pure returns (LockedBalance memory) {
return
LockedBalance({
amount: _locked.amount,
end: _locked.end,
delegatee: _locked.delegatee,
delegated: _locked.delegated
});
}
function _floorToWeek(uint256 _t) internal pure returns (uint256) {
return (_t / WEEK) * WEEK;
}
function _findBlockEpoch(uint256 _block, uint256 _maxEpoch) internal view returns (uint256) {
uint256 min = 0;
uint256 max = _maxEpoch;
for (uint256 i; i < 128; ) {
if (min >= max) break;
uint256 mid = (min + max + 1) / 2;
if (pointHistory[mid].blk <= _block) {
min = mid;
} else {
max = mid - 1;
}
unchecked {
++i;
}
}
return min;
}
function _findUserBlockEpoch(address _addr, uint256 _block) internal view returns (uint256) {
uint256 min = 0;
uint256 max = userPointEpoch[_addr];
for (uint256 i; i < 128; ) {
if (min >= max) {
break;
}
uint256 mid = (min + max + 1) / 2;
if (userPointHistory[_addr][mid].blk <= _block) {
min = mid;
} else {
max = mid - 1;
}
unchecked {
++i;
}
}
return min;
}
function balanceOf(address _owner) public view override returns (uint256) {
uint256 epoch = userPointEpoch[_owner];
if (epoch == 0) {
return 0;
}
Point memory lastPoint = userPointHistory[_owner][epoch];
lastPoint.bias =
lastPoint.bias -
(lastPoint.slope * int128(int256(block.timestamp - lastPoint.ts)));
if (lastPoint.bias < 0) {
lastPoint.bias = 0;
}
return uint256(uint128(lastPoint.bias));
}
function balanceOfAt(address _owner, uint256 _blockNumber)
public
view
override
returns (uint256)
{
require(_blockNumber <= block.number, "Only past block number");
uint256 userEpoch = _findUserBlockEpoch(_owner, _blockNumber);
if (userEpoch == 0) {
return 0;
}
Point memory upoint = userPointHistory[_owner][userEpoch];
uint256 maxEpoch = globalEpoch;
uint256 epoch = _findBlockEpoch(_blockNumber, maxEpoch);
Point memory point0 = pointHistory[epoch];
uint256 dBlock = 0;
uint256 dTime = 0;
if (epoch < maxEpoch) {
Point memory point1 = pointHistory[epoch + 1];
dBlock = point1.blk - point0.blk;
dTime = point1.ts - point0.ts;
} else {
dBlock = block.number - point0.blk;
dTime = block.timestamp - point0.ts;
}
uint256 blockTime = point0.ts;
if (dBlock != 0) {
blockTime = blockTime + ((dTime * (_blockNumber - point0.blk)) / dBlock);
}
upoint.bias = upoint.bias - (upoint.slope * int128(int256(blockTime - upoint.ts)));
if (upoint.bias >= 0) {
return uint256(uint128(upoint.bias));
} else {
return 0;
}
}
function _supplyAt(Point memory _point, uint256 _t) internal view returns (uint256) {
Point memory lastPoint = _point;
uint256 iterativeTime = _floorToWeek(lastPoint.ts);
for (uint256 i; i < 255; ) {
iterativeTime = iterativeTime + WEEK;
int128 dSlope = 0;
if (iterativeTime > _t) {
iterativeTime = _t;
}
else {
dSlope = slopeChanges[iterativeTime];
}
lastPoint.bias =
lastPoint.bias -
(lastPoint.slope * int128(int256(iterativeTime - lastPoint.ts)));
if (iterativeTime == _t) {
break;
}
lastPoint.slope = lastPoint.slope + dSlope;
lastPoint.ts = iterativeTime;
unchecked {
++i;
}
}
if (lastPoint.bias < 0) {
lastPoint.bias = 0;
}
return uint256(uint128(lastPoint.bias));
}
function totalSupply() public view override returns (uint256) {
uint256 epoch_ = globalEpoch;
Point memory lastPoint = pointHistory[epoch_];
return _supplyAt(lastPoint, block.timestamp);
}
function totalSupplyAt(uint256 _blockNumber) public view override returns (uint256) {
require(_blockNumber <= block.number, "Only past block number");
uint256 epoch = globalEpoch;
uint256 targetEpoch = _findBlockEpoch(_blockNumber, epoch);
Point memory point = pointHistory[targetEpoch];
if (point.blk > _blockNumber) {
return 0;
}
uint256 dTime = 0;
if (targetEpoch < epoch) {
Point memory pointNext = pointHistory[targetEpoch + 1];
if (point.blk != pointNext.blk) {
dTime =
((_blockNumber - point.blk) * (pointNext.ts - point.ts)) /
(pointNext.blk - point.blk);
}
} else if (point.blk != block.number) {
dTime =
((_blockNumber - point.blk) * (block.timestamp - point.ts)) /
(block.number - point.blk);
}
return _supplyAt(point, point.ts + dTime);
}
}
文件 9 的 9:draft-IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
{
"compilationTarget": {
"src/sector-token/src/VotingEscrow.sol": "VotingEscrow"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_penaltyRecipient","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"CollectPenalty","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"locktime","type":"uint256"},{"indexed":true,"internalType":"enum VotingEscrow.LockAction","name":"action","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"quitEnabled","type":"bool"}],"name":"QuitEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"TransferOwnership","type":"event"},{"anonymous":false,"inputs":[],"name":"Unlock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"blocklist","type":"address"}],"name":"UpdateBlocklist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"UpdateLockerWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"UpdatePenaltyRecipient","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":true,"internalType":"enum VotingEscrow.LockAction","name":"action","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"MAXTIME","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MULTIPLIER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WEEK","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_blockNumber","type":"uint256"}],"name":"balanceOfAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blocklist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectPenalty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_unlockTime","type":"uint256"}],"name":"createLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"forceUndelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"getLastUserPoint","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"end","type":"uint256"}],"name":"getPenaltyRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increaseAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increaseAmountFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_unlockTime","type":"uint256"}],"name":"increaseUnlockTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"lockEnd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_unlockTime","type":"uint256"}],"name":"lockFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"locked","outputs":[{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"int128","name":"delegated","type":"int128"},{"internalType":"uint96","name":"end","type":"uint96"},{"internalType":"address","name":"delegatee","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lockerWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxPenalty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"penaltyAccumulated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"penaltyRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pointHistory","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quitEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quitLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_quitEnabled","type":"bool"}],"name":"setQuitEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"slopeChanges","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_blockNumber","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"updateBlocklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"updateLockerWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"updatePenaltyRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userPointEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userPointHistory","outputs":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]