@aibtc/tx-schemas 0.1.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/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/core/enums.d.ts +161 -0
- package/dist/core/enums.d.ts.map +1 -0
- package/dist/core/enums.js +98 -0
- package/dist/core/enums.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/payment.d.ts +267 -0
- package/dist/core/payment.d.ts.map +1 -0
- package/dist/core/payment.js +85 -0
- package/dist/core/payment.js.map +1 -0
- package/dist/core/primitives.d.ts +11 -0
- package/dist/core/primitives.d.ts.map +1 -0
- package/dist/core/primitives.js +29 -0
- package/dist/core/primitives.js.map +1 -0
- package/dist/core/schemas.d.ts +2 -0
- package/dist/core/schemas.d.ts.map +1 -0
- package/dist/core/schemas.js +2 -0
- package/dist/core/schemas.js.map +1 -0
- package/dist/core/terminal-reasons.d.ts +154 -0
- package/dist/core/terminal-reasons.d.ts.map +1 -0
- package/dist/core/terminal-reasons.js +82 -0
- package/dist/core/terminal-reasons.js.map +1 -0
- package/dist/http/index.d.ts +2 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +2 -0
- package/dist/http/index.js.map +1 -0
- package/dist/http/schemas.d.ts +424 -0
- package/dist/http/schemas.d.ts.map +1 -0
- package/dist/http/schemas.js +171 -0
- package/dist/http/schemas.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/rpc/index.d.ts +2 -0
- package/dist/rpc/index.d.ts.map +1 -0
- package/dist/rpc/index.js +2 -0
- package/dist/rpc/index.js.map +1 -0
- package/dist/rpc/schemas.d.ts +339 -0
- package/dist/rpc/schemas.d.ts.map +1 -0
- package/dist/rpc/schemas.js +91 -0
- package/dist/rpc/schemas.js.map +1 -0
- package/docs/package-schemas.md +52 -0
- package/docs/x402-approval-spec.md +530 -0
- package/docs/x402-state-machines.md +241 -0
- package/package.json +101 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# x402 and Sponsored Transaction State Machines
|
|
2
|
+
|
|
3
|
+
Related review document:
|
|
4
|
+
|
|
5
|
+
- `x402-approval-spec.md` defines the target approval bar, transport boundaries, and reduction-first architecture expectations for this flow.
|
|
6
|
+
|
|
7
|
+
These diagrams reflect the implemented flow across:
|
|
8
|
+
|
|
9
|
+
- `aibtcdev/skills`
|
|
10
|
+
- `aibtcdev/aibtc-mcp-server`
|
|
11
|
+
- `aibtcdev/landing-page` (`aibtc.com`)
|
|
12
|
+
- `aibtcdev/agent-news` (`aibtc.news`)
|
|
13
|
+
- `aibtcdev/x402-sponsor-relay`
|
|
14
|
+
- Hiro API
|
|
15
|
+
|
|
16
|
+
## 1. Client State Machine: `skills` and `aibtc-mcp-server`
|
|
17
|
+
|
|
18
|
+
This is the client-side control flow used when an x402-protected endpoint returns `402 Payment Required`.
|
|
19
|
+
|
|
20
|
+
```mermaid
|
|
21
|
+
stateDiagram-v2
|
|
22
|
+
[*] --> Requesting
|
|
23
|
+
|
|
24
|
+
Requesting --> CompletedFree: 2xx free response
|
|
25
|
+
Requesting --> PaymentRequired: HTTP 402 + payment-required
|
|
26
|
+
Requesting --> Failed: non-402/non-retryable error
|
|
27
|
+
|
|
28
|
+
PaymentRequired --> ValidateOffer
|
|
29
|
+
ValidateOffer --> Failed: invalid header or no Stacks option
|
|
30
|
+
ValidateOffer --> EnsureWallet
|
|
31
|
+
|
|
32
|
+
EnsureWallet --> Failed: wallet/network/balance problem
|
|
33
|
+
EnsureWallet --> BuildSponsoredTx
|
|
34
|
+
|
|
35
|
+
BuildSponsoredTx --> SignPaymentPayload
|
|
36
|
+
SignPaymentPayload --> SubmitPaidRequest
|
|
37
|
+
|
|
38
|
+
SubmitPaidRequest --> CompletedPaid: 200/201 + payment-response txid
|
|
39
|
+
SubmitPaidRequest --> CompletedPending: 200/201 + paymentStatus=pending + paymentId
|
|
40
|
+
SubmitPaidRequest --> RetrySameTx: 409 NONCE_CONFLICT or relay-side duplicate
|
|
41
|
+
SubmitPaidRequest --> RebuildTx: 409 SENDER_NONCE_STALE or SENDER_NONCE_GAP
|
|
42
|
+
SubmitPaidRequest --> BackoffRetry: 502/503 or retryable relay error
|
|
43
|
+
SubmitPaidRequest --> RecoveryCheck: retries exhausted with seen relay txids
|
|
44
|
+
SubmitPaidRequest --> Failed: non-retryable failure
|
|
45
|
+
|
|
46
|
+
RetrySameTx --> SubmitPaidRequest: reuse cached txHex + paymentId for relay dedup
|
|
47
|
+
RebuildTx --> BuildSponsoredTx: fetch fresh nonce and re-sign
|
|
48
|
+
BackoffRetry --> SubmitPaidRequest
|
|
49
|
+
|
|
50
|
+
RecoveryCheck --> AutoRecover: Hiro shows seen txid confirmed
|
|
51
|
+
RecoveryCheck --> Failed: no confirmed txid found
|
|
52
|
+
AutoRecover --> CompletedPaid: resubmit using paymentTxid proof
|
|
53
|
+
|
|
54
|
+
CompletedFree --> [*]
|
|
55
|
+
CompletedPaid --> [*]
|
|
56
|
+
CompletedPending --> [*]
|
|
57
|
+
Failed --> [*]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Notes:
|
|
61
|
+
|
|
62
|
+
- `aibtc-mcp-server/src/services/x402.service.ts` does the generic `402 -> sign -> retry once` flow.
|
|
63
|
+
- `skills/src/lib/utils/x402-retry.ts` and `aibtc-mcp-server/src/tools/inbox.tools.ts` add inbox-specific nonce recovery, same-tx dedup retries, and txid auto-recovery.
|
|
64
|
+
|
|
65
|
+
## 2. Service State Machine: `aibtc.com` and `aibtc.news`
|
|
66
|
+
|
|
67
|
+
This is how the service workers behave after receiving a paid request.
|
|
68
|
+
|
|
69
|
+
```mermaid
|
|
70
|
+
stateDiagram-v2
|
|
71
|
+
[*] --> UnpaidRequest
|
|
72
|
+
|
|
73
|
+
UnpaidRequest --> Return402: no payment-signature/payment proof
|
|
74
|
+
Return402 --> [*]
|
|
75
|
+
|
|
76
|
+
UnpaidRequest --> VerifyPayment: payment-signature or paymentTxid present
|
|
77
|
+
|
|
78
|
+
VerifyPayment --> RpcSubmit: X402_RELAY service binding available
|
|
79
|
+
VerifyPayment --> HttpSettle: fallback HTTP /settle path
|
|
80
|
+
|
|
81
|
+
RpcSubmit --> RelayRejected: submitPayment accepted=false
|
|
82
|
+
RpcSubmit --> PollPaymentId: submitPayment accepted=true + paymentId
|
|
83
|
+
|
|
84
|
+
PollPaymentId --> DeliverConfirmed: checkPayment=confirmed
|
|
85
|
+
PollPaymentId --> DeliverConfirmed: checkPayment=mempool
|
|
86
|
+
PollPaymentId --> DeliverPending: poll exhausted with queued/submitted/broadcasting/mempool
|
|
87
|
+
PollPaymentId --> RelayRejected: checkPayment=failed
|
|
88
|
+
PollPaymentId --> RelayRejected: checkPayment=replaced
|
|
89
|
+
PollPaymentId --> RelayRejected: checkPayment=not_found
|
|
90
|
+
PollPaymentId --> RelayUnavailable: RPC error / circuit breaker
|
|
91
|
+
|
|
92
|
+
HttpSettle --> DeliverConfirmed: relay returns confirmed
|
|
93
|
+
HttpSettle --> DeliverPending: relay returns pending
|
|
94
|
+
HttpSettle --> RelayRejected: 4xx settle rejection
|
|
95
|
+
HttpSettle --> RelayUnavailable: 5xx / timeout
|
|
96
|
+
|
|
97
|
+
DeliverConfirmed --> StoreResource
|
|
98
|
+
DeliverPending --> StoreResource
|
|
99
|
+
|
|
100
|
+
StoreResource --> SuccessResponse
|
|
101
|
+
SuccessResponse --> [*]
|
|
102
|
+
|
|
103
|
+
RelayRejected --> ErrorResponse
|
|
104
|
+
RelayUnavailable --> ErrorResponse
|
|
105
|
+
ErrorResponse --> [*]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
What the service returns:
|
|
109
|
+
|
|
110
|
+
- `confirmed` or `mempool`: resource is delivered normally.
|
|
111
|
+
- `pending`: resource is still delivered, but response includes `paymentStatus: "pending"` and `paymentId`.
|
|
112
|
+
- `failed`, `replaced`, `not_found`, or relay unavailability: request fails.
|
|
113
|
+
|
|
114
|
+
## 3. Relay Queue Payment State Machine: `x402-sponsor-relay` RPC path
|
|
115
|
+
|
|
116
|
+
This is the async `paymentId` lifecycle behind `submitPayment()` and `checkPayment()`.
|
|
117
|
+
|
|
118
|
+
```mermaid
|
|
119
|
+
stateDiagram-v2
|
|
120
|
+
[*] --> Submitted
|
|
121
|
+
|
|
122
|
+
Submitted --> Failed: invalid tx / not sponsored / stale nonce / duplicate nonce
|
|
123
|
+
Submitted --> Queued: paymentId created + KV record written + queue send ok
|
|
124
|
+
|
|
125
|
+
Queued --> Broadcasting: queue consumer starts work
|
|
126
|
+
Queued --> Queued: held sender nonce gap
|
|
127
|
+
Queued --> Queued: sponsor contention retry
|
|
128
|
+
Queued --> Failed: queue unavailable / terminal sponsor failure
|
|
129
|
+
|
|
130
|
+
Broadcasting --> Queued: nonce conflict or TooMuchChaining retry
|
|
131
|
+
Broadcasting --> Failed: terminal broadcast failure
|
|
132
|
+
Broadcasting --> Mempool: broadcastOnly() returns txid
|
|
133
|
+
|
|
134
|
+
Mempool --> Confirmed: chainhook marks success
|
|
135
|
+
Mempool --> Failed: chainhook sees on-chain abort
|
|
136
|
+
Mempool --> Replaced: NonceDO replacement notification
|
|
137
|
+
Mempool --> Mempool: waiting for chainhook/finalization
|
|
138
|
+
|
|
139
|
+
Failed --> [*]
|
|
140
|
+
Confirmed --> [*]
|
|
141
|
+
Replaced --> [*]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Stored `paymentId` statuses exposed by `checkPayment()`:
|
|
145
|
+
|
|
146
|
+
- Pending/in-flight: `queued`, `submitted`, `broadcasting`, `mempool`
|
|
147
|
+
- Terminal: `confirmed`, `failed`, `replaced`
|
|
148
|
+
- Missing/expired: `not_found`
|
|
149
|
+
|
|
150
|
+
## 4. Direct Relay Settlement State Machine: `/settle` to Hiro final status
|
|
151
|
+
|
|
152
|
+
This is the synchronous/native settlement flow used by the relay itself when handling direct `/settle` or `/relay` style settlement logic.
|
|
153
|
+
|
|
154
|
+
```mermaid
|
|
155
|
+
stateDiagram-v2
|
|
156
|
+
[*] --> ValidatePayment
|
|
157
|
+
|
|
158
|
+
ValidatePayment --> Failed: tx deserialize or payment requirements invalid
|
|
159
|
+
ValidatePayment --> Broadcasting
|
|
160
|
+
|
|
161
|
+
Broadcasting --> BroadcastRetry: Hiro/node 5xx or retryable transport failure
|
|
162
|
+
Broadcasting --> Failed: client rejection or terminal broadcast failure
|
|
163
|
+
Broadcasting --> PendingReturn: caller requested broadcast-only mode
|
|
164
|
+
Broadcasting --> PollingHiro: txid accepted
|
|
165
|
+
|
|
166
|
+
BroadcastRetry --> Broadcasting
|
|
167
|
+
BroadcastRetry --> Failed: retries exhausted
|
|
168
|
+
|
|
169
|
+
PollingHiro --> Confirmed: Hiro tx_status=success + block_height
|
|
170
|
+
PollingHiro --> Failed: Hiro tx_status=abort_*
|
|
171
|
+
PollingHiro --> PollingHiro: tx_status=pending
|
|
172
|
+
PollingHiro --> PollingHiro: tx_status=dropped_* treated as transient
|
|
173
|
+
PollingHiro --> PollingHiro: 404 not yet indexed
|
|
174
|
+
PollingHiro --> PollingHiro: degraded Hiro polling with backoff
|
|
175
|
+
PollingHiro --> PendingReturn: poll budget exhausted
|
|
176
|
+
|
|
177
|
+
PendingReturn --> BackgroundStatusTracking
|
|
178
|
+
BackgroundStatusTracking --> Confirmed: later Hiro/chainhook confirmation
|
|
179
|
+
BackgroundStatusTracking --> Failed: later on-chain abort
|
|
180
|
+
|
|
181
|
+
Confirmed --> [*]
|
|
182
|
+
Failed --> [*]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Important Hiro-specific behavior:
|
|
186
|
+
|
|
187
|
+
- `success` is only terminal when `block_height` is present.
|
|
188
|
+
- `abort_*` is terminal failure.
|
|
189
|
+
- `dropped_*` is treated as transient and the relay keeps polling.
|
|
190
|
+
- If polling budget expires, relay returns `pending` and status is updated later via KV + background/chainhook updates.
|
|
191
|
+
|
|
192
|
+
## 5. End-to-End View
|
|
193
|
+
|
|
194
|
+
```mermaid
|
|
195
|
+
stateDiagram-v2
|
|
196
|
+
[*] --> ClientCallsService
|
|
197
|
+
ClientCallsService --> NeedsPayment: service returns 402
|
|
198
|
+
NeedsPayment --> ClientSignsSponsoredTx
|
|
199
|
+
ClientSignsSponsoredTx --> ServiceVerifies
|
|
200
|
+
|
|
201
|
+
ServiceVerifies --> RelayQueueFlow: landing-page / agent-news RPC submitPayment
|
|
202
|
+
ServiceVerifies --> RelaySettleFlow: fallback HTTP /settle
|
|
203
|
+
|
|
204
|
+
RelayQueueFlow --> HiroObserved
|
|
205
|
+
RelaySettleFlow --> HiroObserved
|
|
206
|
+
|
|
207
|
+
HiroObserved --> FinalConfirmed
|
|
208
|
+
HiroObserved --> FinalFailed
|
|
209
|
+
HiroObserved --> ServicePending
|
|
210
|
+
|
|
211
|
+
ServicePending --> ClientPollsPaymentId
|
|
212
|
+
ClientPollsPaymentId --> FinalConfirmed
|
|
213
|
+
ClientPollsPaymentId --> FinalFailed
|
|
214
|
+
ClientPollsPaymentId --> ServicePending
|
|
215
|
+
|
|
216
|
+
FinalConfirmed --> [*]
|
|
217
|
+
FinalFailed --> [*]
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Code Anchors
|
|
221
|
+
|
|
222
|
+
- Client payment interceptor:
|
|
223
|
+
- `aibtc-mcp-server/src/services/x402.service.ts`
|
|
224
|
+
- `skills/src/lib/services/x402.service.ts`
|
|
225
|
+
- Client inbox retry and recovery:
|
|
226
|
+
- `skills/src/lib/utils/x402-retry.ts`
|
|
227
|
+
- `skills/src/lib/utils/x402-recovery.ts`
|
|
228
|
+
- `aibtc-mcp-server/src/tools/inbox.tools.ts`
|
|
229
|
+
- Service verification and pending-success behavior:
|
|
230
|
+
- `landing-page/app/api/inbox/[address]/route.ts`
|
|
231
|
+
- `landing-page/app/api/payment-status/[paymentId]/route.ts`
|
|
232
|
+
- `agent-news/src/services/x402.ts`
|
|
233
|
+
- `agent-news/src/routes/payment-status.ts`
|
|
234
|
+
- Relay async payment status:
|
|
235
|
+
- `x402-sponsor-relay/src/rpc.ts`
|
|
236
|
+
- `x402-sponsor-relay/src/services/payment-status.ts`
|
|
237
|
+
- `x402-sponsor-relay/src/queue-consumer.ts`
|
|
238
|
+
- `x402-sponsor-relay/src/endpoints/chainhook.ts`
|
|
239
|
+
- `x402-sponsor-relay/src/durable-objects/nonce-do.ts`
|
|
240
|
+
- Relay Hiro settlement polling:
|
|
241
|
+
- `x402-sponsor-relay/src/services/settlement.ts`
|
package/package.json
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aibtc/tx-schemas",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared zod schemas for AIBTC transaction states, internal relay schemas, and external x402 payment schemas.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./core": {
|
|
15
|
+
"types": "./dist/core/index.d.ts",
|
|
16
|
+
"import": "./dist/core/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./core/enums": {
|
|
19
|
+
"types": "./dist/core/enums.d.ts",
|
|
20
|
+
"import": "./dist/core/enums.js"
|
|
21
|
+
},
|
|
22
|
+
"./terminal-reasons": {
|
|
23
|
+
"types": "./dist/core/terminal-reasons.d.ts",
|
|
24
|
+
"import": "./dist/core/terminal-reasons.js"
|
|
25
|
+
},
|
|
26
|
+
"./core/terminal-reasons": {
|
|
27
|
+
"types": "./dist/core/terminal-reasons.d.ts",
|
|
28
|
+
"import": "./dist/core/terminal-reasons.js"
|
|
29
|
+
},
|
|
30
|
+
"./core/primitives": {
|
|
31
|
+
"types": "./dist/core/primitives.d.ts",
|
|
32
|
+
"import": "./dist/core/primitives.js"
|
|
33
|
+
},
|
|
34
|
+
"./core/schemas": {
|
|
35
|
+
"types": "./dist/core/schemas.d.ts",
|
|
36
|
+
"import": "./dist/core/schemas.js"
|
|
37
|
+
},
|
|
38
|
+
"./rpc": {
|
|
39
|
+
"types": "./dist/rpc/index.d.ts",
|
|
40
|
+
"import": "./dist/rpc/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./rpc/schemas": {
|
|
43
|
+
"types": "./dist/rpc/schemas.d.ts",
|
|
44
|
+
"import": "./dist/rpc/schemas.js"
|
|
45
|
+
},
|
|
46
|
+
"./http": {
|
|
47
|
+
"types": "./dist/http/index.d.ts",
|
|
48
|
+
"import": "./dist/http/index.js"
|
|
49
|
+
},
|
|
50
|
+
"./http/schemas": {
|
|
51
|
+
"types": "./dist/http/schemas.d.ts",
|
|
52
|
+
"import": "./dist/http/schemas.js"
|
|
53
|
+
},
|
|
54
|
+
"./package.json": "./package.json"
|
|
55
|
+
},
|
|
56
|
+
"files": [
|
|
57
|
+
"dist",
|
|
58
|
+
"README.md",
|
|
59
|
+
"docs/package-schemas.md",
|
|
60
|
+
"docs/x402-approval-spec.md",
|
|
61
|
+
"docs/x402-state-machines.md",
|
|
62
|
+
"LICENSE"
|
|
63
|
+
],
|
|
64
|
+
"scripts": {
|
|
65
|
+
"build": "tsc -p tsconfig.build.json",
|
|
66
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"test:watch": "vitest",
|
|
69
|
+
"prepublishOnly": "npm run build && npm run typecheck && npm test",
|
|
70
|
+
"pack:dry-run": "npm pack --dry-run"
|
|
71
|
+
},
|
|
72
|
+
"keywords": [
|
|
73
|
+
"aibtc",
|
|
74
|
+
"x402",
|
|
75
|
+
"schemas",
|
|
76
|
+
"zod",
|
|
77
|
+
"stacks",
|
|
78
|
+
"payments",
|
|
79
|
+
"rpc",
|
|
80
|
+
"http"
|
|
81
|
+
],
|
|
82
|
+
"author": "aibtcdev",
|
|
83
|
+
"repository": {
|
|
84
|
+
"type": "git",
|
|
85
|
+
"url": "git+https://github.com/aibtcdev/tx-schemas.git"
|
|
86
|
+
},
|
|
87
|
+
"homepage": "https://github.com/aibtcdev/tx-schemas#readme",
|
|
88
|
+
"license": "MIT",
|
|
89
|
+
"publishConfig": {
|
|
90
|
+
"access": "public",
|
|
91
|
+
"registry": "https://registry.npmjs.org"
|
|
92
|
+
},
|
|
93
|
+
"dependencies": {
|
|
94
|
+
"zod": "^4.3.6"
|
|
95
|
+
},
|
|
96
|
+
"devDependencies": {
|
|
97
|
+
"@types/node": "^22.19.13",
|
|
98
|
+
"typescript": "^5.9.3",
|
|
99
|
+
"vitest": "^4.0.18"
|
|
100
|
+
}
|
|
101
|
+
}
|