👗 Fashion & Textile Design · textile remixing

Fabric Remix

Mint NFTs for remixed fabric patterns proving originality and creative lineage.

NFT provenance mint· onchain authorship
Section · Onchain

The primitive.

full primer →

Fashion designers mint each textile remixing as an ERC-721 token on ZKsync Sepolia pointing at an IPFS CID, so authorship and timestamp are provable from a single ZKsync Explorer link.

Why this primitiveOnchain provenance confirms derivative works anchored to original textile creators.

Kernel
an ERC-721 contract on ZKsync Sepolia that mints a creator-owned token pointing at an IPFS CID, verified in the ZKsync explorer
Drives the UI as
a 'mint to claim authorship' button that returns the tokenId, owner address, and ZKsync explorer link
Appendix · Secrets

Required keys.

METAMASK_PRIVATE_KEY
Exported from MetaMask. Fund on ZKsync Sepolia via the Chainstack faucet.
open ↗
ZKSYNC_SEPOLIA_RPC_URL
Alchemy ZKsync Sepolia HTTPS endpoint. Create a free app → copy the HTTPS URL.
open ↗
ETHERSCAN_API_KEY
Required for npx hardhat verify after deploy.
open ↗
PRIVY_APP_ID
Enables Google sign-in and sponsored transactions.
open ↗
PINATA_JWT
Pins images / JSON / manifests to IPFS.
open ↗

Add these in your Lovable project under Settings → Secrets before pasting the prompt below.

Appendix · Mega-prompt

The build prompt.

Paste into a fresh Lovable project. Make sure all five secrets above are set first. read the build strategy →

Build "Fabric Remix" in ONE Lovable message. Single-page demo on ZKsync Sepolia.

CONCEPT
Mint NFTs for remixed fabric patterns proving originality and creative lineage.
Discipline: Fashion & Textile Design (textile remixing).
Onchain primitive: NFT provenance mint. Why this primitive: Onchain provenance confirms derivative works anchored to original textile creators.

5-CREDIT BUDGET (HARD LIMIT):
- ONE single-page app. No router, no Lovable Cloud, no database, no auth flows beyond Privy drop-in.
- ONE Solidity contract (plus a shared paymaster) compiled with zksolc, deployed to ZKsync Sepolia (chain id 300), verified via the matterlabs verifier.
- Privy is always the auth layer (Google login, embedded wallet). Gas is paid by a ZKsync paymaster — the user never sees a fee.
- Pinata/IPFS only if the idea genuinely needs to store a file or metadata.
- At most ONE AI call per user action (use Lovable AI Gateway with LOVABLE_API_KEY if AI is part of the idea).
- Skip tests, skip CI, skip docs pages. Ship the demo, nothing else.

STACK
- React + Vite single page (the index route).
- SSR-safe Privy mount is mandatory. Never import @privy-io/react-auth at
  module scope of a route file — it crashes SSR. Use
  lazy(() => import('./privy-client-entry')) inside <ClientOnly> + <Suspense>,
  and put <PrivyProvider> only inside privy-client-entry.tsx.
- PrivyProvider config (chainId is passed per-call; do NOT set defaultChain):
    <PrivyProvider appId={import.meta.env.VITE_PRIVY_APP_ID}
      config={{ loginMethods:['google','email'],
                embeddedWallets:{ ethereum:{ createOnLogin:'users-without-wallets' } },
                appearance:{ theme:'dark' } }}>
- Read the embedded wallet from useWallets, not user.wallet:
    const embedded = wallets.find(w => w.walletClientType === 'privy');
- CRITICAL: ZKsync gas is paid by the paymaster contract, NOT Privy. Do NOT
  pass `sponsor: true` (that is Ethereum-only). Do NOT use
  `useSendTransaction` — it emits a plain EVM tx and silently drops the
  ZKsync paymaster field, so the user ends up paying gas. Instead, build
  the ZKsync EIP-712 tx type 113 with `zksync-ethers`, sign the raw
  digest through Privy `useSignRawHash`, and broadcast via a
  `zksync-ethers` Provider.
