@dcentralab/d402-client 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  **D402 Payment Protocol Client** for TypeScript/JavaScript
4
4
 
5
- Complete TypeScript implementation of the D402 payment protocol for HTTP 402 (Payment Required) responses. Designed for **React/Next.js frontends** to call 402-protected APIs directly from the browser with wallet-based payments.
5
+ Implementation of the D402 payment protocol for HTTP 402 (Payment Required) responses. Works in any JavaScript environment (React, Next.js, Vue, Node.js, etc.) with automatic payment signing and retry.
6
6
 
7
- Port of Python IATP package to TypeScript with 1:1 API compatibility.
7
+ Part of the iatp-js monorepo. Future packages will add MCP and A2A protocol support.
8
+
9
+ Built by Dcentralab.
8
10
 
9
11
  ## 🚀 Installation
10
12
 
@@ -27,76 +29,143 @@ yarn add @dcentralab/d402-client viem wagmi
27
29
 
28
30
  ## 📖 Quick Start (React/Next.js)
29
31
 
30
- ### Usage in React Component
32
+ ## 💡 Complete React Example (Recommended Pattern)
31
33
 
32
- **No special configuration needed!** Uses native browser APIs (TextEncoder, TextDecoder, Web Crypto).
34
+ ### Custom Hook with Automatic Wallet Management
33
35
 
