文件 1 的 19: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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(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 的 19:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 3 的 19:Counters.sol
pragma solidity ^0.8.0;
library Counters {
struct Counter {
uint256 _value;
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
unchecked {
counter._value += 1;
}
}
function decrement(Counter storage counter) internal {
uint256 value = counter._value;
require(value > 0, "Counter: decrement overflow");
unchecked {
counter._value = value - 1;
}
}
function reset(Counter storage counter) internal {
counter._value = 0;
}
}
文件 4 的 19:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 5 的 19:ERC721.sol
pragma solidity ^0.8.0;
import "./IERC721.sol";
import "./IERC721Receiver.sol";
import "./extensions/IERC721Metadata.sol";
import "../../utils/Address.sol";
import "../../utils/Context.sol";
import "../../utils/Strings.sol";
import "../../utils/introspection/ERC165.sol";
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
using Address for address;
using Strings for uint256;
string private _name;
string private _symbol;
mapping(uint256 => address) private _owners;
mapping(address => uint256) private _balances;
mapping(uint256 => address) private _tokenApprovals;
mapping(address => mapping(address => bool)) private _operatorApprovals;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: address zero is not a valid owner");
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "ERC721: invalid token ID");
return owner;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
_requireMinted(tokenId);
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
function _baseURI() internal view virtual returns (string memory) {
return "";
}
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(
_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not token owner nor approved for all"
);
_approve(to, tokenId);
}
function getApproved(uint256 tokenId) public view virtual override returns (address) {
_requireMinted(tokenId);
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address operator, bool approved) public virtual override {
_setApprovalForAll(_msgSender(), operator, approved);
}
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
_transfer(from, to, tokenId);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner nor approved");
_safeTransfer(from, to, tokenId, data);
}
function _safeTransfer(
address from,
address to,
uint256 tokenId,
bytes memory data
) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
}
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _owners[tokenId] != address(0);
}
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
}
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
function _safeMint(
address to,
uint256 tokenId,
bytes memory data
) internal virtual {
_mint(to, tokenId);
require(
_checkOnERC721Received(address(0), to, tokenId, data),
"ERC721: transfer to non ERC721Receiver implementer"
);
}
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
_afterTokenTransfer(address(0), to, tokenId);
}
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId);
_approve(address(0), tokenId);
_balances[owner] -= 1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
_afterTokenTransfer(owner, address(0), tokenId);
}
function _transfer(
address from,
address to,
uint256 tokenId
) internal virtual {
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
_approve(address(0), tokenId);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
_afterTokenTransfer(from, to, tokenId);
}
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
}
function _setApprovalForAll(
address owner,
address operator,
bool approved
) internal virtual {
require(owner != operator, "ERC721: approve to caller");
_operatorApprovals[owner][operator] = approved;
emit ApprovalForAll(owner, operator, approved);
}
function _requireMinted(uint256 tokenId) internal view virtual {
require(_exists(tokenId), "ERC721: invalid token ID");
}
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory data
) private returns (bool) {
if (to.isContract()) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
return retval == IERC721Receiver.onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual {}
function _afterTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual {}
}
文件 6 的 19:ERC721Enumerable.sol
pragma solidity ^0.8.0;
import "../ERC721.sol";
import "./IERC721Enumerable.sol";
abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
mapping(uint256 => uint256) private _ownedTokensIndex;
uint256[] private _allTokens;
mapping(uint256 => uint256) private _allTokensIndex;
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
}
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
return _ownedTokens[owner][index];
}
function totalSupply() public view virtual override returns (uint256) {
return _allTokens.length;
}
function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
return _allTokens[index];
}
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override {
super._beforeTokenTransfer(from, to, tokenId);
if (from == address(0)) {
_addTokenToAllTokensEnumeration(tokenId);
} else if (from != to) {
_removeTokenFromOwnerEnumeration(from, tokenId);
}
if (to == address(0)) {
_removeTokenFromAllTokensEnumeration(tokenId);
} else if (to != from) {
_addTokenToOwnerEnumeration(to, tokenId);
}
}
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
uint256 length = ERC721.balanceOf(to);
_ownedTokens[to][length] = tokenId;
_ownedTokensIndex[tokenId] = length;
}
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
uint256 tokenIndex = _ownedTokensIndex[tokenId];
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId;
_ownedTokensIndex[lastTokenId] = tokenIndex;
}
delete _ownedTokensIndex[tokenId];
delete _ownedTokens[from][lastTokenIndex];
}
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
uint256 lastTokenIndex = _allTokens.length - 1;
uint256 tokenIndex = _allTokensIndex[tokenId];
uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId;
_allTokensIndex[lastTokenId] = tokenIndex;
delete _allTokensIndex[tokenId];
_allTokens.pop();
}
}
文件 7 的 19:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 8 的 19: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);
}
文件 9 的 19:IERC721.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/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,
bytes calldata data
) external;
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 setApprovalForAll(address operator, bool _approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 10 的 19:IERC721Enumerable.sol
pragma solidity ^0.8.0;
import "../IERC721.sol";
interface IERC721Enumerable is IERC721 {
function totalSupply() external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
function tokenByIndex(uint256 index) external view returns (uint256);
}
文件 11 的 19:IERC721Metadata.sol
pragma solidity ^0.8.0;
import "../IERC721.sol";
interface IERC721Metadata is IERC721 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
文件 12 的 19:IERC721Receiver.sol
pragma solidity ^0.8.0;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 13 的 19:IStakeRewards.sol
pragma solidity ^0.8.4;
interface IStakeRewards {
function claimReward(bool compound) external;
function depositRewards() external payable;
function getShares(address wallet) external view returns (uint256);
function setShare(
address shareholder,
uint256 balanceUpdate,
bool isRemoving
) external;
}
文件 14 的 19:IYDF.sol
pragma solidity ^0.8.9;
import '@openzeppelin/contracts/interfaces/IERC20.sol';
interface IYDF is IERC20 {
function addToBuyTracker(address _user, uint256 _amount) external;
function burn(uint256 _amount) external;
function stakeMintToVester(uint256 _amount) external;
}
文件 15 的 19:IYDFVester.sol
pragma solidity ^0.8.9;
interface IYDFVester {
function createVest(address user, uint256 amount) external;
}
文件 16 的 19:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 17 的 19:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
function toString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
文件 18 的 19:YDFStake.sol
pragma solidity ^0.8.9;
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/interfaces/IERC20.sol';
import '@openzeppelin/contracts/token/ERC721/ERC721.sol';
import '@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol';
import '@openzeppelin/contracts/utils/Counters.sol';
import './interfaces/IYDF.sol';
import './interfaces/IYDFVester.sol';
import './interfaces/IStakeRewards.sol';
contract YDFStake is ERC721Enumerable, Ownable {
using Strings for uint256;
using Counters for Counters.Counter;
uint256 private constant ONE_YEAR = 365 days;
uint256 private constant ONE_WEEK = 7 days;
uint16 private constant PERCENT_DENOMENATOR = 10000;
IERC20 internal stakeToken;
IYDF internal ydf;
IYDFVester internal vester;
IStakeRewards internal rewards;
struct AprLock {
uint16 apr;
uint256 lockTime;
}
AprLock[] internal _aprLockOptions;
struct Stake {
uint256 created;
uint256 amountStaked;
uint256 amountYDFBaseEarn;
uint16 apr;
uint256 lockTime;
}
mapping(uint256 => Stake) public stakes;
mapping(uint256 => uint256) public yieldClaimed;
mapping(uint256 => uint256) public lastClaim;
mapping(uint256 => bool) public isBlacklisted;
Counters.Counter internal _ids;
string private baseTokenURI;
address public paymentAddress;
address public royaltyAddress;
uint256 private royaltyBasisPoints = 50;
mapping(address => uint256[]) public allUserOwned;
mapping(uint256 => uint256) public ownedIndex;
mapping(uint256 => uint256) public tokenMintedAt;
mapping(uint256 => uint256) public tokenLastTransferred;
event StakeTokens(
address indexed user,
uint256 indexed tokenId,
uint256 amountStaked,
uint256 lockOptionIndex
);
event UnstakeTokens(address indexed user, uint256 indexed tokenId);
event SetAnnualApr(uint256 indexed newApr);
event SetPaymentAddress(address indexed user);
event SetRoyaltyAddress(address indexed user);
event SetRoyaltyBasisPoints(uint256 indexed _royaltyBasisPoints);
event SetBaseTokenURI(string indexed newUri);
event AddAprLockOption(uint16 indexed apr, uint256 lockTime);
event RemoveAprLockOption(
uint256 indexed index,
uint16 indexed apr,
uint256 lockTime
);
event UpdateAprLockOption(
uint256 indexed index,
uint16 indexed oldApr,
uint256 oldLockTime,
uint16 newApr,
uint256 newLockTime
);
event SetTokenBlacklist(uint256 indexed tokenId, bool isBlacklisted);
constructor(
string memory _name,
string memory _symbol,
address _stakeToken,
address _ydf,
address _vester,
address _rewards,
string memory _baseTokenURI
) ERC721(_name, _symbol) {
stakeToken = IERC20(_stakeToken);
ydf = IYDF(_ydf);
vester = IYDFVester(_vester);
rewards = IStakeRewards(_rewards);
baseTokenURI = _baseTokenURI;
}
function stake(uint256 _amount, uint256 _lockOptIndex) external virtual {
_stake(msg.sender, _amount, _amount, _lockOptIndex, true);
}
function _stake(
address _user,
uint256 _amountStaked,
uint256 _amountYDFBaseEarn,
uint256 _lockOptIndex,
bool _transferStakeToken
) internal {
require(_lockOptIndex < _aprLockOptions.length, 'invalid lock option');
_amountStaked = _amountStaked == 0
? stakeToken.balanceOf(_user)
: _amountStaked;
_amountYDFBaseEarn = _amountYDFBaseEarn == 0
? _amountStaked
: _amountYDFBaseEarn;
require(
_amountStaked > 0 && _amountYDFBaseEarn > 0,
'must stake and be earning at least some tokens'
);
if (_transferStakeToken) {
stakeToken.transferFrom(_user, address(this), _amountStaked);
}
_ids.increment();
stakes[_ids.current()] = Stake({
created: block.timestamp,
amountStaked: _amountStaked,
amountYDFBaseEarn: _amountYDFBaseEarn,
apr: _aprLockOptions[_lockOptIndex].apr,
lockTime: _aprLockOptions[_lockOptIndex].lockTime
});
_safeMint(_user, _ids.current());
tokenMintedAt[_ids.current()] = block.timestamp;
emit StakeTokens(_user, _ids.current(), _amountStaked, _lockOptIndex);
}
function unstake(uint256 _tokenId) public {
address _user = msg.sender;
Stake memory _tokenStake = stakes[_tokenId];
require(
_user == ownerOf(_tokenId),
'only the owner of the staked tokens can unstake'
);
bool _isUnstakingEarly = block.timestamp <
_tokenStake.created + _tokenStake.lockTime;
if (_isUnstakingEarly) {
uint256 _timeStaked = block.timestamp - _tokenStake.created;
uint256 _earnedAmount = (_tokenStake.amountStaked * _timeStaked) /
_tokenStake.lockTime;
stakeToken.transfer(_user, _earnedAmount);
if (address(stakeToken) == address(ydf)) {
ydf.burn(_tokenStake.amountStaked - _earnedAmount);
} else {
stakeToken.transfer(owner(), _tokenStake.amountStaked - _earnedAmount);
}
} else {
stakeToken.transfer(_user, _tokenStake.amountStaked);
}
uint256 _totalEarnedAmount = getTotalEarnedAmount(_tokenId);
if (_totalEarnedAmount > yieldClaimed[_tokenId]) {
_createVestAndMint(_user, _totalEarnedAmount - yieldClaimed[_tokenId]);
}
_burn(_tokenId);
emit UnstakeTokens(_user, _tokenId);
}
function unstakeMulti(uint256[] memory _tokenIds) external {
for (uint256 i = 0; i < _tokenIds.length; i++) {
unstake(_tokenIds[i]);
}
}
function claimAndVestRewards(uint256 _tokenId) public {
require(!isBlacklisted[_tokenId], 'blacklisted NFT');
require(block.timestamp > lastClaim[_tokenId] + ONE_WEEK);
lastClaim[_tokenId] = block.timestamp;
uint256 _totalEarnedAmount = getTotalEarnedAmount(_tokenId);
require(
_totalEarnedAmount > yieldClaimed[_tokenId],
'must have some yield to claim'
);
_createVestAndMint(
ownerOf(_tokenId),
_totalEarnedAmount - yieldClaimed[_tokenId]
);
yieldClaimed[_tokenId] = _totalEarnedAmount;
}
function claimAndVestRewardsMulti(uint256[] memory _tokenIds) external {
for (uint256 i = 0; i < _tokenIds.length; i++) {
claimAndVestRewards(_tokenIds[i]);
}
}
function _createVestAndMint(address _user, uint256 _amount) internal {
vester.createVest(_user, _amount);
ydf.stakeMintToVester(_amount);
}
function royaltyInfo(uint256, uint256 _salePrice)
external
view
returns (address receiver, uint256 royaltyAmount)
{
return (royaltyAddress, (_salePrice * royaltyBasisPoints) / 1000);
}
function tokenURI(uint256 _tokenId)
public
view
virtual
override
returns (string memory)
{
require(_exists(_tokenId), 'token does not exist');
return string(abi.encodePacked(_baseURI(), _tokenId.toString(), '.json'));
}
function contractURI() public view returns (string memory) {
return string(abi.encodePacked(_baseURI(), 'contract.json'));
}
function supportsInterface(bytes4 _interfaceId)
public
view
virtual
override(ERC721Enumerable)
returns (bool)
{
return super.supportsInterface(_interfaceId);
}
function getLastMintedTokenId() external view returns (uint256) {
return _ids.current();
}
function isTokenMinted(uint256 _tokenId) external view returns (bool) {
return _exists(_tokenId);
}
function setPaymentAddress(address _address) external onlyOwner {
paymentAddress = _address;
emit SetPaymentAddress(_address);
}
function setRoyaltyAddress(address _address) external onlyOwner {
royaltyAddress = _address;
emit SetRoyaltyAddress(_address);
}
function setRoyaltyBasisPoints(uint256 _points) external onlyOwner {
royaltyBasisPoints = _points;
emit SetRoyaltyBasisPoints(_points);
}
function setBaseURI(string memory _uri) external onlyOwner {
baseTokenURI = _uri;
emit SetBaseTokenURI(_uri);
}
function getAllUserOwned(address _user)
external
view
returns (uint256[] memory)
{
return allUserOwned[_user];
}
function getTotalEarnedAmount(uint256 _tokenId)
public
view
returns (uint256)
{
Stake memory _tokenStake = stakes[_tokenId];
uint256 _secondsStaked = block.timestamp - _tokenStake.created;
return
(_tokenStake.amountYDFBaseEarn * _tokenStake.apr * _secondsStaked) /
PERCENT_DENOMENATOR /
ONE_YEAR;
}
function getAllLockOptions() external view returns (AprLock[] memory) {
return _aprLockOptions;
}
function addAprLockOption(uint16 _apr, uint256 _lockTime) external onlyOwner {
_addAprLockOption(_apr, _lockTime);
emit AddAprLockOption(_apr, _lockTime);
}
function _addAprLockOption(uint16 _apr, uint256 _lockTime) internal {
_aprLockOptions.push(AprLock({ apr: _apr, lockTime: _lockTime }));
}
function removeAprLockOption(uint256 _index) external onlyOwner {
AprLock memory _option = _aprLockOptions[_index];
_aprLockOptions[_index] = _aprLockOptions[_aprLockOptions.length - 1];
_aprLockOptions.pop();
emit RemoveAprLockOption(_index, _option.apr, _option.lockTime);
}
function updateAprLockOption(
uint256 _index,
uint16 _apr,
uint256 _lockTime
) external onlyOwner {
AprLock memory _option = _aprLockOptions[_index];
_aprLockOptions[_index] = AprLock({ apr: _apr, lockTime: _lockTime });
emit UpdateAprLockOption(
_index,
_option.apr,
_option.lockTime,
_apr,
_lockTime
);
}
function setIsBlacklisted(uint256 _tokenId, bool _isBlacklisted)
external
onlyOwner
{
isBlacklisted[_tokenId] = _isBlacklisted;
emit SetTokenBlacklist(_tokenId, _isBlacklisted);
}
function _baseURI() internal view override returns (string memory) {
return baseTokenURI;
}
function _beforeTokenTransfer(
address _from,
address _to,
uint256 _tokenId
) internal virtual override(ERC721Enumerable) {
require(!isBlacklisted[_tokenId], 'blacklisted NFT');
tokenLastTransferred[_tokenId] = block.timestamp;
super._beforeTokenTransfer(_from, _to, _tokenId);
}
function _afterTokenTransfer(
address _from,
address _to,
uint256 _tokenId
) internal virtual override(ERC721) {
Stake memory _tokenStake = stakes[_tokenId];
if (_from != address(0)) {
uint256 _currIndex = ownedIndex[_tokenId];
uint256 _tokenIdMovingIndices = allUserOwned[_from][
allUserOwned[_from].length - 1
];
allUserOwned[_from][_currIndex] = allUserOwned[_from][
allUserOwned[_from].length - 1
];
allUserOwned[_from].pop();
ownedIndex[_tokenIdMovingIndices] = _currIndex;
rewards.setShare(_from, _tokenStake.amountYDFBaseEarn, true);
}
if (_to != address(0)) {
ownedIndex[_tokenId] = allUserOwned[_to].length;
allUserOwned[_to].push(_tokenId);
rewards.setShare(_to, _tokenStake.amountYDFBaseEarn, false);
}
super._afterTokenTransfer(_from, _to, _tokenId);
}
}
文件 19 的 19:sYDF.sol
pragma solidity ^0.8.9;
import './YDFStake.sol';
contract sYDF is YDFStake {
constructor(
address _ydf,
address _vester,
address _rewards,
string memory _baseTokenURI
)
YDFStake(
'Staked Yieldification',
'sYDF',
_ydf,
_ydf,
_vester,
_rewards,
_baseTokenURI
)
{
_addAprLockOption(2500, 0);
_addAprLockOption(5000, 14 days);
_addAprLockOption(10000, 120 days);
_addAprLockOption(15000, 240 days);
_addAprLockOption(20000, 360 days);
}
}
{
"compilationTarget": {
"contracts/sYDF.sol": "sYDF"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_ydf","type":"address"},{"internalType":"address","name":"_vester","type":"address"},{"internalType":"address","name":"_rewards","type":"address"},{"internalType":"string","name":"_baseTokenURI","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"apr","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"lockTime","type":"uint256"}],"name":"AddAprLockOption","type":"event"},{"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":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":true,"internalType":"uint16","name":"apr","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"lockTime","type":"uint256"}],"name":"RemoveAprLockOption","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"newApr","type":"uint256"}],"name":"SetAnnualApr","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"newUri","type":"string"}],"name":"SetBaseTokenURI","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"SetPaymentAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"SetRoyaltyAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_royaltyBasisPoints","type":"uint256"}],"name":"SetRoyaltyBasisPoints","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isBlacklisted","type":"bool"}],"name":"SetTokenBlacklist","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountStaked","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockOptionIndex","type":"uint256"}],"name":"StakeTokens","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":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"UnstakeTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":true,"internalType":"uint16","name":"oldApr","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"oldLockTime","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"newApr","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"newLockTime","type":"uint256"}],"name":"UpdateAprLockOption","type":"event"},{"inputs":[{"internalType":"uint16","name":"_apr","type":"uint16"},{"internalType":"uint256","name":"_lockTime","type":"uint256"}],"name":"addAprLockOption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"allUserOwned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","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"}],"name":"claimAndVestRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"claimAndVestRewardsMulti","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllLockOptions","outputs":[{"components":[{"internalType":"uint16","name":"apr","type":"uint16"},{"internalType":"uint256","name":"lockTime","type":"uint256"}],"internalType":"struct YDFStake.AprLock[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getAllUserOwned","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":[],"name":"getLastMintedTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getTotalEarnedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"uint256","name":"","type":"uint256"}],"name":"isBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"isTokenMinted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"lastClaim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ownedIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"removeAprLockOption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"royaltyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"_salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","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":"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":"string","name":"_uri","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bool","name":"_isBlacklisted","type":"bool"}],"name":"setIsBlacklisted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setPaymentAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setRoyaltyAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_points","type":"uint256"}],"name":"setRoyaltyBasisPoints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_lockOptIndex","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakes","outputs":[{"internalType":"uint256","name":"created","type":"uint256"},{"internalType":"uint256","name":"amountStaked","type":"uint256"},{"internalType":"uint256","name":"amountYDFBaseEarn","type":"uint256"},{"internalType":"uint16","name":"apr","type":"uint16"},{"internalType":"uint256","name":"lockTime","type":"uint256"}],"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":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenLastTransferred","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenMintedAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","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":"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":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"unstakeMulti","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"uint16","name":"_apr","type":"uint16"},{"internalType":"uint256","name":"_lockTime","type":"uint256"}],"name":"updateAprLockOption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"yieldClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]