Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/smart-mice-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@crossmint/client-sdk-walletconnect": patch
"@crossmint/client-sdk-react-native-ui": patch
"@crossmint/wallets-quickstart-devkit": patch
"expo-demo": patch
"@crossmint/client-sdk-react-ui": patch
"@crossmint/auth-ssr-nextjs-demo": patch
"@crossmint/client-sdk-nextjs-starter": patch
"@crossmint/client-sdk-base": patch
"@crossmint/common-sdk-base": patch
"@crossmint/wallets-sdk": patch
---

Add experimental_payX402 to wallets sdk
2 changes: 1 addition & 1 deletion apps/payments/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"tsconfig.json"
],
"scripts": {
"build": "next build",
"build": "NODE_OPTIONS='--max-old-space-size=16384' next build",
"build:analyze": "cross-env ANALYZE=true next build",
"dev": "next dev -p 3005",
"lint": "next lint",
Expand Down
6 changes: 5 additions & 1 deletion apps/wallets/quickstart-devkit/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ NEXT_PUBLIC_FIREBASE_APP_ID=
MAILOSAUR_API_KEY=
MAILOSAUR_SERVER_ID=
MAILOSAUR_PHONE_NUMBER=
TESTS_CROSSMINT_API_KEY=
TESTS_CROSSMINT_API_KEY=
Comment thread
maxsch-xmint marked this conversation as resolved.
# X402
CROSSMINT_SERVER_API_KEY= # API key for X402 tests
X402_ADMIN_PRIVATE_KEY= # Base64-encoded Solana keypair private key for admin signer
X402_ADMIN_EMAIL= # Email address for admin signer
52 changes: 52 additions & 0 deletions apps/wallets/quickstart-devkit/app/api/x402/pay/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { NextResponse } from "next/server";
import { Keypair } from "@solana/web3.js";
import { createCrossmint, CrossmintWallets, SolanaWallet } from "@crossmint/wallets-sdk";

export async function POST(request: Request) {
try {
const { walletAddress, paymentUrl, chain } = await request.json();

const serverKey = process.env.CROSSMINT_SERVER_API_KEY;
const adminPrivateKey = process.env.X402_ADMIN_PRIVATE_KEY;
const adminEmail = process.env.X402_ADMIN_EMAIL;

if (!serverKey || !adminPrivateKey || !adminEmail) {
return NextResponse.json(
{ error: "Missing CROSSMINT_SERVER_API_KEY or X402_ADMIN_PRIVATE_KEY or X402_ADMIN_EMAIL env vars" },
{ status: 500 }
);
Comment thread
maxsch-xmint marked this conversation as resolved.
}

const privateKeyBytes = new Uint8Array(Buffer.from(adminPrivateKey, "base64"));
const keypair = Keypair.fromSecretKey(privateKeyBytes);

const crossmint = createCrossmint({ apiKey: serverKey });
const wallets = CrossmintWallets.from(crossmint);

const wallet = await wallets.getWallet(walletAddress, {
owner: `email:${adminEmail}`,
chain,
signer: {
type: "external-wallet",
address: keypair.publicKey.toBase58(),
onSignTransaction: async (tx) => {
tx.sign([keypair]);
return tx;
},
},
});

if (chain === "solana") {
const solanaWallet = SolanaWallet.from(wallet);
await solanaWallet.experimental_payX402(paymentUrl);
} else {
throw new Error(`X402 payments are not supported for chain ${chain}`);
}

return NextResponse.json({ success: true });
} catch (err: any) {
const message = err?.message || String(err);
console.error("x402 pay error:", err);
return NextResponse.json({ success: false, error: message }, { status: 500 });
Comment thread
maxsch-xmint marked this conversation as resolved.
}
}
52 changes: 52 additions & 0 deletions apps/wallets/quickstart-devkit/app/api/x402/wallet/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { NextResponse } from "next/server";
import { Keypair, type VersionedTransaction } from "@solana/web3.js";
import { createCrossmint, CrossmintWallets } from "@crossmint/wallets-sdk";

export async function POST() {
try {
const serverKey = process.env.CROSSMINT_SERVER_API_KEY;
const adminPrivateKey = process.env.X402_ADMIN_PRIVATE_KEY;
const adminEmail = process.env.X402_ADMIN_EMAIL;

if (!serverKey || !adminPrivateKey || !adminEmail) {
return NextResponse.json(
{ error: "Missing CROSSMINT_SERVER_API_KEY or X402_ADMIN_PRIVATE_KEY or X402_ADMIN_EMAIL env vars" },
{ status: 500 }
);
}
Comment thread
maxsch-xmint marked this conversation as resolved.

const privateKeyBytes = new Uint8Array(Buffer.from(adminPrivateKey, "base64"));
const keypair = Keypair.fromSecretKey(privateKeyBytes);

const crossmint = createCrossmint({ apiKey: serverKey });
const wallets = CrossmintWallets.from(crossmint);

const signerConfig = {
type: "external-wallet" as const,
address: keypair.publicKey.toBase58(),
onSignTransaction: async (tx: VersionedTransaction) => {
tx.sign([keypair]);
return tx;
},
};

let wallet;
try {
wallet = await wallets.getWallet(keypair.publicKey.toBase58(), {
owner: `email:${adminEmail}`,
chain: "solana",
signer: signerConfig,
});
} catch {
wallet = await wallets.createWallet({
owner: `email:${adminEmail}`,
chain: "solana",
signer: signerConfig,
});
}
Comment thread
maxsch-xmint marked this conversation as resolved.

return NextResponse.json({ address: wallet.address });
} catch (err: any) {
return NextResponse.json({ error: err.message }, { status: 500 });
}
Comment thread
maxsch-xmint marked this conversation as resolved.
}
130 changes: 130 additions & 0 deletions apps/wallets/quickstart-devkit/app/x402/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"use client";

import { useState } from "react";

export default function X402Page() {
const [chain, setChain] = useState("solana");
const [paymentUrl, setPaymentUrl] = useState("http://localhost:8402/protected");
const [walletAddress, setWalletAddress] = useState<string | null>(null);
const [walletLoading, setWalletLoading] = useState(false);
const [walletError, setWalletError] = useState<string | null>(null);

const [payStatus, setPayStatus] = useState<"idle" | "success" | "error">("idle");
const [payLoading, setPayLoading] = useState(false);
const [payError, setPayError] = useState<string | null>(null);

async function handleCreateWallet() {
setWalletLoading(true);
setWalletError(null);
try {
const res = await fetch("/api/x402/wallet", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ chain }),
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error ?? "Failed to create wallet");
}
setWalletAddress(data.address);
} catch (err: any) {
setWalletError(err.message);
} finally {
setWalletLoading(false);
}
}

