@x402/extensions 2.5.0 → 2.7.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/dist/cjs/bazaar/index.d.ts +1 -1
- package/dist/cjs/{index-G8RNfr6X.d.ts → index-CtOzXcjN.d.ts} +2 -2
- package/dist/cjs/index.d.ts +33 -23
- package/dist/cjs/index.js +874 -10
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/offer-receipt/index.d.ts +702 -0
- package/dist/cjs/offer-receipt/index.js +909 -0
- package/dist/cjs/offer-receipt/index.js.map +1 -0
- package/dist/cjs/sign-in-with-x/index.js +1 -2
- package/dist/cjs/sign-in-with-x/index.js.map +1 -1
- package/dist/esm/bazaar/index.d.mts +1 -1
- package/dist/esm/{chunk-O34SGKEP.mjs → chunk-QVNCC7CH.mjs} +2 -3
- package/dist/esm/chunk-QVNCC7CH.mjs.map +1 -0
- package/dist/esm/chunk-TYR4QHVX.mjs +828 -0
- package/dist/esm/chunk-TYR4QHVX.mjs.map +1 -0
- package/dist/esm/{index-G8RNfr6X.d.mts → index-CtOzXcjN.d.mts} +2 -2
- package/dist/esm/index.d.mts +33 -23
- package/dist/esm/index.mjs +97 -9
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/offer-receipt/index.d.mts +702 -0
- package/dist/esm/offer-receipt/index.mjs +97 -0
- package/dist/esm/offer-receipt/index.mjs.map +1 -0
- package/dist/esm/sign-in-with-x/index.mjs +1 -1
- package/package.json +14 -2
- package/dist/esm/chunk-O34SGKEP.mjs.map +0 -1
package/dist/cjs/index.js
CHANGED
|
@@ -35,10 +35,13 @@ __export(src_exports, {
|
|
|
35
35
|
ERC20_APPROVAL_GAS_SPONSORING: () => ERC20_APPROVAL_GAS_SPONSORING,
|
|
36
36
|
ERC20_APPROVAL_GAS_SPONSORING_VERSION: () => ERC20_APPROVAL_GAS_SPONSORING_VERSION,
|
|
37
37
|
InMemorySIWxStorage: () => InMemorySIWxStorage,
|
|
38
|
+
OFFER_RECEIPT: () => OFFER_RECEIPT,
|
|
39
|
+
OFFER_TYPES: () => OFFER_TYPES,
|
|
38
40
|
PAYMENT_IDENTIFIER: () => PAYMENT_IDENTIFIER,
|
|
39
41
|
PAYMENT_ID_MAX_LENGTH: () => PAYMENT_ID_MAX_LENGTH,
|
|
40
42
|
PAYMENT_ID_MIN_LENGTH: () => PAYMENT_ID_MIN_LENGTH,
|
|
41
43
|
PAYMENT_ID_PATTERN: () => PAYMENT_ID_PATTERN,
|
|
44
|
+
RECEIPT_TYPES: () => RECEIPT_TYPES,
|
|
42
45
|
SIGN_IN_WITH_X: () => SIGN_IN_WITH_X,
|
|
43
46
|
SIWxPayloadSchema: () => SIWxPayloadSchema,
|
|
44
47
|
SOLANA_DEVNET: () => SOLANA_DEVNET,
|
|
@@ -47,7 +50,19 @@ __export(src_exports, {
|
|
|
47
50
|
appendPaymentIdentifierToExtensions: () => appendPaymentIdentifierToExtensions,
|
|
48
51
|
bazaarResourceServerExtension: () => bazaarResourceServerExtension,
|
|
49
52
|
buildSIWxSchema: () => buildSIWxSchema,
|
|
53
|
+
canonicalize: () => canonicalize,
|
|
54
|
+
convertNetworkStringToCAIP2: () => convertNetworkStringToCAIP2,
|
|
55
|
+
createEIP712OfferReceiptIssuer: () => createEIP712OfferReceiptIssuer,
|
|
50
56
|
createErc20ApprovalGasSponsoringExtension: () => createErc20ApprovalGasSponsoringExtension,
|
|
57
|
+
createJWS: () => createJWS,
|
|
58
|
+
createJWSOfferReceiptIssuer: () => createJWSOfferReceiptIssuer,
|
|
59
|
+
createOfferDomain: () => createOfferDomain,
|
|
60
|
+
createOfferEIP712: () => createOfferEIP712,
|
|
61
|
+
createOfferJWS: () => createOfferJWS,
|
|
62
|
+
createOfferReceiptExtension: () => createOfferReceiptExtension,
|
|
63
|
+
createReceiptDomain: () => createReceiptDomain,
|
|
64
|
+
createReceiptEIP712: () => createReceiptEIP712,
|
|
65
|
+
createReceiptJWS: () => createReceiptJWS,
|
|
51
66
|
createSIWxClientHook: () => createSIWxClientHook,
|
|
52
67
|
createSIWxMessage: () => createSIWxMessage,
|
|
53
68
|
createSIWxPayload: () => createSIWxPayload,
|
|
@@ -56,31 +71,53 @@ __export(src_exports, {
|
|
|
56
71
|
declareDiscoveryExtension: () => declareDiscoveryExtension,
|
|
57
72
|
declareEip2612GasSponsoringExtension: () => declareEip2612GasSponsoringExtension,
|
|
58
73
|
declareErc20ApprovalGasSponsoringExtension: () => declareErc20ApprovalGasSponsoringExtension,
|
|
74
|
+
declareOfferReceiptExtension: () => declareOfferReceiptExtension,
|
|
59
75
|
declarePaymentIdentifierExtension: () => declarePaymentIdentifierExtension,
|
|
60
76
|
declareSIWxExtension: () => declareSIWxExtension,
|
|
61
77
|
decodeBase58: () => decodeBase58,
|
|
78
|
+
decodeSignedOffers: () => decodeSignedOffers,
|
|
62
79
|
encodeBase58: () => encodeBase58,
|
|
63
80
|
encodeSIWxHeader: () => encodeSIWxHeader,
|
|
64
81
|
erc20ApprovalGasSponsoringSchema: () => erc20ApprovalGasSponsoringSchema,
|
|
65
82
|
extractAndValidatePaymentIdentifier: () => extractAndValidatePaymentIdentifier,
|
|
83
|
+
extractChainIdFromCAIP2: () => extractChainIdFromCAIP2,
|
|
66
84
|
extractDiscoveryInfo: () => extractDiscoveryInfo,
|
|
67
85
|
extractDiscoveryInfoFromExtension: () => extractDiscoveryInfoFromExtension,
|
|
68
86
|
extractDiscoveryInfoV1: () => extractDiscoveryInfoV1,
|
|
87
|
+
extractEIP155ChainId: () => extractEIP155ChainId,
|
|
69
88
|
extractEVMChainId: () => extractEVMChainId,
|
|
70
89
|
extractEip2612GasSponsoringInfo: () => extractEip2612GasSponsoringInfo,
|
|
71
90
|
extractErc20ApprovalGasSponsoringInfo: () => extractErc20ApprovalGasSponsoringInfo,
|
|
91
|
+
extractJWSHeader: () => extractJWSHeader,
|
|
92
|
+
extractJWSPayload: () => extractJWSPayload,
|
|
93
|
+
extractOfferPayload: () => extractOfferPayload,
|
|
94
|
+
extractOffersFromPaymentRequired: () => extractOffersFromPaymentRequired,
|
|
72
95
|
extractPaymentIdentifier: () => extractPaymentIdentifier,
|
|
96
|
+
extractPublicKeyFromKid: () => extractPublicKeyFromKid,
|
|
97
|
+
extractReceiptFromResponse: () => extractReceiptFromResponse,
|
|
98
|
+
extractReceiptPayload: () => extractReceiptPayload,
|
|
73
99
|
extractResourceMetadataV1: () => extractResourceMetadataV1,
|
|
74
100
|
extractSolanaChainReference: () => extractSolanaChainReference,
|
|
101
|
+
findAcceptsObjectFromSignedOffer: () => findAcceptsObjectFromSignedOffer,
|
|
75
102
|
formatSIWEMessage: () => formatSIWEMessage,
|
|
76
103
|
formatSIWSMessage: () => formatSIWSMessage,
|
|
77
104
|
generatePaymentId: () => generatePaymentId,
|
|
105
|
+
getCanonicalBytes: () => getCanonicalBytes,
|
|
78
106
|
getEVMAddress: () => getEVMAddress,
|
|
79
107
|
getSolanaAddress: () => getSolanaAddress,
|
|
80
108
|
hasPaymentIdentifier: () => hasPaymentIdentifier,
|
|
109
|
+
hashCanonical: () => hashCanonical,
|
|
110
|
+
hashOfferTypedData: () => hashOfferTypedData,
|
|
111
|
+
hashReceiptTypedData: () => hashReceiptTypedData,
|
|
81
112
|
isBodyExtensionConfig: () => isBodyExtensionConfig,
|
|
82
113
|
isDiscoverableV1: () => isDiscoverableV1,
|
|
114
|
+
isEIP712SignedOffer: () => isEIP712SignedOffer,
|
|
115
|
+
isEIP712SignedReceipt: () => isEIP712SignedReceipt,
|
|
116
|
+
isEIP712Signer: () => isEIP712Signer,
|
|
83
117
|
isEVMSigner: () => isEVMSigner,
|
|
118
|
+
isJWSSignedOffer: () => isJWSSignedOffer,
|
|
119
|
+
isJWSSignedReceipt: () => isJWSSignedReceipt,
|
|
120
|
+
isJWSSigner: () => isJWSSigner,
|
|
84
121
|
isMcpExtensionConfig: () => isMcpExtensionConfig,
|
|
85
122
|
isPaymentIdentifierExtension: () => isPaymentIdentifierExtension,
|
|
86
123
|
isPaymentIdentifierRequired: () => isPaymentIdentifierRequired,
|
|
@@ -90,7 +127,11 @@ __export(src_exports, {
|
|
|
90
127
|
parseSIWxHeader: () => parseSIWxHeader,
|
|
91
128
|
paymentIdentifierResourceServerExtension: () => paymentIdentifierResourceServerExtension,
|
|
92
129
|
paymentIdentifierSchema: () => paymentIdentifierSchema,
|
|
130
|
+
prepareOfferForEIP712: () => prepareOfferForEIP712,
|
|
131
|
+
prepareReceiptForEIP712: () => prepareReceiptForEIP712,
|
|
93
132
|
signEVMMessage: () => signEVMMessage,
|
|
133
|
+
signOfferEIP712: () => signOfferEIP712,
|
|
134
|
+
signReceiptEIP712: () => signReceiptEIP712,
|
|
94
135
|
signSolanaMessage: () => signSolanaMessage,
|
|
95
136
|
siwxResourceServerExtension: () => siwxResourceServerExtension,
|
|
96
137
|
validateAndExtract: () => validateAndExtract,
|
|
@@ -101,6 +142,11 @@ __export(src_exports, {
|
|
|
101
142
|
validatePaymentIdentifierRequirement: () => validatePaymentIdentifierRequirement,
|
|
102
143
|
validateSIWxMessage: () => validateSIWxMessage,
|
|
103
144
|
verifyEVMSignature: () => verifyEVMSignature,
|
|
145
|
+
verifyOfferSignatureEIP712: () => verifyOfferSignatureEIP712,
|
|
146
|
+
verifyOfferSignatureJWS: () => verifyOfferSignatureJWS,
|
|
147
|
+
verifyReceiptMatchesOffer: () => verifyReceiptMatchesOffer,
|
|
148
|
+
verifyReceiptSignatureEIP712: () => verifyReceiptSignatureEIP712,
|
|
149
|
+
verifyReceiptSignatureJWS: () => verifyReceiptSignatureJWS,
|
|
104
150
|
verifySIWxSignature: () => verifySIWxSignature,
|
|
105
151
|
verifySolanaSignature: () => verifySolanaSignature,
|
|
106
152
|
withBazaar: () => withBazaar,
|
|
@@ -832,7 +878,6 @@ function declareSIWxExtension(options = {}) {
|
|
|
832
878
|
}
|
|
833
879
|
|
|
834
880
|
// src/sign-in-with-x/server.ts
|
|
835
|
-
var import_crypto = require("crypto");
|
|
836
881
|
var siwxResourceServerExtension = {
|
|
837
882
|
key: SIGN_IN_WITH_X,
|
|
838
883
|
enrichPaymentRequiredResponse: async (declaration, context) => {
|
|
@@ -852,7 +897,7 @@ var siwxResourceServerExtension = {
|
|
|
852
897
|
} else {
|
|
853
898
|
networks = [...new Set(context.requirements.map((r) => r.network))];
|
|
854
899
|
}
|
|
855
|
-
const nonce = (
|
|
900
|
+
const nonce = Array.from(globalThis.crypto.getRandomValues(new Uint8Array(16))).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
856
901
|
const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
857
902
|
const expirationSeconds = opts.expirationSeconds;
|
|
858
903
|
const expirationTime = expirationSeconds !== void 0 ? new Date(Date.now() + expirationSeconds * 1e3).toISOString() : void 0;
|
|
@@ -1412,6 +1457,785 @@ function createSIWxClientHook(signer) {
|
|
|
1412
1457
|
};
|
|
1413
1458
|
}
|
|
1414
1459
|
|
|
1460
|
+
// src/offer-receipt/types.ts
|
|
1461
|
+
var OFFER_RECEIPT = "offer-receipt";
|
|
1462
|
+
function isJWSSignedOffer(offer) {
|
|
1463
|
+
return offer.format === "jws";
|
|
1464
|
+
}
|
|
1465
|
+
function isEIP712SignedOffer(offer) {
|
|
1466
|
+
return offer.format === "eip712";
|
|
1467
|
+
}
|
|
1468
|
+
function isJWSSignedReceipt(receipt) {
|
|
1469
|
+
return receipt.format === "jws";
|
|
1470
|
+
}
|
|
1471
|
+
function isEIP712SignedReceipt(receipt) {
|
|
1472
|
+
return receipt.format === "eip712";
|
|
1473
|
+
}
|
|
1474
|
+
function isJWSSigner(signer) {
|
|
1475
|
+
return signer.format === "jws";
|
|
1476
|
+
}
|
|
1477
|
+
function isEIP712Signer(signer) {
|
|
1478
|
+
return signer.format === "eip712";
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// src/offer-receipt/signing.ts
|
|
1482
|
+
var jose2 = __toESM(require("jose"));
|
|
1483
|
+
var import_viem2 = require("viem");
|
|
1484
|
+
|
|
1485
|
+
// src/offer-receipt/did.ts
|
|
1486
|
+
var jose = __toESM(require("jose"));
|
|
1487
|
+
var import_base2 = require("@scure/base");
|
|
1488
|
+
var import_secp256k1 = require("@noble/curves/secp256k1");
|
|
1489
|
+
var import_nist = require("@noble/curves/nist");
|
|
1490
|
+
var MULTICODEC_ED25519_PUB = 237;
|
|
1491
|
+
var MULTICODEC_SECP256K1_PUB = 231;
|
|
1492
|
+
var MULTICODEC_P256_PUB = 4608;
|
|
1493
|
+
async function extractPublicKeyFromKid(kid) {
|
|
1494
|
+
const [didPart, fragment] = kid.split("#");
|
|
1495
|
+
const parts = didPart.split(":");
|
|
1496
|
+
if (parts.length < 3 || parts[0] !== "did") {
|
|
1497
|
+
throw new Error(`Invalid DID format: ${kid}`);
|
|
1498
|
+
}
|
|
1499
|
+
const method = parts[1];
|
|
1500
|
+
const identifier = parts.slice(2).join(":");
|
|
1501
|
+
switch (method) {
|
|
1502
|
+
case "key":
|
|
1503
|
+
return extractKeyFromDidKey(identifier);
|
|
1504
|
+
case "jwk":
|
|
1505
|
+
return extractKeyFromDidJwk(identifier);
|
|
1506
|
+
case "web":
|
|
1507
|
+
return resolveDidWeb(identifier, fragment);
|
|
1508
|
+
default:
|
|
1509
|
+
throw new Error(
|
|
1510
|
+
`Unsupported DID method "${method}". Supported: did:key, did:jwk, did:web. Provide the public key directly for other methods.`
|
|
1511
|
+
);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
async function extractKeyFromDidKey(identifier) {
|
|
1515
|
+
if (!identifier.startsWith("z")) {
|
|
1516
|
+
throw new Error(`Unsupported multibase encoding. Expected 'z' (base58-btc).`);
|
|
1517
|
+
}
|
|
1518
|
+
const decoded = import_base2.base58.decode(identifier.slice(1));
|
|
1519
|
+
const { codec, keyBytes } = readMulticodec(decoded);
|
|
1520
|
+
switch (codec) {
|
|
1521
|
+
case MULTICODEC_ED25519_PUB:
|
|
1522
|
+
return importAsymmetricJWK({
|
|
1523
|
+
kty: "OKP",
|
|
1524
|
+
crv: "Ed25519",
|
|
1525
|
+
x: jose.base64url.encode(keyBytes)
|
|
1526
|
+
});
|
|
1527
|
+
case MULTICODEC_SECP256K1_PUB: {
|
|
1528
|
+
const point = import_secp256k1.secp256k1.Point.fromHex(keyBytes);
|
|
1529
|
+
const uncompressed = point.toBytes(false);
|
|
1530
|
+
return importAsymmetricJWK({
|
|
1531
|
+
kty: "EC",
|
|
1532
|
+
crv: "secp256k1",
|
|
1533
|
+
x: jose.base64url.encode(uncompressed.slice(1, 33)),
|
|
1534
|
+
y: jose.base64url.encode(uncompressed.slice(33, 65))
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
case MULTICODEC_P256_PUB: {
|
|
1538
|
+
const point = import_nist.p256.Point.fromHex(keyBytes);
|
|
1539
|
+
const uncompressed = point.toBytes(false);
|
|
1540
|
+
return importAsymmetricJWK({
|
|
1541
|
+
kty: "EC",
|
|
1542
|
+
crv: "P-256",
|
|
1543
|
+
x: jose.base64url.encode(uncompressed.slice(1, 33)),
|
|
1544
|
+
y: jose.base64url.encode(uncompressed.slice(33, 65))
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
default:
|
|
1548
|
+
throw new Error(
|
|
1549
|
+
`Unsupported key type in did:key (multicodec: 0x${codec.toString(16)}). Supported: Ed25519, secp256k1, P-256.`
|
|
1550
|
+
);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
async function extractKeyFromDidJwk(identifier) {
|
|
1554
|
+
const jwkJson = new TextDecoder().decode(jose.base64url.decode(identifier));
|
|
1555
|
+
const jwk = JSON.parse(jwkJson);
|
|
1556
|
+
return importAsymmetricJWK(jwk);
|
|
1557
|
+
}
|
|
1558
|
+
async function resolveDidWeb(identifier, fragment) {
|
|
1559
|
+
const parts = identifier.split(":");
|
|
1560
|
+
const domain = decodeURIComponent(parts[0]);
|
|
1561
|
+
const path = parts.slice(1).map(decodeURIComponent).join("/");
|
|
1562
|
+
const host = domain.split(":")[0];
|
|
1563
|
+
const scheme = host === "localhost" || host === "127.0.0.1" ? "http" : "https";
|
|
1564
|
+
const url = path ? `${scheme}://${domain}/${path}/did.json` : `${scheme}://${domain}/.well-known/did.json`;
|
|
1565
|
+
let didDocument;
|
|
1566
|
+
try {
|
|
1567
|
+
const response = await fetch(url, {
|
|
1568
|
+
headers: { Accept: "application/did+json, application/json" }
|
|
1569
|
+
});
|
|
1570
|
+
if (!response.ok) {
|
|
1571
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1572
|
+
}
|
|
1573
|
+
didDocument = await response.json();
|
|
1574
|
+
} catch (error) {
|
|
1575
|
+
throw new Error(
|
|
1576
|
+
`Failed to resolve did:web:${identifier}: ${error instanceof Error ? error.message : error}`
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
const fullDid = `did:web:${identifier}`;
|
|
1580
|
+
const keyId = fragment ? `${fullDid}#${fragment}` : void 0;
|
|
1581
|
+
const method = findVerificationMethod(didDocument, keyId);
|
|
1582
|
+
if (!method) {
|
|
1583
|
+
throw new Error(`No verification method found for ${keyId || fullDid}`);
|
|
1584
|
+
}
|
|
1585
|
+
if (method.publicKeyJwk) {
|
|
1586
|
+
return importAsymmetricJWK(method.publicKeyJwk);
|
|
1587
|
+
}
|
|
1588
|
+
if (method.publicKeyMultibase) {
|
|
1589
|
+
return extractKeyFromDidKey(method.publicKeyMultibase);
|
|
1590
|
+
}
|
|
1591
|
+
throw new Error(`Verification method ${method.id} has no supported key format`);
|
|
1592
|
+
}
|
|
1593
|
+
function readMulticodec(bytes) {
|
|
1594
|
+
let codec = 0;
|
|
1595
|
+
let shift = 0;
|
|
1596
|
+
let offset = 0;
|
|
1597
|
+
for (const byte of bytes) {
|
|
1598
|
+
codec |= (byte & 127) << shift;
|
|
1599
|
+
offset++;
|
|
1600
|
+
if ((byte & 128) === 0) break;
|
|
1601
|
+
shift += 7;
|
|
1602
|
+
}
|
|
1603
|
+
return { codec, keyBytes: bytes.slice(offset) };
|
|
1604
|
+
}
|
|
1605
|
+
async function importAsymmetricJWK(jwk) {
|
|
1606
|
+
const key = await jose.importJWK(jwk);
|
|
1607
|
+
if (key instanceof Uint8Array) {
|
|
1608
|
+
throw new Error("Symmetric keys are not supported");
|
|
1609
|
+
}
|
|
1610
|
+
return key;
|
|
1611
|
+
}
|
|
1612
|
+
function findVerificationMethod(doc, keyId) {
|
|
1613
|
+
const methods = doc.verificationMethod || [];
|
|
1614
|
+
if (keyId) {
|
|
1615
|
+
return methods.find((m) => m.id === keyId);
|
|
1616
|
+
}
|
|
1617
|
+
for (const ref of doc.assertionMethod || []) {
|
|
1618
|
+
if (typeof ref === "string") {
|
|
1619
|
+
const m = methods.find((m2) => m2.id === ref);
|
|
1620
|
+
if (m) return m;
|
|
1621
|
+
} else {
|
|
1622
|
+
return ref;
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
for (const ref of doc.authentication || []) {
|
|
1626
|
+
if (typeof ref === "string") {
|
|
1627
|
+
const m = methods.find((m2) => m2.id === ref);
|
|
1628
|
+
if (m) return m;
|
|
1629
|
+
} else {
|
|
1630
|
+
return ref;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
return methods[0];
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
// src/offer-receipt/signing.ts
|
|
1637
|
+
function canonicalize(value) {
|
|
1638
|
+
return serializeValue(value);
|
|
1639
|
+
}
|
|
1640
|
+
function serializeValue(value) {
|
|
1641
|
+
if (value === null) return "null";
|
|
1642
|
+
if (value === void 0) return "null";
|
|
1643
|
+
const type = typeof value;
|
|
1644
|
+
if (type === "boolean") return value ? "true" : "false";
|
|
1645
|
+
if (type === "number") return serializeNumber(value);
|
|
1646
|
+
if (type === "string") return serializeString(value);
|
|
1647
|
+
if (Array.isArray(value)) return serializeArray(value);
|
|
1648
|
+
if (type === "object") return serializeObject(value);
|
|
1649
|
+
throw new Error(`Cannot canonicalize value of type ${type}`);
|
|
1650
|
+
}
|
|
1651
|
+
function serializeNumber(num) {
|
|
1652
|
+
if (!Number.isFinite(num)) throw new Error("Cannot canonicalize Infinity or NaN");
|
|
1653
|
+
if (Object.is(num, -0)) return "0";
|
|
1654
|
+
return String(num);
|
|
1655
|
+
}
|
|
1656
|
+
function serializeString(str) {
|
|
1657
|
+
let result = '"';
|
|
1658
|
+
for (let i = 0; i < str.length; i++) {
|
|
1659
|
+
const char = str[i];
|
|
1660
|
+
const code = str.charCodeAt(i);
|
|
1661
|
+
if (code < 32) {
|
|
1662
|
+
result += "\\u" + code.toString(16).padStart(4, "0");
|
|
1663
|
+
} else if (char === '"') {
|
|
1664
|
+
result += '\\"';
|
|
1665
|
+
} else if (char === "\\") {
|
|
1666
|
+
result += "\\\\";
|
|
1667
|
+
} else {
|
|
1668
|
+
result += char;
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
return result + '"';
|
|
1672
|
+
}
|
|
1673
|
+
function serializeArray(arr) {
|
|
1674
|
+
return "[" + arr.map(serializeValue).join(",") + "]";
|
|
1675
|
+
}
|
|
1676
|
+
function serializeObject(obj) {
|
|
1677
|
+
const keys = Object.keys(obj).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1678
|
+
const pairs = [];
|
|
1679
|
+
for (const key of keys) {
|
|
1680
|
+
const value = obj[key];
|
|
1681
|
+
if (value !== void 0) {
|
|
1682
|
+
pairs.push(serializeString(key) + ":" + serializeValue(value));
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return "{" + pairs.join(",") + "}";
|
|
1686
|
+
}
|
|
1687
|
+
async function hashCanonical(obj) {
|
|
1688
|
+
const canonical = canonicalize(obj);
|
|
1689
|
+
const data = new TextEncoder().encode(canonical);
|
|
1690
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
1691
|
+
return new Uint8Array(hashBuffer);
|
|
1692
|
+
}
|
|
1693
|
+
function getCanonicalBytes(obj) {
|
|
1694
|
+
return new TextEncoder().encode(canonicalize(obj));
|
|
1695
|
+
}
|
|
1696
|
+
async function createJWS(payload, signer) {
|
|
1697
|
+
const headerObj = { alg: signer.algorithm, kid: signer.kid };
|
|
1698
|
+
const headerB64 = jose2.base64url.encode(new TextEncoder().encode(JSON.stringify(headerObj)));
|
|
1699
|
+
const canonical = canonicalize(payload);
|
|
1700
|
+
const payloadB64 = jose2.base64url.encode(new TextEncoder().encode(canonical));
|
|
1701
|
+
const signingInput = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
|
|
1702
|
+
const signatureB64 = await signer.sign(signingInput);
|
|
1703
|
+
return `${headerB64}.${payloadB64}.${signatureB64}`;
|
|
1704
|
+
}
|
|
1705
|
+
function extractJWSHeader(jws) {
|
|
1706
|
+
const parts = jws.split(".");
|
|
1707
|
+
if (parts.length !== 3) throw new Error("Invalid JWS format");
|
|
1708
|
+
const headerJson = jose2.base64url.decode(parts[0]);
|
|
1709
|
+
return JSON.parse(new TextDecoder().decode(headerJson));
|
|
1710
|
+
}
|
|
1711
|
+
function extractJWSPayload(jws) {
|
|
1712
|
+
const parts = jws.split(".");
|
|
1713
|
+
if (parts.length !== 3) throw new Error("Invalid JWS format");
|
|
1714
|
+
const payloadJson = jose2.base64url.decode(parts[1]);
|
|
1715
|
+
return JSON.parse(new TextDecoder().decode(payloadJson));
|
|
1716
|
+
}
|
|
1717
|
+
function createOfferDomain() {
|
|
1718
|
+
return { name: "x402 offer", version: "1", chainId: 1 };
|
|
1719
|
+
}
|
|
1720
|
+
function createReceiptDomain() {
|
|
1721
|
+
return { name: "x402 receipt", version: "1", chainId: 1 };
|
|
1722
|
+
}
|
|
1723
|
+
var OFFER_TYPES = {
|
|
1724
|
+
Offer: [
|
|
1725
|
+
{ name: "version", type: "uint256" },
|
|
1726
|
+
{ name: "resourceUrl", type: "string" },
|
|
1727
|
+
{ name: "scheme", type: "string" },
|
|
1728
|
+
{ name: "network", type: "string" },
|
|
1729
|
+
{ name: "asset", type: "string" },
|
|
1730
|
+
{ name: "payTo", type: "string" },
|
|
1731
|
+
{ name: "amount", type: "string" },
|
|
1732
|
+
{ name: "validUntil", type: "uint256" }
|
|
1733
|
+
]
|
|
1734
|
+
};
|
|
1735
|
+
var RECEIPT_TYPES = {
|
|
1736
|
+
Receipt: [
|
|
1737
|
+
{ name: "version", type: "uint256" },
|
|
1738
|
+
{ name: "network", type: "string" },
|
|
1739
|
+
{ name: "resourceUrl", type: "string" },
|
|
1740
|
+
{ name: "payer", type: "string" },
|
|
1741
|
+
{ name: "issuedAt", type: "uint256" },
|
|
1742
|
+
{ name: "transaction", type: "string" }
|
|
1743
|
+
]
|
|
1744
|
+
};
|
|
1745
|
+
function prepareOfferForEIP712(payload) {
|
|
1746
|
+
return {
|
|
1747
|
+
version: BigInt(payload.version),
|
|
1748
|
+
resourceUrl: payload.resourceUrl,
|
|
1749
|
+
scheme: payload.scheme,
|
|
1750
|
+
network: payload.network,
|
|
1751
|
+
asset: payload.asset,
|
|
1752
|
+
payTo: payload.payTo,
|
|
1753
|
+
amount: payload.amount,
|
|
1754
|
+
validUntil: BigInt(payload.validUntil)
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
function prepareReceiptForEIP712(payload) {
|
|
1758
|
+
return {
|
|
1759
|
+
version: BigInt(payload.version),
|
|
1760
|
+
network: payload.network,
|
|
1761
|
+
resourceUrl: payload.resourceUrl,
|
|
1762
|
+
payer: payload.payer,
|
|
1763
|
+
issuedAt: BigInt(payload.issuedAt),
|
|
1764
|
+
transaction: payload.transaction
|
|
1765
|
+
};
|
|
1766
|
+
}
|
|
1767
|
+
function hashOfferTypedData(payload) {
|
|
1768
|
+
return (0, import_viem2.hashTypedData)({
|
|
1769
|
+
domain: createOfferDomain(),
|
|
1770
|
+
types: OFFER_TYPES,
|
|
1771
|
+
primaryType: "Offer",
|
|
1772
|
+
message: prepareOfferForEIP712(payload)
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
function hashReceiptTypedData(payload) {
|
|
1776
|
+
return (0, import_viem2.hashTypedData)({
|
|
1777
|
+
domain: createReceiptDomain(),
|
|
1778
|
+
types: RECEIPT_TYPES,
|
|
1779
|
+
primaryType: "Receipt",
|
|
1780
|
+
message: prepareReceiptForEIP712(payload)
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
async function signOfferEIP712(payload, signTypedData) {
|
|
1784
|
+
return signTypedData({
|
|
1785
|
+
domain: createOfferDomain(),
|
|
1786
|
+
types: OFFER_TYPES,
|
|
1787
|
+
primaryType: "Offer",
|
|
1788
|
+
message: prepareOfferForEIP712(payload)
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
async function signReceiptEIP712(payload, signTypedData) {
|
|
1792
|
+
return signTypedData({
|
|
1793
|
+
domain: createReceiptDomain(),
|
|
1794
|
+
types: RECEIPT_TYPES,
|
|
1795
|
+
primaryType: "Receipt",
|
|
1796
|
+
message: prepareReceiptForEIP712(payload)
|
|
1797
|
+
});
|
|
1798
|
+
}
|
|
1799
|
+
function extractEIP155ChainId(network) {
|
|
1800
|
+
const match = network.match(/^eip155:(\d+)$/);
|
|
1801
|
+
if (!match) {
|
|
1802
|
+
throw new Error(`Invalid network format: ${network}. Expected "eip155:<chainId>"`);
|
|
1803
|
+
}
|
|
1804
|
+
return parseInt(match[1], 10);
|
|
1805
|
+
}
|
|
1806
|
+
var V1_EVM_NETWORK_CHAIN_IDS = {
|
|
1807
|
+
ethereum: 1,
|
|
1808
|
+
sepolia: 11155111,
|
|
1809
|
+
abstract: 2741,
|
|
1810
|
+
"abstract-testnet": 11124,
|
|
1811
|
+
"base-sepolia": 84532,
|
|
1812
|
+
base: 8453,
|
|
1813
|
+
"avalanche-fuji": 43113,
|
|
1814
|
+
avalanche: 43114,
|
|
1815
|
+
iotex: 4689,
|
|
1816
|
+
sei: 1329,
|
|
1817
|
+
"sei-testnet": 1328,
|
|
1818
|
+
polygon: 137,
|
|
1819
|
+
"polygon-amoy": 80002,
|
|
1820
|
+
peaq: 3338,
|
|
1821
|
+
story: 1514,
|
|
1822
|
+
educhain: 41923,
|
|
1823
|
+
"skale-base-sepolia": 324705682
|
|
1824
|
+
};
|
|
1825
|
+
var V1_SOLANA_NETWORKS = {
|
|
1826
|
+
solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
1827
|
+
"solana-devnet": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
|
|
1828
|
+
"solana-testnet": "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z"
|
|
1829
|
+
};
|
|
1830
|
+
function convertNetworkStringToCAIP2(network) {
|
|
1831
|
+
if (network.includes(":")) return network;
|
|
1832
|
+
const chainId = V1_EVM_NETWORK_CHAIN_IDS[network.toLowerCase()];
|
|
1833
|
+
if (chainId !== void 0) {
|
|
1834
|
+
return `eip155:${chainId}`;
|
|
1835
|
+
}
|
|
1836
|
+
const solanaNetwork = V1_SOLANA_NETWORKS[network.toLowerCase()];
|
|
1837
|
+
if (solanaNetwork) {
|
|
1838
|
+
return solanaNetwork;
|
|
1839
|
+
}
|
|
1840
|
+
throw new Error(
|
|
1841
|
+
`Unknown network identifier: "${network}". Expected CAIP-2 format (e.g., "eip155:8453") or v1 name (e.g., "base", "solana").`
|
|
1842
|
+
);
|
|
1843
|
+
}
|
|
1844
|
+
function extractChainIdFromCAIP2(network) {
|
|
1845
|
+
const [namespace, reference] = network.split(":");
|
|
1846
|
+
if (namespace === "eip155" && reference) {
|
|
1847
|
+
const chainId = parseInt(reference, 10);
|
|
1848
|
+
return isNaN(chainId) ? void 0 : chainId;
|
|
1849
|
+
}
|
|
1850
|
+
return void 0;
|
|
1851
|
+
}
|
|
1852
|
+
var DEFAULT_MAX_TIMEOUT_SECONDS = 300;
|
|
1853
|
+
var EXTENSION_VERSION = 1;
|
|
1854
|
+
function createOfferPayload(resourceUrl, input) {
|
|
1855
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1856
|
+
const offerValiditySeconds = input.offerValiditySeconds ?? DEFAULT_MAX_TIMEOUT_SECONDS;
|
|
1857
|
+
return {
|
|
1858
|
+
version: EXTENSION_VERSION,
|
|
1859
|
+
resourceUrl,
|
|
1860
|
+
scheme: input.scheme,
|
|
1861
|
+
network: input.network,
|
|
1862
|
+
asset: input.asset,
|
|
1863
|
+
payTo: input.payTo,
|
|
1864
|
+
amount: input.amount,
|
|
1865
|
+
validUntil: now + offerValiditySeconds
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
async function createOfferJWS(resourceUrl, input, signer) {
|
|
1869
|
+
const payload = createOfferPayload(resourceUrl, input);
|
|
1870
|
+
const jws = await createJWS(payload, signer);
|
|
1871
|
+
return {
|
|
1872
|
+
format: "jws",
|
|
1873
|
+
acceptIndex: input.acceptIndex,
|
|
1874
|
+
signature: jws
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
async function createOfferEIP712(resourceUrl, input, signTypedData) {
|
|
1878
|
+
const payload = createOfferPayload(resourceUrl, input);
|
|
1879
|
+
const signature = await signOfferEIP712(payload, signTypedData);
|
|
1880
|
+
return {
|
|
1881
|
+
format: "eip712",
|
|
1882
|
+
acceptIndex: input.acceptIndex,
|
|
1883
|
+
payload,
|
|
1884
|
+
signature
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
function extractOfferPayload(offer) {
|
|
1888
|
+
if (isJWSSignedOffer(offer)) {
|
|
1889
|
+
return extractJWSPayload(offer.signature);
|
|
1890
|
+
} else if (isEIP712SignedOffer(offer)) {
|
|
1891
|
+
return offer.payload;
|
|
1892
|
+
}
|
|
1893
|
+
throw new Error(`Unknown offer format: ${offer.format}`);
|
|
1894
|
+
}
|
|
1895
|
+
function createReceiptPayloadForEIP712(input) {
|
|
1896
|
+
return {
|
|
1897
|
+
version: EXTENSION_VERSION,
|
|
1898
|
+
network: input.network,
|
|
1899
|
+
resourceUrl: input.resourceUrl,
|
|
1900
|
+
payer: input.payer,
|
|
1901
|
+
issuedAt: Math.floor(Date.now() / 1e3),
|
|
1902
|
+
transaction: input.transaction ?? ""
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
function createReceiptPayloadForJWS(input) {
|
|
1906
|
+
const payload = {
|
|
1907
|
+
version: EXTENSION_VERSION,
|
|
1908
|
+
network: input.network,
|
|
1909
|
+
resourceUrl: input.resourceUrl,
|
|
1910
|
+
payer: input.payer,
|
|
1911
|
+
issuedAt: Math.floor(Date.now() / 1e3)
|
|
1912
|
+
};
|
|
1913
|
+
if (input.transaction) {
|
|
1914
|
+
payload.transaction = input.transaction;
|
|
1915
|
+
}
|
|
1916
|
+
return payload;
|
|
1917
|
+
}
|
|
1918
|
+
async function createReceiptJWS(input, signer) {
|
|
1919
|
+
const payload = createReceiptPayloadForJWS(input);
|
|
1920
|
+
const jws = await createJWS(payload, signer);
|
|
1921
|
+
return { format: "jws", signature: jws };
|
|
1922
|
+
}
|
|
1923
|
+
async function createReceiptEIP712(input, signTypedData) {
|
|
1924
|
+
const payload = createReceiptPayloadForEIP712(input);
|
|
1925
|
+
const signature = await signReceiptEIP712(payload, signTypedData);
|
|
1926
|
+
return { format: "eip712", payload, signature };
|
|
1927
|
+
}
|
|
1928
|
+
function extractReceiptPayload(receipt) {
|
|
1929
|
+
if (isJWSSignedReceipt(receipt)) {
|
|
1930
|
+
return extractJWSPayload(receipt.signature);
|
|
1931
|
+
} else if (isEIP712SignedReceipt(receipt)) {
|
|
1932
|
+
return receipt.payload;
|
|
1933
|
+
}
|
|
1934
|
+
throw new Error(`Unknown receipt format: ${receipt.format}`);
|
|
1935
|
+
}
|
|
1936
|
+
async function verifyOfferSignatureEIP712(offer) {
|
|
1937
|
+
if (offer.format !== "eip712") {
|
|
1938
|
+
throw new Error(`Expected eip712 format, got ${offer.format}`);
|
|
1939
|
+
}
|
|
1940
|
+
if (!offer.payload || !("scheme" in offer.payload)) {
|
|
1941
|
+
throw new Error("Invalid offer: missing or malformed payload");
|
|
1942
|
+
}
|
|
1943
|
+
const signer = await (0, import_viem2.recoverTypedDataAddress)({
|
|
1944
|
+
domain: createOfferDomain(),
|
|
1945
|
+
types: OFFER_TYPES,
|
|
1946
|
+
primaryType: "Offer",
|
|
1947
|
+
message: prepareOfferForEIP712(offer.payload),
|
|
1948
|
+
signature: offer.signature
|
|
1949
|
+
});
|
|
1950
|
+
return { signer, payload: offer.payload };
|
|
1951
|
+
}
|
|
1952
|
+
async function verifyReceiptSignatureEIP712(receipt) {
|
|
1953
|
+
if (receipt.format !== "eip712") {
|
|
1954
|
+
throw new Error(`Expected eip712 format, got ${receipt.format}`);
|
|
1955
|
+
}
|
|
1956
|
+
if (!receipt.payload || !("payer" in receipt.payload)) {
|
|
1957
|
+
throw new Error("Invalid receipt: missing or malformed payload");
|
|
1958
|
+
}
|
|
1959
|
+
const signer = await (0, import_viem2.recoverTypedDataAddress)({
|
|
1960
|
+
domain: createReceiptDomain(),
|
|
1961
|
+
types: RECEIPT_TYPES,
|
|
1962
|
+
primaryType: "Receipt",
|
|
1963
|
+
message: prepareReceiptForEIP712(receipt.payload),
|
|
1964
|
+
signature: receipt.signature
|
|
1965
|
+
});
|
|
1966
|
+
return { signer, payload: receipt.payload };
|
|
1967
|
+
}
|
|
1968
|
+
async function verifyOfferSignatureJWS(offer, publicKey) {
|
|
1969
|
+
if (offer.format !== "jws") {
|
|
1970
|
+
throw new Error(`Expected jws format, got ${offer.format}`);
|
|
1971
|
+
}
|
|
1972
|
+
const key = await resolveVerificationKey(offer.signature, publicKey);
|
|
1973
|
+
const { payload } = await jose2.compactVerify(offer.signature, key);
|
|
1974
|
+
return JSON.parse(new TextDecoder().decode(payload));
|
|
1975
|
+
}
|
|
1976
|
+
async function verifyReceiptSignatureJWS(receipt, publicKey) {
|
|
1977
|
+
if (receipt.format !== "jws") {
|
|
1978
|
+
throw new Error(`Expected jws format, got ${receipt.format}`);
|
|
1979
|
+
}
|
|
1980
|
+
const key = await resolveVerificationKey(receipt.signature, publicKey);
|
|
1981
|
+
const { payload } = await jose2.compactVerify(receipt.signature, key);
|
|
1982
|
+
return JSON.parse(new TextDecoder().decode(payload));
|
|
1983
|
+
}
|
|
1984
|
+
async function resolveVerificationKey(jws, providedKey) {
|
|
1985
|
+
if (providedKey) {
|
|
1986
|
+
if ("kty" in providedKey) {
|
|
1987
|
+
const key = await jose2.importJWK(providedKey);
|
|
1988
|
+
if (key instanceof Uint8Array) {
|
|
1989
|
+
throw new Error("Symmetric keys are not supported for JWS verification");
|
|
1990
|
+
}
|
|
1991
|
+
return key;
|
|
1992
|
+
}
|
|
1993
|
+
return providedKey;
|
|
1994
|
+
}
|
|
1995
|
+
const header = extractJWSHeader(jws);
|
|
1996
|
+
if (!header.kid) {
|
|
1997
|
+
throw new Error("No public key provided and JWS header missing kid");
|
|
1998
|
+
}
|
|
1999
|
+
return extractPublicKeyFromKid(header.kid);
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
// src/offer-receipt/server.ts
|
|
2003
|
+
var OFFER_SCHEMA = {
|
|
2004
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
2005
|
+
type: "object",
|
|
2006
|
+
properties: {
|
|
2007
|
+
offers: {
|
|
2008
|
+
type: "array",
|
|
2009
|
+
items: {
|
|
2010
|
+
type: "object",
|
|
2011
|
+
properties: {
|
|
2012
|
+
format: { type: "string" },
|
|
2013
|
+
acceptIndex: { type: "integer" },
|
|
2014
|
+
payload: {
|
|
2015
|
+
type: "object",
|
|
2016
|
+
properties: {
|
|
2017
|
+
version: { type: "integer" },
|
|
2018
|
+
resourceUrl: { type: "string" },
|
|
2019
|
+
scheme: { type: "string" },
|
|
2020
|
+
network: { type: "string" },
|
|
2021
|
+
asset: { type: "string" },
|
|
2022
|
+
payTo: { type: "string" },
|
|
2023
|
+
amount: { type: "string" },
|
|
2024
|
+
validUntil: { type: "integer" }
|
|
2025
|
+
},
|
|
2026
|
+
required: ["version", "resourceUrl", "scheme", "network", "asset", "payTo", "amount"]
|
|
2027
|
+
},
|
|
2028
|
+
signature: { type: "string" }
|
|
2029
|
+
},
|
|
2030
|
+
required: ["format", "signature"]
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
},
|
|
2034
|
+
required: ["offers"]
|
|
2035
|
+
};
|
|
2036
|
+
var RECEIPT_SCHEMA = {
|
|
2037
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
2038
|
+
type: "object",
|
|
2039
|
+
properties: {
|
|
2040
|
+
receipt: {
|
|
2041
|
+
type: "object",
|
|
2042
|
+
properties: {
|
|
2043
|
+
format: { type: "string" },
|
|
2044
|
+
payload: {
|
|
2045
|
+
type: "object",
|
|
2046
|
+
properties: {
|
|
2047
|
+
version: { type: "integer" },
|
|
2048
|
+
network: { type: "string" },
|
|
2049
|
+
resourceUrl: { type: "string" },
|
|
2050
|
+
payer: { type: "string" },
|
|
2051
|
+
issuedAt: { type: "integer" },
|
|
2052
|
+
transaction: { type: "string" }
|
|
2053
|
+
},
|
|
2054
|
+
required: ["version", "network", "resourceUrl", "payer", "issuedAt"]
|
|
2055
|
+
},
|
|
2056
|
+
signature: { type: "string" }
|
|
2057
|
+
},
|
|
2058
|
+
required: ["format", "signature"]
|
|
2059
|
+
}
|
|
2060
|
+
},
|
|
2061
|
+
required: ["receipt"]
|
|
2062
|
+
};
|
|
2063
|
+
function requirementsToOfferInput(requirements, acceptIndex, offerValiditySeconds) {
|
|
2064
|
+
return {
|
|
2065
|
+
acceptIndex,
|
|
2066
|
+
scheme: requirements.scheme,
|
|
2067
|
+
network: requirements.network,
|
|
2068
|
+
asset: requirements.asset,
|
|
2069
|
+
payTo: requirements.payTo,
|
|
2070
|
+
amount: requirements.amount,
|
|
2071
|
+
offerValiditySeconds: offerValiditySeconds ?? requirements.maxTimeoutSeconds
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
function createOfferReceiptExtension(issuer) {
|
|
2075
|
+
return {
|
|
2076
|
+
key: OFFER_RECEIPT,
|
|
2077
|
+
// Add signed offers to 402 PaymentRequired response
|
|
2078
|
+
enrichPaymentRequiredResponse: async (declaration, context) => {
|
|
2079
|
+
const config = declaration;
|
|
2080
|
+
const resourceUrl = context.paymentRequiredResponse.resource?.url || context.transportContext?.request?.adapter?.getUrl?.();
|
|
2081
|
+
if (!resourceUrl) {
|
|
2082
|
+
console.warn("[offer-receipt] No resource URL available for signing offers");
|
|
2083
|
+
return void 0;
|
|
2084
|
+
}
|
|
2085
|
+
const offers = [];
|
|
2086
|
+
for (let i = 0; i < context.requirements.length; i++) {
|
|
2087
|
+
const requirement = context.requirements[i];
|
|
2088
|
+
try {
|
|
2089
|
+
const offerInput = requirementsToOfferInput(requirement, i, config?.offerValiditySeconds);
|
|
2090
|
+
const signedOffer = await issuer.issueOffer(resourceUrl, offerInput);
|
|
2091
|
+
offers.push(signedOffer);
|
|
2092
|
+
} catch (error) {
|
|
2093
|
+
console.error(`[offer-receipt] Failed to sign offer for requirement ${i}:`, error);
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
if (offers.length === 0) {
|
|
2097
|
+
return void 0;
|
|
2098
|
+
}
|
|
2099
|
+
return {
|
|
2100
|
+
info: {
|
|
2101
|
+
offers
|
|
2102
|
+
},
|
|
2103
|
+
schema: OFFER_SCHEMA
|
|
2104
|
+
};
|
|
2105
|
+
},
|
|
2106
|
+
// Add signed receipt to settlement response
|
|
2107
|
+
enrichSettlementResponse: async (declaration, context) => {
|
|
2108
|
+
const config = declaration;
|
|
2109
|
+
if (!context.result.success) {
|
|
2110
|
+
return void 0;
|
|
2111
|
+
}
|
|
2112
|
+
const payer = context.result.payer;
|
|
2113
|
+
if (!payer) {
|
|
2114
|
+
console.warn("[offer-receipt] No payer available for signing receipt");
|
|
2115
|
+
return void 0;
|
|
2116
|
+
}
|
|
2117
|
+
const network = context.result.network;
|
|
2118
|
+
if (!network) {
|
|
2119
|
+
console.warn("[offer-receipt] No network available for signing receipt");
|
|
2120
|
+
return void 0;
|
|
2121
|
+
}
|
|
2122
|
+
const transaction = context.result.transaction;
|
|
2123
|
+
const resourceUrl = context.transportContext?.request?.adapter?.getUrl?.();
|
|
2124
|
+
if (!resourceUrl) {
|
|
2125
|
+
console.warn("[offer-receipt] No resource URL available for signing receipt");
|
|
2126
|
+
return void 0;
|
|
2127
|
+
}
|
|
2128
|
+
const includeTxHash = config?.includeTxHash === true;
|
|
2129
|
+
try {
|
|
2130
|
+
const signedReceipt = await issuer.issueReceipt(
|
|
2131
|
+
resourceUrl,
|
|
2132
|
+
payer,
|
|
2133
|
+
network,
|
|
2134
|
+
includeTxHash ? transaction || void 0 : void 0
|
|
2135
|
+
);
|
|
2136
|
+
return {
|
|
2137
|
+
info: {
|
|
2138
|
+
receipt: signedReceipt
|
|
2139
|
+
},
|
|
2140
|
+
schema: RECEIPT_SCHEMA
|
|
2141
|
+
};
|
|
2142
|
+
} catch (error) {
|
|
2143
|
+
console.error("[offer-receipt] Failed to sign receipt:", error);
|
|
2144
|
+
return void 0;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
function declareOfferReceiptExtension(config) {
|
|
2150
|
+
return {
|
|
2151
|
+
[OFFER_RECEIPT]: {
|
|
2152
|
+
includeTxHash: config?.includeTxHash,
|
|
2153
|
+
offerValiditySeconds: config?.offerValiditySeconds
|
|
2154
|
+
}
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
function createJWSOfferReceiptIssuer(kid, jwsSigner) {
|
|
2158
|
+
return {
|
|
2159
|
+
kid,
|
|
2160
|
+
format: "jws",
|
|
2161
|
+
async issueOffer(resourceUrl, input) {
|
|
2162
|
+
return createOfferJWS(resourceUrl, input, jwsSigner);
|
|
2163
|
+
},
|
|
2164
|
+
async issueReceipt(resourceUrl, payer, network, transaction) {
|
|
2165
|
+
return createReceiptJWS({ resourceUrl, payer, network, transaction }, jwsSigner);
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
function createEIP712OfferReceiptIssuer(kid, signTypedData) {
|
|
2170
|
+
return {
|
|
2171
|
+
kid,
|
|
2172
|
+
format: "eip712",
|
|
2173
|
+
async issueOffer(resourceUrl, input) {
|
|
2174
|
+
return createOfferEIP712(resourceUrl, input, signTypedData);
|
|
2175
|
+
},
|
|
2176
|
+
async issueReceipt(resourceUrl, payer, network, transaction) {
|
|
2177
|
+
return createReceiptEIP712({ resourceUrl, payer, network, transaction }, signTypedData);
|
|
2178
|
+
}
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
// src/offer-receipt/client.ts
|
|
2183
|
+
var import_http2 = require("@x402/core/http");
|
|
2184
|
+
function verifyReceiptMatchesOffer(receipt, offer, payerAddresses, maxAgeSeconds = 3600) {
|
|
2185
|
+
const payload = extractReceiptPayload(receipt);
|
|
2186
|
+
const resourceUrlMatch = payload.resourceUrl === offer.resourceUrl;
|
|
2187
|
+
const networkMatch = payload.network === offer.network;
|
|
2188
|
+
const payerMatch = payerAddresses.some(
|
|
2189
|
+
(addr) => payload.payer.toLowerCase() === addr.toLowerCase()
|
|
2190
|
+
);
|
|
2191
|
+
const issuedRecently = Math.floor(Date.now() / 1e3) - payload.issuedAt < maxAgeSeconds;
|
|
2192
|
+
return resourceUrlMatch && networkMatch && payerMatch && issuedRecently;
|
|
2193
|
+
}
|
|
2194
|
+
function extractOffersFromPaymentRequired(paymentRequired) {
|
|
2195
|
+
const extData = paymentRequired.extensions?.[OFFER_RECEIPT];
|
|
2196
|
+
return extData?.info?.offers ?? [];
|
|
2197
|
+
}
|
|
2198
|
+
function decodeSignedOffers(offers) {
|
|
2199
|
+
return offers.map((offer) => {
|
|
2200
|
+
const payload = extractOfferPayload(offer);
|
|
2201
|
+
return {
|
|
2202
|
+
// Spread payload fields at top level
|
|
2203
|
+
...payload,
|
|
2204
|
+
// Include metadata
|
|
2205
|
+
signedOffer: offer,
|
|
2206
|
+
format: offer.format,
|
|
2207
|
+
acceptIndex: offer.acceptIndex
|
|
2208
|
+
};
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
function findAcceptsObjectFromSignedOffer(offer, accepts) {
|
|
2212
|
+
const isDecoded = "signedOffer" in offer;
|
|
2213
|
+
const payload = isDecoded ? offer : extractOfferPayload(offer);
|
|
2214
|
+
const acceptIndex = isDecoded ? offer.acceptIndex : offer.acceptIndex;
|
|
2215
|
+
if (acceptIndex !== void 0 && acceptIndex < accepts.length) {
|
|
2216
|
+
const hinted = accepts[acceptIndex];
|
|
2217
|
+
if (hinted.network === payload.network && hinted.scheme === payload.scheme && hinted.asset === payload.asset && hinted.payTo === payload.payTo && hinted.amount === payload.amount) {
|
|
2218
|
+
return hinted;
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
return accepts.find(
|
|
2222
|
+
(req) => req.network === payload.network && req.scheme === payload.scheme && req.asset === payload.asset && req.payTo === payload.payTo && req.amount === payload.amount
|
|
2223
|
+
);
|
|
2224
|
+
}
|
|
2225
|
+
function extractReceiptFromResponse(response) {
|
|
2226
|
+
const paymentResponseHeader = response.headers.get("PAYMENT-RESPONSE") || response.headers.get("X-PAYMENT-RESPONSE");
|
|
2227
|
+
if (!paymentResponseHeader) {
|
|
2228
|
+
return void 0;
|
|
2229
|
+
}
|
|
2230
|
+
try {
|
|
2231
|
+
const settlementResponse = (0, import_http2.decodePaymentResponseHeader)(paymentResponseHeader);
|
|
2232
|
+
const receiptExtData = settlementResponse.extensions?.[OFFER_RECEIPT];
|
|
2233
|
+
return receiptExtData?.info?.receipt;
|
|
2234
|
+
} catch {
|
|
2235
|
+
return void 0;
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
|
|
1415
2239
|
// src/payment-identifier/types.ts
|
|
1416
2240
|
var PAYMENT_IDENTIFIER = "payment-identifier";
|
|
1417
2241
|
var PAYMENT_ID_MIN_LENGTH = 16;
|
|
@@ -1717,14 +2541,8 @@ var ERC20_APPROVAL_GAS_SPONSORING = {
|
|
|
1717
2541
|
key: "erc20ApprovalGasSponsoring"
|
|
1718
2542
|
};
|
|
1719
2543
|
var ERC20_APPROVAL_GAS_SPONSORING_VERSION = "1";
|
|
1720
|
-
function createErc20ApprovalGasSponsoringExtension(signer,
|
|
1721
|
-
return {
|
|
1722
|
-
...ERC20_APPROVAL_GAS_SPONSORING,
|
|
1723
|
-
signer: {
|
|
1724
|
-
...signer,
|
|
1725
|
-
sendRawTransaction: client.sendRawTransaction.bind(client)
|
|
1726
|
-
}
|
|
1727
|
-
};
|
|
2544
|
+
function createErc20ApprovalGasSponsoringExtension(signer, signerForNetwork) {
|
|
2545
|
+
return { ...ERC20_APPROVAL_GAS_SPONSORING, signer, signerForNetwork };
|
|
1728
2546
|
}
|
|
1729
2547
|
|
|
1730
2548
|
// src/erc20-approval-gas-sponsoring/resourceService.ts
|
|
@@ -1806,10 +2624,13 @@ function validateErc20ApprovalGasSponsoringInfo(info) {
|
|
|
1806
2624
|
ERC20_APPROVAL_GAS_SPONSORING,
|
|
1807
2625
|
ERC20_APPROVAL_GAS_SPONSORING_VERSION,
|
|
1808
2626
|
InMemorySIWxStorage,
|
|
2627
|
+
OFFER_RECEIPT,
|
|
2628
|
+
OFFER_TYPES,
|
|
1809
2629
|
PAYMENT_IDENTIFIER,
|
|
1810
2630
|
PAYMENT_ID_MAX_LENGTH,
|
|
1811
2631
|
PAYMENT_ID_MIN_LENGTH,
|
|
1812
2632
|
PAYMENT_ID_PATTERN,
|
|
2633
|
+
RECEIPT_TYPES,
|
|
1813
2634
|
SIGN_IN_WITH_X,
|
|
1814
2635
|
SIWxPayloadSchema,
|
|
1815
2636
|
SOLANA_DEVNET,
|
|
@@ -1818,7 +2639,19 @@ function validateErc20ApprovalGasSponsoringInfo(info) {
|
|
|
1818
2639
|
appendPaymentIdentifierToExtensions,
|
|
1819
2640
|
bazaarResourceServerExtension,
|
|
1820
2641
|
buildSIWxSchema,
|
|
2642
|
+
canonicalize,
|
|
2643
|
+
convertNetworkStringToCAIP2,
|
|
2644
|
+
createEIP712OfferReceiptIssuer,
|
|
1821
2645
|
createErc20ApprovalGasSponsoringExtension,
|
|
2646
|
+
createJWS,
|
|
2647
|
+
createJWSOfferReceiptIssuer,
|
|
2648
|
+
createOfferDomain,
|
|
2649
|
+
createOfferEIP712,
|
|
2650
|
+
createOfferJWS,
|
|
2651
|
+
createOfferReceiptExtension,
|
|
2652
|
+
createReceiptDomain,
|
|
2653
|
+
createReceiptEIP712,
|
|
2654
|
+
createReceiptJWS,
|
|
1822
2655
|
createSIWxClientHook,
|
|
1823
2656
|
createSIWxMessage,
|
|
1824
2657
|
createSIWxPayload,
|
|
@@ -1827,31 +2660,53 @@ function validateErc20ApprovalGasSponsoringInfo(info) {
|
|
|
1827
2660
|
declareDiscoveryExtension,
|
|
1828
2661
|
declareEip2612GasSponsoringExtension,
|
|
1829
2662
|
declareErc20ApprovalGasSponsoringExtension,
|
|
2663
|
+
declareOfferReceiptExtension,
|
|
1830
2664
|
declarePaymentIdentifierExtension,
|
|
1831
2665
|
declareSIWxExtension,
|
|
1832
2666
|
decodeBase58,
|
|
2667
|
+
decodeSignedOffers,
|
|
1833
2668
|
encodeBase58,
|
|
1834
2669
|
encodeSIWxHeader,
|
|
1835
2670
|
erc20ApprovalGasSponsoringSchema,
|
|
1836
2671
|
extractAndValidatePaymentIdentifier,
|
|
2672
|
+
extractChainIdFromCAIP2,
|
|
1837
2673
|
extractDiscoveryInfo,
|
|
1838
2674
|
extractDiscoveryInfoFromExtension,
|
|
1839
2675
|
extractDiscoveryInfoV1,
|
|
2676
|
+
extractEIP155ChainId,
|
|
1840
2677
|
extractEVMChainId,
|
|
1841
2678
|
extractEip2612GasSponsoringInfo,
|
|
1842
2679
|
extractErc20ApprovalGasSponsoringInfo,
|
|
2680
|
+
extractJWSHeader,
|
|
2681
|
+
extractJWSPayload,
|
|
2682
|
+
extractOfferPayload,
|
|
2683
|
+
extractOffersFromPaymentRequired,
|
|
1843
2684
|
extractPaymentIdentifier,
|
|
2685
|
+
extractPublicKeyFromKid,
|
|
2686
|
+
extractReceiptFromResponse,
|
|
2687
|
+
extractReceiptPayload,
|
|
1844
2688
|
extractResourceMetadataV1,
|
|
1845
2689
|
extractSolanaChainReference,
|
|
2690
|
+
findAcceptsObjectFromSignedOffer,
|
|
1846
2691
|
formatSIWEMessage,
|
|
1847
2692
|
formatSIWSMessage,
|
|
1848
2693
|
generatePaymentId,
|
|
2694
|
+
getCanonicalBytes,
|
|
1849
2695
|
getEVMAddress,
|
|
1850
2696
|
getSolanaAddress,
|
|
1851
2697
|
hasPaymentIdentifier,
|
|
2698
|
+
hashCanonical,
|
|
2699
|
+
hashOfferTypedData,
|
|
2700
|
+
hashReceiptTypedData,
|
|
1852
2701
|
isBodyExtensionConfig,
|
|
1853
2702
|
isDiscoverableV1,
|
|
2703
|
+
isEIP712SignedOffer,
|
|
2704
|
+
isEIP712SignedReceipt,
|
|
2705
|
+
isEIP712Signer,
|
|
1854
2706
|
isEVMSigner,
|
|
2707
|
+
isJWSSignedOffer,
|
|
2708
|
+
isJWSSignedReceipt,
|
|
2709
|
+
isJWSSigner,
|
|
1855
2710
|
isMcpExtensionConfig,
|
|
1856
2711
|
isPaymentIdentifierExtension,
|
|
1857
2712
|
isPaymentIdentifierRequired,
|
|
@@ -1861,7 +2716,11 @@ function validateErc20ApprovalGasSponsoringInfo(info) {
|
|
|
1861
2716
|
parseSIWxHeader,
|
|
1862
2717
|
paymentIdentifierResourceServerExtension,
|
|
1863
2718
|
paymentIdentifierSchema,
|
|
2719
|
+
prepareOfferForEIP712,
|
|
2720
|
+
prepareReceiptForEIP712,
|
|
1864
2721
|
signEVMMessage,
|
|
2722
|
+
signOfferEIP712,
|
|
2723
|
+
signReceiptEIP712,
|
|
1865
2724
|
signSolanaMessage,
|
|
1866
2725
|
siwxResourceServerExtension,
|
|
1867
2726
|
validateAndExtract,
|
|
@@ -1872,6 +2731,11 @@ function validateErc20ApprovalGasSponsoringInfo(info) {
|
|
|
1872
2731
|
validatePaymentIdentifierRequirement,
|
|
1873
2732
|
validateSIWxMessage,
|
|
1874
2733
|
verifyEVMSignature,
|
|
2734
|
+
verifyOfferSignatureEIP712,
|
|
2735
|
+
verifyOfferSignatureJWS,
|
|
2736
|
+
verifyReceiptMatchesOffer,
|
|
2737
|
+
verifyReceiptSignatureEIP712,
|
|
2738
|
+
verifyReceiptSignatureJWS,
|
|
1875
2739
|
verifySIWxSignature,
|
|
1876
2740
|
verifySolanaSignature,
|
|
1877
2741
|
withBazaar,
|