@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/README.md +151 -2
- package/dist/card.d.ts +60 -0
- package/dist/card.js +67 -0
- package/dist/card.js.map +1 -0
- package/dist/client.d.ts +75 -0
- package/dist/client.js +252 -0
- package/dist/client.js.map +1 -0
- package/dist/denial.d.ts +81 -0
- package/dist/denial.js +116 -0
- package/dist/denial.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/precheck.d.ts +12 -0
- package/dist/precheck.js +25 -0
- package/dist/precheck.js.map +1 -0
- package/dist/reconcile.d.ts +37 -0
- package/dist/reconcile.js +44 -0
- package/dist/reconcile.js.map +1 -0
- package/dist/scheme.d.ts +87 -0
- package/dist/scheme.js +220 -0
- package/dist/scheme.js.map +1 -0
- package/dist/signer.d.ts +13 -0
- package/dist/signer.js +31 -0
- package/dist/signer.js.map +1 -0
- package/package.json +69 -4
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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"}
|
package/dist/signer.d.ts
ADDED
|
@@ -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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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
|
+
}
|