npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sealed-monad

v0.3.2

Published

SDK and MCP server for Sealed: sealed-offer negotiation between ERC-8004 agents on Monad, with a public desk where any agent can negotiate a paid deal without MON.

Readme

sealed-monad

SDK and MCP server for Sealed: two AI agents agree a price on Monad without either one seeing the other's limit first. Offers go on-chain as salted, domain-separated hashes; a clearing relay tells both sides only whether their numbers crossed; one atomic EIP-712 settlement discloses both offers and settles at the midpoint. Admission is checked against ERC-8004 reputation through ReputationGate.

The package is built from the reference code in the Sealed repository (agents/sealed, agents/relay), bundled so it stands alone. Its encoders are tested against the contracts: they reproduce every commitment of negotiation #4 on Monad testnet and the contract's own settlement digest and policy hash.

Install

npm install sealed-monad

Node 20 or later. Runtime dependencies: ethers 6, express 5 (party server), @modelcontextprotocol/sdk and zod (MCP server).

Deployments

Three SealedNegotiation contracts live on Monad testnet (chain 10143). v3 is the newest: it pays at settlement and the public desk runs on it. The read client defaults to v2 (MONAD_TESTNET) and reads v1 and v3 too; verifySettlement picks the version from the transaction.

| | v3 (MONAD_TESTNET_V3) | v2 (default, MONAD_TESTNET_V2) | v1 (MONAD_TESTNET_V1) | |---|---|---|---| | SealedNegotiation | 0x98e06434C94Ab14eBE0DDbb2f601113Bd8439205 | 0xb9D7c55f77a074f06F449766895eB5b978C273C4 | 0xAdBd2619c8f51873B6dB131843cce3403E0869dD | | ReputationGate | Same as v2 | 0xF43171CE393a79717B35fF689e814B452583E3Da | 0xD7c68cd2197124A7BF3a27467917aBCB982Cc04A | | Commits | As v2; a commit can also be submitted by anyone carrying the party's EIP-712 Commit signature (commitOfferBySig) | One per side per round, at most one round ahead; a second commit before the other side catches up reverts with AlreadyCommitted(index) | Any re-commit replaces that side's commitment and bumps its index | | Settled pair | As v2 | The last round both sides committed, signed with that round as both indices. One side committing ahead cannot void it | Both sides' latest commitments and indices | | Admission policy | As v2, and both agents sign its hash in the OpenNegotiation terms that open the negotiation | policyHash = keccak256(abi.encode(policy)) stored in getNegotiation and emitted in NegotiationCreated; admissionPolicyHash(policy) view | Not stored | | Payment | settle moves price × scale tUSD from the buyer's escrow: the seller receives it less 0.25%, the treasury the 0.25% | None | None | | Published demo runs | Negotiations #1 to #7, the paid ones and the desk's | Negotiations #3 to #7 | Negotiations #1 to #10, the gate refusal, the Privy mandate |

The ERC-8004 registries are the same for all three. The EIP-712 domain name and version are the same too; only verifyingContract differs, so a commitment for one contract never matches another.

Negotiate at the desk in two minutes

The Sealed desk at https://desk.avanc3.pe sells the demo's API access on the paid contract (v3). Any agent can negotiate there without MON: the desk registers an ERC-8004 identity for your wallet, funds its escrow with test dollars, and pays every gas fee. You sign; you never send a transaction.

claude mcp add sealed -- npx -y -p sealed-monad sealed-mcp

Then ask Claude Code something like: "Use the Sealed desk to buy the API access. My limit is 4300. Negotiate." It calls desk_info, desk_open_negotiation with your limit, then desk_submit_offer once per round. A round that crosses settles at the midpoint, and the price moves from your escrow to the seller, less the 0.25% fee, in the same transaction.

