@armory-sh/base 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,85 @@
1
+ /**
2
+ * X402 Protocol Encoding - Coinbase Compatible
3
+ *
4
+ * All payment payloads are Base64 encoded for transport in HTTP headers.
5
+ * This matches the Coinbase x402 SDK format.
6
+ */
7
+ import type { PaymentPayload, SettlementResponse, X402Response, LegacyPaymentPayload, PaymentPayloadV1, PaymentPayloadV2, PaymentRequirements } from "../types/x402";
8
+ /**
9
+ * Safe Base64 encode (URL-safe, no padding)
10
+ */
11
+ export declare function safeBase64Encode(str: string): string;
12
+ /**
13
+ * Safe Base64 decode (handles URL-safe format)
14
+ */
15
+ export declare function safeBase64Decode(str: string): string;
16
+ /**
17
+ * Encode payment payload to Base64 string
18
+ * All versions use Base64 encoding for compatibility
19
+ */
20
+ export declare function encodePayment(payload: PaymentPayload | LegacyPaymentPayload): string;
21
+ /**
22
+ * Decode payment payload from Base64 string
23
+ */
24
+ export declare function decodePayment(encoded: string): PaymentPayload;
25
+ /**
26
+ * Encode settlement response to Base64 string
27
+ */
28
+ export declare function encodeSettlementResponse(response: SettlementResponse): string;
29
+ /**
30
+ * Decode settlement response from Base64 string
31
+ */
32
+ export declare function decodeSettlementResponse(encoded: string): SettlementResponse;
33
+ /**
34
+ * Encode X402 error response
35
+ */
36
+ export declare function encodeX402Response(response: X402Response): string;
37
+ /**
38
+ * Decode X402 error response
39
+ */
40
+ export declare function decodeX402Response(encoded: string): X402Response;
41
+ /**
42
+ * Unified X402 headers - Coinbase compatible
43
+ */
44
+ export declare const X402_HEADERS: {
45
+ readonly PAYMENT: "X-PAYMENT";
46
+ readonly PAYMENT_RESPONSE: "X-PAYMENT-RESPONSE";
47
+ readonly PAYMENT_REQUIRED: "X-PAYMENT-REQUIRED";
48
+ };
49
+ /**
50
+ * Detect payment version from headers
51
+ * Returns null if no payment header found
52
+ *
53
+ * Supports both x402 format (X-PAYMENT header with x402Version)
54
+ * and legacy format (X-PAYMENT for v1, PAYMENT-SIGNATURE for v2)
55
+ */
56
+ export declare function detectPaymentVersion(headers: Headers): number | null;
57
+ /**
58
+ * Extract payment from headers
59
+ */
60
+ export declare function extractPaymentFromHeaders(headers: Headers): PaymentPayload | null;
61
+ /**
62
+ * Create payment required headers
63
+ */
64
+ export declare function createPaymentRequiredHeaders(requirements: PaymentRequirements | PaymentRequirements[]): Record<string, string>;
65
+ /**
66
+ * Create settlement response headers
67
+ */
68
+ export declare function createSettlementHeaders(settlement: SettlementResponse): Record<string, string>;
69
+ export declare const encodePaymentV1: typeof encodePayment;
70
+ export declare const decodePaymentV1: typeof decodePayment;
71
+ export declare const encodePaymentV2: typeof encodePayment;
72
+ export declare const decodePaymentV2: typeof decodePayment;
73
+ export declare const encodeSettlementV1: typeof encodeSettlementResponse;
74
+ export declare const decodeSettlementV1: typeof decodeSettlementResponse;
75
+ export declare const encodeSettlementV2: typeof encodeSettlementResponse;
76
+ export declare const decodeSettlementV2: typeof decodeSettlementResponse;
77
+ /**
78
+ * Type guards for legacy compatibility
79
+ */
80
+ export declare function isLegacyV1(payload: unknown): payload is PaymentPayloadV1;
81
+ export declare function isLegacyV2(payload: unknown): payload is PaymentPayloadV2;
82
+ /**
83
+ * Decode settlement from headers (legacy compatibility)
84
+ */
85
+ export declare function decodeSettlement(headers: Headers): SettlementResponse;
package/dist/index.d.ts CHANGED
@@ -1,16 +1,19 @@
1
+ export type { Address as X402Address, Hex, X402Version, Scheme, Network as X402Network, ExactEvmAuthorization, ExactEvmPayload, PaymentPayload as X402PaymentPayload, UnsignedPaymentPayload as X402UnsignedPaymentPayload, PaymentRequirements as X402PaymentRequirements, SettlementResponse as X402SettlementResponse, VerifyResponse, X402Response, PaymentPayloadV1 as X402PayloadV1, PaymentPayloadV2 as X402PayloadV2, LegacyPaymentPayload as X402LegacyPayload, } from "./types/x402";
2
+ export { X402_VERSION, SCHEMES, isPaymentPayload, isExactEvmPayload, legacyToPaymentPayload, } from "./types/x402";
3
+ export { safeBase64Encode, safeBase64Decode, encodePayment, decodePayment, encodeSettlementResponse, decodeSettlementResponse, encodeX402Response, decodeX402Response, X402_HEADERS, detectPaymentVersion, extractPaymentFromHeaders, createPaymentRequiredHeaders, createSettlementHeaders, isLegacyV1, isLegacyV2, } from "./encoding/x402";
4
+ export { createNonce, toAtomicUnits, fromAtomicUnits, parseSignature, combineSignature, caip2ToNetwork, networkToCaip2, getCurrentTimestamp, isValidAddress, normalizeAddress, } from "./utils/x402";
1
5
  export type { PaymentPayloadV1, PaymentRequirementsV1, SettlementResponseV1, } from "./types/v1";
