Skip to content

Testnet: free test dollars. The real Nuntio runs on Monad mainnet at nuntio.finance.

Provenance

What we built, and what we brought.

The hackathon asks that what you show was built during the six weeks. This is the detailed answer, including the parts that are not finished.

Every claim below names a file, a test count, or something read back from the chain. Counts come from the suites, run on 1 October 2026.

Written during the build window

The policy engine had nothing to inherit.

The team’s earlier payments work has no spending limits, no merchant allowlist and no subscription logic anywhere in it. Every file below was written for this build.

  • NuntioAccount.sol

    The account that holds the funds and decides every payment. Allowance, per payment cap, frequency limit, expiry, pause, and a merchant allowlist held separately for each spender. There is no address parameter on the paying function at all, so naming a recipient is not something a spender can express.

    • packages/contracts/src/NuntioAccount.sol
    • 1,064 lines
    • 185 tests
  • ApprovalQueue.sol

    Where a refused payment goes to ask a human. It is a separate transaction because a revert rolls back its own writes, so blocking a payment and recording that somebody asked for it cannot happen in the same call. Only the owner may agree to a request, and a request is accepted only from the spender it names.

    • packages/contracts/src/ApprovalQueue.sol
    • 800 lines
    • 108 tests
  • ServiceRegistry.sol

    Merchant lookup by id. Resolution reverts on an id nobody registered, rather than returning the zero address, which is what makes an invented recipient fail at lookup instead of sending money to a hole and minting a receipt that claims delivery.

    • packages/contracts/src/ServiceRegistry.sol
    • 205 lines
    • 23 tests
    • packages/contracts/deployments/10143.json
  • NuntioTeamFactory.sol

    Creates a sealed account and approval queue for a new team in one transaction, then hands ownership to whoever asked. It created the Sandbox, the account every tester joins, and anyone signed in can now create a team of their own through it, owned by their own wallet.

    • packages/contracts/src/NuntioTeamFactory.sol
    • 108 lines
    • 13 tests, plus 3 for its deploy script
  • CallerProbe.sol

    A throwaway diagnostic that records what a contract observes as its caller during a real transaction. It exists so that no authorization code has to assume the answer. It is state changing rather than a view, because a read reports what a node computes and not what the chain records.

    • packages/contracts/src/CallerProbe.sol
    • 130 lines
    • 9 tests
    • docs/research/CONSTANTS.md
  • The assistant

    A constrained tool caller that emits a typed payment intent and nothing else. It holds no key, builds no calldata and picks no address. The pipeline runs catalog, provider, shape, resolution and assembly in that order, and the first stage refuses a request before the model is ever called.

    • packages/agent/
    • 260 tests across 12 files
  • Service resolution in the SDK

    A lookup has four outcomes: known, unknown, suspended, and unreadable. Unreadable is the one that gets deleted, and folding it into unknown would let a rate limited node report a real merchant as invented, which is the most misleading direction that failure can take.

    • packages/sdk/src/registry.ts
    • 206 lines
    • 66 tests in the package, plus 4 against the live chain
  • The web application

    Onboarding, policies, spenders, approvals, activity, the assistant screen, the Sandbox and teams: creating your own, invitation links, and owner actions signed by the owner’s own wallet. It includes the held and declined states that the product exists to show. A blocked payment is drawn as a success state, because that is what it is. Payment history reads every account deployment through Envio HyperSync, and each payment shown is read back from its own onchain receipt.

    • apps/web/
    • 996 tests, plus 22 against the live chain
  • Persistence

    PostgreSQL for the state a contract cannot hold: agents, jobs, payment intents, an append-only audit trail, Sandbox joins, and the teams people create with their invitations. Duplicate intents and duplicate chain events are refused by the database itself, not by application code remembering to check. Nothing here authorizes a payment; the contract still does that.

    • packages/db/
    • 4 migrations
    • 114 tests, plus 54 against a real database
  • The agent worker

    The process that lets an assistant act while nobody is signed in. It records an intent before it signs and a transaction hash the moment one exists, so a crash mid-payment is recovered by reading the chain rather than by sending again. It has run live in observe mode only, recording what it would pay and signing nothing.

    • apps/worker/
    • 240 tests, plus 64 against a real database
  • Agent identity

    Each agent gets its own wallet, created by Privy under a key that only the worker holds, so the web server can set an agent up and can never sign for it. The wallet’s own policy allows payments from this account and approval requests for itself, and nothing else; the contract still decides every payment. Its signing and policy code runs live for the Sandbox operator (evidence 10); no agent has been created under the worker’s key yet.

    • packages/agents/
    • 72 tests

494 Solidity tests across 15 suites and 1,748 TypeScript tests across 114 files, all passing on 1 October 2026. Each rule the contracts enforce carries a test that it holds and a test that the invalid case is rejected.

Carried in from earlier work

Two forks are planned. Neither has landed.