What happens on your machine:

  • On first use, the server creates a testnet key in ~/.sealed/guest-key.json (or SEALED_HOME). It never holds MON. SEALED_GUEST_KEY uses a key of your own instead.
  • Your limit stays in the server process. A settlement publishes both offers, so an offer equal to your limit is refused, as is one above it or below your previous offer, before anything is signed. Only the signed commitment, then the offer at reveal, go to the desk.
  • Before revealing an offer to the desk, the server reads the chain itself and waits until both commitments of the round are on-chain. The desk's seller has committed before anyone sees your number.
  • The desk runs the relay and the seller, so it sees both numbers of each round, as any Sealed relay does. It cannot settle or move funds without your signature over your own committed number. The seller follows a fixed schedule published in agents/desk/seller.ts.

The same flow without MCP is DeskClient in the SDK, and scripts/desk-guest.ts in the repository.

SDK in ten lines

import { MONAD_TESTNET_V1, createReadClient, commitmentHash, newSalt, sealedDomain } from "sealed-monad";

const sealed = createReadClient(); // v2 on Monad testnet over its public RPC; pass { rpcUrl } to change it
const v1 = createReadClient({ deployment: MONAD_TESTNET_V1 });
const n = await v1.getNegotiation(4n); // "Settled", settledPrice 4190n, policyHash null on v1

const salt = newSalt(); // fresh every round; never log it, never reuse it
const commitment = commitmentHash({ domain: sealedDomain(), negotiationId: 1n, party: myWallet,
  commitIndex: 1, position: { offer: 4200n, salt } }); // sealedDomain() is v2

const report = await sealed.verifySettlement("0xb4f7adf14c5253ce89e16dd33f7d81365819262e6510148b4dbeb70fd88ce498");
console.log(report.ok, report.contractVersion); // true 1: recognized as a v1 settlement by its contract

commitmentHash makes no network call. You send commitment with commitOffer(negotiationId, commitment) from your agent's own wallet; the offer and the salt stay with your agent until settlement.

Public API

Commitments and authorizations

| Export | What it does | |---|---| | commitmentHash({ domain, negotiationId, party, commitIndex, position }) | keccak256(domainSeparator, negotiationId, party, commitIndex, offer, salt), equal to the contract's commitmentHash | | newSalt(), assertSalt(salt) | 32 bytes of fresh entropy; the check that a salt is 32 bytes | | domainSeparator(domain), sealedDomain(deployment?) | EIP-712 domain separator; the { chainId, verifyingContract } of a deployment (v2 by default) | | settleAuthorizationTypedData(domain, message) | Typed data for signTypedData, for the SettleAuthorization each agent signs every round. On v2 both indices are the round | | settleAuthorizationDigest(domain, message) | The digest, equal to the contract's settleAuthorizationDigest(id) for the settleable pair | | admissionPolicyHash(policy) | keccak256(abi.encode(policy)), equal to v2's admissionPolicyHash and to the policyHash it stores | | SETTLE_AUTHORIZATION_TYPES, SEALED_EIP712_DOMAIN_NAME, SEALED_EIP712_DOMAIN_VERSION | The EIP-712 types and domain name ("Sealed") and version ("1") |

Addresses and ABIs

| Export | What it does | |---|---| | MONAD_TESTNET | The read client's default deployment, v2: chain id 10143, public RPC, SealedNegotiation, ReputationGate, the ERC-8004 registries, the demo admission policy | | MONAD_TESTNET_V3, MONAD_TESTNET_V2, MONAD_TESTNET_V1, SEALED_DEPLOYMENTS | Each deployment, with its version; the list, newest first | | SEALED_NEGOTIATION_ABI, REPUTATION_GATE_ABI | Full human-readable v2 ABIs, checked against the compiled contracts. ethers: new Interface(abi); viem: parseAbi(abi) | | SEALED_NEGOTIATION_V1_ABI | The v1 ABI (twelve-field getNegotiation, no policyHash, no AlreadyCommitted). The gate ABI did not change | | IDENTITY_REGISTRY_ABI, REPUTATION_REGISTRY_ABI | The ERC-8004 functions and event Sealed reads or writes |

Read client (createReadClient(options?) returns a SealedReader for v2, or for options.deployment; no keys)

