engineering
Confidential Signals Without Putting Fiat On-Chain
Naseeg uses optional fhEVM interactions for private signals while keeping fiat settlement and public totals in their proper places.

Privacy features are easy to overstate. A system can use encryption for one part of a workflow while relying on conventional application and payment systems for another. Clear language is more useful than treating the presence of cryptography as a blanket property.
Naseeg's optional Sepolia fhEVM interactions are designed for confidential support signals, private sponsor caps, curation scores, public reveals, and participation badges. They are not the custody layer for fiat support.
Different systems, different responsibilities
Stripe Checkout processes card support. Convex holds product state, payment records, and allocation logic. The on-chain layer can provide bounded proofs and encrypted signals without pretending to determine live public totals or settle every financial event.
This division gives each layer a legible job. It also avoids a harmful false choice between full public disclosure and a vague promise of "private finance." A product can minimize unnecessary exposure while still retaining a clear, accountable source of truth for the state it needs to operate.
Privacy is an interface decision too
Users should be able to understand what is public, what is private, and who can act on the information they provide. The technical architecture matters, but so do product language, defaults, and the refusal to claim more confidentiality than a workflow actually provides.
Start with the authoritative payment path
Naseeg's current application uses Stripe Checkout for card support and Convex for the product record of that contribution. Checkout preparation validates that the Artifact belongs to a published project, that the Artifact is live, that a round is open, and that the project has a curated submission in that round. It then creates a Convex contribution in the initiated state.
const contributionId = await ctx.db.insert("contributions", {
projectId: project._id,
artifactId: artifact._id,
roundId: openRound._id,
supporterUserId: user?._id,
grossAmountCents,
netCreatorAmountCents: grossAmountCents,
currency: artifact.currency,
paymentMethod: "card",
status: "initiated",
createdAt: now(),
});
The checkout route creates the Stripe session, and Stripe's signed webhook is responsible for synchronizing the outcome. The Convex sync mutation looks up the contribution by stripeCheckoutSessionId, handles an already-settled payment idempotently, and records paid, refunded, or chargeback. Allocation logic counts only the states designed to represent completed support; a card entry or a browser redirect is not enough.
if (args.status === "paid" && settledStatuses.includes(contribution.status)) {
return contribution._id;
}
await ctx.db.patch(contribution._id, {
status: args.status,
stripePaymentIntentId: args.stripePaymentIntentId,
paidAt: args.status === "paid" ? now() : contribution.paidAt,
refundedAt: args.status !== "paid" ? now() : contribution.refundedAt,
updatedAt: now(),
});
This is the source-of-truth boundary. Stripe handles the card settlement; Convex stores the accountable product state and allocation inputs. The fhEVM layer does not take custody of the card payment and does not replace either record.
01 / settlement
Stripe + Convex
Checkout, payment status, contributions, and allocations are authoritative here.
02 / optional
fhEVM signal
Encrypted support, caps, and scores are submitted on Sepolia.
03 / reveal
Public proof
A reveal flow publishes an intended public result with proof.
What the optional fhEVM layer does
Naseeg's current contract client targets Sepolia for optional confidential interactions. The client obtains an fhEVM instance, creates encrypted input for the intended contract and the user's address, encrypts an amount or score, then sends the encrypted handle and input proof to the relevant contract.
// Simplified from lib/naseeg-contracts.ts.
const input = fhevm.createEncryptedInput(NASEEG.funding, address);
input.add64(parseUnits(amount, 6));
const encrypted = await input.encrypt();
const tx = await funding.submitSupport(
roundId,
projectId,
encrypted.handles[0],
encrypted.inputProof,
);
return await tx.wait();
The same pattern is used for sponsor caps and curated scores, with the curation client clamping a score to the permitted range before it is encrypted. The result is a bounded privacy capability: an encrypted value can be submitted as an optional signal without making an unsupported claim that fiat settlement, product totals, or every decision has moved onto the chain.
A signal is not a payment state transition
After a settled contribution exists, the product can record an optional on-chain progress state such as signal_pending, signaled, or badge_claimed. That mutation refuses to act on a contribution outside the settled-status set. This ordering prevents a signal from turning an unpaid or invalid checkout attempt into support.
if (!settledStatuses.includes(contribution.status)) {
fail("INVALID_STATE", { resource: "contribution" });
}
await ctx.db.patch(contributionId, {
status: "signaled",
supporterWallet,
onchainTxHash,
updatedAt: now(),
});
This example describes the product boundary, not a guarantee that all optional Sepolia paths will be enabled for every participant. The signal layer is additive and deliberately downstream of the payment state.
Public reveal is a separate, deliberate action
Naseeg's contract client also contains explicit reveal flows. It requests a reveal from the relevant contract, reads result handles, calls publicDecrypt, then publishes the clear values with the decryption proof. The code is useful because it makes publicness an operation rather than an accidental byproduct of storing a value.
await (await funding.requestReveal(roundId, projectId)).wait();
const handles = await funding.resultHandles(roundId, projectId);
const decrypted = await fhevm.publicDecrypt(handles);
return await (
await funding.publishResult(
roundId,
projectId,
decrypted.abiEncodedClearValues,
decrypted.decryptionProof,
)
).wait();
The existence of a public reveal is not evidence that all information should be public. It is evidence that the application distinguishes between encrypted submission, eligible product state, and an intended public result.
A precise privacy claim is stronger than a broad one
The honest description is specific: Naseeg can use optional Sepolia fhEVM interactions for confidential support signals, sponsor caps, curation scores, public reveals, and badges. Stripe Checkout and Convex remain authoritative for fiat settlement, current product state, and allocation inputs. That is not a compromise in the architecture. It is how the system keeps a sensitive signal from being confused with a custodial payment rail or a universal privacy promise.
Naseeg remains Coming Soon. The implementation examples describe the currently built paths and their boundaries, not public availability or an assurance that a participant's data is private beyond the exact workflow described.