34
36
  ```typescript
35
37
  'use client'
36
38
 
37
- import { D402Client } from '@dcentralab/d402-client'
39
+ import { useMutation } from '@tanstack/react-query'
38
40
  import { useWalletClient } from 'wagmi'
39
- import { useState } from 'react'
41
+ import {
42
+ createIATPWallet,
43
+ D402Client,
44
+ getWalletsByOwner
45
+ } from '@dcentralab/d402-client'
40
46
 
41
- export default function AnalyzeButton() {
42
- const { data: walletClient } = useWalletClient()
43
- const [result, setResult] = useState(null)
44
- const [loading, setLoading] = useState(false)
47
+ interface PaymentParams {
48
+ endpoint: string
49
+ payload: Record<string, unknown>
50
+ maxValue: bigint
51
+ network?: 'sepolia'
52
+ }
45
53
 
46
- async function analyzeWithPayment() {
47
- if (!walletClient) {
48
- alert('Connect your wallet first!')
49
- return
50
- }
54
+ export function useD402Payment() {
55
+ const { data: walletClient } = useWalletClient()
51
56
 
52
- setLoading(true)
57
+ const mutation = useMutation({
58
+ mutationFn: async ({
59
+ endpoint,
60
+ payload,
61
+ maxValue,
62
+ network = 'sepolia'
63
+ }: PaymentParams) => {
64
+ if (!walletClient) {
65
+ throw new Error('Please connect your wallet first')
66
+ }
67
+
68
+ // Check if user already has IATP wallet
69
+ const existingWallets = await getWalletsByOwner({
70
+ ownerAddress: walletClient.account.address,
71
+ network
72
+ })
53
73
 
54
- try {
55
- // Create D402 client with user's wallet
74
+ // Get or create wallet
75
+ let iatpWalletAddress: `0x${string}`
76
+
77
+ if (existingWallets.length === 0) {
78
+ // Create new wallet (one-time)
79
+ const newWallet = await createIATPWallet({
80
+ ownerAccount: walletClient.account,
81
+ network
82
+ })
83
+ iatpWalletAddress = newWallet.walletAddress
84
+ } else {
85
+ // Use existing wallet
86
+ iatpWalletAddress = existingWallets[0]
87
+ }
88
+
89
+ // Create D402 client
56
90
  const client = new D402Client({
57
91
  operatorAccount: walletClient.account,
58
- iatpWalletAddress: '0xUserIATPWallet...', // User's IATPWallet
59
- maxValue: 1000000n // 1 USDC max
92
+ iatpWalletAddress,
93
+ maxValue
60
94
  })
61
95
 
62
- // Call 402-protected API directly from browser
63
- const response = await client.fetch('https://sentiment-api.com/analyze', {
96
+ // Call 402-protected API
97
+ const response = await client.fetch(endpoint, {
64
98
  method: 'POST',
65
99
  headers: { 'Content-Type': 'application/json' },
66
- body: JSON.stringify({ text: 'Bitcoin looks bullish' })
100
+ body: JSON.stringify(payload)
67
101
  })
68
102
 
69
- const data = await response.json()
70
- setResult(data)
71
-
72
- } catch (error) {
73
- console.error('Payment failed:', error)
74
- } finally {
75
- setLoading(false)
103
+ return await response.json()
76
104
  }
105
+ })
106
+
107
+ return {
108
+ execute: mutation.mutateAsync,
109
+ isLoading: mutation.isPending,
110
+ result: mutation.data,
111
+ error: mutation.error,
112
+ isWalletConnected: !!walletClient
113
+ }
114
+ }
115
+ ```
116
+
117
+ ### Use in Component
118
+
119
+ ```typescript
120
+ 'use client'
121
+
122
+ import { useD402Payment } from '@/hooks/useD402Payment'
123
+
124
+ export default function AnalyzeButton() {
125
+ const { execute, isLoading, result, error, isWalletConnected } = useD402Payment()
126
+
127
+ async function handleAnalyze() {
128
+ await execute({
129
+ endpoint: 'https://sentiment-api.com/analyze',
130
+ payload: { text: 'Bitcoin looks bullish today' },
131
+ maxValue: 1000000n // 1 USDC max
132
+ })
133
+ }
134
+
135
+ if (!isWalletConnected) {
136
+ return <button disabled>Connect Wallet First</button>
77
137
  }
78
138
 
79
139
  return (
80
140
  <div>
81
- <button onClick={analyzeWithPayment} disabled={loading || !walletClient}>
82
- {loading ? 'Processing...' : 'Analyze (0.01 USDC)'}
141
+ <button onClick={handleAnalyze} disabled={isLoading}>
142
+ {isLoading ? 'Processing...' : 'Analyze Sentiment (0.01 USDC)'}
83
143
  </button>
84
- {result && <pre>{JSON.stringify(result, null, 2)}</pre>}
144
+
145
+ {error && (
146
+ <div className="error">
147
+ Error: {error.message}
148
+ </div>
149
+ )}
150
+
151
+ {result && (
152
+ <div className="success">
153
+ <pre>{JSON.stringify(result, null, 2)}</pre>
154
+ </div>
155
+ )}
85
156
  </div>
86
157
  )
87
158
  }
88
159
  ```
89
160
 
90
- **What happens:**
91
- 1. User clicks button
92
- 2. D402Client makes request to external API
93
- 3. API returns 402
94
- 4. Client signs payment with user's wallet
95
- 5. Client retries with payment
96
- 6. API returns data
97
- 7. User sees result
161
+ **This pattern:**
162
+ - Automatically checks for existing wallet
163
+ - Creates wallet only if needed (one-time)
164
+ - Reuses wallet for future calls
165
+ - Handles loading and error states
166
+ - Works with React Query for caching
98
167
 
99
- **All in browser!** No backend or polyfills needed.
168
+ ---
100
169
 
101
170
  ## 🏗️ Features
102
171
 
@@ -117,16 +186,17 @@ const result = await createIATPWallet({
117
186
  rpcUrl: 'https://ethereum-sepolia-rpc.publicnode.com'
118
187
  })
119
188
 