2
- export { V1_HEADERS, encodePaymentPayload, decodePaymentPayload, encodeSettlementResponse, decodeSettlementResponse, } from "./types/v1";
6
+ export { V1_HEADERS, encodePaymentPayload, decodePaymentPayload, encodeSettlementResponse as encodeSettlementResponseV1, decodeSettlementResponse as decodeSettlementResponseV1, } from "./types/v1";
3
7
  export type { CAIP2ChainId, CAIPAssetId, Address, Signature, PayToV2, Extensions, PaymentPayloadV2, PaymentRequirementsV2, SettlementResponseV2, } from "./types/v2";
4
8
  export { V2_HEADERS, isCAIP2ChainId, isCAIPAssetId, isAddress, } from "./types/v2";
5
9
  export type { PaymentPayload, PaymentRequirements, SettlementResponse, } from "./types/protocol";
6
- export { isV1, isV2, getPaymentVersion as getPaymentVersionFromPayload, getRequirementsVersion, getSettlementVersion, getPaymentHeaderName, getPaymentResponseHeaderName, getPaymentRequiredHeaderName, isSettlementSuccessful, getTxHash, } from "./types/protocol";
10
+ export { isV1, isV2, getPaymentVersion, getRequirementsVersion, getSettlementVersion, getPaymentHeaderName, getPaymentResponseHeaderName, getPaymentRequiredHeaderName, isSettlementSuccessful, getTxHash, } from "./types/protocol";
7
11
  export type { NetworkConfig, CustomToken } from "./types/networks";
8
12
  export { NETWORKS, getNetworkConfig, getNetworkByChainId, getMainnets, getTestnets, registerToken, getCustomToken, getAllCustomTokens, unregisterToken, isCustomToken, } from "./types/networks";
9
13
  export type { TransferWithAuthorizationParams, ReceiveWithAuthorizationParams, BalanceOfParams, BalanceOfReturnType, NameReturnType, SymbolReturnType, ERC20Abi, } from "./abi/erc20";
10
14
  export { ERC20_ABI } from "./abi/erc20";
11
- export type { PaymentPayload as EncodingPaymentPayload, SettlementResponse as EncodingSettlementResponse, } from "./encoding";
12
- export { encodePaymentV1, decodePaymentV1, encodeSettlementV1, decodeSettlementV1, encodePaymentV2, decodePaymentV2, encodeSettlementV2, decodeSettlementV2, detectPaymentVersion, decodePayment, decodeSettlement, isPaymentV1, isPaymentV2, isSettlementV1, isSettlementV2, } from "./encoding";
13
15
  export type { TypedDataDomain, EIP712Domain, TransferWithAuthorization, TypedDataField, EIP712Types, } from "./eip712";
14
16
  export { EIP712_TYPES, USDC_DOMAIN, createEIP712Domain, createTransferWithAuthorization, validateTransferWithAuthorization, } from "./eip712";
15
17
  export { resolveNetwork, resolveToken, resolveFacilitator, checkFacilitatorSupport, validatePaymentConfig, validateAcceptConfig, getAvailableNetworks, getAvailableTokens, isValidationError, isResolvedNetwork, isResolvedToken, createError, } from "./validation";
18
+ export { encodePaymentV1, decodePaymentV1, encodeSettlementV1, decodeSettlementV1, encodePaymentV2, decodePaymentV2, encodeSettlementV2, decodeSettlementV2, detectPaymentVersion as detectPaymentVersionLegacy, decodePayment as decodePaymentLegacy, decodeSettlement as decodeSettlementLegacy, isPaymentV1, isPaymentV2, isSettlementV1, isSettlementV2, } from "./encoding";
16
19
  export type { NetworkId, TokenId, FacilitatorConfig, AcceptPaymentOptions, PaymentResult, PaymentError, PaymentErrorCode, ArmoryPaymentResult, ResolvedNetwork, ResolvedToken, ResolvedFacilitator, ResolvedPaymentConfig, ValidationError, } from "./types/simple";
