文件 1 的 10:ERC721Farm.sol
pragma solidity ^0.8.0;
import '@openzeppelin/contracts/utils/math/Math.sol';
import '@solidstate/contracts/token/ERC721/IERC721.sol';
import '@solidstate/contracts/token/ERC721/IERC721Receiver.sol';
import '@solidstate/contracts/utils/EnumerableSet.sol';
import '../token/IMagic.sol';
contract ERC721Farm is IERC721Receiver {
using EnumerableSet for EnumerableSet.UintSet;
address private immutable MAGIC;
address private immutable ERC721_CONTRACT;
uint256 public immutable EXPIRATION;
uint256 private immutable RATE;
mapping(address => EnumerableSet.UintSet) private _deposits;
mapping(address => mapping(uint256 => uint256)) public depositBlocks;
constructor(
address magic,
address erc721,
uint256 rate,
uint256 expiration
) {
MAGIC = magic;
ERC721_CONTRACT = erc721;
RATE = rate;
EXPIRATION = block.number + expiration;
}
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external pure override returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
function depositsOf(address account)
external
view
returns (uint256[] memory)
{
EnumerableSet.UintSet storage depositSet = _deposits[account];
uint256[] memory tokenIds = new uint256[](depositSet.length());
for (uint256 i; i < depositSet.length(); i++) {
tokenIds[i] = depositSet.at(i);
}
return tokenIds;
}
function calculateRewards(address account, uint256[] memory tokenIds)
public
view
returns (uint256[] memory rewards)
{
rewards = new uint256[](tokenIds.length);
for (uint256 i; i < tokenIds.length; i++) {
uint256 tokenId = tokenIds[i];
rewards[i] =
RATE *
(_deposits[account].contains(tokenId) ? 1 : 0) *
(Math.min(block.number, EXPIRATION) -
depositBlocks[account][tokenId]);
}
}
function claimRewards(uint256[] calldata tokenIds) public {
uint256 reward;
uint256 block = Math.min(block.number, EXPIRATION);
uint256[] memory rewards = calculateRewards(msg.sender, tokenIds);
for (uint256 i; i < tokenIds.length; i++) {
reward += rewards[i];
depositBlocks[msg.sender][tokenIds[i]] = block;
}
if (reward > 0) {
IMagic(MAGIC).mint(msg.sender, reward);
}
}
function deposit(uint256[] calldata tokenIds) external {
claimRewards(tokenIds);
for (uint256 i; i < tokenIds.length; i++) {
IERC721(ERC721_CONTRACT).safeTransferFrom(
msg.sender,
address(this),
tokenIds[i],
''
);
_deposits[msg.sender].add(tokenIds[i]);
}
}
function withdraw(uint256[] calldata tokenIds) external {
claimRewards(tokenIds);
for (uint256 i; i < tokenIds.length; i++) {
require(
_deposits[msg.sender].contains(tokenIds[i]),
'ERC721Farm: token not deposited'
);
_deposits[msg.sender].remove(tokenIds[i]);
IERC721(ERC721_CONTRACT).safeTransferFrom(
address(this),
msg.sender,
tokenIds[i],
''
);
}
}
}
文件 2 的 10:EnumerableSet.sol
pragma solidity ^0.8.0;
library EnumerableSet {
struct Set {
bytes32[] _values;
mapping (bytes32 => uint) _indexes;
}
struct Bytes32Set {
Set _inner;
}
struct AddressSet {
Set _inner;
}
struct UintSet {
Set _inner;
}
function at (
Bytes32Set storage set,
uint index
) internal view returns (bytes32) {
return _at(set._inner, index);
}
function at (
AddressSet storage set,
uint index
) internal view returns (address) {
return address(uint160(uint(_at(set._inner, index))));
}
function at (
UintSet storage set,
uint index
) internal view returns (uint) {
return uint(_at(set._inner, index));
}
function contains (
Bytes32Set storage set,
bytes32 value
) internal view returns (bool) {
return _contains(set._inner, value);
}
function contains (
AddressSet storage set,
address value
) internal view returns (bool) {
return _contains(set._inner, bytes32(uint(uint160(value))));
}
function contains (
UintSet storage set,
uint value
) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function indexOf (
Bytes32Set storage set,
bytes32 value
) internal view returns (uint) {
return _indexOf(set._inner, value);
}
function indexOf (
AddressSet storage set,
address value
) internal view returns (uint) {
return _indexOf(set._inner, bytes32(uint(uint160(value))));
}
function indexOf (
UintSet storage set,
uint value
) internal view returns (uint) {
return _indexOf(set._inner, bytes32(value));
}
function length (
Bytes32Set storage set
) internal view returns (uint) {
return _length(set._inner);
}
function length (
AddressSet storage set
) internal view returns (uint) {
return _length(set._inner);
}
function length (
UintSet storage set
) internal view returns (uint) {
return _length(set._inner);
}
function add (
Bytes32Set storage set,
bytes32 value
) internal returns (bool) {
return _add(set._inner, value);
}
function add (
AddressSet storage set,
address value
) internal returns (bool) {
return _add(set._inner, bytes32(uint(uint160(value))));
}
function add (
UintSet storage set,
uint value
) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove (
Bytes32Set storage set,
bytes32 value
) internal returns (bool) {
return _remove(set._inner, value);
}
function remove (
AddressSet storage set,
address value
) internal returns (bool) {
return _remove(set._inner, bytes32(uint(uint160(value))));
}
function remove (
UintSet storage set,
uint value
) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function _at (
Set storage set,
uint index
) private view returns (bytes32) {
require(set._values.length > index, 'EnumerableSet: index out of bounds');
return set._values[index];
}
function _contains (
Set storage set,
bytes32 value
) private view returns (bool) {
return set._indexes[value] != 0;
}
function _indexOf (
Set storage set,
bytes32 value
) private view returns (uint) {
unchecked {
return set._indexes[value] - 1;
}
}
function _length (
Set storage set
) private view returns (uint) {
return set._values.length;
}
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) {
uint valueIndex = set._indexes[value];
if (valueIndex != 0) {
uint index = valueIndex - 1;
bytes32 last = set._values[set._values.length - 1];
set._values[index] = last;
set._indexes[last] = index + 1;
set._values.pop();
delete set._indexes[value];
return true;
} else {
return false;
}
}
}
文件 3 的 10:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface (
bytes4 interfaceId
) external view returns (bool);
}
文件 4 的 10:IERC20.sol
pragma solidity ^0.8.0;
import {IERC20Internal} from './IERC20Internal.sol';
interface IERC20 is IERC20Internal {
function totalSupply () external view returns (uint256);
function balanceOf (
address account
) external view returns (uint256);
function allowance (
address holder,
address spender
) external view returns (uint256);
function approve (
address spender,
uint256 amount
) external returns (bool);
function transfer (
address recipient,
uint256 amount
) external returns (bool);
function transferFrom (
address holder,
address recipient,
uint256 amount
) external returns (bool);
}
文件 5 的 10:IERC20Internal.sol
pragma solidity ^0.8.0;
interface IERC20Internal {
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}
文件 6 的 10:IERC721.sol
pragma solidity ^0.8.0;
import {IERC165} from '../../introspection/IERC165.sol';
import {IERC721Internal} from './IERC721Internal.sol';
interface IERC721 is IERC721Internal, IERC165 {
function balanceOf (
address account
) external view returns (uint256 balance);
function ownerOf (
uint256 tokenId
) external view returns (address owner);
function safeTransferFrom (
address from,
address to,
uint256 tokenId
) external payable;
function safeTransferFrom (
address from,
address to,
uint256 tokenId,
bytes calldata data
) external payable;
function transferFrom (
address from,
address to,
uint256 tokenId
) external payable;
function approve (
address operator,
uint256 tokenId
) external payable;
function getApproved (
uint256 tokenId
) external view returns (address operator);
function setApprovalForAll (
address operator,
bool status
) external;
function isApprovedForAll (
address account,
address operator
) external view returns (bool status);
}
文件 7 的 10:IERC721Internal.sol
pragma solidity ^0.8.0;
interface IERC721Internal {
event Transfer (
address indexed from,
address indexed to,
uint256 indexed tokenId
);
event Approval (
address indexed owner,
address indexed operator,
uint256 indexed tokenId
);
event ApprovalForAll (
address indexed owner,
address indexed operator,
bool approved
);
}
文件 8 的 10:IERC721Receiver.sol
pragma solidity ^0.8.0;
interface IERC721Receiver {
function onERC721Received (
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 9 的 10:IMagic.sol
pragma solidity ^0.8.0;
import '@solidstate/contracts/token/ERC20/IERC20.sol';
interface IMagic is IERC20 {
function mint(address account, uint256 amount) external;
}
文件 10 的 10:Math.sol
pragma solidity ^0.8.0;
library Math {
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b + (a % b == 0 ? 0 : 1);
}
}
{
"compilationTarget": {
"contracts/farm/ERC721Farm.sol": "ERC721Farm"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"magic","type":"address"},{"internalType":"address","name":"erc721","type":"address"},{"internalType":"uint256","name":"rate","type":"uint256"},{"internalType":"uint256","name":"expiration","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"EXPIRATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"calculateRewards","outputs":[{"internalType":"uint256[]","name":"rewards","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"depositBlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"depositsOf","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]