{
 "language": "Solidity",
 "sources": {
  "LPCGMemes.sol": {
   "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\ninterface IERC721 { function balanceOf(address) external view returns (uint256); }\ninterface IERC1155Receiver { function onERC1155Received(address,address,uint256,uint256,bytes calldata) external returns (bytes4); }\n\n/// @title LPCG MEMES (LPCGMEME) — ERC-1155 community meme drops on Robinhood Chain, created by DCOT.\n/// @notice Owner (DCOT deployer) creates drops: ETH mint price, claim window, LPCG-holder gate (live balanceOf),\n///         per-wallet cap (fixed OR scaled by how many LPCG you hold), max supply, payment splits to collaborators,\n///         and optional burn-to-claim. Metadata (name, image, provenance) is built on-chain by uri().\ncontract LPCGMemes {\n    string public name;            // collection name (set at deploy)\n    string public symbol;          // collection symbol (set at deploy)\n    address public owner;          // DCOT deployer\n    address public immutable LPCG; // LPCG NFT (holder gate) on Robinhood\n    uint256 public dropCount;\n\n    struct Drop {\n        string  art;          // image URI (hosted / IPFS)\n        string  title;        // drop name\n        uint128 price;        // ETH (wei) per unit\n        uint64  start; uint64 end;      // claim window (end=0 → open)\n        uint32  maxSupply; uint32 minted;\n        uint32  perWallet;    // fixed per-wallet cap (allocMode 0)\n        uint32  unitsPerLPCG; // units per LPCG held (allocMode 1)\n        uint8   allocMode;    // 0 = fixed cap, 1 = scaled by LPCG held\n        bool    active; bool requireLPCG;\n        uint256 burnId; uint256 burnAmount;   // burn-to-claim: burn `burnAmount` of THIS collection's token `burnId` (0 amount = off)\n        string  desc;         // editable NFT description\n    }\n    uint256 private _lock;\n    modifier nonReentrant(){ require(_lock == 0, \"reentrant\"); _lock = 1; _; _lock = 0; }\n    mapping(uint256 => Drop) public drops;\n    mapping(uint256 => address[]) public payees;      // split recipients per drop\n    mapping(uint256 => uint16[])  public sharesBps;   // basis points (sum ≤ 10000; remainder → owner)\n    mapping(uint256 => mapping(address => uint256)) public mintedBy;\n    mapping(address => uint256) public owed;          // pull fallback for failed split pushes\n    mapping(uint256 => string[]) private _artChunks;  // FULLY ON-CHAIN art: base64 SVG stored in pieces\n    mapping(uint256 => bool) public onchainArt;       // if true, uri() serves the inline on-chain art (ignores the hosted URL)\n\n    // ERC1155 state\n    mapping(uint256 => mapping(address => uint256)) private _bal;\n    mapping(address => mapping(address => bool)) public isApprovedForAll;\n\n    event TransferSingle(address indexed op, address indexed from, address indexed to, uint256 id, uint256 value);\n    event TransferBatch(address indexed op, address indexed from, address indexed to, uint256[] ids, uint256[] values);\n    event ApprovalForAll(address indexed owner, address indexed op, bool approved);\n    event URI(string value, uint256 indexed id);\n    event DropCreated(uint256 indexed id, string title);\n    event Claimed(uint256 indexed id, address indexed to, uint256 qty, uint256 paid);\n\n    modifier onlyOwner(){ require(msg.sender == owner, \"not owner\"); _; }\n    constructor(string memory name_, string memory symbol_, address _lpcg){ owner = msg.sender; name = name_; symbol = symbol_; LPCG = _lpcg; }\n    function transferOwnership(address a) external onlyOwner { owner = a; }\n\n    // ── create / edit drops (DCOT only) ──\n    struct DropInput {\n        string  title; string art;\n        uint128 price; uint64 start; uint64 end;\n        uint32  maxSupply; uint32 perWallet; uint32 unitsPerLPCG; uint8 allocMode;\n        bool    requireLPCG; uint256 burnId; uint256 burnAmount; string desc;\n    }\n    function createDrop(DropInput calldata in_, address[] calldata payees_, uint16[] calldata shares_) external onlyOwner returns (uint256 id) {\n        require(payees_.length == shares_.length, \"split len\");\n        id = ++dropCount;\n        Drop storage d = drops[id];\n        d.title = in_.title; d.art = in_.art; d.price = in_.price; d.start = in_.start; d.end = in_.end;\n        d.maxSupply = in_.maxSupply; d.perWallet = in_.perWallet; d.unitsPerLPCG = in_.unitsPerLPCG; d.allocMode = in_.allocMode;\n        d.requireLPCG = in_.requireLPCG; d.burnId = in_.burnId; d.burnAmount = in_.burnAmount; d.desc = in_.desc; d.active = true;\n        for (uint256 i = 0; i < payees_.length; i++) { payees[id].push(payees_[i]); sharesBps[id].push(shares_[i]); }\n        emit DropCreated(id, in_.title);\n        emit URI(uri(id), id);\n    }\n    function setActive(uint256 id, bool a) external onlyOwner { drops[id].active = a; }\n    function setMeta(uint256 id, string calldata title_, string calldata desc_, string calldata art_) external onlyOwner { Drop storage d = drops[id]; d.title = title_; d.desc = desc_; d.art = art_; emit URI(uri(id), id); }\n    // ── fully on-chain art: append base64 SVG chunks, then flip onchainArt on ──\n    function appendArt(uint256 id, string calldata chunk) external onlyOwner { _artChunks[id].push(chunk); }\n    function clearArt(uint256 id) external onlyOwner { delete _artChunks[id]; }\n    function setOnchainArt(uint256 id, bool v) external onlyOwner { onchainArt[id] = v; emit URI(uri(id), id); }\n    function artChunkCount(uint256 id) external view returns (uint256) { return _artChunks[id].length; }\n    function setWindow(uint256 id, uint64 s, uint64 e) external onlyOwner { drops[id].start = s; drops[id].end = e; }\n    function setPrice(uint256 id, uint128 p) external onlyOwner { drops[id].price = p; }\n    function setArt(uint256 id, string calldata a) external onlyOwner { drops[id].art = a; emit URI(uri(id), id); }\n\n    // ── claim / mint ──\n    function eligibleCap(uint256 id, address who) public view returns (uint256) {\n        Drop storage d = drops[id];\n        return d.allocMode == 1 ? IERC721(LPCG).balanceOf(who) * d.unitsPerLPCG : d.perWallet;\n    }\n    function mint(uint256 id, uint256 qty) external payable nonReentrant {\n        Drop storage d = drops[id];\n        require(d.active, \"inactive\");\n        require(block.timestamp >= d.start && (d.end == 0 || block.timestamp <= d.end), \"closed\");\n        require(qty > 0, \"qty\");\n        if (d.requireLPCG) require(IERC721(LPCG).balanceOf(msg.sender) > 0, \"need LPCG\");\n        require(mintedBy[id][msg.sender] + qty <= eligibleCap(id, msg.sender), \"over your cap\");\n        if (d.maxSupply > 0) require(d.minted + qty <= d.maxSupply, \"sold out\");\n        require(msg.value == uint256(d.price) * qty, \"bad ETH\");\n        if (d.burnAmount > 0) _burn(msg.sender, d.burnId, d.burnAmount * qty);   // burn-to-claim (self token)\n        mintedBy[id][msg.sender] += qty; d.minted += uint32(qty);\n        _mint(msg.sender, id, qty);\n        _split(id, msg.value);\n        emit Claimed(id, msg.sender, qty, msg.value);\n    }\n    function _split(uint256 id, uint256 amt) internal {\n        if (amt == 0) return;\n        address[] storage ps = payees[id]; uint16[] storage sh = sharesBps[id];\n        uint256 paid;\n        for (uint256 i = 0; i < ps.length; i++) { uint256 a = amt * sh[i] / 10000; if (a > 0) { _pay(ps[i], a); paid += a; } }\n        if (paid < amt) _pay(owner, amt - paid);   // remainder → owner (DCOT)\n    }\n    function _pay(address to, uint256 a) internal { (bool ok, ) = payable(to).call{value: a, gas: 30000}(\"\"); if (!ok) owed[to] += a; }\n    function withdraw() external nonReentrant { uint256 a = owed[msg.sender]; require(a > 0, \"nothing\"); owed[msg.sender] = 0; (bool ok, ) = payable(msg.sender).call{value: a}(\"\"); require(ok, \"fail\"); }\n\n    // ── on-chain metadata (provenance: created by DCOT) ──\n    function uri(uint256 id) public view returns (string memory) {\n        Drop storage d = drops[id];\n        string memory ds = bytes(d.desc).length > 0 ? d.desc : \"LPCG MEMES - community meme art drop by DCOT on Robinhood Chain.\";\n        string memory image = d.art;\n        if (onchainArt[id]) { string memory b; string[] storage ch = _artChunks[id]; for (uint256 i = 0; i < ch.length; i++) b = string(abi.encodePacked(b, ch[i])); image = string(abi.encodePacked(\"data:image/svg+xml;base64,\", b)); }\n        string memory json = string(abi.encodePacked(\n            '{\"name\":\"', d.title, ' #', _u(id),\n            '\",\"description\":\"', ds,\n            '\",\"image\":\"', image, '\",\"attributes\":[',\n            '{\"trait_type\":\"Collection\",\"value\":\"LPCG MEMES\"},',\n            '{\"trait_type\":\"Creator\",\"value\":\"DCOT\"},',\n            '{\"trait_type\":\"Drop\",\"value\":', _u(id), '},',\n            '{\"trait_type\":\"Minted\",\"value\":', _u(d.minted), '}]}'\n        ));\n        return string(abi.encodePacked(\"data:application/json;base64,\", _b64(bytes(json))));\n    }\n    function totalSupply(uint256 id) external view returns (uint256) { return drops[id].minted; }\n\n    // ── minimal ERC1155 ──\n    function balanceOf(address a, uint256 id) public view returns (uint256) { return _bal[id][a]; }\n    function balanceOfBatch(address[] calldata as_, uint256[] calldata ids) external view returns (uint256[] memory r) { r = new uint256[](as_.length); for (uint256 i; i < as_.length; i++) r[i] = _bal[ids[i]][as_[i]]; }\n    function setApprovalForAll(address op, bool ok) external { isApprovedForAll[msg.sender][op] = ok; emit ApprovalForAll(msg.sender, op, ok); }\n    function _mint(address to, uint256 id, uint256 v) internal { _bal[id][to] += v; emit TransferSingle(msg.sender, address(0), to, id, v); _accept(address(0), to, id, v); }\n    function _burn(address from, uint256 id, uint256 v) internal { require(_bal[id][from] >= v, \"burn>bal\"); _bal[id][from] -= v; emit TransferSingle(msg.sender, from, address(0), id, v); }\n    function safeTransferFrom(address from, address to, uint256 id, uint256 v, bytes calldata) external {\n        require(from == msg.sender || isApprovedForAll[from][msg.sender], \"no auth\"); require(to != address(0), \"zero\");\n        require(_bal[id][from] >= v, \"bal\"); _bal[id][from] -= v; _bal[id][to] += v;\n        emit TransferSingle(msg.sender, from, to, id, v); _accept(from, to, id, v);\n    }\n    function _accept(address from, address to, uint256 id, uint256 v) internal {\n        if (to.code.length == 0) return;\n        try IERC1155Receiver(to).onERC1155Received(msg.sender, from, id, v, \"\") returns (bytes4 r) { require(r == 0xf23a6e61, \"reject\"); } catch { revert(\"non-receiver\"); }\n    }\n    function supportsInterface(bytes4 i) external pure returns (bool) { return i == 0xd9b67a26 || i == 0x0e89341c || i == 0x01ffc9a7; }\n\n    // ── helpers ──\n    function _u(uint256 v) internal pure returns (string memory) { if (v == 0) return \"0\"; uint256 j = v; uint256 l; while (j != 0) { l++; j /= 10; } bytes memory b = new bytes(l); while (v != 0) { l--; b[l] = bytes1(uint8(48 + v % 10)); v /= 10; } return string(b); }\n    function _b64(bytes memory d) internal pure returns (string memory) {\n        if (d.length == 0) return \"\";\n        bytes memory T = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n        bytes memory r = new bytes(4 * ((d.length + 2) / 3)); uint256 i; uint256 j;\n        for (; i + 3 <= d.length; i += 3) { uint256 n = (uint256(uint8(d[i])) << 16) | (uint256(uint8(d[i+1])) << 8) | uint256(uint8(d[i+2])); r[j++] = T[(n>>18)&63]; r[j++] = T[(n>>12)&63]; r[j++] = T[(n>>6)&63]; r[j++] = T[n&63]; }\n        if (d.length - i == 1) { uint256 n = uint256(uint8(d[i])) << 16; r[j++] = T[(n>>18)&63]; r[j++] = T[(n>>12)&63]; r[j++] = \"=\"; r[j++] = \"=\"; }\n        else if (d.length - i == 2) { uint256 n = (uint256(uint8(d[i])) << 16) | (uint256(uint8(d[i+1])) << 8); r[j++] = T[(n>>18)&63]; r[j++] = T[(n>>12)&63]; r[j++] = T[(n>>6)&63]; r[j++] = \"=\"; }\n        return string(r);\n    }\n}\n"
  }
 },
 "settings": {
  "optimizer": {
   "enabled": true,
   "runs": 150
  },
  "viaIR": true,
  "outputSelection": {
   "*": {
    "*": [
     "abi",
     "evm.bytecode",
     "evm.deployedBytecode",
     "metadata"
    ]
   }
  }
 }
}