Notation Licensing Swap v10
A dance & choreography tool that turns notation licensing into a one-click, gas-sponsored EVVM payment.
Swap maker/taker· evvm
Section · Onchain
full primer →The primitive.
Choreographers post or fill a notation licensing swap order on EVVM; the Swap contract acts as the pay-leg sender and the fisher pays gas either side.
Why this primitiveMake, cancel, or fill an EVVM swap order — Swap contract is the pay-leg sender.
Kernel
Make, cancel, or fill an EVVM swap order — Swap contract is the pay-leg sender.
Drives the UI as
List asset for USDC, wait for a taker, or fill someone else's order.
Required keys.
METAMASK_PRIVATE_KEY
Sepolia deployer + fisher wallet (single-EOA model). Fund via the Google Cloud faucet.
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.
budget · 1 message
Paste into a fresh Lovable project. Make sure all five secrets above are set first. read the build strategy →
Build "Notation Licensing Swap v10" in ONE Lovable message. Single-page demo on Ethereum Sepolia.
CONCEPT
A dance & choreography tool that turns notation licensing into a one-click, gas-sponsored EVVM payment.
Discipline: Dance & Choreography (notation licensing).
EVVM primitive: Swap maker/taker. Why: Make, cancel, or fill an EVVM swap order — Swap contract is the pay-leg sender.
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: "Notation Licensing Swap v10" — collect the inputs relevant to notation licensing.
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 "Swap maker/taker") 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 "notation licensing".
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 "notation licensing":
- Copy, iconography, and success states should read as Dance & Choreography content, not generic web3.
- Use the discipline vocabulary the audience already knows (choreographers, dancers, dance teachers, movement directors).
- 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.
Market sizing.
TAM
the global dance industry (~$5B; >2M studios worldwide)
SAM
A gas-sponsored payments slice of Dance & Choreography
estimate 10% of TAM.
SOM
The choreographers early-adopter segment
estimate 1% of TAM in year one.
Indicative figures for hackathon pitches — refine with your own research before raising.
Adjacent entries.
tour splits
Tour Splits Swap
A dance & choreography tool that turns tour splits into a one-click, gas-sponsored EVVM payment.
notation licensingNotation Licensing Swap v2
A dance & choreography tool that turns notation licensing into a one-click, gas-sponsored EVVM payment.
tour splitsTour Splits Swap v3
A dance & choreography tool that turns tour splits into a one-click, gas-sponsored EVVM payment.
notation licensingNotation Licensing Swap v4
A dance & choreography tool that turns notation licensing into a one-click, gas-sponsored EVVM payment.