The same authors shipped two payment products before this one. Here is what that gives Nuntio, stated as it stands today rather than as it is meant to end up.

  • A payment router, decided and not yet taken

    The plan is to fork the non custodial transfer router and the handle registry from the team’s earlier private EVM product, and use them as the base for moving money. That fork is not in this repository. The contracts directory holds five files and all five are listed above as new. When the fork arrives, each file it brings will carry a header naming where it came from.

    • docs/hackathon/PROVENANCE.md
    • packages/contracts/src (5 files)
  • A transaction lifecycle layer, same status

    The intent is to lift the four way outcome type that separates a revert from a timeout from an unreachable node, because Monad’s speculative state needs exactly that distinction and writing it again would be writing it worse. It has not been lifted yet.

    • docs/hackathon/PROVENANCE.md
    • 0 files in the tree
  • Conventions, reimplemented rather than copied

    What has actually crossed over is a list of rules the earlier products paid for: a version field in the first commit of every contract, a failed read that returns null instead of zero, money as base units until the render boundary, and cached balances that never authorize a payment. Those are written again here, in a different language, against a different chain.

    • CONTRIBUTING.md
    • NuntioAccount.sol VERSION
    • ApprovalQueue.sol VERSION
  • Third party code the contracts compile against

    Used, not written. These four, and the ERC-20 interface they work through, are the only external Solidity in the build today.

    • OpenZeppelin Contracts 5.7.0
    • Ownable2Step, SafeERC20, Pausable, ReentrancyGuard
  • Third party addresses recorded but not yet called

    The MetaMask delegation framework, Permit2, the x402 proxies and the Chainlink forwarders are written down with the date each was probed on chain. No code in this repository calls any of them yet, and saying otherwise would be counting a plan as a feature.

    • packages/sdk/src/addresses.ts
    • docs/research/CONSTANTS.md

Verified on chain

Checked against Monad testnet, not only in tests.

Chain id 10143. Every address here was read back from the chain before it was allowed into the codebase, and this page reads them from the same file the app does.

ServiceRegistry

0x19861745c5292a6911CbF6535e396d4DD18fef0DView on the Monad explorer

Deployed 10 September 2026 at block 61,434,750. Bytecode present, version reads 1, six demonstration services registered.

NuntioAccount

0xa82ddeA9f0BF9F8C803B2f5905EdC6CDBE96aFc4View on the Monad explorer

The contract that holds the money and decides every payment. Deployed at block 65,974,230 on 27 September 2026 as the second version, migrated from the first with its spenders, services and funds. A payment inside the rules settles here and a payment outside them reverts here.

ApprovalQueue

0xFc3148D8AD23f7238a868d560eB1d7b11B6b1096View on the Monad explorer

Where a refused payment goes to ask the owner. Deployed at block 65,974,226 and bound to this account for good. Filing a request, agreeing to one and settling one are three separate transactions against this address.

CallerProbe

0xfddD1C5B54D5D70485FF640d8C7a75e5f13F7123View on the Monad explorer

The plain account baseline and a sponsored call are both recorded. With the fee paid for it, the contract still sees the paying wallet as the sender, not the service that paid the fee (evidence 09).

  • All six registered services resolve

    Each name held on chain matches the merchant name in the application’s fixtures exactly, and each slug matches the seeding script exactly. They had drifted to no overlap at all, which would have made every payment the assistant could propose revert for a reason that has nothing to do with the assistant.

    • docs/hackathon/evidence/03-every-fixture-merchant-resolves.md
    • 6 of 6
  • An id nobody registered reverts

    Asking the live registry for an invented merchant returns the selector for an unknown service followed by the 32 bytes of the id echoed back. The id the SDK decoded is identical to the one the chain returned, which is what makes the log line usable as evidence afterwards rather than only as a message.

    • docs/hackathon/evidence/01-unknown-service-reverts.md
    • 0xcab75967
  • Both refusal layers fire, and which one fires is recorded

    A merchant outside the spender’s list is refused by the schema before any network call. A merchant on the list that was never registered on chain gets past the schema and is refused by the registry. They catch different mistakes, and the second one is the one that survives an error in our own data.

    • docs/hackathon/evidence/04-two-layers-refuse-a-hallucination.md

Not done

What this page will not claim.

A provenance page that lists only the wins is not evidence of anything.

  • Paying or creating a team from your own browser wallet, fee paid, is not yet observed

    Sponsored sends are switched on. A Privy server wallet and the Sandbox operator have both sent with no funds of their own for fees, a server wallet has created and taken ownership of a team the same way, and each time the contract saw the wallet as the sender. One path has not been observed: a person’s own embedded wallet paying, or creating a team, from the browser. Until it has, treat never paying a network fee as designed, not demonstrated.

    • docs/hackathon/evidence/09-sponsored-server-wallet.md
    • docs/hackathon/evidence/10-sandbox-operator.md
    • docs/hackathon/evidence/12-team-creation.md
  • The flagship’s owner key lives on a server

    Owner actions on the flagship account are signed by a scoped server key, and the Sandbox is owned by an operator wallet the server signs for under a narrow policy. The flagship’s key is bound to an ABI with no pay in it, and the single function it has that moves money takes an approval id rather than a recipient, so it can only settle something the owner already agreed to. Teams do not work this way: a team’s owner signs every owner action with their own wallet, and no server key can act for a team.

    • apps/web/lib/owner-server.ts
    • apps/web/app/api/owner/approvals/route.ts
  • The six services are stand-ins, not merchants

    Each registered service pays its own address, derived from its name, so six names are six recipients and a receipt can tell them apart. Nobody holds the keys to those addresses and no real business is behind them. Test AUSD sent to one stays there. It proves that the right recipient is resolved and paid, not that a merchant was.

    • packages/contracts/script/DistinctPayees.s.sol
    • docs/research/CONSTANTS.md
  • Nothing here has been independently audited

    The contracts carry tests for each rule and for the case each rule is meant to reject. That is not the same thing as review by somebody with no stake in the answer.

    • 494 Solidity tests
    • 0 external reviews

Checking this yourself

  • Run forge test in packages/contracts for the Solidity counts, and pnpm -r test at the repository root for the TypeScript ones.
  • docs/hackathon/PROVENANCE.md is the in repository version of this page, and docs/hackathon/evidence/ holds the raw transcripts these summaries were taken from.
  • docs/research/CONSTANTS.md is the single source for every address, with the date each one was probed. Nothing enters the codebase without appearing there first.
  • The commit history covers the whole build window and carries the reasoning for each decision, including the ones that were later reversed.