{"_id":"@0xlucasliao/mpp","name":"@0xlucasliao/mpp","dist-tags":{"test":"0.0.1","latest":"0.0.1"},"versions":{"0.0.1":{"name":"@0xlucasliao/mpp","description":"BNB Chain EVM Charge implementation of the Machine Payments Protocol (mppx).","version":"0.0.1","type":"module","license":"MIT","repository":{"type":"git","url":"git+https://github.com/bnb-chain/mpp-sdk.git"},"sideEffects":false,"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","src":"./src/index.ts","default":"./dist/index.js"},"./server":{"types":"./dist/server/index.d.ts","src":"./src/server/index.ts","default":"./dist/server/index.js"},"./client":{"types":"./dist/client/index.d.ts","src":"./src/client/index.ts","default":"./dist/client/index.js"}},"scripts":{"build":"zile","changeset:publish":"zile publish:prepare && changeset publish && zile publish:post","changeset:version":"changeset version && vp fmt .","check":"vp lint --fix && vp fmt --write .","check:ci":"vp lint && vp fmt --check .","check:examples":"pnpm -r --filter \"./examples/*\" check:types","check:types":"tsgo -b && tsgo --noEmit -p tsconfig.test.json && pnpm check:examples","deps":"pnpx taze -r --no-ignore-other-workspaces --ignore-paths node_modules","deps:ci":"pnpx actions-up","dev":"zile dev","test":"vp test --config vite.config.ts","test:live":"vp test --config vite.config.ts --project live"},"peerDependencies":{"mppx":"^0.6.28","viem":"^2.51.0"},"devDependencies":{"@changesets/cli":"^2.31.0","@types/node":"^24.0.0","@typescript/native-preview":"7.0.0-dev.20260323.1","fast-check":"^4.8.0","mppx":"^0.6.28","tsx":"^4.21.0","typescript":"~5.9.0","viem":"^2.51.0","vitest":"*","vp":"npm:vite-plus@~0.1.17","zile":"^0.0.25"},"packageManager":"pnpm@11.0.8","engines":{"node":">=22"},"pnpm":{"onlyBuiltDependencies":["bufferutil","keccak","utf-8-validate"]},"keywords":["mpp","mppx","bnb","bnb-chain","evm","payments","402","http-payment-auth"],"_id":"@0xlucasliao/mpp@0.0.1","gitHead":"4fe210ee2c25eb7315c7ac97b0ff063f06449991","bugs":{"url":"https://github.com/bnb-chain/mpp-sdk/issues"},"homepage":"https://github.com/bnb-chain/mpp-sdk#readme","_nodeVersion":"23.7.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-e7Pa50gKcXtema637mNtA6kby4hLYGLeC7Diq+NTTQqyBtKMj5y7rrLotxm7elUxl5dSq80hmCqhM68PyZ9hhg==","shasum":"d5408976768c5263b06b6aa26d11bb97bdfe533d","tarball":"https://registry.npmjs.org/@0xlucasliao/mpp/-/mpp-0.0.1.tgz","fileCount":181,"unpackedSize":1132056,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFhR07w44wbYHmtVbk5NbBtEuxJ62yKjhHtuZgC5SXB8AiAEj346rKoUODylY48EpJx7+Wlykl2CL9gsfcYt6rgzQQ=="}]},"_npmUser":{"name":"0xlucasliao","email":"lucas.liao@11a0.com"},"directories":{},"maintainers":[{"name":"0xlucasliao","email":"lucas.liao@11a0.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mpp_0.0.1_1780836798112_0.15377503418574623"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-07T12:53:18.013Z","0.0.1":"2026-06-07T12:53:18.340Z","modified":"2026-06-07T12:53:18.504Z"},"maintainers":[{"name":"0xlucasliao","email":"lucas.liao@11a0.com"}],"description":"BNB Chain EVM Charge implementation of the Machine Payments Protocol (mppx).","homepage":"https://github.com/bnb-chain/mpp-sdk#readme","keywords":["mpp","mppx","bnb","bnb-chain","evm","payments","402","http-payment-auth"],"repository":{"type":"git","url":"git+https://github.com/bnb-chain/mpp-sdk.git"},"bugs":{"url":"https://github.com/bnb-chain/mpp-sdk/issues"},"license":"MIT","readme":"# @bnb-chain/mpp\n\nEVM Charge implementation of the [Machine Payments Protocol (mppx)](https://github.com/wevm/mppx) — `draft-evm-charge-00`.\n\nBrings the BNB Chain ecosystem (BSC, opBNB) plus the wider EVM landscape into the mppx HTTP payment authentication framework. Composes with `Mppx.create()` to expose `402 Payment Required` flows for `permit2`, `transaction`, `hash`, and `authorization` (EIP-3009) credentials.\n\n## Capabilities\n\n| Area                  | Supported                                                                                                                      |\n| --------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| **Credential types**  | `authorization` (EIP-3009), `permit2` (single + batch with splits), `transaction` (EIP-1559), `hash`                           |\n| **Challenge binding** | `mppx-managed` (under `Mppx.create`), `mppx-hmac` (bare verify), `stored-lookup` (draft §6 zero-deviation)                     |\n| **Settlement**        | Server-side broadcast for `permit2` / `authorization` (settlement signer pays gas); payer-broadcast for `hash` / `transaction` |\n| **Tokens / chains**   | Curated `(chain, token)` matrix — see [Tokens](#tokens) / [Chains](#chains)                                                    |\n| **Receipt**           | `draft §7.6` `Payment-Receipt` via a browser-safe codec (`buildEvmReceipt` / `serializeEvmReceipt`)                            |\n| **Replay protection** | 3-state atomic store (inflight / consumed / rejected); durable backend required in production                                  |\n\nAll four credential paths are live end-to-end (see `examples/charge-server` + `examples/charge-demo`). For the full picture see [`docs/`](docs/) — architecture, spec compliance / extensions, replay store, and example walkthroughs. Release notes are managed with [Changesets](https://github.com/changesets/changesets) (`.changeset/`); `CHANGELOG.md` is generated at publish time.\n\nv1 limits: curated token presets only (no arbitrary BYO ERC-20), and the SDK adds one spec extension (`methodDetails.permit2Spender`) that `draft-evm-charge-00` doesn't define but Permit2 settlement requires — see [`docs/spec-compliance.md`](docs/spec-compliance.md).\n\n## Install\n\n```bash\npnpm add @bnb-chain/mpp mppx viem\n```\n\nPeers: `mppx ^0.6.28`, `viem ^2.51.0`. Node ≥ 22 (development uses Node 22 stable).\n\n## Quickstart\n\n```ts\nimport { Mppx } from 'mppx/server'\nimport { chargeAsync } from '@bnb-chain/mpp/server'\nimport { privateKeyToAccount } from 'viem/accounts'\nimport { createRedisReplayStore } from './your-replay-store-adapter' // see § \"Replay store\" below\n\nconst settlement = privateKeyToAccount(process.env.SETTLEMENT_PRIVATE_KEY as `0x${string}`)\n\nconst handler = Mppx.create({\n  methods: [\n    await chargeAsync({\n      chain: 'ethereum',\n      token: 'USDC',\n      recipient: '0xYourMerchantAddress',\n      credentialTypes: ['permit2', 'transaction', 'hash'],\n      settlementAccount: settlement,\n      challengeBinding: { mode: 'mppx-managed' },\n      // Replay store — REQUIRED in production. See § \"Replay store\" below\n      // for what counts as a durable atomic store and the dev-only default.\n      store: createRedisReplayStore({ url: process.env.REDIS_URL! }),\n    }),\n  ],\n  secretKey: process.env.MPPX_SECRET_KEY!,\n  // No `transport` here — chargeAsync() factory auto-wires evmHttpTransport\n  // on the per-method transport slot (spec §13.4.1 C2 auto-wire).\n})\n\n// Wire into your HTTP server (Hono / Express / Next.js — see mppx docs).\n```\n\n### Replay store\n\n`params.store` is the **replay store** — the durable atomic backend that\nguarantees a given credential settles at most once (`reserve` → settle →\n`markConsumed`). It is independent of `challengeBinding`: the same store\nbacks `mppx-managed`, `mppx-hmac`, and `stored-lookup` modes. Spec §9 +\n[draft-evm-charge-00](https://paymentauth.org/draft-evm-charge-00.html)\nboth require:\n\n1. The store MUST be **durable across processes / pods** — a single Node\n   process Map is not enough on multi-pod deployments (replay protection\n   silently becomes per-pod, allowing N concurrent settlements of the\n   same credential).\n2. The reserve / mark-consumed transitions MUST be **atomic** — `reserve`\n   under a key that's already `consumed` MUST fail without racing.\n\nWhat the SDK enforces and what it doesn't:\n\n- **`NODE_ENV=production` + `params.store` omitted** → `preflightCharge`\n  throws at startup. Production deployments MUST pass a store explicitly.\n- **`NODE_ENV=production` + any `params.store`** → accepted on presence\n  alone. The SDK can't structurally tell a Redis client from a Map\n  wrapper across the FFI boundary, so durability is a deployment-side\n  claim. Honoring spec §9 is on you; the SDK just makes sure you\n  remembered to wire something.\n- **`NODE_ENV=development` / unset + omitted** → defaults to\n  `Store.memory()` (mppx in-process) with a one-time `console.warn`,\n  so the gap is visible before any production cutover.\n- **`NODE_ENV=test` + omitted** → silent default to memory (no log noise\n  in `vitest` / `vp test` runs).\n\nSuggested durable backends:\n\n- **Redis** — `SET key value NX PX <ttl>` for atomic reserve; works on\n  Upstash, AWS ElastiCache, self-hosted.\n- **Postgres** — `INSERT ... ON CONFLICT DO NOTHING` for atomic reserve;\n  works on Neon, Supabase, RDS.\n- **Cloudflare KV / DO** — `put` with `conditional-write` for KV, or\n  Durable Objects' single-writer model for stronger consistency.\n\n`Store.memory()` from mppx is acceptable **only** for tests and local\nsingle-process development; it MUST NOT back a production deployment\nregardless of how many replicas are running (spec §9).\n\nFor routes that take human-readable amounts, the top-level barrel exports `chargeFromDecimal`:\n\n```ts\nimport { chargeFromDecimal } from '@bnb-chain/mpp'\n\nconst request = chargeFromDecimal({ amount: '1.50', decimals: 6 })\n// -> { amount: '1500000' }\n```\n\n## Spec Compliance\n\nThis SDK implements [`draft-evm-charge-00`](https://paymentauth.org/draft-evm-charge-00.html) layered on `mppx@0.6.28` (commit [`5aed74b`](https://github.com/wevm/mppx/tree/5aed74bfe46315ff3f27524ea8bb72e251bf771d)). The compliance choices + the one spec extension are summarized below; full detail (incl. `permit2Spender`) lives in [`docs/spec-compliance.md`](docs/spec-compliance.md).\n\n### 1. Challenge binding\n\nThis deployment uses the `challengeBinding.mode` configured on `ServerParameters`. Three modes are supported:\n\n- `'mppx-managed'` — Compose with `Mppx.create()`'s HTTP entry. mppx automatically runs `Challenge.verify` + `Expires.assert`; the SDK helper only enforces the `method='evm'` + `intent='charge'` guards.\n- `'mppx-hmac'` — Bare `Method.toServer(...).verify` path. The SDK helper runs the full `Challenge.verify({ secretKey })` + `Expires.assert` chain because `Mppx.create()` is not in the pipeline. Use this for custom integrations.\n- `'stored-lookup'` (draft §6 zero-deviation) — the deployment persists every issued challenge via `rememberChallenge(challengeStore, challenge)` at issuance time. On verify, the SDK helper re-derives the canonical wire form of each auth-param field from the inbound credential and constant-time compares against the stored snapshot. Standalone wrt HMAC — the deployment can run completely without a server secret. Production requires a durable `Store.AtomicStore<ChallengeItemMap>` backend (Redis / Postgres / Cloudflare KV); `Store.memory()` is test/dev only.\n\n### 2. Receipt compatibility\n\n```\ndraft-evm-charge-00 §7.6 requires EVM Charge receipts to include\n`method`, `challengeId`, `reference`, `status`, `timestamp`, `chainId`,\nand optionally `externalId`.\n\nCurrent mppx `Receipt.Schema` does not preserve `challengeId` / `chainId`.\nv1 ships a SDK-provided `evmHttpTransport` and the `charge(...)` / `chargeAsync(...)`\nfactory wires it on the per-method transport slot automatically — deployments\ndo NOT need to (and should not) configure `Mppx.create({ transport })` for\nthis. The resulting `Payment-Receipt` header preserves the full EVM Charge\nreceipt payload deterministically, independent of any future change to mppx\ndefault `Receipt.serialize` behavior.\n```\n\nNo deployment-side wiring is required — the Quickstart example deliberately\nomits `transport` from `Mppx.create`.\n\n### 3. v1 token support\n\n```\nv1 token support:\n- v1 only supports curated token presets.\n- Custom ERC-20 / BYO token is not supported in v1.\n- `currency` on wire is always the resolved curated ERC-20 address.\n- BYO token metadata, token registry input, EIP-3009 probing, and\n  arbitrary ERC-20 support are future work.\n\nPreset name semantics (hard rule):\n- `USDC` preset means Circle native USDC only.\n  BSC has no Circle native USDC; the curated matrix does NOT include\n  (`bsc`, `USDC`). Binance-Peg USDC must not be represented as `USDC`\n  in v1; if introduced later it must use a distinct preset name.\n- Bridged / wrapped variants of `USDT`, `EURC`, `FDUSD`, `U` are\n  subject to the same rule: do not reuse the native preset name.\n- `FDUSD` preset means the First Digital Labs \"First Digital USD\"\n  token (BSC mainnet `0xc5f0...6409`).\n- `U` preset means the official \"$U\" / United Stables token (BSC\n  mainnet `0xcE24...6666`). Symbol on-chain is the single letter `U`;\n  the product name is \"$U\". The BSC testnet sibling deploy at\n  `0x2Ae9...0a66` does NOT implement EIP-3009 (different deployment)\n  and is intentionally absent from the matrix.\n- `TEST_USDT` is a testnet-only preset. It MUST NOT appear in any\n  mainnet curated matrix entry, and it MUST NOT be treated as Tether\n  official mainnet `USDT`. Its contract addresses come from testnet\n  deployments (mock / third-party / self-deployed) that have no\n  official Tether provenance; do not rely on EIP-3009, decimals,\n  or mint authority assumptions from mainnet `USDT`.\n```\n\n### 4. `permit2Spender` (spec extension)\n\n`draft-evm-charge-00` does not define a `methodDetails.permit2Spender` field, but Permit2 settlement requires one: Permit2 hashes `msg.sender` (the on-chain caller) as the EIP-712 `spender`, so the payer must sign Permit2 typed data with the settlement signer's address or the on-chain `permitWitnessTransferFrom` reverts `InvalidSigner`. The SDK adds `permit2Spender` to `methodDetails` (OPTIONAL on the wire; REQUIRED for `permit2` credentials) and the server factory injects it automatically from `settlementAccount.address`. It affects only the `permit2` path — `hash` / `transaction` / `authorization` are unaffected. Full rationale + the verifier cross-check in [`docs/spec-compliance.md`](docs/spec-compliance.md) and [`docs/adr/0001-permit2-spender.md`](docs/adr/0001-permit2-spender.md).\n\n## Tokens\n\nThe SDK ships a curated matrix — every `(chain, token)` pair lands alongside its explorer-verified address, on-chain `decimals()` confirmation, and matrix-lock unit tests (`src/server/curated.test.ts`). EIP-3009 entries additionally have their domain `name`/`version` derived from on-chain `DOMAIN_SEPARATOR()` and locked in the same test file. A newly added pair ships with `authorization` **off** (advertising `permit2` / `transaction` / `hash`) until that on-chain `transferWithAuthorization` + `DOMAIN_SEPARATOR()` probe confirms EIP-3009 support and the exact domain — inferring the domain from the token symbol would be a verification bug. Live settlement tests are added separately, alongside the corresponding test fixtures, and are gated under the `live` vitest project (see `test/live/*.live.test.ts`).\n\nThe table below lists the **EIP-3009-enabled** anchors (where the `authorization` path is live). Broader issuer-native stablecoin coverage (probe-gated, `authorization` off) is summarized under [Expanded coverage](#expanded-coverage).\n\n| Chain       | Token            | Contract                                                                                           | Decimals | EIP-3009                                                                       |\n| ----------- | ---------------- | -------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------ |\n| ethereum    | USDC             | [`0xa0b8...eb48`](https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48)         | 6        | yes (Circle native, domain `USD Coin` / `2`)                                   |\n| ethereum    | USDT             | [`0xdac1...1ec7`](https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7)         | 6        | no                                                                             |\n| base        | USDC             | [`0x8335...2913`](https://basescan.org/address/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913)         | 6        | yes (Circle native, domain `USD Coin` / `2`)                                   |\n| bsc         | BINANCE_PEG_USDT | [`0x55d3...7955`](https://bscscan.com/address/0x55d398326f99059ff775485246999027b3197955)          | 18       | no                                                                             |\n| bsc         | FDUSD            | [`0xc5f0...6409`](https://bscscan.com/address/0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409)          | 18       | yes (First Digital Labs, domain `First Digital USD` / `1`)                     |\n| bsc         | U                | [`0xcE24...6666`](https://bscscan.com/address/0xcE24439F2D9C6a2289F741120FE202248B666666)          | 18       | yes (United Stables `$U`, domain `United Stables` / `1`)                       |\n| sepolia     | USDC             | [`0x1c7D...7238`](https://sepolia.etherscan.io/address/0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238) | 6        | yes (Circle native, domain `USDC` / `2`) — testnet, see `examples/charge-demo` |\n| bsc-testnet | TEST_USDT        | TODO (pin verified contract before live tests)                                                     | 18       | no                                                                             |\n\n### Expanded coverage\n\nIssuer-native stablecoins curated across the supported chains. These advertise `permit2` / `transaction` / `hash`; `authorization` (EIP-3009) is **off pending a per-chain probe** (see the note above). Contract addresses + decimals for every pair live in [`src/server/curated.ts`](src/server/curated.ts) and are locked by [`src/server/curated.test.ts`](src/server/curated.test.ts).\n\n- **Circle USDC** — `arbitrum`, `optimism`, `polygon`, `avalanche`, `linea`, plus testnets `base-sepolia`, `arbitrum-sepolia`, `optimism-sepolia`, `polygon-amoy`, `avalanche-fuji`, `linea-sepolia`. Source: [Circle USDC addresses](https://developers.circle.com/stablecoins/usdc-contract-addresses).\n- **Circle EURC** — `ethereum`, `base`, `avalanche`, plus testnets `sepolia`, `base-sepolia`, `avalanche-fuji`. Source: [Circle EURC addresses](https://developers.circle.com/stablecoins/eurc-contract-addresses).\n- **PayPal USD (PYUSD)** — `ethereum`, `arbitrum`, plus testnets `sepolia`, `arbitrum-sepolia`. Source: [Paxos PYUSD](https://docs.paxos.com/stablecoin/pyusd/mainnet).\n- **Paxos USDP / USDG** — `ethereum`. Source: [Paxos USDP](https://docs.paxos.com/guides/stablecoin/usdp/mainnet) / [Paxos USDG](https://docs.paxos.com/stablecoin/usdg/mainnet).\n- **First Digital USD (FDUSD)** — `ethereum`, `arbitrum` (plus the existing `bsc` entry, which is EIP-3009-probed). Source: [First Digital Labs](https://www.firstdigitallabs.com/fdusd).\n- **Tether USD₮** — `avalanche` only (issuer-native; bridged L2 USDT on Arbitrum/Optimism/Polygon is deliberately excluded). Source: [Tether supported protocols](https://tether.to/en/supported-protocols/).\n- **BNB Chain (BSC)** — native EIP-3009 tokens `FDUSD` and `U` are in the table above. Bridged stablecoins use distinct `BINANCE_PEG_*` names so they never wear native-issuer semantics: `BINANCE_PEG_USDC` (`0x8AC7…580d`, **18 dec**, not Circle-native USDC's 6), `BINANCE_PEG_USDT` (BSC-USD, `0x55d3…7955`, 18 dec — migrated off the bare `USDT` alias, which now means native Tether only, e.g. Ethereum), and `BINANCE_PEG_DAI` (`0x1AF3…DBc3`, 18 dec) — all standard BEP-20, no EIP-3009.\n- **opBNB** — deliberately **not** in the default matrix yet. Stablecoin provenance there (native vs bridged, exact addresses, decimals) must be cross-verified against the BNB Chain bridge/token list, opBNBScan verified-contract pages, and on-chain `decimals()` / `symbol()` / `name()` probes before any entry lands.\n\n## Chains\n\n| Preset                                                                      | chainId | Default confirmations | Notes        |\n| --------------------------------------------------------------------------- | ------- | --------------------- | ------------ |\n| ethereum                                                                    | 1       | 12                    |              |\n| base                                                                        | 8453    | 1                     |              |\n| arbitrum                                                                    | 42161   | 1                     |              |\n| optimism                                                                    | 10      | 1                     |              |\n| polygon                                                                     | 137     | 5                     | reorg buffer |\n| avalanche                                                                   | 43114   | 1                     |              |\n| linea                                                                       | 59144   | 1                     |              |\n| bsc                                                                         | 56      | 3                     | reorg buffer |\n| opbnb                                                                       | 204     | 1                     |              |\n| sepolia / _-sepolia / _-amoy / avalanche-fuji / bsc-testnet / opbnb-testnet | various | 0                     | dev velocity |\n\nPermit2 deployment is auto-probed at `preflightCharge` time via `eth_getCode` against the resolved address. v1 does not open arbitrary BYO chain — `rpcUrl` and `chainOverride` may only override an existing preset's RPC / viem `Chain` metadata.\n\n## Scripts\n\n```bash\npnpm install        # via corepack, pnpm@11.0.8\npnpm check          # vp lint --fix + vp fmt --write\npnpm check:ci       # vp lint + vp fmt --check\npnpm check:types    # tsgo -b + tsgo -p tsconfig.test.json + example workspaces\npnpm test           # vp test --config vite.config.ts (unit + interop + live)\npnpm build          # zile -> dist/\n\n# Vitest projects: pass flags through with `--` so pnpm doesn't\n# intercept them as its own options.\npnpm test -- --project unit       # source + test/unit\npnpm test -- --project interop    # cross-impl / wire compat\npnpm test -- --project live       # gated on testnet RPC + signing keys\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-35f6b93dfdb216dec3850f3003481385"}