Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.0.2 #25

Merged
merged 17 commits into from
Nov 6, 2023
Merged
80 changes: 37 additions & 43 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,55 +1,55 @@
AuthorshipTokenTest:test_curtaMint() (gas: 85648)
AuthorshipTokenTest:test_curtaMint_SenderIsNotCurta_RevertsUnauthorized(address) (runs: 256, μ: 10204, ~: 10204)
AuthorshipTokenTest:test_ownerMint_FuzzMintTimestamps_IssuesTokensCorrectly(uint256) (runs: 256, μ: 6541307, ~: 6006304)
AuthorshipTokenTest:test_ownerMint_FuzzMintTimestamps_IssuesTokensCorrectly(uint256) (runs: 256, μ: 6416430, ~: 6008044)
AuthorshipTokenTest:test_ownerMint_SenderIsNotOwner_RevertUnauthorized(address) (runs: 256, μ: 12935, ~: 12935)
AuthorshipTokenTest:test_ownerMint_SenderIsOwner_AllowsMint() (gas: 108202)
AuthorshipTokenTest:test_tokenURI_MintedToken_Succeeds() (gas: 232)
AuthorshipTokenTest:test_tokenURI_UnmintedToken_Fails() (gas: 12629)
CurtaTest:test_Initialization_DeployAddressesMatch() (gas: 11297)
CurtaTest:test_addPuzzle() (gas: 303991)
CurtaTest:test_addPuzzle_UseAuthorshipToken_UpdatesStorage() (gas: 297194)
CurtaTest:test_addPuzzle_UseSameAuthorshipTokenTwice_Fails() (gas: 298014)
CurtaTest:test_addPuzzle() (gas: 305332)
CurtaTest:test_addPuzzle_UseAuthorshipToken_UpdatesStorage() (gas: 298535)
CurtaTest:test_addPuzzle_UseSameAuthorshipTokenTwice_Fails() (gas: 299355)
CurtaTest:test_addPuzzle_UseUnownedAuthorshipToken_RevertsUnauthorized() (gas: 199252)
CurtaTest:test_approve() (gas: 433535)
CurtaTest:test_approve_SenderIsNotOwner_RevertsUnauthorized() (gas: 409039)
CurtaTest:test_approve_WithApprovalForAllTrue_AllowsTransfer() (gas: 455755)
CurtaTest:test_approve() (gas: 434876)
CurtaTest:test_approve_SenderIsNotOwner_RevertsUnauthorized() (gas: 410380)
CurtaTest:test_approve_WithApprovalForAllTrue_AllowsTransfer() (gas: 457096)
CurtaTest:test_balanceOf_ZeroAddress_Fails() (gas: 8674)
CurtaTest:test_setApprovalForAll_False_UpdatesStorage() (gas: 15966)
CurtaTest:test_setApprovalForAll_True_UpdatesStorage() (gas: 35841)
CurtaTest:test_setFermat_AsRandomAccount_Succeeds(address) (runs: 256, μ: 453796, ~: 453796)
CurtaTest:test_setFermat_InitialSet_UpdatesStorage() (gas: 510466)
CurtaTest:test_setFermat_SetAfterTransfer_Succeeds(address) (runs: 256, μ: 873913, ~: 873913)
CurtaTest:test_setFermat_SetDifferentPuzzlesTwiceInIncreasingOrder_Succeeds() (gas: 867472)
CurtaTest:test_setFermat_SetNonFermatPuzzle_Fails() (gas: 777525)
CurtaTest:test_setFermat_SetSamePuzzleTwice_Fails() (gas: 455572)
CurtaTest:test_setFermat_SetUnsolvedPuzzle_Fails() (gas: 298803)
CurtaTest:test_setPuzzleColors() (gas: 304888)
CurtaTest:test_setPuzzleColors_SetUnauthoredPuzzle_RevertsUnauthorized() (gas: 298166)
CurtaTest:test_solve() (gas: 656199)
CurtaTest:test_solve_DuringAllPhases_FirstSolveTimestampOnlySetOnFirstBlood(uint40) (runs: 256, μ: 519878, ~: 518934)
CurtaTest:test_solve_DuringPhase1WithPayment_PaysAuthor(uint256) (runs: 256, μ: 494376, ~: 495633)
CurtaTest:test_solve_DuringPhase2WithPayment_PaysAuthor(uint256) (runs: 256, μ: 532556, ~: 532556)
CurtaTest:test_solve_DuringPhase2_RequiresETH(uint256) (runs: 256, μ: 488012, ~: 486303)
CurtaTest:test_solve_DuringPhase3_Fails(uint40) (runs: 256, μ: 432757, ~: 432757)
CurtaTest:test_solve_FirstBlood_AuthorshipTokenMintPotentialRevertBranch() (gas: 309908)
CurtaTest:test_solve_FirstBlood_MintsAuthorshipToken() (gas: 412017)
CurtaTest:test_solve_FirstBlood_UpdatesFirstSolveTimestamp(uint40) (runs: 256, μ: 405999, ~: 405999)
CurtaTest:test_solve_IncorrectSolution_Fails(uint256) (runs: 256, μ: 307909, ~: 307909)
CurtaTest:test_setFermat_AsRandomAccount_Succeeds(address) (runs: 256, μ: 455137, ~: 455137)
CurtaTest:test_setFermat_InitialSet_UpdatesStorage() (gas: 511807)
CurtaTest:test_setFermat_SetAfterTransfer_Succeeds(address) (runs: 256, μ: 876595, ~: 876595)
CurtaTest:test_setFermat_SetDifferentPuzzlesTwiceInIncreasingOrder_Succeeds() (gas: 870154)
CurtaTest:test_setFermat_SetNonFermatPuzzle_Fails() (gas: 780207)
CurtaTest:test_setFermat_SetSamePuzzleTwice_Fails() (gas: 456913)
CurtaTest:test_setFermat_SetUnsolvedPuzzle_Fails() (gas: 300144)
CurtaTest:test_setPuzzleColors() (gas: 306229)
CurtaTest:test_setPuzzleColors_SetUnauthoredPuzzle_RevertsUnauthorized() (gas: 299507)
CurtaTest:test_solve() (gas: 657540)
CurtaTest:test_solve_DuringAllPhases_FirstSolveTimestampOnlySetOnFirstBlood(uint40) (runs: 256, μ: 520949, ~: 520275)
CurtaTest:test_solve_DuringPhase1WithPayment_PaysAuthor(uint256) (runs: 256, μ: 495665, ~: 496974)
CurtaTest:test_solve_DuringPhase2WithPayment_PaysAuthor(uint256) (runs: 256, μ: 533897, ~: 533897)
CurtaTest:test_solve_DuringPhase2_RequiresETH(uint256) (runs: 256, μ: 489380, ~: 487644)
CurtaTest:test_solve_DuringPhase3_Fails(uint40) (runs: 256, μ: 434098, ~: 434098)
CurtaTest:test_solve_FirstBlood_AuthorshipTokenMintPotentialRevertBranch() (gas: 311249)
CurtaTest:test_solve_FirstBlood_MintsAuthorshipToken() (gas: 413358)
CurtaTest:test_solve_FirstBlood_UpdatesFirstSolveTimestamp(uint40) (runs: 256, μ: 407340, ~: 407340)
CurtaTest:test_solve_IncorrectSolution_Fails(uint256) (runs: 256, μ: 309250, ~: 309250)
CurtaTest:test_solve_NonExistantPuzzle_Fails() (gas: 13650)
CurtaTest:test_solve_SamePuzzleTwice_Fails() (gas: 406056)
CurtaTest:test_solve_Success_MintsFlag() (gas: 407737)
CurtaTest:test_solve_Success_UpdatesSolveCounters() (gas: 612407)
CurtaTest:test_solve_Success_UpdatesStorage() (gas: 406659)
CurtaTest:test_solve_SamePuzzleTwice_Fails() (gas: 407397)
CurtaTest:test_solve_Success_MintsFlag() (gas: 409078)
CurtaTest:test_solve_Success_UpdatesSolveCounters() (gas: 613748)
CurtaTest:test_solve_Success_UpdatesStorage() (gas: 408000)
CurtaTest:test_supportsInterface() (gas: 8058)
CurtaTest:test_tokenURI_MintedToken_Succeeds() (gas: 234)
CurtaTest:test_tokenURI_UnmintedToken_Fails() (gas: 12952)
CurtaTest:test_transferFrom() (gas: 449268)
CurtaTest:test_transferFrom_SenderIsOwner_AllowsTransfer() (gas: 433241)
CurtaTest:test_transferFrom_ToZeroAddress_Fails() (gas: 406595)
CurtaTest:test_transferFrom_Unauthorized_RevertsUnauthorized() (gas: 411497)
CurtaTest:test_transferFrom_WithApprovalForAllTrue_AllowsTransfer() (gas: 459002)
CurtaTest:test_transferFrom_WithTokenApproval_AllowsTransfer() (gas: 439426)
CurtaTest:test_transferFrom_WrongFrom_Fails() (gas: 406517)
CurtaTest:test_transferFrom() (gas: 450609)
CurtaTest:test_transferFrom_SenderIsOwner_AllowsTransfer() (gas: 434582)
CurtaTest:test_transferFrom_ToZeroAddress_Fails() (gas: 407936)
CurtaTest:test_transferFrom_Unauthorized_RevertsUnauthorized() (gas: 412838)
CurtaTest:test_transferFrom_WithApprovalForAllTrue_AllowsTransfer() (gas: 460343)
CurtaTest:test_transferFrom_WithTokenApproval_AllowsTransfer() (gas: 440767)
CurtaTest:test_transferFrom_WrongFrom_Fails() (gas: 407858)
DeployBaseGoerliTest:test_AddressInitializationCorrectness() (gas: 23494)
DeployBaseGoerliTest:test_authorshipTokenAuthorsEquality() (gas: 13651)
DeployBaseGoerliTest:test_authorshipTokenIssueLengthEquality() (gas: 11460)
Expand All @@ -62,12 +62,6 @@ DeployBaseMainnetTest:test_authorshipTokenIssueLengthEquality() (gas: 11460)
DeployBaseMainnetTest:test_authorshipTokenMinting() (gas: 91601)
DeployBaseMainnetTest:test_authorshipTokenOwnerEquality() (gas: 13799)
DeployBaseMainnetTest:test_curtaOwnerEquality() (gas: 13832)
DeployConstellationTest:test_AddressInitializationCorrectness() (gas: 23494)
DeployConstellationTest:test_authorshipTokenAuthorsEquality() (gas: 20777)
DeployConstellationTest:test_authorshipTokenIssueLengthEquality() (gas: 11460)
DeployConstellationTest:test_authorshipTokenMinting() (gas: 91601)
DeployConstellationTest:test_authorshipTokenOwnerEquality() (gas: 13799)
DeployConstellationTest:test_curtaOwnerEquality() (gas: 13832)
DeployGoerliTest:test_AddressInitializationCorrectness() (gas: 23494)
DeployGoerliTest:test_authorshipTokenAuthorsEquality() (gas: 13651)
DeployGoerliTest:test_authorshipTokenIssueLengthEquality() (gas: 11460)
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ broadcast/