- Send flow (privy-client-entry.tsx):
    import { Provider, utils, types, EIP712Signer, Transaction } from "zksync-ethers";
    const provider = new Provider(import.meta.env.VITE_ZKSYNC_SEPOLIA_RPC_URL
      || "https://sepolia.era.zksync.dev");
    const paymasterParams = utils.getPaymasterParams(PAYMASTER_ADDRESS, {
      type: "General", innerInput: new Uint8Array(),
    });
    const tx: types.TransactionRequest = {
      to: CONTRACT_ADDRESS,
      from: embedded.address,
      data,
      chainId: 300,
      type: utils.EIP712_TX_TYPE,
      customData: { gasPerPubdata: utils.DEFAULT_GAS_PER_PUBDATA_LIMIT, paymasterParams },
      value: 0n,
      nonce: await provider.getTransactionCount(embedded.address),
      gasPrice: await provider.getGasPrice(),
    };
    tx.gasLimit = await provider.estimateGas(tx);
    const signer = new EIP712Signer(
      { getAddress: async () => embedded.address, signTypedData: async () => "0x" } as any, 300);
    const digest = await Transaction.from(tx).getSignedDigest?.() ?? await signer.getSignedDigest(tx as any);
    const { signature } = await signRawHash({ address: embedded.address, hash: digest });
    const populated = { ...tx, customData: { ...tx.customData, customSignature: signature } };
    const receipt = await provider.broadcastTransaction(utils.serialize(populated as any));
- Wrap the whole send in a 45s Promise.race timeout whose reject message
  names the exact operational cause:
    new Error("ZKsync paymaster tx timed out after 45s. Check paymaster is funded (>= 0.01 ETH) and RPC is reachable.")
- DASHBOARD PREREQUISITE (one-time): none for ZKsync — sponsorship lives
  in the paymaster contract, not the Privy dashboard.
- src/lib/pinata.ts uploads via `fetch('https://api.pinata.cloud/pinning/pinFileToIPFS', { method:'POST', headers:{ Authorization: `Bearer ${import.meta.env.VITE_PINATA_JWT}` }, body: fd })`.
- Hardhat in /contracts (kept outside the Vite bundle). Install
  `@matterlabs/hardhat-zksync` (single meta-package pulling in
  hardhat-zksync-solc, hardhat-zksync-deploy, hardhat-zksync-verify at
  matched versions) and `@matterlabs/zksync-contracts`.
- hardhat.config.cjs:
    require("@matterlabs/hardhat-zksync");
    module.exports = {
      zksolc: { version: "latest", settings: {} },
      solidity: { version: "0.8.24" },
      defaultNetwork: "zkSyncSepoliaTestnet",
      networks: {
        zkSyncSepoliaTestnet: {
          url: process.env.ZKSYNC_SEPOLIA_RPC_URL || "https://sepolia.era.zksync.dev",
          ethNetwork: "sepolia",
          zksync: true,
          chainId: 300,
          verifyURL: "https://explorer.sepolia.era.zksync.dev/contract_verification",
          accounts: [process.env.METAMASK_PRIVATE_KEY.startsWith("0x")
            ? process.env.METAMASK_PRIVATE_KEY : "0x" + process.env.METAMASK_PRIVATE_KEY],
        },
      },
      etherscan: { apiKey: process.env.ETHERSCAN_API_KEY },
    };
- Deploy scripts use `hre.deployer.deploy(Artifact, [args])` (from
  hardhat-zksync-deploy) — NOT `ethers.getContractFactory`. Deploy the
  concept contract first, then the GaslessPaymaster with the concept
  address as its target. Immediately fund the paymaster:
    await deployer.zkWallet.sendTransaction({ to: paymaster.address,
      value: ethers.parseEther("0.05") });
- Verify (matterlabs verifier, single command per contract):
    `npx hardhat verify --network zkSyncSepoliaTestnet <address> <constructor args>`
  On success the source becomes readable at
  `https://sepolia.explorer.zksync.io/address/<address>#contract`.
- Frontend reads: use a zksync-ethers Provider — never viem's `sepolia`
  chain object, which points at ZKsync Sepolia (chain 11155111), not
  ZKsync Sepolia (chain 300).
- Write both addresses to `src/data/contract.json`:
    { "address": "<concept>", "paymaster": "<paymaster>", "chainId": 300,
      "network": "ZKsync Sepolia",
      "explorer": "https://sepolia.explorer.zksync.io",
      "rpc": "https://sepolia.era.zksync.dev" }

