编译器
0.8.20+commit.a1b79de6
文件 1 的 7:Emissions.sol
pragma solidity ^0.8.20;
contract Emissions {
IERC20 public rewardsToken;
address public ownerEmissions;
uint public duration;
uint public finishAt;
uint public updatedAt;
uint public rewardRate;
uint public rewardPerTokenStored;
mapping(uint256 => uint) public userRewardPerTokenPaid;
mapping(uint256 => uint) public rewards;
uint public totalSupplyScaled = 256;
mapping(uint256 => uint) public balanceOfTokenId;
mapping(uint256 tokenId => uint256) public houseCount;
constructor() {
ownerEmissions = msg.sender;
}
function setRewardToken(address _rewardToken) external onlyOwner {
rewardsToken = IERC20(_rewardToken);
}
modifier onlyOwner() {
require(msg.sender == ownerEmissions, "not authorized");
_;
}
modifier updateReward(uint256 _tokenId) {
rewardPerTokenStored = rewardPerToken();
updatedAt = lastTimeRewardApplicable();
if (_tokenId != 0) {
rewards[_tokenId] = earned(_tokenId);
userRewardPerTokenPaid[_tokenId] = rewardPerTokenStored;
}
_;
}
function lastTimeRewardApplicable() public view returns (uint) {
return _min(finishAt, block.timestamp);
}
function rewardPerToken() public view returns (uint) {
if (totalSupplyScaled == 0) {
return rewardPerTokenStored;
}
return
rewardPerTokenStored +
(rewardRate * (lastTimeRewardApplicable() - updatedAt) * 1e18) /
totalSupplyScaled;
}
function earned(uint256 _tokenId) public view returns (uint) {
return
(((1 + houseCount[_tokenId]) *
(rewardPerToken() - userRewardPerTokenPaid[_tokenId])) / 1e18) +
rewards[_tokenId];
}
function setRewardsDuration(uint _duration) external onlyOwner {
require(finishAt < block.timestamp, "reward duration not finished");
duration = _duration;
}
function notifyRewardAmount(
uint _amount
) external onlyOwner updateReward(0) {
if (block.timestamp >= finishAt) {
rewardRate = _amount / duration;
} else {
uint remainingRewards = (finishAt - block.timestamp) * rewardRate;
rewardRate = (_amount + remainingRewards) / duration;
}
require(rewardRate > 0, "reward rate = 0");
require(
rewardRate * duration <= rewardsToken.balanceOf(address(this)),
"reward amount > balance"
);
finishAt = block.timestamp + duration;
updatedAt = block.timestamp;
}
function _min(uint x, uint y) private pure returns (uint) {
return x <= y ? x : y;
}
}
interface IERC20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
文件 2 的 7:Land.sol
pragma solidity ^0.8.19;
import "./Metadata.sol";
import { WrappedLand } from "./WrappedLand.sol";
import {Emissions} from "./Emissions.sol";
interface Receiver {
function onERC721Received(
address _operator,
address _from,
uint256 _tokenId,
bytes calldata _data
) external returns (bytes4);
}
interface Router {
function WETH() external pure returns (address);
function factory() external pure returns (address);
function addLiquidityETH(
address,
uint256,
uint256,
uint256,
address,
uint256
) external payable returns (uint256, uint256, uint256);
}
interface Factory {
function createPair(address, address) external returns (address);
}
contract Land is Emissions {
uint256 private constant UINT_MAX = type(uint256).max;
uint256 private constant TOTAL_SUPPLY = 256;
uint256 private constant LIQUIDITY_TOKENS = 32;
Router private constant ROUTER =
Router(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
uint256 private constant M1 =
0x5555555555555555555555555555555555555555555555555555555555555555;
uint256 private constant M2 =
0x3333333333333333333333333333333333333333333333333333333333333333;
uint256 private constant M4 =
0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
uint256 private constant H01 =
0x0101010101010101010101010101010101010101010101010101010101010101;
bytes32 private constant TRANSFER_TOPIC =
keccak256(bytes("Transfer(address,address,uint256)"));
bytes32 private constant APPROVAL_TOPIC =
keccak256(bytes("Approval(address,address,uint256)"));
uint256 public constant MINT_COST = 0.15 ether;
uint256 public constant HOUSE_COST = 0.05 ether;
uint8 public constant decimals = 0;
struct User {
bytes32 mask;
mapping(address => uint256) allowance;
mapping(address => bool) approved;
}
struct Info {
bytes32 salt;
address pair;
address owner;
Metadata metadata;
mapping(address => User) users;
mapping(uint256 => address) approved;
address[] holders;
}
Info private info;
mapping(bytes4 => bool) public supportsInterface;
event Transfer(
address indexed from,
address indexed to,
uint256 indexed tokenId
);
event ERC20Transfer(
bytes32 indexed topic0,
address indexed from,
address indexed to,
uint256 tokens
) anonymous;
event Approval(
address indexed owner,
address indexed spender,
uint256 indexed tokenId
);
event ERC20Approval(
bytes32 indexed topic0,
address indexed owner,
address indexed spender,
uint256 tokens
) anonymous;
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
modifier _onlyOwner() {
require(msg.sender == owner());
_;
}
constructor() {
info.owner = tx.origin;
info.metadata = new Metadata();
supportsInterface[0x01ffc9a7] = true;
supportsInterface[0x80ac58cd] = true;
supportsInterface[0x5b5e139f] = true;
info.salt = keccak256(
abi.encodePacked("Salt:", blockhash(block.number - 1))
);
}
function setOwner(address _owner) external _onlyOwner {
info.owner = _owner;
}
function setMetadata(Metadata _metadata) external _onlyOwner {
info.metadata = _metadata;
}
function initialize() external {
require(pair() == address(0x0));
address _this = address(this);
info.users[_this].mask = bytes32(UINT_MAX);
info.holders.push(_this);
emit ERC20Transfer(TRANSFER_TOPIC, address(0x0), _this, TOTAL_SUPPLY);
for (uint256 i = 0; i < TOTAL_SUPPLY; i++) {
emit Transfer(address(0x0), _this, TOTAL_SUPPLY + i + 1);
}
_approveERC20(_this, address(ROUTER), LIQUIDITY_TOKENS);
_transferERC20(_this, tx.origin, 32);
_transferERC20(_this, msg.sender, 60);
_transferERC20(_this, owner(), 4);
}
function initLP() payable external {
require(msg.value > 0);
address _weth = ROUTER.WETH();
address _this = address(this);
info.pair = Factory(ROUTER.factory()).createPair(_weth, _this);
ROUTER.addLiquidityETH{ value: _this.balance }(
_this,
LIQUIDITY_TOKENS,
0,
0,
owner(),
block.timestamp
);
}
function mint() external payable {
address _this = address(this);
uint256 _available = balanceOf(_this);
require(1 <= _available);
uint256 _cost = 1 * MINT_COST;
require(msg.value >= _cost);
_transferERC20(_this, msg.sender, 1);
payable(owner()).transfer(_cost);
if (msg.value > _cost) {
payable(msg.sender).transfer(msg.value - _cost);
}
}
function addHouse(uint256 tokenId) external payable {
uint256 currentCount = houseCount[tokenId];
require(currentCount < 4, "MAX houses");
require(msg.value >= HOUSE_COST);
payable(owner()).transfer(HOUSE_COST);
houseCount[tokenId]++;
totalSupplyScaled++;
if (msg.value > HOUSE_COST) {
payable(msg.sender).transfer(msg.value - HOUSE_COST);
}
}
function approve(address _spender, uint256 _tokens) external returns (bool) {
if (_tokens > TOTAL_SUPPLY && _tokens <= 2 * TOTAL_SUPPLY) {
_approveNFT(_spender, _tokens);
} else {
_approveERC20(msg.sender, _spender, _tokens);
}
return true;
}
function setApprovalForAll(address _operator, bool _approved) external {
info.users[msg.sender].approved[_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
function transfer(address _to, uint256 _tokens) external returns (bool) {
_transferERC20(msg.sender, _to, _tokens);
return true;
}
function transferFrom(
address _from,
address _to,
uint256 _tokens
) external returns (bool) {
if (_tokens > TOTAL_SUPPLY && _tokens <= 2 * TOTAL_SUPPLY) {
_transferNFT(_from, _to, _tokens);
} else {
uint256 _allowance = allowance(_from, msg.sender);
require(_allowance >= _tokens);
if (_allowance != UINT_MAX) {
info.users[_from].allowance[msg.sender] -= _tokens;
}
_transferERC20(_from, _to, _tokens);
}
return true;
}
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
) external {
safeTransferFrom(_from, _to, _tokenId, "");
}
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes memory _data
) public {
_transferNFT(_from, _to, _tokenId);
uint32 _size;
assembly {
_size := extcodesize(_to)
}
if (_size > 0) {
require(
Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) ==
0x150b7a02
);
}
}
function bulkTransfer(address _to, uint256[] memory _tokenIds) external {
_transferNFTs(_to, _tokenIds);
}
function owner() public view returns (address) {
return info.owner;
}
function pair() public view returns (address) {
return info.pair;
}
function holders() public view returns (address[] memory) {
return info.holders;
}
function salt() external view returns (bytes32) {
return info.salt;
}
function metadata() external view returns (address) {
return address(info.metadata);
}
function name() external view returns (string memory) {
return info.metadata.name();
}
function symbol() external view returns (string memory) {
return info.metadata.symbol();
}
function tokenURI(uint256 _tokenId) public view returns (string memory) {
return info.metadata.tokenURI(_tokenId, houseCount[_tokenId]);
}
function totalSupply() public pure returns (uint256) {
return TOTAL_SUPPLY;
}
function maskOf(address _user) public view returns (bytes32) {
return info.users[_user].mask;
}
function balanceOf(address _user) public view returns (uint256) {
return _popcount(maskOf(_user));
}
function allowance(
address _user,
address _spender
) public view returns (uint256) {
return info.users[_user].allowance[_spender];
}
function ownerOf(uint256 _tokenId) public view returns (address) {
unchecked {
require(_tokenId > TOTAL_SUPPLY && _tokenId <= 2 * TOTAL_SUPPLY);
bytes32 _mask = bytes32(1 << (_tokenId - TOTAL_SUPPLY - 1));
address[] memory _holders = holders();
for (uint256 i = 0; i < _holders.length; i++) {
if (maskOf(_holders[i]) & _mask == _mask) {
return _holders[i];
}
}
return address(0x0);
}
}
function getApproved(uint256 _tokenId) public view returns (address) {
require(_tokenId > TOTAL_SUPPLY && _tokenId <= 2 * TOTAL_SUPPLY);
return info.approved[_tokenId];
}
function isApprovedForAll(
address _owner,
address _operator
) public view returns (bool) {
return info.users[_owner].approved[_operator];
}
function getToken(
uint256 _tokenId
)
public
view
returns (address tokenOwner, address approved, string memory uri, uint256 xCoord, uint256 yCoord)
{
return (ownerOf(_tokenId), getApproved(_tokenId), tokenURI(_tokenId), info.metadata.getX(_tokenId), info.metadata.getY(_tokenId));
}
function getTokens(
uint256[] memory _tokenIds
)
public
view
returns (
address[] memory owners,
address[] memory approveds,
string[] memory uris,
uint256[] memory xCoords,
uint256[] memory yCoords
)
{
uint256 _length = _tokenIds.length;
owners = new address[](_length);
approveds = new address[](_length);
uris = new string[](_length);
xCoords = new uint256[](_length);
yCoords = new uint256[](_length);
for (uint256 i = 0; i < _length; i++) {
(owners[i], approveds[i], uris[i], xCoords[i], yCoords[i]) = getToken(_tokenIds[i]);
}
}
function _approveERC20(
address _owner,
address _spender,
uint256 _tokens
) internal {
info.users[_owner].allowance[_spender] = _tokens;
emit ERC20Approval(APPROVAL_TOPIC, _owner, _spender, _tokens);
}
function _approveNFT(address _spender, uint256 _tokenId) internal {
bytes32 _mask = bytes32(1 << (_tokenId - TOTAL_SUPPLY - 1));
require(maskOf(msg.sender) & _mask == _mask);
info.approved[_tokenId] = _spender;
emit Approval(msg.sender, _spender, _tokenId);
}
function _transferERC20(
address _from,
address _to,
uint256 _tokens
) internal {
unchecked {
bytes32 _mask;
uint256 _pos = 0;
uint256 _count = 0;
uint256 _n = uint256(maskOf(_from));
uint256[] memory _tokenIds = new uint256[](_tokens);
while (_n > 0 && _count < _tokens) {
if (_n & 1 == 1) {
_mask |= bytes32(1 << _pos);
_tokenIds[_count++] = TOTAL_SUPPLY + _pos + 1;
}
_pos++;
_n >>= 1;
}
require(_count == _tokens);
require(maskOf(_from) & _mask == _mask);
_transfer(_from, _to, _mask, _tokenIds);
}
}
function _transferNFT(address _from, address _to, uint256 _tokenId) internal {
unchecked {
require(_tokenId > TOTAL_SUPPLY && _tokenId <= 2 * TOTAL_SUPPLY);
bytes32 _mask = bytes32(1 << (_tokenId - TOTAL_SUPPLY - 1));
require(maskOf(_from) & _mask == _mask);
require(
msg.sender == _from ||
msg.sender == getApproved(_tokenId) ||
isApprovedForAll(_from, msg.sender)
);
uint256[] memory _tokenIds = new uint256[](1);
_tokenIds[0] = _tokenId;
_transfer(_from, _to, _mask, _tokenIds);
}
}
function _transferNFTs(address _to, uint256[] memory _tokenIds) internal {
unchecked {
bytes32 _mask;
for (uint256 i = 0; i < _tokenIds.length; i++) {
_mask |= bytes32(1 << (_tokenIds[i] - TOTAL_SUPPLY - 1));
}
require(_popcount(_mask) == _tokenIds.length);
require(maskOf(msg.sender) & _mask == _mask);
_transfer(msg.sender, _to, _mask, _tokenIds);
}
}
function _transfer(
address _from,
address _to,
bytes32 _mask,
uint256[] memory _tokenIds
) internal {
unchecked {
require(_tokenIds.length > 0);
for (uint256 i = 0; i < _tokenIds.length; i++) {
if (getApproved(_tokenIds[i]) != address(0x0)) {
info.approved[_tokenIds[i]] = address(0x0);
emit Approval(address(0x0), address(0x0), _tokenIds[i]);
}
emit Transfer(_from, _to, _tokenIds[i]);
}
info.users[_from].mask ^= _mask;
bool _from0 = maskOf(_from) == 0x0;
bool _to0 = maskOf(_to) == 0x0;
info.users[_to].mask |= _mask;
if (_from0) {
uint256 _index;
address[] memory _holders = holders();
for (uint256 i = 0; i < _holders.length; i++) {
if (_holders[i] == _from) {
_index = i;
break;
}
}
if (_to0) {
info.holders[_index] = _to;
} else {
info.holders[_index] = _holders[_holders.length - 1];
info.holders.pop();
}
} else if (_to0) {
info.holders.push(_to);
}
require(maskOf(_from) & maskOf(_to) == 0x0);
emit ERC20Transfer(TRANSFER_TOPIC, _from, _to, _tokenIds.length);
}
}
function _popcount(bytes32 _b) internal pure returns (uint256) {
uint256 _n = uint256(_b);
if (_n == UINT_MAX) {
return 256;
}
unchecked {
_n -= (_n >> 1) & M1;
_n = (_n & M2) + ((_n >> 2) & M2);
_n = (_n + (_n >> 4)) & M4;
_n = (_n * H01) >> 248;
}
return _n;
}
function getAllData() public view returns (
address[] memory owners,
string[] memory uris,
uint256[] memory xCoords,
uint256[] memory yCoords,
uint256[] memory tokenIdList
) {
tokenIdList = new uint256[](256);
for (uint256 i = 257; i < 513; i++) {
tokenIdList[i - 257] = i;
}
(owners, , uris, xCoords, yCoords) = getTokens(tokenIdList);
return (owners, uris, xCoords, yCoords, tokenIdList);
}
function getReward(uint256 _tokenId) public updateReward(_tokenId) {
require(ownerOf(_tokenId) == msg.sender, "Not Owner");
uint reward = rewards[_tokenId];
if (reward > 0) {
rewards[_tokenId] = 0;
rewardsToken.transfer(msg.sender, reward);
}
}
function getMultipleRewards(uint256[] calldata tokenIds) external {
for (uint i = 0; i < tokenIds.length; i++) {
getReward(tokenIds[i]);
}
}
}
contract Deploy {
Land public immutable land;
WrappedLand public immutable wLand;
constructor() {
land = new Land();
land.initialize();
wLand = new WrappedLand(address(land));
uint256 landBal = land.balanceOf(address(this));
land.approve(address(wLand), type(uint256).max);
wLand.wrap(landBal);
uint256 wLandBal = wLand.balanceOf(address(this));
land.setRewardToken(address(wLand));
land.setRewardsDuration(7 days);
wLand.transfer(address(land), wLandBal);
land.notifyRewardAmount(wLandBal);
}
}
文件 3 的 7:Math.sol
pragma solidity ^0.8.20;
library Math {
error MathOverflowedMulDiv();
enum Rounding {
Floor,
Ceil,
Trunc,
Expand
}
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
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) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
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) {
if (b == 0) {
return a / b;
}
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0 = x * y;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (0 - denominator);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
文件 4 的 7:Metadata.sol
pragma solidity ^0.8.19;
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
library Base64 {
bytes internal constant TABLE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
function encode(bytes memory data) internal pure returns (string memory) {
uint256 len = data.length;
if (len == 0) return "";
uint256 encodedLen = 4 * ((len + 2) / 3);
bytes memory result = new bytes(encodedLen + 32);
bytes memory table = TABLE;
assembly {
let tablePtr := add(table, 1)
let resultPtr := add(result, 32)
for {
let i := 0
} lt(i, len) {
} {
i := add(i, 3)
let input := and(mload(add(data, i)), 0xffffff)
let out := mload(add(tablePtr, and(shr(18, input), 0x3F)))
out := shl(8, out)
out := add(
out,
and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF)
)
out := shl(8, out)
out := add(
out,
and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF)
)
out := shl(8, out)
out := add(out, and(mload(add(tablePtr, and(input, 0x3F))), 0xFF))
out := shl(224, out)
mstore(resultPtr, out)
resultPtr := add(resultPtr, 4)
}
switch mod(len, 3)
case 1 {
mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
}
case 2 {
mstore(sub(resultPtr, 1), shl(248, 0x3d))
}
mstore(result, encodedLen)
}
return string(result);
}
}
interface LAND {
function salt() external view returns (bytes32);
}
contract Metadata {
string public name = "Land";
string public symbol = "LAND";
LAND public immutable land;
uint256 constant WIDTH = 16;
uint256 constant HEIGHT = 16;
constructor() {
land = LAND(msg.sender);
}
function tokenURI(uint256 tokenId, uint256 _houseCount) external view returns (string memory) {
tokenId = tokenId - 256;
string memory fillColor;
if (_houseCount == 0) {
fillColor = "13fc03";
} else if (_houseCount == 1) {
fillColor = "f8fc03";
} else if (_houseCount == 2) {
fillColor = "fcad03";
} else if (_houseCount == 3) {
fillColor = "fc0303";
} else if (_houseCount == 4) {
fillColor = "d203fc";
}
string[17] memory parts;
parts[
0
] = string(abi.encodePacked('<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.base { font-family: monospace; font-size: 28px; }</style><rect width="100%" height="100%" fill="#', fillColor,'" /><text x="50%" y="50%" text-anchor="middle" fill="#648efa" class="base">('));
parts[1] = Strings.toString(getX(tokenId));
parts[2] = ",";
parts[3] = Strings.toString(getY(tokenId));
parts[4] = ")</text></svg>";
string memory output = string(
abi.encodePacked(parts[0], parts[1], parts[2], parts[3], parts[4])
);
string memory json = Base64.encode(
bytes(
string(
abi.encodePacked(
'{"name": "Land (',
Strings.toString(getX(tokenId)),
",",
Strings.toString(getY(tokenId)),
')", "description": "Land is stored on chain and represents a ',
Strings.toString(WIDTH),
"x",
Strings.toString(HEIGHT),
' grid of land. Earn emissions by buying houses!", "image": "data:image/svg+xml;base64,',
Base64.encode(bytes(output)),
'"}'
)
)
)
);
output = string(abi.encodePacked("data:application/json;base64,", json));
return output;
}
function getX(uint256 tokenId) public pure returns (uint256) {
return (tokenId - 1) % WIDTH;
}
function getY(uint256 tokenId) public pure returns (uint256) {
return ((tokenId - 1) / HEIGHT) % HEIGHT;
}
}
文件 5 的 7:SignedMath.sol
pragma solidity ^0.8.20;
library SignedMath {
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
function average(int256 a, int256 b) internal pure returns (int256) {
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
function abs(int256 n) internal pure returns (uint256) {
unchecked {
return uint256(n >= 0 ? n : -n);
}
}
}
文件 6 的 7:Strings.sol
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
error StringsInsufficientHexLength(uint256 value, uint256 length);
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}
文件 7 的 7:WrappedLand.sol
pragma solidity ^0.8.19;
interface Callable {
function tokenCallback(
address _from,
uint256 _tokens,
bytes calldata _data
) external returns (bool);
}
interface ILand {
function balanceOf(address) external view returns (uint256);
function allowance(address, address) external view returns (uint256);
function isApprovedForAll(address, address) external view returns (bool);
function transfer(address _to, uint256 _tokens) external returns (bool);
function transferFrom(
address _from,
address _to,
uint256 _tokens
) external returns (bool);
}
contract WrappedLand {
uint256 private constant UINT_MAX = type(uint256).max;
ILand public immutable land;
string public constant name = "Wrapped Land";
string public constant symbol = "wLAND";
uint8 public constant decimals = 18;
struct User {
uint256 balance;
mapping(address => uint256) allowance;
}
struct Info {
mapping(address => User) users;
}
Info private info;
event Transfer(address indexed from, address indexed to, uint256 tokens);
event Approval(
address indexed owner,
address indexed spender,
uint256 tokens
);
event Wrap(address indexed owner, uint256 tokens);
event Unwrap(address indexed owner, uint256 tokens);
constructor(address _land) {
land = ILand(_land);
}
function wrap(uint256 _tokensOrTokenId) external {
uint256 _balanceBefore = land.balanceOf(address(this));
land.transferFrom(msg.sender, address(this), _tokensOrTokenId);
uint256 _wrapped = land.balanceOf(address(this)) - _balanceBefore;
require(_wrapped > 0);
info.users[msg.sender].balance += _wrapped * 1e18;
emit Transfer(address(0x0), msg.sender, _wrapped * 1e18);
emit Wrap(msg.sender, _wrapped);
}
function unwrap(uint256 _tokens) external {
require(_tokens > 0);
require(balanceOf(msg.sender) >= _tokens * 1e18);
info.users[msg.sender].balance -= _tokens * 1e18;
land.transfer(msg.sender, _tokens);
emit Transfer(msg.sender, address(0x0), _tokens * 1e18);
emit Unwrap(msg.sender, _tokens);
}
function transfer(address _to, uint256 _tokens) external returns (bool) {
return _transfer(msg.sender, _to, _tokens);
}
function approve(address _spender, uint256 _tokens) external returns (bool) {
info.users[msg.sender].allowance[_spender] = _tokens;
emit Approval(msg.sender, _spender, _tokens);
return true;
}
function transferFrom(
address _from,
address _to,
uint256 _tokens
) external returns (bool) {
uint256 _allowance = allowance(_from, msg.sender);
require(_allowance >= _tokens);
if (_allowance != UINT_MAX) {
info.users[_from].allowance[msg.sender] -= _tokens;
}
return _transfer(_from, _to, _tokens);
}
function transferAndCall(
address _to,
uint256 _tokens,
bytes calldata _data
) external returns (bool) {
_transfer(msg.sender, _to, _tokens);
uint32 _size;
assembly {
_size := extcodesize(_to)
}
if (_size > 0) {
require(Callable(_to).tokenCallback(msg.sender, _tokens, _data));
}
return true;
}
function totalSupply() public view returns (uint256) {
return land.balanceOf(address(this)) * 1e18;
}
function balanceOf(address _user) public view returns (uint256) {
return info.users[_user].balance;
}
function allowance(
address _user,
address _spender
) public view returns (uint256) {
return info.users[_user].allowance[_spender];
}
function allInfoFor(
address _user
)
external
view
returns (
uint256 totalTokens,
uint256 userLANDs,
uint256 userAllowance,
bool userApprovedForAll,
uint256 userBalance
)
{
totalTokens = totalSupply();
userLANDs = land.balanceOf(_user);
userAllowance = land.allowance(_user, address(this));
userApprovedForAll = land.isApprovedForAll(_user, address(this));
userBalance = balanceOf(_user);
}
function _transfer(
address _from,
address _to,
uint256 _tokens
) internal returns (bool) {
unchecked {
require(balanceOf(_from) >= _tokens);
info.users[_from].balance -= _tokens;
info.users[_to].balance += _tokens;
emit Transfer(_from, _to, _tokens);
return true;
}
}
}
{
"compilationTarget": {
"src/Land.sol": "Land"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/"
]
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes32","name":"topic0","type":"bytes32"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokens","type":"uint256"}],"name":"ERC20Approval","type":"event"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes32","name":"topic0","type":"bytes32"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokens","type":"uint256"}],"name":"ERC20Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"HOUSE_COST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_COST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"addHouse","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_tokens","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"balanceOfTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"bulkTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"duration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finishAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllData","outputs":[{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"string[]","name":"uris","type":"string[]"},{"internalType":"uint256[]","name":"xCoords","type":"uint256[]"},{"internalType":"uint256[]","name":"yCoords","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenIdList","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"getMultipleRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getToken","outputs":[{"internalType":"address","name":"tokenOwner","type":"address"},{"internalType":"address","name":"approved","type":"address"},{"internalType":"string","name":"uri","type":"string"},{"internalType":"uint256","name":"xCoord","type":"uint256"},{"internalType":"uint256","name":"yCoord","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"getTokens","outputs":[{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"address[]","name":"approveds","type":"address[]"},{"internalType":"string[]","name":"uris","type":"string[]"},{"internalType":"uint256[]","name":"xCoords","type":"uint256[]"},{"internalType":"uint256[]","name":"yCoords","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"holders","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"houseCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initLP","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"maskOf","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadata","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownerEmissions","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pair","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"salt","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Metadata","name":"_metadata","type":"address"}],"name":"setMetadata","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardToken","type":"address"}],"name":"setRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_duration","type":"uint256"}],"name":"setRewardsDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"totalSupplyScaled","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokens","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokens","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updatedAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"userRewardPerTokenPaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]