package/dist/index.js CHANGED
@@ -1,3 +1,307 @@
1
+ // src/types/x402.ts
2
+ var X402_VERSION = 1;
3
+ var SCHEMES = ["exact"];
4
+ var CHAIN_ID_TO_NETWORK = {
5
+ 1: "ethereum",
6
+ 8453: "base",
7
+ 84532: "base-sepolia",
8
+ 137: "polygon",
9
+ 42161: "arbitrum",
10
+ 421614: "arbitrum-sepolia",
11
+ 10: "optimism",
12
+ 11155420: "optimism-sepolia",
13
+ 11155111: "ethereum-sepolia"
14
+ };
15
+ function isPaymentPayload(obj) {
16
+ return typeof obj === "object" && obj !== null && "x402Version" in obj && "scheme" in obj && "network" in obj && "payload" in obj;
17
+ }
18
+ function isExactEvmPayload(obj) {
19
+ return typeof obj === "object" && obj !== null && "signature" in obj && "authorization" in obj;
20
+ }
21
+ function extractNetworkFromLegacy(legacy) {
22
+ if ("network" in legacy && typeof legacy.network === "string") {
23
+ return legacy.network;
24
+ }
25
+ if ("chainId" in legacy && typeof legacy.chainId === "string") {
26
+ const match = legacy.chainId.match(/^eip155:(\d+)$/);
27
+ if (match) {
28
+ const chainId = parseInt(match[1], 10);
29
+ return CHAIN_ID_TO_NETWORK[chainId] || `eip155:${chainId}`;
30
+ }
31
+ }
32
+ if ("chainId" in legacy && typeof legacy.chainId === "number") {
33
+ return CHAIN_ID_TO_NETWORK[legacy.chainId] || `eip155:${legacy.chainId}`;
34
+ }
35
+ return "base";
36
+ }
37
+ function extractSignatureFromLegacy(legacy) {
38
+ if ("v" in legacy && "r" in legacy && "s" in legacy) {
39
+ const v = legacy.v.toString(16).padStart(2, "0");
40
+ const r = legacy.r.startsWith("0x") ? legacy.r.slice(2) : legacy.r;
41
+ const s = legacy.s.startsWith("0x") ? legacy.s.slice(2) : legacy.s;
42
+ return `0x${r}${s}${v}`;
43
+ }
44
+ if ("signature" in legacy && typeof legacy.signature === "object" && legacy.signature !== null) {
45
+ const sig = legacy.signature;
46
+ const v = sig.v.toString(16).padStart(2, "0");
47
+ const r = sig.r.startsWith("0x") ? sig.r.slice(2) : sig.r;
48
+ const s = sig.s.startsWith("0x") ? sig.s.slice(2) : sig.s;
49
+ return `0x${r}${s}${v}`;
50
+ }
51
+ return "0x";
52
+ }
53
+ function convertAmountToAtomic(amount) {
54
+ if (amount.includes(".")) {
55
+ const parts = amount.split(".");
56
+ const whole = parts[0];
57
+ const fractional = parts[1] || "";
58
+ const paddedFractional = fractional.padEnd(6, "0").slice(0, 6);
59
+ return `${whole}${paddedFractional}`;
60
+ }
61
+ return amount;
62
+ }
63
+ function convertNonceToHex(nonce) {
64
+ if (nonce.startsWith("0x") && nonce.length === 66) {
65
+ return nonce;
66
+ }
67
+ const hex = BigInt(nonce).toString(16).padStart(64, "0");
68
+ return `0x${hex}`;
69
+ }
70
+ function extractFromAddress(legacy) {
71
+ return legacy.from;
72
+ }
73
+ function extractToAddress(legacy) {
74
+ if ("to" in legacy && typeof legacy.to === "string") {
75
+ return legacy.to;
76
+ }
77
+ return legacy.from;
78
+ }
79
+ function legacyToPaymentPayload(legacy) {
80
+ if (isPaymentPayload(legacy)) {
81
+ return legacy;
82
+ }
83
+ const network = extractNetworkFromLegacy(legacy);
84
+ const signature = extractSignatureFromLegacy(legacy);
85
+ const value = convertAmountToAtomic(legacy.amount);
86
+ const nonce = convertNonceToHex(legacy.nonce);
87
+ const from = extractFromAddress(legacy);
88
+ const to = extractToAddress(legacy);
89
+ return {
90
+ x402Version: X402_VERSION,
91
+ scheme: "exact",
92
+ network,
93
+ payload: {
94
+ signature,
95
+ authorization: {
96
+ from,
97
+ to,
98
+ value,
99
+ validAfter: "0",
100
+ validBefore: legacy.expiry.toString(),
101
+ nonce
102
+ }
103
+ }
104
+ };
105
+ }
106
+
107
+ // src/encoding/x402.ts
108
+ function safeBase64Encode(str) {
109
+ return Buffer.from(str).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
110
+ }
111
+ function safeBase64Decode(str) {
112
+ const padding = 4 - str.length % 4;
113
+ if (padding !== 4) {
114
+ str += "=".repeat(padding);
115
+ }
116
+ str = str.replace(/-/g, "+").replace(/_/g, "/");
117
+ return Buffer.from(str, "base64").toString("utf-8");
118
+ }
119
+ function encodePayment(payload) {
120
+ let normalizedPayload;
121
+ if (isPaymentPayload(payload)) {
122
+ normalizedPayload = payload;
123
+ } else {
124
+ normalizedPayload = legacyToPaymentPayload(payload);
125
+ }
126
+ const safePayload = JSON.parse(JSON.stringify(normalizedPayload, (_, value) => {
127
+ if (typeof value === "bigint") {
128
+ return value.toString();
129
+ }
130
+ return value;
131
+ }));
132
+ return safeBase64Encode(JSON.stringify(safePayload));
133
+ }
134
+ function decodePayment(encoded) {
135
+ const decoded = safeBase64Decode(encoded);
136
+ const parsed = JSON.parse(decoded);
137
+ if (!isPaymentPayload(parsed)) {
138
+ return legacyToPaymentPayload(parsed);
139
+ }
140
+ return parsed;
141
+ }
142
+ function encodeSettlementResponse(response) {
143
+ const safeResponse = JSON.parse(JSON.stringify(response, (_, value) => {
144
+ if (typeof value === "bigint") {
145
+ return value.toString();
146
+ }
147
+ return value;
148
+ }));
149
+ return safeBase64Encode(JSON.stringify(safeResponse));
150
+ }
151
+ function decodeSettlementResponse(encoded) {
152
+ const decoded = safeBase64Decode(encoded);
153
+ return JSON.parse(decoded);
154
+ }
155
+ function encodeX402Response(response) {
156
+ return safeBase64Encode(JSON.stringify(response));
157
+ }
158
+ function decodeX402Response(encoded) {
159
+ const decoded = safeBase64Decode(encoded);
160
+ return JSON.parse(decoded);
161
+ }
162
+ var X402_HEADERS = {
163
+ PAYMENT: "X-PAYMENT",
164
+ PAYMENT_RESPONSE: "X-PAYMENT-RESPONSE",
165
+ PAYMENT_REQUIRED: "X-PAYMENT-REQUIRED"
166
+ };
167
+ function detectPaymentVersion(headers) {
168
+ if (headers.has("PAYMENT-SIGNATURE")) {
169
+ return 2;
170
+ }
171
+ if (headers.has(X402_HEADERS.PAYMENT)) {
172
+ const encoded = headers.get(X402_HEADERS.PAYMENT);
173
+ if (encoded) {
174
+ try {
175
+ const decoded = decodePayment(encoded);
176
+ if (isPaymentPayload(decoded) && "x402Version" in decoded) {
177
+ return decoded.x402Version;
178
+ }
179
+ return 1;
180
+ } catch {
181
+ return 1;
182
+ }
183
+ }
184
+ return 1;
185
+ }
186
+ return null;
187
+ }
188
+ function extractPaymentFromHeaders(headers) {
189
+ const encoded = headers.get(X402_HEADERS.PAYMENT);
190
+ if (!encoded) return null;
191
+ try {
192
+ return decodePayment(encoded);
193
+ } catch {
194
+ return null;
195
+ }
196
+ }
197
+ function createPaymentRequiredHeaders(requirements) {
198
+ const accepts = Array.isArray(requirements) ? requirements : [requirements];
199
+ const response = {
200
+ x402Version: 1,
201
+ accepts
202
+ };
203
+ return {
204
+ [X402_HEADERS.PAYMENT_REQUIRED]: safeBase64Encode(JSON.stringify(response))
205
+ };
206
+ }
207
+ function createSettlementHeaders(settlement) {
208
+ return {
209
+ [X402_HEADERS.PAYMENT_RESPONSE]: encodeSettlementResponse(settlement)
210
+ };
211
+ }
212
+ function isLegacyV1(payload) {
213
+ return typeof payload === "object" && payload !== null && "v" in payload && typeof payload.v === "number";
214
+ }
215
+ function isLegacyV2(payload) {
216
+ return typeof payload === "object" && payload !== null && "signature" in payload && typeof payload.signature === "object";
217
+ }
218
+
219
+ // src/utils/x402.ts
220
+ import { randomBytes } from "crypto";
221
+ function createNonce() {
222
+ const bytes = randomBytes(32);
223
+ return `0x${bytes.toString("hex")}`;
224
+ }
225
+ function toAtomicUnits(amount, decimals = 6) {
226
+ const parts = amount.split(".");
227
+ const whole = parts[0];
228
+ const fractional = parts[1] || "";
229
+ const paddedFractional = fractional.padEnd(decimals, "0").slice(0, decimals);
230
+ return `${whole}${paddedFractional}`;
231
+ }
232
+ function fromAtomicUnits(amount, decimals = 6) {
233
+ const value = BigInt(amount);
234
+ const divisor = BigInt(10 ** decimals);
235
+ const whole = (value / divisor).toString();
236
+ const fractional = (value % divisor).toString().padStart(decimals, "0").replace(/0+$/, "");
237
+ if (fractional.length === 0) {
238
+ return whole;
239
+ }
240
+ return `${whole}.${fractional}`;
241
+ }
242
+ function parseSignature(signature) {
243
+ const hex = signature.startsWith("0x") ? signature.slice(2) : signature;
244
+ const r = `0x${hex.slice(0, 64)}`;
245
+ const s = `0x${hex.slice(64, 128)}`;
246
+ const v = parseInt(hex.slice(128, 130) || "1c", 16);
247
+ return { r, s, v };
248
+ }
249
+ function combineSignature(r, s, v) {
250
+ const rHex = r.startsWith("0x") ? r.slice(2) : r;
251
+ const sHex = s.startsWith("0x") ? s.slice(2) : s;
252
+ const vHex = v.toString(16).padStart(2, "0");
253
+ return `0x${rHex}${sHex}${vHex}`;
254
+ }
255
+ function caip2ToNetwork(caip2Id) {
256
+ const match = caip2Id.match(/^eip155:(\d+)$/);
257
+ if (!match) {
258
+ return caip2Id;
259
+ }
260
+ const chainId = parseInt(match[1], 10);
261
+ const chainIdToNetwork = {
262
+ 1: "ethereum",
263
+ 8453: "base",
264
+ 84532: "base-sepolia",
265
+ 137: "polygon",
266
+ 42161: "arbitrum",
267
+ 421614: "arbitrum-sepolia",
268
+ 10: "optimism",
269
+ 11155420: "optimism-sepolia",
270
+ 11155111: "ethereum-sepolia"
271
+ };
272
+ return chainIdToNetwork[chainId] || caip2Id;
273
+ }
274
+ function networkToCaip2(network) {
275
+ const networkToChainId = {
276
+ ethereum: 1,
277
+ base: 8453,
278
+ "base-sepolia": 84532,
279
+ polygon: 137,
280
+ arbitrum: 42161,
281
+ "arbitrum-sepolia": 421614,
282
+ optimism: 10,
283
+ "optimism-sepolia": 11155420,
284
+ "ethereum-sepolia": 11155111
285
+ };
286
+ const chainId = networkToChainId[network];
287
+ if (chainId) {
288
+ return `eip155:${chainId}`;
289
+ }
290
+ if (network.startsWith("eip155:")) {
291
+ return network;
292
+ }
293
+ return `eip155:0`;
294
+ }
295
+ function getCurrentTimestamp() {
296
+ return Math.floor(Date.now() / 1e3);
297
+ }
298
+ function isValidAddress(address) {
299
+ return /^0x[a-fA-F0-9]{40}$/.test(address);
300
+ }
301
+ function normalizeAddress(address) {
302
+ return address.toLowerCase();
303
+ }
304
+
1
305
  // src/types/v1.ts