CONTRACT (contracts/FabricRemix.sol):
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
/// @title FabricRemix
/// @notice ERC-721 provenance for: Mint NFTs for remixed fabric patterns proving originality and creative lineage.
/// @notice Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
contract FabricRemix is ERC721 {
    uint256 public nextId;
    mapping(uint256 => string) public cidOf;
    constructor() ERC721("FabricRemix", "FABRIC") {}
    /// @notice Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
    function mint(string calldata cid) external returns (uint256 id) {
        id = ++nextId; cidOf[id] = cid; _safeMint(msg.sender, id);
    }
    function tokenURI(uint256 id) public view override returns (string memory) {
        return string(abi.encodePacked("ipfs://", cidOf[id]));
    }
}
```

PAYMASTER (contracts/GaslessPaymaster.sol):
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { IPaymaster, ExecutionResult, PAYMASTER_VALIDATION_SUCCESS_MAGIC } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IPaymaster.sol";
import { IPaymasterFlow } from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IPaymasterFlow.sol";
import { TransactionHelper, Transaction } from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol";
import { BOOTLOADER_FORMAL_ADDRESS } from "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol";
/// @notice Funded paymaster; pays gas for any tx targeting `target`.
contract GaslessPaymaster is IPaymaster {
    address public immutable target;
    address public immutable owner;
    modifier onlyBootloader() { require(msg.sender == BOOTLOADER_FORMAL_ADDRESS, "!bootloader"); _; }
    constructor(address _target) { target = _target; owner = msg.sender; }
    receive() external payable {}
    function validateAndPayForPaymasterTransaction(bytes32, bytes32, Transaction calldata _tx)
        external payable onlyBootloader returns (bytes4 magic, bytes memory)
    {
        magic = PAYMASTER_VALIDATION_SUCCESS_MAGIC;
        require(address(uint160(_tx.to)) == target, "paymaster: wrong target");
        require(_tx.paymasterInput.length >= 4, "paymasterInput too short");
        bytes4 sel = bytes4(_tx.paymasterInput[0:4]);
        require(sel == IPaymasterFlow.general.selector, "unsupported flow");
        uint256 required = _tx.gasLimit * _tx.maxFeePerGas;
        (bool ok,) = payable(BOOTLOADER_FORMAL_ADDRESS).call{value: required}("");
        require(ok, "paymaster: pay bootloader failed");
    }
    function postTransaction(bytes calldata, Transaction calldata, bytes32, bytes32, ExecutionResult, uint256)
        external payable onlyBootloader {}
    function withdraw(address payable to) external { require(msg.sender == owner, "!owner"); to.transfer(address(this).balance); }
}
```

USER FLOW
1. Land on page -> 'Sign in with Google' (Privy) -> embedded wallet auto-provisioned on ZKsync Sepolia.
2. After the user creates a textile remixing artefact, pin the file to IPFS via Pinata, then call `mint(cid)` on the deployed contract through a ZKsync paymaster-sponsored tx. Show tokenId, IPFS preview (`https://gateway.pinata.cloud/ipfs/<cid>`), and ZKsync explorer mint-tx link.
3. Footer renders: "Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14"

REQUIRED SECRETS (Lovable -> Project Settings -> Secrets):
- METAMASK_PRIVATE_KEY   ZKsync Sepolia deployer key. Fund via https://faucet.chainstack.com/zksync-testnet-faucet or bridge Sepolia ETH at https://portal.zksync.io/bridge/?network=sepolia
- ZKSYNC_SEPOLIA_RPC_URL Optional; defaults to https://sepolia.era.zksync.dev (public RPC works for hackathons; use a dedicated provider for reliability).
- ETHERSCAN_API_KEY      Required by hardhat config plumbing; the actual verify request routes to ZKsync's matterlabs verifier, not Etherscan.
- PRIVY_APP_ID           Google/email sign-in + embedded wallet. Docs: https://docs.privy.io/llms-full.txt
- PINATA_JWT             IPFS uploads (only if the app pins media). Docs: https://docs.pinata.cloud/llms-full.txt

CREDIT (must appear in UI footer AND as NatSpec on every deployed contract):
Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14
Appendix · Market

Market sizing.

TAM
$270M
textile remix and mashup software
SAM
$75M
textile artists experimenting with pattern fusion
SOM
$5M
NFT-based remix pattern authentication

Indicative figures for hackathon pitches — refine with your own research before raising.

See also

Adjacent entries.