@warppay402/sdk 1.0.6 → 1.1.1
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 +8 -3
- package/dist/index.d.ts +16 -66
- package/dist/index.js +113 -79
- package/dist/langchain.d.ts +2 -2
- package/openclaw.plugin.json +2 -2
- package/package.json +5 -2
- package/src/index.ts +164 -145
- package/tsconfig.json +2 -1
package/README.md
CHANGED
|
@@ -11,9 +11,12 @@ npm install @warppay402/sdk
|
|
|
11
11
|
```typescript
|
|
12
12
|
import { WarpPayClient } from "@warppay402/sdk";
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
import { WarpPayClient } from "@warppay402/sdk";
|
|
15
|
+
|
|
16
|
+
// Initialize with Base EVM, Solana L1, or both
|
|
15
17
|
const client = new WarpPayClient({
|
|
16
|
-
privateKey: process.env.
|
|
18
|
+
privateKey: process.env.CUSTOMER_BASE_KEY as `0x${string}`,
|
|
19
|
+
solanaPrivateKey: process.env.CUSTOMER_SOLANA_KEY,
|
|
17
20
|
});
|
|
18
21
|
|
|
19
22
|
async function main() {
|
|
@@ -70,8 +73,10 @@ const tools = createWarpPayLangChainTools(client);
|
|
|
70
73
|
```
|
|
71
74
|
## 🌐 API Gateway & Specs
|
|
72
75
|
|
|
73
|
-
Gateway: https://api.warppay402.com
|
|
76
|
+
MCP Gateway Endpoint: https://api.warppay402.com/mcp
|
|
74
77
|
|
|
75
78
|
MCP Manifest: https://api.warppay402.com/.well-known/mcp.json
|
|
76
79
|
|
|
80
|
+
REST Gateway Base: https://api.warppay402.com
|
|
81
|
+
|
|
77
82
|
OpenAPI Spec: https://api.warppay402.com/openapi.json
|
package/dist/index.d.ts
CHANGED
|
@@ -1,77 +1,27 @@
|
|
|
1
1
|
export interface WarpPayConfig {
|
|
2
|
-
/** Base Mainnet private key of the agent's wallet funding
|
|
3
|
-
privateKey
|
|
2
|
+
/** Base Mainnet private key of the agent's wallet funding micro-payments */
|
|
3
|
+
privateKey?: `0x${string}`;
|
|
4
|
+
/** Solana Mainnet base58 private key funding micro-payments */
|
|
5
|
+
solanaPrivateKey?: string;
|
|
4
6
|
/** Custom gateway URL (Defaults to https://api.warppay402.com) */
|
|
5
7
|
baseUrl?: string;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
success: boolean;
|
|
9
|
-
title: string;
|
|
10
|
-
markdown: string;
|
|
11
|
-
truncated: boolean;
|
|
12
|
-
}
|
|
13
|
-
export interface BaseAnalyticsResponse {
|
|
14
|
-
success: boolean;
|
|
15
|
-
network: string;
|
|
16
|
-
address: string;
|
|
17
|
-
ethBalance: string;
|
|
18
|
-
nonce: number;
|
|
19
|
-
timestamp: string;
|
|
20
|
-
}
|
|
21
|
-
export interface PdfExtractorResponse {
|
|
22
|
-
success: boolean;
|
|
23
|
-
pages: number;
|
|
24
|
-
info: Record<string, any>;
|
|
25
|
-
textPreview: string;
|
|
26
|
-
}
|
|
27
|
-
export interface BrowserScrapeResponse {
|
|
28
|
-
success: boolean;
|
|
29
|
-
url: string;
|
|
30
|
-
content: string;
|
|
31
|
-
}
|
|
32
|
-
export interface ScreenshotResponse {
|
|
33
|
-
success: boolean;
|
|
34
|
-
url: string;
|
|
35
|
-
screenshotUrl?: string;
|
|
36
|
-
base64?: string;
|
|
37
|
-
}
|
|
38
|
-
export interface ExtractJsonResponse {
|
|
39
|
-
success: boolean;
|
|
40
|
-
url: string;
|
|
41
|
-
schema: any;
|
|
42
|
-
extractedData: Record<string, any>;
|
|
8
|
+
/** Custom Solana RPC URL */
|
|
9
|
+
solanaRpcUrl?: string;
|
|
43
10
|
}
|
|
44
11
|
export declare class WarpPayClient {
|
|
45
12
|
private baseUrl;
|
|
46
|
-
private account
|
|
13
|
+
private account?;
|
|
14
|
+
private solanaKeypair?;
|
|
15
|
+
private solanaConnection;
|
|
47
16
|
constructor(config: WarpPayConfig);
|
|
48
17
|
/**
|
|
49
|
-
*
|
|
50
|
-
* EIP-712 signing, and automated retry with x402 payment authorization headers.
|
|
18
|
+
* Handles multi-chain HTTP 402 Payment Required challenges dynamically.
|
|
51
19
|
*/
|
|
52
20
|
private executePaidRequest;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
*/
|
|
60
|
-
getBaseAnalytics(address: string): Promise<BaseAnalyticsResponse>;
|
|
61
|
-
/**
|
|
62
|
-
* Downloads and extracts text preview from a public PDF URL ($0.05 USDC on Base)
|
|
63
|
-
*/
|
|
64
|
-
extractPdf(pdfUrl: string): Promise<PdfExtractorResponse>;
|
|
65
|
-
/**
|
|
66
|
-
* Executes JS-rendering browser scraper via proxy workers ($0.05 USDC on Base)
|
|
67
|
-
*/
|
|
68
|
-
browserScrape(url: string): Promise<BrowserScrapeResponse>;
|
|
69
|
-
/**
|
|
70
|
-
* Renders target URL and returns full-page screenshot data ($0.10 USDC on Base)
|
|
71
|
-
*/
|
|
72
|
-
renderScreenshot(url: string): Promise<ScreenshotResponse>;
|
|
73
|
-
/**
|
|
74
|
-
* Extracts structured JSON schema data from web pages ($0.15 USDC on Base)
|
|
75
|
-
*/
|
|
76
|
-
extractJson(url: string, schema?: object): Promise<ExtractJsonResponse>;
|
|
21
|
+
scrapeWeb(url: string): Promise<any>;
|
|
22
|
+
getBaseAnalytics(address: string): Promise<any>;
|
|
23
|
+
extractPdf(pdfUrl: string): Promise<any>;
|
|
24
|
+
browserScrape(url: string): Promise<any>;
|
|
25
|
+
renderScreenshot(url: string): Promise<any>;
|
|
26
|
+
extractJson(url: string, schema?: object): Promise<any>;
|
|
77
27
|
}
|
package/dist/index.js
CHANGED
|
@@ -7,92 +7,144 @@ exports.WarpPayClient = void 0;
|
|
|
7
7
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
8
8
|
const node_buffer_1 = require("node:buffer");
|
|
9
9
|
const accounts_1 = require("viem/accounts");
|
|
10
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
11
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
12
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
10
13
|
class WarpPayClient {
|
|
11
14
|
baseUrl;
|
|
12
15
|
account;
|
|
16
|
+
solanaKeypair;
|
|
17
|
+
solanaConnection;
|
|
13
18
|
constructor(config) {
|
|
14
19
|
this.baseUrl = (config.baseUrl || "https://api.warppay402.com").replace(/\/$/, "");
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
this.solanaConnection = new web3_js_1.Connection(config.solanaRpcUrl || "https://api.mainnet-beta.solana.com", "confirmed");
|
|
21
|
+
// EVM Account Setup
|
|
22
|
+
if (config.privateKey) {
|
|
23
|
+
const rawKey = config.privateKey.trim();
|
|
24
|
+
const formattedKey = (rawKey.startsWith("0x") ? rawKey : `0x${rawKey}`);
|
|
25
|
+
this.account = (0, accounts_1.privateKeyToAccount)(formattedKey);
|
|
26
|
+
}
|
|
27
|
+
// Solana Account Setup
|
|
28
|
+
if (config.solanaPrivateKey) {
|
|
29
|
+
const decodedSecret = bs58_1.default.decode(config.solanaPrivateKey.trim());
|
|
30
|
+
this.solanaKeypair = web3_js_1.Keypair.fromSecretKey(decodedSecret);
|
|
31
|
+
}
|
|
32
|
+
if (!this.account && !this.solanaKeypair) {
|
|
33
|
+
throw new Error("WarpPayClient requires either a Base privateKey or a solanaPrivateKey.");
|
|
34
|
+
}
|
|
18
35
|
}
|
|
19
36
|
/**
|
|
20
|
-
*
|
|
21
|
-
* EIP-712 signing, and automated retry with x402 payment authorization headers.
|
|
37
|
+
* Handles multi-chain HTTP 402 Payment Required challenges dynamically.
|
|
22
38
|
*/
|
|
23
39
|
async executePaidRequest(endpoint, payload) {
|
|
24
40
|
const url = `${this.baseUrl}${endpoint}`;
|
|
25
|
-
// 1. Initial Request
|
|
41
|
+
// 1. Initial Request Probe
|
|
26
42
|
let response = await fetch(url, {
|
|
27
43
|
method: "POST",
|
|
28
44
|
headers: { "Content-Type": "application/json" },
|
|
29
45
|
body: JSON.stringify(payload),
|
|
30
46
|
});
|
|
31
|
-
// 2.
|
|
47
|
+
// 2. Multi-Chain Settlement Challenge Response
|
|
32
48
|
if (response.status === 402) {
|
|
33
49
|
const challenge = await response.json();
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
50
|
+
const accepts = challenge.x402?.accepts || challenge.accepts || [];
|
|
51
|
+
// Check for Solana offer if solanaKeypair is available
|
|
52
|
+
const solanaReq = accepts.find((a) => a.network?.includes("solana"));
|
|
53
|
+
const evmReq = accepts.find((a) => a.network?.includes("eip155"));
|
|
54
|
+
let paymentPayload;
|
|
55
|
+
if (solanaReq && this.solanaKeypair) {
|
|
56
|
+
// Execute Solana L1 SPL-USDC Transaction Authorization
|
|
57
|
+
const payToPubkey = new web3_js_1.PublicKey(solanaReq.payTo);
|
|
58
|
+
const usdcMint = new web3_js_1.PublicKey(solanaReq.asset || "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
59
|
+
const amountUnits = BigInt(solanaReq.amount || solanaReq.maxAmountRequired || "10000"); // $0.01 USDC
|
|
60
|
+
const senderPubkey = this.solanaKeypair.publicKey;
|
|
61
|
+
const senderAta = await (0, spl_token_1.getAssociatedTokenAddress)(usdcMint, senderPubkey);
|
|
62
|
+
const recipientAta = await (0, spl_token_1.getAssociatedTokenAddress)(usdcMint, payToPubkey);
|
|
63
|
+
const tx = new web3_js_1.Transaction();
|
|
64
|
+
tx.feePayer = senderPubkey;
|
|
65
|
+
tx.recentBlockhash = (await this.solanaConnection.getLatestBlockhash()).blockhash;
|
|
66
|
+
// Idempotently create recipient ATA if required
|
|
67
|
+
tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(senderPubkey, recipientAta, payToPubkey, usdcMint));
|
|
68
|
+
// Append SPL-USDC Transfer instruction
|
|
69
|
+
tx.add((0, spl_token_1.createTransferInstruction)(senderAta, recipientAta, senderPubkey, amountUnits));
|
|
70
|
+
tx.sign(this.solanaKeypair);
|
|
71
|
+
const serializedTx = node_buffer_1.Buffer.from(tx.serialize()).toString("base64");
|
|
72
|
+
paymentPayload = {
|
|
73
|
+
x402Version: 2,
|
|
74
|
+
scheme: solanaReq.scheme || "exact",
|
|
75
|
+
network: solanaReq.network || "solana:5eykt4wA89m8E5b9B5658p445VTc28",
|
|
76
|
+
signature: serializedTx,
|
|
77
|
+
paymentPayload: {
|
|
78
|
+
signature: serializedTx,
|
|
79
|
+
network: solanaReq.network
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
else if (evmReq && this.account) {
|
|
84
|
+
// EVM EIP-712 Base Path
|
|
85
|
+
const payTo = (evmReq.payToAddress || evmReq.payTo);
|
|
86
|
+
const assetContract = (evmReq.asset || evmReq.usdcAddress);
|
|
87
|
+
const value = BigInt(evmReq.maxAmountRequired || evmReq.amount || "10000");
|
|
88
|
+
const domain = {
|
|
89
|
+
name: evmReq.extra?.name || "USD Coin",
|
|
90
|
+
version: evmReq.extra?.version || "2",
|
|
91
|
+
chainId: 8453,
|
|
92
|
+
verifyingContract: assetContract,
|
|
93
|
+
};
|
|
94
|
+
const types = {
|
|
95
|
+
TransferWithAuthorization: [
|
|
96
|
+
{ name: "from", type: "address" },
|
|
97
|
+
{ name: "to", type: "address" },
|
|
98
|
+
{ name: "value", type: "uint256" },
|
|
99
|
+
{ name: "validAfter", type: "uint256" },
|
|
100
|
+
{ name: "validBefore", type: "uint256" },
|
|
101
|
+
{ name: "nonce", type: "bytes32" },
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
const now = Math.floor(Date.now() / 1000);
|
|
105
|
+
const nonce = `0x${node_crypto_1.default.randomBytes(32).toString("hex")}`;
|
|
106
|
+
const message = {
|
|
107
|
+
from: this.account.address,
|
|
108
|
+
to: payTo,
|
|
109
|
+
value,
|
|
110
|
+
validAfter: BigInt(0),
|
|
111
|
+
validBefore: BigInt(now + 3600),
|
|
112
|
+
nonce,
|
|
113
|
+
};
|
|
114
|
+
const signature = await this.account.signTypedData({
|
|
115
|
+
domain,
|
|
116
|
+
types,
|
|
117
|
+
primaryType: "TransferWithAuthorization",
|
|
118
|
+
message,
|
|
119
|
+
});
|
|
120
|
+
paymentPayload = {
|
|
121
|
+
x402Version: 2,
|
|
122
|
+
scheme: evmReq.scheme || "exact",
|
|
123
|
+
network: evmReq.network || "eip155:8453",
|
|
124
|
+
payload: {
|
|
125
|
+
authorization: {
|
|
126
|
+
from: this.account.address,
|
|
127
|
+
to: payTo,
|
|
128
|
+
value: value.toString(),
|
|
129
|
+
validAfter: "0",
|
|
130
|
+
validBefore: (now + 3600).toString(),
|
|
131
|
+
nonce,
|
|
132
|
+
},
|
|
133
|
+
signature,
|
|
83
134
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
throw new Error("No matching private key configured for returned 402 networks.");
|
|
139
|
+
}
|
|
87
140
|
const encodedPayload = node_buffer_1.Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
|
|
88
|
-
//
|
|
141
|
+
// Resubmit request with signed x402 headers
|
|
89
142
|
response = await fetch(url, {
|
|
90
143
|
method: "POST",
|
|
91
144
|
headers: {
|
|
92
145
|
"Content-Type": "application/json",
|
|
93
146
|
"X-PAYMENT": encodedPayload,
|
|
94
147
|
"PAYMENT-SIGNATURE": encodedPayload,
|
|
95
|
-
"X-PAYMENT-SIGNATURE": signature,
|
|
96
148
|
},
|
|
97
149
|
body: JSON.stringify(payload),
|
|
98
150
|
});
|
|
@@ -103,39 +155,21 @@ class WarpPayClient {
|
|
|
103
155
|
}
|
|
104
156
|
return (await response.json());
|
|
105
157
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Scrapes any Web URL into clean Markdown for AI context ($0.01 USDC on Base)
|
|
108
|
-
*/
|
|
109
158
|
async scrapeWeb(url) {
|
|
110
159
|
return this.executePaidRequest("/api/v1/tools/web-scraper", { url });
|
|
111
160
|
}
|
|
112
|
-
/**
|
|
113
|
-
* Fetches Base Mainnet balance and transaction stats for any 0x wallet ($0.02 USDC on Base)
|
|
114
|
-
*/
|
|
115
161
|
async getBaseAnalytics(address) {
|
|
116
162
|
return this.executePaidRequest("/api/v1/tools/base-analytics", { address });
|
|
117
163
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Downloads and extracts text preview from a public PDF URL ($0.05 USDC on Base)
|
|
120
|
-
*/
|
|
121
164
|
async extractPdf(pdfUrl) {
|
|
122
165
|
return this.executePaidRequest("/api/v1/tools/pdf-extractor", { pdfUrl });
|
|
123
166
|
}
|
|
124
|
-
/**
|
|
125
|
-
* Executes JS-rendering browser scraper via proxy workers ($0.05 USDC on Base)
|
|
126
|
-
*/
|
|
127
167
|
async browserScrape(url) {
|
|
128
168
|
return this.executePaidRequest("/api/v1/tools/browser-scraper", { url });
|
|
129
169
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Renders target URL and returns full-page screenshot data ($0.10 USDC on Base)
|
|
132
|
-
*/
|
|
133
170
|
async renderScreenshot(url) {
|
|
134
171
|
return this.executePaidRequest("/api/v1/tools/render-screenshot", { url });
|
|
135
172
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Extracts structured JSON schema data from web pages ($0.15 USDC on Base)
|
|
138
|
-
*/
|
|
139
173
|
async extractJson(url, schema) {
|
|
140
174
|
return this.executePaidRequest("/api/v1/tools/extract-json", { url, schema });
|
|
141
175
|
}
|
package/dist/langchain.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare function createWarpPayLangChainTools(client: WarpPayClient): ({
|
|
|
7
7
|
description: string;
|
|
8
8
|
func: ({ url }: {
|
|
9
9
|
url: string;
|
|
10
|
-
}) => Promise<
|
|
10
|
+
}) => Promise<any>;
|
|
11
11
|
} | {
|
|
12
12
|
name: string;
|
|
13
13
|
description: string;
|
|
@@ -19,5 +19,5 @@ export declare function createWarpPayLangChainTools(client: WarpPayClient): ({
|
|
|
19
19
|
description: string;
|
|
20
20
|
func: ({ pdfUrl }: {
|
|
21
21
|
pdfUrl: string;
|
|
22
|
-
}) => Promise<
|
|
22
|
+
}) => Promise<any>;
|
|
23
23
|
})[];
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "warppay402-sdk",
|
|
3
3
|
"name": "warppay402-sdk",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"description": "Official TypeScript SDK
|
|
4
|
+
"version": "1.1.1",
|
|
5
|
+
"description": "Official WarpPay402 TypeScript SDK: Enabling seamless pay-per-use AI tools across both Base Mainnet and Solana."
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warppay402/sdk",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Official TypeScript SDK
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "Official WarpPay402 TypeScript SDK: Enabling seamless pay-per-use AI tools across both Base Mainnet and Solana.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -24,7 +24,10 @@
|
|
|
24
24
|
"langchain"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"@solana/spl-token": "^0.4.15",
|
|
28
|
+
"@solana/web3.js": "^1.98.4",
|
|
27
29
|
"@warppay402/server": "^1.0.0",
|
|
30
|
+
"bs58": "^6.0.0",
|
|
28
31
|
"viem": "^2.0.0"
|
|
29
32
|
},
|
|
30
33
|
"devDependencies": {
|
package/src/index.ts
CHANGED
|
@@ -1,157 +1,194 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import { Buffer } from "node:buffer";
|
|
3
3
|
import { privateKeyToAccount } from "viem/accounts";
|
|
4
|
+
import { Keypair, Connection, Transaction, PublicKey } from "@solana/web3.js";
|
|
5
|
+
import {
|
|
6
|
+
getAssociatedTokenAddress,
|
|
7
|
+
createTransferInstruction,
|
|
8
|
+
createAssociatedTokenAccountIdempotentInstruction
|
|
9
|
+
} from "@solana/spl-token";
|
|
10
|
+
import bs58 from "bs58";
|
|
4
11
|
|
|
5
12
|
export interface WarpPayConfig {
|
|
6
|
-
/** Base Mainnet private key of the agent's wallet funding
|
|
7
|
-
privateKey
|
|
13
|
+
/** Base Mainnet private key of the agent's wallet funding micro-payments */
|
|
14
|
+
privateKey?: `0x${string}`;
|
|
15
|
+
/** Solana Mainnet base58 private key funding micro-payments */
|
|
16
|
+
solanaPrivateKey?: string;
|
|
8
17
|
/** Custom gateway URL (Defaults to https://api.warppay402.com) */
|
|
9
18
|
baseUrl?: string;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export interface ScrapeResponse {
|
|
13
|
-
success: boolean;
|
|
14
|
-
title: string;
|
|
15
|
-
markdown: string;
|
|
16
|
-
truncated: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface BaseAnalyticsResponse {
|
|
20
|
-
success: boolean;
|
|
21
|
-
network: string;
|
|
22
|
-
address: string;
|
|
23
|
-
ethBalance: string;
|
|
24
|
-
nonce: number;
|
|
25
|
-
timestamp: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface PdfExtractorResponse {
|
|
29
|
-
success: boolean;
|
|
30
|
-
pages: number;
|
|
31
|
-
info: Record<string, any>;
|
|
32
|
-
textPreview: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface BrowserScrapeResponse {
|
|
36
|
-
success: boolean;
|
|
37
|
-
url: string;
|
|
38
|
-
content: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface ScreenshotResponse {
|
|
42
|
-
success: boolean;
|
|
43
|
-
url: string;
|
|
44
|
-
screenshotUrl?: string;
|
|
45
|
-
base64?: string;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface ExtractJsonResponse {
|
|
49
|
-
success: boolean;
|
|
50
|
-
url: string;
|
|
51
|
-
schema: any;
|
|
52
|
-
extractedData: Record<string, any>;
|
|
19
|
+
/** Custom Solana RPC URL */
|
|
20
|
+
solanaRpcUrl?: string;
|
|
53
21
|
}
|
|
54
22
|
|
|
55
23
|
export class WarpPayClient {
|
|
56
24
|
private baseUrl: string;
|
|
57
|
-
private account
|
|
25
|
+
private account?: ReturnType<typeof privateKeyToAccount>;
|
|
26
|
+
private solanaKeypair?: Keypair;
|
|
27
|
+
private solanaConnection: Connection;
|
|
58
28
|
|
|
59
29
|
constructor(config: WarpPayConfig) {
|
|
60
30
|
this.baseUrl = (config.baseUrl || "https://api.warppay402.com").replace(/\/$/, "");
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
31
|
+
this.solanaConnection = new Connection(
|
|
32
|
+
config.solanaRpcUrl || "https://api.mainnet-beta.solana.com",
|
|
33
|
+
"confirmed"
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// EVM Account Setup
|
|
37
|
+
if (config.privateKey) {
|
|
38
|
+
const rawKey = config.privateKey.trim();
|
|
39
|
+
const formattedKey = (rawKey.startsWith("0x") ? rawKey : `0x${rawKey}`) as `0x${string}`;
|
|
40
|
+
this.account = privateKeyToAccount(formattedKey);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Solana Account Setup
|
|
44
|
+
if (config.solanaPrivateKey) {
|
|
45
|
+
const decodedSecret = bs58.decode(config.solanaPrivateKey.trim());
|
|
46
|
+
this.solanaKeypair = Keypair.fromSecretKey(decodedSecret);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!this.account && !this.solanaKeypair) {
|
|
50
|
+
throw new Error("WarpPayClient requires either a Base privateKey or a solanaPrivateKey.");
|
|
51
|
+
}
|
|
65
52
|
}
|
|
66
53
|
|
|
67
54
|
/**
|
|
68
|
-
*
|
|
69
|
-
* EIP-712 signing, and automated retry with x402 payment authorization headers.
|
|
55
|
+
* Handles multi-chain HTTP 402 Payment Required challenges dynamically.
|
|
70
56
|
*/
|
|
71
57
|
private async executePaidRequest<T>(endpoint: string, payload: Record<string, any>): Promise<T> {
|
|
72
58
|
const url = `${this.baseUrl}${endpoint}`;
|
|
73
59
|
|
|
74
|
-
// 1. Initial Request
|
|
60
|
+
// 1. Initial Request Probe
|
|
75
61
|
let response = await fetch(url, {
|
|
76
62
|
method: "POST",
|
|
77
63
|
headers: { "Content-Type": "application/json" },
|
|
78
64
|
body: JSON.stringify(payload),
|
|
79
65
|
});
|
|
80
66
|
|
|
81
|
-
// 2.
|
|
67
|
+
// 2. Multi-Chain Settlement Challenge Response
|
|
82
68
|
if (response.status === 402) {
|
|
83
69
|
const challenge = await response.json();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
70
|
+
const accepts: Array<any> = challenge.x402?.accepts || challenge.accepts || [];
|
|
71
|
+
|
|
72
|
+
// Check for Solana offer if solanaKeypair is available
|
|
73
|
+
const solanaReq = accepts.find((a) => a.network?.includes("solana"));
|
|
74
|
+
const evmReq = accepts.find((a) => a.network?.includes("eip155"));
|
|
75
|
+
|
|
76
|
+
let paymentPayload: any;
|
|
77
|
+
|
|
78
|
+
if (solanaReq && this.solanaKeypair) {
|
|
79
|
+
// Execute Solana L1 SPL-USDC Transaction Authorization
|
|
80
|
+
const payToPubkey = new PublicKey(solanaReq.payTo);
|
|
81
|
+
const usdcMint = new PublicKey(solanaReq.asset || "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
82
|
+
const amountUnits = BigInt(solanaReq.amount || solanaReq.maxAmountRequired || "10000"); // $0.01 USDC
|
|
83
|
+
|
|
84
|
+
const senderPubkey = this.solanaKeypair.publicKey;
|
|
85
|
+
const senderAta = await getAssociatedTokenAddress(usdcMint, senderPubkey);
|
|
86
|
+
const recipientAta = await getAssociatedTokenAddress(usdcMint, payToPubkey);
|
|
87
|
+
|
|
88
|
+
const tx = new Transaction();
|
|
89
|
+
tx.feePayer = senderPubkey;
|
|
90
|
+
tx.recentBlockhash = (await this.solanaConnection.getLatestBlockhash()).blockhash;
|
|
91
|
+
|
|
92
|
+
// Idempotently create recipient ATA if required
|
|
93
|
+
tx.add(
|
|
94
|
+
createAssociatedTokenAccountIdempotentInstruction(
|
|
95
|
+
senderPubkey,
|
|
96
|
+
recipientAta,
|
|
97
|
+
payToPubkey,
|
|
98
|
+
usdcMint
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Append SPL-USDC Transfer instruction
|
|
103
|
+
tx.add(
|
|
104
|
+
createTransferInstruction(senderAta, recipientAta, senderPubkey, amountUnits)
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
tx.sign(this.solanaKeypair);
|
|
108
|
+
const serializedTx = Buffer.from(tx.serialize()).toString("base64");
|
|
109
|
+
|
|
110
|
+
paymentPayload = {
|
|
111
|
+
x402Version: 2,
|
|
112
|
+
scheme: solanaReq.scheme || "exact",
|
|
113
|
+
network: solanaReq.network || "solana:5eykt4wA89m8E5b9B5658p445VTc28",
|
|
114
|
+
signature: serializedTx,
|
|
115
|
+
paymentPayload: {
|
|
116
|
+
signature: serializedTx,
|
|
117
|
+
network: solanaReq.network
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
} else if (evmReq && this.account) {
|
|
121
|
+
// EVM EIP-712 Base Path
|
|
122
|
+
const payTo = (evmReq.payToAddress || evmReq.payTo) as `0x${string}`;
|
|
123
|
+
const assetContract = (evmReq.asset || evmReq.usdcAddress) as `0x${string}`;
|
|
124
|
+
const value = BigInt(evmReq.maxAmountRequired || evmReq.amount || "10000");
|
|
125
|
+
|
|
126
|
+
const domain = {
|
|
127
|
+
name: evmReq.extra?.name || "USD Coin",
|
|
128
|
+
version: evmReq.extra?.version || "2",
|
|
129
|
+
chainId: 8453,
|
|
130
|
+
verifyingContract: assetContract,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const types = {
|
|
134
|
+
TransferWithAuthorization: [
|
|
135
|
+
{ name: "from", type: "address" },
|
|
136
|
+
{ name: "to", type: "address" },
|
|
137
|
+
{ name: "value", type: "uint256" },
|
|
138
|
+
{ name: "validAfter", type: "uint256" },
|
|
139
|
+
{ name: "validBefore", type: "uint256" },
|
|
140
|
+
{ name: "nonce", type: "bytes32" },
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const now = Math.floor(Date.now() / 1000);
|
|
145
|
+
const nonce = `0x${crypto.randomBytes(32).toString("hex")}` as `0x${string}`;
|
|
146
|
+
|
|
147
|
+
const message = {
|
|
148
|
+
from: this.account.address,
|
|
149
|
+
to: payTo,
|
|
150
|
+
value,
|
|
151
|
+
validAfter: BigInt(0),
|
|
152
|
+
validBefore: BigInt(now + 3600),
|
|
153
|
+
nonce,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const signature = await this.account.signTypedData({
|
|
157
|
+
domain,
|
|
158
|
+
types,
|
|
159
|
+
primaryType: "TransferWithAuthorization",
|
|
160
|
+
message,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
paymentPayload = {
|
|
164
|
+
x402Version: 2,
|
|
165
|
+
scheme: evmReq.scheme || "exact",
|
|
166
|
+
network: evmReq.network || "eip155:8453",
|
|
167
|
+
payload: {
|
|
168
|
+
authorization: {
|
|
169
|
+
from: this.account.address,
|
|
170
|
+
to: payTo,
|
|
171
|
+
value: value.toString(),
|
|
172
|
+
validAfter: "0",
|
|
173
|
+
validBefore: (now + 3600).toString(),
|
|
174
|
+
nonce,
|
|
175
|
+
},
|
|
176
|
+
signature,
|
|
140
177
|
},
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
178
|
+
};
|
|
179
|
+
} else {
|
|
180
|
+
throw new Error("No matching private key configured for returned 402 networks.");
|
|
181
|
+
}
|
|
144
182
|
|
|
145
183
|
const encodedPayload = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
|
|
146
184
|
|
|
147
|
-
//
|
|
185
|
+
// Resubmit request with signed x402 headers
|
|
148
186
|
response = await fetch(url, {
|
|
149
187
|
method: "POST",
|
|
150
188
|
headers: {
|
|
151
189
|
"Content-Type": "application/json",
|
|
152
190
|
"X-PAYMENT": encodedPayload,
|
|
153
191
|
"PAYMENT-SIGNATURE": encodedPayload,
|
|
154
|
-
"X-PAYMENT-SIGNATURE": signature,
|
|
155
192
|
},
|
|
156
193
|
body: JSON.stringify(payload),
|
|
157
194
|
});
|
|
@@ -165,45 +202,27 @@ export class WarpPayClient {
|
|
|
165
202
|
return (await response.json()) as T;
|
|
166
203
|
}
|
|
167
204
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
*/
|
|
171
|
-
public async scrapeWeb(url: string): Promise<ScrapeResponse> {
|
|
172
|
-
return this.executePaidRequest<ScrapeResponse>("/api/v1/tools/web-scraper", { url });
|
|
205
|
+
public async scrapeWeb(url: string): Promise<any> {
|
|
206
|
+
return this.executePaidRequest("/api/v1/tools/web-scraper", { url });
|
|
173
207
|
}
|
|
174
208
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
*/
|
|
178
|
-
public async getBaseAnalytics(address: string): Promise<BaseAnalyticsResponse> {
|
|
179
|
-
return this.executePaidRequest<BaseAnalyticsResponse>("/api/v1/tools/base-analytics", { address });
|
|
209
|
+
public async getBaseAnalytics(address: string): Promise<any> {
|
|
210
|
+
return this.executePaidRequest("/api/v1/tools/base-analytics", { address });
|
|
180
211
|
}
|
|
181
212
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
*/
|
|
185
|
-
public async extractPdf(pdfUrl: string): Promise<PdfExtractorResponse> {
|
|
186
|
-
return this.executePaidRequest<PdfExtractorResponse>("/api/v1/tools/pdf-extractor", { pdfUrl });
|
|
213
|
+
public async extractPdf(pdfUrl: string): Promise<any> {
|
|
214
|
+
return this.executePaidRequest("/api/v1/tools/pdf-extractor", { pdfUrl });
|
|
187
215
|
}
|
|
188
216
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
*/
|
|
192
|
-
public async browserScrape(url: string): Promise<BrowserScrapeResponse> {
|
|
193
|
-
return this.executePaidRequest<BrowserScrapeResponse>("/api/v1/tools/browser-scraper", { url });
|
|
217
|
+
public async browserScrape(url: string): Promise<any> {
|
|
218
|
+
return this.executePaidRequest("/api/v1/tools/browser-scraper", { url });
|
|
194
219
|
}
|
|
195
220
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
*/
|
|
199
|
-
public async renderScreenshot(url: string): Promise<ScreenshotResponse> {
|
|
200
|
-
return this.executePaidRequest<ScreenshotResponse>("/api/v1/tools/render-screenshot", { url });
|
|
221
|
+
public async renderScreenshot(url: string): Promise<any> {
|
|
222
|
+
return this.executePaidRequest("/api/v1/tools/render-screenshot", { url });
|
|
201
223
|
}
|
|
202
224
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
*/
|
|
206
|
-
public async extractJson(url: string, schema?: object): Promise<ExtractJsonResponse> {
|
|
207
|
-
return this.executePaidRequest<ExtractJsonResponse>("/api/v1/tools/extract-json", { url, schema });
|
|
225
|
+
public async extractJson(url: string, schema?: object): Promise<any> {
|
|
226
|
+
return this.executePaidRequest("/api/v1/tools/extract-json", { url, schema });
|
|
208
227
|
}
|
|
209
228
|
}
|