OnchainRats
0xf0bd7b5858948f01f53ed4c69bd36188af92e737
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
7,175 bytes
ABI entries
67
21 read · 17 write
License
none
Contract information
- Address
- 0xf0bd7b5858948f01f53ed4c69bd36188af92e737
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x676C5A2048…fB41ae0B53
- Creation tx
- 0x27f7d483ba…9215f66255
Token
- Name
- Onchain Rats
- Symbol
- ORATS
- Decimals
- —
- Holders
- 142
- Market
- View token page →
Read contract (21)
balanceOf(address) → uint256
feeRecipient() → address
getApproved(uint256) → address
isApprovedForAll(address, address) → bool
maxPerWallet() → uint256
maxSupply() → uint256
mintPrice() → uint256
mintedBy(address) → uint256
name() → string
owner() → address
ownerOf(uint256) → address
ownershipHandoverExpiresAt(address) → uint256
renderer() → address
royaltyBps() → uint96
royaltyInfo(uint256, uint256) → address, uint256
royaltyReceiver() → address
saleActive() → bool
supportsInterface(bytes4) → bool
symbol() → string
tokenURI(uint256) → string
totalMinted() → uint256
Events (8)
ApprovalApprovalForAllMintedOwnershipHandoverCanceledOwnershipHandoverRequestedOwnershipTransferredSaleToggledTransfer
ABI
[
{
"inputs": [
{
"internalType": "uint256",
"name": "_maxSupply",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_maxPerWallet",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_mintPrice",
"type": "uint256"
},
{
"internalType": "address",
"name": "_renderer",
"type": "address"
},
{
"internalType": "address",
"name": "_feeRecipient",
"type": "address"
},
{
"internalType": "address",
"name": "_owner",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AccountBalanceOverflow",
"type": "error"
},
{
"inputs": [],
"name": "AlreadyInitialized",
"type": "error"
},
{
"inputs": [],
"name": "BadRoyalty",
"type": "error"
},
{
"inputs": [],
"name": "BalanceQueryForZeroAddress",
"type": "error"
},
{
"inputs": [],
"name": "NewOwnerIsZeroAddress",
"type": "error"
},
{
"inputs": [],
"name": "NoHandoverRequest",
"type": "error"
},
{
"inputs": [],
"name": "NotOwnerNorApproved",
"type": "error"
},
{
"inputs": [],
"name": "Reentrancy",
"type": "error"
},
{
"inputs": [],
"name": "SaleInactive",
"type": "error"
},
{
"inputs": [],
"name": "SoldOut",
"type": "error"
},
{
"inputs": [],
"name": "TokenAlreadyExists",
"type": "error"
},
{
"inputs": [],
"name": "TokenDoesNotExist",
"type": "error"
},
{
"inputs": [],
"name": "TransferFromIncorrectOwner",
"type": "error"
},
{
"inputs": [],
"name": "TransferToNonERC721ReceiverImplementer",
"type": "error"
},
{
"inputs": [],
"name": "TransferToZeroAddress",
"type": "error"
},
{
"inputs": [],
"name": "Unauthorized",
"type": "error"
},
{
"inputs": [],
"name": "WalletCap",
"type": "error"
},
{
"inputs": [],
"name": "WrongValue",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAddress",
"type": "error"
},
{
"inputs": [],
"name": "ZeroMint",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "account",
"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": "isApproved",
"type": "bool"
}
],
"name": "ApprovalForAll",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "Minted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "pendingOwner",
"type": "address"
}
],
"name": "OwnershipHandoverCanceled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "pendingOwner",
"type": "address"
}
],
"name": "OwnershipHandoverRequested",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "oldOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bool",
"name": "active",
"type": "bool"
}
],
"name": "SaleToggled",
"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"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "quantity",
"type": "uint256"
}
],
"name": "OnchainMint",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
}
],
"name": "approve",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "result",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "cancelOwnershipHandover",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "pendingOwner",
"type": "address"
}
],
"name": "completeOwnershipHandover",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "feeRecipient",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
}
],
"name": "getApproved",
"outputs": [
{
"internalType": "address",
"name": "result",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "operator",
"type": "address"
}
],
"name": "isApprovedForAll",
"outputs": [
{
"internalType": "bool",
"name": "result",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maxPerWallet",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maxSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "mintPrice",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "mintedBy",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "result",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "quantity",
"type": "uint256"
}
],
"name": "ownerMint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
}
],
"name": "ownerOf",
"outputs": [
{
"internalType": "address",
"name": "result",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "pendingOwner",
"type": "address"
}
],
"name": "ownershipHandoverExpiresAt",
"outputs": [
{
"internalType": "uint256",
"name": "result",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renderer",
"outputs": [
{
"internalType": "contract IRatRenderer",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "requestOwnershipHandover",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "royaltyBps",
"outputs": [
{
"internalType": "uint96",
"name": "",
"type": "uint96"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "salePrice",
"type": "uint256"
}
],
"name": "royaltyInfo",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "royaltyReceiver",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"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": "payable",
"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": "payable",
"type": "function"
},
{
"inputs": [],
"name": "saleActive",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "operator",
"type": "address"
},
{
"internalType": "bool",
"name": "isApproved",
"type": "bool"
}
],
"name": "setApprovalForAll",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "r",
"type": "address"
}
],
"name": "setFeeRecipient",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "price",
"type": "uint256"
}
],
"name": "setMintPrice",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "r",
"type": "address"
}
],
"name": "setRenderer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "uint96",
"name": "bps",
"type": "uint96"
}
],
"name": "setRoyalty",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "active",
"type": "bool"
}
],
"name": "setSaleActive",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes4",
"name": "id",
"type": "bytes4"
}
],
"name": "supportsInterface",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
}
],
"name": "tokenURI",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalMinted",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"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": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {ERC721} from "solady/tokens/ERC721.sol";
import {Ownable} from "solady/auth/Ownable.sol";
import {ReentrancyGuard} from "solady/utils/ReentrancyGuard.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {LibString} from "solady/utils/LibString.sol";
import {Base64} from "solady/utils/Base64.sol";
interface IRatRenderer {
function tokenURI(uint256 id) external pure returns (string memory);
}
contract OnchainRats is ERC721, Ownable, ReentrancyGuard {
using SafeTransferLib for address;
uint256 public immutable maxSupply;
uint256 public immutable maxPerWallet;
uint256 public mintPrice;
uint256 public totalMinted;
bool public saleActive;
mapping(address => uint256) public mintedBy;
IRatRenderer public renderer;
address public feeRecipient;
uint96 public royaltyBps;
address public royaltyReceiver;
event Minted(address indexed to, uint256 indexed tokenId);
event SaleToggled(bool active);
error SaleInactive();
error SoldOut();
error WrongValue();
error WalletCap();
error ZeroMint();
error BadRoyalty();
error ZeroAddress();
constructor(
uint256 _maxSupply,
uint256 _maxPerWallet,
uint256 _mintPrice,
address _renderer,
address _feeRecipient,
address _owner
) {
if (_feeRecipient == address(0)) revert ZeroAddress();
maxSupply = _maxSupply;
maxPerWallet = _maxPerWallet;
mintPrice = _mintPrice;
renderer = IRatRenderer(_renderer);
feeRecipient = _feeRecipient;
royaltyReceiver = _owner;
royaltyBps = 500;
_initializeOwner(_owner);
}
function name() public pure override returns (string memory) { return "Onchain Rats"; }
function symbol() public pure override returns (string memory) { return "ORATS"; }
function OnchainMint(uint256 quantity) external payable nonReentrant {
if (!saleActive) revert SaleInactive();
if (quantity == 0) revert ZeroMint();
if (totalMinted + quantity > maxSupply) revert SoldOut();
if (mintedBy[msg.sender] + quantity > maxPerWallet) revert WalletCap();
if (msg.value != mintPrice * quantity) revert WrongValue();
mintedBy[msg.sender] += quantity;
for (uint256 i = 0; i < quantity; i++) {
uint256 tokenId = ++totalMinted;
_mint(msg.sender, tokenId);
emit Minted(msg.sender, tokenId);
}
if (msg.value != 0) feeRecipient.safeTransferETH(msg.value);
}
function ownerMint(address to, uint256 quantity) external onlyOwner {
if (quantity == 0) revert ZeroMint();
if (totalMinted + quantity > maxSupply) revert SoldOut();
for (uint256 i = 0; i < quantity; i++) {
uint256 tokenId = ++totalMinted;
_mint(to, tokenId);
emit Minted(to, tokenId);
}
}
function setSaleActive(bool active) external onlyOwner { saleActive = active; emit SaleToggled(active); }
function setMintPrice(uint256 price) external onlyOwner { mintPrice = price; }
function setRenderer(address r) external onlyOwner { renderer = IRatRenderer(r); }
function setFeeRecipient(address r) external onlyOwner {
if (r == address(0)) revert ZeroAddress();
feeRecipient = r;
}
function setRoyalty(address receiver, uint96 bps) external onlyOwner {
if (bps > 10_000) revert BadRoyalty();
royaltyReceiver = receiver; royaltyBps = bps;
}
function royaltyInfo(uint256, uint256 salePrice) external view returns (address, uint256) {
return (royaltyReceiver, (salePrice * royaltyBps) / 10_000);
}
function supportsInterface(bytes4 id) public view override returns (bool) {
return id == 0x2a55205a || super.supportsInterface(id);
}
function tokenURI(uint256 id) public view override returns (string memory) {
if (id == 0 || id > totalMinted) revert TokenDoesNotExist();
return renderer.tokenURI(id);
}
}
struct Traits { uint8 bg; uint8 motif; uint8 fur; uint8 eyes; uint8 hw; uint8 mouth; uint8 collar; }
contract RatRenderer {
using LibString for uint256;
uint256 internal constant G = 24;
uint256 internal constant M = 4;
uint24 constant LIME=0xB8CC58; uint24 constant LIME_DK=0x859A3F;
uint24 constant RH_GREEN=0x2F9952; uint24 constant GREEN_DK=0x1D6C3D;
uint24 constant BLACK_BG=0x0C0C0D; uint24 constant MIDNIGHT=0x08281C;
uint24 constant WHITE_BG=0xF4F4F0; uint24 constant INK=0x0F0F11;
uint24 constant WHITE=0xF7F7F4; uint24 constant SOFT_EDGE=0xDCE0D3;
uint24 constant PINK=0xE28997; uint24 constant GOLD=0xDAB54B; uint24 constant RED=0xD84747;
uint32 constant SET = 0x1000000;
function tokenURI(uint256 id) external pure returns (string memory) {
uint256 seed = uint256(keccak256(abi.encodePacked(id)));
Traits memory t = traits(seed);
string memory image = svg(seed, t);
string memory json = string(abi.encodePacked(
'{"name":"Onchain Rats #', id.toString(),
'","description":"Onchain Rats - a fully on-chain pixel rat colony in Robinhood colors. The little guys moving the markets.",',
'"attributes":[',
_attr("Background", bgName(t.bg)), ',',
_attr("Motif", motifName(t.motif)), ',',
_attr("Fur", furName(t.fur)), ',',
_attr("Eyes", eyeName(t.eyes)), ',',
_attr("Headwear", hwName(t.hw)), ',',
_attr("Mouth", mouthName(t.mouth)), ',',
_attr("Collar", collarName(t.collar)),
'],"image":"data:image/svg+xml;base64,', Base64.encode(bytes(image)), '"}'
));
return string(abi.encodePacked("data:application/json;base64,", Base64.encode(bytes(json))));
}
function _attr(string memory k, string memory v) internal pure returns (string memory) {
return string(abi.encodePacked('{"trait_type":"', k, '","value":"', v, '"}'));
}
function traits(uint256 seed) public pure returns (Traits memory t) {
t.bg = uint8(_pick(seed, 1, _arr5([uint16(35),25,20,12,8])));
t.motif = uint8(_pick(seed, 2, _arr5([uint16(45),20,15,12,8])));
t.fur = uint8(_pick(seed, 3, _arr5([uint16(30),25,18,15,12])));
t.eyes = uint8(_pick(seed, 4, _arr7([uint16(32),16,12,10,10,9,7])));
t.hw = uint8(_pick(seed, 5, _arr6([uint16(38),22,12,10,8,10])));
t.mouth = uint8(_pick(seed, 6, _arr3([uint16(48),30,22])));
t.collar = uint8(_pick(seed, 7, _arr3([uint16(68),20,12])));
}
function _pick(uint256 seed, uint256 salt, uint16[] memory w) internal pure returns (uint256) {
uint256 total; for (uint256 i; i < w.length; i++) total += w[i];
uint256 r = uint256(keccak256(abi.encodePacked(seed, salt))) % total;
uint256 acc;
for (uint256 i; i < w.length; i++) { acc += w[i]; if (r < acc) return i; }
return w.length - 1;
}
function _arr3(uint16[3] memory a) internal pure returns (uint16[] memory o){o=new uint16[](3);for(uint256 i;i<3;i++)o[i]=a[i];}
function _arr5(uint16[5] memory a) internal pure returns (uint16[] memory o){o=new uint16[](5);for(uint256 i;i<5;i++)o[i]=a[i];}
function _arr6(uint16[6] memory a) internal pure returns (uint16[] memory o){o=new uint16[](6);for(uint256 i;i<6;i++)o[i]=a[i];}
function _arr7(uint16[7] memory a) internal pure returns (uint16[] memory o){o=new uint16[](7);for(uint256 i;i<7;i++)o[i]=a[i];}
function bgName(uint8 i) internal pure returns (string memory){ string[5] memory a=["Robin Neon","Midnight","Cash Green","Clean White","Forest"]; return a[i]; }
function motifName(uint8 i) internal pure returns (string memory){ string[5] memory a=["None","Up Only","Candlesticks","Confetti","Feather Mark"]; return a[i]; }
function furName(uint8 i) internal pure returns (string memory){ string[5] memory a=["Void Black","Snow","Neon Lime","Cash Green","Ash Gray"]; return a[i]; }
function eyeName(uint8 i) internal pure returns (string memory){ string[7] memory a=["Beady","Wide","Sleepy","Wink","Visor","Dollar","Laser"]; return a[i]; }
function hwName(uint8 i) internal pure returns (string memory){ string[6] memory a=["None","Feather","Halo","Snapback","Top Hat","Headphones"]; return a[i]; }
function mouthName(uint8 i) internal pure returns (string memory){ string[3] memory a=["Smile","Neutral","Smirk"]; return a[i]; }
function collarName(uint8 i) internal pure returns (string memory){ string[3] memory a=["None","Neon Collar","Gold Chain"]; return a[i]; }
function bgColor(uint8 i) internal pure returns (uint24){ if(i==0)return LIME; if(i==1)return BLACK_BG; if(i==2)return RH_GREEN; if(i==3)return WHITE_BG; return MIDNIGHT; }
function furTones(uint8 i) internal pure returns (uint24 light, uint24 shadow){
if(i==0)return(0x2A2A2E,0x161618); if(i==1)return(0xF4F4F0,0xCACDCA);
if(i==2)return(LIME,LIME_DK); if(i==3)return(RH_GREEN,GREEN_DK); return(0x969BA0,0x686D75);
}
function _p(uint32[] memory b, int256 x, int256 y, uint24 c) internal pure {
if (x>=0 && x<int256(G) && y>=0 && y<int256(G)) b[uint256(y)*G+uint256(x)] = SET | uint32(c);
}
function _sum(uint24 c) internal pure returns (uint256){ return uint256(uint8(c>>16))+uint8(c>>8)+uint8(c); }
function _luma(uint24 c) internal pure returns (uint256){ return 2126*uint256(uint8(c>>16))+7152*uint256(uint8(c>>8))+722*uint256(uint8(c)); }
function _tint(uint24 c, int256 d) internal pure returns (uint24){
int256 r=int256(uint256(uint8(c>>16)))+d; int256 g=int256(uint256(uint8(c>>8)))+d; int256 bl=int256(uint256(uint8(c)))+d;
if(r<0)r=0; if(r>255)r=255; if(g<0)g=0; if(g>255)g=255; if(bl<0)bl=0; if(bl>255)bl=255;
return uint24((uint256(r)<<16)|(uint256(g)<<8)|uint256(bl));
}
function _hex(uint24 c) internal pure returns (string memory){
bytes memory h="0123456789abcdef"; bytes memory o=new bytes(7); o[0]="#";
for (uint256 i; i<6; i++) o[6-i]=h[(uint256(c)>>(4*i))&0xF];
return string(o);
}
function svg(uint256 seed, Traits memory t) public pure returns (string memory) {
uint32[] memory b = new uint32[](G*G);
uint24 bg = bgColor(t.bg);
_motif(b, seed, t.motif, bg);
_rat(b, t, bg);
string memory rects = "";
for (uint256 y; y < G; y++) {
uint256 x;
while (x < G) {
uint32 cell = b[y*G+x];
if (cell == 0) { x++; continue; }
uint256 run = 1;
while (x+run < G && b[y*G+x+run] == cell) run++;
rects = string(abi.encodePacked(
rects, '<rect x="', (x+M).toString(), '" y="', (y+M).toString(),
'" width="', run.toString(), '" height="1" fill="', _hex(uint24(cell)), '"/>'
));
x += run;
}
}
return string(abi.encodePacked(
'<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 32 32" shape-rendering="crispEdges">',
'<rect width="32" height="32" fill="', _hex(bg), '"/>', rects, '</svg>'
));
}
function _motif(uint32[] memory b, uint256 seed, uint8 m, uint24 bg) internal pure {
if (m == 1) {
uint24 c = _sum(bg) < 380 ? _tint(bg, 28) : _tint(bg, -26);
for (int256 i; i < 22; i++){ int256 x=1+i; int256 y=22-i; _p(b,x,y,c); _p(b,x,y-1,c); }
for (int256 k; k < 5; k++){ _p(b,22-k,1+k,c); _p(b,22,1+k,c); _p(b,22-k,1,c); }
} else if (m == 2) {
_candle(b,2,14,20,true); _candle(b,4,10,21,false); _candle(b,20,12,18,true); _candle(b,22,9,19,false);
} else if (m == 3) {
uint24[4] memory pal=[LIME,RH_GREEN,WHITE,GOLD];
for (uint256 k; k<26; k++){ uint256 r=uint256(keccak256(abi.encodePacked(seed, uint256(1000+k))));
_p(b, int256(r%24), int256((r>>8)%24), pal[(r>>16)%4]); }
} else if (m == 4) {
uint24 c = _sum(bg) < 380 ? _tint(bg, 24) : _tint(bg, -22);
int8[2][8] memory seg=[[int8(2),4],[int8(2),5],[int8(3),6],[int8(3),7],[int8(4),8],[int8(5),9],[int8(6),9],[int8(7),10]];
for (uint256 i; i<8; i++){ int256 a=seg[i][0]; int256 bb=seg[i][1]; int256 yy=21-int256(i);
_p(b,a,yy,c); _p(b,bb,yy,c);
for (int256 x=a; x<=bb; x++) if ((uint256(x)+i)%2==0) _p(b,x,yy,c); }
}
}
function _candle(uint32[] memory b, int256 cx, int256 lo, int256 hi, bool up) internal pure {
uint24 col = up ? RH_GREEN : RED;
_p(b,cx,lo-2,col); _p(b,cx,hi+2,col);
for (int256 y=lo; y<=hi; y++){ _p(b, cx<12?cx-1:cx, y, col); _p(b,cx,y,col); }
}
function _rat(uint32[] memory b, Traits memory t, uint24 bg) internal pure {
(uint24 light, uint24 shadow) = furTones(t.fur);
uint24 outline = _luma(bg) >= 85000 ? INK : SOFT_EDGE;
uint24 PINK_DK = _tint(PINK, -45);
int8[2][10] memory TAIL=[[int8(14),20],[int8(15),21],[int8(16),21],[int8(17),22],[int8(18),22],[int8(19),22],[int8(20),21],[int8(20),20],[int8(19),19],[int8(18),19]];
for (uint256 i; i<10; i++){ int256 x=TAIL[i][0]; int256 y=TAIL[i][1]; _p(b,x,y,PINK); _p(b,x,y+1,PINK_DK); }
bool[] memory sil = new bool[](G*G);
_head(sil); _disc(sil,6,5,1156); _disc(sil,17,5,1156);
for (uint256 i; i<G*G; i++) if (sil[i]){ int256 x=int256(i%G); _p(b,x,int256(i/G), x>=15?shadow:light); }
bool[] memory inner = new bool[](G*G);
_disc(inner,6,5,324); _disc(inner,17,5,324);
for (uint256 i; i<G*G; i++) if (inner[i]) _p(b,int256(i%G),int256(i/G),PINK);
int8[2][8] memory NB=[[int8(1),0],[int8(-1),0],[int8(0),1],[int8(0),-1],[int8(1),1],[int8(1),-1],[int8(-1),1],[int8(-1),-1]];
for (uint256 i; i<G*G; i++) if (sil[i]){
int256 x=int256(i%G); int256 y=int256(i/G);
for (uint256 k; k<8; k++){ int256 nx=x+NB[k][0]; int256 ny=y+NB[k][1];
if (nx>=0&&nx<int256(G)&&ny>=0&&ny<int256(G) && !sil[uint256(ny)*G+uint256(nx)]) _p(b,nx,ny,outline); }
}
_eyes(b, t.eyes);
_face(b, t.mouth);
for (uint256 i; i<3; i++){ int256 wx=int256(2+i); _p(b,wx,16,outline); _p(b,23-wx,16,outline); }
_p(b,4,15,outline); _p(b,19,15,outline);
_collar(b, t.collar);
_headwear(b, t.hw, bg);
}
function _head(bool[] memory s) internal pure {
uint8[2][13] memory H=[[uint8(7),16],[uint8(6),17],[uint8(5),18],[uint8(5),18],[uint8(5),18],[uint8(5),18],[uint8(5),18],[uint8(5),18],[uint8(5),18],[uint8(5),18],[uint8(6),17],[uint8(8),15],[uint8(10),13]];
for (uint256 r; r<13; r++){ uint256 y=8+r; for (uint256 x=H[r][0]; x<=H[r][1]; x++) s[y*G+x]=true; }
}
function _disc(bool[] memory s, int256 cx, int256 cy, int256 rr100) internal pure {
for (int256 x=cx-4; x<=cx+4; x++) for (int256 y=cy-4; y<=cy+4; y++){
if (x<0||x>=int256(G)||y<0||y>=int256(G)) continue;
if (((x-cx)*(x-cx)+(y-cy)*(y-cy))*100 <= rr100) s[uint256(y)*G+uint256(x)]=true;
}
}
function _eyes(uint32[] memory b, uint8 e) internal pure {
int256 LX=8; int256 RX=15; int256 EY=13;
if (e==0){
_p(b,LX,EY,INK); _p(b,LX,EY+1,INK); _p(b,LX,EY,WHITE);
_p(b,RX,EY,INK); _p(b,RX,EY+1,INK); _p(b,RX,EY,WHITE);
} else if (e==1){
int256[2] memory C=[LX,RX];
for (uint256 j;j<2;j++){ int256 cx=C[j];
for (int256 yy=EY; yy<EY+2; yy++) for (int256 xx=cx-1; xx<cx+1; xx++) _p(b,xx,yy,WHITE);
_p(b,cx,EY+1,INK); _p(b,cx-1,EY+1,INK); _p(b,cx-1,EY,WHITE); }
} else if (e==2){
int256[2] memory C=[LX,RX];
for (uint256 j;j<2;j++){ int256 cx=C[j]; _p(b,cx-1,EY+1,INK); _p(b,cx,EY+1,INK); _p(b,cx+1,EY+1,INK); }
} else if (e==3){
_p(b,LX,EY,INK); _p(b,LX,EY+1,INK); _p(b,RX-1,EY+1,INK); _p(b,RX,EY+1,INK); _p(b,RX+1,EY+1,INK);
} else if (e==4){
for (int256 xx=LX-2; xx<RX+2; xx++){ _p(b,xx,EY,INK); _p(b,xx,EY+1,RH_GREEN); }
_p(b,LX,EY,RH_GREEN); _p(b,RX,EY,RH_GREEN);
} else if (e==5){
int256[2] memory C=[LX,RX];
for (uint256 j;j<2;j++){ int256 cx=C[j];
for (int256 yy=EY-1; yy<EY+3; yy++) for (int256 xx=cx-1; xx<cx+1; xx++) _p(b,xx,yy,WHITE);
_p(b,cx,EY-1,RH_GREEN); _p(b,cx-1,EY,RH_GREEN); _p(b,cx,EY+1,RH_GREEN);
_p(b,cx,EY+2,RH_GREEN); _p(b,cx-1,EY-1,RH_GREEN); }
} else {
uint24 hot=0xFFD4D4; uint24 rdk=_tint(RED,-55);
int256[2] memory C=[LX,RX];
for (uint256 j;j<2;j++){ int256 cx=C[j];
_p(b,cx-1,EY,rdk); _p(b,cx+1,EY,rdk); _p(b,cx,EY-1,rdk); _p(b,cx,EY+1,RED); _p(b,cx,EY,hot); }
_p(b,LX-1,EY,RED); _p(b,LX-2,EY,_tint(RED,25)); _p(b,RX+1,EY,RED); _p(b,RX+2,EY,_tint(RED,25));
}
}
function _face(uint32[] memory b, uint8 mo) internal pure {
_p(b,11,15,PINK); _p(b,12,15,PINK); _p(b,11,16,PINK); _p(b,12,16,PINK); _p(b,11,17,PINK);
_p(b,12,17,_tint(PINK,-35));
if (mo==0){ _p(b,9,17,INK); _p(b,10,18,INK); _p(b,11,18,INK); _p(b,12,18,INK); _p(b,13,18,INK); _p(b,14,17,INK); }
else if (mo==1){ _p(b,10,18,INK); _p(b,11,18,INK); _p(b,12,18,INK); _p(b,13,18,INK); }
else { _p(b,11,18,INK); _p(b,12,18,INK); _p(b,13,18,INK); _p(b,14,17,INK); }
_p(b,11,19,WHITE); _p(b,12,19,WHITE); _p(b,11,20,WHITE); _p(b,12,20,WHITE);
_p(b,10,19,INK); _p(b,13,19,INK); _p(b,11,21,INK); _p(b,12,21,INK);
}
function _collar(uint32[] memory b, uint8 col) internal pure {
if (col==0) return;
uint24 cc = col==1 ? LIME : GOLD;
for (int256 x=9; x<15; x++) _p(b,x,21,cc);
_p(b,11,22,cc); _p(b,12,22,cc); _p(b,11,23, col==2 ? GOLD : cc);
}
function _headwear(uint32[] memory b, uint8 hw, uint24 bg) internal pure {
if (hw==1){
uint24 body = _luma(bg) >= 85000 ? INK : SOFT_EDGE;
uint24 vein = body==INK ? LIME_DK : INK;
string[11] memory rows=["..XX..",".XXXX.","XXXXXX","XXXXXX","XXXXXX","XXXXXX",".XXXX.",".XXXX.","..XXX.","..XX..","..X..."];
int256 ox=17;
for (uint256 ry; ry<11; ry++){ bytes memory row=bytes(rows[ry]);
for (uint256 rx; rx<6; rx++) if (row[rx]=="X") _p(b, ox+int256(rx), int256(ry), body); }
for (int256 ry=2; ry<9; ry++) _p(b, ox+2, ry, vein);
_p(b,ox+1,4,vein); _p(b,ox+3,4,vein); _p(b,ox+1,5,vein); _p(b,ox+3,5,vein);
} else if (hw==2){
for (int256 x=8; x<16; x++) _p(b,x,1,LIME);
_p(b,7,2,LIME); _p(b,16,2,LIME); _p(b,8,2,LIME); _p(b,15,2,LIME);
} else if (hw==3){
for (int256 x=7; x<17; x++) _p(b,x,8,RH_GREEN);
for (int256 x=6; x<18; x++) _p(b,x,9,RH_GREEN);
for (int256 x=15; x<22; x++) _p(b,x,10,GREEN_DK);
_p(b,11,8,LIME); _p(b,12,8,LIME);
} else if (hw==4){
for (int256 y=0; y<5; y++) for (int256 x=9; x<15; x++) _p(b,x,y,INK);
for (int256 x=7; x<17; x++) _p(b,x,5,INK);
for (int256 x=9; x<15; x++) _p(b,x,3,LIME);
} else if (hw==5){
for (int256 x=7; x<17; x++) _p(b,x,6,INK);
for (int256 x=8; x<16; x++) _p(b,x,5,INK);
int256[4] memory ys=[int256(9),10,11,12];
for (uint256 k;k<4;k++){ int256 y=ys[k]; _p(b,4,y,RH_GREEN); _p(b,5,y,RH_GREEN); _p(b,18,y,RH_GREEN); _p(b,19,y,RH_GREEN); }
}
}
}
Chain explorer5381msChain node82ms