@stellar-agent-services/provider-kit 0.0.0-stage → 0.1.1
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 +508 -2
- package/dist/approve/assets/index-BazIJAI2.css +1 -0
- package/dist/approve/assets/index-CL28cSJy.js +420 -0
- package/dist/approve/favicon.svg +1 -0
- package/dist/approve/index.html +18 -0
- package/dist/cli/approval-view.d.ts +80 -0
- package/dist/cli/approval-view.d.ts.map +1 -0
- package/dist/cli/approval-view.js +5 -0
- package/dist/cli/approval-view.js.map +1 -0
- package/dist/cli/approval.d.ts +132 -0
- package/dist/cli/approval.d.ts.map +1 -0
- package/dist/cli/approval.js +542 -0
- package/dist/cli/approval.js.map +1 -0
- package/dist/cli/check.d.ts +3 -0
- package/dist/cli/check.d.ts.map +1 -0
- package/dist/cli/check.js +137 -0
- package/dist/cli/check.js.map +1 -0
- package/dist/cli/dev-account.d.ts +28 -0
- package/dist/cli/dev-account.d.ts.map +1 -0
- package/dist/cli/dev-account.js +62 -0
- package/dist/cli/dev-account.js.map +1 -0
- package/dist/cli/dev.d.ts +27 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dev.js +226 -0
- package/dist/cli/dev.js.map +1 -0
- package/dist/cli/discovery.d.ts +20 -0
- package/dist/cli/discovery.d.ts.map +1 -0
- package/dist/cli/discovery.js +202 -0
- package/dist/cli/discovery.js.map +1 -0
- package/dist/cli/env-file.d.ts +16 -0
- package/dist/cli/env-file.d.ts.map +1 -0
- package/dist/cli/env-file.js +77 -0
- package/dist/cli/env-file.js.map +1 -0
- package/dist/cli/fly.d.ts +28 -0
- package/dist/cli/fly.d.ts.map +1 -0
- package/dist/cli/fly.js +92 -0
- package/dist/cli/fly.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +11 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +170 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/launch.d.ts +21 -0
- package/dist/cli/launch.d.ts.map +1 -0
- package/dist/cli/launch.js +627 -0
- package/dist/cli/launch.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +158 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/owner-chain.d.ts +88 -0
- package/dist/cli/owner-chain.d.ts.map +1 -0
- package/dist/cli/owner-chain.js +284 -0
- package/dist/cli/owner-chain.js.map +1 -0
- package/dist/cli/owner-steps.d.ts +33 -0
- package/dist/cli/owner-steps.d.ts.map +1 -0
- package/dist/cli/owner-steps.js +121 -0
- package/dist/cli/owner-steps.js.map +1 -0
- package/dist/cli/processes.d.ts +47 -0
- package/dist/cli/processes.d.ts.map +1 -0
- package/dist/cli/processes.js +129 -0
- package/dist/cli/processes.js.map +1 -0
- package/dist/cli/reconcile.d.ts +3 -0
- package/dist/cli/reconcile.d.ts.map +1 -0
- package/dist/cli/reconcile.js +76 -0
- package/dist/cli/reconcile.js.map +1 -0
- package/dist/cli/register.d.ts +9 -0
- package/dist/cli/register.d.ts.map +1 -0
- package/dist/cli/register.js +209 -0
- package/dist/cli/register.js.map +1 -0
- package/dist/cli/request-validation.d.ts +5 -0
- package/dist/cli/request-validation.d.ts.map +1 -0
- package/dist/cli/request-validation.js +144 -0
- package/dist/cli/request-validation.js.map +1 -0
- package/dist/cli/serve.d.ts +25 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +73 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/shared.d.ts +142 -0
- package/dist/cli/shared.d.ts.map +1 -0
- package/dist/cli/shared.js +110 -0
- package/dist/cli/shared.js.map +1 -0
- package/dist/cli/status.d.ts +32 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +248 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/target.d.ts +14 -0
- package/dist/cli/target.d.ts.map +1 -0
- package/dist/cli/target.js +43 -0
- package/dist/cli/target.js.map +1 -0
- package/dist/cli/trustline.d.ts +3 -0
- package/dist/cli/trustline.d.ts.map +1 -0
- package/dist/cli/trustline.js +114 -0
- package/dist/cli/trustline.js.map +1 -0
- package/dist/cli/try.d.ts +54 -0
- package/dist/cli/try.d.ts.map +1 -0
- package/dist/cli/try.js +450 -0
- package/dist/cli/try.js.map +1 -0
- package/dist/cli/tunnel.d.ts +13 -0
- package/dist/cli/tunnel.d.ts.map +1 -0
- package/dist/cli/tunnel.js +43 -0
- package/dist/cli/tunnel.js.map +1 -0
- package/dist/cli/validators.d.ts +6 -0
- package/dist/cli/validators.d.ts.map +1 -0
- package/dist/cli/validators.js +14 -0
- package/dist/cli/validators.js.map +1 -0
- package/dist/define-service.d.ts +71 -0
- package/dist/define-service.d.ts.map +1 -0
- package/dist/define-service.js +86 -0
- package/dist/define-service.js.map +1 -0
- package/dist/gateway/config.d.ts +88 -0
- package/dist/gateway/config.d.ts.map +1 -0
- package/dist/gateway/config.js +201 -0
- package/dist/gateway/config.js.map +1 -0
- package/dist/gateway/openapi.d.ts +69 -0
- package/dist/gateway/openapi.d.ts.map +1 -0
- package/dist/gateway/openapi.js +677 -0
- package/dist/gateway/openapi.js.map +1 -0
- package/dist/gateway/upstream.d.ts +15 -0
- package/dist/gateway/upstream.d.ts.map +1 -0
- package/dist/gateway/upstream.js +95 -0
- package/dist/gateway/upstream.js.map +1 -0
- package/dist/http/bounded.d.ts +3 -0
- package/dist/http/bounded.d.ts.map +1 -0
- package/dist/http/bounded.js +23 -0
- package/dist/http/bounded.js.map +1 -0
- package/dist/http/node-server.d.ts +15 -0
- package/dist/http/node-server.d.ts.map +1 -0
- package/dist/http/node-server.js +120 -0
- package/dist/http/node-server.js.map +1 -0
- package/dist/http/rate-limit.d.ts +11 -0
- package/dist/http/rate-limit.d.ts.map +1 -0
- package/dist/http/rate-limit.js +35 -0
- package/dist/http/rate-limit.js.map +1 -0
- package/dist/http/router.d.ts +29 -0
- package/dist/http/router.d.ts.map +1 -0
- package/dist/http/router.js +329 -0
- package/dist/http/router.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs/memory-store.d.ts +3 -0
- package/dist/jobs/memory-store.d.ts.map +1 -0
- package/dist/jobs/memory-store.js +121 -0
- package/dist/jobs/memory-store.js.map +1 -0
- package/dist/jobs/reconcile.d.ts +22 -0
- package/dist/jobs/reconcile.d.ts.map +1 -0
- package/dist/jobs/reconcile.js +82 -0
- package/dist/jobs/reconcile.js.map +1 -0
- package/dist/jobs/recovery.d.ts +19 -0
- package/dist/jobs/recovery.d.ts.map +1 -0
- package/dist/jobs/recovery.js +65 -0
- package/dist/jobs/recovery.js.map +1 -0
- package/dist/jobs/runner.d.ts +38 -0
- package/dist/jobs/runner.d.ts.map +1 -0
- package/dist/jobs/runner.js +194 -0
- package/dist/jobs/runner.js.map +1 -0
- package/dist/jobs/scheduler.d.ts +66 -0
- package/dist/jobs/scheduler.d.ts.map +1 -0
- package/dist/jobs/scheduler.js +197 -0
- package/dist/jobs/scheduler.js.map +1 -0
- package/dist/jobs/sqlite-store.d.ts +8 -0
- package/dist/jobs/sqlite-store.d.ts.map +1 -0
- package/dist/jobs/sqlite-store.js +231 -0
- package/dist/jobs/sqlite-store.js.map +1 -0
- package/dist/jobs/store.d.ts +118 -0
- package/dist/jobs/store.d.ts.map +1 -0
- package/dist/jobs/store.js +36 -0
- package/dist/jobs/store.js.map +1 -0
- package/dist/manifest.d.ts +11 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +99 -0
- package/dist/manifest.js.map +1 -0
- package/dist/payments/chain.d.ts +23 -0
- package/dist/payments/chain.d.ts.map +1 -0
- package/dist/payments/chain.js +108 -0
- package/dist/payments/chain.js.map +1 -0
- package/dist/payments/facilitator.d.ts +44 -0
- package/dist/payments/facilitator.d.ts.map +1 -0
- package/dist/payments/facilitator.js +125 -0
- package/dist/payments/facilitator.js.map +1 -0
- package/dist/payments/onchain.d.ts +36 -0
- package/dist/payments/onchain.d.ts.map +1 -0
- package/dist/payments/onchain.js +191 -0
- package/dist/payments/onchain.js.map +1 -0
- package/dist/payments/payload-checks.d.ts +22 -0
- package/dist/payments/payload-checks.d.ts.map +1 -0
- package/dist/payments/payload-checks.js +113 -0
- package/dist/payments/payload-checks.js.map +1 -0
- package/dist/payments/settlement.d.ts +22 -0
- package/dist/payments/settlement.d.ts.map +1 -0
- package/dist/payments/settlement.js +65 -0
- package/dist/payments/settlement.js.map +1 -0
- package/dist/payments/x402-server.d.ts +8 -0
- package/dist/payments/x402-server.d.ts.map +1 -0
- package/dist/payments/x402-server.js +74 -0
- package/dist/payments/x402-server.js.map +1 -0
- package/dist/provider.d.ts +63 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +177 -0
- package/dist/provider.js.map +1 -0
- package/dist/registration.d.ts +86 -0
- package/dist/registration.d.ts.map +1 -0
- package/dist/registration.js +162 -0
- package/dist/registration.js.map +1 -0
- package/dist/testing.d.ts +13 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +48 -0
- package/dist/testing.js.map +1 -0
- package/dist/validation-request.d.ts +36 -0
- package/dist/validation-request.d.ts.map +1 -0
- package/dist/validation-request.js +101 -0
- package/dist/validation-request.js.map +1 -0
- package/package.json +55 -4
- package/schemas/gateway-config.schema.json +140 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// Finding a job's payment on chain, for reconciliation [J13].
|
|
2
|
+
//
|
|
3
|
+
// A payment is one signed Soroban authorization: the payer's auth entry, with a nonce, for exactly one
|
|
4
|
+
// SAC transfer(payer, payTo, amount) (payments/payload-checks.ts). Soroban accepts an (address, nonce)
|
|
5
|
+
// pair once, so the successful transaction that carries the payer's auth entry with the job's nonce IS
|
|
6
|
+
// the job's payment. Any other transfer between the same accounts, for the same amount, belongs to
|
|
7
|
+
// another job. The lookup goes by that binding, never by recipient and amount alone:
|
|
8
|
+
//
|
|
9
|
+
// 1. the transaction the facilitator named, if any;
|
|
10
|
+
// 2. otherwise, or if that one failed, the asset's `transfer` events from the payer in every ledger the
|
|
11
|
+
// payment could have landed in, and the transactions behind them.
|
|
12
|
+
//
|
|
13
|
+
// "not_settled" needs proof of absence: the RPC searched every ledger up to the payment's expiration
|
|
14
|
+
// ledger, that ledger has closed, and none of them carried the payment. After it the signature is void,
|
|
15
|
+
// so the payment can never land. Anything less is "undecided" and the job stays payment_uncertain: a
|
|
16
|
+
// ledger still to come, a window older than the RPC's history, an RPC error, or a transaction that used
|
|
17
|
+
// the payment but shows no matching transfer. Nothing is decided from a guess.
|
|
18
|
+
import { Address, rpc, scValToNative, StrKey, xdr } from "@stellar/stellar-sdk";
|
|
19
|
+
import { addressCredentials } from "./payload-checks.js";
|
|
20
|
+
/**
|
|
21
|
+
* How far before its expiration ledger a payment can land. The provider (http/router.ts) and the
|
|
22
|
+
* facilitator both refuse a signature that expires more than maxTimeoutSeconds (at most 300, §4.1) of
|
|
23
|
+
* ledgers after the payment is verified, counting a ledger as at least one second (they take about
|
|
24
|
+
* five), and a payment lands after its verification. So this window holds every ledger it could have
|
|
25
|
+
* landed in, with room to spare.
|
|
26
|
+
*/
|
|
27
|
+
export const SEARCH_WINDOW_LEDGERS = 320;
|
|
28
|
+
/** Ledgers beyond the latest that a signature may expire in, on top of maxTimeoutSeconds. */
|
|
29
|
+
export const EXPIRATION_TOLERANCE_LEDGERS = 10;
|
|
30
|
+
const PAGE_SIZE = 100;
|
|
31
|
+
/** A payer with more transfers than this in one window is checked by hand. */
|
|
32
|
+
const MAX_PAGES = 10;
|
|
33
|
+
/**
|
|
34
|
+
* Looks for the payment `claim` describes. `known` is a transaction the facilitator named. `health` is
|
|
35
|
+
* a recent getHealth() answer, to share one across many lookups; it is read when not given.
|
|
36
|
+
*/
|
|
37
|
+
export async function findPayment(claim, known, search, health) {
|
|
38
|
+
const { chain } = search;
|
|
39
|
+
const checked = new Set();
|
|
40
|
+
const check = async (hash) => {
|
|
41
|
+
if (checked.has(hash))
|
|
42
|
+
return undefined;
|
|
43
|
+
checked.add(hash);
|
|
44
|
+
return paymentIn(await chain.getTransaction(hash), hash, claim, search);
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
if (known !== undefined) {
|
|
48
|
+
const found = await check(known);
|
|
49
|
+
if (found !== undefined)
|
|
50
|
+
return found;
|
|
51
|
+
}
|
|
52
|
+
const range = health ?? (await chain.getHealth());
|
|
53
|
+
const end = claim.expirationLedger;
|
|
54
|
+
const from = Math.max(1, end - SEARCH_WINDOW_LEDGERS);
|
|
55
|
+
const start = Math.max(from, range.oldestLedger);
|
|
56
|
+
// The lowest latest ledger any answer reported: with several RPC nodes behind one URL, the search
|
|
57
|
+
// has covered no more than the least advanced of them had seen.
|
|
58
|
+
let latest = range.latestLedger;
|
|
59
|
+
if (start <= Math.min(end, latest)) {
|
|
60
|
+
const filters = transferFilters(search.asset, claim.payer);
|
|
61
|
+
let request = { filters, startLedger: start, endLedger: Math.min(end, latest) + 1, limit: PAGE_SIZE };
|
|
62
|
+
for (let page = 0;; page++) {
|
|
63
|
+
if (page === MAX_PAGES)
|
|
64
|
+
return undecided(`${claim.payer} made more transfers than reconciliation searches; check its transfers with nonce ${claim.nonce} by hand`);
|
|
65
|
+
const response = await chain.getEvents(request);
|
|
66
|
+
latest = Math.min(latest, response.latestLedger);
|
|
67
|
+
let past = false;
|
|
68
|
+
for (const event of response.events) {
|
|
69
|
+
if (event.ledger > end) {
|
|
70
|
+
past = true;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
// The transaction's own status decides, not the event's: current RPCs leave the deprecated
|
|
74
|
+
// inSuccessfulContractCall unset.
|
|
75
|
+
const found = await check(event.txHash);
|
|
76
|
+
if (found !== undefined)
|
|
77
|
+
return found;
|
|
78
|
+
}
|
|
79
|
+
if (past || response.events.length < PAGE_SIZE)
|
|
80
|
+
break;
|
|
81
|
+
request = { filters, cursor: response.cursor, limit: PAGE_SIZE };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (latest <= end)
|
|
85
|
+
return undecided(`the payment can still land until ledger ${end} closes (the latest is ${latest})`);
|
|
86
|
+
if (start > from) {
|
|
87
|
+
return undecided(`ledgers ${from}–${start - 1} are older than the RPC's history; look for a transfer from ${claim.payer} with nonce ${claim.nonce} in an archive`);
|
|
88
|
+
}
|
|
89
|
+
return { outcome: "not_settled", reason: `no transaction used the payment before it expired at ledger ${end}` };
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
return undecided(`the chain could not be read: ${error.message}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Whether a transaction is the claim's payment: settled, undecided, or undefined when it isn't the payment. */
|
|
96
|
+
function paymentIn(transaction, hash, claim, search) {
|
|
97
|
+
if (transaction.status !== rpc.Api.GetTransactionStatus.SUCCESS)
|
|
98
|
+
return undefined;
|
|
99
|
+
const signed = signedTransfer(transaction.envelopeXdr, claim, search.asset);
|
|
100
|
+
if (signed === undefined)
|
|
101
|
+
return undefined;
|
|
102
|
+
// It used the payer's auth entry, so the signed transfer ran. Its event must say so too [J16].
|
|
103
|
+
const moved = transfersFrom(transaction.events.contractEventsXdr.flat(), search.asset, claim.payer);
|
|
104
|
+
if (!moved.some((transfer) => transfer.to === signed.to && transfer.amount === signed.amount)) {
|
|
105
|
+
return undecided(`transaction ${hash} used the payment, but its events show no transfer of ${signed.amount} to ${signed.to}`);
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
outcome: "settled",
|
|
109
|
+
receipt: {
|
|
110
|
+
network: search.network,
|
|
111
|
+
asset: search.asset,
|
|
112
|
+
amount: signed.amount,
|
|
113
|
+
payTo: signed.to,
|
|
114
|
+
payer: claim.payer,
|
|
115
|
+
transaction: hash,
|
|
116
|
+
settledAt: new Date(transaction.createdAt * 1000).toISOString(),
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The transfer the payer's auth entry with the claim's nonce authorizes, if the transaction carries it.
|
|
122
|
+
* An entry with that nonce for anything else means the nonce was spent elsewhere: not this payment.
|
|
123
|
+
*/
|
|
124
|
+
function signedTransfer(envelope, claim, asset) {
|
|
125
|
+
for (const operation of operationsOf(envelope)) {
|
|
126
|
+
if (operation.body().switch() !== xdr.OperationType.invokeHostFunction())
|
|
127
|
+
continue;
|
|
128
|
+
for (const entry of operation.body().invokeHostFunctionOp().auth()) {
|
|
129
|
+
const credentials = addressCredentials(entry.credentials());
|
|
130
|
+
if (credentials === undefined)
|
|
131
|
+
continue;
|
|
132
|
+
if (Address.fromScAddress(credentials.address()).toString() !== claim.payer || credentials.nonce().toString() !== claim.nonce)
|
|
133
|
+
continue;
|
|
134
|
+
const authorized = entry.rootInvocation().function();
|
|
135
|
+
if (authorized.switch() !== xdr.SorobanAuthorizedFunctionType.sorobanAuthorizedFunctionTypeContractFn())
|
|
136
|
+
return undefined;
|
|
137
|
+
const call = authorized.contractFn();
|
|
138
|
+
if (Address.fromScAddress(call.contractAddress()).toString() !== asset || call.functionName().toString() !== "transfer")
|
|
139
|
+
return undefined;
|
|
140
|
+
const [from, to, amount] = call.args().map((arg) => scValToNative(arg));
|
|
141
|
+
if (from !== claim.payer || typeof to !== "string" || typeof amount !== "bigint")
|
|
142
|
+
return undefined;
|
|
143
|
+
return { to, amount: amount.toString() };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
function operationsOf(envelope) {
|
|
149
|
+
switch (envelope.switch()) {
|
|
150
|
+
// The facilitator may wrap its transaction in a fee bump.
|
|
151
|
+
case xdr.EnvelopeType.envelopeTypeTxFeeBump():
|
|
152
|
+
return envelope.feeBump().tx().innerTx().v1().tx().operations();
|
|
153
|
+
case xdr.EnvelopeType.envelopeTypeTx():
|
|
154
|
+
return envelope.v1().tx().operations();
|
|
155
|
+
default:
|
|
156
|
+
return envelope.v0().tx().operations();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** The asset's transfer events from `payer`. */
|
|
160
|
+
function transfersFrom(events, asset, payer) {
|
|
161
|
+
return events.flatMap((event) => {
|
|
162
|
+
const contractId = event.contractId();
|
|
163
|
+
// Typed as Opaque[], but the SDK's own Buffer at runtime.
|
|
164
|
+
if (contractId === null || StrKey.encodeContract(contractId) !== asset)
|
|
165
|
+
return [];
|
|
166
|
+
const topics = event.body().v0().topics().map((topic) => scValToNative(topic));
|
|
167
|
+
if (topics[0] !== "transfer" || topics[1] !== payer)
|
|
168
|
+
return [];
|
|
169
|
+
return [{ to: topics[2], amount: transferAmount(event.body().v0().data())?.toString() }];
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
// The data is an i128, or a map with `amount` when the payee is a muxed account (CAP-67).
|
|
173
|
+
function transferAmount(data) {
|
|
174
|
+
const value = scValToNative(data);
|
|
175
|
+
if (typeof value === "bigint")
|
|
176
|
+
return value;
|
|
177
|
+
if (typeof value === "object" && value !== null && typeof value.amount === "bigint") {
|
|
178
|
+
return value.amount;
|
|
179
|
+
}
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
/** Transfer events from `payer`: a SAC's have a fourth topic, the asset's name; other SEP-41 tokens' have three. */
|
|
183
|
+
function transferFilters(asset, payer) {
|
|
184
|
+
const transfer = xdr.ScVal.scvSymbol("transfer").toXDR("base64");
|
|
185
|
+
const from = Address.fromString(payer).toScVal().toXDR("base64");
|
|
186
|
+
return [{ type: "contract", contractIds: [asset], topics: [[transfer, from, "*", "*"], [transfer, from, "*"]] }];
|
|
187
|
+
}
|
|
188
|
+
function undecided(reason) {
|
|
189
|
+
return { outcome: "undecided", reason };
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=onchain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onchain.js","sourceRoot":"","sources":["../../src/payments/onchain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { xdr } from "@stellar/stellar-sdk";
|
|
2
|
+
import type { PaymentPayload, PaymentRequirements } from "@x402/core/types";
|
|
3
|
+
/** What the checks learned about a payment that passed them. */
|
|
4
|
+
export interface CheckedPayment {
|
|
5
|
+
payer: string;
|
|
6
|
+
/** The payer's auth-entry nonce, as a decimal string. */
|
|
7
|
+
nonce: string;
|
|
8
|
+
/** The last ledger at which the payer's signature is valid. Reconciliation needs it. */
|
|
9
|
+
expirationLedger: number;
|
|
10
|
+
/** sha256(JCS({ network, payer, nonce })): one per signed payment [P9]. */
|
|
11
|
+
fingerprint: string;
|
|
12
|
+
}
|
|
13
|
+
/** A refused payment: an x402 error reason for the 402 (§4.3). */
|
|
14
|
+
export interface RefusedPayment {
|
|
15
|
+
reason: string;
|
|
16
|
+
}
|
|
17
|
+
/** Checks a decoded x402 payload against the requirements this provider issued. */
|
|
18
|
+
export declare function checkPayment(payload: PaymentPayload, requirements: PaymentRequirements): Promise<CheckedPayment | RefusedPayment>;
|
|
19
|
+
export declare function isRefused(result: CheckedPayment | RefusedPayment): result is RefusedPayment;
|
|
20
|
+
/** Address credentials, in either encoding the x402 facilitator accepts. */
|
|
21
|
+
export declare function addressCredentials(credentials: xdr.SorobanCredentials): xdr.SorobanAddressCredentials | undefined;
|
|
22
|
+
//# sourceMappingURL=payload-checks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-checks.d.ts","sourceRoot":"","sources":["../../src/payments/payload-checks.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAiD,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAC1F,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5E,gEAAgE;AAChE,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,gBAAgB,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,kEAAkE;AAClE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB;AAYD,mFAAmF;AACnF,wBAAsB,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,GAAG,cAAc,CAAC,CA0DvI;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,GAAG,MAAM,IAAI,cAAc,CAE3F;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,GAAG,CAAC,kBAAkB,GAAG,GAAG,CAAC,yBAAyB,GAAG,SAAS,CASjH"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Local payment checks, run BEFORE the facilitator sees the payment.
|
|
2
|
+
//
|
|
3
|
+
// An x402 `exact` payment on Stellar is an unsigned Soroban transaction with one invokeHostFunction:
|
|
4
|
+
// the USDC SAC `transfer(payer, payTo, amount)`, authorized by the payer's signed auth entry. The
|
|
5
|
+
// facilitator checks it too, but the provider does not have to trust that: a payment that would pay
|
|
6
|
+
// someone else, another amount or another asset is refused here with a 402.
|
|
7
|
+
//
|
|
8
|
+
// What these checks can't see is left to the facilitator, which simulates the transaction: whether the
|
|
9
|
+
// signature is valid, for this network, under the payer's account signers or smart-wallet contract.
|
|
10
|
+
//
|
|
11
|
+
// The checks also read the payer's auth-entry nonce. Soroban accepts one (address, nonce) once, so
|
|
12
|
+
// the payment fingerprint is sha256(JCS({ network, payer, nonce })): re-wrapping the same signed auth
|
|
13
|
+
// entry in another transaction still gives the same fingerprint, and the job store refuses a second
|
|
14
|
+
// job for it [P9].
|
|
15
|
+
import { canonicalSha256 } from "@stellar-agent-services/protocol";
|
|
16
|
+
import { Address, Networks, scValToNative, Transaction, xdr } from "@stellar/stellar-sdk";
|
|
17
|
+
const PASSPHRASES = {
|
|
18
|
+
"stellar:testnet": Networks.TESTNET,
|
|
19
|
+
"stellar:pubnet": Networks.PUBLIC,
|
|
20
|
+
};
|
|
21
|
+
/** A signed transfer is about 1 KiB; anything far larger is not one. */
|
|
22
|
+
const MAX_TRANSACTION_BASE64_LENGTH = 16 * 1024;
|
|
23
|
+
const refused = (reason) => ({ reason: `invalid_exact_stellar_payload_${reason}` });
|
|
24
|
+
/** Checks a decoded x402 payload against the requirements this provider issued. */
|
|
25
|
+
export async function checkPayment(payload, requirements) {
|
|
26
|
+
const network = requirements.network;
|
|
27
|
+
const passphrase = PASSPHRASES[network];
|
|
28
|
+
if (passphrase === undefined)
|
|
29
|
+
return { reason: "invalid_network" };
|
|
30
|
+
const encoded = payload.payload?.transaction;
|
|
31
|
+
if (typeof encoded !== "string" || encoded.length > MAX_TRANSACTION_BASE64_LENGTH)
|
|
32
|
+
return refused("malformed");
|
|
33
|
+
let transaction;
|
|
34
|
+
try {
|
|
35
|
+
transaction = new Transaction(encoded, passphrase);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return refused("malformed");
|
|
39
|
+
}
|
|
40
|
+
// One operation: invokeHostFunction calling the asset's contract.
|
|
41
|
+
if (transaction.operations.length !== 1)
|
|
42
|
+
return refused("wrong_operation");
|
|
43
|
+
const operation = transaction.operations[0];
|
|
44
|
+
if (operation.type !== "invokeHostFunction")
|
|
45
|
+
return refused("wrong_operation");
|
|
46
|
+
if (operation.func.switch() !== xdr.HostFunctionType.hostFunctionTypeInvokeContract())
|
|
47
|
+
return refused("wrong_operation");
|
|
48
|
+
const call = operation.func.invokeContract();
|
|
49
|
+
// transfer(payer, payTo, amount) on the USDC contract, for exactly the price.
|
|
50
|
+
let from;
|
|
51
|
+
let to;
|
|
52
|
+
let amount;
|
|
53
|
+
try {
|
|
54
|
+
if (Address.fromScAddress(call.contractAddress()).toString() !== requirements.asset)
|
|
55
|
+
return refused("wrong_asset");
|
|
56
|
+
if (call.functionName().toString() !== "transfer" || call.args().length !== 3)
|
|
57
|
+
return refused("wrong_function_name");
|
|
58
|
+
[from, to, amount] = call.args().map((arg) => scValToNative(arg));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return refused("malformed");
|
|
62
|
+
}
|
|
63
|
+
if (to !== requirements.payTo)
|
|
64
|
+
return refused("wrong_recipient");
|
|
65
|
+
if (typeof amount !== "bigint" || amount !== BigInt(requirements.amount))
|
|
66
|
+
return refused("wrong_amount");
|
|
67
|
+
if (from === requirements.payTo)
|
|
68
|
+
return refused("wrong_payer");
|
|
69
|
+
// Exactly one auth entry: the payer's signature over this same transfer and nothing else.
|
|
70
|
+
const auth = operation.auth ?? [];
|
|
71
|
+
if (auth.length === 0)
|
|
72
|
+
return refused("no_auth_entries");
|
|
73
|
+
if (auth.length > 1)
|
|
74
|
+
return refused("multiple_auth_entries");
|
|
75
|
+
const entry = auth[0];
|
|
76
|
+
const credentials = addressCredentials(entry.credentials());
|
|
77
|
+
if (credentials === undefined)
|
|
78
|
+
return refused("unsupported_credential_type");
|
|
79
|
+
if (Address.fromScAddress(credentials.address()).toString() !== from)
|
|
80
|
+
return refused("auth_not_by_payer");
|
|
81
|
+
if (credentials.signature().switch() === xdr.ScValType.scvVoid())
|
|
82
|
+
return refused("missing_payer_signature");
|
|
83
|
+
const root = entry.rootInvocation();
|
|
84
|
+
if (root.subInvocations().length > 0)
|
|
85
|
+
return refused("has_subinvocations");
|
|
86
|
+
const authorized = root.function();
|
|
87
|
+
if (authorized.switch() !== xdr.SorobanAuthorizedFunctionType.sorobanAuthorizedFunctionTypeContractFn())
|
|
88
|
+
return refused("auth_mismatch");
|
|
89
|
+
if (!authorized.contractFn().toXDR().equals(call.toXDR()))
|
|
90
|
+
return refused("auth_mismatch");
|
|
91
|
+
const nonce = credentials.nonce().toString();
|
|
92
|
+
return {
|
|
93
|
+
payer: from,
|
|
94
|
+
nonce,
|
|
95
|
+
expirationLedger: credentials.signatureExpirationLedger(),
|
|
96
|
+
fingerprint: await canonicalSha256({ network, payer: from, nonce }),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export function isRefused(result) {
|
|
100
|
+
return "reason" in result;
|
|
101
|
+
}
|
|
102
|
+
/** Address credentials, in either encoding the x402 facilitator accepts. */
|
|
103
|
+
export function addressCredentials(credentials) {
|
|
104
|
+
switch (credentials.switch()) {
|
|
105
|
+
case xdr.SorobanCredentialsType.sorobanCredentialsAddress():
|
|
106
|
+
return credentials.address();
|
|
107
|
+
case xdr.SorobanCredentialsType.sorobanCredentialsAddressV2():
|
|
108
|
+
return credentials.addressV2();
|
|
109
|
+
default:
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=payload-checks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload-checks.js","sourceRoot":"","sources":["../../src/payments/payload-checks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PaymentReceipt } from "@stellar-agent-services/protocol";
|
|
2
|
+
import type { PaymentPayload, PaymentRequirements, SettleResponse } from "@x402/core/types";
|
|
3
|
+
import type { Facilitator } from "./facilitator.js";
|
|
4
|
+
export type Settlement = {
|
|
5
|
+
outcome: "settled";
|
|
6
|
+
receipt: PaymentReceipt;
|
|
7
|
+
response: SettleResponse;
|
|
8
|
+
} | {
|
|
9
|
+
outcome: "failed";
|
|
10
|
+
reason: string;
|
|
11
|
+
}
|
|
12
|
+
/** `transaction`: a hash the facilitator named, which reconciliation checks first. */
|
|
13
|
+
| {
|
|
14
|
+
outcome: "uncertain";
|
|
15
|
+
reason: string;
|
|
16
|
+
transaction?: string;
|
|
17
|
+
};
|
|
18
|
+
/** Whether a settle error reason means the transaction was never submitted. */
|
|
19
|
+
export declare function neverSubmitted(reason: string): boolean;
|
|
20
|
+
/** Settles once. `payer` is the address the provider's own payment checks found. */
|
|
21
|
+
export declare function settle(facilitator: Facilitator, payload: PaymentPayload, requirements: PaymentRequirements, payer: string): Promise<Settlement>;
|
|
22
|
+
//# sourceMappingURL=settlement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settlement.d.ts","sourceRoot":"","sources":["../../src/payments/settlement.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,kCAAkC,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC5F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,MAAM,UAAU,GAClB;IAAE,OAAO,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,cAAc,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,GACzE;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACvC,sFAAsF;GACpF;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAoBnE,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEtD;AAID,oFAAoF;AACpF,wBAAsB,MAAM,CAC1B,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,mBAAmB,EACjC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,UAAU,CAAC,CA+BrB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Classifies the settlement outcome. This is the core of "never charge twice, never lose a payment".
|
|
2
|
+
//
|
|
3
|
+
// - "settled": success with a transaction hash, for the payer and network the provider checked.
|
|
4
|
+
// - "failed": ONLY when the facilitator answered success: false, with no transaction hash, for a
|
|
5
|
+
// reason that means the transaction was never submitted (NOT_SUBMITTED). The user is not charged.
|
|
6
|
+
// - "uncertain": everything else: timeouts, 5xx, thrown errors, unknown reasons, a transaction hash
|
|
7
|
+
// without success, or a success that names another payer or network. The job becomes
|
|
8
|
+
// payment_uncertain and is decided from on-chain data, never by charging again [J13].
|
|
9
|
+
/**
|
|
10
|
+
* Reasons the @x402/stellar facilitator gives before it submits anything: its settle() verifies the
|
|
11
|
+
* payment again first, and these are its verify and pre-submission failures. Any reason starting with
|
|
12
|
+
* "invalid_" is a verify failure too. A reason not listed here is treated as uncertain, so a
|
|
13
|
+
* facilitator with other wording never makes the kit drop a payment that may have landed.
|
|
14
|
+
*/
|
|
15
|
+
const NOT_SUBMITTED = new Set([
|
|
16
|
+
"unsupported_scheme",
|
|
17
|
+
"network_mismatch",
|
|
18
|
+
"verification_failed",
|
|
19
|
+
"unexpected_verify_error",
|
|
20
|
+
"settle_exact_stellar_signer_selection_failed",
|
|
21
|
+
"settle_exact_stellar_transaction_signing_failed",
|
|
22
|
+
"settle_exact_stellar_fee_bump_signing_failed",
|
|
23
|
+
// sendTransaction did not accept it (ERROR, TRY_AGAIN_LATER): it never reached a ledger.
|
|
24
|
+
"settle_exact_stellar_transaction_submission_failed",
|
|
25
|
+
]);
|
|
26
|
+
/** Whether a settle error reason means the transaction was never submitted. */
|
|
27
|
+
export function neverSubmitted(reason) {
|
|
28
|
+
return reason.startsWith("invalid_") || NOT_SUBMITTED.has(reason);
|
|
29
|
+
}
|
|
30
|
+
const TRANSACTION_HASH = /^[0-9a-f]{64}$/;
|
|
31
|
+
/** Settles once. `payer` is the address the provider's own payment checks found. */
|
|
32
|
+
export async function settle(facilitator, payload, requirements, payer) {
|
|
33
|
+
let response;
|
|
34
|
+
try {
|
|
35
|
+
response = await facilitator.settle(payload, requirements);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
return { outcome: "uncertain", reason: error instanceof Error ? `${error.name}: ${error.message}` : String(error) };
|
|
39
|
+
}
|
|
40
|
+
const transaction = typeof response.transaction === "string" ? response.transaction.toLowerCase() : "";
|
|
41
|
+
const uncertain = (reason) => TRANSACTION_HASH.test(transaction) ? { outcome: "uncertain", reason, transaction } : { outcome: "uncertain", reason };
|
|
42
|
+
if (response.success === true) {
|
|
43
|
+
if (!TRANSACTION_HASH.test(transaction))
|
|
44
|
+
return uncertain("settled without a transaction hash");
|
|
45
|
+
if (response.payer !== undefined && response.payer !== payer)
|
|
46
|
+
return uncertain("the facilitator named another payer");
|
|
47
|
+
if (response.network !== requirements.network)
|
|
48
|
+
return uncertain("the facilitator named another network");
|
|
49
|
+
const receipt = {
|
|
50
|
+
network: requirements.network,
|
|
51
|
+
asset: requirements.asset,
|
|
52
|
+
amount: requirements.amount,
|
|
53
|
+
payTo: requirements.payTo,
|
|
54
|
+
payer,
|
|
55
|
+
transaction,
|
|
56
|
+
settledAt: new Date().toISOString(),
|
|
57
|
+
};
|
|
58
|
+
return { outcome: "settled", receipt, response };
|
|
59
|
+
}
|
|
60
|
+
const reason = response.errorReason ?? "settlement failed";
|
|
61
|
+
if (transaction === "" && neverSubmitted(reason))
|
|
62
|
+
return { outcome: "failed", reason };
|
|
63
|
+
return uncertain(reason);
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=settlement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settlement.js","sourceRoot":"","sources":["../../src/payments/settlement.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,EAAE;AACF,gGAAgG;AAChG,iGAAiG;AACjG,oGAAoG;AACpG,oGAAoG;AACpG,uFAAuF;AACvF,wFAAwF;AAYxF;;;;;GAKG;AACH,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC;IACjD,oBAAoB;IACpB,kBAAkB;IAClB,qBAAqB;IACrB,yBAAyB;IACzB,8CAA8C;IAC9C,iDAAiD;IACjD,8CAA8C;IAC9C,yFAAyF;IACzF,oDAAoD;CACrD,CAAC,CAAC;AAEH,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,gBAAgB,GAAG,gBAAgB,CAAC;AAE1C,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,WAAwB,EACxB,OAAuB,EACvB,YAAiC,EACjC,KAAa;IAEb,IAAI,QAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACtH,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,QAAQ,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACvG,MAAM,SAAS,GAAG,CAAC,MAAc,EAAc,EAAE,CAC/C,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;IAExH,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC9B,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC;YAAE,OAAO,SAAS,CAAC,oCAAoC,CAAC,CAAC;QAChG,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,IAAI,QAAQ,CAAC,KAAK,KAAK,KAAK;YAAE,OAAO,SAAS,CAAC,qCAAqC,CAAC,CAAC;QACtH,IAAI,QAAQ,CAAC,OAAO,KAAK,YAAY,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC,uCAAuC,CAAC,CAAC;QACzG,MAAM,OAAO,GAAmB;YAC9B,OAAO,EAAE,YAAY,CAAC,OAAyB;YAC/C,KAAK,EAAE,YAAY,CAAC,KAAK;YACzB,MAAM,EAAE,YAAY,CAAC,MAAM;YAC3B,KAAK,EAAE,YAAY,CAAC,KAAK;YACzB,KAAK;YACL,WAAW;YACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IACnD,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,WAAW,IAAI,mBAAmB,CAAC;IAC3D,IAAI,WAAW,KAAK,EAAE,IAAI,cAAc,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACvF,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ProviderManifest } from "@stellar-agent-services/protocol";
|
|
2
|
+
import type { PaymentPayload, PaymentRequired, PaymentRequirements } from "@x402/core/types";
|
|
3
|
+
import type { Service } from "../define-service.js";
|
|
4
|
+
export declare function paymentRequirements(manifest: ProviderManifest, service: Service): PaymentRequirements;
|
|
5
|
+
export declare function paymentRequired(manifest: ProviderManifest, service: Service, error?: string): PaymentRequired;
|
|
6
|
+
/** Decodes PAYMENT-SIGNATURE. Returns undefined for a header that isn't a v2 payload for these requirements. */
|
|
7
|
+
export declare function readPayment(header: string, requirements: PaymentRequirements): PaymentPayload | undefined;
|
|
8
|
+
//# sourceMappingURL=x402-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x402-server.d.ts","sourceRoot":"","sources":["../../src/payments/x402-server.ts"],"names":[],"mappings":"AAOA,OAAO,EAAiC,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAExG,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC7F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAKpD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAGrG;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,CAS7G;AAoCD,gHAAgH;AAChH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,mBAAmB,GAAG,cAAc,GAAG,SAAS,CAWzG"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// x402 resource server side: the 402 challenge and reading the paid request.
|
|
2
|
+
//
|
|
3
|
+
// The challenge is built from the manifest [P4]: exactly one `accepts` entry equal to the manifest's
|
|
4
|
+
// payment option plus `amount` = the service price, and resource.url is the create-job URL [P5]. It
|
|
5
|
+
// carries x402's Bazaar discovery extension, built from the service's schemas and first example [P6],
|
|
6
|
+
// unless that would make the PAYMENT-REQUIRED header too large for common HTTP clients.
|
|
7
|
+
import { createJobUrl } from "@stellar-agent-services/protocol";
|
|
8
|
+
import { decodePaymentSignatureHeader, encodePaymentRequiredHeader } from "@x402/core/http";
|
|
9
|
+
/** Many HTTP stacks cap all headers together at 8–16 KiB, so the extension is left out above this. */
|
|
10
|
+
const MAX_CHALLENGE_HEADER_BYTES = 8 * 1024;
|
|
11
|
+
export function paymentRequirements(manifest, service) {
|
|
12
|
+
const { scheme, network, asset, payTo, maxTimeoutSeconds, extra } = manifest.payment.accepts[0];
|
|
13
|
+
return { scheme, network, asset, amount: service.amount, payTo, maxTimeoutSeconds, extra };
|
|
14
|
+
}
|
|
15
|
+
export function paymentRequired(manifest, service, error) {
|
|
16
|
+
const challenge = {
|
|
17
|
+
x402Version: 2,
|
|
18
|
+
resource: { url: createJobUrl(manifest.baseUrl, service.name), description: service.description, mimeType: "application/json" },
|
|
19
|
+
accepts: [paymentRequirements(manifest, service)],
|
|
20
|
+
};
|
|
21
|
+
if (error !== undefined)
|
|
22
|
+
challenge.error = error;
|
|
23
|
+
const discoverable = { ...challenge, extensions: { bazaar: bazaar(service) } };
|
|
24
|
+
return encodePaymentRequiredHeader(discoverable).length <= MAX_CHALLENGE_HEADER_BYTES ? discoverable : challenge;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The Bazaar discovery extension (x402 `extensions.bazaar`, HTTP body form): an example request and
|
|
28
|
+
* response, and a schema whose `body` is the service's inputSchema.
|
|
29
|
+
*/
|
|
30
|
+
function bazaar(service) {
|
|
31
|
+
const example = service.examples?.[0];
|
|
32
|
+
const info = { input: { type: "http", method: "POST", bodyType: "json", body: example?.input ?? {} } };
|
|
33
|
+
const properties = {
|
|
34
|
+
input: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {
|
|
37
|
+
type: { type: "string", const: "http" },
|
|
38
|
+
method: { type: "string", enum: ["POST"] },
|
|
39
|
+
bodyType: { type: "string", enum: ["json"] },
|
|
40
|
+
body: service.inputSchema,
|
|
41
|
+
},
|
|
42
|
+
required: ["type", "method", "bodyType", "body"],
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
if (example !== undefined) {
|
|
47
|
+
info["output"] = { type: "json", example: example.output };
|
|
48
|
+
properties["output"] = {
|
|
49
|
+
type: "object",
|
|
50
|
+
properties: { type: { type: "string" }, example: service.outputSchema },
|
|
51
|
+
required: ["type"],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
info,
|
|
56
|
+
schema: { $schema: "https://json-schema.org/draft/2020-12/schema", type: "object", properties, required: ["input"] },
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Decodes PAYMENT-SIGNATURE. Returns undefined for a header that isn't a v2 payload for these requirements. */
|
|
60
|
+
export function readPayment(header, requirements) {
|
|
61
|
+
let payload;
|
|
62
|
+
try {
|
|
63
|
+
payload = decodePaymentSignatureHeader(header);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const accepted = payload.accepted;
|
|
69
|
+
const fields = ["scheme", "network", "asset", "amount", "payTo"];
|
|
70
|
+
if (payload.x402Version !== 2 || accepted === undefined)
|
|
71
|
+
return undefined;
|
|
72
|
+
return fields.every((field) => accepted[field] === requirements[field]) ? payload : undefined;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=x402-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x402-server.js","sourceRoot":"","sources":["../../src/payments/x402-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type ProviderInfo, type ProviderManifest, type StellarNetwork } from "@stellar-agent-services/protocol";
|
|
2
|
+
import type { Service } from "./define-service.js";
|
|
3
|
+
import { type RateLimitConfig } from "./http/rate-limit.js";
|
|
4
|
+
import type { JobStore } from "./jobs/store.js";
|
|
5
|
+
import { type ChainReader } from "./payments/chain.js";
|
|
6
|
+
import { type Facilitator } from "./payments/facilitator.js";
|
|
7
|
+
export interface ProviderConfig {
|
|
8
|
+
/** Public HTTPS URL that job paths are relative to. http://localhost only with `dev`. */
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
network: StellarNetwork;
|
|
11
|
+
/** The address that receives payments. Public, never a secret key. */
|
|
12
|
+
payTo: string;
|
|
13
|
+
provider: ProviderInfo;
|
|
14
|
+
services: Service[];
|
|
15
|
+
/** A facilitator URL, or a client: httpFacilitator(url, options) for API-key headers, a fake in tests. */
|
|
16
|
+
facilitator: string | Facilitator;
|
|
17
|
+
/** Default: memoryStore(), which loses jobs on restart. Use sqliteStore(path) in production. */
|
|
18
|
+
store?: JobStore;
|
|
19
|
+
/**
|
|
20
|
+
* The Soroban RPC to read the chain with: a URL, or a ChainReader (a fake one in tests). It decides
|
|
21
|
+
* payment_uncertain jobs and times on-success jobs by ledger. Default: the public RPC on testnet.
|
|
22
|
+
* pubnet has no public one, so pass your own. false: no chain reads, and payment_uncertain jobs wait
|
|
23
|
+
* for `provider-kit reconcile`.
|
|
24
|
+
*/
|
|
25
|
+
rpc?: string | ChainReader | false;
|
|
26
|
+
/** How long a signed payment is valid, 30–300 (§4.1). Default 60. */
|
|
27
|
+
maxTimeoutSeconds?: number;
|
|
28
|
+
/**
|
|
29
|
+
* How long a settlement may take before the job is payment_uncertain. Default maxTimeoutSeconds + 30,
|
|
30
|
+
* since the facilitator waits for the transaction's ledger. A later answer is still recorded.
|
|
31
|
+
*/
|
|
32
|
+
settleTimeoutSeconds?: number;
|
|
33
|
+
/** How long finished jobs stay retrievable [J15]. At least 3600; default 86400. */
|
|
34
|
+
retentionSeconds?: number;
|
|
35
|
+
/** Default /.well-known/stellar-agent-services.json on the baseUrl origin. */
|
|
36
|
+
manifestPath?: string;
|
|
37
|
+
/** How long create-job waits before answering 202. Default and maximum 30 [J9]. */
|
|
38
|
+
holdSeconds?: number;
|
|
39
|
+
/** Per-caller limit on create-job requests (429). Default: none. Capacity is set per service. */
|
|
40
|
+
rateLimit?: RateLimitConfig;
|
|
41
|
+
/**
|
|
42
|
+
* Whether this process runs queued upfront jobs. Default true. Set false on HTTP-only instances when
|
|
43
|
+
* separate worker processes, with the same services and store, run them.
|
|
44
|
+
*/
|
|
45
|
+
runQueuedJobs?: boolean;
|
|
46
|
+
/** How long a worker's claim on a running upfront job lasts without renewal. Default 30. */
|
|
47
|
+
leaseSeconds?: number;
|
|
48
|
+
/** Allows an http://localhost baseUrl and facilitator for local development [M6]. */
|
|
49
|
+
dev?: boolean;
|
|
50
|
+
/** Test-only response-loss drill (docs/job-recovery.md). Never enable in production. */
|
|
51
|
+
loseCreateJobResponse?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface Provider {
|
|
54
|
+
manifest: ProviderManifest;
|
|
55
|
+
manifestUrl: string;
|
|
56
|
+
fetch(request: Request): Promise<Response>;
|
|
57
|
+
/** Startup checks, recovery and workers. Call it before serving; it throws if payments can't work. */
|
|
58
|
+
ready(): Promise<void>;
|
|
59
|
+
/** Stops the workers and timers and closes the store. Jobs in progress are resumed by the next start. */
|
|
60
|
+
close(): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
export declare function createProvider(config: ProviderConfig): Provider;
|
|
63
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAwE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvL,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAOzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAyB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAA2D,KAAK,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAEtH,MAAM,WAAW,cAAc;IAC7B,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,cAAc,CAAC;IACxB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,0GAA0G;IAC1G,WAAW,EAAE,MAAM,GAAG,WAAW,CAAC;IAClC,gGAAgG;IAChG,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,KAAK,CAAC;IACnC,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iGAAiG;IACjG,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qFAAqF;IACrF,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,wFAAwF;IACxF,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3C,sGAAsG;IACtG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,yGAAyG;IACzG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAMD,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,QAAQ,CAyI/D"}
|