@xona-labs/xpay 0.2.30 → 0.2.32

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.
@@ -0,0 +1,116 @@
1
+ /**
2
+ * SIWX free tier - sign in with the wallet instead of paying.
3
+ *
4
+ * Some x402 servers advertise a `sign-in-with-x` (CAIP-122) challenge in the
5
+ * 402 body's `extensions`, granting free access to wallets that prove
6
+ * identity - e.g. Xona's $XONA holder tier, where holding the token earns
7
+ * free daily generations. The flow costs nothing to attempt: sign the
8
+ * challenge, retry with the SIGN-IN-WITH-X header, and fall back to a normal
9
+ * paid settlement if the server declines (not enough tokens, quota used, ...).
10
+ *
11
+ * Only Solana (ed25519) challenges are attempted here: the paid routes that
12
+ * offer this perk today are Solana-gated, and the Solana signer's raw
13
+ * `signMessage` is exactly the primitive SIWS needs.
14
+ */
15
+ function isObj(v) {
16
+ return typeof v === "object" && v !== null;
17
+ }
18
+ /**
19
+ * Pull a Solana SIWX challenge out of a 402 body, if the server offers one.
20
+ * Returns null when the extension is absent or only offered on chains we
21
+ * don't sign SIWS for.
22
+ */
23
+ export function extractSiwxFreeChallenge(data) {
24
+ if (!isObj(data) || !isObj(data.extensions))
25
+ return null;
26
+ const ext = data.extensions["sign-in-with-x"];
27
+ if (!isObj(ext) || !isObj(ext.info))
28
+ return null;
29
+ const info = ext.info;
30
+ if (!info.domain || !info.uri || !info.nonce || !info.issuedAt)
31
+ return null;
32
+ const chains = Array.isArray(ext.supportedChains) ? ext.supportedChains : [];
33
+ const solana = chains.find((c) => isObj(c) && typeof c.chainId === "string" && c.chainId.startsWith("solana:"));
34
+ if (!solana)
35
+ return null;
36
+ return { info, chainId: solana.chainId };
37
+ }
38
+ /** Build the SIWS (CAIP-122) message text the server verifies against. */
39
+ function buildSiwsMessage(challenge, address) {
40
+ const { info, chainId } = challenge;
41
+ const chainRef = chainId.split(":")[1] ?? chainId;
42
+ const lines = [`${info.domain} wants you to sign in with your Solana account:`, address, ""];
43
+ if (info.statement)
44
+ lines.push(info.statement, "");
45
+ lines.push(`URI: ${info.uri}`, `Version: ${info.version || "1"}`, `Chain ID: ${chainRef}`, `Nonce: ${info.nonce}`, `Issued At: ${info.issuedAt}`);
46
+ if (info.expirationTime)
47
+ lines.push(`Expiration Time: ${info.expirationTime}`);
48
+ if (info.notBefore)
49
+ lines.push(`Not Before: ${info.notBefore}`);
50
+ if (info.requestId)
51
+ lines.push(`Request ID: ${info.requestId}`);
52
+ if (info.resources && info.resources.length > 0) {
53
+ lines.push("Resources:");
54
+ for (const r of info.resources)
55
+ lines.push(`- ${r}`);
56
+ }
57
+ return lines.join("\n");
58
+ }
59
+ /**
60
+ * Sign the challenge with the wallet's Solana signer and return the value for
61
+ * the SIGN-IN-WITH-X header (base64 JSON payload, base58 ed25519 signature).
62
+ * Returns null when the wallet has no Solana signer to sign with.
63
+ */
64
+ export async function buildSiwxFreeHeader(wallet, challenge) {
65
+ if (!wallet.has("solana"))
66
+ return null;
67
+ const signer = wallet.signer("solana");
68
+ // Bind the challenge to the host that issued it - a proxied or replayed
69
+ // challenge for another domain must not be signed.
70
+ const message = buildSiwsMessage(challenge, signer.address);
71
+ const sigBytes = await signer.signMessage(new TextEncoder().encode(message));
72
+ const payload = {
73
+ domain: challenge.info.domain,
74
+ address: signer.address,
75
+ statement: challenge.info.statement,
76
+ uri: challenge.info.uri,
77
+ version: challenge.info.version || "1",
78
+ chainId: challenge.chainId,
79
+ type: "ed25519",
80
+ nonce: challenge.info.nonce,
81
+ issuedAt: challenge.info.issuedAt,
82
+ expirationTime: challenge.info.expirationTime,
83
+ notBefore: challenge.info.notBefore,
84
+ requestId: challenge.info.requestId,
85
+ resources: challenge.info.resources,
86
+ signature: encodeBase58(sigBytes),
87
+ };
88
+ return Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
89
+ }
90
+ /** Header the free-tier server echoes the remaining daily quota on. */
91
+ export const SIWX_FREE_REMAINING_HEADER = "x-xona-holder-free-remaining";
92
+ const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
93
+ /** Minimal Base58 encode - avoids pulling in bs58 just for one signature. */
94
+ function encodeBase58(bytes) {
95
+ let zeros = 0;
96
+ while (zeros < bytes.length && bytes[zeros] === 0)
97
+ zeros++;
98
+ const digits = [];
99
+ for (let i = zeros; i < bytes.length; i++) {
100
+ let carry = bytes[i];
101
+ for (let j = 0; j < digits.length; j++) {
102
+ carry += digits[j] << 8;
103
+ digits[j] = carry % 58;
104
+ carry = (carry / 58) | 0;
105
+ }
106
+ while (carry > 0) {
107
+ digits.push(carry % 58);
108
+ carry = (carry / 58) | 0;
109
+ }
110
+ }
111
+ let out = "1".repeat(zeros);
112
+ for (let i = digits.length - 1; i >= 0; i--)
113
+ out += BASE58_ALPHABET[digits[i]];
114
+ return out;
115
+ }
116
+ //# sourceMappingURL=siwx-free.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"siwx-free.js","sourceRoot":"","sources":["../../src/x402/siwx-free.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xona-labs/xpay",
3
- "version": "0.2.30",
3
+ "version": "0.2.32",
4
4
  "mcpName": "io.github.xona-labs/xpay",
5
5
  "description": "Xona Wallet: agent wallet and payment rail for agentic commerce. Multi-network wallet, x402 payments, AgenC marketplace hires, 20k+ services via PayAI and other catalogs",
6
6
  "type": "module",