@4mica/sdk 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +29 -0
- package/.github/workflows/ci.yml +31 -0
- package/.prettierignore +3 -0
- package/.prettierrc +6 -0
- package/LICENSE +21 -0
- package/README.md +444 -0
- package/dist/abi/core4mica.json +1605 -0
- package/dist/abi/erc20.json +187 -0
- package/dist/auth.d.ts +62 -0
- package/dist/auth.js +255 -0
- package/dist/bls.d.ts +5 -0
- package/dist/bls.js +58 -0
- package/dist/client.d.ts +58 -0
- package/dist/client.js +245 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.js +123 -0
- package/dist/contract.d.ts +33 -0
- package/dist/contract.js +134 -0
- package/dist/errors.d.ts +43 -0
- package/dist/errors.js +62 -0
- package/dist/guarantee.d.ts +3 -0
- package/dist/guarantee.js +66 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +28 -0
- package/dist/models.d.ts +150 -0
- package/dist/models.js +176 -0
- package/dist/rpc.d.ts +37 -0
- package/dist/rpc.js +163 -0
- package/dist/signing.d.ts +106 -0
- package/dist/signing.js +223 -0
- package/dist/src/abi/core4mica.json +1605 -0
- package/dist/src/abi/erc20.json +187 -0
- package/dist/src/bls.d.ts +5 -0
- package/dist/src/bls.js +45 -0
- package/dist/src/client.d.ts +55 -0
- package/dist/src/client.js +214 -0
- package/dist/src/config.d.ts +21 -0
- package/dist/src/config.js +76 -0
- package/dist/src/contract.d.ts +33 -0
- package/dist/src/contract.js +121 -0
- package/dist/src/errors.d.ts +17 -0
- package/dist/src/errors.js +31 -0
- package/dist/src/guarantee.d.ts +3 -0
- package/dist/src/guarantee.js +66 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.js +27 -0
- package/dist/src/models.d.ts +119 -0
- package/dist/src/models.js +132 -0
- package/dist/src/rpc.d.ts +30 -0
- package/dist/src/rpc.js +122 -0
- package/dist/src/signing.d.ts +16 -0
- package/dist/src/signing.js +102 -0
- package/dist/src/utils.d.ts +8 -0
- package/dist/src/utils.js +78 -0
- package/dist/src/x402.d.ts +77 -0
- package/dist/src/x402.js +214 -0
- package/dist/tests/config.test.d.ts +1 -0
- package/dist/tests/config.test.js +26 -0
- package/dist/tests/guarantee.test.d.ts +1 -0
- package/dist/tests/guarantee.test.js +26 -0
- package/dist/tests/rpc.test.d.ts +1 -0
- package/dist/tests/rpc.test.js +44 -0
- package/dist/tests/signing.test.d.ts +1 -0
- package/dist/tests/signing.test.js +25 -0
- package/dist/tests/utils.test.d.ts +1 -0
- package/dist/tests/utils.test.js +25 -0
- package/dist/tests/x402.test.d.ts +1 -0
- package/dist/tests/x402.test.js +65 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +78 -0
- package/dist/x402/index.d.ts +22 -0
- package/dist/x402/index.js +136 -0
- package/dist/x402/models.d.ts +80 -0
- package/dist/x402/models.js +2 -0
- package/dist/x402.d.ts +78 -0
- package/dist/x402.js +231 -0
- package/eslint.config.mjs +22 -0
- package/package.json +31 -0
- package/src/abi/core4mica.json +1605 -0
- package/src/abi/erc20.json +187 -0
- package/src/auth.ts +325 -0
- package/src/bls.ts +76 -0
- package/src/client.ts +347 -0
- package/src/config.ts +149 -0
- package/src/contract.ts +194 -0
- package/src/errors.ts +40 -0
- package/src/guarantee.ts +96 -0
- package/src/index.ts +12 -0
- package/src/models.ts +309 -0
- package/src/rpc.ts +225 -0
- package/src/signing.ts +330 -0
- package/src/utils.ts +75 -0
- package/src/x402/index.ts +192 -0
- package/src/x402/models.ts +94 -0
- package/tests/auth.integration.test.ts +97 -0
- package/tests/auth.test.ts +292 -0
- package/tests/config.test.ts +56 -0
- package/tests/guarantee.test.ts +26 -0
- package/tests/rpc.test.ts +76 -0
- package/tests/signing.test.ts +52 -0
- package/tests/utils.test.ts +35 -0
- package/tests/x402.test.ts +152 -0
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +15 -0
- package/vitest.config.ts +12 -0
package/.eslintrc.cjs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
root: true,
|
|
3
|
+
env: {
|
|
4
|
+
es2020: true,
|
|
5
|
+
node: true,
|
|
6
|
+
},
|
|
7
|
+
parser: "@typescript-eslint/parser",
|
|
8
|
+
parserOptions: {
|
|
9
|
+
sourceType: "module",
|
|
10
|
+
ecmaVersion: 2020,
|
|
11
|
+
project: "./tsconfig.json",
|
|
12
|
+
},
|
|
13
|
+
plugins: ["@typescript-eslint"],
|
|
14
|
+
extends: [
|
|
15
|
+
"eslint:recommended",
|
|
16
|
+
"plugin:@typescript-eslint/recommended",
|
|
17
|
+
"eslint-config-prettier",
|
|
18
|
+
],
|
|
19
|
+
rules: {
|
|
20
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
21
|
+
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
22
|
+
"@typescript-eslint/no-unused-vars": [
|
|
23
|
+
"error",
|
|
24
|
+
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
|
25
|
+
],
|
|
26
|
+
"@typescript-eslint/no-var-requires": "off",
|
|
27
|
+
},
|
|
28
|
+
ignorePatterns: ["dist", "node_modules"],
|
|
29
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout
|
|
13
|
+
uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Use Node.js 20
|
|
16
|
+
uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: 20
|
|
19
|
+
cache: npm
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: npm install
|
|
23
|
+
|
|
24
|
+
- name: Format check
|
|
25
|
+
run: npm run fmt
|
|
26
|
+
|
|
27
|
+
- name: Lint
|
|
28
|
+
run: npm run lint
|
|
29
|
+
|
|
30
|
+
- name: Test
|
|
31
|
+
run: npm test
|
package/.prettierignore
ADDED
package/.prettierrc
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 4Mica
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
# x402-4mica Facilitator
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://github.com/4mica-network/x402-4mica/actions/workflows/ci.yml">
|
|
5
|
+
<img src="https://github.com/4mica-network/x402-4mica/actions/workflows/ci.yml/badge.svg" alt="CI">
|
|
6
|
+
</a>
|
|
7
|
+
<a href="LICENSE">
|
|
8
|
+
<img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License: MIT">
|
|
9
|
+
</a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
A facilitator for the x402 protocol that runs the 4mica credit flow. Resource servers call it to
|
|
13
|
+
open tabs, validate `X-PAYMENT` headers against their `paymentRequirements`, and settle by returning
|
|
14
|
+
the BLS certificate to the recipient.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
**Contents**
|
|
18
|
+
- [How to use the system](#how-to-use-the-system)
|
|
19
|
+
- [Integrate from x402](#integrate-from-x402)
|
|
20
|
+
- [Run your own facilitator](#run-your-own-facilitator)
|
|
21
|
+
|
|
22
|
+
## How to use the system
|
|
23
|
+
|
|
24
|
+
### Quick integration (resource servers)
|
|
25
|
+
|
|
26
|
+
- Configure the 4mica facilitator (for example `https://x402.4mica.xyz/`) and choose a POST tab endpoint on your API (e.g. `POST https://api.example.com/x402/tab`). Your `402 Payment Required` responses should advertise `scheme = "4mica-credit"`, a supported `network`, and set `payTo` / `asset` / `maxAmountRequired`, embedding your tab endpoint in `paymentRequirements.extra.tabEndpoint`.
|
|
27
|
+
- Implement the tab endpoint to accept `{ userAddress, paymentRequirements }`. For each call, open or reuse a tab by calling the facilitator’s standard `POST /tabs` with `{ userAddress, recipientAddress = payTo, erc20Token = asset, ttlSeconds? }`, then return the tab response (at least `tabId` and `userAddress`) to the client. Cache tabs per (user, recipient, asset) if you want to avoid unnecessary `/tabs` calls – the facilitator will return the existing tab for that combination either way.
|
|
28
|
+
- Clients combine this tab with your original `paymentRequirements` to build and sign a guarantee, producing a base64-encoded `X-PAYMENT` header that they send on the retried request for the protected resource. You never construct this header yourself; you only need to validate and consume it.
|
|
29
|
+
- When a request arrives with `X-PAYMENT`, base64-decode the header into the standard x402 payment envelope and send its payment payload together with the original `paymentRequirements` to the facilitator’s `/verify` and `/settle` endpoints. Use `/verify` as an optional preflight check before doing work, and `/settle` once you are ready to accept credit and obtain the BLS certificate for downstream remuneration.
|
|
30
|
+
|
|
31
|
+
### Quick integration (clients)
|
|
32
|
+
|
|
33
|
+
- Python SDK:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install sdk-4mica
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import asyncio
|
|
41
|
+
from fourmica_sdk import Client, ConfigBuilder, PaymentRequirements, X402Flow
|
|
42
|
+
|
|
43
|
+
payer_key = "0x..." # wallet private key
|
|
44
|
+
user_address = "0x..." # address to embed in the claims
|
|
45
|
+
|
|
46
|
+
async def main():
|
|
47
|
+
cfg = ConfigBuilder().wallet_private_key(payer_key).rpc_url("https://api.4mica.xyz/").build()
|
|
48
|
+
client = await Client.new(cfg)
|
|
49
|
+
flow = X402Flow.from_client(client)
|
|
50
|
+
|
|
51
|
+
# Fetch the recipient's paymentRequirements (must include extra.tabEndpoint)
|
|
52
|
+
req_raw = fetch_requirements_somehow()[0]
|
|
53
|
+
requirements = PaymentRequirements.from_raw(req_raw)
|
|
54
|
+
|
|
55
|
+
payment = await flow.sign_payment(requirements, user_address)
|
|
56
|
+
headers = {"X-PAYMENT": payment.header} # base64 string to send with the retry
|
|
57
|
+
await client.aclose()
|
|
58
|
+
|
|
59
|
+
asyncio.run(main())
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- TypeScript SDK:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install sdk-4mica
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { Client, ConfigBuilder, PaymentRequirements, X402Flow } from "sdk-4mica";
|
|
70
|
+
|
|
71
|
+
async function run() {
|
|
72
|
+
const cfg = new ConfigBuilder().walletPrivateKey("0x...").build();
|
|
73
|
+
const client = await Client.new(cfg);
|
|
74
|
+
const flow = X402Flow.fromClient(client);
|
|
75
|
+
|
|
76
|
+
const reqRaw = fetchRequirementsSomehow()[0]; // includes extra.tabEndpoint
|
|
77
|
+
const requirements = PaymentRequirements.fromRaw(reqRaw);
|
|
78
|
+
|
|
79
|
+
const payment = await flow.signPayment(requirements, "0xUser");
|
|
80
|
+
const headers = { "X-PAYMENT": payment.header };
|
|
81
|
+
await client.aclose();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
run();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**SIWE auth (optional)**
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { Client, ConfigBuilder } from "sdk-4mica";
|
|
91
|
+
|
|
92
|
+
const cfg = new ConfigBuilder()
|
|
93
|
+
.walletPrivateKey("0x...")
|
|
94
|
+
.rpcUrl("https://api.4mica.xyz/")
|
|
95
|
+
.enableAuth()
|
|
96
|
+
.build();
|
|
97
|
+
|
|
98
|
+
const client = await Client.new(cfg);
|
|
99
|
+
await client.login(); // optional: first RPC call also triggers auth
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Or pass a static bearer token:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const cfg = new ConfigBuilder()
|
|
106
|
+
.walletPrivateKey("0x...")
|
|
107
|
+
.bearerToken("Bearer <access_token>")
|
|
108
|
+
.build();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Env vars: `4MICA_BEARER_TOKEN`, `4MICA_AUTH_URL`, `4MICA_AUTH_REFRESH_MARGIN_SECS`.
|
|
112
|
+
|
|
113
|
+
- Rust SDK: `cargo add rust-sdk-4mica` and call
|
|
114
|
+
`X402Flow::sign_payment(requirements, user_address)` to obtain the same `payment.header` for the
|
|
115
|
+
retry request.
|
|
116
|
+
|
|
117
|
+
### Demo example
|
|
118
|
+
|
|
119
|
+
You can pair the client with `examples/server/mock_paid_api.py`, a FastAPI server that simulates a
|
|
120
|
+
paywalled endpoint. Start it with `python examples/server/mock_paid_api.py` (set `PORT` to override
|
|
121
|
+
the default `9000`). The mock resource will call the facilitator’s `/verify` endpoint (defaulting to
|
|
122
|
+
`https://x402.4mica.xyz/`; override with `FACILITATOR_URL`) whenever it receives an `X-PAYMENT`
|
|
123
|
+
header.
|
|
124
|
+
|
|
125
|
+
The bundled Rust example shows how to sign a payment
|
|
126
|
+
header with `rust-sdk-4mica`:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# requires PAYER_KEY, USER_ADDRESS, RESOURCE_URL and ASSET_ADDRESS
|
|
130
|
+
cargo run --example rust_client
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The example will read environment variables from `examples/.env` (or a root `.env`) if present. A
|
|
134
|
+
Python counterpart lives in `examples/python_client/client.py` (install deps with `pip install -r
|
|
135
|
+
examples/python_client/requirements.txt`). A TypeScript version lives in `examples/ts_client`
|
|
136
|
+
(`npm install && npm start`).
|
|
137
|
+
|
|
138
|
+
### X-PAYMENT header schema
|
|
139
|
+
|
|
140
|
+
`X-PAYMENT` must be a base64-encoded JSON envelope:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"x402Version": 1,
|
|
145
|
+
"scheme": "4mica-credit",
|
|
146
|
+
"network": "polygon-amoy",
|
|
147
|
+
"payload": {
|
|
148
|
+
"claims": {
|
|
149
|
+
"user_address": "<0x-prefixed checksum string>",
|
|
150
|
+
"recipient_address": "<0x-prefixed checksum string>",
|
|
151
|
+
"tab_id": "<decimal or 0x value>",
|
|
152
|
+
"req_id": "<decimal or 0x value>",
|
|
153
|
+
"amount": "<decimal or 0x value>",
|
|
154
|
+
"asset_address": "<0x-prefixed checksum string>",
|
|
155
|
+
"timestamp": 1716500000,
|
|
156
|
+
"version": "v1"
|
|
157
|
+
},
|
|
158
|
+
"signature": "<0x-prefixed wallet signature>",
|
|
159
|
+
"scheme": "eip712"
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The facilitator enforces that:
|
|
165
|
+
|
|
166
|
+
- `scheme` / `network` match both `/supported` and the resource server’s requirements.
|
|
167
|
+
- `payTo` equals the `recipient_address` present inside the claim.
|
|
168
|
+
- `asset` and `maxAmountRequired` must match the signed `amount` exactly (no partial spends).
|
|
169
|
+
- If `X402_GUARANTEE_DOMAIN` is set (legacy `FOUR_MICA_GUARANTEE_DOMAIN` / `4MICA_GUARANTEE_DOMAIN`
|
|
170
|
+
are also honored), the certificate domain returned by core matches it exactly.
|
|
171
|
+
|
|
172
|
+
### HTTP API
|
|
173
|
+
|
|
174
|
+
- `GET /supported` – returns all `(scheme, network)` tuples the facilitator can service (4mica and,
|
|
175
|
+
if configured, any additional `exact` flows).
|
|
176
|
+
- `GET /health` – liveness probe that returns `{ "status": "ok" }`.
|
|
177
|
+
- `POST /tabs`
|
|
178
|
+
- Request: `{ "userAddress", "recipientAddress", "erc20Token"?, "ttlSeconds"? }`.
|
|
179
|
+
Use `erc20Token = null` (or omit it) for ETH tabs; otherwise pass the token contract address.
|
|
180
|
+
- Response: `{ "tabId", "userAddress", "recipientAddress", "assetAddress", "startTimestamp", "ttlSeconds", "nextReqId"? }`.
|
|
181
|
+
`tabId` is always emitted as a canonical hex string. Recipients call this after a user shares
|
|
182
|
+
their wallet; the facilitator reuses the existing tab for that pair whenever possible.
|
|
183
|
+
- `POST /verify`
|
|
184
|
+
- Request: `{ "x402Version": 1, "paymentHeader": "<base64 X-PAYMENT>", "paymentRequirements": { ... } }`.
|
|
185
|
+
- Response: `{ "isValid": true|false, "invalidReason"?, "certificate": null }`.
|
|
186
|
+
- `POST /settle`
|
|
187
|
+
- Request: same shape as `/verify`.
|
|
188
|
+
- Response: for 4mica, `{ "success": true, "networkId": "<network>", "certificate": { "claims", "signature" } }`.
|
|
189
|
+
When delegating to the `exact` facilitator the structure mirrors upstream x402 responses and may
|
|
190
|
+
include `txHash`.
|
|
191
|
+
If `X402_DEBIT_URL` is set, debit requests are proxied to the configured x402-rs
|
|
192
|
+
facilitator, allowing clients to follow the x402 debit flow unchanged.
|
|
193
|
+
|
|
194
|
+
### End-to-end credit flow
|
|
195
|
+
|
|
196
|
+
The sequence below highlights each HTTP request, who sends it, and how data travels through
|
|
197
|
+
x402-4mica and the 4mica core service.
|
|
198
|
+
|
|
199
|
+
1. **Client discovers the paywall**
|
|
200
|
+
- Client → Recipient resource: request protected content.
|
|
201
|
+
- Recipient → Client: `402 Payment Required` that advertises the supported `(scheme, network)`
|
|
202
|
+
and instructs the client where to request a payment tab (for example, `POST /tab` with their
|
|
203
|
+
wallet address). No per-user data is known yet.
|
|
204
|
+
2. **Client requests a tab**
|
|
205
|
+
- Client → Recipient resource: `POST /tab` (or an equivalent endpoint) with their
|
|
206
|
+
`{ userAddress, erc20Token?, ttlSeconds? }`.
|
|
207
|
+
- Recipient → Facilitator: `POST /tabs` using the supplied body. The facilitator will reuse the
|
|
208
|
+
existing tab for that `(user, recipient, asset)` combination or create a fresh one.
|
|
209
|
+
- Facilitator → 4mica core: `POST core/tabs` whenever a new tab is required.
|
|
210
|
+
- Facilitator → Recipient: `{ tabId, userAddress, recipientAddress, assetAddress, startTimestamp, ttlSeconds }`.
|
|
211
|
+
Recipients cache this tab and reuse it until expiry, then hand `tabId`/`userAddress` back to
|
|
212
|
+
the client inside `paymentRequirements`.
|
|
213
|
+
3. **Client signs a guarantee**
|
|
214
|
+
- Client builds the JSON payload that matches the resource requirements, signs it with their
|
|
215
|
+
private key (EIP‑712 by default; EIP‑191 is also accepted), and wraps the result in a base64
|
|
216
|
+
`X-PAYMENT` header.
|
|
217
|
+
4. **Client retries the protected call**
|
|
218
|
+
- Client → Recipient resource: same HTTP request plus `X-PAYMENT: <base64 envelope>`.
|
|
219
|
+
5. **Recipient verifies the header**
|
|
220
|
+
- Recipient → Facilitator: `POST /verify` with
|
|
221
|
+
`{ x402Version, paymentHeader, paymentRequirements }`.
|
|
222
|
+
- Facilitator: decodes `paymentHeader`, ensures `scheme`/`network` match `/supported`, confirms
|
|
223
|
+
the claims reference the advertised tab, user, asset, `payTo`, and that `amount` exactly equals
|
|
224
|
+
`maxAmountRequired`.
|
|
225
|
+
- Facilitator → Recipient: `{ isValid, invalidReason?, certificate: null }`. No request touches
|
|
226
|
+
4mica core here; this is purely structural validation so recipients can pre-flight calls.
|
|
227
|
+
6. **Recipient settles the tab**
|
|
228
|
+
- Recipient → Facilitator: `POST /settle` with the same payload.
|
|
229
|
+
- Facilitator: revalidates the header, then
|
|
230
|
+
- sends `POST core/guarantees` with `{ claims, signature, scheme }` where `claims` contains the
|
|
231
|
+
tab id, user, recipient, asset, amount, and timestamp (plus a version field injected by the
|
|
232
|
+
facilitator),
|
|
233
|
+
- receives a BLS signature over those claims,
|
|
234
|
+
- verifies the certificate by reusing the public parameters fetched at startup, rejecting the
|
|
235
|
+
settlement if the signature or expected domain fail to match.
|
|
236
|
+
- Facilitator → Recipient: `{ success, networkId, certificate, error: null, txHash: null }`.
|
|
237
|
+
`certificate.claims` and `certificate.signature` are byte strings that recipients can persist or
|
|
238
|
+
pass to downstream infrastructure as proof of credit issuance. If the request belonged to a
|
|
239
|
+
delegated `exact` scheme the facilitator instead forwards the settlement to x402-rs and returns
|
|
240
|
+
that response (which may contain a `txHash` instead of a certificate).
|
|
241
|
+
|
|
242
|
+
## Integrate from x402
|
|
243
|
+
|
|
244
|
+
The facilitator can transparently replace the EIP-3009/x402 debit flow. The key is to swap the old
|
|
245
|
+
`exact` scheme for the 4mica credit primitives described below.
|
|
246
|
+
|
|
247
|
+
### Changes resource servers must make
|
|
248
|
+
|
|
249
|
+
1. **Point at the credit facilitator** – set `X402_FACILITATOR_URL=https://x402.4mica.xyz`
|
|
250
|
+
(or the TypeScript `CC_FACILITATOR_URL`). This host validates guarantee envelopes and returns BLS
|
|
251
|
+
certificates instead of ERC-3009 receipts.
|
|
252
|
+
2. **Provision tabs before issuing requirements** – whenever a user shares their wallet, call
|
|
253
|
+
`POST https://x402.4mica.xyz/tabs` with `{ userAddress, recipientAddress, erc20Token?, ttlSeconds? }`.
|
|
254
|
+
Cache `{ tabId, assetAddress, startTimestamp, ttlSeconds }` and reuse that tab per
|
|
255
|
+
`(user, recipient, asset)` combination.
|
|
256
|
+
3. **Emit credit-flavoured `paymentRequirements`** – embed the latest tab metadata and switch the
|
|
257
|
+
identifying strings:
|
|
258
|
+
|
|
259
|
+
```jsonc
|
|
260
|
+
{
|
|
261
|
+
"scheme": "4mica-credit",
|
|
262
|
+
"network": "polygon-amoy",
|
|
263
|
+
"maxAmountRequired": "<decimal or 0x amount>",
|
|
264
|
+
"resource": "/your/resource",
|
|
265
|
+
"description": "Describe the protected work",
|
|
266
|
+
"mimeType": "application/json",
|
|
267
|
+
"payTo": "<recipientAddress>",
|
|
268
|
+
"maxTimeoutSeconds": 300,
|
|
269
|
+
"asset": "<assetAddress>",
|
|
270
|
+
"extra": {
|
|
271
|
+
"tabEndpoint": "<tabEndpoint>",
|
|
272
|
+
"...other metadata you already add..."
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The facilitator enforces that `scheme`, `network`, `payTo` and `asset`
|
|
278
|
+
match the tab exactly, so keep them synchronized.
|
|
279
|
+
|
|
280
|
+
4. **Expect credit certificates during settlement** – `/verify` still performs structural checks and
|
|
281
|
+
`/settle` now returns `{ success, networkId: "polygon-amoy", certificate: { claims, signature } }`.
|
|
282
|
+
Persist the certificate if you need to downstream claim remuneration via 4mica core.
|
|
283
|
+
|
|
284
|
+
### Changes clients (payers) must make
|
|
285
|
+
|
|
286
|
+
Payers sign guarantees instead of EIP-3009 transfers. Use the official SDK `rust-sdk-4mica` to manage collateral and produce signatures.
|
|
287
|
+
|
|
288
|
+
1. **Install the SDK** – inside your agent crate run
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
cargo add rust-sdk-4mica
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
or add the same entry manually to `Cargo.toml`.
|
|
295
|
+
|
|
296
|
+
2. **Configure the client** – create a `Client` with the payer’s signing key. The SDK pulls the
|
|
297
|
+
remaining parameters (domain separator, operator key, etc.) from `X402_CORE_API_URL`.
|
|
298
|
+
|
|
299
|
+
```rust
|
|
300
|
+
use rust_sdk_4mica::{Client, ConfigBuilder};
|
|
301
|
+
|
|
302
|
+
let config = ConfigBuilder::default()
|
|
303
|
+
.rpc_url("https://api.4mica.xyz/".into())
|
|
304
|
+
.wallet_private_key(std::env::var("PAYER_KEY")?)
|
|
305
|
+
.build()?;
|
|
306
|
+
let client = Client::new(config).await?;
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
3. **Fund the tab** – before requesting credit, ensure the payer has collateral using
|
|
310
|
+
`client.user.deposit(...)` (or `approve_erc20` + `deposit` for tokens). Refer to the SDK README
|
|
311
|
+
for concrete examples.
|
|
312
|
+
4. **Sign guarantee claims** – derive `PaymentGuaranteeClaims` from the recipient’s
|
|
313
|
+
`paymentRequirements` (copy `tabId`, `userAddress`, `payTo`, `asset`, and the desired `amount`),
|
|
314
|
+
choose a signing scheme (usually `SigningScheme::Eip712`), and call `client.user.sign_payment`.
|
|
315
|
+
|
|
316
|
+
```rust
|
|
317
|
+
use rust_sdk_4mica::{PaymentGuaranteeClaims, SigningScheme, U256};
|
|
318
|
+
|
|
319
|
+
let claims = PaymentGuaranteeClaims {
|
|
320
|
+
user_address: payer_wallet.clone(),
|
|
321
|
+
recipient_address: pay_to.clone(),
|
|
322
|
+
tab_id: tab_id_u256,
|
|
323
|
+
amount: U256::from(amount_wei),
|
|
324
|
+
asset_address: asset.clone(),
|
|
325
|
+
timestamp: chrono::Utc::now().timestamp() as u64,
|
|
326
|
+
req_id: U256::from(rand::random::<u64>()),
|
|
327
|
+
};
|
|
328
|
+
let signature = client
|
|
329
|
+
.user
|
|
330
|
+
.sign_payment(claims.clone(), SigningScheme::Eip712)
|
|
331
|
+
.await?;
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
5. **Build the `X-PAYMENT` header** – wrap `{ x402Version: 1, scheme: "4mica-credit", network:
|
|
335
|
+
"polygon-amoy", payload: { claims, signature, scheme: "eip712" } }` into base64 (see
|
|
336
|
+
`examples/rust_client/main.rs` or `examples/python_client/client.py`) and send it alongside the retrying
|
|
337
|
+
HTTP request.
|
|
338
|
+
6. **Settle your tabs** – every tab response includes `ttlSeconds`, which is the settlement window in
|
|
339
|
+
seconds from `startTimestamp`. Recipients should call `/settle` (and issue the guarantee) before
|
|
340
|
+
that TTL lapses; once a certificate comes back they must relay the `tabId`, `reqId`, `amount`, and
|
|
341
|
+
`asset` to the payer. Payers are expected to clear the balance within the same TTL window to avoid
|
|
342
|
+
the recipient redeeming their collateral. Use the SDK’s `UserClient::pay_tab` helper to repay the
|
|
343
|
+
outstanding credit with the exact asset used when the tab was opened:
|
|
344
|
+
|
|
345
|
+
```rust
|
|
346
|
+
use rust_sdk_4mica::U256;
|
|
347
|
+
|
|
348
|
+
let receipt = client
|
|
349
|
+
.user
|
|
350
|
+
.pay_tab(tab_id, req_id, U256::from(amount_wei), recipient_address.clone(), erc20_token)
|
|
351
|
+
.await?;
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
After broadcasting the repayment transaction, poll `client.user.get_tab_payment_status(tab_id)`
|
|
355
|
+
(or `client.user.get_user()`) to verify that `paid` equals the guaranteed amount. If the TTL
|
|
356
|
+
expires without repayment the recipient is free to run `recipient.remunerate(cert)` from the SDK,
|
|
357
|
+
which slashes your posted collateral on-chain.
|
|
358
|
+
|
|
359
|
+
## Run your own facilitator
|
|
360
|
+
|
|
361
|
+
### Configuration
|
|
362
|
+
|
|
363
|
+
Environment variables (defaults shown):
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
export HOST=0.0.0.0
|
|
367
|
+
export PORT=8080
|
|
368
|
+
export X402_SCHEME=4mica-credit
|
|
369
|
+
# List of supported networks (JSON). Each entry must include `{ "network", "coreApiUrl" }`.
|
|
370
|
+
export X402_NETWORKS='[{"network":"polygon-amoy","coreApiUrl":"https://api.4mica.xyz/"}]'
|
|
371
|
+
# Legacy single-network fallback if X402_NETWORKS is unset
|
|
372
|
+
export X402_NETWORK=polygon-amoy
|
|
373
|
+
|
|
374
|
+
# 4mica public API – used to fetch operator parameters
|
|
375
|
+
export X402_CORE_API_URL=https://api.4mica.xyz/
|
|
376
|
+
# Default asset address to apply when callers omit assetAddress in /tabs requests
|
|
377
|
+
export ASSET_ADDRESS=0x...
|
|
378
|
+
|
|
379
|
+
# Optional: pin the expected domain separator (32-byte hex, 0x-prefixed)
|
|
380
|
+
export X402_GUARANTEE_DOMAIN=0x...
|
|
381
|
+
# legacy: FOUR_MICA_GUARANTEE_DOMAIN / 4MICA_GUARANTEE_DOMAIN
|
|
382
|
+
|
|
383
|
+
# Optional: proxy x402 debit flows to an existing x402-rs facilitator
|
|
384
|
+
export X402_DEBIT_URL=https://x402.example.com/
|
|
385
|
+
|
|
386
|
+
# Optional: enable standard x402 settlement for EVM networks
|
|
387
|
+
export SIGNER_TYPE=private-key
|
|
388
|
+
export EVM_PRIVATE_KEY=0x...
|
|
389
|
+
export RPC_URL_BASE=https://mainnet.base.org
|
|
390
|
+
export RPC_URL_BASE_SEPOLIA=https://sepolia.base.org
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
When `X402_NETWORKS` is present it overrides the legacy `X402_NETWORK` / `X402_CORE_API_URL`
|
|
394
|
+
environment variables and enables multi-network support. Each configured network gets its own 4mica
|
|
395
|
+
Core API base URL so the facilitator can fetch operator parameters and issue guarantees for that
|
|
396
|
+
network independently.
|
|
397
|
+
|
|
398
|
+
On startup the facilitator loads the public parameters described above and, if the optional x402
|
|
399
|
+
variables are present, initialises the upstream `exact` ERC-3009 facilitator as well. Any schemes
|
|
400
|
+
that fail to initialise are omitted from `/supported`.
|
|
401
|
+
When `X402_DEBIT_URL` is provided, `/supported` also includes the debit schemes advertised by
|
|
402
|
+
the referenced x402-rs facilitator, and `/verify` / `/settle` proxy those requests to it.
|
|
403
|
+
|
|
404
|
+
### Running
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
cargo run
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
The bound address is logged on start-up. Use `GET /supported` to read the `(scheme, network)` pair
|
|
411
|
+
that resource servers should use inside their `402 Payment Required` responses.
|
|
412
|
+
|
|
413
|
+
### Testing
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
cargo test
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Integration-style tests use a mock verifier to exercise `/verify`, `/settle`, `/tabs`, and the
|
|
420
|
+
discovery endpoints without contacting 4mica.
|
|
421
|
+
|
|
422
|
+
Point your x402 resource server at this facilitator to outsource 4mica guarantee verification while
|
|
423
|
+
keeping custody, settlement, and tab management under your own infrastructure.
|
|
424
|
+
|
|
425
|
+
### How the facilitator moves data
|
|
426
|
+
|
|
427
|
+
- **Startup** – the process loads configuration from the environment, then calls
|
|
428
|
+
`X402_CORE_API_URL/core/public-params` (or the first `coreApiUrl` listed inside `X402_NETWORKS`) to
|
|
429
|
+
fetch the operator’s BLS public key, domain separator, and API base URL. Those values are kept in
|
|
430
|
+
memory and reused for all later requests.
|
|
431
|
+
- **Tab provisioning (`POST /tabs`)** – recipients can ask the facilitator to open a payment tab on
|
|
432
|
+
their behalf. The facilitator relays the request to `core/tabs`, converts the 4mica
|
|
433
|
+
response into a plain JSON payload, and hands the tab metadata back to the resource server.
|
|
434
|
+
- **Verification (`POST /verify`)** – recipients send the base64 `X-PAYMENT` header plus the
|
|
435
|
+
`paymentRequirements` they issued to the client. The facilitator decodes the header, validates the
|
|
436
|
+
claims against the requirements, and mirrors the upstream x402 error semantics. No 4mica network
|
|
437
|
+
call is made in this path.
|
|
438
|
+
- **Settlement (`POST /settle`)** – recipients replay the same payload once they are ready to accept
|
|
439
|
+
credit. The facilitator re-runs validation, submits the signed guarantee to
|
|
440
|
+
`core/guarantees`, receives the BLS certificate, verifies it against the cached operator public
|
|
441
|
+
key (and optional domain), and returns the certificate to the caller.
|
|
442
|
+
|
|
443
|
+
If EVM settlement variables are present the facilitator also instantiates the upstream `exact`
|
|
444
|
+
facilitator from `x402-rs`, exposing those `(scheme, network)` pairs on `/supported`.
|