# Coverage
lcov.info

# SVG preview
script/preview
8 changes: 4 additions & 4 deletions script/PrintFlagToken.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ contract PrintFlagTokenScript is Script {
console.log(
flagRenderer.render({
_puzzleData: puzzleData,
_tokenId: (31 << 128) | 21_563,
_tokenId: (31 << 128) | 21,
_author: address(0),
_solveTime: uint40(49 days + 23 minutes + 17 seconds),
_solveMetadata: uint56((0xABCDEF0 << 28) | 0x12345),
_solveTime: uint40(49 days),
_solveMetadata: uint56((0xABCDEF0 << 28) | 0x1122334),
_phase: 0,
_solves: 256,
_colors: 0x181E28181E2827303DF0F6FC94A3B3
_colors: 0x181E287851A927303DF0F6FC94A3B3
})
);
}
Expand Down
66 changes: 66 additions & 0 deletions script/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const { exec } = require('child_process');
const fs = require('fs');
const path = require('path');

// Run the forge script
exec("forge script script/PrintFlagToken.s.sol:PrintFlagTokenScript --rpc-url http://localhost:8545", (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}

const regex = /data:application\/json;base64,[a-zA-Z0-9+/]+=*/g;
const base64Jsons = stdout.match(regex);
const matches = [];

base64Jsons?.forEach((base64Json, index) => {
const startIndex = base64Json.indexOf(',') + 1;
const encodedJson = base64Json.slice(startIndex);

try {
const decodedJson = atob(encodedJson);
const parsedJson = JSON.parse(decodedJson);

if (parsedJson.image_data !== undefined) {
matches.push(parsedJson.image_data);
}
} catch (e) {
console.error('Error in decoding/parsing JSON:', e);
}
});

// Specify the directory
const dir = path.join(__dirname, 'preview');

// Create the directory if it doesn't exist
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}

