Created
January 19, 2022 13:36
-
-
Save ColinPlatt/6d940a410996ffc8d0ca9a0e8062d6da to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.10+commit.fc410830.js&optimize=false&runs=200&gist=
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: AGPL-3.0-only | |
pragma solidity 0.8.10; | |
import {ERC721, ERC721TokenReceiver} from "@rari-capital/solmate/src/tokens/ERC721.sol"; | |
contract Enso721 is ERC721, ERC721TokenReceiver { | |
// An ensō 円相 is a circle that is hand-drawn in one or two uninhibited brushstrokes to express a moment when the mind is free to let the body create. | |
// | |
// This NFT is created by consuming one NFT from another collection, and results in the NFT ID# attributed to the output | |
// Wabi-sabi (侘寂) is a world view centered on the acceptance of transience and imperfection. We use this as the token collection consumed to create an ensō token. | |
IERC721 wabiSabi; | |
uint256 ENSO_MAX_SUPPLY = 7_777; | |
mapping(uint256 => uint256) public EnsoToWS; | |
constructor(IERC721 _wabiSabi) ERC721("Enso", "ENSO") {wabiSabi = _wabiSabi;} | |
function tokenURI(uint256 id) override public view returns (string memory) { | |
string[10] memory svg; | |
svg[0] = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 220 220"><rect width="100%" height="100%" fill="red"/>'; | |
svg[1] = '<defs><path d="M60,120a50,50 0 1,0 100,0a50,50 0 1,0 -100,0" id="textcircle"/></defs>'; | |
svg[2] = '<text dy="40" textLength="310" font-family="sans-serif" font-weight="bold" fill="white"><animateTransform attributeName="transform" begin="0s" dur="20s" type="rotate" from="0 110 120" to="360 110 120" repeatCount="indefinite"/><textPath xlink:href="#textcircle"> -- Every act of creation is first an act of destruction --</textPath></text>'; | |
svg[3] = '<text x="10" y="15" font-family="sans-serif" font-size="10px" fill="000">ens\xc5\x8d: '; | |
svg[4] = Base64.toString(id); | |
svg[5] = '</text><text x="145" y="15" font-family="sans-serif" font-size="10px" fill="000">wabi-sabi: '; | |
svg[6] = Base64.toString(EnsoToWS[id]); | |
svg[7] = '</text><text x="50" y="140" font-size="60px" font-weight="bold" fill="000">'; | |
svg[8] = unicode"円相"; | |
svg[9] = '</text></svg>'; | |
string memory output = string(abi.encodePacked(svg[0], svg[1], svg[2], svg[3], svg[4], svg[5], svg[6], svg[7], svg[8], svg[9])); | |
string memory json = Base64.encode(bytes(string(abi.encodePacked('{"name": "Ens\xc5\x8d #', Base64.toString(id), '", "description": "The mind is free to let the body create.", "image": "data:image/svg+xml;base64,', Base64.encode(bytes(output)), '"}')))); | |
output = string(abi.encodePacked('data:application/json;base64,', json)); | |
return output; | |
} | |
function mint(uint256 id) external { | |
_mint(msg.sender, id); | |
} | |
function claim(uint256 _wsTokenId) public returns (uint256 _ensoTokenId) { | |
require(wabiSabi.ownerOf(_wsTokenId) == msg.sender, "NOT_OWNER"); | |
wabiSabi.safeTransferFrom(msg.sender, address(this), _wsTokenId, unicode"侘寂"); | |
_ensoTokenId = uint256( | |
keccak256( | |
abi.encodePacked( | |
_wsTokenId, | |
wabiSabi.tokenURI(_wsTokenId) | |
) | |
) | |
) % ENSO_MAX_SUPPLY; | |
EnsoToWS[_ensoTokenId] = _wsTokenId; | |
_mint(msg.sender, _ensoTokenId); | |
} | |
function onERC721Received( | |
address, | |
address, | |
uint256, | |
bytes calldata | |
) public virtual override returns (bytes4) { | |
return ERC721TokenReceiver.onERC721Received.selector; | |
} | |
} | |
interface IERC721 { | |
function ownerOf(uint256 tokenId) external view returns (address owner); | |
function safeTransferFrom( | |
address from, | |
address to, | |
uint256 tokenId, | |
bytes calldata data | |
) external; | |
function tokenURI(uint256 tokenId) external view returns (string memory); | |
} | |
library Base64 { | |
bytes internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
/// @notice Encodes some bytes to the base64 representation | |
function encode(bytes memory data) internal pure returns (string memory) { | |
uint256 len = data.length; | |
if (len == 0) return ""; | |
// multiply by 4/3 rounded up | |
uint256 encodedLen = 4 * ((len + 2) / 3); | |
// Add some extra buffer at the end | |
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); | |
} | |
function toString(uint256 value) internal pure returns (string memory) { | |
// Inspired by OraclizeAPI's implementation - MIT license | |
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol | |
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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment