Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tempo-authorize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mppx': patch
---

Added Tempo authorize method support for deferred TIP-20 captures.
34 changes: 34 additions & 0 deletions .changeset/tip-1034-precompile-hash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
'mppx': patch
---

Added opt-in Tempo [TIP-1034](https://github.com/tempoxyz/tempo/blob/main/tips/tip-1034.md) precompile support for payment-channel sessions.

The default `tempo.session(...)` method continued to use the existing session backend. Applications opted into the new precompile-backed flow explicitly by using `tempo.precompile.session(...)` on the client and `tempo.precompile.Server.session(...)` on the server.

```ts
import { Mppx, tempo } from 'mppx/client'

const client = Mppx.create({
methods: [tempo.precompile.session({ account, maxDeposit: '10' })],
})
```

```ts
import { Mppx, tempo } from 'mppx/server'

const server = Mppx.create({
methods: [
tempo.precompile.Server.session({
amount: '1',
chainId,
currency,
recipient,
store,
unitType: 'request',
}),
],
})
```

This added channel ID, expiring nonce hash, voucher, ABI calldata, open/top-up validation, descriptor persistence, credential payload parsing, client credential builder, session manager, server verification, and server-driven fee-payer settle/close helpers for TIP20EscrowChannel precompile channels. It also changed precompile chain helpers to use `*OnChain` names, updated amount APIs to accept plain bigint values while validating uint96 bounds at encoding boundaries, updated precompile voucher signing inputs to match legacy session voucher signing, aligned precompile client session modules with the legacy client layout, and hardened precompile channel validation, atomic store updates, voucher-signing compatibility, finalized channel bookkeeping, and devnet precompile integration test setup.
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Standalone, runnable examples demonstrating the mppx HTTP 402 payment flow.

| Example | Description |
| --------------------------------------------- | ---------------------------------------------------- |
| [authorize](./authorize/) | Deferred capture authorization playground |
| [charge](./charge/) | Payment-gated image generation API |
| [session/multi-fetch](./session/multi-fetch/) | Multiple paid requests over a single payment channel |
| [session/sse](./session/sse/) | Pay-per-token LLM streaming with SSE |
Expand Down
33 changes: 33 additions & 0 deletions examples/authorize/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Authorize Playground

Deferred capture playground for Tempo authorizations.

The app opens real TIP-1034 authorize channels through the HTTP 402 flow. The table then lets the server capture partial chunks, capture and close, or void the authorization.

## Usage

The default target is a local Tempo dev container:

```bash
pnpm dev
```

Override the RPC if your container exposes a different port:

```bash
MPPX_RPC_URL=http://localhost:7545/1 pnpm dev
```

To point at hosted networks instead:

```bash
MPPX_NETWORK=devnet pnpm dev
MPPX_NETWORK=testnet pnpm dev
```

Optional environment:

```bash
MPPX_AUTHORIZE_CURRENCY=0x20c0000000000000000000000000000000000001
MPPX_SERVER_PRIVATE_KEY=0x...
```
286 changes: 286 additions & 0 deletions examples/authorize/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tempo Authorize Playground</title>
<style>
* {
box-sizing: border-box;
}

:root {
color: #171717;
background: #f7f7f4;
font-family:
Inter,
ui-sans-serif,
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
sans-serif;
font-size: 16px;
}

body {
margin: 0;
min-width: 320px;
}

main {
width: min(1280px, calc(100vw - 32px));
margin: 0 auto;
padding: 28px 0 40px;
}

header {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 20px;
margin-bottom: 24px;
}

h1 {
margin: 0 0 6px;
font-size: 28px;
font-weight: 650;
}

p {
margin: 0;
color: #5f625c;
}

.status {
min-width: 260px;
padding: 10px 12px;
border: 1px solid #d7d8d0;
border-radius: 6px;
background: #ffffff;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 12px;
color: #3d4239;
}

.toolbar {
display: grid;
grid-template-columns: minmax(240px, 1fr) repeat(3, auto);
align-items: end;
gap: 12px;
margin-bottom: 18px;
padding: 14px;
border: 1px solid #d7d8d0;
border-radius: 8px;
background: #ffffff;
}

label {
display: grid;
gap: 6px;
font-size: 12px;
color: #5f625c;
}

input {
width: 100%;
min-height: 38px;
padding: 7px 10px;
border: 1px solid #c7c9bf;
border-radius: 5px;
background: #fff;
color: #171717;
font: inherit;
}

button {
min-height: 38px;
padding: 0 13px;
border: 1px solid #171717;
border-radius: 5px;
background: #171717;
color: #fff;
font: inherit;
cursor: pointer;
white-space: nowrap;
}

button.secondary {
border-color: #c7c9bf;
background: #fff;
color: #171717;
}

button.danger {
border-color: #8f2d2d;
background: #8f2d2d;
}

button:disabled {
cursor: not-allowed;
opacity: 0.48;
}

.metric {
min-width: 120px;
padding: 8px 10px;
border: 1px solid #d7d8d0;
border-radius: 6px;
background: #fbfbf8;
}

.metric b {
display: block;
font-size: 12px;
color: #5f625c;
font-weight: 500;
}

.metric span {
font-variant-numeric: tabular-nums;
}

.table-wrap {
overflow-x: auto;
border: 1px solid #d7d8d0;
border-radius: 8px;
background: #ffffff;
}

table {
width: 100%;
min-width: 1120px;
border-collapse: collapse;
}

th,
td {
padding: 10px 12px;
border-bottom: 1px solid #ecede7;
text-align: left;
vertical-align: middle;
}

th {
background: #f1f2ec;
color: #4b5046;
font-size: 12px;
font-weight: 600;
}

td {
font-size: 14px;
}

tbody tr:last-child td {
border-bottom: 0;
}

.mono {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 12px;
}

.pill {
display: inline-flex;
align-items: center;
min-height: 24px;
padding: 0 8px;
border-radius: 999px;
background: #e8f1df;
color: #2f5b25;
font-size: 12px;
}

.pill.closed {
background: #ececec;
color: #555;
}

.pill.voided {
background: #f6e4de;
color: #8f2d2d;
}

.row-actions {
display: grid;
grid-template-columns: 92px auto auto auto;
gap: 6px;
}

.empty {
padding: 40px 12px;
color: #777a72;
text-align: center;
}

@media (max-width: 760px) {
main {
width: min(100vw - 20px, 1280px);
padding-top: 18px;
}

header,
.toolbar {
display: grid;
grid-template-columns: 1fr;
}

.status {
min-width: 0;
}
}
</style>
</head>
<body>
<main>
<header>
<div>
<h1>Tempo Authorize Playground</h1>
<p>Create authorizations, capture partial amounts, close, or void.</p>
</div>
<div class="status" id="status">Booting...</div>
</header>

<section class="toolbar" aria-label="Create authorization">
<label>
Units
<input id="units" type="number" min="1" step="1" value="4" />
</label>
<div class="metric">
<b>Unit price</b>
<span id="unit-price">$0.25</span>
</div>
<div class="metric">
<b>Authorize total</b>
<span id="authorize-total">$1.00</span>
</div>
<button id="authorize" type="button">Authorize</button>
</section>

<section class="table-wrap" aria-label="Authorizations">
<table>
<thead>
<tr>
<th>Authorization</th>
<th>Status</th>
<th>Amount</th>
<th>Captured</th>
<th>Remaining</th>
<th>Open tx</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="authorizations">
<tr>
<td class="empty" colspan="7">No authorizations yet.</td>
</tr>
</tbody>
</table>
</section>
</main>

<script type="module" src="/src/client.ts"></script>
</body>
</html>
20 changes: 20 additions & 0 deletions examples/authorize/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "authorize",
"private": true,
"type": "module",
"scripts": {
"check:types": "tsgo -b",
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@remix-run/node-fetch-server": "^0.13.0",
"@types/node": "^25.6.0",
"@typescript/native-preview": "7.0.0-dev.20260323.1",
"mppx": "workspace:*",
"typescript": "~6.0.3",
"viem": "^2.50.4",
"vite": "^8.0.10"
}
}
Loading
Loading