Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jollyjoker992/e2a1564f98aeb329e7f2c07ab6287f57 to your computer and use it in GitHub Desktop.
Save jollyjoker992/e2a1564f98aeb329e7f2c07ab6287f57 to your computer and use it in GitHub Desktop.
Series data on-chain storage proposal

Problem Statement

  • Artist Requirement: Artist wants to store their custom, unminified JS file (~40 KB) on-chain with ERC‑721 tokens, while keeping the rest of the front-end assets (HTML, CSS, other JS, media) off-chain (e.g., IPFS).
  • Current Architecture: Each artwork series and its member tokens are managed by an ERC‑721 contract. Token metadata includes all source code and asset references.
  • Limitation: The existing smart contract does not support arbitrary on-chain data storage beyond standard metadata fields, so embedding a 40 KB JS file directly on-chain is not currently possible without contract changes.

Proposed Solution

  1. Series Data Mapping

    • Support multiple SSTORE2 pointers per series (for >24 KB payloads):

      // zero or more contracts that hold the data chunks
      mapping(uint256 => address[]) public seriesDataPointers;
      // optional authenticity hash and off‑chain URI
      mapping(uint256 => bytes32)  public seriesDataHash;
      mapping(uint256 => string)   public seriesDataUri;
    • seriesDataPointers[seriesId] can hold one address (≤ 24 KB) or several addresses (e.g., two for 40 KB).

  2. Data Storage Strategies**

    • Raw Data via SSTORE2: Use SSTORE2 to write the unminified JS (~40 KB) to a separate contract, store its address in seriesDataPointer[seriesId].
    • Minified Raw Data: If the JS is minified (e.g., reduced to ~20 KB), gas costs roughly halve.
    • On-Chain Hash Only: Compute bytes32 hash = keccak256(data) and store in seriesDataHash[seriesId].
    • IPFS URI Reference: Upload the JS to IPFS, then store its CID as seriesDataUri[seriesId].
  3. Series Registration Function

    • Register one or more pointers after minting:

      function registerSeriesData(
        uint256          seriesId,
        address[] calldata dataPointers, // one or more
        bytes32           dataHash,
        string  calldata dataUri
      ) external onlyOwner {
        seriesDataPointers[seriesId] = dataPointers;
        seriesDataHash[seriesId]     = dataHash;
        seriesDataUri[seriesId]      = dataUri;
      }
    • Back‑end mint flow remains unchanged; a separate script can call this function with one or multiple pointers.

Storage Cost Estimation

Why “pointers”? SSTORE2 stores large blobs by deploying a tiny contract whose bytecode is the data, then keeps that contract’s address as a pointer. One contract can hold up to 24 576 bytes (~24 KB) of payload. A 40 KB blob therefore needs two such contracts (two pointers); a ~20 KB minified file fits in one. Each extra pointer adds an additional CREATE cost plus per‑byte storage gas.

Strategy Size Gas Estimate* Cost (ETH) USD (@ $3 000/ETH)
Raw via SSTORE2 (2 pointers) 40 KB ≈ 6 950 000 ≈ 0.070 ≈ $210
Minified via SSTORE2 (1 ptr) 20 KB ≈ 3 800 000 ≈ 0.038 ≈ $114
On‑chain Hash (SSTORE) 32 B ≈ 20 000 ≈ 0.00020 ≈ $0.60
IPFS CID (string) 46 B ≈ 25 000 ≈ 0.00025 ≈ $0.75

*Gas estimates use empirical data from the 0xsequence/sstore2 benchmarks, which report ~4.15 M gas to write the maximum 24 576 B chunk and ~170 gas/B for large payloads (github.com). The 40 KB file exceeds the single‑chunk limit, so two pointers are required. Prices assume 10 gwei gas.

Concerns & Considerations

  • Gas Costs:

    • Direct on-chain storage of 40 KB (via SSTORE2) remains the most expensive (~$210 per series).
    • Hash‑only or IPFS URI strategies reduce gas to under ~$1 per series.
  • Redundancy:

    • If token metadata already references the JS file, adding mapping entries may duplicate data.
  • Security & Validation:

    • Off-chain systems should verify seriesDataHash[seriesId] against retrieved JS to prevent tampering.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment