Client SDK
The client SDK handles the payment flow automatically:
- Detects 402 responses
- Prompts user to sign payment
- Submits payment to facilitator
- Retries original request with payment proof
Supported Platforms
| Platform | Package |
|---|---|
| React / Next.js | @puga-labs/x402-mantle-sdk/react |
| Vanilla JS | @puga-labs/x402-mantle-sdk/client |
Auto-Detection
Both facilitatorUrl and projectKey are automatically detected from the backend's 402 response. You typically don't need to configure them on the client side.
React Hooks
useMantleX402
Main hook for making paid requests:
import { useMantleX402 } from '@puga-labs/x402-mantle-sdk/react';
function MyComponent() {
// facilitatorUrl auto-detected from backend 402 response
const { postWithPayment } = useMantleX402();
const handleClick = async () => {
try {
const result = await postWithPayment('/api/endpoint', {
// Your request body
prompt: 'Hello'
});
console.log('Response:', result.response);
console.log('Transaction hash:', result.txHash);
} catch (error) {
if (error.code === 4001) {
console.log('User rejected the transaction');
} else {
console.error('Error:', error.message);
}
}
};
return <button onClick={handleClick}>Make Paid Request</button>;
}
With Custom Configuration
const { postWithPayment } = useMantleX402({
resourceUrl: 'https://your-api.com', // Optional, defaults to window.location.origin
});
useEthersWallet
Separate hook for wallet management:
import { useEthersWallet } from '@puga-labs/x402-mantle-sdk/react';
function WalletButton() {
const {
address, // User's wallet address (undefined if not connected)
isConnected, // Boolean connection status
chainId, // Current chain ID (5000 for Mantle)
provider, // ethers.BrowserProvider instance
connect, // Function to connect wallet
disconnect, // Function to disconnect (clears state)
error // Error object if connection failed
} = useEthersWallet({
autoConnect: false // Set true to auto-connect on mount
});
if (error) {
return <div>Error: {error.message}</div>;
}
if (isConnected) {
return (
<div>
<p>Connected: {address}</p>
<p>Chain: {chainId}</p>
<button onClick={disconnect}>Disconnect</button>
</div>
);
}
return <button onClick={connect}>Connect Wallet</button>;
}
Combining Both Hooks
import { useMantleX402, useEthersWallet } from '@puga-labs/x402-mantle-sdk/react';
function PaymentApp() {
const wallet = useEthersWallet();
const { postWithPayment } = useMantleX402();
const handlePayment = async () => {
if (!wallet.isConnected) {
await wallet.connect();
}
// Check network
if (wallet.chainId !== 5000) {
alert('Please switch to Mantle network');
return;
}
const { response, txHash } = await postWithPayment('/api/generate', {
prompt: 'Hello'
});
console.log('Done!', response, txHash);
};
return (
<div>
{wallet.isConnected ? (
<p>Wallet: {wallet.address}</p>
) : (
<button onClick={wallet.connect}>Connect</button>
)}
<button onClick={handlePayment} disabled={!wallet.isConnected}>
Pay $0.01
</button>
</div>
);
}
Vanilla JavaScript
createMantleClient (High-Level)
Simplified API for most use cases:
import { createMantleClient } from '@puga-labs/x402-mantle-sdk/client';
// You need to manage wallet connection yourself
let userAddress: string | undefined;
// Listen for account changes
window.ethereum?.on('accountsChanged', (accounts: string[]) => {
userAddress = accounts[0];
});
// Create client (facilitatorUrl auto-detected from backend)
const client = createMantleClient({
resourceUrl: 'https://your-api.com',
getProvider: () => window.ethereum,
getAccount: () => userAddress
});
// Make paid request
async function makeRequest() {
try {
const { response, txHash } = await client.postWithPayment('/api/generate', {
prompt: 'Hello world'
});
console.log('Response:', response);
console.log('TxHash:', txHash);
} catch (error) {
console.error('Failed:', error);
}
}
createPaymentClient (Low-Level)
Full control over the payment flow:
import { createPaymentClient } from '@puga-labs/x402-mantle-sdk/client';
// Low-level client (facilitatorUrl auto-detected from backend)
const paymentClient = createPaymentClient({
resourceUrl: 'https://your-api.com',
provider: window.ethereum,
userAddress: '0x...' // Optional, will be fetched if not provided
});
// callWithPayment returns more details
const result = await paymentClient.callWithPayment('/api/endpoint', {
data: 'payload'
});
console.log('Response:', result.response);
console.log('TxHash:', result.txHash);
console.log('Payment Header:', result.paymentHeader);
console.log('Requirements:', result.paymentRequirements);
Configuration Options
MantleClientConfig
| Option | Type | Default | Description |
|---|---|---|---|
facilitatorUrl | string | From 402 response | Auto-detected from backend (rarely needed) |
resourceUrl | string | window.location.origin | Base URL of your API |
projectKey | string | From 402 response | Auto-detected from backend (rarely needed) |
getProvider | function | undefined | Function returning wallet provider |
getAccount | function | undefined | Function returning user's address |
UseMantleX402Options (React)
| Option | Type | Default | Description |
|---|---|---|---|
facilitatorUrl | string | From 402 response | Auto-detected from backend |
resourceUrl | string | window.location.origin | Your API URL |
projectKey | string | From 402 response | Auto-detected from backend |
UseEthersWalletOptions (React)
| Option | Type | Default | Description |
|---|---|---|---|
autoConnect | boolean | false | Auto-connect on component mount |
CallWithPaymentResult
interface CallWithPaymentResult<T> {
response: T; // Your API response
txHash?: string; // Blockchain transaction hash
paymentHeader?: string; // Base64-encoded payment proof
paymentRequirements?: PaymentRequirements; // Payment requirements used
}
Error Handling
try {
const { response, txHash } = await postWithPayment('/api/generate', data);
} catch (error) {
if (error.code === 4001) {
// User rejected the transaction
console.log('User cancelled');
} else if (error.code === -32603) {
// Internal error (insufficient funds, etc.)
console.log('Transaction failed:', error.message);
} else {
// Network or facilitator error
console.log('Error:', error.message);
}
}
Next Steps
- Server SDK - Protect your API endpoints
- Examples - Full working examples