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
Creation tx
0x27f7d483ba…9215f66255

Token

Name
Onchain Rats
Symbol
ORATS
Decimals
Holders
142

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