Skip to content

Wallet-signed agreements

Pact

A Nimiq Pay Mini App that turns an informal deal into terms both sides sign with their wallet — then tracks the work and the payments against it.

LiveWeb3Full StackAIProduct DesignCreator Economy

Deployed. Wallet signing and payments run inside Nimiq Pay; in a regular browser the app offers a full demo instead.

Period
Sep – Oct 2026
Status
Live
Role
Product design, Full-stack engineering
Context
Nimiq Mini Apps Competition — Cycle II special reward.
A Pact whose terms changed after both parties signed: the PACT Seal at the top, the signed fingerprint struck through and the current one shown below it.

Screenshot of the running product.

The problem

Why this needed to exist

Most disputes over small jobs are not about dishonesty. Nobody wrote down what was agreed, and two people remember it differently.

The existing answers are the wrong shape. A contract template is too heavy for a 200 USDT job. A chat thread is not a record anyone can point at. An escrow service takes custody and a fee, and lives outside the wallet where the money already is.

Who it’s for

  • Freelancers and their clients
  • Creators taking brand work
  • Small sellers
  • Friends holding each other to a commitment

The solution

How it works

You describe the deal in your own words. Pact extracts the parties, deliverable, amount, currency, deadline, payment condition and milestones into structured terms you can edit, and a Smart Review flags anything that could be read two ways — "several videos", a missing deadline, uncapped revisions — with a concrete fix for each.

Both sides then sign a Blake2b fingerprint of the exact terms with their Nimiq wallet. Payments are made from the agreement, and the shared timeline records everything both parties see.

From a sentence to a sealed agreement

  1. 01DescribePlain-language deal
  2. 02StructureAI extracts terms (Zod-validated)
  3. 03ReviewAmbiguities flagged with fixes
  4. 04SealBoth wallets sign the digest
  5. 05PayMemo carries the Pact id
  6. 06ConfirmServer reads the chain
Signing happens in the wallet. Confirmation happens on the server, against the network.

Implemented

What’s actually built

  • The Seal is a signature

    Each party calls nimiq.sign() over a canonical digest of the terms. The server verifies the signature and derives the signer’s address from the public key — it never accepts an address a client claims.

  • The brand mark is the fingerprint

    The ring around every agreement renders the digest: each of its 32 hex characters becomes one tick. Two people can compare seals across two phones, and a changed agreement visibly changes shape.

  • Payments carry the agreement on-chain

    NIM payments go out with data = PACT:<id>:<milestone>, so the payment’s purpose is written into the Nimiq ledger itself. USDT settles as an ERC-20 transfer on the chosen chain.

  • Confirmed means the network said so

    A wallet returns a transaction reference, recorded as sent. The server reads the network and confirms only if the transaction exists, succeeded, went to the agreed recipient for the agreed amount with this agreement’s memo.

  • Renegotiation that re-seals

    Either side can propose changes to a sealed pact. Accepting them moves the fingerprint and clears both signatures, so it cannot return to sealed until both people sign what it now says.

  • Public records, by consent

    A published agreement gets a link anyone can open to check the terms, fingerprint and signatures — without delivery notes, transaction references or full wallet addresses.

  • Campaigns

    A brand posts a brief with a fixed reward and a number of spots. Taking one up creates an ordinary Pact. Spots are claimed atomically, one per person, and return if an agreement is declined.

  • Issues, not verdicts

    Either side can raise one of four real problems in their own words. It records a disagreement; it does not adjudicate one, and it moves no money.

Interface

Screens from the real product

  • Edit a term after both people signed, and the agreement says so — the signatures clear, because a signature only matches the exact terms it was made over.
  • The welcome screen: describe a deal, both sides sign, paid when the network confirms.
  • Campaigns: a brand posts work with a fixed reward; taking one up creates an ordinary Pact.

My role

What I did

Concept, product design, the PACT Seal identity, and the full implementation: the Next.js app, the wallet integration layer, the AI Pact Builder with a deterministic fallback, the Postgres schema and the test suite.

Responsibilities

  • Product design
  • Full-stack engineering
  • Wallet integration
  • Brand (the PACT Seal)

Stack (verified from source)

  • Next.js 15
  • TypeScript
  • @nimiq/mini-app-sdk
  • Supabase Postgres
  • Gemini / Anthropic
  • Zod
  • EVM (USDT)

Decisions

Technical and design choices

  1. 01

    No escrow, and say so

    The Mini Apps framework gives an app no way to hold or conditionally release funds. Pact never claims to: payments go wallet to wallet, and a build-time honesty check fails if words like "escrow" or "guarantee payment" appear in user-facing copy without a negation.

  2. 02

    One redaction boundary

    Everything a non-participant can see passes through a single function that builds a separate record type field by field, so a column added later cannot publish itself. A test greps the serialised payload for wallet addresses.

  3. 03

    Allowlists for visibility

    Visibility checks are written as "is SHAREABLE or PUBLIC", never "is not PRIVATE", so a row from before the column existed withholds instead of publishing. This was a real bug caught in testing.

  4. 04

    Reputation is a view, not a table

    Trust metrics are a database view over what actually happened, and always show the denominator — "4 of 5 delivered on time", never a bare 80%.

Trade-offs

Challenges along the way

  1. 01

    A provider that resolves with errors

    The Nimiq provider resolves with an error object instead of throwing. A call site that forgets to check would report a payment as sent when it was not, so every provider call goes through one unwrap() function.

  2. 02

    Money without floats

    USDT has 6 decimals, not 18, and gas is paid in the chain’s native token. Amounts are minor-unit strings end to end, and milestone splits always sum back to the total.

  3. 03

    Judges on laptops

    Wallet features only work inside Nimiq Pay. Outside it, the app detects the missing provider and offers a complete demo instead of failing.

Results

What can be checked

  • Cycle II special reward

    One of five extra picks chosen from 207 submissions to the Nimiq Mini Apps Competition, Cycle II — a $100 reward.

  • Competition submission

    Listed in the official Nimiq Mini Apps Competition Cycle II submissions.

    miniappscompetition.com (opens in a new tab)
  • Tests

    135 tests run without a test framework — Node executes the TypeScript directly — covering signature replay, digest stability, milestone arithmetic and redaction.

    Repository (opens in a new tab)

Lessons

What I took away

  • When a product cannot do something users expect — like hold funds — the strongest design move is to state it plainly on the screen where they would assume it.
  • Making the security property visible (the Seal) did more for trust than any copy could.

What’s next

Planned — not built

Planned work

  • A live, end-to-end payment confirmation run on mainnet (verification has been exercised against recorded mainnet responses and a fake chain)
  • Optional trust-minimised settlement if the Mini Apps framework adds conditional release

Current limits, stated plainly

  • Pact never holds funds and cannot reverse a payment.
  • The AI helps word an agreement; it is not legal advice.