uvd-x402-sdk 2.34.0 → 2.35.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 +75 -0
- package/dist/adapters/index.d.mts +1 -0
- package/dist/adapters/index.d.ts +1 -0
- package/dist/adapters/index.js +298 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/index.mjs +297 -1
- package/dist/adapters/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +296 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +295 -1
- package/dist/index.mjs.map +1 -1
- package/dist/ows-BkQHp_45.d.mts +310 -0
- package/dist/ows-BkQHp_45.d.ts +310 -0
- package/package.json +5 -1
- package/src/adapters/env-key.ts +208 -0
- package/src/adapters/index.ts +7 -1
- package/src/adapters/ows.ts +276 -0
- package/src/index.ts +6 -0
- package/src/wallet.ts +136 -0
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
|
package/dist/adapters/index.d.ts
CHANGED
package/dist/adapters/index.js
CHANGED
|
@@ -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,303 @@ 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 an EIP-3009 ReceiveWithAuthorization for USDC.
|
|
1138
|
+
*
|
|
1139
|
+
* @param params - EIP-3009 parameters
|
|
1140
|
+
* @returns Signed authorization ready for facilitator relay
|
|
1141
|
+
*/
|
|
1142
|
+
async signEIP3009(params) {
|
|
1143
|
+
const chain = getChainByName(params.network);
|
|
1144
|
+
if (!chain) {
|
|
1145
|
+
throw new X402Error(`Unsupported network: ${params.network}`, "CHAIN_NOT_SUPPORTED");
|
|
1146
|
+
}
|
|
1147
|
+
if (chain.networkType !== "evm") {
|
|
1148
|
+
throw new X402Error(
|
|
1149
|
+
`EIP-3009 is only supported on EVM chains. ${params.network} is ${chain.networkType}.`,
|
|
1150
|
+
"CHAIN_NOT_SUPPORTED"
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
const chainId = params.chainId ?? chain.chainId;
|
|
1154
|
+
const usdcAddress = params.usdcContract ?? chain.usdc.address;
|
|
1155
|
+
const from = this.wallet.address;
|
|
1156
|
+
const to = ethers.ethers.getAddress(params.to);
|
|
1157
|
+
const value = ethers.ethers.parseUnits(params.amountUsdc.toString(), chain.usdc.decimals);
|
|
1158
|
+
const validAfter = params.validAfter ?? 0;
|
|
1159
|
+
const validBefore = params.validBefore ?? Math.floor(Date.now() / 1e3) + 300;
|
|
1160
|
+
const nonce = ethers.ethers.hexlify(ethers.ethers.randomBytes(32));
|
|
1161
|
+
const domain = {
|
|
1162
|
+
name: chain.usdc.name,
|
|
1163
|
+
version: chain.usdc.version,
|
|
1164
|
+
chainId,
|
|
1165
|
+
verifyingContract: usdcAddress
|
|
1166
|
+
};
|
|
1167
|
+
const types = {
|
|
1168
|
+
TransferWithAuthorization: [
|
|
1169
|
+
{ name: "from", type: "address" },
|
|
1170
|
+
{ name: "to", type: "address" },
|
|
1171
|
+
{ name: "value", type: "uint256" },
|
|
1172
|
+
{ name: "validAfter", type: "uint256" },
|
|
1173
|
+
{ name: "validBefore", type: "uint256" },
|
|
1174
|
+
{ name: "nonce", type: "bytes32" }
|
|
1175
|
+
]
|
|
1176
|
+
};
|
|
1177
|
+
const message = {
|
|
1178
|
+
from,
|
|
1179
|
+
to,
|
|
1180
|
+
value,
|
|
1181
|
+
validAfter,
|
|
1182
|
+
validBefore,
|
|
1183
|
+
nonce
|
|
1184
|
+
};
|
|
1185
|
+
let signature;
|
|
1186
|
+
try {
|
|
1187
|
+
signature = await this.wallet.signTypedData(domain, types, message);
|
|
1188
|
+
} catch (error) {
|
|
1189
|
+
throw new X402Error(
|
|
1190
|
+
`Failed to sign EIP-3009 authorization: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1191
|
+
"PAYMENT_FAILED",
|
|
1192
|
+
error
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
const sig = ethers.ethers.Signature.from(signature);
|
|
1196
|
+
return {
|
|
1197
|
+
from,
|
|
1198
|
+
to,
|
|
1199
|
+
value: value.toString(),
|
|
1200
|
+
validAfter: validAfter.toString(),
|
|
1201
|
+
validBefore: validBefore.toString(),
|
|
1202
|
+
nonce,
|
|
1203
|
+
v: sig.v,
|
|
1204
|
+
r: sig.r,
|
|
1205
|
+
s: sig.s,
|
|
1206
|
+
signature
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
var OWSWalletAdapter = class {
|
|
1211
|
+
owsWallet;
|
|
1212
|
+
address;
|
|
1213
|
+
/**
|
|
1214
|
+
* Create an OWSWalletAdapter.
|
|
1215
|
+
*
|
|
1216
|
+
* @param owsWallet - An object implementing the OWSWallet interface
|
|
1217
|
+
* (typically from @open-wallet-standard/core or a compatible provider)
|
|
1218
|
+
* @param accountIndex - Which account to use if the wallet has multiple (default: 0)
|
|
1219
|
+
* @throws {X402Error} if the wallet has no accounts
|
|
1220
|
+
*/
|
|
1221
|
+
constructor(owsWallet, accountIndex = 0) {
|
|
1222
|
+
if (!owsWallet || !owsWallet.accounts || owsWallet.accounts.length === 0) {
|
|
1223
|
+
throw new X402Error(
|
|
1224
|
+
"OWS wallet has no accounts. Create or import a wallet first.",
|
|
1225
|
+
"WALLET_NOT_CONNECTED"
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
const account = owsWallet.accounts[accountIndex];
|
|
1229
|
+
if (!account) {
|
|
1230
|
+
throw new X402Error(
|
|
1231
|
+
`OWS wallet account index ${accountIndex} does not exist. Wallet has ${owsWallet.accounts.length} account(s).`,
|
|
1232
|
+
"WALLET_NOT_CONNECTED"
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
this.owsWallet = owsWallet;
|
|
1236
|
+
this.address = ethers.ethers.getAddress(account.address);
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Get the checksummed EVM wallet address.
|
|
1240
|
+
*/
|
|
1241
|
+
getAddress() {
|
|
1242
|
+
return this.address;
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Sign a message using EIP-191 personal_sign.
|
|
1246
|
+
*
|
|
1247
|
+
* @param message - The message string to sign
|
|
1248
|
+
* @returns Hex-encoded signature
|
|
1249
|
+
*/
|
|
1250
|
+
async signMessage(message) {
|
|
1251
|
+
try {
|
|
1252
|
+
const result = await this.owsWallet.signMessage({
|
|
1253
|
+
account: { address: this.address },
|
|
1254
|
+
message
|
|
1255
|
+
});
|
|
1256
|
+
return result.signature;
|
|
1257
|
+
} catch (error) {
|
|
1258
|
+
throw new X402Error(
|
|
1259
|
+
`OWS signMessage failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1260
|
+
"PAYMENT_FAILED",
|
|
1261
|
+
error
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Sign EIP-712 typed structured data.
|
|
1267
|
+
*
|
|
1268
|
+
* @param typedData - JSON string with `domain`, `types`, `primaryType`, and `message` fields
|
|
1269
|
+
* @returns Object with signature and v/r/s components
|
|
1270
|
+
*/
|
|
1271
|
+
async signTypedData(typedData) {
|
|
1272
|
+
const parsed = JSON.parse(typedData);
|
|
1273
|
+
try {
|
|
1274
|
+
const result = await this.owsWallet.signTypedData({
|
|
1275
|
+
account: { address: this.address },
|
|
1276
|
+
domain: parsed.domain,
|
|
1277
|
+
types: parsed.types,
|
|
1278
|
+
primaryType: parsed.primaryType,
|
|
1279
|
+
message: parsed.message
|
|
1280
|
+
});
|
|
1281
|
+
const sig = ethers.ethers.Signature.from(result.signature);
|
|
1282
|
+
return {
|
|
1283
|
+
signature: result.signature,
|
|
1284
|
+
v: sig.v,
|
|
1285
|
+
r: sig.r,
|
|
1286
|
+
s: sig.s
|
|
1287
|
+
};
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
throw new X402Error(
|
|
1290
|
+
`OWS signTypedData failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1291
|
+
"PAYMENT_FAILED",
|
|
1292
|
+
error
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Sign an EIP-3009 ReceiveWithAuthorization for USDC.
|
|
1298
|
+
*
|
|
1299
|
+
* @param params - EIP-3009 parameters
|
|
1300
|
+
* @returns Signed authorization ready for facilitator relay
|
|
1301
|
+
*/
|
|
1302
|
+
async signEIP3009(params) {
|
|
1303
|
+
const chain = getChainByName(params.network);
|
|
1304
|
+
if (!chain) {
|
|
1305
|
+
throw new X402Error(`Unsupported network: ${params.network}`, "CHAIN_NOT_SUPPORTED");
|
|
1306
|
+
}
|
|
1307
|
+
if (chain.networkType !== "evm") {
|
|
1308
|
+
throw new X402Error(
|
|
1309
|
+
`EIP-3009 is only supported on EVM chains. ${params.network} is ${chain.networkType}.`,
|
|
1310
|
+
"CHAIN_NOT_SUPPORTED"
|
|
1311
|
+
);
|
|
1312
|
+
}
|
|
1313
|
+
const chainId = params.chainId ?? chain.chainId;
|
|
1314
|
+
const usdcAddress = params.usdcContract ?? chain.usdc.address;
|
|
1315
|
+
const from = this.address;
|
|
1316
|
+
const to = ethers.ethers.getAddress(params.to);
|
|
1317
|
+
const value = ethers.ethers.parseUnits(params.amountUsdc.toString(), chain.usdc.decimals);
|
|
1318
|
+
const validAfter = params.validAfter ?? 0;
|
|
1319
|
+
const validBefore = params.validBefore ?? Math.floor(Date.now() / 1e3) + 300;
|
|
1320
|
+
const nonce = ethers.ethers.hexlify(ethers.ethers.randomBytes(32));
|
|
1321
|
+
const domain = {
|
|
1322
|
+
name: chain.usdc.name,
|
|
1323
|
+
version: chain.usdc.version,
|
|
1324
|
+
chainId,
|
|
1325
|
+
verifyingContract: usdcAddress
|
|
1326
|
+
};
|
|
1327
|
+
const types = {
|
|
1328
|
+
TransferWithAuthorization: [
|
|
1329
|
+
{ name: "from", type: "address" },
|
|
1330
|
+
{ name: "to", type: "address" },
|
|
1331
|
+
{ name: "value", type: "uint256" },
|
|
1332
|
+
{ name: "validAfter", type: "uint256" },
|
|
1333
|
+
{ name: "validBefore", type: "uint256" },
|
|
1334
|
+
{ name: "nonce", type: "bytes32" }
|
|
1335
|
+
]
|
|
1336
|
+
};
|
|
1337
|
+
const message = {
|
|
1338
|
+
from,
|
|
1339
|
+
to,
|
|
1340
|
+
value: value.toString(),
|
|
1341
|
+
validAfter: validAfter.toString(),
|
|
1342
|
+
validBefore: validBefore.toString(),
|
|
1343
|
+
nonce
|
|
1344
|
+
};
|
|
1345
|
+
let signatureResult;
|
|
1346
|
+
try {
|
|
1347
|
+
signatureResult = await this.owsWallet.signTypedData({
|
|
1348
|
+
account: { address: this.address },
|
|
1349
|
+
domain,
|
|
1350
|
+
types,
|
|
1351
|
+
primaryType: "TransferWithAuthorization",
|
|
1352
|
+
message
|
|
1353
|
+
});
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
throw new X402Error(
|
|
1356
|
+
`OWS signEIP3009 failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
1357
|
+
"PAYMENT_FAILED",
|
|
1358
|
+
error
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
const sig = ethers.ethers.Signature.from(signatureResult.signature);
|
|
1362
|
+
return {
|
|
1363
|
+
from,
|
|
1364
|
+
to,
|
|
1365
|
+
value: value.toString(),
|
|
1366
|
+
validAfter: validAfter.toString(),
|
|
1367
|
+
validBefore: validBefore.toString(),
|
|
1368
|
+
nonce,
|
|
1369
|
+
v: sig.v,
|
|
1370
|
+
r: sig.r,
|
|
1371
|
+
s: sig.s,
|
|
1372
|
+
signature: signatureResult.signature
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1080
1376
|
|
|
1377
|
+
exports.EnvKeyAdapter = EnvKeyAdapter;
|
|
1378
|
+
exports.OWSWalletAdapter = OWSWalletAdapter;
|
|
1081
1379
|
exports.createPaymentFromWalletClient = createPaymentFromWalletClient;
|
|
1082
1380
|
exports.createPaymentWithResult = createPaymentWithResult;
|
|
1083
1381
|
exports.useX402Wagmi = useX402Wagmi;
|