| Method | What it returns | |---|---| | getNegotiation(id, contract?) | Status (None, Open, Locked, Settled, Expired), wallets, agent ids, latest commitments and commit indices, deadline, settled price, terms hash, and layout (1 or 2). On v2 also policyHash and settleableRound, the round settle takes; both are null on v1. Words a later version appends come back raw in extra | | getCommitIndices(id) | { buyer, seller }; a side's next commit is its index plus one | | checkAdmission(agentId, { wallet?, policy? }) | Whether the wallet is the agent's ERC-8004 wallet, whether the agent clears the policy in ReputationGate, both together (admitted), and the policy's policyHash | | readReputation(agentId, { reviewers?, tag1?, tag2? }) | ERC-8004 getSummary over the trusted reviewers: count, average, decimals | | verifySettlement(txHash, { lookbackBlocks? }) | Every check below, each with pass or fail, on a v1, v2 or v3 settlement; on v3 also the payment (report.payment) | | negotiationCount() | Number of negotiations opened on this deployment |

verifySettlement needs only the settlement hash. It recognizes the contract from the transaction's target among the known deployments, and the version from the shape getNegotiation returns. It decodes the settle calldata, works out the settled pair (both latest commitments on v1; on v2 the last round both committed, reading a side that committed ahead from its commit transaction for that round), recomputes both commitments from the disclosed offers and salts and compares them with that pair and with the contract's own commitmentHash, checks the EIP-712 domain and that the digest over the pair equals the contract's, recovers both signers, checks that the offers cross, that NegotiationSettled carries the midpoint and that the contract recorded it, and checks that each settled commitment came from a commitOffer sent by its party carrying only the negotiation id and the hash. On v3 a commitment may instead come from commitOfferBySig, sent by anyone (the desk's operator, at the public desk): it checks that the party's own Commit signature covers that hash for that round and that the calldata holds nothing else. On v3 it also reads the payment terms and checks that Paid reports price * scale from the buyer's escrow with the 0.25% fee rounded up, and that the token's transfers in the same transaction paid the seller the amount less the fee and the treasury the fee. It walks back from the settlement in 100-block eth_getLogs windows, the most Monad's public RPC serves, 3,000 blocks by default.

Party server (option A in INTEGRATING.md: your agent, the Sealed relay)

| Export | What it does | |---|---| | serveParty(agent, token, port?) | Serves your agent on 127.0.0.1 with the relay's routes: /identity, /decide, /commit, /reveal, /authorize, and /rate if your agent rates counterparties. Every request needs Authorization: Bearer <token>; /reveal answers once per commitment | | partyHandler(agent, token) | The same routes as a Node request listener, to mount yourself behind TLS | | HttpParty.connect(url, token), serverUrl(server) | The relay's side of the wire, useful to test your agent | | Party, Decision, Reveal, AgentStep, Stance, STANCES | What your agent implements and returns |

import { serveParty, type Party } from "sealed-monad";

const agent: Party = { wallet, decide, commit, reveal, authorize }; // your model, your key
const server = await serveParty(agent, process.env.PARTY_TOKEN!); // a random secret, 32+ characters

Your agent commits on-chain with its own key in commit, signs in authorize, and decides when to stop. The relay never holds a key that can sign for it. On v2, commit once per round: a second commit in the same round reverts. scripts/agent-process.ts in the repository is a complete agent process built this way.

MCP server

sealed-mcp speaks MCP over stdio. The read tools hold no keys. The desk tools sign with the guest key above; SEALED_DESK_URL=off leaves only the read tools, keyless.

Claude Code:

claude mcp add sealed -- npx -y -p sealed-monad sealed-mcp

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "sealed": { "command": "npx", "args": ["-y", "-p", "sealed-monad", "sealed-mcp"] }
  }
}

To use another RPC endpoint, set SEALED_RPC_URL (or MONAD_RPC_URL), for example claude mcp add sealed -e SEALED_RPC_URL=https://... -- npx -y -p sealed-monad sealed-mcp. The server only ever shows the endpoint's origin, never its path or query, where providers put API keys.

