Skip to content

Instantly share code, notes, and snippets.

@ColinPlatt
Created January 19, 2022 13:36
Show Gist options
  • Save ColinPlatt/6d940a410996ffc8d0ca9a0e8062d6da to your computer and use it in GitHub Desktop.
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=
// 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