@mooring-live/x402-client 0.0.0-stage → 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/dist/scheme.js ADDED
@@ -0,0 +1,220 @@
1
+ import { Address, contract, nativeToScVal, rpc } from "@stellar/stellar-sdk";
2
+ import { findDefaultAsset, getNetworkPassphrase, getRpcUrl, validateStellarAssetAddress, validateStellarDestinationAddress, } from "@x402/stellar";
3
+ import { CARD_ERROR_CODES, CardPolicyDenied, PaymentError, classifyContractError } from "./denial.js";
4
+ import { signCardAuthEntries } from "./signer.js";
5
+ /**
6
+ * Seconds per ledger assumed when turning `maxTimeoutSeconds` into an auth
7
+ * expiration. Deliberately slower than `@x402/stellar`'s 5 s constant: the
8
+ * facilitator derives its own ceiling from a Horizon sample, and a slower
9
+ * ledger makes that ceiling *smaller*. Assuming 6 s keeps the client below it.
10
+ */
11
+ const CONSERVATIVE_LEDGER_SECONDS = 6;
12
+ /** Ledgers subtracted from (and floored at) the derived expiration. */
13
+ const LEDGER_MARGIN = 2;
14
+ /**
15
+ * How far ahead the card's auth entry may be valid.
16
+ *
17
+ * The facilitator computes `maxLedger = currentLedger + ceil(maxTimeoutSeconds
18
+ * / estimatedLedgerSeconds)` and rejects an entry more than 2 ledgers beyond
19
+ * it (`invalid_exact_stellar_signature_expiration_too_far`). Its estimate is
20
+ * sampled from Horizon, so it can be slower than the 5 s the stock client
21
+ * assumes — on pubnet it usually is. Aiming low costs nothing: the expiry only
22
+ * has to outlive settlement, and a shorter one is never rejected.
23
+ */
24
+ export function authExpirationLedgers(maxTimeoutSeconds) {
25
+ return Math.max(LEDGER_MARGIN, Math.ceil(maxTimeoutSeconds / CONSERVATIVE_LEDGER_SECONDS) - LEDGER_MARGIN);
26
+ }
27
+ /**
28
+ * x402 "exact" scheme client whose payer is a Mooring card (a Soroban custom
29
+ * account). It produces exactly the same payload as `@x402/stellar`'s
30
+ * `ExactStellarScheme` — one `invokeHostFunction` calling
31
+ * `asset.transfer(card, payTo, amount)`, re-simulated after signing, returned
32
+ * as a base64 transaction envelope. Only the signing differs: the card's auth
33
+ * entries are signed by the agent key through `signCardAuthEntries`, so
34
+ * `__check_auth` enforces the budget, allowlist and expiry at settlement.
35
+ */
36
+ export class CardExactStellarScheme {
37
+ scheme = "exact";
38
+ findDefaultAsset = findDefaultAsset;
39
+ card;
40
+ agent;
41
+ network;
42
+ rpcUrl;
43
+ passphrase;
44
+ onDenial;
45
+ onPaymentError;
46
+ constructor(opts) {
47
+ this.card = opts.card;
48
+ this.agent = opts.agent;
49
+ this.network = opts.network;
50
+ this.onDenial = opts.onDenial;
51
+ this.onPaymentError = opts.onPaymentError;
52
+ this.rpcUrl = getRpcUrl(opts.network, opts.rpcUrl ? { url: opts.rpcUrl } : undefined);
53
+ this.passphrase = getNetworkPassphrase(opts.network);
54
+ }
55
+ /** Throws unless the requirements are an `exact` payment this client can make. */
56
+ validateRequirements(req) {
57
+ if (req.scheme !== "exact")
58
+ throw new Error(`Unsupported scheme: ${req.scheme}`);
59
+ if (req.network !== this.network) {
60
+ throw new Error(`Unsupported network: ${req.network} (client is ${this.network})`);
61
+ }
62
+ if (!validateStellarDestinationAddress(req.payTo)) {
63
+ throw new Error(`Invalid Stellar destination address: ${req.payTo}`);
64
+ }
65
+ if (!validateStellarAssetAddress(req.asset)) {
66
+ throw new Error(`Invalid Stellar asset address: ${req.asset}`);
67
+ }
68
+ if (!/^\d+$/.test(req.amount) || BigInt(req.amount) <= 0n) {
69
+ throw new Error(`Invalid amount: ${req.amount}. Amount must be a positive integer.`);
70
+ }
71
+ if (req.extra?.areFeesSponsored !== true) {
72
+ throw new Error("Exact scheme requires areFeesSponsored to be true");
73
+ }
74
+ }
75
+ async createPaymentPayload(x402Version, req) {
76
+ this.validateRequirements(req);
77
+ const tx = await this.buildTransfer(req.asset, this.card, req.payTo, BigInt(req.amount));
78
+ this.assertLegacyCardCredentials(tx);
79
+ const pending = tx.needsNonInvokerSigningBy();
80
+ if (!pending.includes(this.card) || pending.length > 1) {
81
+ throw new Error(`Expected to sign with [${this.card}], but got [${pending.join(", ")}]`);
82
+ }
83
+ // The stock client samples Horizon for the real ledger close time; this
84
+ // avoids that dependency by assuming a slower ledger than it would find,
85
+ // which is the safe direction (see `authExpirationLedgers`).
86
+ const maxLedger = (await this.latestLedger()) + authExpirationLedgers(req.maxTimeoutSeconds);
87
+ await this.sign(tx, maxLedger);
88
+ // Re-simulate so the transaction carries the signed auth entries and the
89
+ // resources they cost. Signed entries make this simulation *enforcing*:
90
+ // the card's `__check_auth` runs, so a payment its policy refuses fails
91
+ // here — before the facilitator, and before any fee is paid.
92
+ try {
93
+ await tx.simulate({ useUpgradedAuth: false });
94
+ assertSimulationOk(tx.simulation);
95
+ }
96
+ catch (err) {
97
+ const denial = this.denialFromCard(err);
98
+ if (!denial)
99
+ throw err;
100
+ this.onDenial?.(denial);
101
+ throw denial;
102
+ }
103
+ const still = tx.needsNonInvokerSigningBy();
104
+ if (still.length > 0) {
105
+ throw new Error(`unexpected signer(s) required: [${still.join(", ")}]`);
106
+ }
107
+ return { x402Version, payload: { transaction: tx.built.toXDR() } };
108
+ }
109
+ /**
110
+ * Turns a failed enforcing simulation into a typed denial when it was the
111
+ * card's `__check_auth` that said no.
112
+ *
113
+ * The card's address alone is not evidence: it appears in the `transfer`
114
+ * arguments of every diagnostic this transaction can produce, and the token's
115
+ * own error codes overlap the card's 1–8 range — a SAC balance error would be
116
+ * read as a policy decision. What identifies an auth failure is the host's
117
+ * phrase, which names the failing account and its error together:
118
+ *
119
+ * ["failed account authentication with error", <card>, Error(Contract, #N)]
120
+ *
121
+ * so the reason is taken from that fragment and nowhere else. Anything that
122
+ * does not match stays an ordinary error.
123
+ */
124
+ denialFromCard(err) {
125
+ const text = err instanceof Error ? err.message : String(err);
126
+ const fragment = new RegExp(`failed account authentication with error"?,\\s*${this.card}\\s*,[^\\]]*`).exec(text);
127
+ if (!fragment)
128
+ return null;
129
+ const parsed = classifyContractError(fragment[0]);
130
+ if (!parsed || !(parsed.code in CARD_ERROR_CODES))
131
+ return null;
132
+ return new CardPolicyDenied(parsed.reason, "simulate", {
133
+ contractError: parsed.code,
134
+ detail: text.split("\n")[0],
135
+ });
136
+ }
137
+ /**
138
+ * Refuses to build a payment whose card auth entry the deployed facilitator
139
+ * cannot decode.
140
+ *
141
+ * `buildTransfer` asks the RPC to record legacy v1 `ADDRESS` credentials
142
+ * (`useUpgradedAuth: false`) because OZ Channels rejects CAP-71 `ADDRESS_V2`
143
+ * as `invalid_exact_stellar_payload_malformed`. That flag is transitional —
144
+ * it becomes a no-op from protocol 28 — and an RPC or SDK that ignores it
145
+ * would otherwise produce a payload that is signed, sent and refused with a
146
+ * reason that says nothing about why. This checks what was actually
147
+ * recorded, and fails before the agent key signs anything.
148
+ */
149
+ assertLegacyCardCredentials(tx) {
150
+ const op = tx.built?.operations?.[0];
151
+ for (const entry of op?.auth ?? []) {
152
+ const credentials = entry.credentials;
153
+ if (credentials.type !== "sorobanCredentialsAddressV2")
154
+ continue;
155
+ if (Address.fromScAddress(credentials.value.address).toString() !== this.card)
156
+ continue;
157
+ const error = new PaymentError({
158
+ kind: "rejected",
159
+ detail: "RPC returned CAP-71 ADDRESS_V2 credentials; the deployed OZ Channels " +
160
+ "facilitator rejects them (see docs/x402-integration.md, CAP-71 note)",
161
+ });
162
+ this.onPaymentError?.(error);
163
+ throw error;
164
+ }
165
+ }
166
+ // --- collaborators (protected so tests can stub them) ---
167
+ /** Builds and simulates `asset.transfer(from, to, amount)` (SEP-41). */
168
+ async buildTransfer(asset, from, to, amount) {
169
+ const tx = await contract.AssembledTransaction.build({
170
+ contractId: asset,
171
+ method: "transfer",
172
+ args: [
173
+ nativeToScVal(Address.fromString(from), { type: "address" }),
174
+ nativeToScVal(Address.fromString(to), { type: "address" }),
175
+ nativeToScVal(amount, { type: "i128" }),
176
+ ],
177
+ networkPassphrase: this.passphrase,
178
+ rpcUrl: this.rpcUrl,
179
+ parseResultXdr: (r) => r,
180
+ // Record legacy (v1) address credentials rather than the CAP-71 v2 ones
181
+ // stellar-sdk 17 asks for by default. Measured 2026-09-12 against OZ
182
+ // Channels testnet: a v2 payload is rejected as
183
+ // `invalid_exact_stellar_payload_malformed`, the identical v1 payload
184
+ // verifies. The `@x402/stellar` JS library does understand v2, so the
185
+ // refusal comes from somewhere else in the deployed facilitator stack —
186
+ // most likely a Rust `stellar-xdr` decoder, since `stellar xdr decode`
187
+ // (CLI 26.1.0) also fails on a v2 envelope. Both formats are valid
188
+ // on-chain and carry the same agent signature; only the signed preimage
189
+ // differs (v2 binds the address). Drop this once OZ Channels accepts
190
+ // ADDRESS_V2 — and note the SDK flag is transitional: it becomes a no-op
191
+ // in protocol 28, so that has to happen first.
192
+ useUpgradedAuth: false,
193
+ });
194
+ assertSimulationOk(tx.simulation);
195
+ return tx;
196
+ }
197
+ /** Signs the card's auth entries with the agent key. */
198
+ async sign(tx, expirationLedger) {
199
+ await signCardAuthEntries(tx, this.card, this.agent, expirationLedger);
200
+ }
201
+ /** Current ledger sequence, used as the auth-entry expiration base. */
202
+ async latestLedger() {
203
+ const server = new rpc.Server(this.rpcUrl, {
204
+ allowHttp: this.network === "stellar:testnet",
205
+ });
206
+ return (await server.getLatestLedger()).sequence;
207
+ }
208
+ }
209
+ /** Throws unless a simulation succeeded outright (no error, no restore). */
210
+ function assertSimulationOk(sim) {
211
+ if (!sim)
212
+ throw new Error("Stellar simulation result is undefined");
213
+ if (rpc.Api.isSimulationError(sim)) {
214
+ throw new Error(`Stellar simulation failed${sim.error ? `: ${sim.error}` : ""}`);
215
+ }
216
+ if (rpc.Api.isSimulationRestore(sim)) {
217
+ throw new Error("Stellar simulation requires a ledger entry restore");
218
+ }
219
+ }
220
+ //# sourceMappingURL=scheme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheme.js","sourceRoot":"","sources":["../src/scheme.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { contract, Keypair } from "@stellar/stellar-sdk";
2
+ /**
3
+ * Signs every auth entry addressed to `cardAddress` with the agent key.
4
+ *
5
+ * The stock SEP-43 path cannot do this: `signAuthEntries`' default
6
+ * `authorizeEntry` derives the signer public key from the entry's credential
7
+ * address, which here is the card's `C…` address, and `Keypair.fromPublicKey`
8
+ * rejects a contract strkey. So we override `authorizeEntry` and hand it a
9
+ * `SigningCallback` that returns `{ signature, publicKey }`. The SDK then
10
+ * encodes the credential signature as `Vec[{ public_key, signature }]`, which
11
+ * is exactly what the card's `__check_auth` (`Signature = Vec<Sig>`) consumes.
12
+ */
13
+ export declare function signCardAuthEntries(tx: contract.AssembledTransaction<unknown>, cardAddress: string, agent: Keypair, expirationLedger: number): Promise<void>;
package/dist/signer.js ADDED
@@ -0,0 +1,31 @@
1
+ import { authorizeEntry, } from "@stellar/stellar-sdk";
2
+ /**
3
+ * Signs every auth entry addressed to `cardAddress` with the agent key.
4
+ *
5
+ * The stock SEP-43 path cannot do this: `signAuthEntries`' default
6
+ * `authorizeEntry` derives the signer public key from the entry's credential
7
+ * address, which here is the card's `C…` address, and `Keypair.fromPublicKey`
8
+ * rejects a contract strkey. So we override `authorizeEntry` and hand it a
9
+ * `SigningCallback` that returns `{ signature, publicKey }`. The SDK then
10
+ * encodes the credential signature as `Vec[{ public_key, signature }]`, which
11
+ * is exactly what the card's `__check_auth` (`Signature = Vec<Sig>`) consumes.
12
+ */
13
+ export async function signCardAuthEntries(tx, cardAddress, agent, expirationLedger) {
14
+ // `payload` is already the `Uint8Array` `Keypair.sign` takes, and the result
15
+ // is one too: no node `Buffer` is involved, so the client signs in a browser.
16
+ const signPayload = async (_preimage, payload) => ({
17
+ signature: agent.sign(payload),
18
+ publicKey: agent.publicKey(),
19
+ });
20
+ await tx.signAuthEntries({
21
+ address: cardAddress,
22
+ expiration: expirationLedger,
23
+ authorizeEntry: (entry, _signer, validUntilLedgerSeq, networkPassphrase,
24
+ // `authorizeEntry`'s real 5th parameter, used for nested/delegate
25
+ // credentials. `signAuthEntries` calls this callback with 4 arguments,
26
+ // so it is always `undefined` here; it is forwarded verbatim so the
27
+ // override stays a drop-in for the SDK's default.
28
+ forAddress) => authorizeEntry(entry, signPayload, validUntilLedgerSeq, networkPassphrase, forAddress),
29
+ });
30
+ }
31
+ //# sourceMappingURL=signer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signer.js","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAKf,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,EAA0C,EAC1C,WAAmB,EACnB,KAAc,EACd,gBAAwB;IAExB,6EAA6E;IAC7E,8EAA8E;IAC9E,MAAM,WAAW,GAAoB,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;QAClE,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;QAC9B,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE;KAC7B,CAAC,CAAC;IAEH,MAAM,EAAE,CAAC,eAAe,CAAC;QACvB,OAAO,EAAE,WAAW;QACpB,UAAU,EAAE,gBAAgB;QAC5B,cAAc,EAAE,CACd,KAAoC,EACpC,OAAkC,EAClC,mBAA2B,EAC3B,iBAAyB;QACzB,kEAAkE;QAClE,uEAAuE;QACvE,oEAAoE;QACpE,kDAAkD;QAClD,UAAmB,EACnB,EAAE,CACF,cAAc,CACZ,KAAK,EACL,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,CACX;KACJ,CAAC,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,71 @@
1
1
  {
2
2
  "name": "@mooring-live/x402-client",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Pay x402-protected APIs from a Mooring spending card on Stellar.",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/berkay1532/mooring.git",
9
+ "directory": "packages/x402-client"
10
+ },
11
+ "homepage": "https://mooring.live",
12
+ "bugs": {
13
+ "url": "https://github.com/berkay1532/mooring/issues"
14
+ },
15
+ "keywords": [
16
+ "stellar",
17
+ "soroban",
18
+ "x402",
19
+ "usdc",
20
+ "ai-agents",
21
+ "agentic-payments",
22
+ "spending-limits",
23
+ "custom-account"
24
+ ],
25
+ "engines": {
26
+ "node": ">=20"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "type": "module",
32
+ "main": "./dist/index.js",
33
+ "types": "./dist/index.d.ts",
34
+ "exports": {
35
+ ".": {
36
+ "types": "./dist/index.d.ts",
37
+ "import": "./dist/index.js"
38
+ },
39
+ "./errors": {
40
+ "types": "./dist/denial.d.ts",
41
+ "import": "./dist/denial.js"
42
+ }
43
+ },
44
+ "//exports": "./errors is the card error table on its own, so a consumer that only translates contract errors (the web app) does not pull the payment client, its scheme and the @x402/* packages into its bundle.",
45
+ "sideEffects": false,
46
+ "files": [
47
+ "dist",
48
+ "README.md"
49
+ ],
50
+ "scripts": {
51
+ "prepublishOnly": "npm run build",
52
+ "build": "tsc -p tsconfig.json",
53
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
54
+ "test": "vitest run",
55
+ "e2e": "tsx e2e/testnet.e2e.ts"
56
+ },
57
+ "//": "@stellar/stellar-sdk is pinned exactly: CardExactStellarScheme records legacy (v1) auth credentials via `useUpgradedAuth: false`, a transitional flag the deployed OZ Channels facilitator still requires (see docs/x402-integration.md, CAP-71 note). An SDK that changes how auth credentials are recorded would silently change the payload; the client also asserts the recorded format at build time.",
58
+ "dependencies": {
59
+ "@stellar/stellar-sdk": "17.0.1",
60
+ "@x402/core": "2.25.0",
61
+ "@x402/fetch": "2.25.0",
62
+ "@x402/stellar": "2.25.0"
63
+ },
64
+ "devDependencies": {
65
+ "@types/node": "^22.0.0",
66
+ "dotenv": "^16.4.5",
67
+ "tsx": "^4.19.0",
68
+ "typescript": "^5.6.0",
69
+ "vitest": "^3.0.0"
70
+ }
71
+ }