编译器
0.8.23+commit.f704f362
文件 1 的 17:Base64.sol
pragma solidity ^0.8.20;
library Base64 {
string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
function encode(bytes memory data) internal pure returns (string memory) {
if (data.length == 0) return "";
string memory table = _TABLE;
string memory result = new string(4 * ((data.length + 2) / 3));
assembly {
let tablePtr := add(table, 1)
let resultPtr := add(result, 32)
for {
let dataPtr := data
let endPtr := add(data, mload(data))
} lt(dataPtr, endPtr) {
} {
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr := add(resultPtr, 1)
mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr := add(resultPtr, 1)
}
switch mod(mload(data), 3)
case 1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case 2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
}
return result;
}
}
文件 2 的 17:Constants.sol
pragma solidity ^0.8.23;
contract Constants {
uint256 public constant NUM_ROWS = 25;
uint256 public constant NUM_COLUMNS = 25;
}
文件 3 的 17:Context.sol
pragma solidity ^0.8.20;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 4 的 17:Descriptor.sol
pragma solidity ^0.8.23;
import {Constants} from "./Constants.sol";
import {ITokenDescriptor} from "./ITokenDescriptor.sol";
import {JsonWriter} from "solidity-json-writer/JsonWriter.sol";
import {Base64} from '@openzeppelin/contracts/utils/Base64.sol';
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
contract Descriptor is ITokenDescriptor, Constants {
using JsonWriter for JsonWriter.Json;
function generateMetadata(uint256 tokenId, Token calldata token)
external
view
returns (string memory)
{
JsonWriter.Json memory writer;
writer = writer.writeStartObject();
writer = writer.writeStringProperty(
'name',
string.concat('LINE ', Strings.toString(tokenId))
);
writer = writer.writeStringProperty(
'description',
'LINE is a dynamic photographic series of 250 tokens, each positioned within a meticulously crafted landscape of 625 coordinates. Tokens act like lenses, where location influences perception. They may cross this terrain while their paths intersect in time and space. By Figure31'
);
writer = writer.writeStringProperty(
'external_url',
'https://line.fingerprintsdao.xyz'
);
uint256 currentImageIndex = _getCurrentPanoramicImageIndex(token);
writer = writer.writeStringProperty(
'image',
string.concat('ar://Y-05cY1jiKkVn9aCL3Di3sOWfCUZRPLaoASs0LYJOsU/', Strings.toString(currentImageIndex), '.jpg')
);
writer = _generateAttributes(writer, token);
writer = writer.writeEndObject();
return string(
abi.encodePacked(
'data:application/json;base64,',
Base64.encode(abi.encodePacked(writer.value))
)
);
}
function _determineCurrentImagePoint(Token calldata token)
private
view
returns (uint256, uint256)
{
uint256 numDaysPassed = (block.timestamp - token.timestamp) / 1 days;
uint256 numPanoramicPoints;
if (!token.isStar) {
numPanoramicPoints = 10;
} else {
numPanoramicPoints = 16;
}
uint256 panoramicPoint = numDaysPassed % numPanoramicPoints;
if (panoramicPoint % 2 == 0) {
return (token.current.x, token.current.y);
}
uint256 x;
uint256 y;
if (token.isStar) {
if (panoramicPoint == 1) {
x = token.current.x - 1;
y = token.current.y;
} else if (panoramicPoint == 3) {
x = token.current.x - 1;
y = token.current.y + 1;
} else if (panoramicPoint == 5) {
x = token.current.x;
y = token.current.y + 1;
} else if (panoramicPoint == 7) {
x = token.current.x + 1;
y = token.current.y + 1;
} else if (panoramicPoint == 9) {
x = token.current.x + 1;
y = token.current.y;
} else if (panoramicPoint == 11) {
x = token.current.x + 1;
y = token.current.y - 1;
} else if (panoramicPoint == 13) {
x = token.current.x;
y = token.current.y - 1;
} else if (panoramicPoint == 15) {
x = token.current.x - 1;
y = token.current.y - 1;
}
return (x,y);
}
if (token.direction == Direction.DOWN) {
if (panoramicPoint == 1) {
x = token.current.x - 1;
y = token.current.y;
} else if (panoramicPoint == 3) {
x = token.current.x - 1;
y = token.current.y - 1;
} else if (panoramicPoint == 5) {
x = token.current.x;
y = token.current.y - 1;
} else if (panoramicPoint == 7) {
x = token.current.x + 1;
y = token.current.y - 1;
} else if (panoramicPoint == 9) {
x = token.current.x + 1;
y = token.current.y;
}
}
if (token.direction == Direction.UP) {
if (panoramicPoint == 1) {
x = token.current.x - 1;
y = token.current.y;
} else if (panoramicPoint == 3) {
x = token.current.x - 1;
y = token.current.y + 1;
} else if (panoramicPoint == 5) {
x = token.current.x;
y = token.current.y + 1;
} else if (panoramicPoint == 7) {
x = token.current.x + 1;
y = token.current.y + 1;
} else if (panoramicPoint == 9) {
x = token.current.x + 1;
y = token.current.y;
}
}
return (x,y);
}
function _getCurrentPanoramicImageIndex(Token calldata token)
private
view
returns (uint256)
{
(uint256 x, uint256 y) = _determineCurrentImagePoint(token);
return _calculateImageIndex(x, y);
}
function _calculateImageIndex(uint256 x, uint256 y)
private
pure
returns (uint256)
{
uint256 yIndex = (NUM_ROWS - 1) - y;
return ((NUM_ROWS - yIndex - 1) * NUM_COLUMNS) + x;
}
function _generateAttributes(JsonWriter.Json memory _writer, Token calldata token)
private
view
returns (JsonWriter.Json memory writer)
{
writer = _writer.writeStartArray('attributes');
(uint256 imagePointX, uint256 imagePointY) = _determineCurrentImagePoint(token);
writer = _addStringAttribute(writer, 'Origin Point', string.concat(Strings.toString(token.current.x), ',', Strings.toString(token.current.y)));
writer = _addStringAttribute(writer, 'Image Point', string.concat(Strings.toString(imagePointX), ',', Strings.toString(imagePointY)));
writer = _addStringAttribute(writer, 'Type', token.direction == Direction.UP ? 'Up' : 'Down');
writer = _addStringAttribute(writer, 'Starting Point', string.concat(Strings.toString(token.initial.x), ',', Strings.toString(token.initial.y)));
writer = _addStringAttribute(writer, 'Has Reached End', token.hasReachedEnd == true ? 'Yes' : 'No');
writer = _addStringAttribute(writer, 'Is Star', token.isStar == true ? 'Yes' : 'No');
writer = _addStringAttribute(writer, 'Movements', Strings.toString(token.numMovements));
writer = writer.writeEndArray();
}
function _addStringAttribute(
JsonWriter.Json memory _writer,
string memory key,
string memory value
) private pure returns (JsonWriter.Json memory writer) {
writer = _writer.writeStartObject();
writer = writer.writeStringProperty('trait_type', key);
writer = writer.writeStringProperty('value', value);
writer = writer.writeEndObject();
}
}
文件 5 的 17:ERC20.sol
pragma solidity >=0.8.0;
abstract contract ERC20 {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
文件 6 的 17:ERC721.sol
pragma solidity >=0.8.0;
abstract contract ERC721 {
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
function approve(address spender, uint256 id) public virtual {
address owner = _ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
"NOT_AUTHORIZED"
);
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes calldata data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 ||
interfaceId == 0x80ac58cd ||
interfaceId == 0x5b5e139f;
}
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
文件 7 的 17:ITokenDescriptor.sol
pragma solidity ^0.8.23;
interface ITokenDescriptor {
enum Direction {
UP,
DOWN
}
struct Coordinate {
uint256 x;
uint256 y;
}
struct Token {
Coordinate initial;
Coordinate current;
uint256 timestamp;
bool hasReachedEnd;
bool isStar;
Direction direction;
uint256 numMovements;
}
function generateMetadata(uint256 tokenId, ITokenDescriptor.Token calldata token)
external
view
returns (string memory);
}
文件 8 的 17:JsonWriter.sol
pragma solidity ^0.8.0;
library JsonWriter {
using JsonWriter for string;
struct Json {
int256 depthBitTracker;
string value;
}
bytes1 constant BACKSLASH = bytes1(uint8(92));
bytes1 constant BACKSPACE = bytes1(uint8(8));
bytes1 constant CARRIAGE_RETURN = bytes1(uint8(13));
bytes1 constant DOUBLE_QUOTE = bytes1(uint8(34));
bytes1 constant FORM_FEED = bytes1(uint8(12));
bytes1 constant FRONTSLASH = bytes1(uint8(47));
bytes1 constant HORIZONTAL_TAB = bytes1(uint8(9));
bytes1 constant NEWLINE = bytes1(uint8(10));
string constant TRUE = "true";
string constant FALSE = "false";
bytes1 constant OPEN_BRACE = "{";
bytes1 constant CLOSED_BRACE = "}";
bytes1 constant OPEN_BRACKET = "[";
bytes1 constant CLOSED_BRACKET = "]";
bytes1 constant LIST_SEPARATOR = ",";
int256 constant MAX_INT256 = type(int256).max;
function writeStartArray(Json memory json)
internal
pure
returns (Json memory)
{
return writeStart(json, OPEN_BRACKET);
}
function writeStartArray(Json memory json, string memory propertyName)
internal
pure
returns (Json memory)
{
return writeStart(json, propertyName, OPEN_BRACKET);
}
function writeStartObject(Json memory json)
internal
pure
returns (Json memory)
{
return writeStart(json, OPEN_BRACE);
}
function writeStartObject(Json memory json, string memory propertyName)
internal
pure
returns (Json memory)
{
return writeStart(json, propertyName, OPEN_BRACE);
}
function writeEndArray(Json memory json)
internal
pure
returns (Json memory)
{
return writeEnd(json, CLOSED_BRACKET);
}
function writeEndObject(Json memory json)
internal
pure
returns (Json memory)
{
return writeEnd(json, CLOSED_BRACE);
}
function writeAddressProperty(
Json memory json,
string memory propertyName,
address value
) internal pure returns (Json memory) {
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, '"', propertyName, '": "', addressToString(value), '"'));
} else {
json.value = string(abi.encodePacked(json.value, '"', propertyName, '": "', addressToString(value), '"'));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeAddressValue(Json memory json, address value)
internal
pure
returns (Json memory)
{
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, '"', addressToString(value), '"'));
} else {
json.value = string(abi.encodePacked(json.value, '"', addressToString(value), '"'));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeBooleanProperty(
Json memory json,
string memory propertyName,
bool value
) internal pure returns (Json memory) {
string memory strValue;
if (value) {
strValue = TRUE;
} else {
strValue = FALSE;
}
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, '"', propertyName, '": ', strValue));
} else {
json.value = string(abi.encodePacked(json.value, '"', propertyName, '": ', strValue));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeBooleanValue(Json memory json, bool value)
internal
pure
returns (Json memory)
{
string memory strValue;
if (value) {
strValue = TRUE;
} else {
strValue = FALSE;
}
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, strValue));
} else {
json.value = string(abi.encodePacked(json.value, strValue));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeIntProperty(
Json memory json,
string memory propertyName,
int256 value
) internal pure returns (Json memory) {
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, '"', propertyName, '": ', intToString(value)));
} else {
json.value = string(abi.encodePacked(json.value, '"', propertyName, '": ', intToString(value)));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeIntValue(Json memory json, int256 value)
internal
pure
returns (Json memory)
{
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, intToString(value)));
} else {
json.value = string(abi.encodePacked(json.value, intToString(value)));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeNullProperty(Json memory json, string memory propertyName)
internal
pure
returns (Json memory)
{
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, '"', propertyName, '": null'));
} else {
json.value = string(abi.encodePacked(json.value, '"', propertyName, '": null'));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeNullValue(Json memory json)
internal
pure
returns (Json memory)
{
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, "null"));
} else {
json.value = string(abi.encodePacked(json.value, "null"));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeStringProperty(
Json memory json,
string memory propertyName,
string memory value
) internal pure returns (Json memory) {
string memory jsonEscapedString = escapeJsonString(value);
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, '"', propertyName, '": "', jsonEscapedString, '"'));
} else {
json.value = string(abi.encodePacked(json.value, '"', propertyName, '": "', jsonEscapedString, '"'));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeStringValue(Json memory json, string memory value)
internal
pure
returns (Json memory)
{
string memory jsonEscapedString = escapeJsonString(value);
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, '"', jsonEscapedString, '"'));
} else {
json.value = string(abi.encodePacked(json.value, '"', jsonEscapedString, '"'));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeUintProperty(
Json memory json,
string memory propertyName,
uint256 value
) internal pure returns (Json memory) {
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, '"', propertyName, '": ', uintToString(value)));
} else {
json.value = string(abi.encodePacked(json.value, '"', propertyName, '": ', uintToString(value)));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeUintValue(Json memory json, uint256 value)
internal
pure
returns (Json memory)
{
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, uintToString(value)));
} else {
json.value = string(abi.encodePacked(json.value, uintToString(value)));
}
json.depthBitTracker = setListSeparatorFlag(json);
return json;
}
function writeStart(Json memory json, bytes1 token)
private
pure
returns (Json memory)
{
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, token));
} else {
json.value = string(abi.encodePacked(json.value, token));
}
json.depthBitTracker &= MAX_INT256;
json.depthBitTracker++;
return json;
}
function writeStart(
Json memory json,
string memory propertyName,
bytes1 token
) private pure returns (Json memory) {
if (json.depthBitTracker < 0) {
json.value = string(abi.encodePacked(json.value, LIST_SEPARATOR, '"', propertyName, '": ', token));
} else {
json.value = string(abi.encodePacked(json.value, '"', propertyName, '": ', token));
}
json.depthBitTracker &= MAX_INT256;
json.depthBitTracker++;
return json;
}
function writeEnd(Json memory json, bytes1 token)
private
pure
returns (Json memory)
{
json.value = string(abi.encodePacked(json.value, token));
json.depthBitTracker = setListSeparatorFlag(json);
if (getCurrentDepth(json) != 0) {
json.depthBitTracker--;
}
return json;
}
function escapeJsonString(string memory value)
private
pure
returns (string memory str)
{
bytes memory b = bytes(value);
bool foundEscapeChars;
for (uint256 i; i < b.length; i++) {
if (b[i] == BACKSLASH) {
foundEscapeChars = true;
break;
} else if (b[i] == DOUBLE_QUOTE) {
foundEscapeChars = true;
break;
} else if (b[i] == FRONTSLASH) {
foundEscapeChars = true;
break;
} else if (b[i] == HORIZONTAL_TAB) {
foundEscapeChars = true;
break;
} else if (b[i] == FORM_FEED) {
foundEscapeChars = true;
break;
} else if (b[i] == NEWLINE) {
foundEscapeChars = true;
break;
} else if (b[i] == CARRIAGE_RETURN) {
foundEscapeChars = true;
break;
} else if (b[i] == BACKSPACE) {
foundEscapeChars = true;
break;
}
}
if (!foundEscapeChars) {
return value;
}
for (uint256 i; i < b.length; i++) {
if (b[i] == BACKSLASH) {
str = string(abi.encodePacked(str, "\\\\"));
} else if (b[i] == DOUBLE_QUOTE) {
str = string(abi.encodePacked(str, '\\"'));
} else if (b[i] == FRONTSLASH) {
str = string(abi.encodePacked(str, "\\/"));
} else if (b[i] == HORIZONTAL_TAB) {
str = string(abi.encodePacked(str, "\\t"));
} else if (b[i] == FORM_FEED) {
str = string(abi.encodePacked(str, "\\f"));
} else if (b[i] == NEWLINE) {
str = string(abi.encodePacked(str, "\\n"));
} else if (b[i] == CARRIAGE_RETURN) {
str = string(abi.encodePacked(str, "\\r"));
} else if (b[i] == BACKSPACE) {
str = string(abi.encodePacked(str, "\\b"));
} else {
str = string(abi.encodePacked(str, b[i]));
}
}
return str;
}
function getCurrentDepth(Json memory json) private pure returns (int256) {
return json.depthBitTracker & MAX_INT256;
}
function setListSeparatorFlag(Json memory json)
private
pure
returns (int256)
{
return json.depthBitTracker | (int256(1) << 255);
}
function addressToString(address _address)
internal
pure
returns (string memory)
{
bytes32 value = bytes32(uint256(uint160(_address)));
bytes16 alphabet = "0123456789abcdef";
bytes memory str = new bytes(42);
str[0] = "0";
str[1] = "x";
for (uint256 i; i < 20; i++) {
str[2 + i * 2] = alphabet[uint8(value[i + 12] >> 4)];
str[3 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)];
}
return string(str);
}
function intToString(int256 i) internal pure returns (string memory) {
if (i == 0) {
return "0";
}
if (i == type(int256).min) {
return "-57896044618658097711785492504343953926634992332820282019728792003956564819968";
}
bool negative = i < 0;
uint256 len;
uint256 j;
if(!negative) {
j = uint256(i);
} else {
j = uint256(-i);
++len;
}
uint256 l = j;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint256 k = len;
while (l != 0) {
bstr[--k] = bytes1((48 + uint8(l - (l / 10) * 10)));
l /= 10;
}
if (negative) {
bstr[0] = "-";
}
return string(bstr);
}
function uintToString(uint256 _i) internal pure returns (string memory) {
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint256 k = len;
while (_i != 0) {
bstr[--k] = bytes1((48 + uint8(_i - (_i / 10) * 10)));
_i /= 10;
}
return string(bstr);
}
}
文件 9 的 17:LINE.sol
pragma solidity ^0.8.23;
import {Constants} from "./Constants.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {Descriptor} from "./Descriptor.sol";
import {ITokenDescriptor} from "./ITokenDescriptor.sol";
import {ERC721} from "solmate/tokens/ERC721.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
error AlreadyStarToken();
error ExceedsMaxMintPerTransaction();
error HasNotReachedEnd();
error IncorrectPrice();
error InvalidDirection();
error MaxStarTokensReached();
error MintingClosed();
error MovementLocked();
error NotMinted();
error NotTokenOwner();
error PositionCurrentlyTaken(uint256 x, uint256 y);
error PositionNotMintable(uint256 x, uint256 y);
error PositionOutOfBounds(uint256 x, uint256 y);
contract LINE is ERC721, Ownable2Step, ReentrancyGuard, Constants {
using SafeTransferLib for address payable;
struct SalesConfig {
uint64 startTime;
uint64 endTime;
uint256 startPriceInWei;
uint256 endPriceInWei;
address payable fundsRecipient;
}
uint256 public constant MAX_STAR_TOKENS = 25;
uint256 public constant MAX_MINT_PER_TX = 5;
uint256 public constant MAX_SUPPLY = 250;
uint256 internal immutable FUNDS_SEND_GAS_LIMIT = 210_000;
bytes32 public holdersMerkleRoot;
bytes32 public fpMembersMerkleRoot;
uint256 public currentTokenId = 1;
uint256 public numStarTokens;
bool public canMove;
bool private _isMintingClosed;
uint256 private _totalSupply;
ITokenDescriptor public descriptor;
SalesConfig public config;
uint256[NUM_COLUMNS][NUM_ROWS] internal _grid;
ITokenDescriptor.Coordinate[] internal _availableCoordinates;
mapping(bytes32 => uint256) internal _coordinateHashToIndex;
mapping(bytes32 => bool) internal _mintableCoordinates;
mapping(uint256 => ITokenDescriptor.Token) public tokenIdToTokenInfo;
constructor(address _descriptor) ERC721("LINE", "LINE") Ownable(msg.sender) {
descriptor = ITokenDescriptor(_descriptor);
config.startTime = uint64(1708538400);
config.endTime = uint64(1708538400 + 3600);
config.startPriceInWei = 1000000000000000000;
config.endPriceInWei = 150000000000000000;
config.fundsRecipient = payable(0x943ccdd95803e35369Ccf42e9618f992fD2Fea2E);
}
function mintRandom(uint256 quantity, bytes32[] calldata merkleProof) external payable nonReentrant {
if (block.timestamp < config.startTime || _isMintingClosed) {
revert MintingClosed();
}
if (quantity > MAX_MINT_PER_TX) {
revert ExceedsMaxMintPerTransaction();
}
uint256 currentPrice = getCurrentPrice();
if (merkleProof.length > 0) {
currentPrice = _getDiscountedCurrentPrice(merkleProof, msg.sender, currentPrice);
}
uint256 totalPrice = currentPrice * quantity;
if (msg.value < totalPrice) {
revert IncorrectPrice();
}
uint256 ethToReturn;
for (uint256 i=0; i < quantity;) {
bool success;
if (_availableCoordinates.length == 0) {
success = false;
} else {
ITokenDescriptor.Coordinate memory coordinateToMint = _availableCoordinates[0];
success = _mintWithChecks(coordinateToMint, msg.sender);
}
if (!success) {
ethToReturn += currentPrice;
}
unchecked {
++i;
}
}
if (ethToReturn > 0) {
payable(msg.sender).safeTransferETH(ethToReturn);
}
}
function mintAtPosition(ITokenDescriptor.Coordinate[] memory coordinates, bytes32[] calldata merkleProof) external payable nonReentrant {
if (block.timestamp < config.startTime || _isMintingClosed) {
revert MintingClosed();
}
uint256 numCoordinates = coordinates.length;
if (numCoordinates > MAX_MINT_PER_TX) {
revert ExceedsMaxMintPerTransaction();
}
uint256 currentPrice = getCurrentPrice();
if (merkleProof.length > 0) {
currentPrice = _getDiscountedCurrentPrice(merkleProof, msg.sender, currentPrice);
}
if (msg.value < (currentPrice * numCoordinates)) {
revert IncorrectPrice();
}
uint256 ethToReturn;
for (uint256 i=0; i < numCoordinates;) {
bool success = _mintWithChecks(coordinates[i], msg.sender);
if (!success) {
ethToReturn += currentPrice;
}
unchecked {
++i;
}
}
if (ethToReturn > 0) {
payable(msg.sender).safeTransferETH(ethToReturn);
}
}
function artistMint(address receiver, ITokenDescriptor.Coordinate[] memory coordinates) external onlyOwner {
if (_isMintingClosed) {
revert MintingClosed();
}
uint256 numCoordinates = coordinates.length;
for (uint256 i=0; i < numCoordinates;) {
_mintWithChecks(coordinates[i], receiver);
unchecked {
++i;
}
}
}
function closeMint() external onlyOwner {
_closeMint();
}
function moveNorth(uint256 tokenId) external {
if (tokenIdToTokenInfo[tokenId].direction != ITokenDescriptor.Direction.UP) {
revert InvalidDirection();
}
_move(tokenId, 0, -1);
}
function moveNorthwest(uint256 tokenId) external {
if (tokenIdToTokenInfo[tokenId].direction != ITokenDescriptor.Direction.UP) {
revert InvalidDirection();
}
_move(tokenId, -1, -1);
}
function moveNortheast(uint256 tokenId) external {
if (tokenIdToTokenInfo[tokenId].direction != ITokenDescriptor.Direction.UP) {
revert InvalidDirection();
}
_move(tokenId, 1, -1);
}
function moveSouth(uint256 tokenId) external {
if (tokenIdToTokenInfo[tokenId].direction != ITokenDescriptor.Direction.DOWN) {
revert InvalidDirection();
}
_move(tokenId, 0, 1);
}
function moveSouthwest(uint256 tokenId) external {
if (tokenIdToTokenInfo[tokenId].direction != ITokenDescriptor.Direction.DOWN) {
revert InvalidDirection();
}
_move(tokenId, -1, 1);
}
function moveSoutheast(uint256 tokenId) external {
if (tokenIdToTokenInfo[tokenId].direction != ITokenDescriptor.Direction.DOWN) {
revert InvalidDirection();
}
_move(tokenId, 1, 1);
}
function moveWest(uint256 tokenId) external {
_move(tokenId, -1, 0);
}
function moveEast(uint256 tokenId) external {
_move(tokenId, 1, 0);
}
function lockAsStar(uint256 tokenId, uint256 x, uint256 y) external {
if (msg.sender != ownerOf(tokenId)) {
revert NotTokenOwner();
}
ITokenDescriptor.Token memory token = tokenIdToTokenInfo[tokenId];
if (!_isPositionWithinBounds(x, y, token.direction)) {
revert PositionOutOfBounds(x,y);
}
uint256 yGridIndex = _calculateYGridIndex(y);
if (_grid[yGridIndex][x] > 0) {
revert PositionCurrentlyTaken(x,y);
}
if (numStarTokens == MAX_STAR_TOKENS) {
revert MaxStarTokensReached();
}
if (!token.hasReachedEnd) {
revert HasNotReachedEnd();
}
if (token.isStar) {
revert AlreadyStarToken();
}
_grid[_calculateYGridIndex(token.current.y)][token.current.x] = 0;
_grid[yGridIndex][x] = tokenId;
tokenIdToTokenInfo[tokenId].current = ITokenDescriptor.Coordinate({x: x, y: y});
tokenIdToTokenInfo[tokenId].timestamp = block.timestamp;
tokenIdToTokenInfo[tokenId].isStar = true;
numStarTokens++;
}
function setInitialAvailableCoordinates(ITokenDescriptor.Coordinate[] calldata coordinates) external onlyOwner {
uint256 currentNumTokens = _availableCoordinates.length;
for (uint256 i = 0; i < coordinates.length;) {
bytes32 hash = _getCoordinateHash(coordinates[i]);
_mintableCoordinates[hash] = true;
_coordinateHashToIndex[hash] = currentNumTokens + i;
_availableCoordinates.push(coordinates[i]);
unchecked {
++i;
}
}
}
function updateConfig(
uint64 startTime,
uint64 endTime,
uint256 startPriceInWei,
uint256 endPriceInWei,
address payable fundsRecipient
) external onlyOwner {
config.startTime = startTime;
config.endTime = endTime;
config.startPriceInWei = startPriceInWei;
config.endPriceInWei = endPriceInWei;
config.fundsRecipient = fundsRecipient;
}
function setDescriptor(address _descriptor) external onlyOwner {
descriptor = ITokenDescriptor(_descriptor);
}
function updateMerkleRoots(bytes32 _holdersRoot, bytes32 _fpMembersRoot) external onlyOwner {
holdersMerkleRoot = _holdersRoot;
fpMembersMerkleRoot = _fpMembersRoot;
}
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
(bool success, ) = config.fundsRecipient.call{
value: balance,
gas: FUNDS_SEND_GAS_LIMIT
}("");
require(success, "Transfer failed.");
}
function getAvailableCoordinates() external view returns (ITokenDescriptor.Coordinate[] memory) {
return _availableCoordinates;
}
function getGrid() external view returns (uint256[NUM_COLUMNS][NUM_ROWS] memory) {
return _grid;
}
function getToken(uint256 tokenId) external view returns (ITokenDescriptor.Token memory) {
return tokenIdToTokenInfo[tokenId];
}
function getTokens() external view returns (ITokenDescriptor.Token[] memory) {
ITokenDescriptor.Token[] memory tokens = new ITokenDescriptor.Token[](_totalSupply);
for(uint256 i=0;i < _totalSupply;) {
tokens[i] = tokenIdToTokenInfo[i+1];
unchecked {
++i;
}
}
return tokens;
}
function checkMerkleProof(
bytes32[] calldata merkleProof,
address _address,
bytes32 _root
) public pure returns (bool) {
bytes32 leaf = keccak256(abi.encodePacked(_address));
return MerkleProof.verify(merkleProof, _root, leaf);
}
function getCurrentPrice() public view returns (uint256) {
uint256 duration = config.endTime - config.startTime;
uint256 halflife = 950;
if (block.timestamp < config.startTime) {
return config.startPriceInWei;
}
uint256 elapsedTime = ((block.timestamp - config.startTime) / 10 ) * 10;
if (elapsedTime >= duration) {
return config.endPriceInWei;
}
uint256 decayedPrice = config.startPriceInWei;
decayedPrice >>= elapsedTime / halflife;
decayedPrice -= (decayedPrice * (elapsedTime % halflife)) / halflife / 2;
if (decayedPrice < config.endPriceInWei) {
return config.endPriceInWei;
}
return (decayedPrice / 1000000000000000) * 1000000000000000;
}
function tokensOfOwner(address _owner) public view returns (uint256[] memory) {
uint256 balance = balanceOf(_owner);
uint256[] memory tokens = new uint256[](balance);
uint256 index;
unchecked {
for (uint256 i=1; i <= _totalSupply; i++) {
if (ownerOf(i) == _owner) {
tokens[index] = i;
index++;
}
}
}
return tokens;
}
function tokenURI(uint256 id) public view virtual override returns (string memory) {
if (ownerOf(id) == address(0)) {
revert NotMinted();
}
ITokenDescriptor.Token memory token = tokenIdToTokenInfo[id];
return descriptor.generateMetadata(id, token);
}
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
function _mintWithChecks(ITokenDescriptor.Coordinate memory coordinate, address receiver) internal returns (bool) {
uint256 tokenId = currentTokenId;
uint256 x = coordinate.x;
uint256 y = coordinate.y;
uint256 yIndex = _calculateYGridIndex(y);
if (_grid[yIndex][x] > 0) {
return false;
}
bytes32 hash = _getCoordinateHash(ITokenDescriptor.Coordinate({x: x, y: y}));
if (!_mintableCoordinates[hash]) {
revert PositionNotMintable(x, y);
}
_grid[yIndex][x] = tokenId;
tokenIdToTokenInfo[tokenId] = ITokenDescriptor.Token({
initial: ITokenDescriptor.Coordinate({x: x, y: y}),
current: ITokenDescriptor.Coordinate({x: x, y: y}),
timestamp: block.timestamp,
hasReachedEnd: false,
isStar: false,
direction: y >= 12 ? ITokenDescriptor.Direction.DOWN : ITokenDescriptor.Direction.UP,
numMovements: 0
});
if (tokenId != MAX_SUPPLY) {
currentTokenId++;
} else {
_closeMint();
}
_totalSupply++;
_removeFromAvailability(_coordinateHashToIndex[hash]);
_mint(receiver, tokenId);
return true;
}
function _move(uint256 tokenId, int256 xDelta, int256 yDelta) private {
if (msg.sender != ownerOf(tokenId)) {
revert NotTokenOwner();
}
if (!canMove) {
revert MovementLocked();
}
ITokenDescriptor.Token memory token = tokenIdToTokenInfo[tokenId];
if (token.isStar) {
revert MovementLocked();
}
uint256 x = token.current.x;
if (xDelta == -1) {
x--;
} else if (xDelta == 1) {
x++;
}
uint256 y = token.current.y;
if (yDelta == -1) {
y++;
} else if (yDelta == 1) {
y--;
}
if (!_isPositionWithinBounds(x, y, token.direction)) {
revert PositionOutOfBounds(x,y);
}
uint256 yGridIndex = _calculateYGridIndex(y);
if (_grid[yGridIndex][x] > 0) {
revert PositionCurrentlyTaken(x,y);
}
_grid[_calculateYGridIndex(token.current.y)][token.current.x] = 0;
_grid[yGridIndex][x] = tokenId;
tokenIdToTokenInfo[tokenId].current = ITokenDescriptor.Coordinate({x: x, y: y});
tokenIdToTokenInfo[tokenId].hasReachedEnd = ((token.direction == ITokenDescriptor.Direction.UP && y == (NUM_ROWS - 2)) || (token.direction == ITokenDescriptor.Direction.DOWN && y == 1));
tokenIdToTokenInfo[tokenId].numMovements = ++token.numMovements;
tokenIdToTokenInfo[tokenId].timestamp = block.timestamp;
}
function _closeMint() private {
_isMintingClosed = true;
canMove = true;
}
function _calculateYGridIndex(uint256 y) private pure returns (uint256) {
return (NUM_ROWS - 1) - y;
}
function _getCoordinateHash(ITokenDescriptor.Coordinate memory coordinate) private pure returns (bytes32) {
return keccak256(abi.encode(coordinate));
}
function _getDiscountedCurrentPrice(bytes32[] calldata merkleProof, address addressToCheck, uint256 currentPrice) private view returns (uint256) {
bool isFp = checkMerkleProof(merkleProof, addressToCheck, fpMembersMerkleRoot);
bool isPartner = checkMerkleProof(merkleProof, addressToCheck, holdersMerkleRoot);
if (isFp) {
currentPrice = (currentPrice * 75) / 100;
} else if (isPartner) {
currentPrice = (currentPrice * 85) / 100;
}
return currentPrice;
}
function _isPositionWithinBounds(uint256 x, uint256 y, ITokenDescriptor.Direction tokenDirection) private pure returns (bool) {
if (x < 1 || x >= NUM_COLUMNS - 1) {
return false;
}
if (tokenDirection == ITokenDescriptor.Direction.DOWN) {
return y > 0;
} else {
return y < NUM_ROWS - 1;
}
}
function _removeFromAvailability(uint256 index) private {
uint256 lastCoordinateIndex = _availableCoordinates.length - 1;
ITokenDescriptor.Coordinate memory lastCoordinate = _availableCoordinates[lastCoordinateIndex];
ITokenDescriptor.Coordinate memory coordinateToBeRemoved = _availableCoordinates[index];
_availableCoordinates[index] = lastCoordinate;
_coordinateHashToIndex[_getCoordinateHash(lastCoordinate)] = index;
delete _coordinateHashToIndex[_getCoordinateHash(coordinateToBeRemoved)];
_availableCoordinates.pop();
}
}
文件 10 的 17: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;
}
}
文件 11 的 17:MerkleProof.sol
pragma solidity ^0.8.20;
library MerkleProof {
error MerkleProofInvalidMultiproof();
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProof(proof, leaf) == root;
}
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
return processProofCalldata(proof, leaf) == root;
}
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
bytes32 computedHash = leaf;
for (uint256 i = 0; i < proof.length; i++) {
computedHash = _hashPair(computedHash, proof[i]);
}
return computedHash;
}
function multiProofVerify(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProof(proof, proofFlags, leaves) == root;
}
function multiProofVerifyCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32 root,
bytes32[] memory leaves
) internal pure returns (bool) {
return processMultiProofCalldata(proof, proofFlags, leaves) == root;
}
function processMultiProof(
bytes32[] memory proof,
bool[] memory proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function processMultiProofCalldata(
bytes32[] calldata proof,
bool[] calldata proofFlags,
bytes32[] memory leaves
) internal pure returns (bytes32 merkleRoot) {
uint256 leavesLen = leaves.length;
uint256 proofLen = proof.length;
uint256 totalHashes = proofFlags.length;
if (leavesLen + proofLen != totalHashes + 1) {
revert MerkleProofInvalidMultiproof();
}
bytes32[] memory hashes = new bytes32[](totalHashes);
uint256 leafPos = 0;
uint256 hashPos = 0;
uint256 proofPos = 0;
for (uint256 i = 0; i < totalHashes; i++) {
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
bytes32 b = proofFlags[i]
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
: proof[proofPos++];
hashes[i] = _hashPair(a, b);
}
if (totalHashes > 0) {
if (proofPos != proofLen) {
revert MerkleProofInvalidMultiproof();
}
unchecked {
return hashes[totalHashes - 1];
}
} else if (leavesLen > 0) {
return leaves[0];
} else {
return proof[0];
}
}
function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
}
function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
assembly {
mstore(0x00, a)
mstore(0x20, b)
value := keccak256(0x00, 0x40)
}
}
}
文件 12 的 17:Ownable.sol
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 13 的 17:Ownable2Step.sol
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}
文件 14 的 17:ReentrancyGuard.sol
pragma solidity ^0.8.20;
abstract contract ReentrancyGuard {
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
_status = ENTERED;
}
function _nonReentrantAfter() private {
_status = NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
文件 15 的 17:SafeTransferLib.sol
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
library SafeTransferLib {
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 68), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
文件 16 的 17: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);
}
}
}
文件 17 的 17: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));
}
}
{
"compilationTarget": {
"src/LINE.sol": "LINE"
},
"evmVersion": "paris",
"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/",
":solidity-json-writer/=lib/solidity-json-writer/contracts/",
":solmate/=lib/solmate/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_descriptor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyStarToken","type":"error"},{"inputs":[],"name":"ExceedsMaxMintPerTransaction","type":"error"},{"inputs":[],"name":"HasNotReachedEnd","type":"error"},{"inputs":[],"name":"IncorrectPrice","type":"error"},{"inputs":[],"name":"InvalidDirection","type":"error"},{"inputs":[],"name":"MaxStarTokensReached","type":"error"},{"inputs":[],"name":"MintingClosed","type":"error"},{"inputs":[],"name":"MovementLocked","type":"error"},{"inputs":[],"name":"NotMinted","type":"error"},{"inputs":[],"name":"NotTokenOwner","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"PositionCurrentlyTaken","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"PositionNotMintable","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"PositionOutOfBounds","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"MAX_MINT_PER_TX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_STAR_TOKENS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NUM_COLUMNS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NUM_ROWS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct ITokenDescriptor.Coordinate[]","name":"coordinates","type":"tuple[]"}],"name":"artistMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canMove","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"address","name":"_address","type":"address"},{"internalType":"bytes32","name":"_root","type":"bytes32"}],"name":"checkMerkleProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"closeMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"config","outputs":[{"internalType":"uint64","name":"startTime","type":"uint64"},{"internalType":"uint64","name":"endTime","type":"uint64"},{"internalType":"uint256","name":"startPriceInWei","type":"uint256"},{"internalType":"uint256","name":"endPriceInWei","type":"uint256"},{"internalType":"address payable","name":"fundsRecipient","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"descriptor","outputs":[{"internalType":"contract ITokenDescriptor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fpMembersMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAvailableCoordinates","outputs":[{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct ITokenDescriptor.Coordinate[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGrid","outputs":[{"internalType":"uint256[25][25]","name":"","type":"uint256[25][25]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getToken","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct ITokenDescriptor.Coordinate","name":"initial","type":"tuple"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct ITokenDescriptor.Coordinate","name":"current","type":"tuple"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bool","name":"hasReachedEnd","type":"bool"},{"internalType":"bool","name":"isStar","type":"bool"},{"internalType":"enum ITokenDescriptor.Direction","name":"direction","type":"uint8"},{"internalType":"uint256","name":"numMovements","type":"uint256"}],"internalType":"struct ITokenDescriptor.Token","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokens","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct ITokenDescriptor.Coordinate","name":"initial","type":"tuple"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct ITokenDescriptor.Coordinate","name":"current","type":"tuple"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bool","name":"hasReachedEnd","type":"bool"},{"internalType":"bool","name":"isStar","type":"bool"},{"internalType":"enum ITokenDescriptor.Direction","name":"direction","type":"uint8"},{"internalType":"uint256","name":"numMovements","type":"uint256"}],"internalType":"struct ITokenDescriptor.Token[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"holdersMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"lockAsStar","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct ITokenDescriptor.Coordinate[]","name":"coordinates","type":"tuple[]"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"mintAtPosition","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"name":"mintRandom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"moveEast","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"moveNorth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"moveNortheast","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"moveNorthwest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"moveSouth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"moveSoutheast","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"moveSouthwest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"moveWest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numStarTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","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":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_descriptor","type":"address"}],"name":"setDescriptor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct ITokenDescriptor.Coordinate[]","name":"coordinates","type":"tuple[]"}],"name":"setInitialAvailableCoordinates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenIdToTokenInfo","outputs":[{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct ITokenDescriptor.Coordinate","name":"initial","type":"tuple"},{"components":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"internalType":"struct ITokenDescriptor.Coordinate","name":"current","type":"tuple"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"bool","name":"hasReachedEnd","type":"bool"},{"internalType":"bool","name":"isStar","type":"bool"},{"internalType":"enum ITokenDescriptor.Direction","name":"direction","type":"uint8"},{"internalType":"uint256","name":"numMovements","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"tokensOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"startTime","type":"uint64"},{"internalType":"uint64","name":"endTime","type":"uint64"},{"internalType":"uint256","name":"startPriceInWei","type":"uint256"},{"internalType":"uint256","name":"endPriceInWei","type":"uint256"},{"internalType":"address payable","name":"fundsRecipient","type":"address"}],"name":"updateConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_holdersRoot","type":"bytes32"},{"internalType":"bytes32","name":"_fpMembersRoot","type":"bytes32"}],"name":"updateMerkleRoots","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]