uvd-x402-sdk 2.34.0 → 2.36.0

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
@@ -12,6 +12,7 @@ Users sign a message or transaction, and the Ultravioleta facilitator handles on
12
12
  - **Gasless**: Facilitator pays all network fees
13
13
  - **Type-Safe**: Full TypeScript support
14
14
  - **React & Wagmi**: First-class integrations
15
+ - **Signing Wallet Adapters**: EnvKeyAdapter (server/CLI), OWSWalletAdapter (Open Wallet Standard), or bring your own
15
16
  - **ERC-8004 Trustless Agents**: On-chain reputation and identity (EVM + Solana)
16
17
  - **Escrow & Refunds**: Hold payments with dispute resolution
17
18
  - **`/accepts` Negotiation**: Discover facilitator capabilities before constructing payments
@@ -476,6 +477,80 @@ function PayButton() {
476
477
  }
477
478
  ```
478
479
 
480
+ ## Signing Wallet Adapters
481
+
482
+ Low-level signing primitives for server-side agents, CLI tools, and Open Wallet Standard wallets. These adapters implement EIP-191, EIP-712, and EIP-3009 (gasless USDC transfers).
483
+
484
+ ### EnvKeyAdapter (Server / CLI / Agents)
485
+
486
+ Signs with a raw private key from the environment or constructor. **Never use in browser contexts.**
487
+
488
+ ```typescript
489
+ import { EnvKeyAdapter } from 'uvd-x402-sdk';
490
+
491
+ // Option 1: Reads process.env.WALLET_PRIVATE_KEY
492
+ const wallet = new EnvKeyAdapter();
493
+
494
+ // Option 2: Explicit key
495
+ const wallet = new EnvKeyAdapter(process.env.MY_AGENT_KEY!);
496
+
497
+ console.log(wallet.getAddress()); // 0x...
498
+
499
+ // Sign EIP-3009 gasless USDC transfer
500
+ const auth = await wallet.signEIP3009({
501
+ to: '0xRecipient...',
502
+ amountUsdc: 1.00,
503
+ network: 'base',
504
+ });
505
+ // auth contains: from, to, value, nonce, v, r, s, signature
506
+
507
+ // Sign arbitrary message (EIP-191)
508
+ const sig = await wallet.signMessage('Hello x402');
509
+
510
+ // Sign EIP-712 typed data
511
+ const result = await wallet.signTypedData(JSON.stringify({
512
+ domain: { name: 'MyApp', version: '1', chainId: 8453 },
513
+ types: { Order: [{ name: 'id', type: 'uint256' }] },
514
+ primaryType: 'Order',
515
+ message: { id: 42 },
516
+ }));
517
+ ```
518
+
519
+ ### OWSWalletAdapter (Open Wallet Standard)
520
+
521
+ Delegates signing to any wallet that implements the [Open Wallet Standard](https://github.com/open-wallet-standard/open-wallet-standard). Works with browser wallets, agent vaults, and hardware-backed signers.
522
+
523
+ ```bash
524
+ npm install @open-wallet-standard/core # optional peer dependency
525
+ ```
526
+
527
+ ```typescript
528
+ import { OWSWalletAdapter } from 'uvd-x402-sdk';
529
+
530
+ const wallet = new OWSWalletAdapter(owsWalletInstance);
531
+
532
+ const auth = await wallet.signEIP3009({
533
+ to: '0xRecipient...',
534
+ amountUsdc: 0.50,
535
+ network: 'base',
536
+ });
537
+ ```
538
+
539
+ ### Custom Adapter
540
+
541
+ Implement the `SigningWalletAdapter` interface for your own signer:
542
+
543
+ ```typescript
544
+ import type { SigningWalletAdapter, EIP3009Params, EIP3009Authorization } from 'uvd-x402-sdk';
545
+
546
+ class MyAdapter implements SigningWalletAdapter {
547
+ getAddress(): string { /* ... */ }
548
+ async signMessage(message: string): Promise<string> { /* ... */ }
549
+ async signTypedData(typedData: string): Promise<{ signature: string; v: number; r: string; s: string }> { /* ... */ }
550
+ async signEIP3009(params: EIP3009Params): Promise<EIP3009Authorization> { /* ... */ }
551
+ }
552
+ ```
553
+
479
554
  ## Multi-Stablecoin (EVM)
480
555
 
481
556
  ```typescript
