🎮 Games & Interactive · in-game trades

In-Game Trades Fisher-signed v8

A games & interactive tool that turns in-game trades into a one-click, gas-sponsored EVVM payment.

Fisher-signed pay· evvm
Section · Onchain

The primitive.

full primer →

Indie game devs sign a one-line EVVM message in the browser to run in-game trades; a fisher wallet replays it on Sepolia and pays the gas — no wallet extension, no faucet.

Why this primitiveUser signs an EVVM EIP-191 CSV message; a fisher wallet replays it on-chain and pays gas.

Kernel
User signs an EVVM EIP-191 CSV message; a fisher wallet replays it on-chain and pays gas.
Drives the UI as
One button — Google login via Privy, then Pay with USDC. No wallet extension, no faucet.
Appendix · Secrets

Required keys.

METAMASK_PRIVATE_KEY
Sepolia deployer + fisher wallet (single-EOA model). Fund via the Google Cloud faucet.
open ↗
SEPOLIA_RPC_URL
Alchemy Sepolia HTTPS endpoint. Public RPCs throttle under hackathon load.
open ↗
ETHERSCAN_API_KEY
Required for forge verify-contract after deploy.
open ↗
PRIVY_APP_ID
Enables Google sign-in and sponsored transactions. Toggle Gas sponsorship → Sepolia in the dashboard.
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 "In-Game Trades Fisher-signed v8" in ONE Lovable message. Single-page demo on Ethereum Sepolia.

CONCEPT
A games & interactive tool that turns in-game trades into a one-click, gas-sponsored EVVM payment.
Discipline: Games & Interactive (in-game trades).
EVVM primitive: Fisher-signed pay. Why: User signs an EVVM EIP-191 CSV message; a fisher wallet replays it on-chain and pays gas.

EVVM STANDING ORDERS (self-contained — do NOT rely on any external skill)

Chain: Ethereum Sepolia (chainId 11155111). Auth: Privy (Google/email/wallet), embedded wallet.
Gas: Native Privy sponsorship — enable in Privy dashboard → Gas sponsorship → App pays → Sepolia →
      "Allow transactions from the client".

Signing rule: EVVM messages are EIP-191 personal_sign of a CSV STRING (not abi-encoded):
    "{evvmId},{senderExecutor},{hashPayload},{originExecutor},{nonce},{isAsyncExec}"
  Addresses lowercase, booleans literal "true"/"false".

hashPayload rule: hashPayload = keccak256(abi.encode("<functionName>", ...on-chain hash args ONLY))
  Include ONLY the fields the on-chain hashDataForX view hashes — nothing else. Do NOT include
  evvmId, nonce, or isAsyncExec in the hash; they live in the outer CSV. Example for Core.pay
  it is 5 args exactly:
    keccak256(abi.encode("pay", to, identity, token, amount, priorityFee))
  A wrong shape reverts InvalidSignature (selector 0x8baa579f) with no useful message. Always
  cross-check locally against the hashDataForX view before shipping.

Sender rules (multi-leg flows):
- Single-leg op:   senderExecutor = 0x0
- Two-leg (MNS reveal, staking, swap, split):
    op leg  → senderExecutor = 0x0
    pay leg → senderExecutor = address of the CALLING contract (NameService / Staking / Swap / Core)

Nonces: random uint256 from 32 bytes of crypto.getRandomValues; isAsyncExec = true.

Browser-signing rule: any op that binds a user 'from' address (deposit, withdraw, split, MNS, staking,
swap) MUST be signed by the connected Privy wallet. The fisher route re-derives the expected message,
runs verifyMessage against the user's address, then broadcasts with its own EOA paying gas.

Privy mount rule: NEVER import @privy-io/react-auth at module scope of a route file.
  Wrap in <ClientOnly><Suspense><LazyPrivyEntry/></Suspense></ClientOnly>. PrivyProvider config:
    { loginMethods:["google","email","wallet"],
      embeddedWallets:{ ethereum:{ createOnLogin:"users-without-wallets" } },
      defaultChain: sepolia, supportedChains:[sepolia] }
  DO NOT pass uiOptions:{ showWalletUIs:false } — aborts with "signal is aborted without reason".
  DO NOT wire ZeroDev/paymaster URLs — native Privy sponsorship handles it.

Sponsored host-chain sends (Fund-EVVM, any host-chain user tx):
  await sendTransaction(
    { to, data, chainId: 11155111 },
    { sponsor: true, address: embedded.address }
  );
  Both fields (sponsor + address) are required or Privy silently falls back to a user-paid path.

Fund-EVVM is a TWO-STEP host-chain flow (both via the sponsored send above):
  1. USDC.approve(Treasury, amount)      // atomic USDC units, uint256
  2. Treasury.deposit(USDC, amount)      // pulls the approved amount into EVVM
  Refetch Core.getBalance(user, USDC) after settle. Show approve+deposit as two buttons.

Decimal USDC UX (non-negotiable):
- Inputs: <input inputMode="decimal" defaultValue="0.01" /> — never "USDC (micro)" labels.
- Parse:  parseUnits(str.trim(), 6)   // → bigint atomic
- Format: `${formatUnits(wei, 6)} USDC`
- Never render raw atomic values like "10000" in the UI. Contracts still receive the atomic bigint.

