编译器
0.8.23+commit.f704f362
文件 1 的 5:Dependencies.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
}
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
interface IERC721Metadata is IERC721 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
function toString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
}
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);
}
}
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_setOwner(_msgSender());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
_setOwner(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
using Address for address;
using Strings for uint256;
string private _name;
string private _symbol;
mapping(uint256 => address) private _owners;
mapping(address => uint256) private _balances;
mapping(uint256 => address) private _tokenApprovals;
mapping(address => mapping(address => bool)) private _operatorApprovals;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: balance query for the zero address");
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "ERC721: owner query for nonexistent token");
return owner;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
function _baseURI() internal view virtual returns (string memory) {
return "";
}
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(
_msgSender() == owner || isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not owner nor approved for all"
);
_approve(to, tokenId);
}
function getApproved(uint256 tokenId) public view virtual override returns (address) {
require(_exists(tokenId), "ERC721: approved query for nonexistent token");
return _tokenApprovals[tokenId];
}
function setApprovalForAll(address operator, bool approved) public virtual override {
require(operator != _msgSender(), "ERC721: approve to caller");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
}
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_safeTransfer(from, to, tokenId, _data);
}
function _safeTransfer(
address from,
address to,
uint256 tokenId,
bytes memory _data
) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
}
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _owners[tokenId] != address(0);
}
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
require(_exists(tokenId), "ERC721: operator query for nonexistent token");
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
}
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721.ownerOf(tokenId);
_approve(address(0), tokenId);
_balances[owner] -= 1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
}
function _transfer(
address from,
address to,
uint256 tokenId
) internal virtual {
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own");
require(to != address(0), "ERC721: transfer to the zero address");
_approve(address(0), tokenId);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
}
function _checkOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory _data
) private returns (bool) {
if (to.isContract()) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
return retval == IERC721Receiver(to).onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert("ERC721: transfer to non ERC721Receiver implementer");
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
} else {
return true;
}
}
}
文件 2 的 5:Free33.sol
pragma solidity ^0.8.23;
import "./FreeChecker.sol";
import "./ThreeBallsGrid.sol";
interface GrailsV {
function ownerOf(uint256 tokenId) external returns (address owner);
}
contract Free33 is FreeChecker {
ThreeBallsGrid public threeBallsGrid;
GrailsV public grailsV = GrailsV(0x92A50Fe6eDE411BD26e171B97472e24D245349B8);
mapping(uint256 => bool) public THREE_BALLS;
mapping(uint256 => uint256) public ballX;
mapping(uint256 => uint256) public ballY;
constructor() {
THREE_BALLS[12] = true;
THREE_BALLS[30] = true;
THREE_BALLS[36] = true;
THREE_BALLS[37] = true;
THREE_BALLS[38] = true;
THREE_BALLS[39] = true;
THREE_BALLS[40] = true;
THREE_BALLS[42] = true;
THREE_BALLS[46] = true;
THREE_BALLS[52] = true;
THREE_BALLS[54] = true;
THREE_BALLS[56] = true;
THREE_BALLS[57] = true;
THREE_BALLS[58] = true;
THREE_BALLS[59] = true;
THREE_BALLS[60] = true;
THREE_BALLS[64] = true;
THREE_BALLS[65] = true;
THREE_BALLS[66] = true;
THREE_BALLS[69] = true;
THREE_BALLS[70] = true;
THREE_BALLS[72] = true;
THREE_BALLS[77] = true;
THREE_BALLS[79] = true;
THREE_BALLS[80] = true;
THREE_BALLS[81] = true;
THREE_BALLS[86] = true;
THREE_BALLS[87] = true;
THREE_BALLS[90] = true;
THREE_BALLS[91] = true;
THREE_BALLS[94] = true;
THREE_BALLS[95] = true;
THREE_BALLS[98] = true;
THREE_BALLS[99] = true;
THREE_BALLS[101] = true;
THREE_BALLS[102] = true;
threeBallsGrid = new ThreeBallsGrid(msg.sender);
}
function ballCoords(uint256 tokenId) external view returns (uint256 x, uint256 y) {
return (ballX[tokenId], ballY[tokenId]);
}
function throwBall(uint256 grailsVTokenId) external {
require(grailsV.ownerOf(grailsVTokenId) == msg.sender, 'Only owner can throw');
require(THREE_BALLS[grailsVTokenId], 'Can only throw a ball');
uint256 hash = uint256(keccak256(abi.encodePacked(
block.prevrandao, block.gaslimit, grailsVTokenId
)));
ballX[grailsVTokenId] = 1 + hash % 6;
ballY[grailsVTokenId] = 1 + (hash / 100000) % 6;
threeBallsGrid.update();
}
function isLine(
int[2] memory ball_a,
int[2] memory ball_b,
int[2] memory ball_c
) public pure returns (bool) {
return _isLine(ball_a, ball_b, ball_c) && _isLine(ball_b, ball_c, ball_a);
}
function isOutOfBounds(int[2] memory ball) external pure returns (bool) {
return _outOfBounds(ball[0]) || _outOfBounds(ball[1]);
}
function _outOfBounds(int n) internal pure returns (bool) {
return 1 > n || n > 6;
}
function _isLine(
int[2] memory ball_a,
int[2] memory ball_b,
int[2] memory ball_c
) internal pure returns (bool) {
int ax = ball_a[0];
int ay = ball_a[1];
int bx = ball_b[0];
int by = ball_b[1];
int cx = ball_c[0];
int cy = ball_c[1];
if (
_outOfBounds(ax) ||
_outOfBounds(ay) ||
_outOfBounds(bx) ||
_outOfBounds(by) ||
_outOfBounds(cx) ||
_outOfBounds(cy)
) return false;
int a_b_YDiff = by - ay;
int a_c_YDiff = cy - ay;
int a_b_XDiff = bx - ax;
int a_c_XDiff = cx - ax;
if (a_b_YDiff == 0 && a_c_YDiff == 0) return true;
if (a_b_YDiff == 0) return a_b_XDiff == 0;
if (a_c_YDiff == 0) return a_c_XDiff == 0;
return (
(a_b_XDiff * 60) / a_b_YDiff
==
(a_c_XDiff * 60) / a_c_YDiff
);
}
function claim(
uint256 free0TokenId,
uint256 ownedBallTokenId,
uint256 supportingBallTokenId1,
uint256 supportingBallTokenId2
) external {
preCheck(free0TokenId, '33');
require(grailsV.ownerOf(ownedBallTokenId) == msg.sender, 'Not owner of ball');
require(
ownedBallTokenId != supportingBallTokenId1 &&
ownedBallTokenId != supportingBallTokenId2 &&
supportingBallTokenId1 != supportingBallTokenId2,
'Invalid supporting balls'
);
require(isLine(
[int(ballX[ownedBallTokenId]), int(ballY[ownedBallTokenId])],
[int(ballX[supportingBallTokenId1]), int(ballY[supportingBallTokenId1])],
[int(ballX[supportingBallTokenId2]), int(ballY[supportingBallTokenId2])]
),
'Balls not thrown in a straight line'
);
ballX[ownedBallTokenId] = 0;
ballY[ownedBallTokenId] = 0;
postCheck(free0TokenId, 33, '33');
}
}
文件 3 的 5:FreeChecker.sol
pragma solidity ^0.8.23;
interface IFree {
function totalSupply() external view returns (uint256);
function balanceOf(address) external view returns (uint256);
function ownerOf(uint256 tokenId) external view returns (address owner);
function tokenIdToCollectionId(uint256 tokenId) external view returns (uint256 collectionId);
function collectionSupply(uint256 collectionId) external view returns (uint256);
function collectionIdToMinter(uint256 collectionId) external view returns (address);
function mint(uint256 collectionId, address to) external;
function appendAttributeToToken(uint256 tokenId, string memory attrKey, string memory attrValue) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
}
abstract contract FreeChecker {
mapping(uint256 => bool) public free0TokenIdUsed;
IFree public immutable free = IFree(0x30b541f1182ef19c56a39634B2fdACa5a0F2A741);
function preCheck(uint256 free0TokenId, string memory freeStr) internal view {
require(free.tokenIdToCollectionId(free0TokenId) == 0, 'Invalid Free0');
require(!free0TokenIdUsed[free0TokenId],
string(abi.encodePacked('This Free0 has already been used to mint a Free', freeStr))
);
require(free.ownerOf(free0TokenId) == msg.sender, 'You must be the owner of this Free0');
}
function postCheck(uint256 free0TokenId, uint256 freeNumber, string memory freeStr) internal {
free0TokenIdUsed[free0TokenId] = true;
free.appendAttributeToToken(free0TokenId,
string(abi.encodePacked('Used For Free', freeStr, ' Mint')),
'true'
);
free.mint(freeNumber, msg.sender);
}
function checkFreeToken(uint256 freeTokenId, uint256 collectionId) internal view {
require(free.ownerOf(freeTokenId) == msg.sender, 'Not owner of token');
require(free.tokenIdToCollectionId(freeTokenId) == collectionId, 'Token collection mismatch');
}
}
文件 4 的 5:ThreeBallsGrid.sol
import "./Dependencies.sol";
import "./Free33.sol";
import "./ThreeBallsGridURI.sol";
pragma solidity ^0.8.23;
contract ThreeBallsGrid is ERC721, Ownable {
uint256 public maxSupply = 333;
uint256 public totalSupply;
address public free33;
ThreeBallsGridURI public tokenURIContract;
ThreeBallsGridMinter public minter;
struct Balls {
uint256 a;
uint256 b;
uint256 c;
}
mapping(uint256 => Balls) public tokenIdToBalls;
mapping(uint256 => bool) public isLight;
event MetadataUpdate(uint256 _tokenId);
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
constructor(address newOwner) ERC721('Three Balls Grid', '3BG') {
minter = new ThreeBallsGridMinter();
tokenURIContract = new ThreeBallsGridURI(msg.sender);
free33 = msg.sender;
transferOwnership(newOwner);
_mint(newOwner, totalSupply++);
}
function exists(uint256 tokenId) external view returns (bool) {
return _exists(tokenId);
}
function mint(address recipient) public {
require(address(minter) == msg.sender, 'Caller is not the minting address');
require(totalSupply < maxSupply, 'Cannot exceed 333');
_mint(recipient, totalSupply++);
}
function update() external {
require(msg.sender == free33);
emit BatchMetadataUpdate(0, totalSupply);
}
function setMinter(address newMinter) external onlyOwner {
minter = ThreeBallsGridMinter(newMinter);
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
return tokenURIContract.tokenURI(tokenId);
}
function setURIContract(address _uriContract) external onlyOwner {
tokenURIContract = ThreeBallsGridURI(_uriContract);
emit BatchMetadataUpdate(0, totalSupply);
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721) returns (bool) {
return (
interfaceId == bytes4(0x49064906) ||
super.supportsInterface(interfaceId)
);
}
function setBalls(uint256 tokenId, uint256 a, uint256 b, uint256 c) external {
require(ownerOf(tokenId) == msg.sender, 'Must own token');
tokenIdToBalls[tokenId].a = a;
tokenIdToBalls[tokenId].b = b;
tokenIdToBalls[tokenId].c = c;
emit MetadataUpdate(tokenId);
}
function setLightMode(uint256 tokenId, bool _isLight) external {
require(ownerOf(tokenId) == msg.sender, 'Must own token');
isLight[tokenId] = _isLight;
emit MetadataUpdate(tokenId);
}
}
interface IFree19 {
function lastAssigned() external view returns (uint256);
function claimer() external view returns (address);
}
contract ThreeBallsGridMinter {
IFree19 public free19 = IFree19(0xaBCeF3a4aDC27A6c962b4fC17181F47E62244EF0);
address public baseContract;
mapping(address => uint256) public minterToTimestamp;
constructor() {
baseContract = msg.sender;
}
function mint() external {
require(free19.claimer() == msg.sender, 'Must be Free19 contract claimer');
require(free19.lastAssigned() + 30 minutes < block.timestamp, 'Must be Free19 contract claimer for > 30 minutes');
require(minterToTimestamp[msg.sender] + 15 minutes < block.timestamp, 'Must wait at least 15 minutes between mints');
minterToTimestamp[msg.sender] = block.timestamp;
ThreeBallsGrid(baseContract).mint(msg.sender);
}
}
文件 5 的 5:ThreeBallsGridURI.sol
import "./Dependencies.sol";
pragma solidity ^0.8.23;
interface IThreeBallsGrid {
function owner() external view returns (address);
function tokenIdToBalls(uint256) external view returns (uint256 a, uint256 b, uint256 c);
function isLight(uint256) external view returns (bool);
}
interface IFree33 {
function THREE_BALLS(uint256) external view returns (bool);
function ballCoords(uint256) external view returns (uint256 x, uint256 y);
function isLine(
int[2] memory ball_a,
int[2] memory ball_b,
int[2] memory ball_c
) external view returns (bool);
}
contract ThreeBallsGridURI {
using Strings for uint256;
IThreeBallsGrid public baseContract;
IFree33 public free33;
string public description = 'A visualizer for Free33. Call `setBalls(uint256 tokenId, uint256 a, uint256 b, uint256 c)` to display the position of balls A, B, and C. Call `isLight(uint256 tokenId, bool val)` to display in Light Mode.';
string public externalUrl = 'https://steviep.xyz/three-balls';
string public license = 'CC0';
constructor(address free33Addr) {
baseContract = IThreeBallsGrid(msg.sender);
free33 = IFree33(free33Addr);
}
function tokenURI(uint256 tokenId) external view returns (string memory) {
bytes memory json = abi.encodePacked(
'data:application/json;utf8,',
'{"name": "Three Balls Grid",'
'"description": "', description, '",'
'"license": "', license, '",'
'"external_url": "', externalUrl, '",'
'"attributes": ', attrs(tokenId),','
'"image": "', encodedSVG(tokenId),
'"}'
);
return string(json);
}
function encodedSVG(uint256 tokenId) public view returns (string memory) {
return string(abi.encodePacked(
'data:image/svg+xml;base64,',
Base64.encode(rawSVG(tokenId))
));
}
function attrs(uint256 tokenId) public view returns (bytes memory) {
(uint256 a, uint256 b, uint256 c) = baseContract.tokenIdToBalls(tokenId);
(uint256 ax, uint256 ay) = free33.ballCoords(a);
(uint256 bx, uint256 by) = free33.ballCoords(b);
(uint256 cx, uint256 cy) = free33.ballCoords(c);
bool isLine = free33.isLine([int(ax), int(ay)], [int(bx), int(by)], [int(cx), int(cy)]);
bool isLight = baseContract.isLight(tokenId);
return abi.encodePacked(
'[{"trait_type": "Ball A", "value": "',a.toString(),'"},',
'{"trait_type": "Ball B", "value": "',b.toString(),'"},',
'{"trait_type": "Ball C", "value": "',c.toString(),'"},',
'{"trait_type": "Is Line", "value": "',isLine ? 'true' : 'false','"},',
'{"trait_type": "Light Mode", "value": "',isLight ? 'true' : 'false','"}]'
);
}
function genStyle(uint256 tokenId) internal view returns (bytes memory) {
bool isLight = baseContract.isLight(tokenId);
string memory colorA = isLight ? '#d12c00' : '#ff005b';
string memory colorB = isLight ? '#2800ff' : '#0079ff';
string memory colorC = isLight ? '#04b700' : '#dbec49';
string memory colorLight = isLight ? '#000' : '#fff';
string memory colorDark = isLight ? '#fff' : '#000';
bytes memory colorStrokes = abi.encodePacked('.strokeA{stroke:',colorA,'}.strokeB{stroke:',colorB,'}.strokeC{stroke:',colorC,'}');
bytes memory colorFills = abi.encodePacked('.fillA{fill:',colorA,'}.fillB{fill:',colorB,'}.fillC{fill:',colorC,'}');
bytes memory bw = abi.encodePacked('.strokeLight{stroke:',colorLight,'}.fillLight{fill:',colorLight,'}.fillDark{fill:',colorDark,'}');
return abi.encodePacked(
'<style>',
colorStrokes,
colorFills,
bw,
'.g{font-size:15}.isline{stroke-width:2.5}line{stroke-dasharray:4}circle{stroke-width:2}text{text-anchor:middle;dominant-baseline:middle}text{font:12px monospace}</style>'
);
}
function rawSVG(uint256 tokenId) public view returns (bytes memory) {
(uint256 a, uint256 b, uint256 c) = baseContract.tokenIdToBalls(tokenId);
(uint256 ax, uint256 ay) = free33.ballCoords(a);
(uint256 bx, uint256 by) = free33.ballCoords(b);
(uint256 cx, uint256 cy) = free33.ballCoords(c);
bool isLine = free33.isLine([int(ax), int(ay)], [int(bx), int(by)], [int(cx), int(cy)]);
bytes memory svg = abi.encodePacked(
'<svg viewBox="0 0 420 420" xmlns="http://www.w3.org/2000/svg">',
genStyle(tokenId),
'<rect class="fillDark" x="0" y="0" width="420" height="420" /><rect x="30" y="30" width="360" height="360" class="strokeLight" fill="none"/>'
);
for (uint256 i; i < 6; ++i) {
uint256 p = 30 + i * 60;
if (i > 0) svg = abi.encodePacked(
svg,
'<line class="strokeLight" y1="30" y2="390" x1="',
p.toString(),
'" x2="',
p.toString(),
'"></line><line class="strokeLight" x1="30" x2="390" y1="',
p.toString(),
'" y2="',
p.toString(),
'"></line>'
);
svg = abi.encodePacked(
svg,
'<text class="fillLight" x="',(p+30).toString(),'" y="15">',(i+1).toString(),'</text>',
'<text class="fillLight" y="',(p+30).toString(),'" x="15">',(i+1).toString(),'</text>'
);
}
if (isLine) {
svg = abi.encodePacked(
svg,
'<path class="isline strokeLight" d="M ',
(60 + (ax-1)*60).toString(),
' ',
(60 + (ay-1)*60).toString(),
' L',
(60 + (bx-1)*60).toString(),
' ',
(60 + (by-1)*60).toString(),
' ',
(60 + (cx-1)*60).toString(),
' ',
(60 + (cy-1)*60).toString(),
'" />'
);
}
for (uint256 x = 1; x < 7; ++x) {
for (uint256 y = 1; y < 7; ++y) {
uint256 r = 20;
uint256 _cx = 60 + (x-1) * 60;
uint256 _cy = 60 + (y-1) * 60;
if (x == ax && y == ay) {
svg = abi.encodePacked(
svg,
'<circle class="strokeA fillDark" cx="', _cx.toString(),'" cy="', _cy.toString(),'" r="', r.toString(),'"/>'
);
r -= 8;
}
if (x == bx && y == by) {
svg = abi.encodePacked(
svg,
'<circle class="strokeB fillDark" cx="', _cx.toString(),'" cy="', _cy.toString(),'" r="', r.toString(),'"/>'
);
r -= 8;
}
if (x == cx && y == cy) {
svg = abi.encodePacked(
svg,
'<circle class="strokeC fillDark" cx="', _cx.toString(),'" cy="', _cy.toString(),'" r="', r.toString(),'"/>'
);
}
}
}
bytes memory aId = abi.encodePacked('<text x="150" y="405" class="g fillA">', (free33.THREE_BALLS(a) ? a.toString() : ''), '</text>');
bytes memory bId = abi.encodePacked('<text x="210" y="405" class="g fillB">', (free33.THREE_BALLS(b) ? b.toString() : ''), '</text>');
bytes memory cId = abi.encodePacked('<text x="270" y="405" class="g fillC">', (free33.THREE_BALLS(c) ? c.toString() : ''), '</text>');
svg = abi.encodePacked(
svg,
aId,
bId,
cId
);
return abi.encodePacked(svg, '</svg>');
}
function updateMetadata(string calldata _externalUrl, string calldata _description, string calldata _license) external {
require(msg.sender == baseContract.owner(), 'Ownable: caller is not the owner');
externalUrl = _externalUrl;
description = _description;
license = _license;
}
}
{
"compilationTarget": {
"Free33.sol": "Free33"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"THREE_BALLS","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ballCoords","outputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ballX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ballY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"free0TokenId","type":"uint256"},{"internalType":"uint256","name":"ownedBallTokenId","type":"uint256"},{"internalType":"uint256","name":"supportingBallTokenId1","type":"uint256"},{"internalType":"uint256","name":"supportingBallTokenId2","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"free","outputs":[{"internalType":"contract IFree","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"free0TokenIdUsed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"grailsV","outputs":[{"internalType":"contract GrailsV","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256[2]","name":"ball_a","type":"int256[2]"},{"internalType":"int256[2]","name":"ball_b","type":"int256[2]"},{"internalType":"int256[2]","name":"ball_c","type":"int256[2]"}],"name":"isLine","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"int256[2]","name":"ball","type":"int256[2]"}],"name":"isOutOfBounds","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"threeBallsGrid","outputs":[{"internalType":"contract ThreeBallsGrid","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"grailsVTokenId","type":"uint256"}],"name":"throwBall","outputs":[],"stateMutability":"nonpayable","type":"function"}]