@relai-fi/x402 0.5.22 → 0.5.23
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/crossmint.cjs +2 -0
- package/dist/crossmint.cjs.map +1 -1
- package/dist/crossmint.d.cts +2 -0
- package/dist/crossmint.d.ts +2 -0
- package/dist/crossmint.js +2 -0
- package/dist/crossmint.js.map +1 -1
- package/package.json +1 -1
package/dist/crossmint.cjs
CHANGED
|
@@ -218,6 +218,7 @@ function createCrossmintX402Fetch(config) {
|
|
|
218
218
|
);
|
|
219
219
|
const sig = await pollForSignature(apiBase, apiKey, wallet, txData.id, maxPolls, pollMs, txData);
|
|
220
220
|
if (!sig) throw new Error("[x402/crossmint] Timed out waiting for tx confirmation");
|
|
221
|
+
config.onPayment?.(sig);
|
|
221
222
|
const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);
|
|
222
223
|
return fetch(url, {
|
|
223
224
|
...init,
|
|
@@ -286,6 +287,7 @@ function createCrossmintDelegatedX402Fetch(config) {
|
|
|
286
287
|
approvalResp
|
|
287
288
|
);
|
|
288
289
|
if (!sig) throw new Error("[x402/crossmint] Timed out waiting for tx confirmation");
|
|
290
|
+
config.onPayment?.(sig);
|
|
289
291
|
const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);
|
|
290
292
|
return fetch(url, {
|
|
291
293
|
...init,
|
package/dist/crossmint.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/crossmint.ts"],"sourcesContent":["/**\n * Crossmint smart wallet integration for RelAI x402.\n *\n * Two modes:\n *\n * **API-key mode** — zero private keys, Crossmint signs + broadcasts:\n * ```ts\n * import { createCrossmintX402Fetch } from \"@relai-fi/x402/crossmint\";\n *\n * const fetch402 = createCrossmintX402Fetch({\n * apiKey: process.env.CROSSMINT_API_KEY!,\n * wallet: process.env.CROSSMINT_WALLET!,\n * connection: new Connection(process.env.RPC_URL!),\n * });\n * const resp = await fetch402(\"https://api.example.com/protected\");\n * ```\n *\n * **Delegated mode** — external signer approves each transaction:\n * ```ts\n * import { createCrossmintDelegatedX402Fetch } from \"@relai-fi/x402/crossmint\";\n *\n * const fetch402 = createCrossmintDelegatedX402Fetch({\n * apiKey: process.env.CROSSMINT_API_KEY!,\n * wallet: process.env.CROSSMINT_WALLET!,\n * signerSecretKey: myKeypairBytes, // 64-byte Ed25519\n * connection: new Connection(process.env.RPC_URL!),\n * });\n * const resp = await fetch402(\"https://api.example.com/protected\");\n * ```\n *\n * @module\n */\nimport {\n Connection,\n PublicKey,\n VersionedTransaction,\n TransactionMessage,\n} from \"@solana/web3.js\";\nimport {\n getAssociatedTokenAddress,\n createTransferCheckedInstruction,\n getMint,\n TOKEN_PROGRAM_ID,\n TOKEN_2022_PROGRAM_ID,\n} from \"@solana/spl-token\";\n\nimport nacl from \"tweetnacl\";\n\n// ── Types ───────────────────────────────────────────────────────────\n\nexport interface CrossmintX402Config {\n /** Crossmint server-side API key (`sk_production_...` or `sk_staging_...`) */\n apiKey: string;\n\n /** Crossmint smart wallet address (Solana public key) */\n wallet: string;\n\n /** Solana RPC connection */\n connection: Connection;\n\n /** Override Crossmint API base URL (default: `https://www.crossmint.com/api/2025-06-09`) */\n crossmintApiBase?: string;\n\n /** Max polling attempts for tx confirmation (default: 30) */\n maxPollAttempts?: number;\n\n /** Polling interval in ms (default: 2000) */\n pollIntervalMs?: number;\n}\n\nexport interface CrossmintDelegatedX402Config extends CrossmintX402Config {\n /**\n * Ed25519 secret key for the external signer (64 bytes).\n * The corresponding public key must be registered as an external signer\n * on the Crossmint smart wallet.\n */\n signerSecretKey: Uint8Array;\n\n /** Crossmint environment: \"staging\" or \"production\" (default: auto-detect from apiKey) */\n environment?: \"staging\" | \"production\";\n}\n\n// ── Internals ───────────────────────────────────────────────────────\n\nconst DEFAULT_API_BASE = \"https://www.crossmint.com/api/2025-06-09\";\nconst STAGING_API_BASE = \"https://staging.crossmint.com/api/2025-06-09\";\n\n/** Auto-detect Crossmint API base from API key prefix. */\nfunction detectApiBase(apiKey: string): string {\n if (apiKey.startsWith(\"sk_staging\")) return STAGING_API_BASE;\n return DEFAULT_API_BASE;\n}\n\n/** Encode bytes to base58 without external dependency. */\nfunction toBase58(bytes: Uint8Array): string {\n const ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n const digits = [0];\n for (const byte of bytes) {\n let carry = byte;\n for (let j = 0; j < digits.length; j++) {\n carry += digits[j] << 8;\n digits[j] = carry % 58;\n carry = (carry / 58) | 0;\n }\n while (carry > 0) {\n digits.push(carry % 58);\n carry = (carry / 58) | 0;\n }\n }\n let str = \"\";\n for (const byte of bytes) {\n if (byte === 0) str += ALPHABET[0];\n else break;\n }\n for (let i = digits.length - 1; i >= 0; i--) {\n str += ALPHABET[digits[i]];\n }\n return str;\n}\n\n/** Decode base58 string to bytes. */\nfunction fromBase58(str: string): Uint8Array {\n const ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n const BASE_MAP = new Uint8Array(256).fill(255);\n for (let i = 0; i < ALPHABET.length; i++) BASE_MAP[ALPHABET.charCodeAt(i)] = i;\n const bytes: number[] = [0];\n for (const ch of str) {\n const carry_init = BASE_MAP[ch.charCodeAt(0)];\n if (carry_init === 255) throw new Error(`Invalid base58 character: ${ch}`);\n let carry = carry_init;\n for (let j = 0; j < bytes.length; j++) {\n carry += bytes[j] * 58;\n bytes[j] = carry & 0xff;\n carry >>= 8;\n }\n while (carry > 0) {\n bytes.push(carry & 0xff);\n carry >>= 8;\n }\n }\n for (const ch of str) {\n if (ch === ALPHABET[0]) bytes.push(0);\n else break;\n }\n return new Uint8Array(bytes.reverse());\n}\n\n/** Extract Solana signature from Crossmint's full serialized tx (base58). */\nfunction extractSignature(onChainTx: string): string | null {\n if (!onChainTx) return null;\n if (onChainTx.length <= 100) return onChainTx; // Already a bare signature\n try {\n const decoded = fromBase58(onChainTx);\n const vtx = VersionedTransaction.deserialize(decoded);\n if (vtx.signatures?.[0]) return toBase58(vtx.signatures[0]);\n } catch {\n // Not a valid serialized tx\n }\n return null;\n}\n\n/** Make a Crossmint API call. */\nasync function crossmintApi(\n baseUrl: string, apiKey: string, endpoint: string,\n body?: unknown, method: \"GET\" | \"POST\" = \"POST\",\n): Promise<any> {\n const url = `${baseUrl}/wallets/${endpoint}`;\n const opts: RequestInit = {\n method,\n headers: { \"Content-Type\": \"application/json\", \"X-API-KEY\": apiKey },\n };\n if (method === \"POST\" && body) opts.body = JSON.stringify(body);\n const resp = await fetch(url, opts);\n const json = await resp.json();\n if (!resp.ok) throw new Error(`[x402/crossmint] API ${resp.status}: ${JSON.stringify(json)}`);\n return json;\n}\n\n/** Sign a message with Ed25519. Message can be base58-encoded or UTF-8 string. */\nfunction signMessage(message: string, secretKey: Uint8Array): string {\n let messageBytes: Uint8Array;\n try {\n messageBytes = fromBase58(message);\n } catch {\n messageBytes = new TextEncoder().encode(message);\n }\n const signature = nacl.sign.detached(messageBytes, secretKey);\n return toBase58(signature);\n}\n\n/** Derive public key address from a 64-byte secret key. */\nfunction deriveSignerAddress(secretKey: Uint8Array): string {\n const publicKey = secretKey.length === 64\n ? secretKey.slice(32)\n : nacl.sign.keyPair.fromSeed(secretKey).publicKey;\n return toBase58(publicKey);\n}\n\n/** Poll Crossmint for on-chain txId. Returns signature or null. */\nasync function pollForSignature(\n apiBase: string, apiKey: string, wallet: string,\n txId: string, maxPolls: number, pollMs: number,\n initialData?: any,\n): Promise<string | null> {\n let sig = initialData?.onChain?.txId\n || extractSignature(initialData?.onChain?.transaction) || null;\n\n for (let i = 0; i < maxPolls && !sig; i++) {\n await new Promise((r) => setTimeout(r, pollMs));\n const p = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions/${txId}`, undefined, \"GET\");\n if (p.status === \"failed\") {\n throw new Error(`[x402/crossmint] Crossmint tx failed: ${p.error?.message || \"unknown\"}`);\n }\n sig = p.onChain?.txId || extractSignature(p.onChain?.transaction) || null;\n }\n return sig;\n}\n\n/** Build X-PAYMENT header from unsigned tx + on-chain signature + payment requirements. */\nfunction buildXPaymentHeader(unsignedTx: VersionedTransaction, sig: string, accept: any): string {\n const payload = {\n x402Version: 2,\n scheme: \"exact\",\n network: accept.network,\n payload: {\n transaction: Buffer.from(unsignedTx.serialize()).toString(\"base64\"),\n txId: sig,\n },\n accepted: {\n scheme: accept.scheme,\n network: accept.network,\n amount: accept.amount,\n asset: accept.asset,\n payTo: accept.payTo,\n },\n };\n return Buffer.from(JSON.stringify(payload)).toString(\"base64\");\n}\n\n/** Build unsigned SPL transfer tx from 402 payment requirements. */\nasync function buildTransferTx(\n connection: Connection, userPubkey: PublicKey, accept: any,\n): Promise<VersionedTransaction> {\n const mintPubkey = new PublicKey(accept.asset);\n const merchantPubkey = new PublicKey(accept.payTo);\n const mintAccountInfo = await connection.getAccountInfo(mintPubkey);\n const programId = mintAccountInfo?.owner.equals(TOKEN_2022_PROGRAM_ID)\n ? TOKEN_2022_PROGRAM_ID\n : TOKEN_PROGRAM_ID;\n\n const mintInfo = await getMint(connection, mintPubkey, \"confirmed\", programId);\n const amount = BigInt(accept.amount);\n\n const sourceAta = await getAssociatedTokenAddress(mintPubkey, userPubkey, true, programId);\n const destAta = await getAssociatedTokenAddress(mintPubkey, merchantPubkey, true, programId);\n\n const ix = createTransferCheckedInstruction(\n sourceAta, mintPubkey, destAta, userPubkey,\n amount, mintInfo.decimals, [], programId,\n );\n\n const { blockhash } = await connection.getLatestBlockhash(\"confirmed\");\n const msg = new TransactionMessage({\n payerKey: userPubkey,\n recentBlockhash: blockhash,\n instructions: [ix],\n }).compileToV0Message();\n\n return new VersionedTransaction(msg);\n}\n\n// ── Public API ──────────────────────────────────────────────────────\n\n/**\n * **API-key mode.** Create a fetch wrapper that auto-handles x402 `402`\n * responses using a Crossmint smart wallet on Solana.\n *\n * Crossmint signs and broadcasts — no private key needed.\n * The returned function has the same signature as `fetch()`.\n */\nexport function createCrossmintX402Fetch(config: CrossmintX402Config) {\n const { apiKey, wallet, connection } = config;\n const apiBase = config.crossmintApiBase || detectApiBase(apiKey);\n const maxPolls = config.maxPollAttempts ?? 30;\n const pollMs = config.pollIntervalMs ?? 2000;\n const userPubkey = new PublicKey(wallet);\n\n return async function fetch402(url: string, init?: RequestInit): Promise<Response> {\n // 1. Initial request\n const firstResp = await fetch(url, {\n ...init, headers: { Accept: \"application/json\", ...init?.headers },\n });\n if (firstResp.status !== 402) return firstResp;\n\n // 2. Parse 402\n const requirements = (await firstResp.json()) as any;\n const accept = requirements.accepts?.[0];\n if (!accept) throw new Error(\"[x402/crossmint] No payment requirements in 402 response\");\n\n // 3. Build transfer tx\n const unsignedTx = await buildTransferTx(connection, userPubkey, accept);\n\n // 4. Submit to Crossmint (signs + broadcasts)\n const txData = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions`,\n { params: { transaction: toBase58(unsignedTx.serialize()) } });\n\n // 5. Poll for on-chain signature\n const sig = await pollForSignature(apiBase, apiKey, wallet, txData.id, maxPolls, pollMs, txData);\n if (!sig) throw new Error(\"[x402/crossmint] Timed out waiting for tx confirmation\");\n\n // 6. Retry with X-PAYMENT\n const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);\n return fetch(url, {\n ...init, headers: { Accept: \"application/json\", \"X-PAYMENT\": xPayment, ...init?.headers },\n });\n };\n}\n\n/**\n * **Delegated mode.** Create a fetch wrapper that auto-handles x402 `402`\n * responses using a Crossmint smart wallet with external signer approval.\n *\n * Each transaction requires cryptographic approval from `signerSecretKey`\n * before Crossmint broadcasts. This provides an extra security layer —\n * even with the API key, transactions cannot execute without the signer.\n *\n * The external signer must be registered on the Crossmint smart wallet\n * via the Crossmint console or API.\n */\nexport function createCrossmintDelegatedX402Fetch(config: CrossmintDelegatedX402Config) {\n const { apiKey, wallet, connection, signerSecretKey } = config;\n const apiBase = config.crossmintApiBase || detectApiBase(apiKey);\n const maxPolls = config.maxPollAttempts ?? 30;\n const pollMs = config.pollIntervalMs ?? 2000;\n const userPubkey = new PublicKey(wallet);\n const signerAddress = deriveSignerAddress(signerSecretKey);\n\n return async function fetch402(url: string, init?: RequestInit): Promise<Response> {\n // 1. Initial request\n const firstResp = await fetch(url, {\n ...init, headers: { Accept: \"application/json\", ...init?.headers },\n });\n if (firstResp.status !== 402) return firstResp;\n\n // 2. Parse 402\n const requirements = (await firstResp.json()) as any;\n const accept = requirements.accepts?.[0];\n if (!accept) throw new Error(\"[x402/crossmint] No payment requirements in 402 response\");\n\n // 3. Build transfer tx\n const unsignedTx = await buildTransferTx(connection, userPubkey, accept);\n\n // 4. Submit to Crossmint with external signer\n const createResp = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions`,\n {\n params: {\n transaction: toBase58(unsignedTx.serialize()),\n signer: {\n type: \"external-wallet\",\n locator: `external-wallet:${signerAddress}`,\n },\n },\n });\n\n const transactionId = createResp.id;\n const pendingApproval = createResp.approvals?.pending?.[0];\n\n if (!transactionId || !pendingApproval) {\n throw new Error(\n `[x402/crossmint] No pending approval returned. ` +\n `Ensure external signer ${signerAddress} is registered on wallet ${wallet}. ` +\n `Response: ${JSON.stringify(createResp)}`\n );\n }\n\n // 5. Sign the approval message\n const pendingSigner = pendingApproval.signer?.address\n ?? pendingApproval.signer?.locator?.split(\":\")[1]\n ?? signerAddress;\n\n const approvalSig = signMessage(pendingApproval.message, signerSecretKey);\n\n // 6. Submit approval\n const approvalResp = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions/${encodeURIComponent(transactionId)}/approvals`,\n {\n approvals: [{\n signer: `external-wallet:${pendingSigner}`,\n signature: approvalSig,\n }],\n });\n\n // 7. Poll for on-chain signature\n const sig = await pollForSignature(\n apiBase, apiKey, wallet, transactionId, maxPolls, pollMs, approvalResp);\n if (!sig) throw new Error(\"[x402/crossmint] Timed out waiting for tx confirmation\");\n\n // 8. Retry with X-PAYMENT\n const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);\n return fetch(url, {\n ...init, headers: { Accept: \"application/json\", \"X-PAYMENT\": xPayment, ...init?.headers },\n });\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCA,kBAKO;AACP,uBAMO;AAEP,uBAAiB;AAsCjB,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAGzB,SAAS,cAAc,QAAwB;AAC7C,MAAI,OAAO,WAAW,YAAY,EAAG,QAAO;AAC5C,SAAO;AACT;AAGA,SAAS,SAAS,OAA2B;AAC3C,QAAM,WAAW;AACjB,QAAM,SAAS,CAAC,CAAC;AACjB,aAAW,QAAQ,OAAO;AACxB,QAAI,QAAQ;AACZ,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,eAAS,OAAO,CAAC,KAAK;AACtB,aAAO,CAAC,IAAI,QAAQ;AACpB,cAAS,QAAQ,KAAM;AAAA,IACzB;AACA,WAAO,QAAQ,GAAG;AAChB,aAAO,KAAK,QAAQ,EAAE;AACtB,cAAS,QAAQ,KAAM;AAAA,IACzB;AAAA,EACF;AACA,MAAI,MAAM;AACV,aAAW,QAAQ,OAAO;AACxB,QAAI,SAAS,EAAG,QAAO,SAAS,CAAC;AAAA,QAC5B;AAAA,EACP;AACA,WAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,WAAO,SAAS,OAAO,CAAC,CAAC;AAAA,EAC3B;AACA,SAAO;AACT;AAGA,SAAS,WAAW,KAAyB;AAC3C,QAAM,WAAW;AACjB,QAAM,WAAW,IAAI,WAAW,GAAG,EAAE,KAAK,GAAG;AAC7C,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,IAAK,UAAS,SAAS,WAAW,CAAC,CAAC,IAAI;AAC7E,QAAM,QAAkB,CAAC,CAAC;AAC1B,aAAW,MAAM,KAAK;AACpB,UAAM,aAAa,SAAS,GAAG,WAAW,CAAC,CAAC;AAC5C,QAAI,eAAe,IAAK,OAAM,IAAI,MAAM,6BAA6B,EAAE,EAAE;AACzE,QAAI,QAAQ;AACZ,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,eAAS,MAAM,CAAC,IAAI;AACpB,YAAM,CAAC,IAAI,QAAQ;AACnB,gBAAU;AAAA,IACZ;AACA,WAAO,QAAQ,GAAG;AAChB,YAAM,KAAK,QAAQ,GAAI;AACvB,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,aAAW,MAAM,KAAK;AACpB,QAAI,OAAO,SAAS,CAAC,EAAG,OAAM,KAAK,CAAC;AAAA,QAC/B;AAAA,EACP;AACA,SAAO,IAAI,WAAW,MAAM,QAAQ,CAAC;AACvC;AAGA,SAAS,iBAAiB,WAAkC;AAC1D,MAAI,CAAC,UAAW,QAAO;AACvB,MAAI,UAAU,UAAU,IAAK,QAAO;AACpC,MAAI;AACF,UAAM,UAAU,WAAW,SAAS;AACpC,UAAM,MAAM,iCAAqB,YAAY,OAAO;AACpD,QAAI,IAAI,aAAa,CAAC,EAAG,QAAO,SAAS,IAAI,WAAW,CAAC,CAAC;AAAA,EAC5D,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAGA,eAAe,aACb,SAAiB,QAAgB,UACjC,MAAgB,SAAyB,QAC3B;AACd,QAAM,MAAM,GAAG,OAAO,YAAY,QAAQ;AAC1C,QAAM,OAAoB;AAAA,IACxB;AAAA,IACA,SAAS,EAAE,gBAAgB,oBAAoB,aAAa,OAAO;AAAA,EACrE;AACA,MAAI,WAAW,UAAU,KAAM,MAAK,OAAO,KAAK,UAAU,IAAI;AAC9D,QAAM,OAAO,MAAM,MAAM,KAAK,IAAI;AAClC,QAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,MAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,wBAAwB,KAAK,MAAM,KAAK,KAAK,UAAU,IAAI,CAAC,EAAE;AAC5F,SAAO;AACT;AAGA,SAAS,YAAY,SAAiB,WAA+B;AACnE,MAAI;AACJ,MAAI;AACF,mBAAe,WAAW,OAAO;AAAA,EACnC,QAAQ;AACN,mBAAe,IAAI,YAAY,EAAE,OAAO,OAAO;AAAA,EACjD;AACA,QAAM,YAAY,iBAAAA,QAAK,KAAK,SAAS,cAAc,SAAS;AAC5D,SAAO,SAAS,SAAS;AAC3B;AAGA,SAAS,oBAAoB,WAA+B;AAC1D,QAAM,YAAY,UAAU,WAAW,KACnC,UAAU,MAAM,EAAE,IAClB,iBAAAA,QAAK,KAAK,QAAQ,SAAS,SAAS,EAAE;AAC1C,SAAO,SAAS,SAAS;AAC3B;AAGA,eAAe,iBACb,SAAiB,QAAgB,QACjC,MAAc,UAAkB,QAChC,aACwB;AACxB,MAAI,MAAM,aAAa,SAAS,QAC3B,iBAAiB,aAAa,SAAS,WAAW,KAAK;AAE5D,WAAS,IAAI,GAAG,IAAI,YAAY,CAAC,KAAK,KAAK;AACzC,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC;AAC9C,UAAM,IAAI,MAAM;AAAA,MAAa;AAAA,MAAS;AAAA,MACpC,GAAG,mBAAmB,MAAM,CAAC,iBAAiB,IAAI;AAAA,MAAI;AAAA,MAAW;AAAA,IAAK;AACxE,QAAI,EAAE,WAAW,UAAU;AACzB,YAAM,IAAI,MAAM,yCAAyC,EAAE,OAAO,WAAW,SAAS,EAAE;AAAA,IAC1F;AACA,UAAM,EAAE,SAAS,QAAQ,iBAAiB,EAAE,SAAS,WAAW,KAAK;AAAA,EACvE;AACA,SAAO;AACT;AAGA,SAAS,oBAAoB,YAAkC,KAAa,QAAqB;AAC/F,QAAM,UAAU;AAAA,IACd,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,SAAS,OAAO;AAAA,IAChB,SAAS;AAAA,MACP,aAAa,OAAO,KAAK,WAAW,UAAU,CAAC,EAAE,SAAS,QAAQ;AAAA,MAClE,MAAM;AAAA,IACR;AAAA,IACA,UAAU;AAAA,MACR,QAAQ,OAAO;AAAA,MACf,SAAS,OAAO;AAAA,MAChB,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,MACd,OAAO,OAAO;AAAA,IAChB;AAAA,EACF;AACA,SAAO,OAAO,KAAK,KAAK,UAAU,OAAO,CAAC,EAAE,SAAS,QAAQ;AAC/D;AAGA,eAAe,gBACb,YAAwB,YAAuB,QAChB;AAC/B,QAAM,aAAa,IAAI,sBAAU,OAAO,KAAK;AAC7C,QAAM,iBAAiB,IAAI,sBAAU,OAAO,KAAK;AACjD,QAAM,kBAAkB,MAAM,WAAW,eAAe,UAAU;AAClE,QAAM,YAAY,iBAAiB,MAAM,OAAO,sCAAqB,IACjE,yCACA;AAEJ,QAAM,WAAW,UAAM,0BAAQ,YAAY,YAAY,aAAa,SAAS;AAC7E,QAAM,SAAS,OAAO,OAAO,MAAM;AAEnC,QAAM,YAAY,UAAM,4CAA0B,YAAY,YAAY,MAAM,SAAS;AACzF,QAAM,UAAU,UAAM,4CAA0B,YAAY,gBAAgB,MAAM,SAAS;AAE3F,QAAM,SAAK;AAAA,IACT;AAAA,IAAW;AAAA,IAAY;AAAA,IAAS;AAAA,IAChC;AAAA,IAAQ,SAAS;AAAA,IAAU,CAAC;AAAA,IAAG;AAAA,EACjC;AAEA,QAAM,EAAE,UAAU,IAAI,MAAM,WAAW,mBAAmB,WAAW;AACrE,QAAM,MAAM,IAAI,+BAAmB;AAAA,IACjC,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,cAAc,CAAC,EAAE;AAAA,EACnB,CAAC,EAAE,mBAAmB;AAEtB,SAAO,IAAI,iCAAqB,GAAG;AACrC;AAWO,SAAS,yBAAyB,QAA6B;AACpE,QAAM,EAAE,QAAQ,QAAQ,WAAW,IAAI;AACvC,QAAM,UAAU,OAAO,oBAAoB,cAAc,MAAM;AAC/D,QAAM,WAAW,OAAO,mBAAmB;AAC3C,QAAM,SAAS,OAAO,kBAAkB;AACxC,QAAM,aAAa,IAAI,sBAAU,MAAM;AAEvC,SAAO,eAAe,SAAS,KAAa,MAAuC;AAEjF,UAAM,YAAY,MAAM,MAAM,KAAK;AAAA,MACjC,GAAG;AAAA,MAAM,SAAS,EAAE,QAAQ,oBAAoB,GAAG,MAAM,QAAQ;AAAA,IACnE,CAAC;AACD,QAAI,UAAU,WAAW,IAAK,QAAO;AAGrC,UAAM,eAAgB,MAAM,UAAU,KAAK;AAC3C,UAAM,SAAS,aAAa,UAAU,CAAC;AACvC,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,0DAA0D;AAGvF,UAAM,aAAa,MAAM,gBAAgB,YAAY,YAAY,MAAM;AAGvE,UAAM,SAAS,MAAM;AAAA,MAAa;AAAA,MAAS;AAAA,MACzC,GAAG,mBAAmB,MAAM,CAAC;AAAA,MAC7B,EAAE,QAAQ,EAAE,aAAa,SAAS,WAAW,UAAU,CAAC,EAAE,EAAE;AAAA,IAAC;AAG/D,UAAM,MAAM,MAAM,iBAAiB,SAAS,QAAQ,QAAQ,OAAO,IAAI,UAAU,QAAQ,MAAM;AAC/F,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,wDAAwD;AAGlF,UAAM,WAAW,oBAAoB,YAAY,KAAK,MAAM;AAC5D,WAAO,MAAM,KAAK;AAAA,MAChB,GAAG;AAAA,MAAM,SAAS,EAAE,QAAQ,oBAAoB,aAAa,UAAU,GAAG,MAAM,QAAQ;AAAA,IAC1F,CAAC;AAAA,EACH;AACF;AAaO,SAAS,kCAAkC,QAAsC;AACtF,QAAM,EAAE,QAAQ,QAAQ,YAAY,gBAAgB,IAAI;AACxD,QAAM,UAAU,OAAO,oBAAoB,cAAc,MAAM;AAC/D,QAAM,WAAW,OAAO,mBAAmB;AAC3C,QAAM,SAAS,OAAO,kBAAkB;AACxC,QAAM,aAAa,IAAI,sBAAU,MAAM;AACvC,QAAM,gBAAgB,oBAAoB,eAAe;AAEzD,SAAO,eAAe,SAAS,KAAa,MAAuC;AAEjF,UAAM,YAAY,MAAM,MAAM,KAAK;AAAA,MACjC,GAAG;AAAA,MAAM,SAAS,EAAE,QAAQ,oBAAoB,GAAG,MAAM,QAAQ;AAAA,IACnE,CAAC;AACD,QAAI,UAAU,WAAW,IAAK,QAAO;AAGrC,UAAM,eAAgB,MAAM,UAAU,KAAK;AAC3C,UAAM,SAAS,aAAa,UAAU,CAAC;AACvC,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,0DAA0D;AAGvF,UAAM,aAAa,MAAM,gBAAgB,YAAY,YAAY,MAAM;AAGvE,UAAM,aAAa,MAAM;AAAA,MAAa;AAAA,MAAS;AAAA,MAC7C,GAAG,mBAAmB,MAAM,CAAC;AAAA,MAC7B;AAAA,QACE,QAAQ;AAAA,UACN,aAAa,SAAS,WAAW,UAAU,CAAC;AAAA,UAC5C,QAAQ;AAAA,YACN,MAAM;AAAA,YACN,SAAS,mBAAmB,aAAa;AAAA,UAC3C;AAAA,QACF;AAAA,MACF;AAAA,IAAC;AAEH,UAAM,gBAAgB,WAAW;AACjC,UAAM,kBAAkB,WAAW,WAAW,UAAU,CAAC;AAEzD,QAAI,CAAC,iBAAiB,CAAC,iBAAiB;AACtC,YAAM,IAAI;AAAA,QACR,yEAC0B,aAAa,4BAA4B,MAAM,eAC5D,KAAK,UAAU,UAAU,CAAC;AAAA,MACzC;AAAA,IACF;AAGA,UAAM,gBAAgB,gBAAgB,QAAQ,WACzC,gBAAgB,QAAQ,SAAS,MAAM,GAAG,EAAE,CAAC,KAC7C;AAEL,UAAM,cAAc,YAAY,gBAAgB,SAAS,eAAe;AAGxE,UAAM,eAAe,MAAM;AAAA,MAAa;AAAA,MAAS;AAAA,MAC/C,GAAG,mBAAmB,MAAM,CAAC,iBAAiB,mBAAmB,aAAa,CAAC;AAAA,MAC/E;AAAA,QACE,WAAW,CAAC;AAAA,UACV,QAAQ,mBAAmB,aAAa;AAAA,UACxC,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IAAC;AAGH,UAAM,MAAM,MAAM;AAAA,MAChB;AAAA,MAAS;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAe;AAAA,MAAU;AAAA,MAAQ;AAAA,IAAY;AACxE,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,wDAAwD;AAGlF,UAAM,WAAW,oBAAoB,YAAY,KAAK,MAAM;AAC5D,WAAO,MAAM,KAAK;AAAA,MAChB,GAAG;AAAA,MAAM,SAAS,EAAE,QAAQ,oBAAoB,aAAa,UAAU,GAAG,MAAM,QAAQ;AAAA,IAC1F,CAAC;AAAA,EACH;AACF;","names":["nacl"]}
|
|
1
|
+
{"version":3,"sources":["../src/crossmint.ts"],"sourcesContent":["/**\n * Crossmint smart wallet integration for RelAI x402.\n *\n * Two modes:\n *\n * **API-key mode** — zero private keys, Crossmint signs + broadcasts:\n * ```ts\n * import { createCrossmintX402Fetch } from \"@relai-fi/x402/crossmint\";\n *\n * const fetch402 = createCrossmintX402Fetch({\n * apiKey: process.env.CROSSMINT_API_KEY!,\n * wallet: process.env.CROSSMINT_WALLET!,\n * connection: new Connection(process.env.RPC_URL!),\n * });\n * const resp = await fetch402(\"https://api.example.com/protected\");\n * ```\n *\n * **Delegated mode** — external signer approves each transaction:\n * ```ts\n * import { createCrossmintDelegatedX402Fetch } from \"@relai-fi/x402/crossmint\";\n *\n * const fetch402 = createCrossmintDelegatedX402Fetch({\n * apiKey: process.env.CROSSMINT_API_KEY!,\n * wallet: process.env.CROSSMINT_WALLET!,\n * signerSecretKey: myKeypairBytes, // 64-byte Ed25519\n * connection: new Connection(process.env.RPC_URL!),\n * });\n * const resp = await fetch402(\"https://api.example.com/protected\");\n * ```\n *\n * @module\n */\nimport {\n Connection,\n PublicKey,\n VersionedTransaction,\n TransactionMessage,\n} from \"@solana/web3.js\";\nimport {\n getAssociatedTokenAddress,\n createTransferCheckedInstruction,\n getMint,\n TOKEN_PROGRAM_ID,\n TOKEN_2022_PROGRAM_ID,\n} from \"@solana/spl-token\";\n\nimport nacl from \"tweetnacl\";\n\n// ── Types ───────────────────────────────────────────────────────────\n\nexport interface CrossmintX402Config {\n /** Crossmint server-side API key (`sk_production_...` or `sk_staging_...`) */\n apiKey: string;\n\n /** Crossmint smart wallet address (Solana public key) */\n wallet: string;\n\n /** Solana RPC connection */\n connection: Connection;\n\n /** Override Crossmint API base URL (default: `https://www.crossmint.com/api/2025-06-09`) */\n crossmintApiBase?: string;\n\n /** Max polling attempts for tx confirmation (default: 30) */\n maxPollAttempts?: number;\n\n /** Polling interval in ms (default: 2000) */\n pollIntervalMs?: number;\n\n /** Called after successful on-chain payment with the Solana tx signature */\n onPayment?: (txHash: string) => void;\n}\n\nexport interface CrossmintDelegatedX402Config extends CrossmintX402Config {\n /**\n * Ed25519 secret key for the external signer (64 bytes).\n * The corresponding public key must be registered as an external signer\n * on the Crossmint smart wallet.\n */\n signerSecretKey: Uint8Array;\n\n /** Crossmint environment: \"staging\" or \"production\" (default: auto-detect from apiKey) */\n environment?: \"staging\" | \"production\";\n}\n\n// ── Internals ───────────────────────────────────────────────────────\n\nconst DEFAULT_API_BASE = \"https://www.crossmint.com/api/2025-06-09\";\nconst STAGING_API_BASE = \"https://staging.crossmint.com/api/2025-06-09\";\n\n/** Auto-detect Crossmint API base from API key prefix. */\nfunction detectApiBase(apiKey: string): string {\n if (apiKey.startsWith(\"sk_staging\")) return STAGING_API_BASE;\n return DEFAULT_API_BASE;\n}\n\n/** Encode bytes to base58 without external dependency. */\nfunction toBase58(bytes: Uint8Array): string {\n const ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n const digits = [0];\n for (const byte of bytes) {\n let carry = byte;\n for (let j = 0; j < digits.length; j++) {\n carry += digits[j] << 8;\n digits[j] = carry % 58;\n carry = (carry / 58) | 0;\n }\n while (carry > 0) {\n digits.push(carry % 58);\n carry = (carry / 58) | 0;\n }\n }\n let str = \"\";\n for (const byte of bytes) {\n if (byte === 0) str += ALPHABET[0];\n else break;\n }\n for (let i = digits.length - 1; i >= 0; i--) {\n str += ALPHABET[digits[i]];\n }\n return str;\n}\n\n/** Decode base58 string to bytes. */\nfunction fromBase58(str: string): Uint8Array {\n const ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n const BASE_MAP = new Uint8Array(256).fill(255);\n for (let i = 0; i < ALPHABET.length; i++) BASE_MAP[ALPHABET.charCodeAt(i)] = i;\n const bytes: number[] = [0];\n for (const ch of str) {\n const carry_init = BASE_MAP[ch.charCodeAt(0)];\n if (carry_init === 255) throw new Error(`Invalid base58 character: ${ch}`);\n let carry = carry_init;\n for (let j = 0; j < bytes.length; j++) {\n carry += bytes[j] * 58;\n bytes[j] = carry & 0xff;\n carry >>= 8;\n }\n while (carry > 0) {\n bytes.push(carry & 0xff);\n carry >>= 8;\n }\n }\n for (const ch of str) {\n if (ch === ALPHABET[0]) bytes.push(0);\n else break;\n }\n return new Uint8Array(bytes.reverse());\n}\n\n/** Extract Solana signature from Crossmint's full serialized tx (base58). */\nfunction extractSignature(onChainTx: string): string | null {\n if (!onChainTx) return null;\n if (onChainTx.length <= 100) return onChainTx; // Already a bare signature\n try {\n const decoded = fromBase58(onChainTx);\n const vtx = VersionedTransaction.deserialize(decoded);\n if (vtx.signatures?.[0]) return toBase58(vtx.signatures[0]);\n } catch {\n // Not a valid serialized tx\n }\n return null;\n}\n\n/** Make a Crossmint API call. */\nasync function crossmintApi(\n baseUrl: string, apiKey: string, endpoint: string,\n body?: unknown, method: \"GET\" | \"POST\" = \"POST\",\n): Promise<any> {\n const url = `${baseUrl}/wallets/${endpoint}`;\n const opts: RequestInit = {\n method,\n headers: { \"Content-Type\": \"application/json\", \"X-API-KEY\": apiKey },\n };\n if (method === \"POST\" && body) opts.body = JSON.stringify(body);\n const resp = await fetch(url, opts);\n const json = await resp.json();\n if (!resp.ok) throw new Error(`[x402/crossmint] API ${resp.status}: ${JSON.stringify(json)}`);\n return json;\n}\n\n/** Sign a message with Ed25519. Message can be base58-encoded or UTF-8 string. */\nfunction signMessage(message: string, secretKey: Uint8Array): string {\n let messageBytes: Uint8Array;\n try {\n messageBytes = fromBase58(message);\n } catch {\n messageBytes = new TextEncoder().encode(message);\n }\n const signature = nacl.sign.detached(messageBytes, secretKey);\n return toBase58(signature);\n}\n\n/** Derive public key address from a 64-byte secret key. */\nfunction deriveSignerAddress(secretKey: Uint8Array): string {\n const publicKey = secretKey.length === 64\n ? secretKey.slice(32)\n : nacl.sign.keyPair.fromSeed(secretKey).publicKey;\n return toBase58(publicKey);\n}\n\n/** Poll Crossmint for on-chain txId. Returns signature or null. */\nasync function pollForSignature(\n apiBase: string, apiKey: string, wallet: string,\n txId: string, maxPolls: number, pollMs: number,\n initialData?: any,\n): Promise<string | null> {\n let sig = initialData?.onChain?.txId\n || extractSignature(initialData?.onChain?.transaction) || null;\n\n for (let i = 0; i < maxPolls && !sig; i++) {\n await new Promise((r) => setTimeout(r, pollMs));\n const p = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions/${txId}`, undefined, \"GET\");\n if (p.status === \"failed\") {\n throw new Error(`[x402/crossmint] Crossmint tx failed: ${p.error?.message || \"unknown\"}`);\n }\n sig = p.onChain?.txId || extractSignature(p.onChain?.transaction) || null;\n }\n return sig;\n}\n\n/** Build X-PAYMENT header from unsigned tx + on-chain signature + payment requirements. */\nfunction buildXPaymentHeader(unsignedTx: VersionedTransaction, sig: string, accept: any): string {\n const payload = {\n x402Version: 2,\n scheme: \"exact\",\n network: accept.network,\n payload: {\n transaction: Buffer.from(unsignedTx.serialize()).toString(\"base64\"),\n txId: sig,\n },\n accepted: {\n scheme: accept.scheme,\n network: accept.network,\n amount: accept.amount,\n asset: accept.asset,\n payTo: accept.payTo,\n },\n };\n return Buffer.from(JSON.stringify(payload)).toString(\"base64\");\n}\n\n/** Build unsigned SPL transfer tx from 402 payment requirements. */\nasync function buildTransferTx(\n connection: Connection, userPubkey: PublicKey, accept: any,\n): Promise<VersionedTransaction> {\n const mintPubkey = new PublicKey(accept.asset);\n const merchantPubkey = new PublicKey(accept.payTo);\n const mintAccountInfo = await connection.getAccountInfo(mintPubkey);\n const programId = mintAccountInfo?.owner.equals(TOKEN_2022_PROGRAM_ID)\n ? TOKEN_2022_PROGRAM_ID\n : TOKEN_PROGRAM_ID;\n\n const mintInfo = await getMint(connection, mintPubkey, \"confirmed\", programId);\n const amount = BigInt(accept.amount);\n\n const sourceAta = await getAssociatedTokenAddress(mintPubkey, userPubkey, true, programId);\n const destAta = await getAssociatedTokenAddress(mintPubkey, merchantPubkey, true, programId);\n\n const ix = createTransferCheckedInstruction(\n sourceAta, mintPubkey, destAta, userPubkey,\n amount, mintInfo.decimals, [], programId,\n );\n\n const { blockhash } = await connection.getLatestBlockhash(\"confirmed\");\n const msg = new TransactionMessage({\n payerKey: userPubkey,\n recentBlockhash: blockhash,\n instructions: [ix],\n }).compileToV0Message();\n\n return new VersionedTransaction(msg);\n}\n\n// ── Public API ──────────────────────────────────────────────────────\n\n/**\n * **API-key mode.** Create a fetch wrapper that auto-handles x402 `402`\n * responses using a Crossmint smart wallet on Solana.\n *\n * Crossmint signs and broadcasts — no private key needed.\n * The returned function has the same signature as `fetch()`.\n */\nexport function createCrossmintX402Fetch(config: CrossmintX402Config) {\n const { apiKey, wallet, connection } = config;\n const apiBase = config.crossmintApiBase || detectApiBase(apiKey);\n const maxPolls = config.maxPollAttempts ?? 30;\n const pollMs = config.pollIntervalMs ?? 2000;\n const userPubkey = new PublicKey(wallet);\n\n return async function fetch402(url: string, init?: RequestInit): Promise<Response> {\n // 1. Initial request\n const firstResp = await fetch(url, {\n ...init, headers: { Accept: \"application/json\", ...init?.headers },\n });\n if (firstResp.status !== 402) return firstResp;\n\n // 2. Parse 402\n const requirements = (await firstResp.json()) as any;\n const accept = requirements.accepts?.[0];\n if (!accept) throw new Error(\"[x402/crossmint] No payment requirements in 402 response\");\n\n // 3. Build transfer tx\n const unsignedTx = await buildTransferTx(connection, userPubkey, accept);\n\n // 4. Submit to Crossmint (signs + broadcasts)\n const txData = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions`,\n { params: { transaction: toBase58(unsignedTx.serialize()) } });\n\n // 5. Poll for on-chain signature\n const sig = await pollForSignature(apiBase, apiKey, wallet, txData.id, maxPolls, pollMs, txData);\n if (!sig) throw new Error(\"[x402/crossmint] Timed out waiting for tx confirmation\");\n\n config.onPayment?.(sig);\n\n // 6. Retry with X-PAYMENT\n const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);\n return fetch(url, {\n ...init, headers: { Accept: \"application/json\", \"X-PAYMENT\": xPayment, ...init?.headers },\n });\n };\n}\n\n/**\n * **Delegated mode.** Create a fetch wrapper that auto-handles x402 `402`\n * responses using a Crossmint smart wallet with external signer approval.\n *\n * Each transaction requires cryptographic approval from `signerSecretKey`\n * before Crossmint broadcasts. This provides an extra security layer —\n * even with the API key, transactions cannot execute without the signer.\n *\n * The external signer must be registered on the Crossmint smart wallet\n * via the Crossmint console or API.\n */\nexport function createCrossmintDelegatedX402Fetch(config: CrossmintDelegatedX402Config) {\n const { apiKey, wallet, connection, signerSecretKey } = config;\n const apiBase = config.crossmintApiBase || detectApiBase(apiKey);\n const maxPolls = config.maxPollAttempts ?? 30;\n const pollMs = config.pollIntervalMs ?? 2000;\n const userPubkey = new PublicKey(wallet);\n const signerAddress = deriveSignerAddress(signerSecretKey);\n\n return async function fetch402(url: string, init?: RequestInit): Promise<Response> {\n // 1. Initial request\n const firstResp = await fetch(url, {\n ...init, headers: { Accept: \"application/json\", ...init?.headers },\n });\n if (firstResp.status !== 402) return firstResp;\n\n // 2. Parse 402\n const requirements = (await firstResp.json()) as any;\n const accept = requirements.accepts?.[0];\n if (!accept) throw new Error(\"[x402/crossmint] No payment requirements in 402 response\");\n\n // 3. Build transfer tx\n const unsignedTx = await buildTransferTx(connection, userPubkey, accept);\n\n // 4. Submit to Crossmint with external signer\n const createResp = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions`,\n {\n params: {\n transaction: toBase58(unsignedTx.serialize()),\n signer: {\n type: \"external-wallet\",\n locator: `external-wallet:${signerAddress}`,\n },\n },\n });\n\n const transactionId = createResp.id;\n const pendingApproval = createResp.approvals?.pending?.[0];\n\n if (!transactionId || !pendingApproval) {\n throw new Error(\n `[x402/crossmint] No pending approval returned. ` +\n `Ensure external signer ${signerAddress} is registered on wallet ${wallet}. ` +\n `Response: ${JSON.stringify(createResp)}`\n );\n }\n\n // 5. Sign the approval message\n const pendingSigner = pendingApproval.signer?.address\n ?? pendingApproval.signer?.locator?.split(\":\")[1]\n ?? signerAddress;\n\n const approvalSig = signMessage(pendingApproval.message, signerSecretKey);\n\n // 6. Submit approval\n const approvalResp = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions/${encodeURIComponent(transactionId)}/approvals`,\n {\n approvals: [{\n signer: `external-wallet:${pendingSigner}`,\n signature: approvalSig,\n }],\n });\n\n // 7. Poll for on-chain signature\n const sig = await pollForSignature(\n apiBase, apiKey, wallet, transactionId, maxPolls, pollMs, approvalResp);\n if (!sig) throw new Error(\"[x402/crossmint] Timed out waiting for tx confirmation\");\n\n config.onPayment?.(sig);\n\n // 8. Retry with X-PAYMENT\n const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);\n return fetch(url, {\n ...init, headers: { Accept: \"application/json\", \"X-PAYMENT\": xPayment, ...init?.headers },\n });\n };\n}\n"],"mappings":"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","names":["nacl"]}
|
package/dist/crossmint.d.cts
CHANGED
|
@@ -46,6 +46,8 @@ interface CrossmintX402Config {
|
|
|
46
46
|
maxPollAttempts?: number;
|
|
47
47
|
/** Polling interval in ms (default: 2000) */
|
|
48
48
|
pollIntervalMs?: number;
|
|
49
|
+
/** Called after successful on-chain payment with the Solana tx signature */
|
|
50
|
+
onPayment?: (txHash: string) => void;
|
|
49
51
|
}
|
|
50
52
|
interface CrossmintDelegatedX402Config extends CrossmintX402Config {
|
|
51
53
|
/**
|
package/dist/crossmint.d.ts
CHANGED
|
@@ -46,6 +46,8 @@ interface CrossmintX402Config {
|
|
|
46
46
|
maxPollAttempts?: number;
|
|
47
47
|
/** Polling interval in ms (default: 2000) */
|
|
48
48
|
pollIntervalMs?: number;
|
|
49
|
+
/** Called after successful on-chain payment with the Solana tx signature */
|
|
50
|
+
onPayment?: (txHash: string) => void;
|
|
49
51
|
}
|
|
50
52
|
interface CrossmintDelegatedX402Config extends CrossmintX402Config {
|
|
51
53
|
/**
|
package/dist/crossmint.js
CHANGED
|
@@ -193,6 +193,7 @@ function createCrossmintX402Fetch(config) {
|
|
|
193
193
|
);
|
|
194
194
|
const sig = await pollForSignature(apiBase, apiKey, wallet, txData.id, maxPolls, pollMs, txData);
|
|
195
195
|
if (!sig) throw new Error("[x402/crossmint] Timed out waiting for tx confirmation");
|
|
196
|
+
config.onPayment?.(sig);
|
|
196
197
|
const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);
|
|
197
198
|
return fetch(url, {
|
|
198
199
|
...init,
|
|
@@ -261,6 +262,7 @@ function createCrossmintDelegatedX402Fetch(config) {
|
|
|
261
262
|
approvalResp
|
|
262
263
|
);
|
|
263
264
|
if (!sig) throw new Error("[x402/crossmint] Timed out waiting for tx confirmation");
|
|
265
|
+
config.onPayment?.(sig);
|
|
264
266
|
const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);
|
|
265
267
|
return fetch(url, {
|
|
266
268
|
...init,
|
package/dist/crossmint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/crossmint.ts"],"sourcesContent":["/**\n * Crossmint smart wallet integration for RelAI x402.\n *\n * Two modes:\n *\n * **API-key mode** — zero private keys, Crossmint signs + broadcasts:\n * ```ts\n * import { createCrossmintX402Fetch } from \"@relai-fi/x402/crossmint\";\n *\n * const fetch402 = createCrossmintX402Fetch({\n * apiKey: process.env.CROSSMINT_API_KEY!,\n * wallet: process.env.CROSSMINT_WALLET!,\n * connection: new Connection(process.env.RPC_URL!),\n * });\n * const resp = await fetch402(\"https://api.example.com/protected\");\n * ```\n *\n * **Delegated mode** — external signer approves each transaction:\n * ```ts\n * import { createCrossmintDelegatedX402Fetch } from \"@relai-fi/x402/crossmint\";\n *\n * const fetch402 = createCrossmintDelegatedX402Fetch({\n * apiKey: process.env.CROSSMINT_API_KEY!,\n * wallet: process.env.CROSSMINT_WALLET!,\n * signerSecretKey: myKeypairBytes, // 64-byte Ed25519\n * connection: new Connection(process.env.RPC_URL!),\n * });\n * const resp = await fetch402(\"https://api.example.com/protected\");\n * ```\n *\n * @module\n */\nimport {\n Connection,\n PublicKey,\n VersionedTransaction,\n TransactionMessage,\n} from \"@solana/web3.js\";\nimport {\n getAssociatedTokenAddress,\n createTransferCheckedInstruction,\n getMint,\n TOKEN_PROGRAM_ID,\n TOKEN_2022_PROGRAM_ID,\n} from \"@solana/spl-token\";\n\nimport nacl from \"tweetnacl\";\n\n// ── Types ───────────────────────────────────────────────────────────\n\nexport interface CrossmintX402Config {\n /** Crossmint server-side API key (`sk_production_...` or `sk_staging_...`) */\n apiKey: string;\n\n /** Crossmint smart wallet address (Solana public key) */\n wallet: string;\n\n /** Solana RPC connection */\n connection: Connection;\n\n /** Override Crossmint API base URL (default: `https://www.crossmint.com/api/2025-06-09`) */\n crossmintApiBase?: string;\n\n /** Max polling attempts for tx confirmation (default: 30) */\n maxPollAttempts?: number;\n\n /** Polling interval in ms (default: 2000) */\n pollIntervalMs?: number;\n}\n\nexport interface CrossmintDelegatedX402Config extends CrossmintX402Config {\n /**\n * Ed25519 secret key for the external signer (64 bytes).\n * The corresponding public key must be registered as an external signer\n * on the Crossmint smart wallet.\n */\n signerSecretKey: Uint8Array;\n\n /** Crossmint environment: \"staging\" or \"production\" (default: auto-detect from apiKey) */\n environment?: \"staging\" | \"production\";\n}\n\n// ── Internals ───────────────────────────────────────────────────────\n\nconst DEFAULT_API_BASE = \"https://www.crossmint.com/api/2025-06-09\";\nconst STAGING_API_BASE = \"https://staging.crossmint.com/api/2025-06-09\";\n\n/** Auto-detect Crossmint API base from API key prefix. */\nfunction detectApiBase(apiKey: string): string {\n if (apiKey.startsWith(\"sk_staging\")) return STAGING_API_BASE;\n return DEFAULT_API_BASE;\n}\n\n/** Encode bytes to base58 without external dependency. */\nfunction toBase58(bytes: Uint8Array): string {\n const ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n const digits = [0];\n for (const byte of bytes) {\n let carry = byte;\n for (let j = 0; j < digits.length; j++) {\n carry += digits[j] << 8;\n digits[j] = carry % 58;\n carry = (carry / 58) | 0;\n }\n while (carry > 0) {\n digits.push(carry % 58);\n carry = (carry / 58) | 0;\n }\n }\n let str = \"\";\n for (const byte of bytes) {\n if (byte === 0) str += ALPHABET[0];\n else break;\n }\n for (let i = digits.length - 1; i >= 0; i--) {\n str += ALPHABET[digits[i]];\n }\n return str;\n}\n\n/** Decode base58 string to bytes. */\nfunction fromBase58(str: string): Uint8Array {\n const ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n const BASE_MAP = new Uint8Array(256).fill(255);\n for (let i = 0; i < ALPHABET.length; i++) BASE_MAP[ALPHABET.charCodeAt(i)] = i;\n const bytes: number[] = [0];\n for (const ch of str) {\n const carry_init = BASE_MAP[ch.charCodeAt(0)];\n if (carry_init === 255) throw new Error(`Invalid base58 character: ${ch}`);\n let carry = carry_init;\n for (let j = 0; j < bytes.length; j++) {\n carry += bytes[j] * 58;\n bytes[j] = carry & 0xff;\n carry >>= 8;\n }\n while (carry > 0) {\n bytes.push(carry & 0xff);\n carry >>= 8;\n }\n }\n for (const ch of str) {\n if (ch === ALPHABET[0]) bytes.push(0);\n else break;\n }\n return new Uint8Array(bytes.reverse());\n}\n\n/** Extract Solana signature from Crossmint's full serialized tx (base58). */\nfunction extractSignature(onChainTx: string): string | null {\n if (!onChainTx) return null;\n if (onChainTx.length <= 100) return onChainTx; // Already a bare signature\n try {\n const decoded = fromBase58(onChainTx);\n const vtx = VersionedTransaction.deserialize(decoded);\n if (vtx.signatures?.[0]) return toBase58(vtx.signatures[0]);\n } catch {\n // Not a valid serialized tx\n }\n return null;\n}\n\n/** Make a Crossmint API call. */\nasync function crossmintApi(\n baseUrl: string, apiKey: string, endpoint: string,\n body?: unknown, method: \"GET\" | \"POST\" = \"POST\",\n): Promise<any> {\n const url = `${baseUrl}/wallets/${endpoint}`;\n const opts: RequestInit = {\n method,\n headers: { \"Content-Type\": \"application/json\", \"X-API-KEY\": apiKey },\n };\n if (method === \"POST\" && body) opts.body = JSON.stringify(body);\n const resp = await fetch(url, opts);\n const json = await resp.json();\n if (!resp.ok) throw new Error(`[x402/crossmint] API ${resp.status}: ${JSON.stringify(json)}`);\n return json;\n}\n\n/** Sign a message with Ed25519. Message can be base58-encoded or UTF-8 string. */\nfunction signMessage(message: string, secretKey: Uint8Array): string {\n let messageBytes: Uint8Array;\n try {\n messageBytes = fromBase58(message);\n } catch {\n messageBytes = new TextEncoder().encode(message);\n }\n const signature = nacl.sign.detached(messageBytes, secretKey);\n return toBase58(signature);\n}\n\n/** Derive public key address from a 64-byte secret key. */\nfunction deriveSignerAddress(secretKey: Uint8Array): string {\n const publicKey = secretKey.length === 64\n ? secretKey.slice(32)\n : nacl.sign.keyPair.fromSeed(secretKey).publicKey;\n return toBase58(publicKey);\n}\n\n/** Poll Crossmint for on-chain txId. Returns signature or null. */\nasync function pollForSignature(\n apiBase: string, apiKey: string, wallet: string,\n txId: string, maxPolls: number, pollMs: number,\n initialData?: any,\n): Promise<string | null> {\n let sig = initialData?.onChain?.txId\n || extractSignature(initialData?.onChain?.transaction) || null;\n\n for (let i = 0; i < maxPolls && !sig; i++) {\n await new Promise((r) => setTimeout(r, pollMs));\n const p = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions/${txId}`, undefined, \"GET\");\n if (p.status === \"failed\") {\n throw new Error(`[x402/crossmint] Crossmint tx failed: ${p.error?.message || \"unknown\"}`);\n }\n sig = p.onChain?.txId || extractSignature(p.onChain?.transaction) || null;\n }\n return sig;\n}\n\n/** Build X-PAYMENT header from unsigned tx + on-chain signature + payment requirements. */\nfunction buildXPaymentHeader(unsignedTx: VersionedTransaction, sig: string, accept: any): string {\n const payload = {\n x402Version: 2,\n scheme: \"exact\",\n network: accept.network,\n payload: {\n transaction: Buffer.from(unsignedTx.serialize()).toString(\"base64\"),\n txId: sig,\n },\n accepted: {\n scheme: accept.scheme,\n network: accept.network,\n amount: accept.amount,\n asset: accept.asset,\n payTo: accept.payTo,\n },\n };\n return Buffer.from(JSON.stringify(payload)).toString(\"base64\");\n}\n\n/** Build unsigned SPL transfer tx from 402 payment requirements. */\nasync function buildTransferTx(\n connection: Connection, userPubkey: PublicKey, accept: any,\n): Promise<VersionedTransaction> {\n const mintPubkey = new PublicKey(accept.asset);\n const merchantPubkey = new PublicKey(accept.payTo);\n const mintAccountInfo = await connection.getAccountInfo(mintPubkey);\n const programId = mintAccountInfo?.owner.equals(TOKEN_2022_PROGRAM_ID)\n ? TOKEN_2022_PROGRAM_ID\n : TOKEN_PROGRAM_ID;\n\n const mintInfo = await getMint(connection, mintPubkey, \"confirmed\", programId);\n const amount = BigInt(accept.amount);\n\n const sourceAta = await getAssociatedTokenAddress(mintPubkey, userPubkey, true, programId);\n const destAta = await getAssociatedTokenAddress(mintPubkey, merchantPubkey, true, programId);\n\n const ix = createTransferCheckedInstruction(\n sourceAta, mintPubkey, destAta, userPubkey,\n amount, mintInfo.decimals, [], programId,\n );\n\n const { blockhash } = await connection.getLatestBlockhash(\"confirmed\");\n const msg = new TransactionMessage({\n payerKey: userPubkey,\n recentBlockhash: blockhash,\n instructions: [ix],\n }).compileToV0Message();\n\n return new VersionedTransaction(msg);\n}\n\n// ── Public API ──────────────────────────────────────────────────────\n\n/**\n * **API-key mode.** Create a fetch wrapper that auto-handles x402 `402`\n * responses using a Crossmint smart wallet on Solana.\n *\n * Crossmint signs and broadcasts — no private key needed.\n * The returned function has the same signature as `fetch()`.\n */\nexport function createCrossmintX402Fetch(config: CrossmintX402Config) {\n const { apiKey, wallet, connection } = config;\n const apiBase = config.crossmintApiBase || detectApiBase(apiKey);\n const maxPolls = config.maxPollAttempts ?? 30;\n const pollMs = config.pollIntervalMs ?? 2000;\n const userPubkey = new PublicKey(wallet);\n\n return async function fetch402(url: string, init?: RequestInit): Promise<Response> {\n // 1. Initial request\n const firstResp = await fetch(url, {\n ...init, headers: { Accept: \"application/json\", ...init?.headers },\n });\n if (firstResp.status !== 402) return firstResp;\n\n // 2. Parse 402\n const requirements = (await firstResp.json()) as any;\n const accept = requirements.accepts?.[0];\n if (!accept) throw new Error(\"[x402/crossmint] No payment requirements in 402 response\");\n\n // 3. Build transfer tx\n const unsignedTx = await buildTransferTx(connection, userPubkey, accept);\n\n // 4. Submit to Crossmint (signs + broadcasts)\n const txData = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions`,\n { params: { transaction: toBase58(unsignedTx.serialize()) } });\n\n // 5. Poll for on-chain signature\n const sig = await pollForSignature(apiBase, apiKey, wallet, txData.id, maxPolls, pollMs, txData);\n if (!sig) throw new Error(\"[x402/crossmint] Timed out waiting for tx confirmation\");\n\n // 6. Retry with X-PAYMENT\n const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);\n return fetch(url, {\n ...init, headers: { Accept: \"application/json\", \"X-PAYMENT\": xPayment, ...init?.headers },\n });\n };\n}\n\n/**\n * **Delegated mode.** Create a fetch wrapper that auto-handles x402 `402`\n * responses using a Crossmint smart wallet with external signer approval.\n *\n * Each transaction requires cryptographic approval from `signerSecretKey`\n * before Crossmint broadcasts. This provides an extra security layer —\n * even with the API key, transactions cannot execute without the signer.\n *\n * The external signer must be registered on the Crossmint smart wallet\n * via the Crossmint console or API.\n */\nexport function createCrossmintDelegatedX402Fetch(config: CrossmintDelegatedX402Config) {\n const { apiKey, wallet, connection, signerSecretKey } = config;\n const apiBase = config.crossmintApiBase || detectApiBase(apiKey);\n const maxPolls = config.maxPollAttempts ?? 30;\n const pollMs = config.pollIntervalMs ?? 2000;\n const userPubkey = new PublicKey(wallet);\n const signerAddress = deriveSignerAddress(signerSecretKey);\n\n return async function fetch402(url: string, init?: RequestInit): Promise<Response> {\n // 1. Initial request\n const firstResp = await fetch(url, {\n ...init, headers: { Accept: \"application/json\", ...init?.headers },\n });\n if (firstResp.status !== 402) return firstResp;\n\n // 2. Parse 402\n const requirements = (await firstResp.json()) as any;\n const accept = requirements.accepts?.[0];\n if (!accept) throw new Error(\"[x402/crossmint] No payment requirements in 402 response\");\n\n // 3. Build transfer tx\n const unsignedTx = await buildTransferTx(connection, userPubkey, accept);\n\n // 4. Submit to Crossmint with external signer\n const createResp = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions`,\n {\n params: {\n transaction: toBase58(unsignedTx.serialize()),\n signer: {\n type: \"external-wallet\",\n locator: `external-wallet:${signerAddress}`,\n },\n },\n });\n\n const transactionId = createResp.id;\n const pendingApproval = createResp.approvals?.pending?.[0];\n\n if (!transactionId || !pendingApproval) {\n throw new Error(\n `[x402/crossmint] No pending approval returned. ` +\n `Ensure external signer ${signerAddress} is registered on wallet ${wallet}. ` +\n `Response: ${JSON.stringify(createResp)}`\n );\n }\n\n // 5. Sign the approval message\n const pendingSigner = pendingApproval.signer?.address\n ?? pendingApproval.signer?.locator?.split(\":\")[1]\n ?? signerAddress;\n\n const approvalSig = signMessage(pendingApproval.message, signerSecretKey);\n\n // 6. Submit approval\n const approvalResp = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions/${encodeURIComponent(transactionId)}/approvals`,\n {\n approvals: [{\n signer: `external-wallet:${pendingSigner}`,\n signature: approvalSig,\n }],\n });\n\n // 7. Poll for on-chain signature\n const sig = await pollForSignature(\n apiBase, apiKey, wallet, transactionId, maxPolls, pollMs, approvalResp);\n if (!sig) throw new Error(\"[x402/crossmint] Timed out waiting for tx confirmation\");\n\n // 8. Retry with X-PAYMENT\n const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);\n return fetch(url, {\n ...init, headers: { Accept: \"application/json\", \"X-PAYMENT\": xPayment, ...init?.headers },\n });\n };\n}\n"],"mappings":";AAgCA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,OAAO,UAAU;AAsCjB,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAGzB,SAAS,cAAc,QAAwB;AAC7C,MAAI,OAAO,WAAW,YAAY,EAAG,QAAO;AAC5C,SAAO;AACT;AAGA,SAAS,SAAS,OAA2B;AAC3C,QAAM,WAAW;AACjB,QAAM,SAAS,CAAC,CAAC;AACjB,aAAW,QAAQ,OAAO;AACxB,QAAI,QAAQ;AACZ,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,eAAS,OAAO,CAAC,KAAK;AACtB,aAAO,CAAC,IAAI,QAAQ;AACpB,cAAS,QAAQ,KAAM;AAAA,IACzB;AACA,WAAO,QAAQ,GAAG;AAChB,aAAO,KAAK,QAAQ,EAAE;AACtB,cAAS,QAAQ,KAAM;AAAA,IACzB;AAAA,EACF;AACA,MAAI,MAAM;AACV,aAAW,QAAQ,OAAO;AACxB,QAAI,SAAS,EAAG,QAAO,SAAS,CAAC;AAAA,QAC5B;AAAA,EACP;AACA,WAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,WAAO,SAAS,OAAO,CAAC,CAAC;AAAA,EAC3B;AACA,SAAO;AACT;AAGA,SAAS,WAAW,KAAyB;AAC3C,QAAM,WAAW;AACjB,QAAM,WAAW,IAAI,WAAW,GAAG,EAAE,KAAK,GAAG;AAC7C,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,IAAK,UAAS,SAAS,WAAW,CAAC,CAAC,IAAI;AAC7E,QAAM,QAAkB,CAAC,CAAC;AAC1B,aAAW,MAAM,KAAK;AACpB,UAAM,aAAa,SAAS,GAAG,WAAW,CAAC,CAAC;AAC5C,QAAI,eAAe,IAAK,OAAM,IAAI,MAAM,6BAA6B,EAAE,EAAE;AACzE,QAAI,QAAQ;AACZ,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,eAAS,MAAM,CAAC,IAAI;AACpB,YAAM,CAAC,IAAI,QAAQ;AACnB,gBAAU;AAAA,IACZ;AACA,WAAO,QAAQ,GAAG;AAChB,YAAM,KAAK,QAAQ,GAAI;AACvB,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,aAAW,MAAM,KAAK;AACpB,QAAI,OAAO,SAAS,CAAC,EAAG,OAAM,KAAK,CAAC;AAAA,QAC/B;AAAA,EACP;AACA,SAAO,IAAI,WAAW,MAAM,QAAQ,CAAC;AACvC;AAGA,SAAS,iBAAiB,WAAkC;AAC1D,MAAI,CAAC,UAAW,QAAO;AACvB,MAAI,UAAU,UAAU,IAAK,QAAO;AACpC,MAAI;AACF,UAAM,UAAU,WAAW,SAAS;AACpC,UAAM,MAAM,qBAAqB,YAAY,OAAO;AACpD,QAAI,IAAI,aAAa,CAAC,EAAG,QAAO,SAAS,IAAI,WAAW,CAAC,CAAC;AAAA,EAC5D,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAGA,eAAe,aACb,SAAiB,QAAgB,UACjC,MAAgB,SAAyB,QAC3B;AACd,QAAM,MAAM,GAAG,OAAO,YAAY,QAAQ;AAC1C,QAAM,OAAoB;AAAA,IACxB;AAAA,IACA,SAAS,EAAE,gBAAgB,oBAAoB,aAAa,OAAO;AAAA,EACrE;AACA,MAAI,WAAW,UAAU,KAAM,MAAK,OAAO,KAAK,UAAU,IAAI;AAC9D,QAAM,OAAO,MAAM,MAAM,KAAK,IAAI;AAClC,QAAM,OAAO,MAAM,KAAK,KAAK;AAC7B,MAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,wBAAwB,KAAK,MAAM,KAAK,KAAK,UAAU,IAAI,CAAC,EAAE;AAC5F,SAAO;AACT;AAGA,SAAS,YAAY,SAAiB,WAA+B;AACnE,MAAI;AACJ,MAAI;AACF,mBAAe,WAAW,OAAO;AAAA,EACnC,QAAQ;AACN,mBAAe,IAAI,YAAY,EAAE,OAAO,OAAO;AAAA,EACjD;AACA,QAAM,YAAY,KAAK,KAAK,SAAS,cAAc,SAAS;AAC5D,SAAO,SAAS,SAAS;AAC3B;AAGA,SAAS,oBAAoB,WAA+B;AAC1D,QAAM,YAAY,UAAU,WAAW,KACnC,UAAU,MAAM,EAAE,IAClB,KAAK,KAAK,QAAQ,SAAS,SAAS,EAAE;AAC1C,SAAO,SAAS,SAAS;AAC3B;AAGA,eAAe,iBACb,SAAiB,QAAgB,QACjC,MAAc,UAAkB,QAChC,aACwB;AACxB,MAAI,MAAM,aAAa,SAAS,QAC3B,iBAAiB,aAAa,SAAS,WAAW,KAAK;AAE5D,WAAS,IAAI,GAAG,IAAI,YAAY,CAAC,KAAK,KAAK;AACzC,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC;AAC9C,UAAM,IAAI,MAAM;AAAA,MAAa;AAAA,MAAS;AAAA,MACpC,GAAG,mBAAmB,MAAM,CAAC,iBAAiB,IAAI;AAAA,MAAI;AAAA,MAAW;AAAA,IAAK;AACxE,QAAI,EAAE,WAAW,UAAU;AACzB,YAAM,IAAI,MAAM,yCAAyC,EAAE,OAAO,WAAW,SAAS,EAAE;AAAA,IAC1F;AACA,UAAM,EAAE,SAAS,QAAQ,iBAAiB,EAAE,SAAS,WAAW,KAAK;AAAA,EACvE;AACA,SAAO;AACT;AAGA,SAAS,oBAAoB,YAAkC,KAAa,QAAqB;AAC/F,QAAM,UAAU;AAAA,IACd,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,SAAS,OAAO;AAAA,IAChB,SAAS;AAAA,MACP,aAAa,OAAO,KAAK,WAAW,UAAU,CAAC,EAAE,SAAS,QAAQ;AAAA,MAClE,MAAM;AAAA,IACR;AAAA,IACA,UAAU;AAAA,MACR,QAAQ,OAAO;AAAA,MACf,SAAS,OAAO;AAAA,MAChB,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,MACd,OAAO,OAAO;AAAA,IAChB;AAAA,EACF;AACA,SAAO,OAAO,KAAK,KAAK,UAAU,OAAO,CAAC,EAAE,SAAS,QAAQ;AAC/D;AAGA,eAAe,gBACb,YAAwB,YAAuB,QAChB;AAC/B,QAAM,aAAa,IAAI,UAAU,OAAO,KAAK;AAC7C,QAAM,iBAAiB,IAAI,UAAU,OAAO,KAAK;AACjD,QAAM,kBAAkB,MAAM,WAAW,eAAe,UAAU;AAClE,QAAM,YAAY,iBAAiB,MAAM,OAAO,qBAAqB,IACjE,wBACA;AAEJ,QAAM,WAAW,MAAM,QAAQ,YAAY,YAAY,aAAa,SAAS;AAC7E,QAAM,SAAS,OAAO,OAAO,MAAM;AAEnC,QAAM,YAAY,MAAM,0BAA0B,YAAY,YAAY,MAAM,SAAS;AACzF,QAAM,UAAU,MAAM,0BAA0B,YAAY,gBAAgB,MAAM,SAAS;AAE3F,QAAM,KAAK;AAAA,IACT;AAAA,IAAW;AAAA,IAAY;AAAA,IAAS;AAAA,IAChC;AAAA,IAAQ,SAAS;AAAA,IAAU,CAAC;AAAA,IAAG;AAAA,EACjC;AAEA,QAAM,EAAE,UAAU,IAAI,MAAM,WAAW,mBAAmB,WAAW;AACrE,QAAM,MAAM,IAAI,mBAAmB;AAAA,IACjC,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,cAAc,CAAC,EAAE;AAAA,EACnB,CAAC,EAAE,mBAAmB;AAEtB,SAAO,IAAI,qBAAqB,GAAG;AACrC;AAWO,SAAS,yBAAyB,QAA6B;AACpE,QAAM,EAAE,QAAQ,QAAQ,WAAW,IAAI;AACvC,QAAM,UAAU,OAAO,oBAAoB,cAAc,MAAM;AAC/D,QAAM,WAAW,OAAO,mBAAmB;AAC3C,QAAM,SAAS,OAAO,kBAAkB;AACxC,QAAM,aAAa,IAAI,UAAU,MAAM;AAEvC,SAAO,eAAe,SAAS,KAAa,MAAuC;AAEjF,UAAM,YAAY,MAAM,MAAM,KAAK;AAAA,MACjC,GAAG;AAAA,MAAM,SAAS,EAAE,QAAQ,oBAAoB,GAAG,MAAM,QAAQ;AAAA,IACnE,CAAC;AACD,QAAI,UAAU,WAAW,IAAK,QAAO;AAGrC,UAAM,eAAgB,MAAM,UAAU,KAAK;AAC3C,UAAM,SAAS,aAAa,UAAU,CAAC;AACvC,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,0DAA0D;AAGvF,UAAM,aAAa,MAAM,gBAAgB,YAAY,YAAY,MAAM;AAGvE,UAAM,SAAS,MAAM;AAAA,MAAa;AAAA,MAAS;AAAA,MACzC,GAAG,mBAAmB,MAAM,CAAC;AAAA,MAC7B,EAAE,QAAQ,EAAE,aAAa,SAAS,WAAW,UAAU,CAAC,EAAE,EAAE;AAAA,IAAC;AAG/D,UAAM,MAAM,MAAM,iBAAiB,SAAS,QAAQ,QAAQ,OAAO,IAAI,UAAU,QAAQ,MAAM;AAC/F,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,wDAAwD;AAGlF,UAAM,WAAW,oBAAoB,YAAY,KAAK,MAAM;AAC5D,WAAO,MAAM,KAAK;AAAA,MAChB,GAAG;AAAA,MAAM,SAAS,EAAE,QAAQ,oBAAoB,aAAa,UAAU,GAAG,MAAM,QAAQ;AAAA,IAC1F,CAAC;AAAA,EACH;AACF;AAaO,SAAS,kCAAkC,QAAsC;AACtF,QAAM,EAAE,QAAQ,QAAQ,YAAY,gBAAgB,IAAI;AACxD,QAAM,UAAU,OAAO,oBAAoB,cAAc,MAAM;AAC/D,QAAM,WAAW,OAAO,mBAAmB;AAC3C,QAAM,SAAS,OAAO,kBAAkB;AACxC,QAAM,aAAa,IAAI,UAAU,MAAM;AACvC,QAAM,gBAAgB,oBAAoB,eAAe;AAEzD,SAAO,eAAe,SAAS,KAAa,MAAuC;AAEjF,UAAM,YAAY,MAAM,MAAM,KAAK;AAAA,MACjC,GAAG;AAAA,MAAM,SAAS,EAAE,QAAQ,oBAAoB,GAAG,MAAM,QAAQ;AAAA,IACnE,CAAC;AACD,QAAI,UAAU,WAAW,IAAK,QAAO;AAGrC,UAAM,eAAgB,MAAM,UAAU,KAAK;AAC3C,UAAM,SAAS,aAAa,UAAU,CAAC;AACvC,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,0DAA0D;AAGvF,UAAM,aAAa,MAAM,gBAAgB,YAAY,YAAY,MAAM;AAGvE,UAAM,aAAa,MAAM;AAAA,MAAa;AAAA,MAAS;AAAA,MAC7C,GAAG,mBAAmB,MAAM,CAAC;AAAA,MAC7B;AAAA,QACE,QAAQ;AAAA,UACN,aAAa,SAAS,WAAW,UAAU,CAAC;AAAA,UAC5C,QAAQ;AAAA,YACN,MAAM;AAAA,YACN,SAAS,mBAAmB,aAAa;AAAA,UAC3C;AAAA,QACF;AAAA,MACF;AAAA,IAAC;AAEH,UAAM,gBAAgB,WAAW;AACjC,UAAM,kBAAkB,WAAW,WAAW,UAAU,CAAC;AAEzD,QAAI,CAAC,iBAAiB,CAAC,iBAAiB;AACtC,YAAM,IAAI;AAAA,QACR,yEAC0B,aAAa,4BAA4B,MAAM,eAC5D,KAAK,UAAU,UAAU,CAAC;AAAA,MACzC;AAAA,IACF;AAGA,UAAM,gBAAgB,gBAAgB,QAAQ,WACzC,gBAAgB,QAAQ,SAAS,MAAM,GAAG,EAAE,CAAC,KAC7C;AAEL,UAAM,cAAc,YAAY,gBAAgB,SAAS,eAAe;AAGxE,UAAM,eAAe,MAAM;AAAA,MAAa;AAAA,MAAS;AAAA,MAC/C,GAAG,mBAAmB,MAAM,CAAC,iBAAiB,mBAAmB,aAAa,CAAC;AAAA,MAC/E;AAAA,QACE,WAAW,CAAC;AAAA,UACV,QAAQ,mBAAmB,aAAa;AAAA,UACxC,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IAAC;AAGH,UAAM,MAAM,MAAM;AAAA,MAChB;AAAA,MAAS;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAe;AAAA,MAAU;AAAA,MAAQ;AAAA,IAAY;AACxE,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,wDAAwD;AAGlF,UAAM,WAAW,oBAAoB,YAAY,KAAK,MAAM;AAC5D,WAAO,MAAM,KAAK;AAAA,MAChB,GAAG;AAAA,MAAM,SAAS,EAAE,QAAQ,oBAAoB,aAAa,UAAU,GAAG,MAAM,QAAQ;AAAA,IAC1F,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/crossmint.ts"],"sourcesContent":["/**\n * Crossmint smart wallet integration for RelAI x402.\n *\n * Two modes:\n *\n * **API-key mode** — zero private keys, Crossmint signs + broadcasts:\n * ```ts\n * import { createCrossmintX402Fetch } from \"@relai-fi/x402/crossmint\";\n *\n * const fetch402 = createCrossmintX402Fetch({\n * apiKey: process.env.CROSSMINT_API_KEY!,\n * wallet: process.env.CROSSMINT_WALLET!,\n * connection: new Connection(process.env.RPC_URL!),\n * });\n * const resp = await fetch402(\"https://api.example.com/protected\");\n * ```\n *\n * **Delegated mode** — external signer approves each transaction:\n * ```ts\n * import { createCrossmintDelegatedX402Fetch } from \"@relai-fi/x402/crossmint\";\n *\n * const fetch402 = createCrossmintDelegatedX402Fetch({\n * apiKey: process.env.CROSSMINT_API_KEY!,\n * wallet: process.env.CROSSMINT_WALLET!,\n * signerSecretKey: myKeypairBytes, // 64-byte Ed25519\n * connection: new Connection(process.env.RPC_URL!),\n * });\n * const resp = await fetch402(\"https://api.example.com/protected\");\n * ```\n *\n * @module\n */\nimport {\n Connection,\n PublicKey,\n VersionedTransaction,\n TransactionMessage,\n} from \"@solana/web3.js\";\nimport {\n getAssociatedTokenAddress,\n createTransferCheckedInstruction,\n getMint,\n TOKEN_PROGRAM_ID,\n TOKEN_2022_PROGRAM_ID,\n} from \"@solana/spl-token\";\n\nimport nacl from \"tweetnacl\";\n\n// ── Types ───────────────────────────────────────────────────────────\n\nexport interface CrossmintX402Config {\n /** Crossmint server-side API key (`sk_production_...` or `sk_staging_...`) */\n apiKey: string;\n\n /** Crossmint smart wallet address (Solana public key) */\n wallet: string;\n\n /** Solana RPC connection */\n connection: Connection;\n\n /** Override Crossmint API base URL (default: `https://www.crossmint.com/api/2025-06-09`) */\n crossmintApiBase?: string;\n\n /** Max polling attempts for tx confirmation (default: 30) */\n maxPollAttempts?: number;\n\n /** Polling interval in ms (default: 2000) */\n pollIntervalMs?: number;\n\n /** Called after successful on-chain payment with the Solana tx signature */\n onPayment?: (txHash: string) => void;\n}\n\nexport interface CrossmintDelegatedX402Config extends CrossmintX402Config {\n /**\n * Ed25519 secret key for the external signer (64 bytes).\n * The corresponding public key must be registered as an external signer\n * on the Crossmint smart wallet.\n */\n signerSecretKey: Uint8Array;\n\n /** Crossmint environment: \"staging\" or \"production\" (default: auto-detect from apiKey) */\n environment?: \"staging\" | \"production\";\n}\n\n// ── Internals ───────────────────────────────────────────────────────\n\nconst DEFAULT_API_BASE = \"https://www.crossmint.com/api/2025-06-09\";\nconst STAGING_API_BASE = \"https://staging.crossmint.com/api/2025-06-09\";\n\n/** Auto-detect Crossmint API base from API key prefix. */\nfunction detectApiBase(apiKey: string): string {\n if (apiKey.startsWith(\"sk_staging\")) return STAGING_API_BASE;\n return DEFAULT_API_BASE;\n}\n\n/** Encode bytes to base58 without external dependency. */\nfunction toBase58(bytes: Uint8Array): string {\n const ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n const digits = [0];\n for (const byte of bytes) {\n let carry = byte;\n for (let j = 0; j < digits.length; j++) {\n carry += digits[j] << 8;\n digits[j] = carry % 58;\n carry = (carry / 58) | 0;\n }\n while (carry > 0) {\n digits.push(carry % 58);\n carry = (carry / 58) | 0;\n }\n }\n let str = \"\";\n for (const byte of bytes) {\n if (byte === 0) str += ALPHABET[0];\n else break;\n }\n for (let i = digits.length - 1; i >= 0; i--) {\n str += ALPHABET[digits[i]];\n }\n return str;\n}\n\n/** Decode base58 string to bytes. */\nfunction fromBase58(str: string): Uint8Array {\n const ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n const BASE_MAP = new Uint8Array(256).fill(255);\n for (let i = 0; i < ALPHABET.length; i++) BASE_MAP[ALPHABET.charCodeAt(i)] = i;\n const bytes: number[] = [0];\n for (const ch of str) {\n const carry_init = BASE_MAP[ch.charCodeAt(0)];\n if (carry_init === 255) throw new Error(`Invalid base58 character: ${ch}`);\n let carry = carry_init;\n for (let j = 0; j < bytes.length; j++) {\n carry += bytes[j] * 58;\n bytes[j] = carry & 0xff;\n carry >>= 8;\n }\n while (carry > 0) {\n bytes.push(carry & 0xff);\n carry >>= 8;\n }\n }\n for (const ch of str) {\n if (ch === ALPHABET[0]) bytes.push(0);\n else break;\n }\n return new Uint8Array(bytes.reverse());\n}\n\n/** Extract Solana signature from Crossmint's full serialized tx (base58). */\nfunction extractSignature(onChainTx: string): string | null {\n if (!onChainTx) return null;\n if (onChainTx.length <= 100) return onChainTx; // Already a bare signature\n try {\n const decoded = fromBase58(onChainTx);\n const vtx = VersionedTransaction.deserialize(decoded);\n if (vtx.signatures?.[0]) return toBase58(vtx.signatures[0]);\n } catch {\n // Not a valid serialized tx\n }\n return null;\n}\n\n/** Make a Crossmint API call. */\nasync function crossmintApi(\n baseUrl: string, apiKey: string, endpoint: string,\n body?: unknown, method: \"GET\" | \"POST\" = \"POST\",\n): Promise<any> {\n const url = `${baseUrl}/wallets/${endpoint}`;\n const opts: RequestInit = {\n method,\n headers: { \"Content-Type\": \"application/json\", \"X-API-KEY\": apiKey },\n };\n if (method === \"POST\" && body) opts.body = JSON.stringify(body);\n const resp = await fetch(url, opts);\n const json = await resp.json();\n if (!resp.ok) throw new Error(`[x402/crossmint] API ${resp.status}: ${JSON.stringify(json)}`);\n return json;\n}\n\n/** Sign a message with Ed25519. Message can be base58-encoded or UTF-8 string. */\nfunction signMessage(message: string, secretKey: Uint8Array): string {\n let messageBytes: Uint8Array;\n try {\n messageBytes = fromBase58(message);\n } catch {\n messageBytes = new TextEncoder().encode(message);\n }\n const signature = nacl.sign.detached(messageBytes, secretKey);\n return toBase58(signature);\n}\n\n/** Derive public key address from a 64-byte secret key. */\nfunction deriveSignerAddress(secretKey: Uint8Array): string {\n const publicKey = secretKey.length === 64\n ? secretKey.slice(32)\n : nacl.sign.keyPair.fromSeed(secretKey).publicKey;\n return toBase58(publicKey);\n}\n\n/** Poll Crossmint for on-chain txId. Returns signature or null. */\nasync function pollForSignature(\n apiBase: string, apiKey: string, wallet: string,\n txId: string, maxPolls: number, pollMs: number,\n initialData?: any,\n): Promise<string | null> {\n let sig = initialData?.onChain?.txId\n || extractSignature(initialData?.onChain?.transaction) || null;\n\n for (let i = 0; i < maxPolls && !sig; i++) {\n await new Promise((r) => setTimeout(r, pollMs));\n const p = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions/${txId}`, undefined, \"GET\");\n if (p.status === \"failed\") {\n throw new Error(`[x402/crossmint] Crossmint tx failed: ${p.error?.message || \"unknown\"}`);\n }\n sig = p.onChain?.txId || extractSignature(p.onChain?.transaction) || null;\n }\n return sig;\n}\n\n/** Build X-PAYMENT header from unsigned tx + on-chain signature + payment requirements. */\nfunction buildXPaymentHeader(unsignedTx: VersionedTransaction, sig: string, accept: any): string {\n const payload = {\n x402Version: 2,\n scheme: \"exact\",\n network: accept.network,\n payload: {\n transaction: Buffer.from(unsignedTx.serialize()).toString(\"base64\"),\n txId: sig,\n },\n accepted: {\n scheme: accept.scheme,\n network: accept.network,\n amount: accept.amount,\n asset: accept.asset,\n payTo: accept.payTo,\n },\n };\n return Buffer.from(JSON.stringify(payload)).toString(\"base64\");\n}\n\n/** Build unsigned SPL transfer tx from 402 payment requirements. */\nasync function buildTransferTx(\n connection: Connection, userPubkey: PublicKey, accept: any,\n): Promise<VersionedTransaction> {\n const mintPubkey = new PublicKey(accept.asset);\n const merchantPubkey = new PublicKey(accept.payTo);\n const mintAccountInfo = await connection.getAccountInfo(mintPubkey);\n const programId = mintAccountInfo?.owner.equals(TOKEN_2022_PROGRAM_ID)\n ? TOKEN_2022_PROGRAM_ID\n : TOKEN_PROGRAM_ID;\n\n const mintInfo = await getMint(connection, mintPubkey, \"confirmed\", programId);\n const amount = BigInt(accept.amount);\n\n const sourceAta = await getAssociatedTokenAddress(mintPubkey, userPubkey, true, programId);\n const destAta = await getAssociatedTokenAddress(mintPubkey, merchantPubkey, true, programId);\n\n const ix = createTransferCheckedInstruction(\n sourceAta, mintPubkey, destAta, userPubkey,\n amount, mintInfo.decimals, [], programId,\n );\n\n const { blockhash } = await connection.getLatestBlockhash(\"confirmed\");\n const msg = new TransactionMessage({\n payerKey: userPubkey,\n recentBlockhash: blockhash,\n instructions: [ix],\n }).compileToV0Message();\n\n return new VersionedTransaction(msg);\n}\n\n// ── Public API ──────────────────────────────────────────────────────\n\n/**\n * **API-key mode.** Create a fetch wrapper that auto-handles x402 `402`\n * responses using a Crossmint smart wallet on Solana.\n *\n * Crossmint signs and broadcasts — no private key needed.\n * The returned function has the same signature as `fetch()`.\n */\nexport function createCrossmintX402Fetch(config: CrossmintX402Config) {\n const { apiKey, wallet, connection } = config;\n const apiBase = config.crossmintApiBase || detectApiBase(apiKey);\n const maxPolls = config.maxPollAttempts ?? 30;\n const pollMs = config.pollIntervalMs ?? 2000;\n const userPubkey = new PublicKey(wallet);\n\n return async function fetch402(url: string, init?: RequestInit): Promise<Response> {\n // 1. Initial request\n const firstResp = await fetch(url, {\n ...init, headers: { Accept: \"application/json\", ...init?.headers },\n });\n if (firstResp.status !== 402) return firstResp;\n\n // 2. Parse 402\n const requirements = (await firstResp.json()) as any;\n const accept = requirements.accepts?.[0];\n if (!accept) throw new Error(\"[x402/crossmint] No payment requirements in 402 response\");\n\n // 3. Build transfer tx\n const unsignedTx = await buildTransferTx(connection, userPubkey, accept);\n\n // 4. Submit to Crossmint (signs + broadcasts)\n const txData = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions`,\n { params: { transaction: toBase58(unsignedTx.serialize()) } });\n\n // 5. Poll for on-chain signature\n const sig = await pollForSignature(apiBase, apiKey, wallet, txData.id, maxPolls, pollMs, txData);\n if (!sig) throw new Error(\"[x402/crossmint] Timed out waiting for tx confirmation\");\n\n config.onPayment?.(sig);\n\n // 6. Retry with X-PAYMENT\n const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);\n return fetch(url, {\n ...init, headers: { Accept: \"application/json\", \"X-PAYMENT\": xPayment, ...init?.headers },\n });\n };\n}\n\n/**\n * **Delegated mode.** Create a fetch wrapper that auto-handles x402 `402`\n * responses using a Crossmint smart wallet with external signer approval.\n *\n * Each transaction requires cryptographic approval from `signerSecretKey`\n * before Crossmint broadcasts. This provides an extra security layer —\n * even with the API key, transactions cannot execute without the signer.\n *\n * The external signer must be registered on the Crossmint smart wallet\n * via the Crossmint console or API.\n */\nexport function createCrossmintDelegatedX402Fetch(config: CrossmintDelegatedX402Config) {\n const { apiKey, wallet, connection, signerSecretKey } = config;\n const apiBase = config.crossmintApiBase || detectApiBase(apiKey);\n const maxPolls = config.maxPollAttempts ?? 30;\n const pollMs = config.pollIntervalMs ?? 2000;\n const userPubkey = new PublicKey(wallet);\n const signerAddress = deriveSignerAddress(signerSecretKey);\n\n return async function fetch402(url: string, init?: RequestInit): Promise<Response> {\n // 1. Initial request\n const firstResp = await fetch(url, {\n ...init, headers: { Accept: \"application/json\", ...init?.headers },\n });\n if (firstResp.status !== 402) return firstResp;\n\n // 2. Parse 402\n const requirements = (await firstResp.json()) as any;\n const accept = requirements.accepts?.[0];\n if (!accept) throw new Error(\"[x402/crossmint] No payment requirements in 402 response\");\n\n // 3. Build transfer tx\n const unsignedTx = await buildTransferTx(connection, userPubkey, accept);\n\n // 4. Submit to Crossmint with external signer\n const createResp = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions`,\n {\n params: {\n transaction: toBase58(unsignedTx.serialize()),\n signer: {\n type: \"external-wallet\",\n locator: `external-wallet:${signerAddress}`,\n },\n },\n });\n\n const transactionId = createResp.id;\n const pendingApproval = createResp.approvals?.pending?.[0];\n\n if (!transactionId || !pendingApproval) {\n throw new Error(\n `[x402/crossmint] No pending approval returned. ` +\n `Ensure external signer ${signerAddress} is registered on wallet ${wallet}. ` +\n `Response: ${JSON.stringify(createResp)}`\n );\n }\n\n // 5. Sign the approval message\n const pendingSigner = pendingApproval.signer?.address\n ?? pendingApproval.signer?.locator?.split(\":\")[1]\n ?? signerAddress;\n\n const approvalSig = signMessage(pendingApproval.message, signerSecretKey);\n\n // 6. Submit approval\n const approvalResp = await crossmintApi(apiBase, apiKey,\n `${encodeURIComponent(wallet)}/transactions/${encodeURIComponent(transactionId)}/approvals`,\n {\n approvals: [{\n signer: `external-wallet:${pendingSigner}`,\n signature: approvalSig,\n }],\n });\n\n // 7. Poll for on-chain signature\n const sig = await pollForSignature(\n apiBase, apiKey, wallet, transactionId, maxPolls, pollMs, approvalResp);\n if (!sig) throw new Error(\"[x402/crossmint] Timed out waiting for tx confirmation\");\n\n config.onPayment?.(sig);\n\n // 8. Retry with X-PAYMENT\n const xPayment = buildXPaymentHeader(unsignedTx, sig, accept);\n return fetch(url, {\n ...init, headers: { Accept: \"application/json\", \"X-PAYMENT\": xPayment, ...init?.headers },\n });\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relai-fi/x402",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.23",
|
|
4
4
|
"description": "Unified x402 payment SDK for Solana, Base, Avalanche, SKALE Base, SKALE BITE, Polygon, and Ethereum. Automatic 402 handling with zero gas fees.",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.js",
|