编译器
0.8.17+commit.8df45f5f
文件 1 的 21:AuthorshipToken.sol
pragma solidity ^0.8.17;
import { IShieldsAPI } from "shields-api/interfaces/IShieldsAPI.sol";
import { Owned } from "solmate/auth/Owned.sol";
import { ERC721 } from "solmate/tokens/ERC721.sol";
import { LibString } from "solmate/utils/LibString.sol";
import { ICurta } from "@/contracts/interfaces/ICurta.sol";
import { Base64 } from "@/contracts/utils/Base64.sol";
contract AuthorshipToken is ERC721, Owned {
using LibString for uint256;
IShieldsAPI constant shieldsAPI = IShieldsAPI(0x740CBbF0116a82F64e83E1AE68c92544870B0C0F);
bytes32 constant SALT = bytes32("Curta.AuthorshipToken");
error NoTokensAvailable();
error Unauthorized();
address public immutable curta;
uint256 public immutable issueLength;
uint256 public immutable deployTimestamp;
uint256 public numClaimedByOwner;
uint256 public totalSupply;
constructor(address _curta, uint256 _issueLength, address[] memory _authors)
ERC721("Authorship Token", "AUTH")
Owned(msg.sender)
{
curta = _curta;
issueLength = _issueLength;
deployTimestamp = block.timestamp;
uint256 length = _authors.length;
for (uint256 i; i < length;) {
_mint(_authors[i], i + 1);
unchecked {
++i;
}
}
_mint(0x58593392d72A9D90b133e1C8ecEec581C354687f, length + 1);
totalSupply = length + 1;
}
function curtaMint(address _to) external {
if (msg.sender != curta) revert Unauthorized();
unchecked {
uint256 tokenId = ++totalSupply;
_mint(_to, tokenId);
}
}
function ownerMint(address _to) external onlyOwner {
unchecked {
uint256 numIssued = (block.timestamp - deployTimestamp) / issueLength;
uint256 numMintable = numIssued - numClaimedByOwner++;
if (numMintable == 0) revert NoTokensAvailable();
uint256 tokenId = ++totalSupply;
_mint(_to, tokenId);
}
}
function tokenURI(uint256 _tokenId) public view override returns (string memory) {
require(_ownerOf[_tokenId] != address(0), "NOT_MINTED");
uint256 seed = uint256(keccak256(abi.encodePacked(_tokenId, SALT)));
uint256 colors = 0x6351CEFF00FFB300FF6B00B5000A007FFF78503C323232FE7FFF6C28A2FF007A;
unchecked {
uint256 shift = 24 * (seed % 11);
colors = (colors & ((type(uint256).max ^ (0xFFFFFF << shift)) ^ 0xFFFFFF))
| ((colors & 0xFFFFFF) << shift) | ((colors >> shift) & 0xFFFFFF);
seed >>= 4;
shift = 24 * (seed % 10);
colors = (colors & ((type(uint256).max ^ (0xFFFFFF << shift)) ^ (0xFFFFFF << 24)))
| (((colors >> 24) & 0xFFFFFF) << shift) | (((colors >> shift) & 0xFFFFFF) << 24);
seed >>= 4;
shift = 24 * (seed % 9);
colors = (colors & ((type(uint256).max ^ (0xFFFFFF << shift)) ^ (0xFFFFFF << 48)))
| (((colors >> 48) & 0xFFFFFF) << shift) | (((colors >> shift) & 0xFFFFFF) << 48);
seed >>= 4;
shift = 24 * (seed & 7);
colors = (colors & ((type(uint256).max ^ (0xFFFFFF << shift)) ^ (0xFFFFFF << 72)))
| (((colors >> 72) & 0xFFFFFF) << shift) | (((colors >> shift) & 0xFFFFFF) << 72);
seed >>= 3;
}
return string.concat(
"data:application/json;base64,",
Base64.encode(
abi.encodePacked(
'{"name":"Authorship Token #',
_tokenId.toString(),
'","description":"This token allows 1 puzzle to be added to Curta. Once it has '
'been used, it can never be used again.","image_data":"data:image/svg+xml;base6'
"4,",
Base64.encode(
abi.encodePacked(
'<svg width="750" height="750" xmlns="http://www.w3.org/2000/svg" fill='
'"none" viewBox="0 0 750 750"><style>.a{filter:url(#c)drop-shadow(0 0 2'
"px #007fff);fill:#fff;width:4px}.b{filter:drop-shadow(0 0 .5px #007fff"
");fill:#000;width:3px}.c{height:13px}.d{height:6px}.e{height:4px}.f{he"
"ight:12px}.g{height:5px}.h{height:3px}.i{width:320px;height:620px}.j{c"
"x:375px;r:20px}.k{stroke:#27303d}.l{fill:#000}.n{fill:#0d1017}.o{strok"
'e-width:2px}@font-face{font-family:"A";src:url(data:font/woff2;charset'
"=utf-8;base64,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"
'MW6dCcHc6I9atPW9msuGc/bptop2dPAAA=)}</style><defs><radialGradient id="'
'b"><stop stop-color="#007FFF"/><stop offset="100%" stop-opacity="0"/><'
'/radialGradient><filter id="c"><feGaussianBlur stdDeviation="8" in="So'
'urceGraphic" result="offset-blur"/><feComposite operator="out" in="Sou'
'rceGraphic" in2="offset-blur" result="inverse"/><feFlood flood-color="'
'#007FFF" flood-opacity=".95" result="color"/><feComposite operator="in'
'" in="color" in2="inverse" result="shadow"/><feComposite in="shadow" i'
'n2="SourceGraphic"/><feComposite operator="atop" in="shadow" in2="Sour'
'ceGraphic"/></filter><mask id="a"><path fill="#000" d="M0 0h750v750H0z'
'"/><rect class="i" x="215" y="65" rx="20" fill="#FFF"/><circle class="'
'j l" cy="65"/><circle class="j l" cy="685"/></mask></defs><path fill="'
'#10131C" d="M0 0h750v750H0z"/><rect class="i n" x="215" y="65" mask="u'
'rl(#a)" rx="20"/><circle mask="url(#a)" fill="url(#b)" cx="375" cy="38'
'1" r="180"/><circle class="j k n" cy="125"/><g transform="translate(35'
'9 110)"><circle class="n" cy="16" cx="16" r="16"/><rect class="a c" x='
'"8" y="7" rx="2"/><rect class="b f" x="8.5" y="7.5" rx="1.5"/><rect cl'
'ass="a e" x="8" y="21" rx="2"/><rect class="b h" x="8.5" y="21.5" rx="'
'1.5"/><rect class="a d" x="14" y="7" rx="2"/><rect class="b g" x="14.5'
'" y="7.5" rx="1.5"/><rect class="a e" x="14" y="14" rx="2"/><rect clas'
's="b h" x="14.5" y="14.5" rx="1.5"/><rect class="a d" x="14" y="19" rx'
'="2"/><rect class="b g" x="14.5" y="19.5" rx="1.5"/><rect class="a c" '
'x="20" y="12" rx="2"/><rect class="b f" x="20.5" y="12.5" rx="1.5"/><r'
'ect class="a e" x="20" y="7" rx="2"/><rect class="b h" x="20.5" y="7.5'
'" rx="1.5"/></g><path d="M338.814 168.856c-.373 0-.718-.063-1.037-.191'
"a2.829 2.829 0 0 1-.878-.606 2.828 2.828 0 0 1-.606-.878 2.767 2.767 0"
" 0 1-.193-1.037v-.336c0-.372.064-.723.192-1.053.138-.319.34-.611.606-."
"877a2.59 2.59 0 0 1 .878-.59 2.58 2.58 0 0 1 1.038-.208h4.26c.245 0 .4"
"8.032.703.096.212.053.425.143.638.27.223.118.415.256.574.416.16.16.304"
".345.431.558.043.064.07.133.08.208a.301.301 0 0 1-.016.095.346.346 0 0"
" 1-.175.256.42.42 0 0 1-.32.032.333.333 0 0 1-.239-.192 3.016 3.016 0 "
"0 0-.303-.399 2.614 2.614 0 0 0-.415-.303 1.935 1.935 0 0 0-.463-.191 "
"1.536 1.536 0 0 0-.495-.048c-.712 0-1.42-.006-2.122-.016-.713 0-1.425."
"005-2.138.016-.266 0-.51.042-.734.127-.234.096-.442.24-.623.431a1.988 "
"1.988 0 0 0-.43.623 1.961 1.961 0 0 0-.144.75v.335a1.844 1.844 0 0 0 ."
"574 1.356 1.844 1.844 0 0 0 1.356.574h4.261c.17 0 .33-.015.48-.047a2.0"
"2 2.02 0 0 0 .446-.192c.149-.074.282-.165.399-.271.106-.107.207-.229.3"
"03-.367a.438.438 0 0 1 .255-.144c.096-.01.187.01.272.064a.35.35 0 0 1 "
".16.24.306.306 0 0 1-.033.27 2.653 2.653 0 0 1-.43.527c-.16.139-.346.2"
"66-.559.383-.213.117-.42.197-.622.24-.213.053-.436.08-.67.08h-4.262Zm1"
"7.553 0c-.713 0-1.324-.266-1.835-.797a2.69 2.69 0 0 1-.766-1.931v-2.66"
"5c0-.117.037-.213.112-.287a.37.37 0 0 1 .27-.112c.118 0 .214.037.288.1"
"12a.39.39 0 0 1 .112.287v2.664c0 .533.18.99.542 1.373a1.71 1.71 0 0 0 "
"1.293.559h3.878c.51 0 .941-.187 1.292-.559a1.93 1.93 0 0 0 .543-1.372v"
"-2.665a.39.39 0 0 1 .111-.287.389.389 0 0 1 .288-.112.37.37 0 0 1 .271"
".112.39.39 0 0 1 .112.287v2.664c0 .756-.256 1.4-.766 1.932-.51.531-1.1"
"28.797-1.851.797h-3.894Zm23.824-.718a.456.456 0 0 1 .16.192c.01.042.01"
"6.09.016.143a.47.47 0 0 1-.016.112.355.355 0 0 1-.143.208.423.423 0 0 "
"1-.24.063h-.048a.141.141 0 0 1-.064-.016c-.02 0-.037-.005-.047-.016a10"
"4.86 104.86 0 0 1-1.18-.83c-.374-.265-.746-.531-1.118-.797-.011 0-.016"
"-.006-.016-.016-.01 0-.016-.005-.016-.016-.01 0-.016-.005-.016-.016h-5"
".553v1.324a.39.39 0 0 1-.112.288.425.425 0 0 1-.287.111.37.37 0 0 1-.2"
"72-.111.389.389 0 0 1-.111-.288v-4.946c0-.054.005-.107.016-.16a.502.50"
"2 0 0 1 .095-.128.374.374 0 0 1 .128-.08.316.316 0 0 1 .144-.031h6.893"
"c.256 0 .49.048.702.143.224.085.42.218.59.4.182.18.32.377.416.59.085.2"
"23.127.457.127.702v.335c0 .223-.032.43-.095.622a2.107 2.107 0 0 1-.32."
"527c-.138.18-.292.319-.462.415-.17.106-.362.186-.575.24l.702.51c.234.1"
"7.469.345.703.526Zm-8.281-4.228v2.425h6.494a.954.954 0 0 0 .4-.08.776."
"776 0 0 0 .334-.223c.107-.106.186-.218.24-.335.053-.128.08-.266.08-.41"
"5v-.32a.954.954 0 0 0-.08-.398 1.232 1.232 0 0 0-.224-.351 1.228 1.228"
" 0 0 0-.35-.224.954.954 0 0 0-.4-.08h-6.494Zm24.67-.782c.106 0 .202.03"
"7.287.111a.37.37 0 0 1 .112.272.39.39 0 0 1-.112.287.425.425 0 0 1-.28"
"7.112h-3.64v4.579a.37.37 0 0 1-.111.272.348.348 0 0 1-.271.127.397.397"
" 0 0 1-.288-.127.37.37 0 0 1-.111-.272v-4.579h-3.639a.37.37 0 0 1-.271"
"-.111.39.39 0 0 1-.112-.287.37.37 0 0 1 .112-.272.37.37 0 0 1 .271-.11"
"1h8.058Zm15.782-.048c.723 0 1.34.266 1.85.798.511.532.767 1.17.767 1.9"
"15v2.68a.37.37 0 0 1-.112.272.397.397 0 0 1-.287.127.348.348 0 0 1-.27"
"2-.127.348.348 0 0 1-.127-.272v-1.196h-7.532v1.196a.348.348 0 0 1-.128"
".272.348.348 0 0 1-.271.127.348.348 0 0 1-.271-.127.348.348 0 0 1-.128"
"-.272v-2.68c0-.745.255-1.383.766-1.915.51-.532 1.128-.798 1.851-.798h3"
".894Zm-5.697 3.415h7.548v-.702c0-.532-.176-.984-.527-1.357-.362-.383-."
"792-.574-1.292-.574H408.5c-.51 0-.942.191-1.293.574a1.875 1.875 0 0 0-"
".542 1.357v.702ZM297.898 204.5h4.16l1.792-5.152h9.408l1.824 5.152h4.44"
"8l-8.704-23.2h-4.288l-8.64 23.2Zm10.624-18.496 3.52 9.952h-7.008l3.488"
"-9.952Zm22.81 18.496h3.807v-17.216h-3.808v9.184c0 3.104-1.024 5.344-3."
"872 5.344s-3.168-2.272-3.168-4.608v-9.92h-3.808v10.848c0 4.096 1.664 6"
".784 5.76 6.784 2.336 0 4.096-.992 5.088-2.784v2.368Zm7.678-17.216h-2."
"56v2.752h2.56v9.952c0 3.52.736 4.512 4.416 4.512h2.816v-2.912h-1.376c-"
"1.632 0-2.048-.416-2.048-2.176v-9.376h3.456v-2.752h-3.456v-4.544h-3.80"
"8v4.544Zm13.179-5.984h-3.809v23.2h3.808v-9.152c0-3.104 1.088-5.344 4-5"
".344s3.264 2.272 3.264 4.608v9.888h3.808v-10.816c0-4.096-1.696-6.784-5"
".856-6.784-2.4 0-4.224.992-5.216 2.784V181.3Zm16.86 14.624c0-3.968 2.1"
"44-5.92 4.544-5.92 2.4 0 4.544 1.952 4.544 5.92s-2.144 5.888-4.544 5.8"
"88c-2.4 0-4.544-1.92-4.544-5.888Zm4.544-9.024c-4.192 0-8.48 2.816-8.48"
" 9.024 0 6.208 4.288 8.992 8.48 8.992s8.48-2.784 8.48-8.992c0-6.208-4."
"288-9.024-8.48-9.024Zm20.057.416a10.32 10.32 0 0 0-.992-.064c-2.08.032"
"-3.744 1.184-4.672 3.104v-3.072h-3.744V204.5h3.808v-9.024c0-3.456 1.37"
"6-4.416 3.776-4.416.576 0 1.184.032 1.824.096v-3.84Zm14.665 4.672c-.70"
"4-3.456-3.776-5.088-7.136-5.088-3.744 0-7.008 1.952-7.008 4.992 0 3.13"
"6 2.272 4.448 5.184 5.024l2.592.512c1.696.32 2.976.96 2.976 2.368s-1.4"
"72 2.24-3.456 2.24c-2.24 0-3.52-1.024-3.872-2.784h-3.712c.416 3.264 3."
"232 5.664 7.456 5.664 3.904 0 7.296-1.984 7.296-5.568 0-3.36-2.656-4.4"
"48-6.144-5.12l-2.432-.48c-1.472-.288-2.304-.896-2.304-2.048 0-1.152 1."
"536-1.888 3.2-1.888 1.92 0 3.36.608 3.776 2.176h3.584Zm6.284-10.688h-3"
".808v23.2h3.808v-9.152c0-3.104 1.088-5.344 4-5.344s3.264 2.272 3.264 4"
".608v9.888h3.808v-10.816c0-4.096-1.696-6.784-5.856-6.784-2.4 0-4.224.9"
"92-5.216 2.784V181.3Zm14.076 0v3.84h3.808v-3.84h-3.808Zm0 5.984V204.5h"
"3.808v-17.216h-3.808Zm10.781 8.608c0-3.968 1.952-5.888 4.448-5.888 2.6"
"56 0 4.256 2.272 4.256 5.888 0 3.648-1.6 5.92-4.256 5.92-2.496 0-4.448"
"-1.952-4.448-5.92Zm-3.648-8.608V210.1h3.808v-7.872c1.024 1.696 2.816 2"
".688 5.12 2.688 4.192 0 7.392-3.488 7.392-9.024 0-5.504-3.2-8.992-7.39"
'2-8.992-2.304 0-4.096.992-5.12 2.688v-2.304h-3.808Z" fill="#F0F6FC"/><'
'path class="k" stroke-dashoffset="5" stroke-dasharray="10" d="M215 545'
'h320"/><g transform="translate(231 237) scale(0.384)">',
shieldsAPI.getShieldSVG({
field: uint16(seed % 300),
colors: [
uint24(colors & 0xFFFFFF),
uint24((colors >> 24) & 0xFFFFFF),
uint24((colors >> 48) & 0xFFFFFF),
uint24((colors >> 72) & 0xFFFFFF)
],
hardware: uint16((seed >> 9) % 120),
frame: uint16((seed >> 17) % 5)
}),
'</g><text font-family="A" x="50%" y="605" fill="#F0F6FC" font-size="40'
'" dominant-baseline="central" text-anchor="middle">#',
_zfill(_tokenId),
'</text><rect class="i k o" x="215" y="65" mask="url(#a)" rx="20"/><cir'
'cle class="j k o" cy="65" mask="url(#a)"/><circle class="j k o" cy="68'
'5" mask="url(#a)"/></svg>'
)
),
'","attributes":[{"trait_type":"Used","value":',
ICurta(curta).hasUsedAuthorshipToken(_tokenId) ? "true" : "false",
"}]}"
)
)
);
}
function _zfill(uint256 _value) internal pure returns (string memory) {
string memory result = _value.toString();
if (_value < 10) return string.concat("000000", result);
else if (_value < 100) return string.concat("00000", result);
else if (_value < 1000) return string.concat("0000", result);
else if (_value < 10_000) return string.concat("000", result);
else if (_value < 100_000) return string.concat("00", result);
else if (_value < 1_000_000) return string.concat("0", result);
return result;
}
}
文件 2 的 21:Base64.sol
pragma solidity ^0.8.17;
library Base64 {
string internal constant TABLE =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678" "9+/";
function encode(bytes memory data) internal pure returns (string memory) {
if (data.length == 0) return "";
string memory table = TABLE;
uint256 encodedLength = ((data.length + 2) / 3) << 2;
string memory result = new string(encodedLength + 0x20);
assembly {
mstore(result, encodedLength)
let tablePtr := add(table, 1)
let dataPtr := data
let endPtr := add(dataPtr, mload(data))
let resultPtr := add(result, 0x20)
for { } lt(dataPtr, endPtr) { } {
dataPtr := add(dataPtr, 3)
let input := mload(dataPtr)
mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(0x12, input), 0x3F)))))
resultPtr := add(resultPtr, 1)
mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(0xC, input), 0x3F)))))
resultPtr := add(resultPtr, 1)
mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(shr(6, input), 0x3F)))))
resultPtr := add(resultPtr, 1)
mstore(resultPtr, shl(0xF8, mload(add(tablePtr, and(input, 0x3F)))))
resultPtr := add(resultPtr, 1)
}
switch mod(mload(data), 3)
case 1 { mstore(sub(resultPtr, 2), shl(0xF0, 0x3D3D)) }
case 2 { mstore(sub(resultPtr, 1), shl(0xF8, 0x3D)) }
}
return result;
}
}
文件 3 的 21:Curta.sol
pragma solidity ^0.8.17;
import { Owned } from "solmate/auth/Owned.sol";
import { LibString } from "solmate/utils/LibString.sol";
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { FlagRenderer } from "./FlagRenderer.sol";
import { FlagsERC721 } from "./FlagsERC721.sol";
import { AuthorshipToken } from "@/contracts/AuthorshipToken.sol";
import { ICurta } from "@/contracts/interfaces/ICurta.sol";
import { IPuzzle } from "@/contracts/interfaces/IPuzzle.sol";
import { Base64 } from "@/contracts/utils/Base64.sol";
contract Curta is ICurta, FlagsERC721, Owned {
using LibString for uint256;
uint256 constant PHASE_ONE_LENGTH = 2 days;
uint256 constant SUBMISSION_LENGTH = 5 days;
uint256 constant PHASE_TWO_MINIMUM_FEE = 0.02 ether;
uint256 constant PHASE_TWO_PROTOCOL_FEE = 0.01 ether;
uint120 constant DEFAULT_FLAG_COLORS = 0x181E28181E2827303DF0F6FC94A3B3;
AuthorshipToken public immutable override authorshipToken;
FlagRenderer public immutable override flagRenderer;
uint32 public override puzzleId = 0;
Fermat public override fermat;
mapping(uint32 => PuzzleColorsAndSolves) public override getPuzzleColorsAndSolves;
mapping(uint32 => PuzzleData) public override getPuzzle;
mapping(uint32 => address) public override getPuzzleAuthor;
mapping(address => mapping(uint32 => bool)) public override hasSolvedPuzzle;
mapping(uint256 => bool) public override hasUsedAuthorshipToken;
constructor(AuthorshipToken _authorshipToken, FlagRenderer _flagRenderer)
FlagsERC721("Curta", "CTF")
Owned(msg.sender)
{
authorshipToken = _authorshipToken;
flagRenderer = _flagRenderer;
puzzleId = 1;
getPuzzleColorsAndSolves[1] = PuzzleColorsAndSolves({
colors: DEFAULT_FLAG_COLORS,
phase0Solves: 1,
phase1Solves: 1,
phase2Solves: 0,
solves: 2
});
getPuzzle[1] = PuzzleData({
puzzle: IPuzzle(0xc220AE2Ac78e9Fa4B8b0BBA87bdB0Bca23F368c2),
addedTimestamp: uint40(1677715079),
firstSolveTimestamp: uint40(1677719903)
});
getPuzzleAuthor[1] = 0xA85572Cd96f1643458f17340b6f0D6549Af482F5;
hasSolvedPuzzle[0x58593392d72A9D90b133e1C8ecEec581C354687f][1] = true;
hasSolvedPuzzle[0x03433830468d771A921314D75b9A1DeA53C165d7][1] = true;
hasUsedAuthorshipToken[1] = true;
}
function solve(uint32 _puzzleId, uint256 _solution) external payable {
if (hasSolvedPuzzle[msg.sender][_puzzleId]) {
revert PuzzleAlreadySolved(_puzzleId);
}
PuzzleData memory puzzleData = getPuzzle[_puzzleId];
IPuzzle puzzle = puzzleData.puzzle;
if (address(puzzle) == address(0)) revert PuzzleDoesNotExist(_puzzleId);
uint40 firstSolveTimestamp = puzzleData.firstSolveTimestamp;
uint40 solveTimestamp = uint40(block.timestamp);
uint8 phase = _computePhase(firstSolveTimestamp, solveTimestamp);
if (phase == 3) revert SubmissionClosed(_puzzleId);
if (!puzzle.verify(puzzle.generate(msg.sender), _solution)) {
revert IncorrectSolution();
}
if (firstSolveTimestamp == 0) {
getPuzzle[_puzzleId].firstSolveTimestamp = solveTimestamp;
++getPuzzleColorsAndSolves[_puzzleId].phase0Solves;
authorshipToken.curtaMint(msg.sender);
}
hasSolvedPuzzle[msg.sender][_puzzleId] = true;
uint256 ethRemaining = msg.value;
unchecked {
_mint({
_to: msg.sender,
_id: (uint256(_puzzleId) << 128) | getPuzzleColorsAndSolves[_puzzleId].solves++,
_solveMetadata: uint56(((uint160(msg.sender) >> 132) << 28) | (_solution & 0xFFFFFFF)),
_phase: phase
});
if (phase == 1) {
++getPuzzleColorsAndSolves[_puzzleId].phase1Solves;
} else if (phase == 2) {
if (ethRemaining < PHASE_TWO_MINIMUM_FEE) revert InsufficientFunds();
++getPuzzleColorsAndSolves[_puzzleId].phase2Solves;
SafeTransferLib.safeTransferETH(owner, PHASE_TWO_PROTOCOL_FEE);
ethRemaining -= PHASE_TWO_PROTOCOL_FEE;
}
}
SafeTransferLib.safeTransferETH(getPuzzleAuthor[_puzzleId], ethRemaining);
emit SolvePuzzle({ id: _puzzleId, solver: msg.sender, solution: _solution, phase: phase });
}
function addPuzzle(IPuzzle _puzzle, uint256 _tokenId) external {
if (msg.sender != authorshipToken.ownerOf(_tokenId)) revert Unauthorized();
if (hasUsedAuthorshipToken[_tokenId]) revert AuthorshipTokenAlreadyUsed(_tokenId);
hasUsedAuthorshipToken[_tokenId] = true;
unchecked {
uint32 curPuzzleId = ++puzzleId;
getPuzzle[curPuzzleId] = PuzzleData({
puzzle: _puzzle,
addedTimestamp: uint40(block.timestamp),
firstSolveTimestamp: 0
});
getPuzzleAuthor[curPuzzleId] = msg.sender;
getPuzzleColorsAndSolves[curPuzzleId].colors = DEFAULT_FLAG_COLORS;
emit AddPuzzle(curPuzzleId, msg.sender, _puzzle);
}
}
function setPuzzleColors(uint32 _puzzleId, uint120 _colors) external {
if (getPuzzleAuthor[_puzzleId] != msg.sender) revert Unauthorized();
getPuzzleColorsAndSolves[_puzzleId].colors = _colors;
emit UpdatePuzzleColors(_puzzleId, _colors);
}
function setFermat(uint32 _puzzleId) external {
PuzzleData memory puzzleData = getPuzzle[_puzzleId];
if (puzzleData.firstSolveTimestamp == 0) revert PuzzleNotSolved(_puzzleId);
if (fermat.puzzleId == _puzzleId) revert PuzzleAlreadyFermat(_puzzleId);
unchecked {
uint40 timeTaken = puzzleData.firstSolveTimestamp - puzzleData.addedTimestamp;
if (timeTaken < fermat.timeTaken) revert PuzzleNotFermat(_puzzleId);
fermat.puzzleId = _puzzleId;
fermat.timeTaken = timeTaken;
}
address puzzleAuthor = getPuzzleAuthor[_puzzleId];
address currentOwner = getTokenData[0].owner;
unchecked {
if (currentOwner != address(0)) {
getUserBalances[currentOwner].balance--;
delete getApproved[0];
emit Transfer(currentOwner, address(0), 0);
}
getUserBalances[puzzleAuthor].balance++;
}
getTokenData[0].owner = puzzleAuthor;
emit Transfer(address(0), puzzleAuthor, 0);
}
function tokenURI(uint256 _tokenId) external view override returns (string memory) {
TokenData memory tokenData = getTokenData[_tokenId];
require(tokenData.owner != address(0), "NOT_MINTED");
if (_tokenId == 0) {
return "data:application/json;base64,eyJuYW1lIjoiRmVybWF0IiwiZGVzY3JpcHRpb24iOiJMb25nZX"
"N0IHVuc29sdmVkIHB1enpsZS4iLCJpbWFnZV9kYXRhIjoiZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4y"
"WnlCM2FXUjBhRDBpTlRVd0lpQm9aV2xuYUhROUlqVTFNQ0lnZG1sbGQwSnZlRDBpTUNBd0lEVTFNQ0ExTlRBaU"
"lHWnBiR3c5SW01dmJtVWlJSGh0Ykc1elBTSm9kSFJ3T2k4dmQzZDNMbmN6TG05eVp5OHlNREF3TDNOMlp5SStQ"
"SEJoZEdnZ1ptbHNiRDBpSXpFNE1VVXlPQ0lnWkQwaVRUQWdNR2czTlRCMk56VXdTREI2SWk4K1BISmxZM1FnZU"
"QwaU1UUXpJaUI1UFNJMk9TSWdkMmxrZEdnOUlqSTJOQ0lnYUdWcFoyaDBQU0kwTVRJaUlISjRQU0k0SWlCbWFX"
"eHNQU0lqTWpjek1ETkVJaTgrUEhKbFkzUWdlRDBpTVRRM0lpQjVQU0kzTXlJZ2MzUnliMnRsUFNJak1UQXhNek"
"ZESWlCM2FXUjBhRDBpTWpVMklpQm9aV2xuYUhROUlqUXdOQ0lnY25nOUlqUWlJR1pwYkd3OUlpTXdaREV3TVRj"
"aUx6NDhMM04yWno0PSJ9";
}
uint32 _puzzleId = uint32(_tokenId >> 128);
PuzzleData memory puzzleData = getPuzzle[_puzzleId];
address author = getPuzzleAuthor[_puzzleId];
uint32 solves = getPuzzleColorsAndSolves[_puzzleId].solves;
uint120 colors = getPuzzleColorsAndSolves[_puzzleId].colors;
uint8 phase = tokenData.solveTimestamp == puzzleData.firstSolveTimestamp
? 0
: tokenData.solveTimestamp < puzzleData.firstSolveTimestamp + PHASE_ONE_LENGTH
? 1
: 2;
return flagRenderer.render({
_puzzleData: puzzleData,
_tokenId: _tokenId + 1,
_author: author,
_solveTime: tokenData.solveTimestamp - puzzleData.addedTimestamp,
_solveMetadata: tokenData.solveMetadata,
_phase: phase,
_solves: solves,
_colors: colors
});
}
function _computePhase(uint40 _firstSolveTimestamp, uint40 _solveTimestamp)
internal
pure
returns (uint8 phase)
{
assembly {
phase :=
mul(
iszero(iszero(_firstSolveTimestamp)),
add(
1,
add(
gt(_solveTimestamp, add(_firstSolveTimestamp, PHASE_ONE_LENGTH)),
gt(_solveTimestamp, add(_firstSolveTimestamp, SUBMISSION_LENGTH))
)
)
)
}
}
}
文件 4 的 21: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);
}
}
文件 5 的 21: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;
}
}
文件 6 的 21:FlagRenderer.sol
pragma solidity ^0.8.17;
import { LibString } from "solady/utils/LibString.sol";
import { ICurta } from "@/contracts/interfaces/ICurta.sol";
import { IColormapRegistry } from "@/contracts/interfaces/IColormapRegistry.sol";
import { Base64 } from "@/contracts/utils/Base64.sol";
contract FlagRenderer {
using LibString for uint256;
using LibString for address;
using LibString for string;
IColormapRegistry constant colormapRegistry =
IColormapRegistry(0x0000000012883D1da628e31c0FE52e35DcF95D50);
function render(
ICurta.PuzzleData memory _puzzleData,
uint256 _tokenId,
address _author,
uint40 _solveTime,
uint56 _solveMetadata,
uint8 _phase,
uint32 _solves,
uint120 _colors
) external view returns (string memory) {
string memory attributes;
{
attributes = string.concat(
'[{"trait_type":"Puzzle","value":"',
_puzzleData.puzzle.name(),
'"},{"trait_type":"Puzzle ID","value":',
uint256(_tokenId >> 128).toString(),
'},{"trait_type":"Author","value":"',
_author.toHexStringChecksumed(),
'"},{"trait_type":"Phase","value":"',
uint256(_phase).toString(),
'"},{"trait_type":"Solver","value":"',
_formatValueAsAddress(uint256(_solveMetadata & 0xFFFFFFF)),
'"},{"trait_type":"Solve time","value":',
uint256(_solveTime).toString(),
'},{"trait_type":"Rank","value":',
uint256(uint128(_tokenId)).toString(),
"}]"
);
}
string memory image;
{
image = string.concat(
'<svg xmlns="http://www.w3.org/2000/svg" width="550" height="550" viewBox="0 0 550 '
'550"><style>@font-face{font-family:A;src:url(data:font/woff2;charset-utf-8;base64,'
"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)}@font-face{font-family:B;src:u"
"rl(data:font/woff2;charset-utf-8;base64,d09GMgABAAAAAAhQABAAAAAAD4gAAAf2AAEAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAGhYbhCocUAZgP1NUQVRIAHQRCAqPQIx0Cy4AATYCJANYBCAFg1YHIAwHG/YM"
"UZRR0hhkPxNMlbEboUgq+fBI0pEkVMwtRsc8kTqPvob/GX6NoH7st/e+aHLMEyXSaHjI5tU7oVEJRaw0mk"
"2nBHj/P27694UIJFCoHdq50ok6paJURELVmdGOUTWmxtTDXPn9zPxom4glMvErMJW4alpRu79ZXw6UtFRH"
"/wP4wv6/tVZ3013cAY9nCTwkHhFCIlPKnxP7J3PoDKpJxLtGOqGIyGnDsiZIBUIhtMhTmqVhJtyMGedU9E"
"0+RQBNABIEsYYgoG6AMbCFBeszo257Y86ocMBvxbAtP8gZBbBEOEyDoQ1DoYwEq3ZEjMdbW2SsoQwLL9Uh"
"NpGWBcwcl5DPdS1HrnGR9VrcRDbPruGaAV4JWAbmEqeVSq7ZMHXB1nex0hbQ7QIMGoIBRSRh5+0YDIXAw8"
"cnIpISHHITgyjoEDy8B/JkgkRVMjPzT0ne3bgMTGT5pDwUY8MWguETwiujkBoBKnOkFvhQmsKnmteaEAWY"
"WD5Z4+HCpeXNialh5VNv+btjzhGmFhB3gDI+YIIQClkOOkqaOegsIERWDHC0nRNKUiu0EeCAkCI1SO+AE4"
"ipIPIVYOpAkCGiaJwXSj7pfs51YvNESuDpkwVAc2RxpYRAv9NtV69OtSqVdLQhmCmgnBPDnjp9HpgAaRGY"
"wgN4i0LEjm6MCA/yIm5l/dGcHT5ukKVUUrUMxoUUjvZBN7psscGtpPxTq/D4bFn6FhxEFz65g2XefcoU3U"
"ZFwXAXP6GLn93jZd1/Lip6ajQyrPgy8C6k33lCWyDxCB/K0OgsTGKThMmsLuw4pBuRzJcbjRKoJz675Shy"
"M8IaR8dbz4jdf85bnhrlYYVT2BnD0GMZQhnGvnWOjwKmaDOIpBG9neMc9Tiq4LBwHGe9E8MyOLNNYIlkcO"
"GtW6No8Ey/60O677y3abuTVPjsnh9qqYNxJEMwSXO5Y3y/A8ZPGNPzLoAVsszIg05NKXOFnMNODMskJRfG"
"SkdB50AuxIVJEMXSiYsB4nhFEptEs/2FwESO8kv7S4xPCzIqBnJLp3Z2bpOwu7B2Ua3WGKwZTRqj011j8o"
"xzIpPHha8aCJpgXUCCXJmkQtrOSuUNnBlHLLWvo3k0x9g9NCkk0zPtzybSyaIkhg2a8EEKIy9UKCZclMjh"
"OlbA8MtvYnFa4MsODySK8Cs+J/27bXBorTb80ET/gyMObQ+j6SMQO+jPoelfaJ1t0Pfuuo3f7ndInLeS5f"
"/e3p43DwAYuRUA/r8bv0JDgd3msXUbu+/Zs1HqUNUlezKk4Zyny3aWdd133rdpN1ADRpys6J1V0ac3rwLk"
"uWBZ/q/5YP4FReQW5ZoVseyyPFnp1YkrbU4t3p0ad1rd1MEsqTQ9mFWy9w/TDIKYfM2NRcmlT92ymiL9o9"
"IOrAzp+Wvh//Ok/652HIRppafOjC3e/QnXwtb+G/4LL7qhyQ5o6HS/MSpYVGSpt1o1cjJ/U019feuxjYPW"
"Pl45teicrmtG7BJDv90hCXf3fTZdE0ZFRB1Y6X2z2BCm7PS689S3R6ZNssU6V6ZqnFegC7tXBd8pYPpsU+"
"6m0is1JwYfb/T5+LnRd+AJw6YdSs/ULT1MvH96P/7JTaWbzuh86YY6JX6mQqM+qfbBwMHQxzAKRrLPHzVA"
"6+ITGr3STVVu89kEfHhh6rHFsGWHMuZm6QLWTN8xFsYztQYA+tU1+NJndDB+9s0ezTrVoTSZOgInXLiW+H"
"blsZ1RA2/71lzanul/8XBcesmlwrCHafn6RPNtVZm7ZlxYcM+Vz2f2j2nV//CCO0CCCUigFsJ8pfucOUr3"
"+RQC7BrN6y+rbh9w8aZNr5MjgA+pnKb41lw/8qQOeiyCusn8nV6YbUYnG/M1FRfyJyVLpsS/L268QoAYN+"
"FtB9an1u04sfmNuUdWa02uW5W3V0eUAXrM1elMWi8yPvZ+qV73NjUDTl0zGfYcutAQtd3b7xCMS+9zbdJp"
"vU+XWe9HzmsP7mfu4kAXXb1ZdbHWm9JqvQUX66uqjkD0YaeaOwW3ZwJ/1ovPPd6CDdmMgPmzpqYsXvmNZG"
"jU/ZLCbsuRg3nnOuHoSL2bb13gJZi4AM71OPc6wgTEft8pSrc+4wbbPRt9M1FuNcf3qFy1LReWYLHF+Sgm"
"XJL0eZ1e/Tg17fKWw2c3Ou11Vh0ANxL654L5qIFu2eck/kDEzGnuVnZTr8ptsxj/wGGxwxMGVHy/h5x5Mj"
"w+Q9e2FPjZMIubRWZfGrtooUym+FjZQvrqYxfOA9vclhZu1qXt/P6O9b6hupPn9Ry3PTSR9fFJTycGEECh"
"+PB3s7bZUzztizLMAOD746FhAH7kq37/J79VI7EQgDIMIMA/MRs9B/Xnz5P/q/nfq5t9V8A9xYfvchFqyH"
"GgJjnNkZI9UA3AdFqI3+6QckegVpOREqmoyd0+3E61AmyUfZtlfcBL/bB44l2MhC8goGcTCEWSQ2KJ31aX"
"ioeKAgC3AA8DQlMbMBa0Bh6ZFgOumzsGwgQf9aRuMSQFEoMw5hgkQMcKlLoLda5C6QNEAiFEjCE7ij7WNW"
"Rv4GI55AEQq4C8PeZxhGK2B7FQUwAKOMSBQCUTC6OrsgDRIJW02jp85uWZrlBkKHiDHM/Xg/zFI0/gIWUB"
"Q6PIOJhsqjVONk6WsEh7Pr4zO4JXtRkVKeSIU0uSok0mEmMOtIMT+OlRClJ9471uzlPTJCrkYX7/PQbo/I"
"z+3axYAg==)}text,tspan{dominant-baseline:central}.a{font-family:A;letter-spacing:-"
".05em}.b{font-family:B}.c{font-size:16px}.d{font-size:12px}.f{fill:#",
uint256((_colors >> 72) & 0xFFFFFF).toHexStringNoPrefix(3),
"}.h{fill:#",
uint256((_colors >> 24) & 0xFFFFFF).toHexStringNoPrefix(3),
"}.i{fill:#",
uint256(_colors & 0xFFFFFF).toHexStringNoPrefix(3),
"}.j{fill:none;stroke-linejoin:round;stroke-linecap:round;stroke:#",
uint256(_colors & 0xFFFFFF).toHexStringNoPrefix(3)
);
}
{
image = string.concat(
image,
'}.x{width:1px;height:1px}</style><mask id="m"><rect width="20" height="20" rx="0.3'
'70370" fill="#FFF"/></mask><path d="M0 0h550v550H0z" style="fill:#',
uint256((_colors >> 96) & 0xFFFFFF).toHexStringNoPrefix(3),
'"/><rect x="143" y="69" width="264" height="412" rx="8" fill="#',
uint256((_colors >> 48) & 0xFFFFFF).toHexStringNoPrefix(3),
'"/><rect class="f" x="147" y="73" width="256" height="404" rx="4"/>',
_drawStars(_phase)
);
}
{
image = string.concat(
image,
'<text class="a h" x="163" y="101" font-size="20">Puzzle #',
(_tokenId >> 128).toString(),
'</text><text x="163" y="121"><tspan class="b d i">Created by </tspan><tspan class='
'"a d h">'
);
}
{
uint256 luma =
((_colors >> 88) & 0xFF) + ((_colors >> 80) & 0xFF) + ((_colors >> 72) & 0xFF);
image = string.concat(
image,
_formatValueAsAddress(uint160(_author) >> 132),
'</tspan></text><rect x="163" y="137" width="224" height="224" fill="rgba(',
luma < ((255 * 3) >> 1) ? "255,255,255" : "0,0,0",
',0.2)" rx="8"/>',
_drawDrunkenBishop(_solveMetadata, _tokenId),
'<path class="j" d="M176.988 387.483A4.992 4.992 0 0 0 173 385.5a4.992 4.992 0 0 0-'
"3.988 1.983m7.975 0a6 6 0 1 0-7.975 0m7.975 0A5.977 5.977 0 0 1 173 389a5.977 5.97"
'7 0 0 1-3.988-1.517M175 381.5a2 2 0 1 1-4 0 2 2 0 0 1 4 0z"/><text class="a c h" x'
'="187" y="383">',
_formatValueAsAddress(_solveMetadata >> 28),
'</text><text class="b d i" x="187" y="403">Captured by</text><path class="j" d="m2'
"85.5 380 2 1.5-2 1.5m3 0h2m-6 5.5h9a1.5 1.5 0 0 0 1.5-1.5v-8a1.5 1.5 0 0 0-1.5-1.5"
'h-9a1.5 1.5 0 0 0-1.5 1.5v8a1.5 1.5 0 0 0 1.5 1.5z"/><text class="a c h" x="303" y'
'="383">',
_formatValueAsAddress(_solveMetadata & 0xFFFFFFF),
'</text><text class="b d i" x="303" y="403">Solution</text><path class="j" d="M176 '
"437.5h-6m6 0a2 2 0 0 1 2 2h-10a2 2 0 0 1 2-2m6 0v-2.25a.75.75 0 0 0-.75-.75h-.58m-"
"4.67 3v-2.25a.75.75 0 0 1 .75-.75h.581m3.338 0h-3.338m3.338 0a4.97 4.97 0 0 1-.654"
"-2.115m-2.684 2.115a4.97 4.97 0 0 0 .654-2.115m-3.485-4.561c-.655.095-1.303.211-1."
"944.347a4.002 4.002 0 0 0 3.597 3.314m-1.653-3.661V428a4.49 4.49 0 0 0 1.653 3.485"
"m-1.653-3.661v-1.01a32.226 32.226 0 0 1 4.5-.314c1.527 0 3.03.107 4.5.313v1.011m-7"
".347 3.661a4.484 4.484 0 0 0 1.832.9m5.515-4.561V428a4.49 4.49 0 0 1-1.653 3.485m1"
".653-3.661a30.88 30.88 0 0 1 1.944.347 4.002 4.002 0 0 1-3.597 3.314m0 0a4.484 4.4"
'84 0 0 1-1.832.9m0 0a4.515 4.515 0 0 1-2.03 0"/><text><tspan class="a c h" x="187"'
' y="433">'
);
}
{
image = string.concat(
image,
uint256(uint128(_tokenId)).toString(),
' </tspan><tspan class="a d i" y="435">/ ',
uint256(_solves).toString(),
'</tspan></text><text class="b d i" x="187" y="453">Rank</text><path class="j" d="M'
'289 429v4h3m3 0a6 6 0 1 1-12 0 6 6 0 0 1 12 0z"/><text class="a c h" x="303" y="43'
'3">',
_formatTime(_solveTime),
'</text><text class="b d i" x="303" y="453">Solve time</text></svg>'
);
}
return string.concat(
"data:application/json;base64,",
Base64.encode(
abi.encodePacked(
'{"name":"',
_puzzleData.puzzle.name(),
": Flag #",
uint256(uint128(_tokenId)).toString(),
'","description":"This token represents solve #',
uint256(uint128(_tokenId)).toString(),
" in puzzle #",
uint256(_tokenId >> 128).toString(),
'.","image_data": "data:image/svg+xml;base64,',
Base64.encode(abi.encodePacked(image)),
'","attributes":',
attributes,
"}"
)
)
);
}
function _drawDrunkenBishop(uint56 _solveMetadata, uint256 _tokenId)
internal
view
returns (string memory)
{
uint256 seed = uint256(keccak256(abi.encodePacked(_tokenId, _solveMetadata)));
bytes32 colormapHash = [
bytes32(0xfd29b65966772202ffdb08f653439b30c849f91409915665d99dbfa5e5dab938),
bytes32(0x850ce48e7291439b1e41d21fc3f75dddd97580a4ff94aa9ebdd2bcbd423ea1e8),
bytes32(0x4f5e8ea8862eff315c110b682ee070b459ba8983a7575c9a9c4c25007039109d),
bytes32(0xf2e92189cb6903b98d854cd74ece6c3fafdb2d3472828a950633fdaa52e05032),
bytes32(0xa33e6c7c5627ecabfd54c4d85f9bf04815fe89a91379fcf56ccd8177e086db21),
bytes32(0xaa84b30df806b46f859a413cb036bc91466307aec5903fc4635c00a421f25d5c),
bytes32(0x864a6ee98b9b21ac0291523750d637250405c24a6575e1f75cfbd7209a810ce6),
bytes32(0xfd60cd3811f002814944a7d36167b7c9436187a389f2ee476dc883e37dc76bd2),
bytes32(0xa8309447f8bd3b5e5e88a0abc05080b7682e4456c388b8636d45f5abb2ad2587),
bytes32(0x3be719b0c342797212c4cb33fde865ed9cbe486eb67176265bc0869b54dee925),
bytes32(0xca0da6b6309ed2117508207d68a59a18ccaf54ba9aa329f4f60a77481fcf2027),
bytes32(0x5ccb29670bb9de0e3911d8e47bde627b0e3640e49c3d6a88d51ff699160dfbe1),
bytes32(0x3de8f27f386dab3dbab473f3cc16870a717fe5692b4f6a45003d175c559dfcba),
bytes32(0x026736ef8439ebcf8e7b8006bf8cb7482ced84d71b900407a9ed63e1b7bfe234),
bytes32(0xc1806ea961848ac00c1f20aa0611529da522a7bd125a3036fe4641b07ee5c61c),
bytes32(0x87970b686eb726750ec792d49da173387a567764d691294d764e53439359c436),
bytes32(0xaa6277ab923279cf59d78b9b5b7fb5089c90802c353489571fca3c138056fb1b),
bytes32(0xdc1cecffc00e2f3196daaf53c27e53e6052a86dc875adb91607824d62469b2bf)
][seed % 18];
uint256 index = 210;
uint256 max = 1;
uint8[] memory counts = new uint8[](400);
counts[index] = 1;
unchecked {
while (seed != 0) {
(uint256 x, uint256 y) = (index % 20, index / 20);
assembly {
switch and(shr(1, seed), 1)
case 0 { index := add(index, mul(20, iszero(eq(y, 19)))) }
default { index := sub(index, mul(20, iszero(eq(y, 0)))) }
switch and(seed, 1)
case 0 { index := add(index, iszero(eq(x, 19))) }
default { index := sub(index, iszero(eq(y, 0))) }
}
if (++counts[index] > max) max = counts[index];
seed >>= 2;
}
}
string memory image = '<g transform="translate(167 141) scale(10.8)" mask="url(#m)">';
unchecked {
for (uint256 i; i < 400; ++i) {
image = string.concat(
image,
'<rect class="x" x="',
(i % 20).toString(),
'" y="',
(i / 20).toString(),
'" fill="#',
colormapRegistry.getValueAsHexString(
colormapHash, uint8((uint256(counts[i]) * 255) / max)
),
'"/>'
);
}
}
return string.concat(image, "</g>");
}
function _drawStars(uint8 _phase) internal pure returns (string memory) {
unchecked {
uint256 width = ((4 - _phase) << 4);
return string.concat(
'<rect class="h" x="',
(383 - width).toString(),
'" y="97" width="',
width.toString(),
'" height="24" rx="12"/><path id="s" d="M366.192 103.14c.299-.718 1.317-.718 1.616 '
"0l1.388 3.338 3.603.289c.776.062 1.09 1.03.499 1.536l-2.745 2.352.838 3.515c.181.7"
"57-.642 1.355-1.306.95L367 113.236l-3.085 1.884c-.664.405-1.487-.193-1.306-.95l.83"
'8-3.515-2.745-2.352c-.591-.506-.277-1.474.5-1.536l3.602-.289 1.388-3.337z"/>',
_phase < 2 ? '<use href="#s" x="-16" />' : "",
_phase < 1 ? '<use href="#s" x="-32" />' : ""
);
}
}
function _formatValueAsAddress(uint256 _value) internal pure returns (string memory) {
return string.concat(
string(abi.encodePacked(bytes32("0123456789ABCDEF")[(_value >> 24) & 0xF])),
(_value & 0xFFFFFF).toHexStringNoPrefix(3).toCase(true)
);
}
function _formatTime(uint40 _solveTime) internal pure returns (string memory) {
if (_solveTime < 96 hours) {
uint256 numHours = _solveTime / (1 hours);
uint256 numMinutes = (_solveTime % (1 hours)) / (1 minutes);
uint256 numSeconds = _solveTime % (1 minutes);
return string.concat(
_zeroPadOne(numHours), ":", _zeroPadOne(numMinutes), ":", _zeroPadOne(numSeconds)
);
} else if (_solveTime < 41 days) {
return string.concat(uint256(_solveTime / (1 hours)).toString(), " hours");
} else if (_solveTime < 730 days) {
return string.concat(uint256(_solveTime / (1 days)).toString(), " days");
}
return string.concat(uint256(_solveTime / (365 days)).toString(), " years");
}
function _zeroPadOne(uint256 _value) internal pure returns (string memory) {
if (_value < 10) {
return string.concat("0", _value.toString());
}
return _value.toString();
}
}
文件 7 的 21:FlagsERC721.sol
pragma solidity ^0.8.17;
import { ERC721TokenReceiver } from "solmate/tokens/ERC721.sol";
abstract contract FlagsERC721 {
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
event Transfer(address indexed from, address indexed to, uint256 indexed id);
struct TokenData {
address owner;
uint40 solveTimestamp;
uint56 solveMetadata;
}
struct UserBalance {
uint32 phase0Solves;
uint32 phase1Solves;
uint32 phase2Solves;
uint32 solves;
uint32 balance;
}
string public name;
string public symbol;
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
mapping(uint256 => TokenData) public getTokenData;
mapping(address => UserBalance) public getUserBalances;
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
getTokenData[(1 << 128)] = TokenData({
owner: address(0x58593392d72A9D90b133e1C8ecEec581C354687f),
solveTimestamp: uint40(1677719903),
solveMetadata: uint56(24867875967088679)
});
getTokenData[(1 << 128) | 1] = TokenData({
owner: address(0x03433830468d771A921314D75b9A1DeA53C165d7),
solveTimestamp: uint40(1677724451),
solveMetadata: uint56(918333653799954)
});
getUserBalances[0x58593392d72A9D90b133e1C8ecEec581C354687f] = UserBalance({
phase0Solves: 1,
phase1Solves: 0,
phase2Solves: 0,
solves: 1,
balance: 1
});
getUserBalances[0x03433830468d771A921314D75b9A1DeA53C165d7] = UserBalance({
phase0Solves: 0,
phase1Solves: 1,
phase2Solves: 0,
solves: 1,
balance: 1
});
}
function _mint(address _to, uint256 _id, uint56 _solveMetadata, uint8 _phase) internal {
unchecked {
++getUserBalances[_to].balance;
++getUserBalances[_to].solves;
if (_phase == 0) ++getUserBalances[_to].phase0Solves;
else if (_phase == 1) ++getUserBalances[_to].phase1Solves;
else ++getUserBalances[_to].phase2Solves;
}
getTokenData[_id] = TokenData({
owner: _to,
solveMetadata: _solveMetadata,
solveTimestamp: uint40(block.timestamp)
});
emit Transfer(address(0), _to, _id);
}
function ownerOf(uint256 _id) public view returns (address owner) {
require((owner = getTokenData[_id].owner) != address(0), "NOT_MINTED");
}
function balanceOf(address _owner) external view returns (uint256) {
require(_owner != address(0), "ZERO_ADDRESS");
return getUserBalances[_owner].balance;
}
function approve(address _spender, uint256 _id) external {
address owner = getTokenData[_id].owner;
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[_id] = _spender;
emit Approval(owner, _spender, _id);
}
function setApprovalForAll(address _operator, bool _approved) external {
isApprovedForAll[msg.sender][_operator] = _approved;
emit ApprovalForAll(msg.sender, _operator, _approved);
}
function transferFrom(address _from, address _to, uint256 _id) public virtual {
require(_from == getTokenData[_id].owner, "WRONG_FROM");
require(_to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == _from || isApprovedForAll[_from][msg.sender]
|| msg.sender == getApproved[_id],
"NOT_AUTHORIZED"
);
unchecked {
getUserBalances[_from].balance--;
getUserBalances[_to].balance++;
}
getTokenData[_id].owner = _to;
delete getApproved[_id];
emit Transfer(_from, _to, _id);
}
function safeTransferFrom(address _from, address _to, uint256 _id) external {
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)
external
{
transferFrom(_from, _to, _id);
require(
_to.code.length == 0
|| ERC721TokenReceiver(_to).onERC721Received(msg.sender, _from, _id, _data)
== ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function tokenURI(uint256 _tokenId) external view virtual returns (string memory);
function supportsInterface(bytes4 _interfaceId) external pure returns (bool) {
return _interfaceId == 0x01FFC9A7
|| _interfaceId == 0x80AC58CD
|| _interfaceId == 0x5B5E139F;
}
}
文件 8 的 21:IColormapRegistry.sol
pragma solidity ^0.8.17;
import { IPaletteGenerator } from "@/contracts/interfaces/IPaletteGenerator.sol";
interface IColormapRegistry {
error ColormapAlreadyExists(bytes32 _colormapHash);
error ColormapDoesNotExist(bytes32 _colormapHash);
error SegmentDataInvalid(uint256 _segmentData);
struct SegmentData {
uint256 r;
uint256 g;
uint256 b;
}
event RegisterColormap(bytes32 _hash, IPaletteGenerator _paletteGenerator);
event RegisterColormap(bytes32 _hash, SegmentData _segmentData);
function segments(bytes32 _colormapHash) external view returns (uint256, uint256, uint256);
function paletteGenerators(bytes32 _colormapHash) external view returns (IPaletteGenerator);
function register(IPaletteGenerator _paletteGenerator) external;
function register(SegmentData memory _segmentData) external;
function getValue(bytes32 _colormapHash, uint256 _position)
external
view
returns (uint256, uint256, uint256);
function getValueAsUint8(bytes32 _colormapHash, uint8 _position)
external
view
returns (uint8, uint8, uint8);
function getValueAsHexString(bytes32 _colormapHash, uint8 _position)
external
view
returns (string memory);
}
文件 9 的 21:ICurta.sol
pragma solidity ^0.8.17;
import { IPuzzle } from "./IPuzzle.sol";
import { AuthorshipToken } from "@/contracts/AuthorshipToken.sol";
import { FlagRenderer } from "@/contracts/FlagRenderer.sol";
interface ICurta {
error AuthorshipTokenAlreadyUsed(uint256 _tokenId);
error IncorrectSolution();
error InsufficientFunds();
error PuzzleAlreadyFermat(uint32 _puzzleId);
error PuzzleAlreadySolved(uint32 _puzzleId);
error PuzzleDoesNotExist(uint32 _puzzleId);
error PuzzleNotFermat(uint32 _puzzleId);
error PuzzleNotSolved(uint32 _puzzleId);
error SubmissionClosed(uint32 _puzzleId);
error Unauthorized();
struct Fermat {
uint32 puzzleId;
uint40 timeTaken;
}
struct PuzzleData {
IPuzzle puzzle;
uint40 addedTimestamp;
uint40 firstSolveTimestamp;
}
struct PuzzleColorsAndSolves {
uint120 colors;
uint32 phase0Solves;
uint32 phase1Solves;
uint32 phase2Solves;
uint32 solves;
}
event AddPuzzle(uint32 indexed id, address indexed author, IPuzzle puzzle);
event SolvePuzzle(uint32 indexed id, address indexed solver, uint256 solution, uint8 phase);
event UpdatePuzzleColors(uint32 indexed id, uint256 colors);
function flagRenderer() external view returns (FlagRenderer);
function authorshipToken() external view returns (AuthorshipToken);
function puzzleId() external view returns (uint32);
function fermat() external view returns (uint32 puzzleId, uint40 timeTaken);
function getPuzzleColorsAndSolves(uint32 _puzzleId)
external
view
returns (
uint120 colors,
uint32 phase0Solves,
uint32 phase1Solves,
uint32 phase2Solves,
uint32 solves
);
function getPuzzle(uint32 _puzzleId)
external
view
returns (IPuzzle puzzle, uint40 addedTimestamp, uint40 firstSolveTimestamp);
function getPuzzleAuthor(uint32 _puzzleId) external view returns (address);
function hasSolvedPuzzle(address _solver, uint32 _puzzleId) external view returns (bool);
function hasUsedAuthorshipToken(uint256 _tokenId) external view returns (bool);
function solve(uint32 _puzzleId, uint256 _solution) external payable;
function addPuzzle(IPuzzle _puzzle, uint256 _id) external;
function setPuzzleColors(uint32 _puzzleId, uint120 _colors) external;
function setFermat(uint32 _puzzleId) external;
}
文件 10 的 21:IEmblemWeaver.sol
pragma solidity ^0.8.9;
import "./IShields.sol";
import "./IFrameGenerator.sol";
import "./IFieldGenerator.sol";
import "./IHardwareGenerator.sol";
import "./IShieldBadgeSVGs.sol";
interface IEmblemWeaver {
function fieldGenerator() external returns (IFieldGenerator);
function hardwareGenerator() external returns (IHardwareGenerator);
function frameGenerator() external returns (IFrameGenerator);
function shieldBadgeSVGGenerator() external returns (IShieldBadgeSVGs);
function generateShieldURI(IShields.Shield memory shield)
external
view
returns (string memory);
function generateShieldBadgeURI(IShields.ShieldBadge shieldBadge)
external
view
returns (string memory);
}
文件 11 的 21:IFieldGenerator.sol
pragma solidity ^0.8.9;
interface IFieldGenerator {
enum FieldCategories {
MYTHIC,
HERALDIC
}
struct FieldData {
string title;
FieldCategories fieldType;
string svgString;
}
function generateField(uint16 field, uint24[4] memory colors)
external
view
returns (FieldData memory);
}
文件 12 的 21:IFrameGenerator.sol
pragma solidity ^0.8.9;
interface IFrameGenerator {
struct FrameData {
string title;
uint256 fee;
string svgString;
}
function generateFrame(uint16 Frame)
external
view
returns (FrameData memory);
}
文件 13 的 21:IHardwareGenerator.sol
pragma solidity ^0.8.9;
interface IHardwareGenerator {
enum HardwareCategories {
STANDARD,
SPECIAL
}
struct HardwareData {
string title;
HardwareCategories hardwareType;
string svgString;
}
function generateHardware(uint16 hardware)
external
view
returns (HardwareData memory);
}
文件 14 的 21:IPaletteGenerator.sol
pragma solidity ^0.8.17;
interface IPaletteGenerator {
error InvalidPosition(uint256 _position);
function r(uint256 _position) external pure returns (uint256);
function g(uint256 _position) external pure returns (uint256);
function b(uint256 _position) external pure returns (uint256);
}
文件 15 的 21:IPuzzle.sol
pragma solidity ^0.8.17;
interface IPuzzle {
function name() external pure returns (string memory);
function generate(address _seed) external returns (uint256);
function verify(uint256 _start, uint256 _solution) external returns (bool);
}
文件 16 的 21:IShieldBadgeSVGs.sol
pragma solidity ^0.8.9;
import "./IShields.sol";
interface IShieldBadgeSVGs {
function generateShieldBadgeSVG(IShields.ShieldBadge shieldBadge)
external
view
returns (string memory);
}
文件 17 的 21:IShields.sol
pragma solidity ^0.8.9;
import "./IEmblemWeaver.sol";
interface IShields {
enum ShieldBadge {
MAKER,
STANDARD
}
struct Shield {
bool built;
uint16 field;
uint16 hardware;
uint16 frame;
ShieldBadge shieldBadge;
uint24[4] colors;
}
function emblemWeaver() external view returns (IEmblemWeaver);
function shields(uint256 tokenId)
external
view
returns (
uint16 field,
uint16 hardware,
uint16 frame,
uint24 color1,
uint24 color2,
uint24 color3,
uint24 color4,
ShieldBadge shieldBadge
);
}
文件 18 的 21:IShieldsAPI.sol
pragma solidity ^0.8.9;
import "./IShields.sol";
import "./IFieldGenerator.sol";
import "./IHardwareGenerator.sol";
import "./IFrameGenerator.sol";
interface IShieldsAPI {
function getShield(uint256 shieldId)
external
view
returns (IShields.Shield memory);
function getShieldSVG(uint256 shieldId)
external
view
returns (string memory);
function getShieldSVG(
uint16 field,
uint24[4] memory colors,
uint16 hardware,
uint16 frame
) external view returns (string memory);
function isShieldBuilt(uint256 shieldId) external view returns (bool);
function getField(uint16 field, uint24[4] memory colors)
external
view
returns (IFieldGenerator.FieldData memory);
function getFieldTitle(uint16 field, uint24[4] memory colors)
external
view
returns (string memory);
function getFieldSVG(uint16 field, uint24[4] memory colors)
external
view
returns (string memory);
function getHardware(uint16 hardware)
external
view
returns (IHardwareGenerator.HardwareData memory);
function getHardwareTitle(uint16 hardware)
external
view
returns (string memory);
function getHardwareSVG(uint16 hardware)
external
view
returns (string memory);
function getFrame(uint16 frame)
external
view
returns (IFrameGenerator.FrameData memory);
function getFrameTitle(uint16 frame) external view returns (string memory);
function getFrameSVG(uint16 frame) external view returns (string memory);
}
文件 19 的 21:LibString.sol
pragma solidity >=0.8.0;
library LibString {
function toString(uint256 value) internal pure returns (string memory str) {
assembly {
let newFreeMemoryPointer := add(mload(0x40), 160)
mstore(0x40, newFreeMemoryPointer)
str := sub(newFreeMemoryPointer, 32)
mstore(str, 0)
let end := str
for { let temp := value } 1 { } {
str := sub(str, 1)
mstore8(str, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
str := sub(str, 32)
mstore(str, length)
}
}
}
文件 20 的 21:Owned.sol
pragma solidity >=0.8.0;
abstract contract Owned {
event OwnershipTransferred(address indexed user, address indexed newOwner);
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
文件 21 的 21: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), from)
mstore(add(freeMemoryPointer, 36), to)
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), to)
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), to)
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");
}
}
{
"compilationTarget": {
"src/Curta.sol": "Curta"
},
"evmVersion": "london",
"libraries": {
"src/utils/LibRLP.sol:LibRLP": "0xcf6ab024429c9709e5de85647fea31b078d71915"
},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 875
},
"remappings": [
":@/contracts/=src/",
":@/script/=script/",
":@/test/=test/",
":colormap-registry/=lib/colormap-registry/src/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":shields-api-contract/=lib/shields-api-contract/contracts/",
":shields-api/=lib/shields-api-contract/contracts/",
":solady/=lib/solady/src/",
":solmate/=lib/solmate/src/"
]
}
[{"inputs":[{"internalType":"contract AuthorshipToken","name":"_authorshipToken","type":"address"},{"internalType":"contract FlagRenderer","name":"_flagRenderer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"AuthorshipTokenAlreadyUsed","type":"error"},{"inputs":[],"name":"IncorrectSolution","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[{"internalType":"uint32","name":"_puzzleId","type":"uint32"}],"name":"PuzzleAlreadyFermat","type":"error"},{"inputs":[{"internalType":"uint32","name":"_puzzleId","type":"uint32"}],"name":"PuzzleAlreadySolved","type":"error"},{"inputs":[{"internalType":"uint32","name":"_puzzleId","type":"uint32"}],"name":"PuzzleDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint32","name":"_puzzleId","type":"uint32"}],"name":"PuzzleNotFermat","type":"error"},{"inputs":[{"internalType":"uint32","name":"_puzzleId","type":"uint32"}],"name":"PuzzleNotSolved","type":"error"},{"inputs":[{"internalType":"uint32","name":"_puzzleId","type":"uint32"}],"name":"SubmissionClosed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"id","type":"uint32"},{"indexed":true,"internalType":"address","name":"author","type":"address"},{"indexed":false,"internalType":"contract IPuzzle","name":"puzzle","type":"address"}],"name":"AddPuzzle","type":"event"},{"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":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"id","type":"uint32"},{"indexed":true,"internalType":"address","name":"solver","type":"address"},{"indexed":false,"internalType":"uint256","name":"solution","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"phase","type":"uint8"}],"name":"SolvePuzzle","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"id","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"colors","type":"uint256"}],"name":"UpdatePuzzleColors","type":"event"},{"inputs":[{"internalType":"contract IPuzzle","name":"_puzzle","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"addPuzzle","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":[],"name":"authorshipToken","outputs":[{"internalType":"contract AuthorshipToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fermat","outputs":[{"internalType":"uint32","name":"puzzleId","type":"uint32"},{"internalType":"uint40","name":"timeTaken","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flagRenderer","outputs":[{"internalType":"contract FlagRenderer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"getPuzzle","outputs":[{"internalType":"contract IPuzzle","name":"puzzle","type":"address"},{"internalType":"uint40","name":"addedTimestamp","type":"uint40"},{"internalType":"uint40","name":"firstSolveTimestamp","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"getPuzzleAuthor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"getPuzzleColorsAndSolves","outputs":[{"internalType":"uint120","name":"colors","type":"uint120"},{"internalType":"uint32","name":"phase0Solves","type":"uint32"},{"internalType":"uint32","name":"phase1Solves","type":"uint32"},{"internalType":"uint32","name":"phase2Solves","type":"uint32"},{"internalType":"uint32","name":"solves","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getTokenData","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint40","name":"solveTimestamp","type":"uint40"},{"internalType":"uint56","name":"solveMetadata","type":"uint56"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getUserBalances","outputs":[{"internalType":"uint32","name":"phase0Solves","type":"uint32"},{"internalType":"uint32","name":"phase1Solves","type":"uint32"},{"internalType":"uint32","name":"phase2Solves","type":"uint32"},{"internalType":"uint32","name":"solves","type":"uint32"},{"internalType":"uint32","name":"balance","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"hasSolvedPuzzle","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"hasUsedAuthorshipToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"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":"puzzleId","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","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":"uint32","name":"_puzzleId","type":"uint32"}],"name":"setFermat","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_puzzleId","type":"uint32"},{"internalType":"uint120","name":"_colors","type":"uint120"}],"name":"setPuzzleColors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_puzzleId","type":"uint32"},{"internalType":"uint256","name":"_solution","type":"uint256"}],"name":"solve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"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"}]