ServiceRegistry
0x19861745c5292a6911CbF6535e396d4DD18fef0DView on the Monad explorerDeployed 10 September 2026 at block 61,434,750. Bytecode present, version reads 1, six demonstration services registered.
Testnet: free test dollars. The real Nuntio runs on Monad mainnet at nuntio.finance.
Provenance
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 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
Used, not written. These four, and the ERC-20 interface they work through, are the only external Solidity in the build today.
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.
Verified on chain
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.
Deployed 10 September 2026 at block 61,434,750. Bytecode present, version reads 1, six demonstration services registered.
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.
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.
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).
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.
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.
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.
Not done
A provenance page that lists only the wins is not evidence of anything.
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.
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.
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.
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.
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.