文件 1 的 32:Address.sol
pragma solidity >=0.6.2 <0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 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) private 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 的 32:Clones.sol
pragma solidity >=0.6.0 <0.8.0;
library Clones {
function clone(address master) internal returns (address instance) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, master))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create(0, ptr, 0x37)
}
require(instance != address(0), "ERC1167: create failed");
}
function cloneDeterministic(address master, bytes32 salt) internal returns (address instance) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, master))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create2(0, ptr, 0x37, salt)
}
require(instance != address(0), "ERC1167: create2 failed");
}
function predictDeterministicAddress(address master, bytes32 salt, address deployer) internal pure returns (address predicted) {
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, master))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
mstore(add(ptr, 0x38), shl(0x60, deployer))
mstore(add(ptr, 0x4c), salt)
mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
predicted := keccak256(add(ptr, 0x37), 0x55)
}
}
function predictDeterministicAddress(address master, bytes32 salt) internal view returns (address predicted) {
return predictDeterministicAddress(master, salt, address(this));
}
}
文件 3 的 32:Context.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 4 的 32:ECDSA.sol
pragma solidity >=0.6.0 <0.8.0;
library ECDSA {
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
if (signature.length != 65) {
revert("ECDSA: invalid signature length");
}
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return recover(hash, v, r, s);
}
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");
return signer;
}
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
}
文件 5 的 32:EIP712.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract EIP712 {
bytes32 private immutable _HASHED_NAME;
bytes32 private immutable _HASHED_VERSION;
bytes32 private constant _TYPE_HASH =
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
constructor(string memory name, string memory version) {
_HASHED_NAME = keccak256(bytes(name));
_HASHED_VERSION = keccak256(bytes(version));
}
function _domainSeparatorV4() internal view returns (bytes32) {
return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
}
function _buildDomainSeparator(
bytes32 typeHash,
bytes32 name,
bytes32 version
) private view returns (bytes32) {
return keccak256(abi.encode(typeHash, name, version, _getChainId(), address(this)));
}
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
}
function _getChainId() private view returns (uint256 chainId) {
this;
assembly {
chainId := chainid()
}
}
function _EIP712NameHash() internal view virtual returns (bytes32) {
return _HASHED_NAME;
}
function _EIP712VersionHash() internal view virtual returns (bytes32) {
return _HASHED_VERSION;
}
}
文件 6 的 32:ERC1271.sol
pragma solidity 0.7.6;
import {ECDSA} from "@openzeppelin/contracts/cryptography/ECDSA.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
interface IERC1271 {
function isValidSignature(bytes32 _messageHash, bytes memory _signature)
external
view
returns (bytes4 magicValue);
}
library SignatureChecker {
function isValidSignature(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
if (Address.isContract(signer)) {
bytes4 selector = IERC1271.isValidSignature.selector;
(bool success, bytes memory returndata) =
signer.staticcall(abi.encodeWithSelector(selector, hash, signature));
return success && abi.decode(returndata, (bytes4)) == selector;
} else {
return ECDSA.recover(hash, signature) == signer;
}
}
}
abstract contract ERC1271 is IERC1271 {
bytes4 internal constant VALID_SIG = IERC1271.isValidSignature.selector;
bytes4 internal constant INVALID_SIG = bytes4(0);
modifier onlyValidSignature(bytes32 permissionHash, bytes memory signature) {
require(
isValidSignature(permissionHash, signature) == VALID_SIG,
"ERC1271: Invalid signature"
);
_;
}
function _getOwner() internal view virtual returns (address owner);
function isValidSignature(bytes32 permissionHash, bytes memory signature)
public
view
override
returns (bytes4)
{
return
SignatureChecker.isValidSignature(_getOwner(), permissionHash, signature)
? VALID_SIG
: INVALID_SIG;
}
}
文件 7 的 32:ERC165.sol
pragma solidity >=0.6.0 <0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () internal {
_registerInterface(_INTERFACE_ID_ERC165);
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return _supportedInterfaces[interfaceId];
}
function _registerInterface(bytes4 interfaceId) internal virtual {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}
文件 8 的 32:ERC721.sol
pragma solidity >=0.6.0 <0.8.0;
import "../../utils/Context.sol";
import "./IERC721.sol";
import "./IERC721Metadata.sol";
import "./IERC721Enumerable.sol";
import "./IERC721Receiver.sol";
import "../../introspection/ERC165.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
import "../../utils/EnumerableSet.sol";
import "../../utils/EnumerableMap.sol";
import "../../utils/Strings.sol";
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {
using SafeMath for uint256;
using Address for address;
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableMap for EnumerableMap.UintToAddressMap;
using Strings for uint256;
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
mapping (address => EnumerableSet.UintSet) private _holderTokens;
EnumerableMap.UintToAddressMap private _tokenOwners;
mapping (uint256 => address) private _tokenApprovals;
mapping (address => mapping (address => bool)) private _operatorApprovals;
string private _name;
string private _symbol;
mapping (uint256 => string) private _tokenURIs;
string private _baseURI;
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
constructor (string memory name_, string memory symbol_) public {
_name = name_;
_symbol = symbol_;
_registerInterface(_INTERFACE_ID_ERC721);
_registerInterface(_INTERFACE_ID_ERC721_METADATA);
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
}
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: balance query for the zero address");
return _holderTokens[owner].length();
}
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
}
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) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = baseURI();
if (bytes(base).length == 0) {
return _tokenURI;
}
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
return string(abi.encodePacked(base, tokenId.toString()));
}
function baseURI() public view virtual returns (string memory) {
return _baseURI;
}
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
return _holderTokens[owner].at(index);
}
function totalSupply() public view virtual override returns (uint256) {
return _tokenOwners.length();
}
function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
(uint256 tokenId, ) = _tokenOwners.at(index);
return tokenId;
}
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 || ERC721.isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not owner nor approved for all"
);
_approve(to, tokenId);
}
function getApproved(uint256 tokenId) public view virtual override returns (address) {
require(_exists(tokenId), "ERC721: approved query for nonexistent token");
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address operator, bool approved) public virtual override {
require(operator != _msgSender(), "ERC721: approve to caller");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_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: transfer caller is not 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: transfer caller is not 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 _tokenOwners.contains(tokenId);
}
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
require(_exists(tokenId), "ERC721: operator query for nonexistent token");
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(owner, 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);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
emit Transfer(address(0), to, tokenId);
}
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721.ownerOf(tokenId);
_beforeTokenTransfer(owner, address(0), tokenId);
_approve(address(0), tokenId);
if (bytes(_tokenURIs[tokenId]).length != 0) {
delete _tokenURIs[tokenId];
}
_holderTokens[owner].remove(tokenId);
_tokenOwners.remove(tokenId);
emit Transfer(owner, address(0), tokenId);
}
function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
_approve(address(0), tokenId);
_holderTokens[from].remove(tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
emit Transfer(from, to, tokenId);
}
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
function _setBaseURI(string memory baseURI_) internal virtual {
_baseURI = baseURI_;
}
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
private returns (bool)
{
if (!to.isContract()) {
return true;
}
bytes memory returndata = to.functionCall(abi.encodeWithSelector(
IERC721Receiver(to).onERC721Received.selector,
_msgSender(),
from,
tokenId,
_data
), "ERC721: transfer to non ERC721Receiver implementer");
bytes4 retval = abi.decode(returndata, (bytes4));
return (retval == _ERC721_RECEIVED);
}
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
}
文件 9 的 32:EnumerableMap.sol
pragma solidity >=0.6.0 <0.8.0;
library EnumerableMap {
struct MapEntry {
bytes32 _key;
bytes32 _value;
}
struct Map {
MapEntry[] _entries;
mapping (bytes32 => uint256) _indexes;
}
function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
uint256 keyIndex = map._indexes[key];
if (keyIndex == 0) {
map._entries.push(MapEntry({ _key: key, _value: value }));
map._indexes[key] = map._entries.length;
return true;
} else {
map._entries[keyIndex - 1]._value = value;
return false;
}
}
function _remove(Map storage map, bytes32 key) private returns (bool) {
uint256 keyIndex = map._indexes[key];
if (keyIndex != 0) {
uint256 toDeleteIndex = keyIndex - 1;
uint256 lastIndex = map._entries.length - 1;
MapEntry storage lastEntry = map._entries[lastIndex];
map._entries[toDeleteIndex] = lastEntry;
map._indexes[lastEntry._key] = toDeleteIndex + 1;
map._entries.pop();
delete map._indexes[key];
return true;
} else {
return false;
}
}
function _contains(Map storage map, bytes32 key) private view returns (bool) {
return map._indexes[key] != 0;
}
function _length(Map storage map) private view returns (uint256) {
return map._entries.length;
}
function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
require(map._entries.length > index, "EnumerableMap: index out of bounds");
MapEntry storage entry = map._entries[index];
return (entry._key, entry._value);
}
function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) {
uint256 keyIndex = map._indexes[key];
if (keyIndex == 0) return (false, 0);
return (true, map._entries[keyIndex - 1]._value);
}
function _get(Map storage map, bytes32 key) private view returns (bytes32) {
uint256 keyIndex = map._indexes[key];
require(keyIndex != 0, "EnumerableMap: nonexistent key");
return map._entries[keyIndex - 1]._value;
}
function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
uint256 keyIndex = map._indexes[key];
require(keyIndex != 0, errorMessage);
return map._entries[keyIndex - 1]._value;
}
struct UintToAddressMap {
Map _inner;
}
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return _remove(map._inner, bytes32(key));
}
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return _contains(map._inner, bytes32(key));
}
function length(UintToAddressMap storage map) internal view returns (uint256) {
return _length(map._inner);
}
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = _at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key)))));
}
function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));
}
}
文件 10 的 32:EnumerableSet.sol
pragma solidity >=0.6.0 <0.8.0;
library EnumerableSet {
struct Set {
bytes32[] _values;
mapping (bytes32 => uint256) _indexes;
}
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
function _remove(Set storage set, bytes32 value) private returns (bool) {
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
bytes32 lastvalue = set._values[lastIndex];
set._values[toDeleteIndex] = lastvalue;
set._indexes[lastvalue] = toDeleteIndex + 1;
set._values.pop();
delete set._indexes[value];
return true;
} else {
return false;
}
}
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
struct Bytes32Set {
Set _inner;
}
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
struct AddressSet {
Set _inner;
}
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
struct UintSet {
Set _inner;
}
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}
文件 11 的 32:IERC165.sol
pragma solidity >=0.6.0 <0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 12 的 32:IERC20.sol
pragma solidity >=0.6.0 <0.8.0;
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);
}
文件 13 的 32:IERC2917.sol
pragma solidity 0.7.6;
interface IERC2917 {
event InterestRatePerBlockChanged (uint oldValue, uint newValue);
event ProductivityIncreased (address indexed user, uint value);
event ProductivityDecreased (address indexed user, uint value);
function initialize() external;
function setImplementor(address newImplementor) external;
function interestsPerBlock() external view returns (uint);
function changeInterestRatePerBlock(uint value) external returns (bool);
function getProductivity(address user) external view returns (uint, uint);
function increaseProductivity(address user, uint value) external returns (bool, uint, uint);
function decreaseProductivity(address user, uint value) external returns (bool, uint, uint);
function take() external view returns (uint);
function takeWithBlock() external view returns (uint, uint);
function mint() external returns (uint);
}
文件 14 的 32:IERC721.sol
pragma solidity >=0.6.2 <0.8.0;
import "../../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) 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;
}
文件 15 的 32:IERC721Enumerable.sol
pragma solidity >=0.6.2 <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 tokenId);
function tokenByIndex(uint256 index) external view returns (uint256);
}
文件 16 的 32:IERC721Metadata.sol
pragma solidity >=0.6.2 <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);
}
文件 17 的 32:IERC721Receiver.sol
pragma solidity >=0.6.0 <0.8.0;
interface IERC721Receiver {
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}
文件 18 的 32:IFactory.sol
pragma solidity 0.7.6;
interface IFactory {
function create(bytes calldata args) external returns (address instance);
function create2(bytes calldata args, bytes32 salt) external returns (address instance);
}
文件 19 的 32:Initializable.sol
pragma solidity >=0.4.24 <0.8.0;
import "../utils/Address.sol";
abstract contract Initializable {
bool private _initialized;
bool private _initializing;
modifier initializer() {
require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
function _isConstructor() private view returns (bool) {
return !Address.isContract(address(this));
}
}
文件 20 的 32:InstanceRegistry.sol
pragma solidity 0.7.6;
import {EnumerableSet} from "@openzeppelin/contracts/utils/EnumerableSet.sol";
interface IInstanceRegistry {
event InstanceAdded(address instance);
event InstanceRemoved(address instance);
function isInstance(address instance) external view returns (bool validity);
function instanceCount() external view returns (uint256 count);
function instanceAt(uint256 index) external view returns (address instance);
}
contract InstanceRegistry is IInstanceRegistry {
using EnumerableSet for EnumerableSet.AddressSet;
EnumerableSet.AddressSet private _instanceSet;
function isInstance(address instance) external view override returns (bool validity) {
return _instanceSet.contains(instance);
}
function instanceCount() external view override returns (uint256 count) {
return _instanceSet.length();
}
function instanceAt(uint256 index) external view override returns (address instance) {
return _instanceSet.at(index);
}
function _register(address instance) internal {
require(_instanceSet.add(instance), "InstanceRegistry: already registered");
emit InstanceAdded(instance);
}
}
文件 21 的 32:MethodNFTFactory.sol
pragma solidity 0.7.6;
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IFactory} from "../factory/IFactory.sol";
import {IInstanceRegistry} from "../factory/InstanceRegistry.sol";
import {ProxyFactory} from "../factory/ProxyFactory.sol";
import {IUniversalVault} from "./MethodVault.sol";
contract MethodNFTFactory is Ownable, IFactory, IInstanceRegistry, ERC721 {
using SafeMath for uint256;
bytes32[] public names;
mapping(bytes32=>address) public templates;
bytes32 public activeTemplate;
uint256 public tokenSerialNumber;
mapping(uint256=>uint256) public serialNumberToTokenId;
mapping(uint256=>uint256) public tokenIdToSerialNumber;
mapping(address=>address[]) private ownerToVaultsMap;
event TemplateAdded(bytes32 indexed name, address indexed template);
event TemplateActive(bytes32 indexed name, address indexed template);
constructor() ERC721("MethodNFT", "MTHDNFT") {
ERC721._setBaseURI("https://api.methodfi.co/nft/");
}
function addTemplate(bytes32 name, address template) public onlyOwner {
require(templates[name] == address(0), "Template already exists");
templates[name] = template;
if(names.length == 0) {
activeTemplate = name;
emit TemplateActive(name, template);
}
names.push(name);
emit TemplateAdded(name, template);
}
function setActive(bytes32 name) public onlyOwner {
require(templates[name] != address(0), "Template does not exist");
activeTemplate = name;
emit TemplateActive(name, templates[name]);
}
function isInstance(address instance) external view override returns (bool validity) {
return ERC721._exists(uint256(instance));
}
function instanceCount() external view override returns (uint256 count) {
return ERC721.totalSupply();
}
function instanceAt(uint256 index) external view override returns (address instance) {
return address(ERC721.tokenByIndex(index));
}
function create(bytes calldata) external override returns (address vault) {
return createSelected(activeTemplate);
}
function create2(bytes calldata, bytes32 salt) external override returns (address vault) {
return createSelected2(activeTemplate, salt);
}
function create() public returns (address vault) {
return createSelected(activeTemplate);
}
function create2(bytes32 salt) public returns (address vault) {
return createSelected2(activeTemplate, salt);
}
function createSelected(bytes32 name) public returns (address vault) {
vault = ProxyFactory._create(
templates[name],
abi.encodeWithSelector(IUniversalVault.initialize.selector)
);
uint256 tokenId = uint256(vault);
ERC721._safeMint(msg.sender, tokenId);
ownerToVaultsMap[msg.sender].push(vault);
tokenSerialNumber = tokenSerialNumber.add(1);
serialNumberToTokenId[tokenSerialNumber] = tokenId;
tokenIdToSerialNumber[tokenId] = tokenSerialNumber;
emit InstanceAdded(vault);
return vault;
}
function createSelected2(bytes32 name, bytes32 salt) public returns (address vault) {
vault = ProxyFactory._create2(
templates[name],
abi.encodeWithSelector(IUniversalVault.initialize.selector),
salt
);
uint256 tokenId = uint256(vault);
ERC721._safeMint(msg.sender, tokenId);
ownerToVaultsMap[msg.sender].push(vault);
tokenSerialNumber = tokenSerialNumber.add(1);
serialNumberToTokenId[tokenSerialNumber] = tokenId;
tokenIdToSerialNumber[tokenId] = tokenSerialNumber;
emit InstanceAdded(vault);
return vault;
}
function nameCount() public view returns(uint256) {
return names.length;
}
function vaultCount(address owner) public view returns(uint256) {
return ownerToVaultsMap[owner].length;
}
function getVault(address owner, uint256 index) public view returns (address) {
return ownerToVaultsMap[owner][index];
}
function getAllVaults(address owner) public view returns (address [] memory) {
return ownerToVaultsMap[owner];
}
function getTemplate() external view returns (address) {
return templates[activeTemplate];
}
function getVaultOfNFT(uint256 nftId) public pure returns (address) {
return address(nftId);
}
function getNFTOfVault(address vault) public pure returns (uint256) {
return uint256(vault);
}
}
文件 22 的 32:MethodVault.sol
pragma solidity 0.7.6;
pragma abicoder v2;
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {Initializable} from "@openzeppelin/contracts/proxy/Initializable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/EnumerableSet.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {TransferHelper} from "@uniswap/lib/contracts/libraries/TransferHelper.sol";
import {EIP712} from "./EIP712.sol";
import {ERC1271} from "./ERC1271.sol";
import {OwnableByERC721} from "./OwnableByERC721.sol";
import {IRageQuit} from "../staking/UniStaker.sol";
interface IUniversalVault {
event Locked(address delegate, address token, uint256 amount);
event Unlocked(address delegate, address token, uint256 amount);
event RageQuit(address delegate, address token, bool notified, string reason);
struct LockData {
address delegate;
address token;
uint256 balance;
}
function initialize() external;
function lock(
address token,
uint256 amount,
bytes calldata permission
) external;
function unlock(
address token,
uint256 amount,
bytes calldata permission
) external;
function rageQuit(address delegate, address token)
external
returns (bool notified, string memory error);
function transferERC20(
address token,
address to,
uint256 amount
) external;
function transferETH(address to, uint256 amount) external payable;
function calculateLockID(address delegate, address token)
external
pure
returns (bytes32 lockID);
function getPermissionHash(
bytes32 eip712TypeHash,
address delegate,
address token,
uint256 amount,
uint256 nonce
) external view returns (bytes32 permissionHash);
function getNonce() external view returns (uint256 nonce);
function owner() external view returns (address ownerAddress);
function getLockSetCount() external view returns (uint256 count);
function getLockAt(uint256 index) external view returns (LockData memory lockData);
function getBalanceDelegated(address token, address delegate)
external
view
returns (uint256 balance);
function getBalanceLocked(address token) external view returns (uint256 balance);
function checkBalances() external view returns (bool validity);
}
contract MethodVault is
IUniversalVault,
EIP712("UniversalVault", "1.0.0"),
ERC1271,
OwnableByERC721,
Initializable
{
using SafeMath for uint256;
using Address for address;
using Address for address payable;
using EnumerableSet for EnumerableSet.Bytes32Set;
uint256 public constant RAGEQUIT_GAS = 500000;
bytes32 public constant LOCK_TYPEHASH =
keccak256("Lock(address delegate,address token,uint256 amount,uint256 nonce)");
bytes32 public constant UNLOCK_TYPEHASH =
keccak256("Unlock(address delegate,address token,uint256 amount,uint256 nonce)");
string public constant VERSION = "1.0.0";
uint256 private _nonce;
mapping(bytes32 => LockData) private _locks;
EnumerableSet.Bytes32Set private _lockSet;
function initializeLock() external initializer {}
function initialize() external override initializer {
OwnableByERC721._setNFT(msg.sender);
}
receive() external payable {}
function _getOwner() internal view override(ERC1271) returns (address ownerAddress) {
return OwnableByERC721.owner();
}
function calculateLockID(address delegate, address token)
public
pure
override
returns (bytes32 lockID)
{
return keccak256(abi.encodePacked(delegate, token));
}
function getPermissionHash(
bytes32 eip712TypeHash,
address delegate,
address token,
uint256 amount,
uint256 nonce
) public view override returns (bytes32 permissionHash) {
return
EIP712._hashTypedDataV4(
keccak256(abi.encode(eip712TypeHash, delegate, token, amount, nonce))
);
}
function getNonce() external view override returns (uint256 nonce) {
return _nonce;
}
function owner()
public
view
override(IUniversalVault, OwnableByERC721)
returns (address ownerAddress)
{
return OwnableByERC721.owner();
}
function getLockSetCount() external view override returns (uint256 count) {
return _lockSet.length();
}
function getLockAt(uint256 index) external view override returns (LockData memory lockData) {
return _locks[_lockSet.at(index)];
}
function getBalanceDelegated(address token, address delegate)
external
view
override
returns (uint256 balance)
{
return _locks[calculateLockID(delegate, token)].balance;
}
function getBalanceLocked(address token) public view override returns (uint256 balance) {
uint256 count = _lockSet.length();
for (uint256 index; index < count; index++) {
LockData storage _lockData = _locks[_lockSet.at(index)];
if (_lockData.token == token && _lockData.balance > balance)
balance = _lockData.balance;
}
return balance;
}
function checkBalances() external view override returns (bool validity) {
uint256 count = _lockSet.length();
for (uint256 index; index < count; index++) {
LockData storage _lockData = _locks[_lockSet.at(index)];
if (IERC20(_lockData.token).balanceOf(address(this)) < _lockData.balance) return false;
}
return true;
}
function lock(
address token,
uint256 amount,
bytes calldata permission
)
external
override
onlyValidSignature(
getPermissionHash(LOCK_TYPEHASH, msg.sender, token, amount, _nonce),
permission
)
{
bytes32 lockID = calculateLockID(msg.sender, token);
if (_lockSet.contains(lockID)) {
_locks[lockID].balance = _locks[lockID].balance.add(amount);
} else {
assert(_lockSet.add(lockID));
_locks[lockID] = LockData(msg.sender, token, amount);
}
require(
IERC20(token).balanceOf(address(this)) >= _locks[lockID].balance,
"UniversalVault: insufficient balance"
);
_nonce += 1;
emit Locked(msg.sender, token, amount);
}
function unlock(
address token,
uint256 amount,
bytes calldata permission
)
external
override
onlyValidSignature(
getPermissionHash(UNLOCK_TYPEHASH, msg.sender, token, amount, _nonce),
permission
)
{
bytes32 lockID = calculateLockID(msg.sender, token);
require(_lockSet.contains(lockID), "UniversalVault: missing lock");
if (_locks[lockID].balance > amount) {
_locks[lockID].balance = _locks[lockID].balance.sub(amount);
} else {
delete _locks[lockID];
assert(_lockSet.remove(lockID));
}
_nonce += 1;
emit Unlocked(msg.sender, token, amount);
}
function rageQuit(address delegate, address token)
external
override
onlyOwner
returns (bool notified, string memory error)
{
bytes32 lockID = calculateLockID(delegate, token);
require(_lockSet.contains(lockID), "UniversalVault: missing lock");
if (delegate.isContract()) {
require(gasleft() >= RAGEQUIT_GAS, "UniversalVault: insufficient gas");
try IRageQuit(delegate).rageQuit{gas: RAGEQUIT_GAS}() {
notified = true;
} catch Error(string memory res) {
notified = false;
error = res;
} catch (bytes memory) {
notified = false;
}
}
assert(_lockSet.remove(lockID));
delete _locks[lockID];
emit RageQuit(delegate, token, notified, error);
}
function transferERC20(
address token,
address to,
uint256 amount
) external override onlyOwner {
require(
IERC20(token).balanceOf(address(this)) >= getBalanceLocked(token).add(amount),
"UniversalVault: insufficient balance"
);
TransferHelper.safeTransfer(token, to, amount);
}
function transferETH(address to, uint256 amount) external payable override onlyOwner {
TransferHelper.safeTransferETH(to, amount);
}
}
文件 23 的 32:Ownable.sol
pragma solidity >=0.6.0 <0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
文件 24 的 32:OwnableByERC721.sol
pragma solidity 0.7.6;
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
contract OwnableByERC721 {
address private _nftAddress;
modifier onlyOwner() {
require(owner() == msg.sender, "OwnableByERC721: caller is not the owner");
_;
}
function _setNFT(address nftAddress) internal {
_nftAddress = nftAddress;
}
function nft() public view virtual returns (address nftAddress) {
return _nftAddress;
}
function owner() public view virtual returns (address ownerAddress) {
return IERC721(_nftAddress).ownerOf(uint256(address(this)));
}
}
文件 25 的 32:PowerSwitch.sol
pragma solidity 0.7.6;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
interface IPowerSwitch {
event PowerOn();
event PowerOff();
event EmergencyShutdown();
enum State {Online, Offline, Shutdown}
function powerOn() external;
function powerOff() external;
function emergencyShutdown() external;
function isOnline() external view returns (bool status);
function isOffline() external view returns (bool status);
function isShutdown() external view returns (bool status);
function getStatus() external view returns (State status);
function getPowerController() external view returns (address controller);
}
contract PowerSwitch is IPowerSwitch, Ownable {
IPowerSwitch.State private _status;
constructor(address owner) {
require(owner != address(0), "PowerSwitch: invalid owner");
Ownable.transferOwnership(owner);
}
function powerOn() external override onlyOwner {
require(_status == IPowerSwitch.State.Offline, "PowerSwitch: cannot power on");
_status = IPowerSwitch.State.Online;
emit PowerOn();
}
function powerOff() external override onlyOwner {
require(_status == IPowerSwitch.State.Online, "PowerSwitch: cannot power off");
_status = IPowerSwitch.State.Offline;
emit PowerOff();
}
function emergencyShutdown() external override onlyOwner {
require(_status != IPowerSwitch.State.Shutdown, "PowerSwitch: cannot shutdown");
_status = IPowerSwitch.State.Shutdown;
emit EmergencyShutdown();
}
function isOnline() external view override returns (bool status) {
return _status == IPowerSwitch.State.Online;
}
function isOffline() external view override returns (bool status) {
return _status == IPowerSwitch.State.Offline;
}
function isShutdown() external view override returns (bool status) {
return _status == IPowerSwitch.State.Shutdown;
}
function getStatus() external view override returns (IPowerSwitch.State status) {
return _status;
}
function getPowerController() external view override returns (address controller) {
return Ownable.owner();
}
}
文件 26 的 32:Powered.sol
pragma solidity 0.7.6;
import {IPowerSwitch} from "./PowerSwitch.sol";
interface IPowered {
function isOnline() external view returns (bool status);
function isOffline() external view returns (bool status);
function isShutdown() external view returns (bool status);
function getPowerSwitch() external view returns (address powerSwitch);
function getPowerController() external view returns (address controller);
}
contract Powered is IPowered {
address private _powerSwitch;
modifier onlyOnline() {
require(isOnline(), "Powered: is not online");
_;
}
modifier onlyOffline() {
require(isOffline(), "Powered: is not offline");
_;
}
modifier notShutdown() {
require(!isShutdown(), "Powered: is shutdown");
_;
}
modifier onlyShutdown() {
require(isShutdown(), "Powered: is not shutdown");
_;
}
function _setPowerSwitch(address powerSwitch) internal {
_powerSwitch = powerSwitch;
}
function isOnline() public view override returns (bool status) {
return IPowerSwitch(_powerSwitch).isOnline();
}
function isOffline() public view override returns (bool status) {
return IPowerSwitch(_powerSwitch).isOffline();
}
function isShutdown() public view override returns (bool status) {
return IPowerSwitch(_powerSwitch).isShutdown();
}
function getPowerSwitch() public view override returns (address powerSwitch) {
return _powerSwitch;
}
function getPowerController() public view override returns (address controller) {
return IPowerSwitch(_powerSwitch).getPowerController();
}
}
文件 27 的 32:ProxyFactory.sol
pragma solidity 0.7.6;
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
library ProxyFactory {
function _create(address logic, bytes memory data) internal returns (address proxy) {
proxy = Clones.clone(logic);
if (data.length > 0) {
(bool success, bytes memory err) = proxy.call(data);
require(success, string(err));
}
return proxy;
}
function _create2(
address logic,
bytes memory data,
bytes32 salt
) internal returns (address proxy) {
proxy = Clones.cloneDeterministic(logic, salt);
if (data.length > 0) {
(bool success, bytes memory err) = proxy.call(data);
require(success, string(err));
}
return proxy;
}
}
文件 28 的 32:RewardPool.sol
pragma solidity 0.7.6;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {TransferHelper} from "@uniswap/lib/contracts/libraries/TransferHelper.sol";
import {Powered} from "./Powered.sol";
interface IRewardPool {
function sendERC20(
address token,
address to,
uint256 value
) external;
function rescueERC20(address[] calldata tokens, address recipient) external;
}
contract RewardPool is IRewardPool, Powered, Ownable {
constructor(address powerSwitch) {
Powered._setPowerSwitch(powerSwitch);
}
function sendERC20(
address token,
address to,
uint256 value
) external override onlyOwner onlyOnline {
TransferHelper.safeTransfer(token, to, value);
}
function rescueERC20(address[] calldata tokens, address recipient)
external
override
onlyShutdown
{
require(
msg.sender == Powered.getPowerController(),
"RewardPool: only controller can withdraw after shutdown"
);
require(recipient != address(0), "RewardPool: recipient not defined");
for (uint256 index = 0; index < tokens.length; index++) {
address token = tokens[index];
uint256 balance = IERC20(token).balanceOf(address(this));
TransferHelper.safeTransfer(token, recipient, balance);
}
}
}
文件 29 的 32:SafeMath.sol
pragma solidity >=0.6.0 <0.8.0;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
文件 30 的 32:Strings.sol
pragma solidity >=0.6.0 <0.8.0;
library Strings {
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);
uint256 index = digits - 1;
temp = value;
while (temp != 0) {
buffer[index--] = bytes1(uint8(48 + temp % 10));
temp /= 10;
}
return string(buffer);
}
}
文件 31 的 32:TransferHelper.sol
pragma solidity >=0.6.0;
library TransferHelper {
function safeApprove(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeApprove: approve failed'
);
}
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeTransfer: transfer failed'
);
}
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::transferFrom: transferFrom failed'
);
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
}
}
文件 32 的 32:UniStaker.sol
pragma solidity 0.7.6;
pragma abicoder v2;
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/EnumerableSet.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {TransferHelper} from "@uniswap/lib/contracts/libraries/TransferHelper.sol";
import {IFactory} from "../factory/IFactory.sol";
import {IInstanceRegistry} from "../factory/InstanceRegistry.sol";
import {IUniversalVault} from "../methodNFT/MethodVault.sol";
import {MethodNFTFactory} from "../methodNFT/MethodNFTFactory.sol";
import {IRewardPool} from "./RewardPool.sol";
import {Powered} from "./Powered.sol";
import {IERC2917} from "./IERC2917.sol";
import {ProxyFactory} from "../factory/ProxyFactory.sol";
interface IRageQuit {
function rageQuit() external;
}
interface IUniStaker is IRageQuit {
event UniStakerCreated(address rewardPool, address powerSwitch);
event UniStakerFunded(address token, uint256 amount);
event BonusTokenRegistered(address token);
event BonusTokenRemoved(address token);
event VaultFactoryRegistered(address factory);
event VaultFactoryRemoved(address factory);
event AdminshipTransferred(address indexed previousAdmin, address indexed newAdmin);
event Staked(address vault, uint256 amount);
event Unstaked(address vault, uint256 amount);
event RageQuit(address vault);
event RewardClaimed(address vaultFactory, address recipient, address token, uint256 amount);
event VestedRewardClaimed(address recipient, address token, uint amount);
struct VaultData {
mapping(address => uint) tokenStake;
EnumerableSet.AddressSet tokens;
}
struct LMRewardData {
uint256 amount;
uint256 duration;
uint256 startedAt;
address rewardCalcInstance;
EnumerableSet.AddressSet bonusTokens;
mapping(address => uint) bonusTokenAmounts;
}
struct LMRewardVestingData {
uint amount;
uint startedAt;
}
function getBonusTokenSetLength() external view returns (uint256 length);
function getBonusTokenAtIndex(uint256 index) external view returns (address bonusToken);
function getVaultFactorySetLength() external view returns (uint256 length);
function getVaultFactoryAtIndex(uint256 index) external view returns (address factory);
function getNumVaults() external view returns (uint256 num);
function getVaultAt(uint256 index) external view returns (address vault);
function getNumTokensStaked() external view returns (uint256 num);
function getTokenStakedAt(uint256 index) external view returns (address token);
function getNumTokensStakedInVault(address vault) external view returns (uint256 num);
function getVaultTokenAtIndex(address vault, uint256 index) external view returns (address vaultToken);
function getVaultTokenStake(address vault, address token) external view returns (uint256 tokenStake);
function getLMRewardData(address token) external view returns (uint amount, uint duration, uint startedAt, address rewardCalcInstance);
function getLMRewardBonusTokensLength(address token) external view returns (uint length);
function getLMRewardBonusTokenAt(address token, uint index) external view returns (address bonusToken, uint bonusTokenAmount);
function getNumVestingLMTokenRewards(address user) external view returns (uint num);
function getVestingLMTokenAt(address user, uint index) external view returns (address token);
function getNumVests(address user, address token) external view returns (uint num);
function getNumRewardCalcTemplates() external view returns (uint num);
function getLMRewardVestingData(address user, address token, uint index) external view returns (uint amount, uint startedAt);
function isValidAddress(address target) external view returns (bool validity);
function isValidVault(address vault, address factory) external view returns (bool validity);
function stake(
address vault,
address vaultFactory,
address token,
uint256 amount,
bytes calldata permission
) external;
function unstakeAndClaim(
address vault,
address vaultFactory,
address recipient,
address token,
uint256 amount,
bool claimBonusReward,
bytes calldata permission
) external;
function claimAirdropReward(address nftFactory) external;
function claimAirdropReward(address nftFactory, uint256[] calldata tokenIds) external;
function claimVestedReward() external;
function claimVestedReward(address token) external;
}
contract UniStaker is IUniStaker, Powered {
using SafeMath for uint256;
using EnumerableSet for EnumerableSet.AddressSet;
string public constant PLATINUM = "PLATINUM";
string public constant GOLD = "GOLD";
string public constant MINT = "MINT";
string public constant BLACK = "BLACK";
uint public PLATINUM_LM_REWARD_MULTIPLIER_NUM = 5;
uint public PLATINUM_LM_REWARD_MULTIPLIER_DENOM = 2;
uint public GOLD_LM_REWARD_MULTIPLIER_NUM = 2;
uint public GOLD_LM_REWARD_MULTIPLIER_DENOM = 1;
uint public MINT_LM_REWARD_MULTIPLIER_NUM = 3;
uint public MINT_LM_REWARD_MULTIPLIER_DENOM = 2;
uint public BLACK_LM_REWARD_MULTIPLIER_NUM = 1;
uint public BLACK_LM_REWARD_MULTIPLIER_DENOM = 1;
uint public LM_REWARD_VESTING_PERIOD = 7776000;
uint public LM_REWARD_VESTING_PORTION_NUM = 1;
uint public LM_REWARD_VESTING_PORTION_DENOM = 2;
uint256 public MAX_TOKENS_STAKED_PER_VAULT = 30;
uint256 public MAX_BONUS_TOKENS = 50;
uint256 public MIN_AIRDROP_REWARD_CLAIM_FREQUENCY = 604800;
address public admin;
address public rewardToken;
address public rewardPool;
EnumerableSet.AddressSet private _vaultSet;
mapping(address => VaultData) private _vaults;
EnumerableSet.AddressSet private _bonusTokenSet;
EnumerableSet.AddressSet private _vaultFactorySet;
EnumerableSet.AddressSet private _allStakedTokens;
mapping(address => uint256) public stakedTokenTotal;
mapping(address => LMRewardData) private lmRewards;
mapping(address => mapping(address => uint)) public earnedLMRewards;
mapping(address => mapping(address => LMRewardVestingData[])) public vestingLMRewards;
mapping(address => EnumerableSet.AddressSet) private vestingLMTokenRewards;
mapping(string => uint256) public weeklyAirdropAmounts;
mapping(string => uint256) public balancesRequiredToClaim;
mapping(uint256 => uint256) public nftLastClaimedRewardAt;
string[] public rewardCalcTemplateNames;
mapping(string => address) public rewardCalcTemplates;
string public activeRewardCalcTemplate;
event RewardCalcTemplateAdded(string indexed name, address indexed template);
event RewardCalcTemplateActive(string indexed name, address indexed template);
constructor(
address adminAddress,
address rewardPoolFactory,
address powerSwitchFactory,
address rewardTokenAddress
) {
address powerSwitch = IFactory(powerSwitchFactory).create(abi.encode(adminAddress));
rewardPool = IFactory(rewardPoolFactory).create(abi.encode(powerSwitch));
admin = adminAddress;
rewardToken = rewardTokenAddress;
Powered._setPowerSwitch(powerSwitch);
weeklyAirdropAmounts[PLATINUM] = uint256(166).mul(1e18);
weeklyAirdropAmounts[GOLD] = uint256(18).mul(1e18);
weeklyAirdropAmounts[MINT] = uint256(4).mul(1e18);
balancesRequiredToClaim[PLATINUM] = uint256(166).mul(1e18);
balancesRequiredToClaim[GOLD] = uint256(18).mul(1e18);
balancesRequiredToClaim[MINT] = uint256(4).mul(1e18);
emit UniStakerCreated(rewardPool, powerSwitch);
}
function _admin() private {
require(msg.sender == admin, "not allowed");
}
function renounceAdminship() public {
_admin();
emit AdminshipTransferred(admin, address(0));
admin = address(0);
}
function transferAdminship(address newAdmin) public {
_admin();
require(newAdmin != address(0), "new admin can't the zero address");
emit AdminshipTransferred(admin, newAdmin);
admin = newAdmin;
}
function fund(address token, uint256 amount) external {
_admin();
require(_bonusTokenSet.contains(token) || token == rewardToken, "cannot fund with unrecognized token");
TransferHelper.safeTransferFrom(
token,
msg.sender,
rewardPool,
amount
);
emit UniStakerFunded(token, amount);
}
function rescueTokensFromRewardPool(
address token,
address recipient,
uint256 amount
) external {
_admin();
require(isValidAddress(recipient), "invalid recipient");
IRewardPool(rewardPool).sendERC20(token, recipient, amount);
}
function registerVaultFactory(address factory) external {
_admin();
require(_vaultFactorySet.add(factory), "UniStaker: vault factory already registered");
emit VaultFactoryRegistered(factory);
}
function removeVaultFactory(address factory) external {
_admin();
require(_vaultFactorySet.remove(factory), "UniStaker: vault factory not registered");
emit VaultFactoryRemoved(factory);
}
function registerBonusToken(address bonusToken) external {
_admin();
require(isValidAddress(bonusToken), "invalid bonus token address or is already present");
require(_bonusTokenSet.length() < MAX_BONUS_TOKENS, "UniStaker: max bonus tokens reached ");
_bonusTokenSet.add(bonusToken);
emit BonusTokenRegistered(bonusToken);
}
function removeBonusToken(address bonusToken) external {
_admin();
require(_bonusTokenSet.remove(bonusToken), "UniStaker: bonus token not present ");
emit BonusTokenRemoved(bonusToken);
}
function addRewardCalcTemplate(string calldata name, address template) external {
_admin();
require(rewardCalcTemplates[name] == address(0), "Template already exists");
rewardCalcTemplates[name] = template;
if(rewardCalcTemplateNames.length == 0) {
activeRewardCalcTemplate = name;
emit RewardCalcTemplateActive(name, template);
}
rewardCalcTemplateNames.push(name);
emit RewardCalcTemplateAdded(name, template);
}
function setRewardCalcActiveTemplate(string calldata name) external {
_admin();
require(rewardCalcTemplates[name] != address(0), "Template does not exist");
activeRewardCalcTemplate = name;
emit RewardCalcTemplateActive(name, rewardCalcTemplates[name]);
}
function startLMRewards(address token, uint256 amount, uint256 duration) external {
startLMRewards(token, amount, duration, activeRewardCalcTemplate);
}
function startLMRewards(address token, uint256 amount, uint256 duration, string memory rewardCalcTemplateName) public {
_admin();
require(lmRewards[token].startedAt == 0, "A reward program already live for this token");
require(rewardCalcTemplates[rewardCalcTemplateName] != address(0), "Reward Calculator Template does not exist");
address rewardCalcInstance = ProxyFactory._create(rewardCalcTemplates[rewardCalcTemplateName], abi.encodeWithSelector(IERC2917.initialize.selector));
LMRewardData storage lmrd = lmRewards[token];
lmrd.amount = amount;
lmrd.duration = duration;
lmrd.startedAt = block.timestamp;
lmrd.rewardCalcInstance = rewardCalcInstance;
}
function setImplementorForRewardsCalculator(address token, address newImplementor) public {
_admin();
require(lmRewards[token].startedAt != 0, "No reward program currently live for this token");
address rewardCalcInstance = lmRewards[token].rewardCalcInstance;
IERC2917(rewardCalcInstance).setImplementor(newImplementor);
}
function setLMRewardsPerBlock(address token, uint value) public onlyOnline {
_admin();
require(lmRewards[token].startedAt != 0, "No reward program currently live for this token");
address rewardCalcInstance = lmRewards[token].rewardCalcInstance;
IERC2917(rewardCalcInstance).changeInterestRatePerBlock(value);
}
function addBonusTokenToLMRewards(address lmToken, address bonusToken, uint256 bonusTokenAmount) public {
_admin();
require(lmRewards[lmToken].startedAt != 0, "No reward program currently live for this LM token");
require(_bonusTokenSet.contains(bonusToken), "Bonus token not registered");
lmRewards[lmToken].bonusTokens.add(bonusToken);
lmRewards[lmToken].bonusTokenAmounts[bonusToken] = lmRewards[lmToken].bonusTokenAmounts[bonusToken].add(bonusTokenAmount);
}
function endLMRewards(address token, bool removeBonusTokenData) public {
_admin();
lmRewards[token].amount = 0;
lmRewards[token].duration = 0;
lmRewards[token].startedAt = 0;
lmRewards[token].rewardCalcInstance = address(0);
if (removeBonusTokenData) {
for (uint index = 0; index < lmRewards[token].bonusTokens.length(); index++) {
address bonusToken = lmRewards[token].bonusTokens.at(index);
lmRewards[token].bonusTokens.remove(bonusToken);
delete lmRewards[token].bonusTokenAmounts[bonusToken];
}
}
}
function setWeeklyAirdropAmount(string calldata tier, uint256 amount) external {
_admin();
weeklyAirdropAmounts[tier] = amount;
}
function setBalanceRequiredToClaim(string calldata tier, uint256 amount) external {
_admin();
balancesRequiredToClaim[tier] = amount;
}
function setMaxStakesPerVault(uint256 amount) external {
_admin();
MAX_TOKENS_STAKED_PER_VAULT = amount;
}
function setMaxBonusTokens(uint256 amount) external {
_admin();
MAX_BONUS_TOKENS = amount;
}
function setMinRewardClaimFrequency(uint256 amount) external {
_admin();
MIN_AIRDROP_REWARD_CLAIM_FREQUENCY = amount;
}
function setPlatinumLMRewardMultiplier(uint256 numerator, uint256 denominator) external {
_admin();
PLATINUM_LM_REWARD_MULTIPLIER_NUM = numerator;
PLATINUM_LM_REWARD_MULTIPLIER_DENOM = denominator;
}
function setGoldLMRewardMultiplier(uint256 numerator, uint256 denominator) external {
_admin();
GOLD_LM_REWARD_MULTIPLIER_NUM = numerator;
GOLD_LM_REWARD_MULTIPLIER_DENOM = denominator;
}
function setMintLMRewardMultiplier(uint256 numerator, uint256 denominator) external {
_admin();
MINT_LM_REWARD_MULTIPLIER_NUM = numerator;
MINT_LM_REWARD_MULTIPLIER_DENOM = denominator;
}
function setBlackLMRewardMultiplier(uint256 numerator, uint256 denominator) external {
_admin();
BLACK_LM_REWARD_MULTIPLIER_NUM = numerator;
BLACK_LM_REWARD_MULTIPLIER_DENOM = denominator;
}
function setLMRewardVestingPeriod(uint256 amount) external {
_admin();
LM_REWARD_VESTING_PERIOD = amount;
}
function setLMRewardVestingPortion(uint256 numerator, uint denominator) external {
_admin();
LM_REWARD_VESTING_PORTION_NUM = numerator;
LM_REWARD_VESTING_PORTION_DENOM = denominator;
}
function getBonusTokenSetLength() external view override returns (uint256 length) {
return _bonusTokenSet.length();
}
function getBonusTokenAtIndex(uint256 index)
external
view
override
returns (address bonusToken)
{
return _bonusTokenSet.at(index);
}
function getVaultFactorySetLength() external view override returns (uint256 length) {
return _vaultFactorySet.length();
}
function getVaultFactoryAtIndex(uint256 index)
external
view
override
returns (address factory)
{
return _vaultFactorySet.at(index);
}
function getNumVaults() external view override returns (uint256 num) {
return _vaultSet.length();
}
function getVaultAt(uint256 index) external view override returns (address vault) {
return _vaultSet.at(index);
}
function getNumTokensStaked() external view override returns (uint256 num) {
return _allStakedTokens.length();
}
function getTokenStakedAt(uint256 index) external view override returns (address token) {
return _allStakedTokens.at(index);
}
function getNumTokensStakedInVault(address vault)
external
view
override
returns (uint256 num)
{
return _vaults[vault].tokens.length();
}
function getVaultTokenAtIndex(address vault, uint256 index)
external
view
override
returns (address vaultToken)
{
return _vaults[vault].tokens.at(index);
}
function getVaultTokenStake(address vault, address token)
external
view
override
returns (uint256 tokenStake)
{
return _vaults[vault].tokenStake[token];
}
function getNftTier(uint256 nftId, address nftFactory) public view returns (string memory tier) {
uint256 serialNumber = MethodNFTFactory(nftFactory).tokenIdToSerialNumber(nftId);
if (serialNumber >= 1 && serialNumber <= 100) {
tier = PLATINUM;
} else if (serialNumber >= 101 && serialNumber <= 1000) {
tier = GOLD;
} else if (serialNumber >= 1001 && serialNumber <= 5000) {
tier = MINT;
} else if (serialNumber >= 5001) {
tier = BLACK;
}
}
function getNftsOfOwner(address owner, address nftFactory) public view returns (uint256[] memory nftIds) {
uint256 balance = MethodNFTFactory(nftFactory).balanceOf(owner);
nftIds = new uint256[](balance);
for (uint256 index = 0; index < balance; index++) {
uint256 nftId = MethodNFTFactory(nftFactory).tokenOfOwnerByIndex(owner, index);
nftIds[index] = nftId;
}
}
function getLMRewardData(address token) external view override returns (uint amount, uint duration, uint startedAt, address rewardCalcInstance) {
return (lmRewards[token].amount, lmRewards[token].duration, lmRewards[token].startedAt, lmRewards[token].rewardCalcInstance);
}
function getLMRewardBonusTokensLength(address token) external view override returns (uint length) {
return lmRewards[token].bonusTokens.length();
}
function getLMRewardBonusTokenAt(address token, uint index) external view override returns (address bonusToken, uint bonusTokenAmount) {
return (lmRewards[token].bonusTokens.at(index), lmRewards[token].bonusTokenAmounts[lmRewards[token].bonusTokens.at(index)]);
}
function getNumVestingLMTokenRewards(address user) external view override returns (uint num) {
return vestingLMTokenRewards[user].length();
}
function getVestingLMTokenAt(address user, uint index) external view override returns (address token) {
return vestingLMTokenRewards[user].at(index);
}
function getNumVests(address user, address token) external view override returns (uint num) {
return vestingLMRewards[user][token].length;
}
function getLMRewardVestingData(address user, address token, uint index) external view override returns (uint amount, uint startedAt) {
return (vestingLMRewards[user][token][index].amount, vestingLMRewards[user][token][index].startedAt);
}
function getNumRewardCalcTemplates() external view override returns (uint num) {
return rewardCalcTemplateNames.length;
}
function isValidVault(address vault, address factory) public view override returns (bool validity) {
return _vaultFactorySet.contains(factory) && IInstanceRegistry(factory).isInstance(vault);
}
function isValidAddress(address target) public view override returns (bool validity) {
return
target != address(this) &&
target != address(0) &&
target != rewardToken &&
target != rewardPool &&
!_bonusTokenSet.contains(target);
}
function calculateAirdropReward(address owner, address nftFactory) public returns (uint256 amount, uint256 balanceRequiredToClaim, uint256 balanceLocked) {
uint256[] memory nftIds = getNftsOfOwner(owner, nftFactory);
return calculateAirdropReward(nftFactory, nftIds);
}
function calculateAirdropReward(address nftFactory, uint256[] memory nftIds) public returns (uint256 amount, uint256 balanceRequiredToClaim, uint256 balanceLocked) {
for (uint256 index = 0; index < nftIds.length; index++) {
uint256 nftId = nftIds[index];
(uint256 amnt, uint256 balRequired, uint256 balLocked) = calculateAirdropReward(nftFactory, nftId);
amount = amount.add(amnt);
balanceRequiredToClaim = balanceRequiredToClaim.add(balRequired);
balanceLocked = balanceLocked.add(balLocked);
}
}
function calculateAirdropReward(address nftFactory, uint256 nftId) public returns (uint256 amount, uint256 balanceRequiredToClaim, uint256 balanceLocked) {
address vaultAddress = address(nftId);
require(isValidVault(vaultAddress, nftFactory), "UniStaker: vault is not valid");
if (nftLastClaimedRewardAt[nftId] == 0) {
nftLastClaimedRewardAt[nftId] = block.timestamp;
return (0,0,0);
}
uint256 secondsSinceLastClaim = block.timestamp.sub(nftLastClaimedRewardAt[nftId]);
require(secondsSinceLastClaim > MIN_AIRDROP_REWARD_CLAIM_FREQUENCY, "Claimed reward recently");
string memory tier = getNftTier(nftId, nftFactory);
uint256 balanceLockedInVault = IUniversalVault(vaultAddress).getBalanceLocked(rewardToken);
balanceLocked = balanceLocked.add(balanceLockedInVault);
uint256 epochsSinceLastClaim = secondsSinceLastClaim.div(MIN_AIRDROP_REWARD_CLAIM_FREQUENCY);
uint256 accruedReward;
bytes32 tierHash = keccak256(abi.encodePacked(tier));
if (tierHash == keccak256(abi.encodePacked(PLATINUM))) {
accruedReward = weeklyAirdropAmounts[PLATINUM].mul(epochsSinceLastClaim);
amount = amount.add(accruedReward);
balanceRequiredToClaim = balanceRequiredToClaim.add(balancesRequiredToClaim[PLATINUM]);
} else if (tierHash == keccak256(abi.encodePacked(GOLD))) {
accruedReward = weeklyAirdropAmounts[GOLD].mul(epochsSinceLastClaim);
amount = amount.add(accruedReward);
balanceRequiredToClaim = balanceRequiredToClaim.add(balancesRequiredToClaim[GOLD]);
} else if (tierHash == keccak256(abi.encodePacked(MINT))) {
accruedReward = weeklyAirdropAmounts[MINT].mul(epochsSinceLastClaim);
amount = amount.add(accruedReward);
balanceRequiredToClaim = balanceRequiredToClaim.add(balancesRequiredToClaim[MINT]);
} else if (tierHash == keccak256(abi.encodePacked(BLACK))) {
accruedReward = weeklyAirdropAmounts[BLACK].mul(epochsSinceLastClaim);
amount = amount.add(accruedReward);
balanceRequiredToClaim = balanceRequiredToClaim.add(balancesRequiredToClaim[BLACK]);
}
}
function _processAirdropRewardClaim(address nftFactory, uint256[] memory nftIds) private {
(uint256 amount, uint256 balanceRequiredToClaim, uint256 balanceLocked) = calculateAirdropReward(nftFactory, nftIds);
require(balanceLocked > balanceRequiredToClaim, "Insufficient MTHD tokens staked for claiming airdrop reward");
_updateClaimTimes(nftIds);
IRewardPool(rewardPool).sendERC20(rewardToken, msg.sender, amount);
emit RewardClaimed(nftFactory, msg.sender, rewardToken, amount);
}
function _updateClaimTimes(uint256[] memory nftIds) private {
for (uint256 index = 0; index < nftIds.length; index++) {
uint256 nftId = nftIds[index];
nftLastClaimedRewardAt[nftId] = block.timestamp;
}
}
function _tierMultipliedReward(uint nftId, address nftFactory, uint reward) private view returns (uint multipliedReward) {
string memory tier = getNftTier(nftId, nftFactory);
bytes32 tierHash = keccak256(abi.encodePacked(tier));
if (tierHash == keccak256(abi.encodePacked(PLATINUM))) {
multipliedReward = reward.mul(PLATINUM_LM_REWARD_MULTIPLIER_NUM).div(PLATINUM_LM_REWARD_MULTIPLIER_DENOM);
} else if (tierHash == keccak256(abi.encodePacked(GOLD))) {
multipliedReward = reward.mul(GOLD_LM_REWARD_MULTIPLIER_NUM).div(GOLD_LM_REWARD_MULTIPLIER_DENOM);
} else if (tierHash == keccak256(abi.encodePacked(MINT))) {
multipliedReward = reward.mul(MINT_LM_REWARD_MULTIPLIER_NUM).div(MINT_LM_REWARD_MULTIPLIER_DENOM);
} else if (tierHash == keccak256(abi.encodePacked(BLACK))) {
multipliedReward = reward.mul(BLACK_LM_REWARD_MULTIPLIER_NUM).div(BLACK_LM_REWARD_MULTIPLIER_DENOM);
}
}
function rageQuit() external override {
require(_vaultSet.contains(msg.sender), "UniStaker: no vault");
VaultData storage vaultData = _vaults[msg.sender];
EnumerableSet.AddressSet storage vaultTokens = vaultData.tokens;
require(vaultTokens.length() > 0, "UniStaker: no stake");
for (uint256 index = 0; index < vaultTokens.length(); index++) {
address token = vaultTokens.at(index);
vaultTokens.remove(token);
uint256 amount = vaultData.tokenStake[token];
uint256 newTotal = stakedTokenTotal[token].sub(amount);
assert(newTotal >= 0);
if (newTotal == 0) {
_allStakedTokens.remove(token);
delete stakedTokenTotal[token];
} else {
stakedTokenTotal[token] = newTotal;
}
delete vaultData.tokenStake[token];
}
_vaultSet.remove(msg.sender);
delete _vaults[msg.sender];
emit RageQuit(msg.sender);
}
function stake(
address vault,
address vaultFactory,
address token,
uint256 amount,
bytes calldata permission
) external override onlyOnline {
require(isValidVault(vault, vaultFactory), "UniStaker: vault is not valid");
require(amount != 0, "UniStaker: no amount staked");
require(IERC20(token).balanceOf(msg.sender) >= amount, "insufficient token balance");
_vaultSet.add(vault);
VaultData storage vaultData = _vaults[vault];
require(vaultData.tokens.length() < MAX_TOKENS_STAKED_PER_VAULT, "UniStaker: MAX_TOKENS_STAKED_PER_VAULT reached");
vaultData.tokens.add(token);
vaultData.tokenStake[token] = vaultData.tokenStake[token].add(amount);
_allStakedTokens.add(token);
stakedTokenTotal[token] = stakedTokenTotal[token].add(amount);
TransferHelper.safeTransferFrom(token, msg.sender, vault, amount);
IUniversalVault(vault).lock(token, amount, permission);
if (lmRewards[token].startedAt != 0) {
address rewardCalcInstance = lmRewards[token].rewardCalcInstance;
(,uint rewardEarned,) = IERC2917(rewardCalcInstance).increaseProductivity(msg.sender, amount);
earnedLMRewards[msg.sender][token] = earnedLMRewards[msg.sender][token].add(rewardEarned);
}
emit Staked(vault, amount);
}
function unstakeAndClaim(
address vault,
address vaultFactory,
address recipient,
address token,
uint256 amount,
bool claimBonusReward,
bytes calldata permission
) external override onlyOnline {
require(_vaultSet.contains(vault), "UniStaker: no vault");
VaultData storage vaultData = _vaults[vault];
require(amount != 0, "UniStaker: no amount unstaked");
require(isValidAddress(recipient), "UniStaker: invalid recipient");
require(vaultData.tokens.contains(token), "UniStaker: no token in vault");
require(vaultData.tokenStake[token] >= amount, "UniStaker: insufficient vault token stake");
require(stakedTokenTotal[token] >= amount, "stakedTokenTotal[token] is less than amount being unstaked");
uint rewardEarned = earnedLMRewards[msg.sender][token];
if (lmRewards[token].startedAt != 0) {
address rewardCalcInstance = lmRewards[token].rewardCalcInstance;
(,uint newReward,) = IERC2917(rewardCalcInstance).decreaseProductivity(msg.sender, amount);
rewardEarned = rewardEarned.add(newReward);
}
vaultData.tokenStake[token] = vaultData.tokenStake[token].sub(amount);
if (vaultData.tokenStake[token] == 0) {
vaultData.tokens.remove(token);
delete vaultData.tokenStake[token];
}
stakedTokenTotal[token] = stakedTokenTotal[token].sub(amount);
if (stakedTokenTotal[token] == 0) {
_allStakedTokens.remove(token);
delete stakedTokenTotal[token];
}
IUniversalVault(vault).unlock(token, amount, permission);
emit Unstaked(vault, amount);
if (rewardEarned > 0) {
if (claimBonusReward && lmRewards[token].startedAt != 0) {
for (uint256 index = 0; index < lmRewards[token].bonusTokens.length(); index++) {
address bonusToken = lmRewards[token].bonusTokens.at(index);
uint256 bonusAmount = rewardEarned.mul(lmRewards[token].bonusTokenAmounts[bonusToken]).div(lmRewards[token].amount);
IRewardPool(rewardPool).sendERC20(bonusToken, recipient, bonusAmount);
emit RewardClaimed(vault, recipient, bonusToken, bonusAmount);
}
}
uint multipliedReward = _tierMultipliedReward(uint(vault), vaultFactory, rewardEarned);
uint vestingPortion = multipliedReward.mul(LM_REWARD_VESTING_PORTION_NUM).div(LM_REWARD_VESTING_PORTION_DENOM);
vestingLMRewards[msg.sender][token].push(LMRewardVestingData(vestingPortion, block.timestamp));
vestingLMTokenRewards[msg.sender].add(token);
earnedLMRewards[msg.sender][token] = 0;
IRewardPool(rewardPool).sendERC20(rewardToken, recipient, multipliedReward.sub(vestingPortion));
emit RewardClaimed(vault, recipient, rewardToken, rewardEarned);
}
}
function claimAirdropReward(address nftFactory) external override onlyOnline {
uint256[] memory nftIds = getNftsOfOwner(msg.sender, nftFactory);
_processAirdropRewardClaim(nftFactory, nftIds);
}
function claimAirdropReward(address nftFactory, uint256[] calldata nftIds) external override onlyOnline {
_processAirdropRewardClaim(nftFactory, nftIds);
}
function claimVestedReward() external override onlyOnline {
uint numTokens = vestingLMTokenRewards[msg.sender].length();
for (uint index = 0; index < numTokens; index++) {
address token = vestingLMTokenRewards[msg.sender].at(index);
claimVestedReward(token, vestingLMRewards[msg.sender][token].length);
}
}
function claimVestedReward(address token) external override onlyOnline {
claimVestedReward(token, vestingLMRewards[msg.sender][token].length);
}
function claimVestedReward(address token, uint numVests) public onlyOnline {
require(numVests <= vestingLMRewards[msg.sender][token].length, "num vests can't be greater than available vests");
LMRewardVestingData[] storage vests = vestingLMRewards[msg.sender][token];
uint vestedReward;
for (uint index = 0; index < numVests; index++) {
LMRewardVestingData storage vest = vests[index];
uint duration = block.timestamp.sub(vest.startedAt);
uint vested = vest.amount.mul(duration).div(LM_REWARD_VESTING_PERIOD);
if (vested >= vest.amount) {
vested = vest.amount;
vests[index] = vests[vests.length - 1];
vests.pop();
index--;
numVests--;
if (vests.length == 0) {
vestingLMTokenRewards[msg.sender].remove(token);
break;
}
} else {
vest.amount = vest.amount.sub(vested);
}
vestedReward = vestedReward.add(vested);
}
if (vestedReward > 0) {
IRewardPool(rewardPool).sendERC20(rewardToken, msg.sender, vestedReward);
emit VestedRewardClaimed(msg.sender, rewardToken, vestedReward);
}
}
}
{
"compilationTarget": {
"contracts/staking/UniStaker.sol": "UniStaker"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"adminAddress","type":"address"},{"internalType":"address","name":"rewardPoolFactory","type":"address"},{"internalType":"address","name":"powerSwitchFactory","type":"address"},{"internalType":"address","name":"rewardTokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminshipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"BonusTokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"BonusTokenRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vault","type":"address"}],"name":"RageQuit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"name","type":"string"},{"indexed":true,"internalType":"address","name":"template","type":"address"}],"name":"RewardCalcTemplateActive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"name","type":"string"},{"indexed":true,"internalType":"address","name":"template","type":"address"}],"name":"RewardCalcTemplateAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vaultFactory","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"rewardPool","type":"address"},{"indexed":false,"internalType":"address","name":"powerSwitch","type":"address"}],"name":"UniStakerCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UniStakerFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"factory","type":"address"}],"name":"VaultFactoryRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"factory","type":"address"}],"name":"VaultFactoryRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VestedRewardClaimed","type":"event"},{"inputs":[],"name":"BLACK","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BLACK_LM_REWARD_MULTIPLIER_DENOM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BLACK_LM_REWARD_MULTIPLIER_NUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GOLD","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GOLD_LM_REWARD_MULTIPLIER_DENOM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GOLD_LM_REWARD_MULTIPLIER_NUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LM_REWARD_VESTING_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LM_REWARD_VESTING_PORTION_DENOM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LM_REWARD_VESTING_PORTION_NUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BONUS_TOKENS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TOKENS_STAKED_PER_VAULT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_LM_REWARD_MULTIPLIER_DENOM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_LM_REWARD_MULTIPLIER_NUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_AIRDROP_REWARD_CLAIM_FREQUENCY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PLATINUM","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PLATINUM_LM_REWARD_MULTIPLIER_DENOM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PLATINUM_LM_REWARD_MULTIPLIER_NUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"activeRewardCalcTemplate","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lmToken","type":"address"},{"internalType":"address","name":"bonusToken","type":"address"},{"internalType":"uint256","name":"bonusTokenAmount","type":"uint256"}],"name":"addBonusTokenToLMRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"template","type":"address"}],"name":"addRewardCalcTemplate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"balancesRequiredToClaim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftFactory","type":"address"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"name":"calculateAirdropReward","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"balanceRequiredToClaim","type":"uint256"},{"internalType":"uint256","name":"balanceLocked","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftFactory","type":"address"},{"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"calculateAirdropReward","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"balanceRequiredToClaim","type":"uint256"},{"internalType":"uint256","name":"balanceLocked","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"nftFactory","type":"address"}],"name":"calculateAirdropReward","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"balanceRequiredToClaim","type":"uint256"},{"internalType":"uint256","name":"balanceLocked","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftFactory","type":"address"},{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"name":"claimAirdropReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftFactory","type":"address"}],"name":"claimAirdropReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimVestedReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"numVests","type":"uint256"}],"name":"claimVestedReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"claimVestedReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"earnedLMRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"removeBonusTokenData","type":"bool"}],"name":"endLMRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"fund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getBonusTokenAtIndex","outputs":[{"internalType":"address","name":"bonusToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBonusTokenSetLength","outputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getLMRewardBonusTokenAt","outputs":[{"internalType":"address","name":"bonusToken","type":"address"},{"internalType":"uint256","name":"bonusTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getLMRewardBonusTokensLength","outputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getLMRewardData","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"startedAt","type":"uint256"},{"internalType":"address","name":"rewardCalcInstance","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getLMRewardVestingData","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startedAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"address","name":"nftFactory","type":"address"}],"name":"getNftTier","outputs":[{"internalType":"string","name":"tier","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"nftFactory","type":"address"}],"name":"getNftsOfOwner","outputs":[{"internalType":"uint256[]","name":"nftIds","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumRewardCalcTemplates","outputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumTokensStaked","outputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"getNumTokensStakedInVault","outputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumVaults","outputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getNumVestingLMTokenRewards","outputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"getNumVests","outputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPowerController","outputs":[{"internalType":"address","name":"controller","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPowerSwitch","outputs":[{"internalType":"address","name":"powerSwitch","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getTokenStakedAt","outputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getVaultAt","outputs":[{"internalType":"address","name":"vault","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getVaultFactoryAtIndex","outputs":[{"internalType":"address","name":"factory","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVaultFactorySetLength","outputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getVaultTokenAtIndex","outputs":[{"internalType":"address","name":"vaultToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"getVaultTokenStake","outputs":[{"internalType":"uint256","name":"tokenStake","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getVestingLMTokenAt","outputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOffline","outputs":[{"internalType":"bool","name":"status","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOnline","outputs":[{"internalType":"bool","name":"status","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isShutdown","outputs":[{"internalType":"bool","name":"status","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"isValidAddress","outputs":[{"internalType":"bool","name":"validity","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"factory","type":"address"}],"name":"isValidVault","outputs":[{"internalType":"bool","name":"validity","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nftLastClaimedRewardAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rageQuit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bonusToken","type":"address"}],"name":"registerBonusToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"factory","type":"address"}],"name":"registerVaultFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bonusToken","type":"address"}],"name":"removeBonusToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"factory","type":"address"}],"name":"removeVaultFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceAdminship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueTokensFromRewardPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardCalcTemplateNames","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"rewardCalcTemplates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"tier","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setBalanceRequiredToClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numerator","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"setBlackLMRewardMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numerator","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"setGoldLMRewardMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"newImplementor","type":"address"}],"name":"setImplementorForRewardsCalculator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setLMRewardVestingPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numerator","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"setLMRewardVestingPortion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setLMRewardsPerBlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setMaxBonusTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setMaxStakesPerVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setMinRewardClaimFrequency","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numerator","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"setMintLMRewardMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numerator","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"setPlatinumLMRewardMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"setRewardCalcActiveTemplate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"tier","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setWeeklyAirdropAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"vaultFactory","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"permission","type":"bytes"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakedTokenTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"string","name":"rewardCalcTemplateName","type":"string"}],"name":"startLMRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"startLMRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"transferAdminship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"vaultFactory","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"claimBonusReward","type":"bool"},{"internalType":"bytes","name":"permission","type":"bytes"}],"name":"unstakeAndClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingLMRewards","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startedAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"weeklyAirdropAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]