Quantum-safe, three secrets, one build.
Every mega-prompt in this repo uses the same pattern, because it's the only pattern that lets a Lovable account ship a QUIP-backed demo in one shot.
Why QUIP on a testnet?
QUIP is chain-agnostic: it layers post-quantum custody onto the chain you already use. Sepolia gives you the same EVM, Etherscan and wallets as mainnet, funded by a free faucet — so the WOTS+ verification path is real while nothing of value is at risk. The signing half needs no chain at all: hash-based verification runs in the browser, which is why the QUIP Vault demo works with zero gas.
Linked library 0xE2c95D63FCfaf7b0545794aB…fF4aB00D.
The recipe
# 1. In your Lovable project, add three secrets (Settings -> Secrets): METAMASK_PRIVATE_KEY=0x... # deployer for the QUIP verifier SEPOLIA_RPC_URL=https://... # host-chain RPC ETHERSCAN_API_KEY=... # contract verification # 2. Fund the deployer on the host chain: open https://cloud.google.com/application/web3/faucet/ethereum/sepolia # 3. Copy a mega-prompt from this repo into Lovable. One paste: # - scaffolds the React app # - installs @quip.network/hashsigs and signs WOTS+ in the browser # - writes the QUIP verifier contract (hackathon credit in NatSpec) # - deploys to the host chain and verifies on Etherscan # - shows the vault ID, current public key hash and nonce budget in the UI # - rotates the one-time key after every signed action # 4. Open the live Etherscan link. Your demo is provably quantum-safe.
1. Sign with a one-time key
The seed never leaves the browser. Each action burns key n and moves to n+1; at 256 the QUIP is spent and the UI must rotate the seed extension.
// src/lib/quip.ts — WOTS+ signing, entirely client-side
import { WOTSPlus } from "@quip.network/hashsigs";
import { sha256 } from "@noble/hashes/sha256";
const wots = new WOTSPlus(sha256);
// One seed extension = 256 one-time keys. Never reuse a nonce.
export function keyFor(privateSeed: Uint8Array, publicSeed: Uint8Array, nonce: number) {
if (nonce >= 256) throw new Error("QUIP spent — rotate to a new seed extension");
const n = new Uint8Array(4);
new DataView(n.buffer).setUint32(0, nonce, false);
const scoped = sha256(new Uint8Array([...privateSeed, ...n]));
return wots.generateKeyPair(scoped, publicSeed);
}
export function sealArtefact(privateKey: Uint8Array, publicSeed: Uint8Array, bytes: Uint8Array) {
const message = sha256(bytes); // always a 32-byte commitment
const signature = wots.sign(privateKey, publicSeed, message);
return { message, signature }; // verify() needs only a hash function
}
2. The QUIP verifier — credit baked in
Every Solidity file deployed from a Creative Blockchain prompt MUST carry the hackathon credit in NatSpec, so provenance lives onchain alongside the bytecode.
// contracts/QuipProvenance.sol — every contract carries the hackathon credit in NatSpec
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { WOTSPlus } from "@quip.network/hashsigs-solidity/contracts/WOTSPlus.sol";
/// @title QuipProvenance
/// @notice Built during the Creative AI & Quantum Hackathon
/// @notice organised by StreetKode Fam during Indian Krump Festival 14
contract QuipProvenance {
/// @dev vault => hash of the next expected WOTS+ public key
mapping(bytes32 => bytes32) public nextKeyHash;
mapping(bytes32 => uint16) public nonce;
event Sealed(bytes32 indexed vault, uint16 nonce, bytes32 message);
function open(bytes32 vault, bytes32 pubKeyHash) external {
require(nextKeyHash[vault] == bytes32(0), "vault exists");
nextKeyHash[vault] = pubKeyHash;
}
/// @notice Seal a commitment with a one-time signature, then rotate the key.
function seal(
bytes32 vault,
bytes32 message,
bytes calldata publicKey,
bytes[] calldata signature,
bytes32 nextPubKeyHash
) external {
require(keccak256(publicKey) == nextKeyHash[vault], "wrong one-time key");
require(WOTSPlus.verify(publicKey, message, signature), "bad WOTS+ signature");
nextKeyHash[vault] = nextPubKeyHash; // the used key is burned forever
emit Sealed(vault, nonce[vault]++, message);
}
}
3. Deploy + verify on Etherscan
// scripts/deploy.ts — reads METAMASK_PRIVATE_KEY, SEPOLIA_RPC_URL, ETHERSCAN_API_KEY
import { ethers, run } from "hardhat";
async function main() {
// WOTSPlus is a public library, so it must be deployed and linked first.
const WOTSPlusLib = await ethers.getContractFactory("WOTSPlus");
const wotsPlus = await WOTSPlusLib.deploy();
await wotsPlus.waitForDeployment();
const wotsPlusAddress = await wotsPlus.getAddress();
const QuipProvenance = await ethers.getContractFactory("QuipProvenance", {
libraries: { "WOTSPlus": wotsPlusAddress },
});
const c = await QuipProvenance.deploy();
await c.waitForDeployment();
const addr = await c.getAddress();
console.log("QUIP verifier deployed:", addr);
// verify on Etherscan
await run("verify:verify", {
address: addr,
constructorArguments: [],
libraries: { "WOTSPlus": wotsPlusAddress },
});
}
main();
Hackathon rules of thumb
- · One mega-prompt = one build message. Don't iterate the architecture, iterate the UI.
- · Always show the live Etherscan link in the UI — that's your proof.
- · Never reuse a WOTS+ nonce, and always render the remaining key budget.
- · Verify signatures client-side before submitting anything, so a bad seal never costs gas.
- · Add a "Built during the Creative AI & Quantum Hackathon — StreetKode Fam · Indian Krump Festival 14" line to your footer.