async function handlePay() {
if (!walletAddress) return;
setPayLoading(true);
setPayStatus("idle");
setPayError(null);
try {
const res = await fetch("/api/x402/pay", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ walletAddress, paymentUrl, chain }),
});
const data = await res.json();
if (data.success) {
setPayStatus("success");
} else {
setPayStatus("error");
setPayError(data.error ?? "Payment failed");
}
} catch (err: any) {
setPayStatus("error");
setPayError(err.message);
} finally {
setPayLoading(false);
}
}

const statusColor = payStatus === "success" ? "bg-green-500" : payStatus === "error" ? "bg-red-500" : "bg-gray-400";

return (
<div className="min-h-screen p-8 max-w-2xl mx-auto">
<h1 className="text-2xl font-bold mb-8">X402 Payment Test Suite</h1>

<section className="mb-8 space-y-4">
<h2 className="text-lg font-semibold">Setup</h2>

<div>
<label className="block text-sm font-medium mb-1">Chain</label>
<select
value={chain}
onChange={(e) => setChain(e.target.value)}
className="border rounded px-3 py-2 w-full"
>
<option value="solana">solana</option>
</select>
</div>

<div>
<label className="block text-sm font-medium mb-1">Payment URL</label>
<input
type="text"
value={paymentUrl}
onChange={(e) => setPaymentUrl(e.target.value)}
placeholder="https://..."
className="border rounded px-3 py-2 w-full"
/>
</div>

<button
onClick={handleCreateWallet}
disabled={walletLoading}
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 disabled:opacity-50"
>
{walletLoading ? "Creating..." : "Create / Get Wallet"}
</button>

{walletAddress && (
<p className="text-sm">
Wallet address: <code className="bg-gray-100 px-1 rounded">{walletAddress}</code>
</p>
)}
{walletError && <p className="text-sm text-red-600">{walletError}</p>}
</section>

{walletAddress && (
<section className="space-y-4">
<h2 className="text-lg font-semibold">Test Suite</h2>

<div className="flex items-center gap-4">
<button
onClick={handlePay}
disabled={payLoading}
className="bg-purple-600 text-white px-4 py-2 rounded hover:bg-purple-700 disabled:opacity-50"
>
{payLoading ? "Paying..." : "farameter/corbits"}
</button>
<span className={`inline-block w-4 h-4 rounded-full ${statusColor}`} />
</div>

{payError && <p className="text-sm text-red-600">{payError}</p>}
</section>
)}
</div>
);
}
4 changes: 2 additions & 2 deletions apps/wallets/quickstart-devkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"build": "NODE_OPTIONS='--max-old-space-size=16384' next build",
"start": "next start",
"lint": "next lint",
"test": "playwright test",
Expand All @@ -20,7 +20,7 @@
"@firebase/auth": "1.10.8",
"@privy-io/react-auth": "2.13.0",
"@solana/spl-token": "0.4.13",
"@solana/web3.js": "1.98.1",
"@solana/web3.js": "1.98.4",
"@stellar/stellar-sdk": "v14.0.0-rc.3",
"clsx": "2.1.1",
"firebase": "^10.3.1",
Expand Down
2 changes: 1 addition & 1 deletion apps/wallets/smart-wallet/expo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"@react-navigation/bottom-tabs": "^7.2.0",
"@react-navigation/native": "^7.0.14",
"@solana/spl-memo": "0.2.5",
"@solana/web3.js": "1.98.1",
"@solana/web3.js": "1.98.4",
"bs58": "^5.0.0",
"buffer": "^6.0.3",
"expo": "~54.0.27",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
},
"pnpm": {
"overrides": {
"@solana/web3.js": "1.98.4",
"axios@0": "0.30.0",
"axios@1": "1.12.2",
"elliptic@6": "6.6.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/client/base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
},
"devDependencies": {
"@ethersproject/transactions": "5.7.0",
"@solana/web3.js": "1.98.1",
"@solana/web3.js": "1.98.4",
"@types/uuid": "9.0.4"
}
}
2 changes: 1 addition & 1 deletion packages/client/ui/react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"@crossmint/common-sdk-auth": "workspace:*",
"@crossmint/common-sdk-base": "workspace:*",
"@crossmint/wallets-sdk": "workspace:*",
"@solana/web3.js": "1.98.1",
"@solana/web3.js": "1.98.4",
"bs58": "^5.0.0",
"lodash.clonedeep": "4.5.0",
"lodash.isequal": "4.5.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/client/ui/react-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@ethersproject/transactions": "5.7.0",
"@farcaster/auth-kit": "0.6.0",
"@mysten/sui": "1.24.0",
"@solana/web3.js": "1.98.1",
"@solana/web3.js": "1.98.4",
"bs58": "5.0.0",
"clsx": "2.1.1",
"color": "4.2.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ function DynamicWalletStateProvider({
type: "external-wallet",
address: connectedDynamicWallet.address,
onSignTransaction: async (transaction: VersionedTransaction) => {
Comment thread
maxsch-xmint marked this conversation as resolved.
return await signer.signTransaction(transaction);
return (await signer.signTransaction(transaction)) as VersionedTransaction;
},
} as SolanaExternalWalletSignerConfig;
onWalletConnected(externalWalletSigner);
Expand Down
2 changes: 1 addition & 1 deletion packages/client/wallets/walletconnect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"devDependencies": {
"@ethersproject/abstract-provider": "5.7.0",
"@ethersproject/providers": "5.7.2",
"@solana/web3.js": "1.98.1",
"@solana/web3.js": "1.98.4",
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
"autoprefixer": "10.4.17",
Expand Down
2 changes: 1 addition & 1 deletion packages/common/base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"test:vitest": "vitest run"
},
"dependencies": {
"@solana/web3.js": "1.98.1",
"@solana/web3.js": "1.98.4",
"bs58": "5.0.0",
"tweetnacl": "1.0.3",
"viem": "2.33.1"
Expand Down
7 changes: 6 additions & 1 deletion packages/wallets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@
"@crossmint/common-sdk-auth": "workspace:*",
"@crossmint/common-sdk-base": "workspace:*",
"@hey-api/client-fetch": "0.8.1",
"@solana/web3.js": "1.98.1",
"@solana/web3.js": "1.98.4",
"@stellar/stellar-sdk": "v14.0.0-rc.3",
"@faremeter/fetch": "0.17.1",
"@faremeter/info": "^0.17.1",
"@faremeter/types": "^0.17.1",
"@faremeter/payment-solana": "0.17.1",
"@logtape/logtape": "1.0.4",
"abitype": "1.0.8",
"bs58": "5.0.0",
"ox": "0.6.9",
Expand Down
Loading
Loading