fs.readdir(dir, (err, files) => {
if (err) throw err;

for (let i = 0; i < matches.length; i++) {
const base64Data = matches[i].replace('data:image/svg+xml;base64,', '');

let fileName;
let counter = 0;
do {
counter++;
fileName = `${i + counter}.svg`;
} while (files.includes(fileName));

fs.writeFile(path.join(dir, fileName), Buffer.from(base64Data, 'base64'), { flag: 'w' }, function (err) {
if (err) {
console.log(err);
} else {
console.log(`saved ${fileName}`);
}
});
}
});
});
12 changes: 9 additions & 3 deletions src/Curta.sol
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ contract Curta is ICurta, FlagsERC721, Owned {
// Revert if the puzzle has already been used.
if (hasUsedAuthorshipToken[_tokenId]) revert AuthorshipTokenAlreadyUsed(_tokenId);

// Revert if puzzle address is the zero address or the Curta address
if (address(_puzzle) == address(0) || address(_puzzle) == address(this)) {
revert PuzzleInvalidAddress();
}

// Revert if puzzle name is an empty string or not implemented.
if (bytes(_puzzle.name()).length == 0) revert PuzzleNotNamed();

// Mark token as used.
hasUsedAuthorshipToken[_tokenId] = true;

Expand Down Expand Up @@ -301,9 +309,7 @@ contract Curta is ICurta, FlagsERC721, Owned {
// `tokenData.solveTimestamp == puzzleData.firstSolveTimestamp + SUBMISSION_LENGTH`
uint8 phase = tokenData.solveTimestamp == puzzleData.firstSolveTimestamp
? 0
: tokenData.solveTimestamp < puzzleData.firstSolveTimestamp + PHASE_ONE_LENGTH
? 1
: 2;
: tokenData.solveTimestamp < puzzleData.firstSolveTimestamp + PHASE_ONE_LENGTH ? 1 : 2;

return flagRenderer.render({
_puzzleData: puzzleData,
Expand Down
54 changes: 29 additions & 25 deletions src/FlagRenderer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ contract FlagRenderer {
// -------------------------------------------------------------------------

/// @notice The colormap registry.
/// @dev v0.0.2
IColormapRegistry constant colormapRegistry =
IColormapRegistry(0x0000000012883D1da628e31c0FE52e35DcF95D50);
IColormapRegistry(0x00000000A84FcdF3E9C165e6955945E87dA2cB0D);

/// @notice Render the JSON and SVG for a Flag token.
/// @param _puzzleData The puzzle data.
Expand Down Expand Up @@ -56,7 +57,7 @@ contract FlagRenderer {
'"},{"trait_type":"Phase","value":"',
uint256(_phase).toString(),
'"},{"trait_type":"Solver","value":"',
_formatValueAsAddress(uint256(_solveMetadata & 0xFFFFFFF)),
_formatValueAsAddress(uint256(_solveMetadata >> 28)),
'"},{"trait_type":"Solve time","value":',
uint256(_solveTime).toString(),
'},{"trait_type":"Rank","value":',
Expand Down Expand Up @@ -197,12 +198,12 @@ contract FlagRenderer {
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:#',
'70370" fill="#FFF"/></mask><path d="M0 0h550v550H0z" fill="#',
uint256((_colors >> 96) & 0xFFFFFF).toHexStringNoPrefix(3), // Background
'"/><rect x="143" y="69" width="264" height="412" rx="8" fill="#',
uint256((_colors >> 48) & 0xFFFFFF).toHexStringNoPrefix(3), // Border
'"/><rect class="f" x="147" y="73" width="256" height="404" rx="4"/>',
_drawStars(_phase)
_drawStars(_phase, _colors)
);
}
{
Expand Down Expand Up @@ -295,25 +296,25 @@ contract FlagRenderer {
{
uint256 seed = uint256(keccak256(abi.encodePacked(_tokenId, _solveMetadata)));
// Select the colormap.
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)
bytes8 colormapHash = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it better if we packed 4 of these per bytes32? I guess it's unnecessary lmao

bytes8(0xfd29b65966772202),
bytes8(0x850ce48e7291439b),
bytes8(0x4f5e8ea8862eff31),
bytes8(0xf2e92189cb6903b9),
bytes8(0xa33e6c7c5627ecab),
bytes8(0xaa84b30df806b46f),
bytes8(0x864a6ee98b9b21ac),
bytes8(0xfd60cd3811f00281),
bytes8(0xa8309447f8bd3b5e),
bytes8(0x3be719b0c3427972),
bytes8(0xca0da6b6309ed211),
bytes8(0x5ccb29670bb9de0e),
bytes8(0x3de8f27f386dab3d),
bytes8(0x026736ef8439ebcf),
bytes8(0xc1806ea961848ac0),
bytes8(0x87970b686eb72675),
bytes8(0xaa6277ab923279cf),
bytes8(0xdc1cecffc00e2f31)
][seed % 18];

// We start at the middle of the board.
Expand Down Expand Up @@ -375,8 +376,9 @@ contract FlagRenderer {
/// that the SVGs are returned positioned relative to the whole SVG for the
/// Flag.
/// @param _phase The phase of the solve.
/// @param _colors The colors of the Flag.
/// @return string memory The SVG for the stars.
function _drawStars(uint8 _phase) internal pure returns (string memory) {
function _drawStars(uint8 _phase, uint128 _colors) internal pure returns (string memory) {
// This will never underflow because `_phase` is always in the range
// [0, 4].
unchecked {
Expand All @@ -387,7 +389,9 @@ contract FlagRenderer {
(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 '
'" height="24" rx="12"/><path id="s" fill="#',
uint256((_colors >> 72) & 0xFFFFFF).toHexStringNoPrefix(3),
'" 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"/>',
Expand Down
Loading
Loading