@@ -1,4 +1,6 @@
1
1
  import { L as X402Version, k as PaymentResult } from '../index-MNVg3WMv.mjs';
2
+ export { E as EnvKeyAdapter, O as OWSWallet, a as OWSWalletAdapter } from '../ows-CYIVd4xO.mjs';
3
+ import '../wallet-0cX9Pw2F.mjs';
2
4
 
3
5
  /**
4
6
  * uvd-x402-sdk - Wagmi/Viem Adapter
@@ -1,4 +1,6 @@
1
1
  import { L as X402Version, k as PaymentResult } from '../index-MNVg3WMv.js';
2
+ export { E as EnvKeyAdapter, O as OWSWallet, a as OWSWalletAdapter } from '../ows-DTDixPzO.js';
3
+ import '../wallet-0cX9Pw2F.js';
2
4
 
3
5
  /**
4
6
  * uvd-x402-sdk - Wagmi/Viem Adapter
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var ethers = require('ethers');
4
+
3
5
  // src/chains/index.ts
4
6
  var DEFAULT_FACILITATOR_URL = "https://facilitator.ultravioletadao.xyz";
5
7
  var SUPPORTED_CHAINS = {
@@ -1077,7 +1079,344 @@ function useX402Wagmi(walletClient) {
1077
1079
  createPaymentFull
1078
1080
  };
1079
1081
  }
1082
+ var EnvKeyAdapter = class {
1083
+ wallet;
1084
+ /**
1085
+ * Create an EnvKeyAdapter.
1086
+ *
1087
+ * @param privateKey - Hex-encoded private key (with or without 0x prefix).
1088
+ * If omitted, reads from `process.env.WALLET_PRIVATE_KEY`.
1089
+ * @throws {X402Error} if no private key is available
1090
+ */
1091
+ constructor(privateKey) {
1092
+ const key = privateKey || (typeof process !== "undefined" ? process.env.WALLET_PRIVATE_KEY : void 0);
1093
+ if (!key) {
1094
+ throw new X402Error(
1095
+ "No private key provided. Pass it to the constructor or set WALLET_PRIVATE_KEY env var.",
1096
+ "WALLET_NOT_CONNECTED"
1097
+ );
1098
+ }
1099
+ this.wallet = new ethers.ethers.Wallet(key);
1100
+ }
1101
+ /**
1102
+ * Get the checksummed EVM wallet address.
1103
+ */
1104
+ getAddress() {
1105
+ return this.wallet.address;
1106
+ }
1107
+ /**
1108
+ * Sign a message using EIP-191 personal_sign.
1109
+ *
1110
+ * @param message - The message string to sign
1111
+ * @returns Hex-encoded signature
1112
+ */
1113
+ async signMessage(message) {
1114
+ return this.wallet.signMessage(message);
1115
+ }
1116
+ /**
1117
+ * Sign EIP-712 typed structured data.
1118
+ *
1119
+ * @param typedData - JSON string with `domain`, `types`, `primaryType`, and `message` fields
1120
+ * @returns Object with signature and v/r/s components
1121
+ */
1122
+ async signTypedData(typedData) {
1123
+ const parsed = JSON.parse(typedData);
1124
+ const { domain, types, message } = parsed;
1125
+ const cleanTypes = { ...types };
1126
+ delete cleanTypes["EIP712Domain"];
1127
+ const signature = await this.wallet.signTypedData(domain, cleanTypes, message);
1128
+ const sig = ethers.ethers.Signature.from(signature);
1129
+ return {
1130
+ signature,
1131
+ v: sig.v,
1132
+ r: sig.r,
1133
+ s: sig.s
1134
+ };
1135
+ }
1136
+ /**
1137
+ * Sign a serialized EVM transaction.
1138
+ *
1139
+ * Uses the raw private key via ethers.Wallet.signTransaction().
1140
+ * Used by AdvancedEscrowClient for on-chain operations (release,
1141
+ * refund, charge) when constructed with a SigningWalletAdapter.
1142
+ *
1143
+ * @param unsignedTx - Hex-encoded unsigned transaction (ethers serialized)
1144
+ * @returns Hex-encoded signed raw transaction, ready for broadcast
1145
+ */
1146
+ async signTransaction(unsignedTx) {
1147
+ const tx = ethers.ethers.Transaction.from(unsignedTx);
1148
+ return this.wallet.signTransaction(tx);
1149
+ }
1150
+ /**
1151
+ * Sign an EIP-3009 ReceiveWithAuthorization for USDC.
1152
+ *
1153
+ * @param params - EIP-3009 parameters
1154
+ * @returns Signed authorization ready for facilitator relay
1155
+ */
1156
+ async signEIP3009(params) {
1157
+ const chain = getChainByName(params.network);
1158
+ if (!chain) {
1159
+ throw new X402Error(`Unsupported network: ${params.network}`, "CHAIN_NOT_SUPPORTED");
1160
+ }
1161
+ if (chain.networkType !== "evm") {
1162
+ throw new X402Error(
1163
+ `EIP-3009 is only supported on EVM chains. ${params.network} is ${chain.networkType}.`,
1164
+ "CHAIN_NOT_SUPPORTED"
1165
+ );
1166
+ }
1167
+ const chainId = params.chainId ?? chain.chainId;
1168
+ const usdcAddress = params.usdcContract ?? chain.usdc.address;
1169
+ const from = this.wallet.address;
1170
+ const to = ethers.ethers.getAddress(params.to);
1171
+ const value = ethers.ethers.parseUnits(params.amountUsdc.toString(), chain.usdc.decimals);
1172
+ const validAfter = params.validAfter ?? 0;
1173
+ const validBefore = params.validBefore ?? Math.floor(Date.now() / 1e3) + 300;
1174
+ const nonce = ethers.ethers.hexlify(ethers.ethers.randomBytes(32));
1175
+ const domain = {
1176
+ name: chain.usdc.name,
1177
+ version: chain.usdc.version,
1178
+ chainId,
1179
+ verifyingContract: usdcAddress
1180
+ };
1181
+ const types = {
1182
+ TransferWithAuthorization: [
1183
+ { name: "from", type: "address" },
1184
+ { name: "to", type: "address" },
1185
+ { name: "value", type: "uint256" },
1186
+ { name: "validAfter", type: "uint256" },
1187
+ { name: "validBefore", type: "uint256" },
1188
+ { name: "nonce", type: "bytes32" }
1189
+ ]
1190
+ };
1191
+ const message = {
1192
+ from,
1193
+ to,
1194
+ value,
1195
+ validAfter,
1196
+ validBefore,
1197
+ nonce
1198
+ };
1199
+ let signature;
1200
+ try {
1201
+ signature = await this.wallet.signTypedData(domain, types, message);
1202
+ } catch (error) {
1203
+ throw new X402Error(
1204
+ `Failed to sign EIP-3009 authorization: ${error instanceof Error ? error.message : "Unknown error"}`,
1205
+ "PAYMENT_FAILED",
1206
+ error
1207
+ );
1208
+ }
1209
+ const sig = ethers.ethers.Signature.from(signature);
1210
+ return {
1211
+ from,
1212
+ to,
1213
+ value: value.toString(),
1214
+ validAfter: validAfter.toString(),
1215
+ validBefore: validBefore.toString(),
1216
+ nonce,
1217
+ v: sig.v,
1218
+ r: sig.r,
1219
+ s: sig.s,
1220
+ signature
1221
+ };
1222
+ }
1223
+ };
1224
+ var OWSWalletAdapter = class {
1225
+ owsWallet;
1226
+ address;
1227
+ /**
1228
+ * Create an OWSWalletAdapter.
1229
+ *
1230
+ * @param owsWallet - An object implementing the OWSWallet interface
1231
+ * (typically from @open-wallet-standard/core or a compatible provider)
1232
+ * @param accountIndex - Which account to use if the wallet has multiple (default: 0)
1233
+ * @throws {X402Error} if the wallet has no accounts
1234
+ */
1235
+ constructor(owsWallet, accountIndex = 0) {
1236
+ if (!owsWallet || !owsWallet.accounts || owsWallet.accounts.length === 0) {
1237
+ throw new X402Error(
1238
+ "OWS wallet has no accounts. Create or import a wallet first.",
1239
+ "WALLET_NOT_CONNECTED"
1240
+ );
1241
+ }
1242
+ const account = owsWallet.accounts[accountIndex];
1243
+ if (!account) {
1244
+ throw new X402Error(
1245
+ `OWS wallet account index ${accountIndex} does not exist. Wallet has ${owsWallet.accounts.length} account(s).`,
1246
+ "WALLET_NOT_CONNECTED"
1247
+ );
1248
+ }
1249
+ this.owsWallet = owsWallet;
1250
+ this.address = ethers.ethers.getAddress(account.address);
1251
+ }
1252
+ /**
1253
+ * Get the checksummed EVM wallet address.
1254
+ */
1255
+ getAddress() {
1256
+ return this.address;
1257
+ }
1258
+ /**
1259
+ * Sign a message using EIP-191 personal_sign.
1260
+ *
1261
+ * @param message - The message string to sign
1262
+ * @returns Hex-encoded signature
1263
+ */
1264
+ async signMessage(message) {
1265
+ try {
1266
+ const result = await this.owsWallet.signMessage({
1267
+ account: { address: this.address },
1268
+ message
1269
+ });
1270
+ return result.signature;
1271
+ } catch (error) {
1272
+ throw new X402Error(
1273
+ `OWS signMessage failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1274
+ "PAYMENT_FAILED",
1275
+ error
1276
+ );
1277
+ }
1278
+ }
1279
+ /**
1280
+ * Sign EIP-712 typed structured data.
1281
+ *
1282
+ * @param typedData - JSON string with `domain`, `types`, `primaryType`, and `message` fields
1283
+ * @returns Object with signature and v/r/s components
1284
+ */
1285
+ async signTypedData(typedData) {
1286
+ const parsed = JSON.parse(typedData);
1287
+ try {
1288
+ const result = await this.owsWallet.signTypedData({
1289
+ account: { address: this.address },
1290
+ domain: parsed.domain,
1291
+ types: parsed.types,
1292
+ primaryType: parsed.primaryType,
1293
+ message: parsed.message
1294
+ });
1295
+ const sig = ethers.ethers.Signature.from(result.signature);
1296
+ return {
1297
+ signature: result.signature,
1298
+ v: sig.v,
1299
+ r: sig.r,
1300
+ s: sig.s
1301
+ };
1302
+ } catch (error) {
1303
+ throw new X402Error(
1304
+ `OWS signTypedData failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1305
+ "PAYMENT_FAILED",
1306
+ error
1307
+ );
1308
+ }
1309
+ }
1310
+ /**
1311
+ * Sign a serialized EVM transaction.
1312
+ *
1313
+ * Delegates to the OWS wallet's signTransaction method.
1314
+ * Used by AdvancedEscrowClient for on-chain operations (release,
1315
+ * refund, charge) where the wallet signs instead of a raw private key.
1316
+ *
1317
+ * @param unsignedTx - Hex-encoded unsigned transaction
1318
+ * @returns Hex-encoded signed raw transaction
1319
+ */
1320
+ async signTransaction(unsignedTx) {
1321
+ try {
1322
+ const result = await this.owsWallet.signTransaction({
1323
+ account: { address: this.address },
1324
+ transaction: unsignedTx,
1325
+ chainId: "1"
1326
+ // chainId is embedded in the serialized TX; this is a hint
1327
+ });
1328
+ return result.signedTransaction;
1329
+ } catch (error) {
1330
+ throw new X402Error(
1331
+ `OWS signTransaction failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1332
+ "PAYMENT_FAILED",
1333
+ error
1334
+ );
1335
+ }
1336
+ }
1337
+ /**
1338
+ * Sign an EIP-3009 ReceiveWithAuthorization for USDC.
1339
+ *
1340
+ * @param params - EIP-3009 parameters
1341
+ * @returns Signed authorization ready for facilitator relay
1342
+ */
1343
+ async signEIP3009(params) {
1344
+ const chain = getChainByName(params.network);
1345
+ if (!chain) {
1346
+ throw new X402Error(`Unsupported network: ${params.network}`, "CHAIN_NOT_SUPPORTED");
1347
+ }
1348
+ if (chain.networkType !== "evm") {
1349
+ throw new X402Error(
1350
+ `EIP-3009 is only supported on EVM chains. ${params.network} is ${chain.networkType}.`,
1351
+ "CHAIN_NOT_SUPPORTED"
1352
+ );
1353
+ }
1354
+ const chainId = params.chainId ?? chain.chainId;
1355
+ const usdcAddress = params.usdcContract ?? chain.usdc.address;
1356
+ const from = this.address;
1357
+ const to = ethers.ethers.getAddress(params.to);
1358
+ const value = ethers.ethers.parseUnits(params.amountUsdc.toString(), chain.usdc.decimals);
1359
+ const validAfter = params.validAfter ?? 0;
1360
+ const validBefore = params.validBefore ?? Math.floor(Date.now() / 1e3) + 300;
1361
+ const nonce = ethers.ethers.hexlify(ethers.ethers.randomBytes(32));
1362
+ const domain = {
1363
+ name: chain.usdc.name,
1364
+ version: chain.usdc.version,
1365
+ chainId,
1366
+ verifyingContract: usdcAddress
1367
+ };
1368
+ const types = {
1369
+ TransferWithAuthorization: [
1370
+ { name: "from", type: "address" },
1371
+ { name: "to", type: "address" },
1372
+ { name: "value", type: "uint256" },
1373
+ { name: "validAfter", type: "uint256" },
1374
+ { name: "validBefore", type: "uint256" },
1375
+ { name: "nonce", type: "bytes32" }
1376
+ ]
1377
+ };
1378
+ const message = {
1379
+ from,
1380
+ to,
1381
+ value: value.toString(),
1382
+ validAfter: validAfter.toString(),
1383
+ validBefore: validBefore.toString(),
1384
+ nonce
1385
+ };
1386
+ let signatureResult;
1387
+ try {
1388
+ signatureResult = await this.owsWallet.signTypedData({
1389
+ account: { address: this.address },
1390
+ domain,
1391
+ types,
1392
+ primaryType: "TransferWithAuthorization",
1393
+ message
1394
+ });
1395
+ } catch (error) {
1396
+ throw new X402Error(
1397
+ `OWS signEIP3009 failed: ${error instanceof Error ? error.message : "Unknown error"}`,
1398
+ "PAYMENT_FAILED",
1399
+ error
1400
+ );
1401
+ }
1402
+ const sig = ethers.ethers.Signature.from(signatureResult.signature);
1403
+ return {
1404
+ from,
1405
+ to,
1406
+ value: value.toString(),
1407
+ validAfter: validAfter.toString(),
1408
+ validBefore: validBefore.toString(),
1409
+ nonce,
1410
+ v: sig.v,
1411
+ r: sig.r,
1412
+ s: sig.s,
1413
+ signature: signatureResult.signature
1414
+ };
1415
+ }
1416
+ };
1080
1417
 
1418
+ exports.EnvKeyAdapter = EnvKeyAdapter;
1419
+ exports.OWSWalletAdapter = OWSWalletAdapter;
1081
1420
  exports.createPaymentFromWalletClient = createPaymentFromWalletClient;
1082
1421
  exports.createPaymentWithResult = createPaymentWithResult;
1083
1422
  exports.useX402Wagmi = useX402Wagmi;