@warppay402/sdk 1.1.5 → 1.1.7
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/.env.example +12 -0
- package/dist/index.cjs +231 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +194 -185
- package/openclaw.plugin.json +1 -1
- package/package.json +16 -6
- package/dist/langchain.d.ts +0 -23
- package/dist/langchain.js +0 -34
package/.env.example
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# EVM / Base Mainnet Wallet Setup
|
|
2
|
+
# Private key for funding Base x402 USDC micropayments (must start with 0x)
|
|
3
|
+
CUSTOMER_BASE_KEY=0x_your_evm_private_key_here
|
|
4
|
+
|
|
5
|
+
# Solana L1 Wallet Setup
|
|
6
|
+
# Base58-encoded secret key for funding Solana SPL USDC micropayments
|
|
7
|
+
CUSTOMER_SOLANA_KEY=base58_encoded_solana_secret_key_here
|
|
8
|
+
|
|
9
|
+
# Optional Configuration Overrides
|
|
10
|
+
# BASE_URL=https://api.warppay402.com
|
|
11
|
+
# BASE_RPC_URL=https://mainnet.base.org
|
|
12
|
+
# SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
WarpPayClient: () => WarpPayClient
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
var import_node_crypto = __toESM(require("crypto"), 1);
|
|
37
|
+
var import_node_buffer = require("buffer");
|
|
38
|
+
var import_accounts = require("viem/accounts");
|
|
39
|
+
var import_web3 = require("@solana/web3.js");
|
|
40
|
+
var import_spl_token = require("@solana/spl-token");
|
|
41
|
+
var import_bs58 = __toESM(require("bs58"), 1);
|
|
42
|
+
var bs58 = import_bs58.default.default || import_bs58.default;
|
|
43
|
+
var WarpPayClient = class {
|
|
44
|
+
baseUrl;
|
|
45
|
+
account;
|
|
46
|
+
solanaKeypair;
|
|
47
|
+
solanaConnection;
|
|
48
|
+
constructor(config) {
|
|
49
|
+
this.baseUrl = (config.baseUrl || "https://api.warppay402.com").replace(/\/$/, "");
|
|
50
|
+
this.solanaConnection = new import_web3.Connection(
|
|
51
|
+
config.solanaRpcUrl || "https://api.mainnet-beta.solana.com",
|
|
52
|
+
"confirmed"
|
|
53
|
+
);
|
|
54
|
+
if (config.privateKey) {
|
|
55
|
+
const rawKey = config.privateKey.trim();
|
|
56
|
+
const formattedKey = rawKey.startsWith("0x") ? rawKey : `0x${rawKey}`;
|
|
57
|
+
this.account = (0, import_accounts.privateKeyToAccount)(formattedKey);
|
|
58
|
+
}
|
|
59
|
+
if (config.solanaPrivateKey) {
|
|
60
|
+
const decodedSecret = bs58.decode(config.solanaPrivateKey.trim());
|
|
61
|
+
this.solanaKeypair = import_web3.Keypair.fromSecretKey(decodedSecret);
|
|
62
|
+
}
|
|
63
|
+
if (!this.account && !this.solanaKeypair) {
|
|
64
|
+
throw new Error("WarpPayClient requires either a Base privateKey or a solanaPrivateKey.");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Handles multi-chain HTTP 402 Payment Required challenges dynamically.
|
|
69
|
+
*/
|
|
70
|
+
async executePaidRequest(endpoint, payload, method = "POST") {
|
|
71
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
72
|
+
console.log(`
|
|
73
|
+
\u{1F50D} [SDK DEBUG] Initiating ${method} request to: ${url}`);
|
|
74
|
+
const fetchOptions = {
|
|
75
|
+
method,
|
|
76
|
+
headers: { "Content-Type": "application/json" }
|
|
77
|
+
};
|
|
78
|
+
if (method === "POST" && payload) {
|
|
79
|
+
fetchOptions.body = JSON.stringify(payload);
|
|
80
|
+
console.log(`\u{1F50D} [SDK DEBUG] Request Payload:`, JSON.stringify(payload));
|
|
81
|
+
}
|
|
82
|
+
let response = await fetch(url, fetchOptions);
|
|
83
|
+
console.log(`\u{1F50D} [SDK DEBUG] Probe Response Status: ${response.status} ${response.statusText}`);
|
|
84
|
+
if (response.status === 402) {
|
|
85
|
+
const challenge = await response.json();
|
|
86
|
+
console.log(`\u{1F50D} [SDK DEBUG] Received 402 Challenge Payload:`, JSON.stringify(challenge, null, 2));
|
|
87
|
+
const accepts = challenge.x402?.accepts || challenge.accepts || [];
|
|
88
|
+
console.log(`\u{1F50D} [SDK DEBUG] Accepted Payment Rails (${accepts.length}):`, accepts.map((a) => a.network));
|
|
89
|
+
const solanaReq = accepts.find((a) => a.network?.includes("solana"));
|
|
90
|
+
const evmReq = accepts.find((a) => a.network?.includes("eip155"));
|
|
91
|
+
let paymentPayload;
|
|
92
|
+
if (solanaReq && this.solanaKeypair) {
|
|
93
|
+
console.log(`\u{1F50D} [SDK DEBUG] Processing Solana L1 SPL Payment challenge...`);
|
|
94
|
+
const payToPubkey = new import_web3.PublicKey(solanaReq.payTo);
|
|
95
|
+
const usdcMint = new import_web3.PublicKey(solanaReq.asset || "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
96
|
+
const amountUnits = BigInt(solanaReq.amount || solanaReq.maxAmountRequired || "10000");
|
|
97
|
+
const senderPubkey = this.solanaKeypair.publicKey;
|
|
98
|
+
const senderAta = await (0, import_spl_token.getAssociatedTokenAddress)(usdcMint, senderPubkey);
|
|
99
|
+
const recipientAta = await (0, import_spl_token.getAssociatedTokenAddress)(usdcMint, payToPubkey);
|
|
100
|
+
const tx = new import_web3.Transaction();
|
|
101
|
+
tx.feePayer = senderPubkey;
|
|
102
|
+
tx.recentBlockhash = (await this.solanaConnection.getLatestBlockhash()).blockhash;
|
|
103
|
+
tx.add(
|
|
104
|
+
(0, import_spl_token.createAssociatedTokenAccountIdempotentInstruction)(
|
|
105
|
+
senderPubkey,
|
|
106
|
+
recipientAta,
|
|
107
|
+
payToPubkey,
|
|
108
|
+
usdcMint
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
tx.add(
|
|
112
|
+
(0, import_spl_token.createTransferInstruction)(senderAta, recipientAta, senderPubkey, amountUnits)
|
|
113
|
+
);
|
|
114
|
+
tx.sign(this.solanaKeypair);
|
|
115
|
+
const serializedTx = import_node_buffer.Buffer.from(tx.serialize()).toString("base64");
|
|
116
|
+
paymentPayload = {
|
|
117
|
+
x402Version: 2,
|
|
118
|
+
scheme: solanaReq.scheme || "exact",
|
|
119
|
+
network: solanaReq.network || "solana:5eykt4wA89m8E5b9B5658p445VTc28",
|
|
120
|
+
signature: serializedTx,
|
|
121
|
+
paymentPayload: {
|
|
122
|
+
signature: serializedTx,
|
|
123
|
+
network: solanaReq.network
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
} else if (evmReq && this.account) {
|
|
127
|
+
console.log(`\u{1F50D} [SDK DEBUG] Processing EVM EIP-712 Payment challenge...`);
|
|
128
|
+
const payTo = evmReq.payToAddress || evmReq.payTo;
|
|
129
|
+
const assetContract = evmReq.asset || evmReq.usdcAddress;
|
|
130
|
+
const value = BigInt(evmReq.maxAmountRequired || evmReq.amount || "10000");
|
|
131
|
+
console.log(`\u{1F50D} [SDK DEBUG] Signer Wallet: ${this.account.address} -> PayTo: ${payTo} | Amount: ${value.toString()}`);
|
|
132
|
+
const domain = {
|
|
133
|
+
name: evmReq.extra?.name || "USD Coin",
|
|
134
|
+
version: evmReq.extra?.version || "2",
|
|
135
|
+
chainId: 8453,
|
|
136
|
+
verifyingContract: assetContract
|
|
137
|
+
};
|
|
138
|
+
const types = {
|
|
139
|
+
TransferWithAuthorization: [
|
|
140
|
+
{ name: "from", type: "address" },
|
|
141
|
+
{ name: "to", type: "address" },
|
|
142
|
+
{ name: "value", type: "uint256" },
|
|
143
|
+
{ name: "validAfter", type: "uint256" },
|
|
144
|
+
{ name: "validBefore", type: "uint256" },
|
|
145
|
+
{ name: "nonce", type: "bytes32" }
|
|
146
|
+
]
|
|
147
|
+
};
|
|
148
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
149
|
+
const nonce = `0x${import_node_crypto.default.randomBytes(32).toString("hex")}`;
|
|
150
|
+
const message = {
|
|
151
|
+
from: this.account.address,
|
|
152
|
+
to: payTo,
|
|
153
|
+
value,
|
|
154
|
+
validAfter: BigInt(0),
|
|
155
|
+
validBefore: BigInt(now + 3600),
|
|
156
|
+
nonce
|
|
157
|
+
};
|
|
158
|
+
const signature = await this.account.signTypedData({
|
|
159
|
+
domain,
|
|
160
|
+
types,
|
|
161
|
+
primaryType: "TransferWithAuthorization",
|
|
162
|
+
message
|
|
163
|
+
});
|
|
164
|
+
paymentPayload = {
|
|
165
|
+
x402Version: 2,
|
|
166
|
+
scheme: evmReq.scheme || "exact",
|
|
167
|
+
network: evmReq.network || "eip155:8453",
|
|
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
|
|
177
|
+
};
|
|
178
|
+
} else {
|
|
179
|
+
console.error(`\u274C [SDK DEBUG] Matching key missing! EVM Account Present: ${Boolean(this.account)} | Solana Keypair Present: ${Boolean(this.solanaKeypair)}`);
|
|
180
|
+
throw new Error("No matching private key configured for returned 402 networks.");
|
|
181
|
+
}
|
|
182
|
+
const encodedPayload = import_node_buffer.Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
|
|
183
|
+
console.log(`\u{1F50D} [SDK DEBUG] Generated Signed x402 Base64 Header (Length: ${encodedPayload.length})`);
|
|
184
|
+
const retryHeaders = {
|
|
185
|
+
"Content-Type": "application/json",
|
|
186
|
+
"PAYMENT-SIGNATURE": encodedPayload,
|
|
187
|
+
"X-PAYMENT-RESPONSE": encodedPayload,
|
|
188
|
+
"X-PAYMENT": encodedPayload,
|
|
189
|
+
"authorization": `Bearer ${encodedPayload}`
|
|
190
|
+
};
|
|
191
|
+
const retryOptions = {
|
|
192
|
+
method,
|
|
193
|
+
headers: retryHeaders
|
|
194
|
+
};
|
|
195
|
+
if (method === "POST" && payload) {
|
|
196
|
+
retryOptions.body = JSON.stringify(payload);
|
|
197
|
+
}
|
|
198
|
+
console.log(`\u{1F50D} [SDK DEBUG] Resubmitting paid ${method} request to: ${url}`);
|
|
199
|
+
response = await fetch(url, retryOptions);
|
|
200
|
+
console.log(`\u{1F50D} [SDK DEBUG] Paid Retry Response Status: ${response.status} ${response.statusText}`);
|
|
201
|
+
}
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
const errText = await response.text();
|
|
204
|
+
console.error(`\u274C [SDK DEBUG] Request Rejection Body:`, errText);
|
|
205
|
+
throw new Error(`WarpPay API Error (${response.status}): ${errText}`);
|
|
206
|
+
}
|
|
207
|
+
return await response.json();
|
|
208
|
+
}
|
|
209
|
+
async scrapeWeb(url) {
|
|
210
|
+
return this.executePaidRequest("/api/v1/tools/web-scraper", { url });
|
|
211
|
+
}
|
|
212
|
+
async getBaseAnalytics(address) {
|
|
213
|
+
return this.executePaidRequest("/api/v1/tools/base-analytics", { address });
|
|
214
|
+
}
|
|
215
|
+
async extractPdf(pdfUrl) {
|
|
216
|
+
return this.executePaidRequest("/api/v1/tools/pdf-extractor", { pdfUrl });
|
|
217
|
+
}
|
|
218
|
+
async browserScrape(url) {
|
|
219
|
+
return this.executePaidRequest("/api/v1/tools/browser-scraper", { url });
|
|
220
|
+
}
|
|
221
|
+
async renderScreenshot(url) {
|
|
222
|
+
return this.executePaidRequest("/api/v1/tools/render-screenshot", { url });
|
|
223
|
+
}
|
|
224
|
+
async extractJson(url, schema) {
|
|
225
|
+
return this.executePaidRequest("/api/v1/tools/extract-json", { url, schema });
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
229
|
+
0 && (module.exports = {
|
|
230
|
+
WarpPayClient
|
|
231
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
interface WarpPayConfig {
|
|
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;
|
|
6
|
+
/** Custom gateway URL (Defaults to https://api.warppay402.com) */
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
/** Custom Solana RPC URL */
|
|
9
|
+
solanaRpcUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
declare class WarpPayClient {
|
|
12
|
+
private baseUrl;
|
|
13
|
+
private account?;
|
|
14
|
+
private solanaKeypair?;
|
|
15
|
+
private solanaConnection;
|
|
16
|
+
constructor(config: WarpPayConfig);
|
|
17
|
+
/**
|
|
18
|
+
* Handles multi-chain HTTP 402 Payment Required challenges dynamically.
|
|
19
|
+
*/
|
|
20
|
+
executePaidRequest<T>(endpoint: string, payload?: Record<string, any>, method?: "GET" | "POST"): Promise<T>;
|
|
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>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { WarpPayClient, type WarpPayConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
interface WarpPayConfig {
|
|
2
2
|
/** Base Mainnet private key of the agent's wallet funding micro-payments */
|
|
3
3
|
privateKey?: `0x${string}`;
|
|
4
4
|
/** Solana Mainnet base58 private key funding micro-payments */
|
|
@@ -8,7 +8,7 @@ export interface WarpPayConfig {
|
|
|
8
8
|
/** Custom Solana RPC URL */
|
|
9
9
|
solanaRpcUrl?: string;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
declare class WarpPayClient {
|
|
12
12
|
private baseUrl;
|
|
13
13
|
private account?;
|
|
14
14
|
private solanaKeypair?;
|
|
@@ -25,3 +25,5 @@ export declare class WarpPayClient {
|
|
|
25
25
|
renderScreenshot(url: string): Promise<any>;
|
|
26
26
|
extractJson(url: string, schema?: object): Promise<any>;
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
export { WarpPayClient, type WarpPayConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,191 +1,200 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
}
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Handles multi-chain HTTP 402 Payment Required challenges dynamically.
|
|
38
|
-
*/
|
|
39
|
-
async executePaidRequest(endpoint, payload, method = "POST") {
|
|
40
|
-
const url = `${this.baseUrl}${endpoint}`;
|
|
41
|
-
console.log(`\n🔍 [SDK DEBUG] Initiating ${method} request to: ${url}`);
|
|
42
|
-
const fetchOptions = {
|
|
43
|
-
method,
|
|
44
|
-
headers: { "Content-Type": "application/json" },
|
|
45
|
-
};
|
|
46
|
-
if (method === "POST" && payload) {
|
|
47
|
-
fetchOptions.body = JSON.stringify(payload);
|
|
48
|
-
console.log(`🔍 [SDK DEBUG] Request Payload:`, JSON.stringify(payload));
|
|
49
|
-
}
|
|
50
|
-
// 1. Initial Request Probe
|
|
51
|
-
let response = await fetch(url, fetchOptions);
|
|
52
|
-
console.log(`🔍 [SDK DEBUG] Probe Response Status: ${response.status} ${response.statusText}`);
|
|
53
|
-
// 2. Multi-Chain Settlement Challenge Response
|
|
54
|
-
if (response.status === 402) {
|
|
55
|
-
const challenge = await response.json();
|
|
56
|
-
console.log(`🔍 [SDK DEBUG] Received 402 Challenge Payload:`, JSON.stringify(challenge, null, 2));
|
|
57
|
-
const accepts = challenge.x402?.accepts || challenge.accepts || [];
|
|
58
|
-
console.log(`🔍 [SDK DEBUG] Accepted Payment Rails (${accepts.length}):`, accepts.map((a) => a.network));
|
|
59
|
-
const solanaReq = accepts.find((a) => a.network?.includes("solana"));
|
|
60
|
-
const evmReq = accepts.find((a) => a.network?.includes("eip155"));
|
|
61
|
-
let paymentPayload;
|
|
62
|
-
if (solanaReq && this.solanaKeypair) {
|
|
63
|
-
console.log(`🔍 [SDK DEBUG] Processing Solana L1 SPL Payment challenge...`);
|
|
64
|
-
const payToPubkey = new web3_js_1.PublicKey(solanaReq.payTo);
|
|
65
|
-
const usdcMint = new web3_js_1.PublicKey(solanaReq.asset || "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
66
|
-
const amountUnits = BigInt(solanaReq.amount || solanaReq.maxAmountRequired || "10000");
|
|
67
|
-
const senderPubkey = this.solanaKeypair.publicKey;
|
|
68
|
-
const senderAta = await (0, spl_token_1.getAssociatedTokenAddress)(usdcMint, senderPubkey);
|
|
69
|
-
const recipientAta = await (0, spl_token_1.getAssociatedTokenAddress)(usdcMint, payToPubkey);
|
|
70
|
-
const tx = new web3_js_1.Transaction();
|
|
71
|
-
tx.feePayer = senderPubkey;
|
|
72
|
-
tx.recentBlockhash = (await this.solanaConnection.getLatestBlockhash()).blockhash;
|
|
73
|
-
tx.add((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(senderPubkey, recipientAta, payToPubkey, usdcMint));
|
|
74
|
-
tx.add((0, spl_token_1.createTransferInstruction)(senderAta, recipientAta, senderPubkey, amountUnits));
|
|
75
|
-
tx.sign(this.solanaKeypair);
|
|
76
|
-
const serializedTx = node_buffer_1.Buffer.from(tx.serialize()).toString("base64");
|
|
77
|
-
paymentPayload = {
|
|
78
|
-
x402Version: 2,
|
|
79
|
-
scheme: solanaReq.scheme || "exact",
|
|
80
|
-
network: solanaReq.network || "solana:5eykt4wA89m8E5b9B5658p445VTc28",
|
|
81
|
-
signature: serializedTx,
|
|
82
|
-
paymentPayload: {
|
|
83
|
-
signature: serializedTx,
|
|
84
|
-
network: solanaReq.network
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
else if (evmReq && this.account) {
|
|
89
|
-
console.log(`🔍 [SDK DEBUG] Processing EVM EIP-712 Payment challenge...`);
|
|
90
|
-
const payTo = (evmReq.payToAddress || evmReq.payTo);
|
|
91
|
-
const assetContract = (evmReq.asset || evmReq.usdcAddress);
|
|
92
|
-
const value = BigInt(evmReq.maxAmountRequired || evmReq.amount || "10000");
|
|
93
|
-
console.log(`🔍 [SDK DEBUG] Signer Wallet: ${this.account.address} -> PayTo: ${payTo} | Amount: ${value.toString()}`);
|
|
94
|
-
const domain = {
|
|
95
|
-
name: evmReq.extra?.name || "USD Coin",
|
|
96
|
-
version: evmReq.extra?.version || "2",
|
|
97
|
-
chainId: 8453,
|
|
98
|
-
verifyingContract: assetContract,
|
|
99
|
-
};
|
|
100
|
-
const types = {
|
|
101
|
-
TransferWithAuthorization: [
|
|
102
|
-
{ name: "from", type: "address" },
|
|
103
|
-
{ name: "to", type: "address" },
|
|
104
|
-
{ name: "value", type: "uint256" },
|
|
105
|
-
{ name: "validAfter", type: "uint256" },
|
|
106
|
-
{ name: "validBefore", type: "uint256" },
|
|
107
|
-
{ name: "nonce", type: "bytes32" },
|
|
108
|
-
],
|
|
109
|
-
};
|
|
110
|
-
const now = Math.floor(Date.now() / 1000);
|
|
111
|
-
const nonce = `0x${node_crypto_1.default.randomBytes(32).toString("hex")}`;
|
|
112
|
-
const message = {
|
|
113
|
-
from: this.account.address,
|
|
114
|
-
to: payTo,
|
|
115
|
-
value,
|
|
116
|
-
validAfter: BigInt(0),
|
|
117
|
-
validBefore: BigInt(now + 3600),
|
|
118
|
-
nonce,
|
|
119
|
-
};
|
|
120
|
-
const signature = await this.account.signTypedData({
|
|
121
|
-
domain,
|
|
122
|
-
types,
|
|
123
|
-
primaryType: "TransferWithAuthorization",
|
|
124
|
-
message,
|
|
125
|
-
});
|
|
126
|
-
paymentPayload = {
|
|
127
|
-
x402Version: 2,
|
|
128
|
-
scheme: evmReq.scheme || "exact",
|
|
129
|
-
network: evmReq.network || "eip155:8453",
|
|
130
|
-
authorization: {
|
|
131
|
-
from: this.account.address,
|
|
132
|
-
to: payTo,
|
|
133
|
-
value: value.toString(),
|
|
134
|
-
validAfter: "0",
|
|
135
|
-
validBefore: (now + 3600).toString(),
|
|
136
|
-
nonce,
|
|
137
|
-
},
|
|
138
|
-
signature,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
console.error(`❌ [SDK DEBUG] Matching key missing! EVM Account Present: ${Boolean(this.account)} | Solana Keypair Present: ${Boolean(this.solanaKeypair)}`);
|
|
143
|
-
throw new Error("No matching private key configured for returned 402 networks.");
|
|
144
|
-
}
|
|
145
|
-
const encodedPayload = node_buffer_1.Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
|
|
146
|
-
console.log(`🔍 [SDK DEBUG] Generated Signed x402 Base64 Header (Length: ${encodedPayload.length})`);
|
|
147
|
-
const retryHeaders = {
|
|
148
|
-
"Content-Type": "application/json",
|
|
149
|
-
"PAYMENT-SIGNATURE": encodedPayload,
|
|
150
|
-
"X-PAYMENT-RESPONSE": encodedPayload,
|
|
151
|
-
"X-PAYMENT": encodedPayload,
|
|
152
|
-
"authorization": `Bearer ${encodedPayload}`,
|
|
153
|
-
};
|
|
154
|
-
const retryOptions = {
|
|
155
|
-
method,
|
|
156
|
-
headers: retryHeaders,
|
|
157
|
-
};
|
|
158
|
-
if (method === "POST" && payload) {
|
|
159
|
-
retryOptions.body = JSON.stringify(payload);
|
|
160
|
-
}
|
|
161
|
-
console.log(`🔍 [SDK DEBUG] Resubmitting paid ${method} request to: ${url}`);
|
|
162
|
-
response = await fetch(url, retryOptions);
|
|
163
|
-
console.log(`🔍 [SDK DEBUG] Paid Retry Response Status: ${response.status} ${response.statusText}`);
|
|
164
|
-
}
|
|
165
|
-
if (!response.ok) {
|
|
166
|
-
const errText = await response.text();
|
|
167
|
-
console.error(`❌ [SDK DEBUG] Request Rejection Body:`, errText);
|
|
168
|
-
throw new Error(`WarpPay API Error (${response.status}): ${errText}`);
|
|
169
|
-
}
|
|
170
|
-
return (await response.json());
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { Buffer } from "buffer";
|
|
4
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
5
|
+
import { Keypair, Connection, Transaction, PublicKey } from "@solana/web3.js";
|
|
6
|
+
import {
|
|
7
|
+
getAssociatedTokenAddress,
|
|
8
|
+
createTransferInstruction,
|
|
9
|
+
createAssociatedTokenAccountIdempotentInstruction
|
|
10
|
+
} from "@solana/spl-token";
|
|
11
|
+
import bs58Module from "bs58";
|
|
12
|
+
var bs58 = bs58Module.default || bs58Module;
|
|
13
|
+
var WarpPayClient = class {
|
|
14
|
+
baseUrl;
|
|
15
|
+
account;
|
|
16
|
+
solanaKeypair;
|
|
17
|
+
solanaConnection;
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.baseUrl = (config.baseUrl || "https://api.warppay402.com").replace(/\/$/, "");
|
|
20
|
+
this.solanaConnection = new Connection(
|
|
21
|
+
config.solanaRpcUrl || "https://api.mainnet-beta.solana.com",
|
|
22
|
+
"confirmed"
|
|
23
|
+
);
|
|
24
|
+
if (config.privateKey) {
|
|
25
|
+
const rawKey = config.privateKey.trim();
|
|
26
|
+
const formattedKey = rawKey.startsWith("0x") ? rawKey : `0x${rawKey}`;
|
|
27
|
+
this.account = privateKeyToAccount(formattedKey);
|
|
171
28
|
}
|
|
172
|
-
|
|
173
|
-
|
|
29
|
+
if (config.solanaPrivateKey) {
|
|
30
|
+
const decodedSecret = bs58.decode(config.solanaPrivateKey.trim());
|
|
31
|
+
this.solanaKeypair = Keypair.fromSecretKey(decodedSecret);
|
|
174
32
|
}
|
|
175
|
-
|
|
176
|
-
|
|
33
|
+
if (!this.account && !this.solanaKeypair) {
|
|
34
|
+
throw new Error("WarpPayClient requires either a Base privateKey or a solanaPrivateKey.");
|
|
177
35
|
}
|
|
178
|
-
|
|
179
|
-
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Handles multi-chain HTTP 402 Payment Required challenges dynamically.
|
|
39
|
+
*/
|
|
40
|
+
async executePaidRequest(endpoint, payload, method = "POST") {
|
|
41
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
42
|
+
console.log(`
|
|
43
|
+
\u{1F50D} [SDK DEBUG] Initiating ${method} request to: ${url}`);
|
|
44
|
+
const fetchOptions = {
|
|
45
|
+
method,
|
|
46
|
+
headers: { "Content-Type": "application/json" }
|
|
47
|
+
};
|
|
48
|
+
if (method === "POST" && payload) {
|
|
49
|
+
fetchOptions.body = JSON.stringify(payload);
|
|
50
|
+
console.log(`\u{1F50D} [SDK DEBUG] Request Payload:`, JSON.stringify(payload));
|
|
180
51
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
52
|
+
let response = await fetch(url, fetchOptions);
|
|
53
|
+
console.log(`\u{1F50D} [SDK DEBUG] Probe Response Status: ${response.status} ${response.statusText}`);
|
|
54
|
+
if (response.status === 402) {
|
|
55
|
+
const challenge = await response.json();
|
|
56
|
+
console.log(`\u{1F50D} [SDK DEBUG] Received 402 Challenge Payload:`, JSON.stringify(challenge, null, 2));
|
|
57
|
+
const accepts = challenge.x402?.accepts || challenge.accepts || [];
|
|
58
|
+
console.log(`\u{1F50D} [SDK DEBUG] Accepted Payment Rails (${accepts.length}):`, accepts.map((a) => a.network));
|
|
59
|
+
const solanaReq = accepts.find((a) => a.network?.includes("solana"));
|
|
60
|
+
const evmReq = accepts.find((a) => a.network?.includes("eip155"));
|
|
61
|
+
let paymentPayload;
|
|
62
|
+
if (solanaReq && this.solanaKeypair) {
|
|
63
|
+
console.log(`\u{1F50D} [SDK DEBUG] Processing Solana L1 SPL Payment challenge...`);
|
|
64
|
+
const payToPubkey = new PublicKey(solanaReq.payTo);
|
|
65
|
+
const usdcMint = new PublicKey(solanaReq.asset || "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
66
|
+
const amountUnits = BigInt(solanaReq.amount || solanaReq.maxAmountRequired || "10000");
|
|
67
|
+
const senderPubkey = this.solanaKeypair.publicKey;
|
|
68
|
+
const senderAta = await getAssociatedTokenAddress(usdcMint, senderPubkey);
|
|
69
|
+
const recipientAta = await getAssociatedTokenAddress(usdcMint, payToPubkey);
|
|
70
|
+
const tx = new Transaction();
|
|
71
|
+
tx.feePayer = senderPubkey;
|
|
72
|
+
tx.recentBlockhash = (await this.solanaConnection.getLatestBlockhash()).blockhash;
|
|
73
|
+
tx.add(
|
|
74
|
+
createAssociatedTokenAccountIdempotentInstruction(
|
|
75
|
+
senderPubkey,
|
|
76
|
+
recipientAta,
|
|
77
|
+
payToPubkey,
|
|
78
|
+
usdcMint
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
tx.add(
|
|
82
|
+
createTransferInstruction(senderAta, recipientAta, senderPubkey, amountUnits)
|
|
83
|
+
);
|
|
84
|
+
tx.sign(this.solanaKeypair);
|
|
85
|
+
const serializedTx = Buffer.from(tx.serialize()).toString("base64");
|
|
86
|
+
paymentPayload = {
|
|
87
|
+
x402Version: 2,
|
|
88
|
+
scheme: solanaReq.scheme || "exact",
|
|
89
|
+
network: solanaReq.network || "solana:5eykt4wA89m8E5b9B5658p445VTc28",
|
|
90
|
+
signature: serializedTx,
|
|
91
|
+
paymentPayload: {
|
|
92
|
+
signature: serializedTx,
|
|
93
|
+
network: solanaReq.network
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
} else if (evmReq && this.account) {
|
|
97
|
+
console.log(`\u{1F50D} [SDK DEBUG] Processing EVM EIP-712 Payment challenge...`);
|
|
98
|
+
const payTo = evmReq.payToAddress || evmReq.payTo;
|
|
99
|
+
const assetContract = evmReq.asset || evmReq.usdcAddress;
|
|
100
|
+
const value = BigInt(evmReq.maxAmountRequired || evmReq.amount || "10000");
|
|
101
|
+
console.log(`\u{1F50D} [SDK DEBUG] Signer Wallet: ${this.account.address} -> PayTo: ${payTo} | Amount: ${value.toString()}`);
|
|
102
|
+
const domain = {
|
|
103
|
+
name: evmReq.extra?.name || "USD Coin",
|
|
104
|
+
version: evmReq.extra?.version || "2",
|
|
105
|
+
chainId: 8453,
|
|
106
|
+
verifyingContract: assetContract
|
|
107
|
+
};
|
|
108
|
+
const types = {
|
|
109
|
+
TransferWithAuthorization: [
|
|
110
|
+
{ name: "from", type: "address" },
|
|
111
|
+
{ name: "to", type: "address" },
|
|
112
|
+
{ name: "value", type: "uint256" },
|
|
113
|
+
{ name: "validAfter", type: "uint256" },
|
|
114
|
+
{ name: "validBefore", type: "uint256" },
|
|
115
|
+
{ name: "nonce", type: "bytes32" }
|
|
116
|
+
]
|
|
117
|
+
};
|
|
118
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
119
|
+
const nonce = `0x${crypto.randomBytes(32).toString("hex")}`;
|
|
120
|
+
const message = {
|
|
121
|
+
from: this.account.address,
|
|
122
|
+
to: payTo,
|
|
123
|
+
value,
|
|
124
|
+
validAfter: BigInt(0),
|
|
125
|
+
validBefore: BigInt(now + 3600),
|
|
126
|
+
nonce
|
|
127
|
+
};
|
|
128
|
+
const signature = await this.account.signTypedData({
|
|
129
|
+
domain,
|
|
130
|
+
types,
|
|
131
|
+
primaryType: "TransferWithAuthorization",
|
|
132
|
+
message
|
|
133
|
+
});
|
|
134
|
+
paymentPayload = {
|
|
135
|
+
x402Version: 2,
|
|
136
|
+
scheme: evmReq.scheme || "exact",
|
|
137
|
+
network: evmReq.network || "eip155:8453",
|
|
138
|
+
authorization: {
|
|
139
|
+
from: this.account.address,
|
|
140
|
+
to: payTo,
|
|
141
|
+
value: value.toString(),
|
|
142
|
+
validAfter: "0",
|
|
143
|
+
validBefore: (now + 3600).toString(),
|
|
144
|
+
nonce
|
|
145
|
+
},
|
|
146
|
+
signature
|
|
147
|
+
};
|
|
148
|
+
} else {
|
|
149
|
+
console.error(`\u274C [SDK DEBUG] Matching key missing! EVM Account Present: ${Boolean(this.account)} | Solana Keypair Present: ${Boolean(this.solanaKeypair)}`);
|
|
150
|
+
throw new Error("No matching private key configured for returned 402 networks.");
|
|
151
|
+
}
|
|
152
|
+
const encodedPayload = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
|
|
153
|
+
console.log(`\u{1F50D} [SDK DEBUG] Generated Signed x402 Base64 Header (Length: ${encodedPayload.length})`);
|
|
154
|
+
const retryHeaders = {
|
|
155
|
+
"Content-Type": "application/json",
|
|
156
|
+
"PAYMENT-SIGNATURE": encodedPayload,
|
|
157
|
+
"X-PAYMENT-RESPONSE": encodedPayload,
|
|
158
|
+
"X-PAYMENT": encodedPayload,
|
|
159
|
+
"authorization": `Bearer ${encodedPayload}`
|
|
160
|
+
};
|
|
161
|
+
const retryOptions = {
|
|
162
|
+
method,
|
|
163
|
+
headers: retryHeaders
|
|
164
|
+
};
|
|
165
|
+
if (method === "POST" && payload) {
|
|
166
|
+
retryOptions.body = JSON.stringify(payload);
|
|
167
|
+
}
|
|
168
|
+
console.log(`\u{1F50D} [SDK DEBUG] Resubmitting paid ${method} request to: ${url}`);
|
|
169
|
+
response = await fetch(url, retryOptions);
|
|
170
|
+
console.log(`\u{1F50D} [SDK DEBUG] Paid Retry Response Status: ${response.status} ${response.statusText}`);
|
|
186
171
|
}
|
|
187
|
-
|
|
188
|
-
|
|
172
|
+
if (!response.ok) {
|
|
173
|
+
const errText = await response.text();
|
|
174
|
+
console.error(`\u274C [SDK DEBUG] Request Rejection Body:`, errText);
|
|
175
|
+
throw new Error(`WarpPay API Error (${response.status}): ${errText}`);
|
|
189
176
|
}
|
|
190
|
-
|
|
191
|
-
|
|
177
|
+
return await response.json();
|
|
178
|
+
}
|
|
179
|
+
async scrapeWeb(url) {
|
|
180
|
+
return this.executePaidRequest("/api/v1/tools/web-scraper", { url });
|
|
181
|
+
}
|
|
182
|
+
async getBaseAnalytics(address) {
|
|
183
|
+
return this.executePaidRequest("/api/v1/tools/base-analytics", { address });
|
|
184
|
+
}
|
|
185
|
+
async extractPdf(pdfUrl) {
|
|
186
|
+
return this.executePaidRequest("/api/v1/tools/pdf-extractor", { pdfUrl });
|
|
187
|
+
}
|
|
188
|
+
async browserScrape(url) {
|
|
189
|
+
return this.executePaidRequest("/api/v1/tools/browser-scraper", { url });
|
|
190
|
+
}
|
|
191
|
+
async renderScreenshot(url) {
|
|
192
|
+
return this.executePaidRequest("/api/v1/tools/render-screenshot", { url });
|
|
193
|
+
}
|
|
194
|
+
async extractJson(url, schema) {
|
|
195
|
+
return this.executePaidRequest("/api/v1/tools/extract-json", { url, schema });
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
export {
|
|
199
|
+
WarpPayClient
|
|
200
|
+
};
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
|
|
2
1
|
{
|
|
3
2
|
"name": "@warppay402/sdk",
|
|
4
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
5
4
|
"license": "MIT",
|
|
6
5
|
"description": "Official WarpPay402 TypeScript SDK: Enabling seamless pay-per-use AI tools across Base Mainnet, Solana, and Arbitrum One.",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
9
17
|
"scripts": {
|
|
10
|
-
"build": "
|
|
18
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean --external viem,@solana/web3.js,@solana/spl-token",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
11
20
|
},
|
|
12
21
|
"repository": {
|
|
13
22
|
"type": "git",
|
|
@@ -36,6 +45,7 @@
|
|
|
36
45
|
},
|
|
37
46
|
"devDependencies": {
|
|
38
47
|
"@types/node": "^26.2.0",
|
|
48
|
+
"tsup": "^8.5.1",
|
|
39
49
|
"typescript": "^5.0.0"
|
|
40
50
|
},
|
|
41
51
|
"openclaw": {
|
|
@@ -59,4 +69,4 @@
|
|
|
59
69
|
}
|
|
60
70
|
}
|
|
61
71
|
}
|
|
62
|
-
}
|
|
72
|
+
}
|
package/dist/langchain.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { WarpPayClient } from "./index.js";
|
|
2
|
-
/**
|
|
3
|
-
* Generates LangChain-compatible tool definitions initialized with WarpPay402
|
|
4
|
-
*/
|
|
5
|
-
export declare function createWarpPayLangChainTools(client: WarpPayClient): ({
|
|
6
|
-
name: string;
|
|
7
|
-
description: string;
|
|
8
|
-
func: ({ url }: {
|
|
9
|
-
url: string;
|
|
10
|
-
}) => Promise<any>;
|
|
11
|
-
} | {
|
|
12
|
-
name: string;
|
|
13
|
-
description: string;
|
|
14
|
-
func: ({ address }: {
|
|
15
|
-
address: string;
|
|
16
|
-
}) => Promise<string>;
|
|
17
|
-
} | {
|
|
18
|
-
name: string;
|
|
19
|
-
description: string;
|
|
20
|
-
func: ({ pdfUrl }: {
|
|
21
|
-
pdfUrl: string;
|
|
22
|
-
}) => Promise<any>;
|
|
23
|
-
})[];
|
package/dist/langchain.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createWarpPayLangChainTools = createWarpPayLangChainTools;
|
|
4
|
-
/**
|
|
5
|
-
* Generates LangChain-compatible tool definitions initialized with WarpPay402
|
|
6
|
-
*/
|
|
7
|
-
function createWarpPayLangChainTools(client) {
|
|
8
|
-
return [
|
|
9
|
-
{
|
|
10
|
-
name: "web_scraper",
|
|
11
|
-
description: "Scrapes a web page URL and returns clean markdown content. Costs $0.01 USDC on Base.",
|
|
12
|
-
func: async ({ url }) => {
|
|
13
|
-
const result = await client.scrapeWeb(url);
|
|
14
|
-
return result.markdown;
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
name: "base_analytics",
|
|
19
|
-
description: "Fetches ETH balance and nonce for a Base 0x wallet address. Costs $0.02 USDC on Base.",
|
|
20
|
-
func: async ({ address }) => {
|
|
21
|
-
const result = await client.getBaseAnalytics(address);
|
|
22
|
-
return JSON.stringify(result);
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
name: "pdf_extractor",
|
|
27
|
-
description: "Extracts text preview from a public PDF URL. Costs $0.05 USDC on Base.",
|
|
28
|
-
func: async ({ pdfUrl }) => {
|
|
29
|
-
const result = await client.extractPdf(pdfUrl);
|
|
30
|
-
return result.textPreview;
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
];
|
|
34
|
-
}
|