| Tool | Input | Output | |---|---|---| | get_negotiation | negotiationId, optional deployment (v2 default, v1, v3) | Status, parties, agent ids, commitments, commit indices and next indices, deadline, settled price; on v2 policyHash and settleableRound | | check_admission | agentId, optional wallet, policy, deployment | Wallet check, policy check, admitted, the policy's hash | | read_reputation | agentId, optional reviewers, tag1, tag2 | Count and average of ERC-8004 reviews from those reviewers | | compute_commitment | negotiationId, party, commitIndex, offer, salt, optional deployment, chainId, verifyingContract | The commitment and domain separator, computed locally | | verify_settlement | txHash, optional lookbackBlocks | Every check of verifySettlement, with pass or fail; v1, v2 or v3 is picked from the transaction, and a v3 settlement includes the payment checks | | describe_protocol | none | How Sealed works, the three versions and their addresses, the demo policy | | desk_info | none | The desk's terms, unit, payment rule, seller list price and concession rule, contract, trust model | | desk_open_negotiation | limit | Registers a guest identity on first use, checks and signs the terms, opens a paid negotiation on v3 | | desk_submit_offer | offer, below your limit | Plays one round: commit, wait for both commitments on-chain, reveal, sign the settlement authorization. Returns whether it crossed and, if so, the price, amount, fee and transactions | | desk_status | none | Rounds, outcome and transactions of the negotiation opened from this session |

The same protocol text is also a resource, sealed://protocol.

What it does not do

  • The read tools cannot commit, sign, settle, expire or rate. Only the desk tools sign, with a testnet guest key, and only for the desk's negotiation on v3.
  • The SDK does not run a negotiation for you. The party server needs your own agent and your own key; the clearing relay itself, which pays gas for create, settle and expire, lives in the repository (agents/relay/clearingRelay.ts), not in this package.
  • compute_commitment is for checking published reveals. A live salt is a secret that belongs to its agent; do not paste one into a tool whose transcript others can read.
  • The relay is trusted with confidentiality: it sees both numbers of every round. It cannot forge or change a deal. The repository README explains the trust model in full.
  • Monad testnet only for now. A deployment is one object (SealedDeployment); pass your own to createReadClient({ deployment }).

Development

npm install
npm test                        # builds, then unit tests, the party server, and an MCP stdio smoke test, offline
npm run live                    # through the MCP server on Monad testnet: the v1 settlement of #4, reads on v1 and v2
npm run live -- <v2 settle tx>  # the same, plus a v2 settlement
npm run live:desk               # a paid negotiation at the public desk, through the MCP desk tools, with a fresh guest key

At the repository root, npx hardhat test test/sdk.test.ts deploys v2 and v3 locally and checks the encoders, the ABIs and the read client against them, including a settlement where one side committed a round ahead and a paid v3 settlement with commits sent by a relayer.

Changelog

  • 0.3.2: the desk client refuses an offer equal to the buyer's limit, since a settlement publishes both offers, and no longer sends the desk a stance with each commitment (it told the relay when an offer was the limit). desk_submit_offer takes only offer. The npm README lists v3 next to v1 and v2.
  • 0.3.1: verifySettlement and verify_settlement verify v3 settlements, including the desk's: commits submitted by signature (commitOfferBySig) and the payment (Paid, the seller's and the treasury's transfers). MONAD_TESTNET_V3 is a full deployment (version: 3) and is in SEALED_DEPLOYMENTS; SEALED_NEGOTIATION_V3_EXTRA_ABI; the MCP read tools take deployment: "v3".
  • 0.3.0: the desk tools (desk_info, desk_open_negotiation, desk_submit_offer, desk_status) and DeskClient: negotiate a paid deal on v3 at the public Sealed desk without MON. MONAD_TESTNET_V3 (SealedNegotiationV3, SealedTestUSD) and SEALED_DESK_URL. The protocol guide covers v3.
  • 0.2.0: v2 is the default (MONAD_TESTNET); v1 stays available as MONAD_TESTNET_V1 with SEALED_NEGOTIATION_V1_ABI. getNegotiation reads both layouts and returns policyHash, settleableRound and layout; admissionPolicyHash; verifySettlement handles v1 and v2 settlements; the MCP read tools take deployment.
  • 0.1.0: first release, v1.

License

MIT