编译器
0.8.15+commit.e14f2714
文件 1 的 14:Address.sol
pragma solidity 0.8.15;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call(data);
return verifyCallResult(success, returndata, errorMessage);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 14:Base64.sol
pragma solidity 0.8.15;
library Base64 {
bytes internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
function encode(bytes memory data) internal pure returns (string memory) {
uint len = data.length;
if (len == 0) return "";
uint encodedLen = 4 * ((len + 2) / 3);
bytes memory result = new bytes(encodedLen + 32);
bytes memory table = TABLE;
assembly {
let tablePtr := add(table, 1)
let resultPtr := add(result, 32)
for {
let i := 0
} lt(i, len) {
} {
i := add(i, 3)
let input := and(mload(add(data, i)), 0xffffff)
let out := mload(add(tablePtr, and(shr(18, input), 0x3F)))
out := shl(8, out)
out := add(out, and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF))
out := shl(8, out)
out := add(out, and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF))
out := shl(8, out)
out := add(out, and(mload(add(tablePtr, and(input, 0x3F))), 0xFF))
out := shl(224, out)
mstore(resultPtr, out)
resultPtr := add(resultPtr, 4)
}
switch mod(len, 3)
case 1 {
mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
}
case 2 {
mstore(sub(resultPtr, 1), shl(248, 0x3d))
}
mstore(result, encodedLen)
}
return string(result);
}
}
文件 3 的 14:IController.sol
pragma solidity 0.8.15;
interface IController {
function governance() external view returns (address);
function veDist() external view returns (address);
function voter() external view returns (address);
}
文件 4 的 14:IERC165.sol
pragma solidity 0.8.15;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 5 的 14:IERC20.sol
pragma solidity 0.8.15;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, 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 sender,
address recipient,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 6 的 14:IERC721.sol
pragma solidity 0.8.15;
import "./IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
}
文件 7 的 14:IERC721Metadata.sol
pragma solidity 0.8.15;
import "./IERC721.sol";
interface IERC721Metadata is IERC721 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint tokenId) external view returns (string memory);
}
文件 8 的 14:IERC721Receiver.sol
pragma solidity 0.8.15;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 9 的 14:IVe.sol
pragma solidity 0.8.15;
interface IVe {
enum DepositType {
DEPOSIT_FOR_TYPE,
CREATE_LOCK_TYPE,
INCREASE_LOCK_AMOUNT,
INCREASE_UNLOCK_TIME,
MERGE_TYPE
}
struct Point {
int128 bias;
int128 slope;
uint ts;
uint blk;
}
struct LockedBalance {
int128 amount;
uint end;
}
function token() external view returns (address);
function balanceOfNFT(uint) external view returns (uint);
function isApprovedOrOwner(address, uint) external view returns (bool);
function createLockFor(uint, uint, address) external returns (uint);
function userPointEpoch(uint tokenId) external view returns (uint);
function epoch() external view returns (uint);
function userPointHistory(uint tokenId, uint loc) external view returns (Point memory);
function pointHistory(uint loc) external view returns (Point memory);
function checkpoint() external;
function depositFor(uint tokenId, uint value) external;
function attachToken(uint tokenId) external;
function detachToken(uint tokenId) external;
function voting(uint tokenId) external;
function abstain(uint tokenId) external;
}
文件 10 的 14:Math.sol
pragma solidity 0.8.15;
library Math {
function max(uint a, uint b) internal pure returns (uint) {
return a >= b ? a : b;
}
function min(uint a, uint b) internal pure returns (uint) {
return a < b ? a : b;
}
function positiveInt128(int128 value) internal pure returns (int128) {
return value < 0 ? int128(0) : value;
}
function closeTo(uint a, uint b, uint target) internal pure returns (bool) {
if (a > b) {
if (a - b <= target) {
return true;
}
} else {
if (b - a <= target) {
return true;
}
}
return false;
}
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
文件 11 的 14:Reentrancy.sol
pragma solidity 0.8.15;
abstract contract Reentrancy {
uint internal _unlocked = 1;
modifier lock() {
require(_unlocked == 1, "Reentrant call");
_unlocked = 2;
_;
_unlocked = 1;
}
}
文件 12 的 14:SafeERC20.sol
pragma solidity 0.8.15;
import "../interface/IERC20.sol";
import "./Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint value
) internal {
uint newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
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");
}
}
}
文件 13 的 14:Ve.sol
pragma solidity 0.8.15;
import "../../interface/IERC20.sol";
import "../../interface/IERC721.sol";
import "../../interface/IERC721Metadata.sol";
import "../../interface/IVe.sol";
import "../../interface/IERC721Receiver.sol";
import "../../interface/IController.sol";
import "../Reentrancy.sol";
import "../../lib/SafeERC20.sol";
import "../../lib/Math.sol";
import "./VeLogo.sol";
contract Ve is IERC721, IERC721Metadata, IVe, Reentrancy {
using SafeERC20 for IERC20;
uint internal constant WEEK = 1 weeks;
uint internal constant MAX_TIME = 4 * 365 * 86400;
int128 internal constant I_MAX_TIME = 4 * 365 * 86400;
uint internal constant MULTIPLIER = 1 ether;
address immutable public override token;
mapping(uint => LockedBalance) public locked;
mapping(uint => uint) public ownershipChange;
uint public override epoch;
mapping(uint => Point) internal _pointHistory;
mapping(uint => Point[1000000000]) internal _userPointHistory;
mapping(uint => uint) public override userPointEpoch;
mapping(uint => int128) public slopeChanges;
mapping(uint => uint) public attachments;
mapping(uint => bool) public voted;
address public controller;
string constant public override name = "veSLIZ";
string constant public override symbol = "veSLIZ";
string constant public version = "1.0.0";
uint8 constant public decimals = 18;
uint internal tokenId;
mapping(uint => address) internal idToOwner;
mapping(uint => address) internal idToApprovals;
mapping(address => uint) internal ownerToNFTokenCount;
mapping(address => mapping(uint => uint)) internal ownerToNFTokenIdList;
mapping(uint => uint) internal tokenToOwnerIndex;
mapping(address => mapping(address => bool)) internal ownerToOperators;
mapping(bytes4 => bool) internal supportedInterfaces;
bytes4 internal constant ERC165_INTERFACE_ID = 0x01ffc9a7;
bytes4 internal constant ERC721_INTERFACE_ID = 0x80ac58cd;
bytes4 internal constant ERC721_METADATA_INTERFACE_ID = 0x5b5e139f;
event Deposit(
address indexed provider,
uint tokenId,
uint value,
uint indexed locktime,
DepositType depositType,
uint ts
);
event Withdraw(address indexed provider, uint tokenId, uint value, uint ts);
constructor(address token_, address controller_) {
token = token_;
controller = controller_;
_pointHistory[0].blk = block.number;
_pointHistory[0].ts = block.timestamp;
supportedInterfaces[ERC165_INTERFACE_ID] = true;
supportedInterfaces[ERC721_INTERFACE_ID] = true;
supportedInterfaces[ERC721_METADATA_INTERFACE_ID] = true;
emit Transfer(address(0), address(this), tokenId);
emit Transfer(address(this), address(0), tokenId);
}
function _voter() internal view returns (address) {
return IController(controller).voter();
}
function supportsInterface(bytes4 _interfaceID) external view override returns (bool) {
return supportedInterfaces[_interfaceID];
}
function getLastUserSlope(uint _tokenId) external view returns (int128) {
uint uEpoch = userPointEpoch[_tokenId];
return _userPointHistory[_tokenId][uEpoch].slope;
}
function userPointHistoryTs(uint _tokenId, uint _idx) external view returns (uint) {
return _userPointHistory[_tokenId][_idx].ts;
}
function lockedEnd(uint _tokenId) external view returns (uint) {
return locked[_tokenId].end;
}
function _balance(address _owner) internal view returns (uint) {
return ownerToNFTokenCount[_owner];
}
function balanceOf(address _owner) external view override returns (uint) {
return _balance(_owner);
}
function ownerOf(uint _tokenId) public view override returns (address) {
return idToOwner[_tokenId];
}
function getApproved(uint _tokenId) external view override returns (address) {
return idToApprovals[_tokenId];
}
function isApprovedForAll(address _owner, address _operator) external view override returns (bool) {
return (ownerToOperators[_owner])[_operator];
}
function tokenOfOwnerByIndex(address _owner, uint _tokenIndex) external view returns (uint) {
return ownerToNFTokenIdList[_owner][_tokenIndex];
}
function _isApprovedOrOwner(address _spender, uint _tokenId) internal view returns (bool) {
address owner = idToOwner[_tokenId];
bool spenderIsOwner = owner == _spender;
bool spenderIsApproved = _spender == idToApprovals[_tokenId];
bool spenderIsApprovedForAll = (ownerToOperators[owner])[_spender];
return spenderIsOwner || spenderIsApproved || spenderIsApprovedForAll;
}
function isApprovedOrOwner(address _spender, uint _tokenId) external view override returns (bool) {
return _isApprovedOrOwner(_spender, _tokenId);
}
function _addTokenToOwnerList(address _to, uint _tokenId) internal {
uint currentCount = _balance(_to);
ownerToNFTokenIdList[_to][currentCount] = _tokenId;
tokenToOwnerIndex[_tokenId] = currentCount;
}
function _removeTokenFromOwnerList(address _from, uint _tokenId) internal {
uint currentCount = _balance(_from) - 1;
uint currentIndex = tokenToOwnerIndex[_tokenId];
if (currentCount == currentIndex) {
ownerToNFTokenIdList[_from][currentCount] = 0;
tokenToOwnerIndex[_tokenId] = 0;
} else {
uint lastTokenId = ownerToNFTokenIdList[_from][currentCount];
ownerToNFTokenIdList[_from][currentIndex] = lastTokenId;
tokenToOwnerIndex[lastTokenId] = currentIndex;
ownerToNFTokenIdList[_from][currentCount] = 0;
tokenToOwnerIndex[_tokenId] = 0;
}
}
function _addTokenTo(address _to, uint _tokenId) internal {
idToOwner[_tokenId] = _to;
_addTokenToOwnerList(_to, _tokenId);
ownerToNFTokenCount[_to] += 1;
}
function _removeTokenFrom(address _from, uint _tokenId) internal {
require(idToOwner[_tokenId] == _from, "!owner remove");
idToOwner[_tokenId] = address(0);
_removeTokenFromOwnerList(_from, _tokenId);
ownerToNFTokenCount[_from] -= 1;
}
function _transferFrom(
address _from,
address _to,
uint _tokenId,
address _sender
) internal {
require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");
require(_isApprovedOrOwner(_sender, _tokenId), "!owner sender");
require(_to != address(0), "dst is zero");
if (idToApprovals[_tokenId] != address(0)) {
idToApprovals[_tokenId] = address(0);
}
_removeTokenFrom(_from, _tokenId);
_addTokenTo(_to, _tokenId);
ownershipChange[_tokenId] = block.number;
emit Transfer(_from, _to, _tokenId);
}
function transferFrom(
address _from,
address _to,
uint _tokenId
) external override {
_transferFrom(_from, _to, _tokenId, msg.sender);
}
function _isContract(address account) internal view returns (bool) {
uint size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function safeTransferFrom(
address _from,
address _to,
uint _tokenId,
bytes memory _data
) public override {
_transferFrom(_from, _to, _tokenId, msg.sender);
if (_isContract(_to)) {
try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) returns (bytes4) {} catch (
bytes memory reason
) {
if (reason.length == 0) {
revert('ERC721: transfer to non ERC721Receiver implementer');
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
function safeTransferFrom(
address _from,
address _to,
uint _tokenId
) external override {
safeTransferFrom(_from, _to, _tokenId, '');
}
function approve(address _approved, uint _tokenId) public override {
address owner = idToOwner[_tokenId];
require(owner != address(0), "invalid id");
require(_approved != owner, "self approve");
bool senderIsOwner = (idToOwner[_tokenId] == msg.sender);
bool senderIsApprovedForAll = (ownerToOperators[owner])[msg.sender];
require(senderIsOwner || senderIsApprovedForAll, "!owner");
idToApprovals[_tokenId] = _approved;
emit Approval(owner, _approved, _tokenId);
}
function setApprovalForAll(address _operator, bool _approved) external override {
require(_operator != msg.sender, "operator is sender");
ownerToOperators[msg.sender][_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
function _mint(address _to, uint _tokenId) internal returns (bool) {
require(_to != address(0), "zero dst");
_addTokenTo(_to, _tokenId);
emit Transfer(address(0), _to, _tokenId);
return true;
}
function _checkpoint(
uint _tokenId,
LockedBalance memory oldLocked,
LockedBalance memory newLocked
) internal {
Point memory uOld;
Point memory uNew;
int128 oldDSlope = 0;
int128 newDSlope = 0;
uint _epoch = epoch;
if (_tokenId != 0) {
if (oldLocked.end > block.timestamp && oldLocked.amount > 0) {
uOld.slope = oldLocked.amount / I_MAX_TIME;
uOld.bias = uOld.slope * int128(int256(oldLocked.end - block.timestamp));
}
if (newLocked.end > block.timestamp && newLocked.amount > 0) {
uNew.slope = newLocked.amount / I_MAX_TIME;
uNew.bias = uNew.slope * int128(int256(newLocked.end - block.timestamp));
}
oldDSlope = slopeChanges[oldLocked.end];
if (newLocked.end != 0) {
if (newLocked.end == oldLocked.end) {
newDSlope = oldDSlope;
} else {
newDSlope = slopeChanges[newLocked.end];
}
}
}
Point memory lastPoint = Point({bias : 0, slope : 0, ts : block.timestamp, blk : block.number});
if (_epoch > 0) {
lastPoint = _pointHistory[_epoch];
}
uint lastCheckpoint = lastPoint.ts;
Point memory initialLastPoint = lastPoint;
uint blockSlope = 0;
if (block.timestamp > lastPoint.ts) {
blockSlope = (MULTIPLIER * (block.number - lastPoint.blk)) / (block.timestamp - lastPoint.ts);
}
{
uint ti = (lastCheckpoint / WEEK) * WEEK;
for (uint i = 0; i < 255; ++i) {
ti += WEEK;
int128 dSlope = 0;
if (ti > block.timestamp) {
ti = block.timestamp;
} else {
dSlope = slopeChanges[ti];
}
lastPoint.bias = Math.positiveInt128(lastPoint.bias - lastPoint.slope * int128(int256(ti - lastCheckpoint)));
lastPoint.slope = Math.positiveInt128(lastPoint.slope + dSlope);
lastCheckpoint = ti;
lastPoint.ts = ti;
lastPoint.blk = initialLastPoint.blk + (blockSlope * (ti - initialLastPoint.ts)) / MULTIPLIER;
_epoch += 1;
if (ti == block.timestamp) {
lastPoint.blk = block.number;
break;
} else {
_pointHistory[_epoch] = lastPoint;
}
}
}
epoch = _epoch;
if (_tokenId != 0) {
lastPoint.slope = Math.positiveInt128(lastPoint.slope + (uNew.slope - uOld.slope));
lastPoint.bias = Math.positiveInt128(lastPoint.bias + (uNew.bias - uOld.bias));
}
_pointHistory[_epoch] = lastPoint;
if (_tokenId != 0) {
if (oldLocked.end > block.timestamp) {
oldDSlope += uOld.slope;
if (newLocked.end == oldLocked.end) {
oldDSlope -= uNew.slope;
}
slopeChanges[oldLocked.end] = oldDSlope;
}
if (newLocked.end > block.timestamp) {
if (newLocked.end > oldLocked.end) {
newDSlope -= uNew.slope;
slopeChanges[newLocked.end] = newDSlope;
}
}
uint userEpoch = userPointEpoch[_tokenId] + 1;
userPointEpoch[_tokenId] = userEpoch;
uNew.ts = block.timestamp;
uNew.blk = block.number;
_userPointHistory[_tokenId][userEpoch] = uNew;
}
}
function _depositFor(
uint _tokenId,
uint _value,
uint unlockTime,
LockedBalance memory lockedBalance,
DepositType depositType
) internal {
LockedBalance memory _locked = lockedBalance;
LockedBalance memory oldLocked;
(oldLocked.amount, oldLocked.end) = (_locked.amount, _locked.end);
_locked.amount += int128(int256(_value));
if (unlockTime != 0) {
_locked.end = unlockTime;
}
locked[_tokenId] = _locked;
_checkpoint(_tokenId, oldLocked, _locked);
address from = msg.sender;
if (_value != 0 && depositType != DepositType.MERGE_TYPE) {
IERC20(token).safeTransferFrom(from, address(this), _value);
}
emit Deposit(from, _tokenId, _value, _locked.end, depositType, block.timestamp);
}
function voting(uint _tokenId) external override {
require(msg.sender == _voter(), "!voter");
voted[_tokenId] = true;
}
function abstain(uint _tokenId) external override {
require(msg.sender == _voter(), "!voter");
voted[_tokenId] = false;
}
function attachToken(uint _tokenId) external override {
require(msg.sender == _voter(), "!voter");
attachments[_tokenId] = attachments[_tokenId] + 1;
}
function detachToken(uint _tokenId) external override {
require(msg.sender == _voter(), "!voter");
attachments[_tokenId] = attachments[_tokenId] - 1;
}
function merge(uint _from, uint _to) external {
require(attachments[_from] == 0 && !voted[_from], "attached");
require(_from != _to, "the same");
require(_isApprovedOrOwner(msg.sender, _from), "!owner from");
require(_isApprovedOrOwner(msg.sender, _to), "!owner to");
LockedBalance memory _locked0 = locked[_from];
LockedBalance memory _locked1 = locked[_to];
uint value0 = uint(int256(_locked0.amount));
uint end = _locked0.end >= _locked1.end ? _locked0.end : _locked1.end;
locked[_from] = LockedBalance(0, 0);
_checkpoint(_from, _locked0, LockedBalance(0, 0));
_burn(_from);
_depositFor(_to, value0, end, _locked1, DepositType.MERGE_TYPE);
}
function block_number() external view returns (uint) {
return block.number;
}
function checkpoint() external override {
_checkpoint(0, LockedBalance(0, 0), LockedBalance(0, 0));
}
function depositFor(uint _tokenId, uint _value) external lock override {
require(_value > 0, "zero value");
LockedBalance memory _locked = locked[_tokenId];
require(_locked.amount > 0, 'No existing lock found');
require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');
_depositFor(_tokenId, _value, 0, _locked, DepositType.DEPOSIT_FOR_TYPE);
}
function _createLock(uint _value, uint _lockDuration, address _to) internal returns (uint) {
require(_value > 0, "zero value");
uint unlockTime = (block.timestamp + _lockDuration) / WEEK * WEEK;
require(unlockTime > block.timestamp, 'Can only lock until time in the future');
require(unlockTime <= block.timestamp + MAX_TIME, 'Voting lock can be 4 years max');
++tokenId;
uint _tokenId = tokenId;
_mint(_to, _tokenId);
_depositFor(_tokenId, _value, unlockTime, locked[_tokenId], DepositType.CREATE_LOCK_TYPE);
return _tokenId;
}
function createLockFor(uint _value, uint _lockDuration, address _to)
external lock override returns (uint) {
return _createLock(_value, _lockDuration, _to);
}
function createLock(uint _value, uint _lockDuration) external lock returns (uint) {
return _createLock(_value, _lockDuration, msg.sender);
}
function increaseAmount(uint _tokenId, uint _value) external lock {
LockedBalance memory _locked = locked[_tokenId];
require(_locked.amount > 0, 'No existing lock found');
require(_locked.end > block.timestamp, 'Cannot add to expired lock. Withdraw');
require(_isApprovedOrOwner(msg.sender, _tokenId), "!owner");
require(_value > 0, "zero value");
_depositFor(_tokenId, _value, 0, _locked, DepositType.INCREASE_LOCK_AMOUNT);
}
function increaseUnlockTime(uint _tokenId, uint _lockDuration) external lock {
LockedBalance memory _locked = locked[_tokenId];
uint unlockTime = (block.timestamp + _lockDuration) / WEEK * WEEK;
require(_locked.amount > 0, 'Nothing is locked');
require(_locked.end > block.timestamp, 'Lock expired');
require(unlockTime > _locked.end, 'Can only increase lock duration');
require(unlockTime <= block.timestamp + MAX_TIME, 'Voting lock can be 4 years max');
require(_isApprovedOrOwner(msg.sender, _tokenId), "!owner");
_depositFor(_tokenId, 0, unlockTime, _locked, DepositType.INCREASE_UNLOCK_TIME);
}
function withdraw(uint _tokenId) external lock {
require(_isApprovedOrOwner(msg.sender, _tokenId), "!owner");
require(attachments[_tokenId] == 0 && !voted[_tokenId], "attached");
LockedBalance memory _locked = locked[_tokenId];
require(block.timestamp >= _locked.end, "The lock did not expire");
uint value = uint(int256(_locked.amount));
locked[_tokenId] = LockedBalance(0, 0);
_checkpoint(_tokenId, _locked, LockedBalance(0, 0));
IERC20(token).safeTransfer(msg.sender, value);
_burn(_tokenId);
emit Withdraw(msg.sender, _tokenId, value, block.timestamp);
}
function _findBlockEpoch(uint _block, uint maxEpoch) internal view returns (uint) {
uint _min = 0;
uint _max = maxEpoch;
for (uint i = 0; i < 128; ++i) {
if (_min >= _max) {
break;
}
uint _mid = (_min + _max + 1) / 2;
if (_pointHistory[_mid].blk <= _block) {
_min = _mid;
} else {
_max = _mid - 1;
}
}
return _min;
}
function _balanceOfNFT(uint _tokenId, uint _t) internal view returns (uint) {
uint _epoch = userPointEpoch[_tokenId];
if (_epoch == 0) {
return 0;
} else {
Point memory lastPoint = _userPointHistory[_tokenId][_epoch];
lastPoint.bias -= lastPoint.slope * int128(int256(_t) - int256(lastPoint.ts));
if (lastPoint.bias < 0) {
lastPoint.bias = 0;
}
return uint(int256(lastPoint.bias));
}
}
function tokenURI(uint _tokenId) external view override returns (string memory) {
require(idToOwner[_tokenId] != address(0), "Query for nonexistent token");
LockedBalance memory _locked = locked[_tokenId];
return
_tokenURI(
_tokenId,
_balanceOfNFT(_tokenId, block.timestamp),
_locked.end,
uint(int256(_locked.amount))
);
}
function balanceOfNFT(uint _tokenId) external view override returns (uint) {
if (ownershipChange[_tokenId] == block.number) {
return 0;
}
return _balanceOfNFT(_tokenId, block.timestamp);
}
function balanceOfNFTAt(uint _tokenId, uint _t) external view returns (uint) {
return _balanceOfNFT(_tokenId, _t);
}
function _balanceOfAtNFT(uint _tokenId, uint _block) internal view returns (uint) {
require(_block <= block.number, "only old block");
uint _min = 0;
uint _max = userPointEpoch[_tokenId];
for (uint i = 0; i < 128; ++i) {
if (_min >= _max) {
break;
}
uint _mid = (_min + _max + 1) / 2;
if (_userPointHistory[_tokenId][_mid].blk <= _block) {
_min = _mid;
} else {
_max = _mid - 1;
}
}
Point memory uPoint = _userPointHistory[_tokenId][_min];
uint maxEpoch = epoch;
uint _epoch = _findBlockEpoch(_block, maxEpoch);
Point memory point0 = _pointHistory[_epoch];
uint dBlock = 0;
uint dt = 0;
if (_epoch < maxEpoch) {
Point memory point1 = _pointHistory[_epoch + 1];
dBlock = point1.blk - point0.blk;
dt = point1.ts - point0.ts;
} else {
dBlock = block.number - point0.blk;
dt = block.timestamp - point0.ts;
}
uint blockTime = point0.ts;
if (dBlock != 0 && _block > point0.blk) {
blockTime += (dt * (_block - point0.blk)) / dBlock;
}
uPoint.bias -= uPoint.slope * int128(int256(blockTime - uPoint.ts));
return uint(uint128(Math.positiveInt128(uPoint.bias)));
}
function balanceOfAtNFT(uint _tokenId, uint _block) external view returns (uint) {
return _balanceOfAtNFT(_tokenId, _block);
}
function _supplyAt(Point memory point, uint t) internal view returns (uint) {
Point memory lastPoint = point;
uint ti = (lastPoint.ts / WEEK) * WEEK;
for (uint i = 0; i < 255; ++i) {
ti += WEEK;
int128 dSlope = 0;
if (ti > t) {
ti = t;
} else {
dSlope = slopeChanges[ti];
}
lastPoint.bias -= lastPoint.slope * int128(int256(ti - lastPoint.ts));
if (ti == t) {
break;
}
lastPoint.slope += dSlope;
lastPoint.ts = ti;
}
return uint(uint128(Math.positiveInt128(lastPoint.bias)));
}
function totalSupplyAtT(uint t) public view returns (uint) {
uint _epoch = epoch;
Point memory lastPoint = _pointHistory[_epoch];
return _supplyAt(lastPoint, t);
}
function totalSupply() external view returns (uint) {
return totalSupplyAtT(block.timestamp);
}
function totalSupplyAt(uint _block) external view returns (uint) {
require(_block <= block.number, "only old blocks");
uint _epoch = epoch;
uint targetEpoch = _findBlockEpoch(_block, _epoch);
Point memory point = _pointHistory[targetEpoch];
if (point.blk > _block) {
return 0;
}
uint dt = 0;
if (targetEpoch < _epoch) {
Point memory point_next = _pointHistory[targetEpoch + 1];
dt = ((_block - point.blk) * (point_next.ts - point.ts)) / (point_next.blk - point.blk);
} else {
if (point.blk != block.number) {
dt = ((_block - point.blk) * (block.timestamp - point.ts)) / (block.number - point.blk);
}
}
return _supplyAt(point, point.ts + dt);
}
function _tokenURI(uint _tokenId, uint _balanceOf, uint _lockedEnd, uint _value) internal view returns (string memory output) {
uint untilEnd = (block.timestamp < _lockedEnd) ? _lockedEnd - block.timestamp : 0;
return VeLogo.tokenURI(_tokenId, _balanceOf, untilEnd, _value);
}
function _burn(uint _tokenId) internal {
address owner = ownerOf(_tokenId);
approve(address(0), _tokenId);
_removeTokenFrom(msg.sender, _tokenId);
emit Transfer(owner, address(0), _tokenId);
}
function userPointHistory(uint _tokenId, uint _loc) external view override returns (Point memory) {
return _userPointHistory[_tokenId][_loc];
}
function pointHistory(uint _loc) external view override returns (Point memory) {
return _pointHistory[_loc];
}
}
文件 14 的 14:VeLogo.sol
pragma solidity 0.8.15;
import "./../../lib/Base64.sol";
library VeLogo {
function tokenURI(uint _tokenId, uint _balanceOf, uint untilEnd, uint _value) public pure returns (string memory output) {
output = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 900"><style>.b{fill:#4F6295;}.g{fill:#D3F85A;}.f{fill:#D0DA55;}.w{fill:#FFFFFF;}.s{font-size:37px;}</style><rect fill="#2B3A5B" width="600" height="900"/><rect class="b" x="55" y="424" width="544" height="98"/><rect class="b" x="0" y="544" width="517" height="98"/><rect class="b" x="0" y="772" width="516" height="98"/><rect class="b" x="55" y="658" width="544" height="98"/><path class="g" d="M62.2,419.7v97.8c0,0.5,0.4,0.9,0.9,0.9H600v-1.8H64v-96h536v-1.8H63.1C62.6,418.8,62.2,419.2,62.2,419.7z"/><path class="g" d="M62.2,651.8v97.8c0,0.5,0.4,0.9,0.9,0.9H600v-1.8H64v-96h536v-1.8H63.1C62.6,650.9,62.2,651.3,62.2,651.8z"/><path class="g" d="M512.3,636.3v-97.8c0-0.5-0.4-0.9-0.9-0.9H0v1.8h510.5v96H0v1.8h511.4C511.9,637.2,512.3,636.8,512.3,636.3z"/><path class="g" d="M512.3,863.8V766c0-0.5-0.4-0.9-0.9-0.9H0v1.8h510.5v96H0v1.8h511.4C511.9,864.7,512.3,864.3,512.3,863.8z"/>';
output = string(abi.encodePacked(output, '<text transform="matrix(1 0 0 1 88 463)" class="f s">ID:</text><text transform="matrix(1 0 0 1 88 502)" class="w s">', _toString(_tokenId), '</text>'));
output = string(abi.encodePacked(output, '<text transform="matrix(1 0 0 1 88 579)" class="f s">Balance:</text><text transform="matrix(1 0 0 1 88 618)" class="w s">', _toString(_balanceOf / 1e18), '</text>'));
output = string(abi.encodePacked(output, '<text transform="matrix(1 0 0 1 88 694)" class="f s">Until unlock:</text><text transform="matrix(1 0 0 1 88 733)" class="w s">', _toString(untilEnd / 60 / 60 / 24), ' days</text>'));
output = string(abi.encodePacked(output, '<text transform="matrix(1 0 0 1 88 804)" class="f s">Power:</text><text transform="matrix(1 0 0 1 88 843)" class="w s">', _toString(_value / 1e18), '</text></svg>'));
string memory json = Base64.encode(bytes(string(abi.encodePacked('{"name": "veSLIZ #', _toString(_tokenId), '", "description": "Locked LIZARD tokens", "image": "data:image/svg+xml;base64,', Base64.encode(bytes(output)), '"}'))));
output = string(abi.encodePacked('data:application/json;base64,', json));
}
function _toString(uint value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint temp = value;
uint digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint(value % 10)));
value /= 10;
}
return string(buffer);
}
}
{
"compilationTarget": {
"contracts/base/vote/Ve.sol": "Ve"
},
"evmVersion": "london",
"libraries": {
"contracts/base/vote/VeLogo.sol:VeLogo": "0xf699eddeac1541e7202c14f6c4d656eee88ff064"
},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"controller_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"locktime","type":"uint256"},{"indexed":false,"internalType":"enum IVe.DepositType","name":"depositType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"abstain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_approved","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"attachToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"attachments","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":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"balanceOfAtNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"balanceOfNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_t","type":"uint256"}],"name":"balanceOfNFTAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"block_number","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpoint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lockDuration","type":"uint256"}],"name":"createLock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_lockDuration","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"createLockFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"depositFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"detachToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getLastUserSlope","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increaseAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_lockDuration","type":"uint256"}],"name":"increaseUnlockTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"isApprovedOrOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"locked","outputs":[{"internalType":"int128","name":"amount","type":"int128"},{"internalType":"uint256","name":"end","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"lockedEnd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"}],"name":"merge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ownershipChange","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loc","type":"uint256"}],"name":"pointHistory","outputs":[{"components":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"internalType":"struct IVe.Point","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"slopeChanges","outputs":[{"internalType":"int128","name":"","type":"int128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_tokenIndex","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_block","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"t","type":"uint256"}],"name":"totalSupplyAtT","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":"_tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"userPointEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_loc","type":"uint256"}],"name":"userPointHistory","outputs":[{"components":[{"internalType":"int128","name":"bias","type":"int128"},{"internalType":"int128","name":"slope","type":"int128"},{"internalType":"uint256","name":"ts","type":"uint256"},{"internalType":"uint256","name":"blk","type":"uint256"}],"internalType":"struct IVe.Point","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"userPointHistoryTs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"voted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"voting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]