120
- console.log('Wallet:', result.iatpWalletAddress)
121
- console.log('Operator key:', result.operatorPrivateKey) // Store securely!
189
+ console.log('Wallet:', result.walletAddress)
190
+ console.log('Owner:', result.ownerAddress)
122
191
  ```
123
192
 
124
193
  **Returns:**
125
- - `iatpWalletAddress` - IATPWallet contract address
126
- - `operatorAddress` - Operator's address
127
- - `operatorPrivateKey` - Operator's private key (store securely!)
194
+ - `walletAddress` - IATPWallet contract address
195
+ - `ownerAddress` - Owner's address (also the operator)
128
196
  - `transactionHash` - Creation transaction
129
197
  - `blockNumber` - Block where wallet was created
198
+ - `network` - Network where deployed
199
+ - `chainId` - Chain ID
130
200
 
131
201
  ### 2. Automatic 402 Payment Handling
132
202
 
@@ -137,8 +207,8 @@ const client = new D402Client({
137
207
  operatorAccount,
138
208
  iatpWalletAddress: '0xYourWallet...',
139
209
  maxValue: 1000000n,
140
- networkFilter: 'base-mainnet', // Only Base network
141
- schemeFilter: 'exact' // Only exact payments
210
+ networkFilter: 'sepolia', // Only Sepolia network
211
+ schemeFilter: 'exact' // Only exact payments
142
212
  })
143
213
 
144
214
  // Client automatically:
@@ -242,10 +312,9 @@ Get maximum payment limit.
242
312
  Create new IATPWallet contract on-chain.
243
313
 
244
314
  **Parameters:**
245
- - `ownerAccount: Account` - Owner's viem account
246
- - `network?: 'sepolia' | 'localhost'` - Network (default: sepolia)
315
+ - `ownerAccount: Account` - Owner's viem account (will also be the operator)
316
+ - `network?: 'sepolia'` - Network (default: sepolia)
247
317
  - `rpcUrl?: string` - Custom RPC URL
248
- - `operatorPrivateKey?: 0x${string}` - Use specific operator key
249
318
 
250
319
  **`parsePaymentRequirement(response): Promise<PaymentRequirement>`**
251
320
 
@@ -382,27 +451,19 @@ const response = await fetch('/api/analyze', {
382
451
  })
383
452
  ```
384
453
 
385
- **Flow:** Browser → Your Backend → D402Client → External 402 API
386
-
387
- **Pros:**
388
- - ✅ Simpler frontend (no wallet needed)
389
- - ✅ Smooth UX (no wallet popups)
390
-
391
454
  **Cons:**
392
455
  - ⚠️ Backend controls payments
393
456
  - ⚠️ More centralized
394
457
 
395
458
  ## 🌐 Supported Networks
396
459
 
397
- | Network | Chain ID | USDC Address |
398
- |---------|----------|--------------|
399
- | Ethereum Mainnet | 1 | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` |
400
- | Sepolia Testnet | 11155111 | `0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238` |
401
- | Base | 8453 | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
402
- | Base Sepolia | 84532 | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` |
403
- | Polygon | 137 | `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359` |
404
- | Arbitrum One | 42161 | `0xaf88d065e77c8cC2239327C5EDb3A432268e5831` |
405
- | Arbitrum Sepolia | 421614 | `0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d` |
460
+ IATP contracts are currently deployed on:
461
+
462
+ | Network | Type | Chain ID | USDC Address | RPC URL |
463
+ |----------------------|---------|------------|----------------------------------------------|---------------------------------------------------|
464
+ | **Sepolia Testnet** | Testnet | `11155111` | `0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238` | [Chainlist](https://chainlist.org/chain/11155111) |
465
+
466
+ > **Note:** Additional networks (mainnet, Base, Polygon, etc.) will be supported once IATP contracts are deployed on those chains.
406
467
 
407
468
  ## 📦 Package Exports
408
469
 
@@ -544,7 +605,6 @@ Part of the IATP-JS monorepo:
544
605
 
545
606
  ## 🔗 Related Projects
546
607
 
547
- - [IATP Python](https://github.com/Traia-IO/IATP) - Python implementation
548
608
  - [Viem](https://viem.sh) - TypeScript Web3 library
549
609
  - [D402 Protocol](https://docs.cdp.coinbase.com/x402) - HTTP 402 specification
550
610