Balance preflight (mandatory before ANY user-signed op that spends inside EVVM — pay, split, MNS
pay leg, staking, swap, x402 settle, shielded deposit): read Core.getBalance(user, token) on mount
and after every settle. Gate the submit button when balance < required and render an amber
"Insufficient EVVM USDC — top up first" notice pointing at the Fund-EVVM card. Skipping this
produces InsufficientBalance (selector 0xf4d678b8) reverts the user can't self-diagnose.

Deployment (in a Lovable Linux sandbox — participants deploy from THEIR project, not locally):
1. bash scripts/install-foundry.sh    # installs foundry via Nix into the sandbox
2. node scripts/deploy-evvm.mjs       # deploys the 6-contract EVVM stack + registers evvmId
   Uses METAMASK_PRIVATE_KEY as deployer AND admin AND goldenFisher AND activator (single-EOA model).
   Writes src/evvm/deployment.json { evvmId, core, staking, nameService, swap, treasury, usdc, chainId }.
3. node scripts/set-evvm-id.mjs       # CRITICAL — Registry.register assigns the id but Core still
   reports evvmId=0 until an admin calls Core.setEvvmID(assignedId). Every user signature (built
   with the Registry id) reverts InvalidSignature (0x8baa579f) until this runs. Re-run whenever
   Core is redeployed. The script reads evvmId from src/evvm/deployment.json and signs with
   METAMASK_PRIVATE_KEY (the single-EOA admin).

Fisher: server-side viem wallet at src/lib/fisher.server.ts, uses METAMASK_PRIVATE_KEY.
Fisher routes: src/routes/api/public/fisher/*.ts (the /api/public/* prefix bypasses site auth on
published builds — verify signatures inside every handler).

Fisher response contract (every fisher route returns this exact shape):
  { ok: boolean, txHash?: `0x${string}`, explorer?: string, revert?: string, error?: string }
  Build `explorer` server-side: `${evvmCfg.explorer}/tx/${txHash}`. On success the UI renders
  a clickable "↗ View on Etherscan · <txHash slice>". On failure surface `revert` (decoded
  selector) or `error` verbatim in the step log — do NOT swallow it.

Common revert selectors — decode and show human strings, do NOT show raw 0x-selectors:
  InvalidSignature         0x8baa579f  → wrong hashPayload shape, or Core.setEvvmID never ran
  InsufficientBalance      0xf4d678b8  → payer EVVM balance too low; preflight must have caught this
  ProofLengthWrongWithLogN 0x59895a53  → zkVault verifier ↔ bb.js version mismatch

REQUIRED SECRETS:
- PRIVY_APP_ID              — Privy dashboard → App settings
- METAMASK_PRIVATE_KEY      — Sepolia EOA that deploys + fishes; fund via https://cloud.google.com/application/web3/faucet/ethereum/sepolia
- SEPOLIA_RPC_URL           — Alchemy Sepolia HTTPS URL (public RPCs throttle under load)
- ETHERSCAN_API_KEY         — for verification via forge verify-contract

CREDIT LINE (must appear in UI footer AND as a NatSpec @notice on any deployed contract):
  Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14.


MINIMUM VIABLE FLOW
1. Landing page. "Sign in with Google" via Privy — embedded wallet is auto-provisioned.
2. Show the user their EVVM USDC balance. If zero, render the fund-EVVM empty-state (address + copy
   button + Circle faucet link + refresh).
3. Primary action: "In-Game Trades Fisher-signed v8" — collect the inputs relevant to in-game trades.
4. On submit:
   a. Client builds hashPayload = keccak256(abi.encode("<functionName>", ...typedArgs)) exactly per
      the on-chain hashDataForX view for this flow.
   b. Client builds the CSV message with the sender rules above (single-leg or two-leg as required
      by "Fisher-signed pay") and calls personal_sign via Privy.
   c. Client POSTs { user, signature, args } to /api/public/fisher/<flow>.
   d. Fisher route re-derives the expected message, verifyMessage's the user signature, then submits
      the tx with METAMASK_PRIVATE_KEY (see src/lib/fisher.server.ts). Return the tx hash.
5. UI shows a Sepolia Etherscan link for the tx and a receipt tuned to "in-game trades".

CONTRACTS: reuse the deployed EVVM stack — see src/evvm/deployment.json. Do NOT redeploy per-idea.
If deployment.json.deployed is false, run: node scripts/deploy-evvm.mjs (from a Lovable sandbox
terminal, NOT from a user's laptop).

TAILORING NOTES for "in-game trades":
- Copy, iconography, and success states should read as Games & Interactive content, not generic web3.
- Use the discipline vocabulary the audience already knows (indie game devs, modders, esports operators).
- Keep the whole build to ONE index route + ONE fisher route.

CREDIT (footer + on-chain @notice):
  Built during the Creative AI & Quantum Hackathon organised by StreetKode Fam during Indian Krump Festival 14.
Appendix · Market

Market sizing.

TAM
the interactive-entertainment market ($187B; 3B players)
SAM
A gas-sponsored payments slice of Games & Interactive
estimate 10% of TAM.
SOM
The indie game devs early-adopter segment
estimate 1% of TAM in year one.

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

See also

Adjacent entries.