2
306
  var V1_HEADERS = {
3
307
  PAYMENT: "X-PAYMENT",
@@ -9,10 +313,10 @@ function encodePaymentPayload(payload) {
9
313
  function decodePaymentPayload(encoded) {
10
314
  return JSON.parse(Buffer.from(encoded, "base64").toString("utf-8"));
11
315
  }
12
- function encodeSettlementResponse(response) {
316
+ function encodeSettlementResponse2(response) {
13
317
  return Buffer.from(JSON.stringify(response)).toString("base64");
14
318
  }
15
- function decodeSettlementResponse(encoded) {
319
+ function decodeSettlementResponse2(encoded) {
16
320
  return JSON.parse(Buffer.from(encoded, "base64").toString("utf-8"));
17
321
  }
18
322
 
@@ -167,46 +471,6 @@ var ERC20_ABI = [
167
471
  }
168
472
  ];
169
473
 
170
- // src/encoding.ts
171
- var base64Encode = (data) => Buffer.from(JSON.stringify(data)).toString("base64");
172
- var base64Decode = (encoded) => JSON.parse(Buffer.from(encoded, "base64").toString("utf-8"));
173
- var jsonEncode = (data) => JSON.stringify(data);
174
- var jsonDecode = (encoded) => JSON.parse(encoded);
175
- var encodePaymentV1 = (payload) => base64Encode(payload);
176
- var decodePaymentV1 = (encoded) => base64Decode(encoded);
177
- var encodeSettlementV1 = (response) => base64Encode(response);
178
- var decodeSettlementV1 = (encoded) => base64Decode(encoded);
179
- var encodePaymentV2 = (payload) => jsonEncode(payload);
180
- var decodePaymentV2 = (encoded) => jsonDecode(encoded);
181
- var encodeSettlementV2 = (response) => jsonEncode(response);
182
- var decodeSettlementV2 = (encoded) => jsonDecode(encoded);
183
- var detectPaymentVersion = (headers) => {
184
- if (headers.has(V1_HEADERS.PAYMENT)) return 1;
185
- if (headers.has(V2_HEADERS.PAYMENT_SIGNATURE)) return 2;
186
- return null;
187
- };
188
- var decodePayment = (headers) => {
189
- const version = detectPaymentVersion(headers);
190
- if (version === null) {
191
- throw new Error("No valid payment headers found. Expected X-PAYMENT (v1) or PAYMENT-SIGNATURE (v2)");
192
- }
193
- const headerName = version === 1 ? V1_HEADERS.PAYMENT : V2_HEADERS.PAYMENT_SIGNATURE;
194
- const encoded = headers.get(headerName);
195
- if (!encoded) throw new Error(`Header ${headerName} is empty`);
196
- return version === 1 ? decodePaymentV1(encoded) : decodePaymentV2(encoded);
197
- };
198
- var decodeSettlement = (headers) => {
199
- const v1Response = headers.get(V1_HEADERS.PAYMENT_RESPONSE);
200
- if (v1Response) return decodeSettlementV1(v1Response);
201
- const v2Response = headers.get(V2_HEADERS.PAYMENT_RESPONSE);
202
- if (v2Response) return decodeSettlementV2(v2Response);
203
- throw new Error("No valid settlement response headers found. Expected X-PAYMENT-RESPONSE (v1) or PAYMENT-RESPONSE (v2)");
204
- };
205
- var isPaymentV1 = (payload) => "v" in payload && typeof payload.v === "number";
206
- var isPaymentV2 = (payload) => "signature" in payload && typeof payload.signature === "object";
207
- var isSettlementV1 = (response) => "success" in response;
208
- var isSettlementV2 = (response) => "status" in response;
209
-
210
474
  // src/eip712.ts
211
475
  var EIP712_TYPES = {
212
476
  TransferWithAuthorization: [
@@ -664,35 +928,93 @@ var isResolvedNetwork = (value) => {
664
928
  var isResolvedToken = (value) => {
665
929
  return typeof value === "object" && value !== null && "config" in value && "caipAsset" in value;
666
930
  };
931
+
932
+ // src/encoding.ts
933
+ var base64Encode = (data) => Buffer.from(JSON.stringify(data)).toString("base64");
934
+ var base64Decode = (encoded) => JSON.parse(Buffer.from(encoded, "base64").toString("utf-8"));
935
+ var jsonEncode = (data) => JSON.stringify(data);
936
+ var jsonDecode = (encoded) => JSON.parse(encoded);
937
+ var encodePaymentV1 = (payload) => base64Encode(payload);
938
+ var decodePaymentV1 = (encoded) => base64Decode(encoded);
939
+ var encodeSettlementV1 = (response) => base64Encode(response);
940
+ var decodeSettlementV1 = (encoded) => base64Decode(encoded);
941
+ var encodePaymentV2 = (payload) => jsonEncode(payload);
942
+ var decodePaymentV2 = (encoded) => jsonDecode(encoded);
943
+ var encodeSettlementV2 = (response) => jsonEncode(response);
944
+ var decodeSettlementV2 = (encoded) => jsonDecode(encoded);
945
+ var detectPaymentVersion2 = (headers) => {
946
+ if (headers.has(V1_HEADERS.PAYMENT)) return 1;
947
+ if (headers.has(V2_HEADERS.PAYMENT_SIGNATURE)) return 2;
948
+ return null;
949
+ };
950
+ var decodePayment2 = (headers) => {
951
+ const version = detectPaymentVersion2(headers);
952
+ if (version === null) {
953
+ throw new Error("No valid payment headers found. Expected X-PAYMENT (v1) or PAYMENT-SIGNATURE (v2)");
954
+ }
955
+ const headerName = version === 1 ? V1_HEADERS.PAYMENT : V2_HEADERS.PAYMENT_SIGNATURE;
956
+ const encoded = headers.get(headerName);
957
+ if (!encoded) throw new Error(`Header ${headerName} is empty`);
958
+ return version === 1 ? decodePaymentV1(encoded) : decodePaymentV2(encoded);
959
+ };
960
+ var decodeSettlement = (headers) => {
961
+ const v1Response = headers.get(V1_HEADERS.PAYMENT_RESPONSE);
962
+ if (v1Response) return decodeSettlementV1(v1Response);
963
+ const v2Response = headers.get(V2_HEADERS.PAYMENT_RESPONSE);
964
+ if (v2Response) return decodeSettlementV2(v2Response);
965
+ throw new Error("No valid settlement response headers found. Expected X-PAYMENT-RESPONSE (v1) or PAYMENT-RESPONSE (v2)");
966
+ };
967
+ var isPaymentV1 = (payload) => "v" in payload && typeof payload.v === "number";
968
+ var isPaymentV2 = (payload) => "signature" in payload && typeof payload.signature === "object";
969
+ var isSettlementV1 = (response) => "success" in response;
970
+ var isSettlementV2 = (response) => "status" in response;
667
971
  export {
668
972
  EIP712_TYPES,
669
973
  ERC20_ABI,
670
974
  NETWORKS,
975
+ SCHEMES,
671
976
  USDC_DOMAIN,
672
977
  V1_HEADERS,
673
978
  V2_HEADERS,
979
+ X402_HEADERS,
980
+ X402_VERSION,
981
+ caip2ToNetwork,
674
982
  checkFacilitatorSupport,
983
+ combineSignature,
675
984
  createEIP712Domain,
676
985
  createError,
986
+ createNonce,
987
+ createPaymentRequiredHeaders,
988
+ createSettlementHeaders,
677
989
  createTransferWithAuthorization,
678
990
  decodePayment,
991
+ decodePayment2 as decodePaymentLegacy,
679
992
  decodePaymentPayload,
680
993
  decodePaymentV1,
681
994
  decodePaymentV2,
682
- decodeSettlement,
995
+ decodeSettlement as decodeSettlementLegacy,
683
996
  decodeSettlementResponse,
997
+ decodeSettlementResponse2 as decodeSettlementResponseV1,
684
998
  decodeSettlementV1,
685
999
  decodeSettlementV2,
1000
+ decodeX402Response,
686
1001
  detectPaymentVersion,
1002
+ detectPaymentVersion2 as detectPaymentVersionLegacy,
1003
+ encodePayment,
687
1004
  encodePaymentPayload,
688
1005
  encodePaymentV1,
689
1006
  encodePaymentV2,
690
1007
  encodeSettlementResponse,
1008
+ encodeSettlementResponse2 as encodeSettlementResponseV1,
691
1009
  encodeSettlementV1,
692
1010
  encodeSettlementV2,
1011
+ encodeX402Response,
1012
+ extractPaymentFromHeaders,
1013
+ fromAtomicUnits,
693
1014
  getAllCustomTokens,
694
1015
  getAvailableNetworks,
695
1016
  getAvailableTokens,
1017
+ getCurrentTimestamp,
696
1018
  getCustomToken,
697
1019
  getMainnets,
698
1020
  getNetworkByChainId,
@@ -700,7 +1022,7 @@ export {
700
1022
  getPaymentHeaderName,
701
1023
  getPaymentRequiredHeaderName,
702
1024
  getPaymentResponseHeaderName,
703
- getPaymentVersion as getPaymentVersionFromPayload,
1025
+ getPaymentVersion,
704
1026
  getRequirementsVersion,
705
1027
  getSettlementVersion,
706
1028
  getTestnets,
@@ -709,6 +1031,10 @@ export {
709
1031
  isCAIP2ChainId,
710
1032
  isCAIPAssetId,
711
1033
  isCustomToken,
1034
+ isExactEvmPayload,
1035
+ isLegacyV1,
1036
+ isLegacyV2,
1037
+ isPaymentPayload,
712
1038
  isPaymentV1,
713
1039
  isPaymentV2,
714
1040
  isResolvedNetwork,
@@ -718,11 +1044,19 @@ export {
718
1044
  isSettlementV2,
719
1045
  isV1,
720
1046
  isV2,
1047
+ isValidAddress,
721
1048
  isValidationError,
1049
+ legacyToPaymentPayload,
1050
+ networkToCaip2,
1051
+ normalizeAddress,
1052
+ parseSignature,
722
1053
  registerToken,
723
1054
  resolveFacilitator,
724
1055
  resolveNetwork,
725
1056
  resolveToken,
1057
+ safeBase64Decode,
1058
+ safeBase64Encode,
1059
+ toAtomicUnits,
726
1060
  unregisterToken,
727
1061
  validateAcceptConfig,
728
1062
  validatePaymentConfig,
@@ -0,0 +1,136 @@
1
+ /**
2
+ * X402 Protocol Types - Coinbase Compatible Format
3
+ *
4
+ * This module provides types that match the official Coinbase x402 SDK format
5
+ * for full interoperability with their facilitator and extensions.
6
+ */
7
+ export type Address = `0x${string}`;
8
+ export type Hex = `0x${string}`;
9
+ export declare const X402_VERSION: 1;
10
+ export type X402Version = typeof X402_VERSION;
11
+ export declare const SCHEMES: readonly ["exact"];
12
+ export type Scheme = (typeof SCHEMES)[number];
13
+ export type Network = "base" | "base-sepolia" | "ethereum" | "ethereum-sepolia" | "polygon" | "polygon-amoy" | "arbitrum" | "arbitrum-sepolia" | "optimism" | "optimism-sepolia" | string;
14
+ /**
15
+ * EIP-3009 TransferWithAuthorization authorization data
16
+ */
17
+ export interface ExactEvmAuthorization {
18
+ from: Address;
19
+ to: Address;
20
+ value: string;
21
+ validAfter: string;
22
+ validBefore: string;
23
+ nonce: Hex;
24
+ }
25
+ /**
26
+ * Exact EVM payment payload structure
27
+ */
28
+ export interface ExactEvmPayload {
29
+ signature: Hex;
30
+ authorization: ExactEvmAuthorization;
31
+ }
32
+ /**
33
+ * Payment payload - Coinbase compatible format
34
+ * This is the unified format that works with both v1 and v2
35
+ */
36
+ export interface PaymentPayload {
37
+ x402Version: X402Version;
38
+ scheme: Scheme;
39
+ network: Network;
40
+ payload: ExactEvmPayload;
41
+ }
42
+ /**
43
+ * Unsigned payment payload (before signing)
44
+ */
45
+ export interface UnsignedPaymentPayload {
46
+ x402Version: X402Version;
47
+ scheme: Scheme;
48
+ network: Network;
49
+ payload: Omit<ExactEvmPayload, "signature"> & {
50
+ signature: undefined;
51
+ };
52
+ }
53
+ /**
54
+ * Payment requirements for 402 response
55
+ */
56
+ export interface PaymentRequirements {
57
+ scheme: Scheme;
58
+ network: Network;
59
+ maxAmountRequired: string;
60
+ resource: string;
61
+ description: string;
62
+ mimeType: string;
63
+ outputSchema?: Record<string, unknown>;
64
+ payTo: Address;
65
+ maxTimeoutSeconds: number;
66
+ asset: Address;
67
+ extra?: Record<string, unknown>;
68
+ }
69
+ /**
70
+ * Settlement response
71
+ */
72
+ export interface SettlementResponse {
73
+ success: boolean;
74
+ errorReason?: string;
75
+ payer?: Address;
76
+ transaction: string;
77
+ network: Network;
78
+ }
79
+ /**
80
+ * Verify response
81
+ */
82
+ export interface VerifyResponse {
83
+ isValid: boolean;
84
+ invalidReason?: string;
85
+ payer?: Address;
86
+ }
87
+ /**
88
+ * X402 response structure for 402 errors
89
+ */
90
+ export interface X402Response {
91
+ x402Version: X402Version;
92
+ error?: string;
93
+ accepts?: PaymentRequirements[];
94
+ payer?: Address;
95
+ }
96
+ export interface PaymentPayloadV1 {
97
+ from: string;
98
+ to: string;
99
+ amount: string;
100
+ nonce: string;
101
+ expiry: number;
102
+ v: number;
103
+ r: string;
104
+ s: string;
105
+ chainId: number;
106
+ contractAddress: string;
107
+ network: string;
108
+ }
109
+ export interface PaymentPayloadV2 {
110
+ from: Address;
111
+ to: Address | {
112
+ role?: string;
113
+ callback?: string;
114
+ };
115
+ amount: string;
116
+ nonce: string;
117
+ expiry: number;
118
+ signature: {
119
+ v: number;
120
+ r: string;
121
+ s: string;
122
+ };
123
+ chainId: `eip155:${string}`;
124
+ assetId: `eip155:${string}/erc20:${string}`;
125
+ extensions?: Record<string, unknown>;
126
+ }
127
+ export type LegacyPaymentPayload = PaymentPayloadV1 | PaymentPayloadV2;
128
+ /**
129
+ * Type guards
130
+ */
131
+ export declare function isPaymentPayload(obj: unknown): obj is PaymentPayload;
132
+ export declare function isExactEvmPayload(obj: unknown): obj is ExactEvmPayload;
133
+ /**
134
+ * Convert legacy payload to new format
135
+ */
136
+ export declare function legacyToPaymentPayload(legacy: LegacyPaymentPayload | PaymentPayload): PaymentPayload;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * X402 Protocol Utilities - Coinbase Compatible
3
+ *
4
+ * Helper functions for nonce generation, amount conversion, and other utilities.
5
+ */
6
+ import type { Hex, Address } from "../types/x402";
7
+ /**
8
+ * Generate a random 32-byte nonce as hex string
9
+ * Matches Coinbase SDK format: "0x" + 64 hex characters
10
+ */
11
+ export declare function createNonce(): Hex;
12
+ /**
13
+ * Convert human-readable amount to atomic units
14
+ * e.g., "1.5" USDC -> "1500000" (6 decimals)
15
+ */
16
+ export declare function toAtomicUnits(amount: string, decimals?: number): string;
17
+ /**
18
+ * Convert atomic units to human-readable amount
19
+ * e.g., "1500000" -> "1.5" USDC (6 decimals)
20
+ */
21
+ export declare function fromAtomicUnits(amount: string, decimals?: number): string;
22
+ /**
23
+ * Parse signature from hex string to components
24
+ * Returns { r, s, v } where v is 27 or 28
25
+ */
26
+ export declare function parseSignature(signature: Hex): {
27
+ r: Hex;
28
+ s: Hex;
29
+ v: number;
30
+ };
31
+ /**
32
+ * Combine signature components into hex string
33
+ */
34
+ export declare function combineSignature(r: Hex, s: Hex, v: number): Hex;
35
+ /**
36
+ * Convert CAIP-2 chain ID to network name
37
+ * e.g., "eip155:8453" -> "base"
38
+ */
39
+ export declare function caip2ToNetwork(caip2Id: string): string;
40
+ /**
41
+ * Convert network name to CAIP-2 chain ID
42
+ * e.g., "base" -> "eip155:8453"
43
+ */
44
+ export declare function networkToCaip2(network: string): string;
45
+ /**
46
+ * Get current Unix timestamp in seconds
47
+ */
48
+ export declare function getCurrentTimestamp(): number;
49
+ /**
50
+ * Validate Ethereum address
51
+ */
52
+ export declare function isValidAddress(address: string): address is Address;
53
+ /**
54
+ * Normalize address to checksum format (basic lowercase)
55
+ */
56
+ export declare function normalizeAddress(address: string): Address;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Comprehensive unit tests for validation layer
3
+ * Tests all input formats, cross-checks, and error messages
4
+ */
5
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armory-sh/base",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "license": "MIT",
5
5
  "author": "Sawyer Cutler <sawyer@dirtroad.dev>",
6
6
  "type": "module",