@armory-sh/client-web3 0.2.5 → 0.2.9
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/index.d.ts +97 -3
- package/dist/index.js +279 -72
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Web3BaseWalletAccount, Web3BaseWallet } from 'web3-types';
|
|
2
|
-
import { PaymentPayloadV1, PaymentPayloadV2, CustomToken, NetworkConfig, PaymentRequirementsV1, PaymentRequirementsV2, SettlementResponseV1, SettlementResponseV2 } from '@armory-sh/base';
|
|
3
|
-
export { EIP712_TYPES as CORE_EIP712_TYPES, USDC_DOMAIN as CORE_USDC_DOMAIN, NETWORKS, NetworkConfig, PaymentPayload, PaymentPayloadV1, PaymentPayloadV2, PaymentRequirements, PaymentRequirementsV1, PaymentRequirementsV2, SettlementResponse, SettlementResponseV1, SettlementResponseV2, V1_HEADERS, V2_HEADERS, createEIP712Domain as createCoreEIP712Domain, createTransferWithAuthorization as createCoreTransferWithAuthorization, decodePaymentV1, decodePaymentV2, decodeSettlementV1, decodeSettlementV2, encodePaymentV1, encodePaymentV2, encodeSettlementV1, encodeSettlementV2, getMainnets, getNetworkByChainId, getNetworkConfig, getPaymentHeaderName, getPaymentRequiredHeaderName, getPaymentResponseHeaderName, getPaymentVersion, getRequirementsVersion, getSettlementVersion, getTestnets, getTxHash,
|
|
2
|
+
import { PaymentPayloadV1, PaymentPayloadV2, CustomToken, NetworkConfig, PaymentRequirementsV1, PaymentRequirementsV2, SettlementResponseV1, SettlementResponseV2, X402PaymentRequiredV1, PaymentRequiredV2, X402PaymentPayloadV1 } from '@armory-sh/base';
|
|
3
|
+
export { EIP712_TYPES as CORE_EIP712_TYPES, USDC_DOMAIN as CORE_USDC_DOMAIN, NETWORKS, NetworkConfig, PaymentPayload, PaymentPayloadV1, PaymentPayloadV2, PaymentRequirements, PaymentRequirementsV1, PaymentRequirementsV2, SettlementResponse, SettlementResponseV1, SettlementResponseV2, V1_HEADERS, V2_HEADERS, combineSignatureV2, createEIP712Domain as createCoreEIP712Domain, createTransferWithAuthorization as createCoreTransferWithAuthorization, createNonce, decodePaymentV1, decodePaymentV2, decodeSettlementV1, decodeSettlementV2, encodePaymentV1, encodePaymentV2, encodeSettlementV1, encodeSettlementV2, getMainnets, getNetworkByChainId, getNetworkConfig, getPaymentHeaderName, getPaymentRequiredHeaderName, getPaymentResponseHeaderName, getPaymentVersion, getRequirementsVersion, getSettlementVersion, getTestnets, getTxHash, isSettlementSuccessful, isV1, isV2, isX402V1Payload, isX402V1Requirements, isX402V1Settlement, isX402V2Payload, isX402V2Requirements, isX402V2Settlement, parseSignatureV2, validateTransferWithAuthorization as validateCoreTransferWithAuthorization } from '@armory-sh/base';
|
|
4
4
|
|
|
5
5
|
type Web3Account = Web3BaseWalletAccount | Web3BaseWallet<Web3BaseWalletAccount>;
|
|
6
6
|
/** Token configuration - can use pre-configured tokens from @armory-sh/tokens */
|
|
@@ -82,9 +82,103 @@ declare const isV2Settlement: (response: SettlementResponseV1 | SettlementRespon
|
|
|
82
82
|
|
|
83
83
|
declare const createX402Client: (config: Web3ClientConfig) => Web3X402Client;
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Create an x402 transport layer for handling payment-required responses
|
|
87
|
+
*/
|
|
85
88
|
declare const createX402Transport: (options: X402TransportOptions) => X402Transport;
|
|
89
|
+
/**
|
|
90
|
+
* Create a fetch function bound to an x402 transport
|
|
91
|
+
*/
|
|
86
92
|
declare const createFetchWithX402: (transport: X402Transport) => (url: string | Request, init?: RequestInit) => Promise<Response>;
|
|
87
93
|
|
|
94
|
+
/**
|
|
95
|
+
* X402 Protocol Detection and Parsing Functions
|
|
96
|
+
*
|
|
97
|
+
* Handles both x402 V1 and V2 protocol detection and parsing from HTTP responses.
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
type X402Version = 1 | 2;
|
|
101
|
+
interface ParsedPaymentRequired {
|
|
102
|
+
version: X402Version;
|
|
103
|
+
requirements: PaymentRequirementsV1[] | PaymentRequirementsV2[];
|
|
104
|
+
raw: X402PaymentRequiredV1 | PaymentRequiredV2;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Detect x402 protocol version from response headers
|
|
108
|
+
* Returns V2 if PAYMENT-REQUIRED header exists, V1 for X-PAYMENT-REQUIRED
|
|
109
|
+
* Falls back to body detection if no headers present
|
|
110
|
+
*/
|
|
111
|
+
declare const detectX402Version: (response: Response, fallbackVersion?: X402Version) => X402Version;
|
|
112
|
+
/**
|
|
113
|
+
* Detect version from a parsed payment required object
|
|
114
|
+
*/
|
|
115
|
+
declare const detectVersionFromObject: (obj: unknown) => X402Version | null;
|
|
116
|
+
/**
|
|
117
|
+
* Parse PAYMENT-REQUIRED header or response body
|
|
118
|
+
* Handles both V1 (base64 encoded) and V2 (JSON) formats
|
|
119
|
+
*/
|
|
120
|
+
declare const parsePaymentRequired: (response: Response, version?: X402Version) => Promise<ParsedPaymentRequired>;
|
|
121
|
+
/**
|
|
122
|
+
* Parse payment required from header value
|
|
123
|
+
*/
|
|
124
|
+
declare const parsePaymentRequiredFromHeader: (header: string, version: X402Version) => ParsedPaymentRequired;
|
|
125
|
+
/**
|
|
126
|
+
* Parse payment required from response body
|
|
127
|
+
*/
|
|
128
|
+
declare const parsePaymentRequiredFromBody: (body: string, version: X402Version) => ParsedPaymentRequired;
|
|
129
|
+
/**
|
|
130
|
+
* Create x402 V1 payment payload
|
|
131
|
+
*/
|
|
132
|
+
declare const createX402V1Payment: (params: {
|
|
133
|
+
from: string;
|
|
134
|
+
to: string;
|
|
135
|
+
value: string;
|
|
136
|
+
nonce: `0x${string}`;
|
|
137
|
+
validAfter: string;
|
|
138
|
+
validBefore: string;
|
|
139
|
+
signature: `0x${string}`;
|
|
140
|
+
network: string;
|
|
141
|
+
}) => X402PaymentPayloadV1;
|
|
142
|
+
/**
|
|
143
|
+
* Create x402 V2 payment payload
|
|
144
|
+
*/
|
|
145
|
+
declare const createX402V2Payment: (params: {
|
|
146
|
+
from: string;
|
|
147
|
+
to: string;
|
|
148
|
+
value: string;
|
|
149
|
+
nonce: `0x${string}`;
|
|
150
|
+
validAfter: string;
|
|
151
|
+
validBefore: string;
|
|
152
|
+
signature: `0x${string}`;
|
|
153
|
+
accepted: PaymentRequirementsV2;
|
|
154
|
+
resource?: {
|
|
155
|
+
url: string;
|
|
156
|
+
description?: string;
|
|
157
|
+
mimeType?: string;
|
|
158
|
+
};
|
|
159
|
+
}) => PaymentPayloadV2;
|
|
160
|
+
/**
|
|
161
|
+
* Create payment header for request
|
|
162
|
+
*/
|
|
163
|
+
declare const createPaymentHeader: (payload: X402PaymentPayloadV1 | PaymentPayloadV2, version: X402Version) => string;
|
|
164
|
+
/**
|
|
165
|
+
* Get the payment header name for a version
|
|
166
|
+
*/
|
|
167
|
+
declare const getPaymentHeader: (version: X402Version) => string;
|
|
168
|
+
/**
|
|
169
|
+
* Get the payment required header name for a version
|
|
170
|
+
*/
|
|
171
|
+
declare const getPaymentRequiredHeader: (version: X402Version) => string;
|
|
172
|
+
/**
|
|
173
|
+
* Check if response indicates payment is required
|
|
174
|
+
*/
|
|
175
|
+
declare const isPaymentRequiredResponse: (response: Response) => boolean;
|
|
176
|
+
/**
|
|
177
|
+
* Extract requirements for a specific scheme from accepts array
|
|
178
|
+
* Uses type assertion to handle union types
|
|
179
|
+
*/
|
|
180
|
+
declare const selectSchemeRequirements: (requirements: PaymentRequirementsV1[] | PaymentRequirementsV2[], scheme?: string) => PaymentRequirementsV1 | PaymentRequirementsV2 | undefined;
|
|
181
|
+
|
|
88
182
|
declare const EIP712_TYPES: {
|
|
89
183
|
readonly EIP712Domain: readonly [{
|
|
90
184
|
readonly name: "name";
|
|
@@ -144,4 +238,4 @@ declare const signWithPrivateKey: (_privateKey: string, _domain: Web3EIP712Domai
|
|
|
144
238
|
s: string;
|
|
145
239
|
}>;
|
|
146
240
|
|
|
147
|
-
export { EIP712_TYPES, type PaymentSignOptions, type PaymentSignatureResult, type Token, USDC_DOMAIN, type Web3Account, type Web3ClientConfig, type Web3EIP712Domain, type Web3TransferWithAuthorization, type Web3X402Client, type X402RequestContext, type X402Transport, type X402TransportOptions, adjustVForChainId, concatenateSignature, createEIP712Domain, createFetchWithX402, createTransferWithAuthorization, createX402Client, createX402Transport, isV1Requirements, isV1Settlement, isV2Requirements, isV2Settlement, parseSignature, signTypedData, signWithPrivateKey, validateTransferWithAuthorization };
|
|
241
|
+
export { EIP712_TYPES, type ParsedPaymentRequired, type PaymentSignOptions, type PaymentSignatureResult, type Token, USDC_DOMAIN, type Web3Account, type Web3ClientConfig, type Web3EIP712Domain, type Web3TransferWithAuthorization, type Web3X402Client, type X402RequestContext, type X402Transport, type X402TransportOptions, type X402Version, adjustVForChainId, concatenateSignature, createEIP712Domain, createFetchWithX402, createPaymentHeader, createTransferWithAuthorization, createX402Client, createX402Transport, createX402V1Payment, createX402V2Payment, detectVersionFromObject, detectX402Version, getPaymentHeader, getPaymentRequiredHeader, isPaymentRequiredResponse, isV1Requirements, isV1Settlement, isV2Requirements, isV2Settlement, parsePaymentRequired, parsePaymentRequiredFromBody, parsePaymentRequiredFromHeader, parseSignature, selectSchemeRequirements, signTypedData, signWithPrivateKey, validateTransferWithAuthorization };
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,10 @@ import { Web3 } from "web3";
|
|
|
3
3
|
import {
|
|
4
4
|
getNetworkConfig,
|
|
5
5
|
encodePaymentV1,
|
|
6
|
-
encodePaymentV2
|
|
6
|
+
encodePaymentV2,
|
|
7
|
+
isX402V1Requirements,
|
|
8
|
+
isX402V2Requirements,
|
|
9
|
+
combineSignatureV2
|
|
7
10
|
} from "@armory-sh/base";
|
|
8
11
|
|
|
9
12
|
// src/eip3009.ts
|
|
@@ -118,6 +121,150 @@ var signWithPrivateKey = async (_privateKey, _domain, _message) => {
|
|
|
118
121
|
);
|
|
119
122
|
};
|
|
120
123
|
|
|
124
|
+
// src/protocol.ts
|
|
125
|
+
import {
|
|
126
|
+
V1_HEADERS,
|
|
127
|
+
V2_HEADERS,
|
|
128
|
+
decodeX402PaymentRequiredV1,
|
|
129
|
+
isX402V1PaymentRequired,
|
|
130
|
+
isX402V2PaymentRequired,
|
|
131
|
+
getPaymentRequiredHeaderName,
|
|
132
|
+
getPaymentHeaderName
|
|
133
|
+
} from "@armory-sh/base";
|
|
134
|
+
var detectX402Version = (response, fallbackVersion = 2) => {
|
|
135
|
+
if (response.headers.has(V2_HEADERS.PAYMENT_REQUIRED)) {
|
|
136
|
+
return 2;
|
|
137
|
+
}
|
|
138
|
+
if (response.headers.has(V1_HEADERS.PAYMENT_REQUIRED)) {
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
141
|
+
if (response.headers.has("Payment-Required")) {
|
|
142
|
+
return 2;
|
|
143
|
+
}
|
|
144
|
+
return fallbackVersion;
|
|
145
|
+
};
|
|
146
|
+
var detectVersionFromObject = (obj) => {
|
|
147
|
+
if (isX402V2PaymentRequired(obj)) return 2;
|
|
148
|
+
if (isX402V1PaymentRequired(obj)) return 1;
|
|
149
|
+
return null;
|
|
150
|
+
};
|
|
151
|
+
var parsePaymentRequired = async (response, version) => {
|
|
152
|
+
const detectedVersion = version ?? detectX402Version(response);
|
|
153
|
+
const headerName = getPaymentRequiredHeaderName(detectedVersion);
|
|
154
|
+
const header = response.headers.get(headerName);
|
|
155
|
+
if (header) {
|
|
156
|
+
return parsePaymentRequiredFromHeader(header, detectedVersion);
|
|
157
|
+
}
|
|
158
|
+
const body = await response.clone().text();
|
|
159
|
+
return parsePaymentRequiredFromBody(body, detectedVersion);
|
|
160
|
+
};
|
|
161
|
+
var parsePaymentRequiredFromHeader = (header, version) => {
|
|
162
|
+
if (version === 1) {
|
|
163
|
+
const decoded = decodeX402PaymentRequiredV1(header);
|
|
164
|
+
return {
|
|
165
|
+
version: 1,
|
|
166
|
+
requirements: decoded.accepts,
|
|
167
|
+
raw: decoded
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
let parsed;
|
|
171
|
+
try {
|
|
172
|
+
parsed = JSON.parse(header);
|
|
173
|
+
} catch {
|
|
174
|
+
const decoded = Buffer.from(header, "base64").toString("utf-8");
|
|
175
|
+
parsed = JSON.parse(decoded);
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
version: 2,
|
|
179
|
+
requirements: parsed.accepts,
|
|
180
|
+
raw: parsed
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
var parsePaymentRequiredFromBody = (body, version) => {
|
|
184
|
+
let parsed;
|
|
185
|
+
try {
|
|
186
|
+
parsed = JSON.parse(body);
|
|
187
|
+
} catch {
|
|
188
|
+
const decoded = Buffer.from(body, "base64").toString("utf-8");
|
|
189
|
+
parsed = JSON.parse(decoded);
|
|
190
|
+
}
|
|
191
|
+
const detectedVersion = detectVersionFromObject(parsed) ?? version;
|
|
192
|
+
if (detectedVersion === 1 && isX402V1PaymentRequired(parsed)) {
|
|
193
|
+
return {
|
|
194
|
+
version: 1,
|
|
195
|
+
requirements: parsed.accepts,
|
|
196
|
+
raw: parsed
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
if (detectedVersion === 2 && isX402V2PaymentRequired(parsed)) {
|
|
200
|
+
const v2Parsed = parsed;
|
|
201
|
+
return {
|
|
202
|
+
version: 2,
|
|
203
|
+
requirements: v2Parsed.accepts,
|
|
204
|
+
raw: v2Parsed
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
throw new Error("Unable to parse payment required response");
|
|
208
|
+
};
|
|
209
|
+
var createX402V1Payment = (params) => {
|
|
210
|
+
const authorization = {
|
|
211
|
+
from: params.from,
|
|
212
|
+
to: params.to,
|
|
213
|
+
value: params.value,
|
|
214
|
+
validAfter: params.validAfter,
|
|
215
|
+
validBefore: params.validBefore,
|
|
216
|
+
nonce: params.nonce
|
|
217
|
+
};
|
|
218
|
+
return {
|
|
219
|
+
x402Version: 1,
|
|
220
|
+
scheme: "exact",
|
|
221
|
+
network: params.network,
|
|
222
|
+
payload: {
|
|
223
|
+
signature: params.signature,
|
|
224
|
+
authorization
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
var createX402V2Payment = (params) => {
|
|
229
|
+
const authorization = {
|
|
230
|
+
from: params.from,
|
|
231
|
+
to: params.to,
|
|
232
|
+
value: params.value,
|
|
233
|
+
validAfter: params.validAfter,
|
|
234
|
+
validBefore: params.validBefore,
|
|
235
|
+
nonce: params.nonce
|
|
236
|
+
};
|
|
237
|
+
return {
|
|
238
|
+
x402Version: 2,
|
|
239
|
+
resource: params.resource,
|
|
240
|
+
accepted: params.accepted,
|
|
241
|
+
payload: {
|
|
242
|
+
signature: params.signature,
|
|
243
|
+
authorization
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
var createPaymentHeader = (payload, version) => {
|
|
248
|
+
const headerName = getPaymentHeaderName(version);
|
|
249
|
+
const encoded = Buffer.from(JSON.stringify(payload)).toString("base64");
|
|
250
|
+
return encoded;
|
|
251
|
+
};
|
|
252
|
+
var getPaymentHeader = (version) => {
|
|
253
|
+
return getPaymentHeaderName(version);
|
|
254
|
+
};
|
|
255
|
+
var getPaymentRequiredHeader = (version) => {
|
|
256
|
+
return getPaymentRequiredHeaderName(version);
|
|
257
|
+
};
|
|
258
|
+
var isPaymentRequiredResponse = (response) => {
|
|
259
|
+
if (response.status === 402) return true;
|
|
260
|
+
return response.headers.has(V1_HEADERS.PAYMENT_REQUIRED) || response.headers.has(V2_HEADERS.PAYMENT_REQUIRED) || response.headers.has("Payment-Required");
|
|
261
|
+
};
|
|
262
|
+
var selectSchemeRequirements = (requirements, scheme = "exact") => {
|
|
263
|
+
return requirements.find(
|
|
264
|
+
(r) => "scheme" in r && r.scheme === scheme
|
|
265
|
+
);
|
|
266
|
+
};
|
|
267
|
+
|
|
121
268
|
// src/client.ts
|
|
122
269
|
var DEFAULT_EXPIRY_SECONDS = 3600;
|
|
123
270
|
var DEFAULT_VALID_AFTER = 0;
|
|
@@ -167,7 +314,7 @@ var signTypedDataWrapper = async (account, domain, message) => {
|
|
|
167
314
|
const sig = await acc.signTypedData(domain, message);
|
|
168
315
|
return parseSignature2(sig);
|
|
169
316
|
}
|
|
170
|
-
const
|
|
317
|
+
const getAddressLocal = () => {
|
|
171
318
|
if ("address" in account) return account.address;
|
|
172
319
|
if (Array.isArray(account) && account[0]) return account[0].address;
|
|
173
320
|
throw new Error("Unable to get address from account");
|
|
@@ -175,7 +322,7 @@ var signTypedDataWrapper = async (account, domain, message) => {
|
|
|
175
322
|
if (typeof acc.request === "function") {
|
|
176
323
|
const sig = await acc.request({
|
|
177
324
|
method: "eth_signTypedData_v4",
|
|
178
|
-
params: [
|
|
325
|
+
params: [getAddressLocal(), JSON.stringify({ domain, message })]
|
|
179
326
|
});
|
|
180
327
|
return parseSignature2(sig);
|
|
181
328
|
}
|
|
@@ -197,7 +344,7 @@ var signPaymentV1 = async (state, params) => {
|
|
|
197
344
|
nonce: `0x${nonce}`
|
|
198
345
|
});
|
|
199
346
|
const signature = await signTypedDataWrapper(state.account, domain, message);
|
|
200
|
-
const
|
|
347
|
+
const legacyPayload = {
|
|
201
348
|
from,
|
|
202
349
|
to,
|
|
203
350
|
amount,
|
|
@@ -210,10 +357,10 @@ var signPaymentV1 = async (state, params) => {
|
|
|
210
357
|
contractAddress: network.usdcAddress,
|
|
211
358
|
network: network.name.toLowerCase().replace(" ", "-")
|
|
212
359
|
};
|
|
213
|
-
return { v: signature.v, r: signature.r, s: signature.s, payload };
|
|
360
|
+
return { v: signature.v, r: signature.r, s: signature.s, payload: legacyPayload };
|
|
214
361
|
};
|
|
215
362
|
var signPaymentV2 = async (state, params) => {
|
|
216
|
-
const { from, to, amount, nonce, expiry } = params;
|
|
363
|
+
const { from, to, amount, nonce, expiry, accepted } = params;
|
|
217
364
|
const { network, domainName, domainVersion } = state;
|
|
218
365
|
const domain = createEIP712Domain(network.chainId, network.usdcAddress, domainName, domainVersion);
|
|
219
366
|
const message = createTransferWithAuthorization({
|
|
@@ -225,21 +372,33 @@ var signPaymentV2 = async (state, params) => {
|
|
|
225
372
|
nonce: `0x${nonce}`
|
|
226
373
|
});
|
|
227
374
|
const signature = await signTypedDataWrapper(state.account, domain, message);
|
|
228
|
-
const
|
|
375
|
+
const combinedSig = combineSignatureV2(signature.v, signature.r, signature.s);
|
|
376
|
+
const defaultAccepted = accepted ?? {
|
|
377
|
+
scheme: "exact",
|
|
378
|
+
network: network.caip2Id,
|
|
379
|
+
amount,
|
|
380
|
+
asset: network.usdcAddress,
|
|
381
|
+
payTo: to,
|
|
382
|
+
maxTimeoutSeconds: expiry - Math.floor(Date.now() / 1e3)
|
|
383
|
+
};
|
|
384
|
+
const x402Payload = createX402V2Payment({
|
|
229
385
|
from,
|
|
230
386
|
to,
|
|
231
|
-
amount,
|
|
232
|
-
nonce,
|
|
233
|
-
|
|
387
|
+
value: amount,
|
|
388
|
+
nonce: `0x${nonce.padStart(64, "0")}`,
|
|
389
|
+
validAfter: "0x0",
|
|
390
|
+
validBefore: `0x${expiry.toString(16)}`,
|
|
391
|
+
signature: combinedSig,
|
|
392
|
+
accepted: defaultAccepted
|
|
393
|
+
});
|
|
394
|
+
return {
|
|
234
395
|
signature: {
|
|
235
396
|
v: signature.v,
|
|
236
397
|
r: signature.r,
|
|
237
398
|
s: signature.s
|
|
238
399
|
},
|
|
239
|
-
|
|
240
|
-
assetId: network.caipAssetId
|
|
400
|
+
payload: x402Payload
|
|
241
401
|
};
|
|
242
|
-
return { signature: payload.signature, payload };
|
|
243
402
|
};
|
|
244
403
|
var createX402Client = (config) => {
|
|
245
404
|
const state = createClientState(config);
|
|
@@ -276,28 +435,55 @@ var createX402Client = (config) => {
|
|
|
276
435
|
return headers;
|
|
277
436
|
},
|
|
278
437
|
handlePaymentRequired: async (requirements) => {
|
|
279
|
-
|
|
438
|
+
const version = detectVersionFromRequirements(requirements);
|
|
439
|
+
if (version === 1) {
|
|
280
440
|
const req2 = requirements;
|
|
441
|
+
if (isX402V1Requirements(req2)) {
|
|
442
|
+
const x402Req = req2;
|
|
443
|
+
return signPayment({
|
|
444
|
+
amount: x402Req.maxAmountRequired,
|
|
445
|
+
to: x402Req.payTo
|
|
446
|
+
}, state);
|
|
447
|
+
}
|
|
448
|
+
const legacyReq = req2;
|
|
281
449
|
return signPayment({
|
|
282
|
-
amount:
|
|
283
|
-
to:
|
|
284
|
-
expiry:
|
|
450
|
+
amount: legacyReq.amount,
|
|
451
|
+
to: legacyReq.payTo,
|
|
452
|
+
expiry: legacyReq.expiry
|
|
285
453
|
}, state);
|
|
286
454
|
}
|
|
287
455
|
const req = requirements;
|
|
288
|
-
const to = typeof req.
|
|
289
|
-
|
|
290
|
-
|
|
456
|
+
const to = typeof req.payTo === "string" ? req.payTo : "0x0000000000000000000000000000000000000000";
|
|
457
|
+
const from = getAddress(state.account);
|
|
458
|
+
return signPaymentV2(state, {
|
|
459
|
+
from,
|
|
291
460
|
to,
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
461
|
+
amount: req.amount,
|
|
462
|
+
nonce: crypto.randomUUID(),
|
|
463
|
+
expiry: Math.floor(Date.now() / 1e3) + DEFAULT_EXPIRY_SECONDS,
|
|
464
|
+
accepted: req
|
|
465
|
+
});
|
|
295
466
|
},
|
|
296
467
|
verifySettlement: (response) => {
|
|
297
|
-
|
|
468
|
+
if ("success" in response) {
|
|
469
|
+
return response.success;
|
|
470
|
+
}
|
|
471
|
+
return false;
|
|
298
472
|
}
|
|
299
473
|
};
|
|
300
474
|
};
|
|
475
|
+
var detectVersionFromRequirements = (requirements) => {
|
|
476
|
+
if (isX402V2Requirements(requirements)) {
|
|
477
|
+
return 2;
|
|
478
|
+
}
|
|
479
|
+
if (isX402V1Requirements(requirements)) {
|
|
480
|
+
return 1;
|
|
481
|
+
}
|
|
482
|
+
if ("contractAddress" in requirements) {
|
|
483
|
+
return 1;
|
|
484
|
+
}
|
|
485
|
+
return 2;
|
|
486
|
+
};
|
|
301
487
|
var signPayment = async (options, state) => {
|
|
302
488
|
const from = getAddress(state.account);
|
|
303
489
|
if (state.version === 1) {
|
|
@@ -320,50 +506,47 @@ var signPayment = async (options, state) => {
|
|
|
320
506
|
};
|
|
321
507
|
|
|
322
508
|
// src/transport.ts
|
|
323
|
-
import {
|
|
509
|
+
import {
|
|
510
|
+
V1_HEADERS as V1_HEADERS2,
|
|
511
|
+
V2_HEADERS as V2_HEADERS2,
|
|
512
|
+
encodePaymentV1 as encodePaymentV12,
|
|
513
|
+
encodePaymentV2 as encodePaymentV22,
|
|
514
|
+
isX402V1Requirements as isX402V1Requirements2,
|
|
515
|
+
isX402V2Requirements as isX402V2Requirements2
|
|
516
|
+
} from "@armory-sh/base";
|
|
324
517
|
var DEFAULT_MAX_RETRIES = 3;
|
|
325
|
-
var detectProtocolVersion = (response, client) => {
|
|
326
|
-
if (response.headers.has(V2_HEADERS.PAYMENT_REQUIRED) || response.headers.has("PAYMENT-REQUIRED")) {
|
|
327
|
-
return 2;
|
|
328
|
-
}
|
|
329
|
-
if (response.headers.has("X-PAYMENT-REQUIRED")) {
|
|
330
|
-
return 1;
|
|
331
|
-
}
|
|
332
|
-
return client.getVersion();
|
|
333
|
-
};
|
|
334
|
-
var parsePaymentRequirements = async (response, version) => {
|
|
335
|
-
if (version === 2) {
|
|
336
|
-
const header = response.headers.get(V2_HEADERS.PAYMENT_REQUIRED);
|
|
337
|
-
if (header) return JSON.parse(header);
|
|
338
|
-
} else {
|
|
339
|
-
const header = response.headers.get("X-PAYMENT-REQUIRED");
|
|
340
|
-
if (header) {
|
|
341
|
-
const json = Buffer.from(header, "base64").toString("utf-8");
|
|
342
|
-
return JSON.parse(json);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
return JSON.parse(await response.clone().text());
|
|
346
|
-
};
|
|
347
518
|
var createPaymentHeaders = (payload, version) => {
|
|
348
519
|
const headers = new Headers();
|
|
349
520
|
if (version === 1) {
|
|
350
|
-
headers.set(
|
|
521
|
+
headers.set(V1_HEADERS2.PAYMENT, encodePaymentV12(payload));
|
|
351
522
|
} else {
|
|
352
|
-
headers.set(
|
|
523
|
+
headers.set(V2_HEADERS2.PAYMENT_SIGNATURE, encodePaymentV22(payload));
|
|
353
524
|
}
|
|
354
525
|
return headers;
|
|
355
526
|
};
|
|
356
|
-
var isPaymentRelatedError = (error) => error.message.includes("402") || error.message.includes("payment") || error.message.includes("signature");
|
|
527
|
+
var isPaymentRelatedError = (error) => error.message.includes("402") || error.message.includes("payment") || error.message.includes("signature") || error.message.includes("Payment");
|
|
357
528
|
var backoff = (attempt) => {
|
|
358
|
-
const
|
|
359
|
-
|
|
529
|
+
const baseDelay = Math.min(1e3 * Math.pow(2, attempt - 1), 1e4);
|
|
530
|
+
const jitter = Math.random() * 100;
|
|
531
|
+
return new Promise((resolve) => setTimeout(resolve, baseDelay + jitter));
|
|
360
532
|
};
|
|
361
533
|
var handlePaymentRequired = async (response, client) => {
|
|
362
|
-
const version =
|
|
363
|
-
const
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
534
|
+
const version = detectX402Version(response, client.getVersion());
|
|
535
|
+
const parsed = await parsePaymentRequired(response, version);
|
|
536
|
+
const selectedRequirements = selectSchemeRequirements(parsed.requirements, "exact");
|
|
537
|
+
if (!selectedRequirements) {
|
|
538
|
+
throw new Error("No supported payment scheme found in requirements");
|
|
539
|
+
}
|
|
540
|
+
let result;
|
|
541
|
+
if (version === 1 && isX402V1Requirements2(selectedRequirements)) {
|
|
542
|
+
const req = selectedRequirements;
|
|
543
|
+
result = await client.handlePaymentRequired(req);
|
|
544
|
+
} else if (version === 2 && isX402V2Requirements2(selectedRequirements)) {
|
|
545
|
+
const req = selectedRequirements;
|
|
546
|
+
result = await client.handlePaymentRequired(req);
|
|
547
|
+
} else {
|
|
548
|
+
result = await client.handlePaymentRequired(selectedRequirements);
|
|
549
|
+
}
|
|
367
550
|
return createPaymentHeaders(result.payload, version);
|
|
368
551
|
};
|
|
369
552
|
var mergePaymentHeaders = (init = {}, paymentHeaders) => {
|
|
@@ -386,7 +569,7 @@ var createX402Transport = (options) => {
|
|
|
386
569
|
attempt++;
|
|
387
570
|
try {
|
|
388
571
|
const response = await fetch(url, init);
|
|
389
|
-
if (response
|
|
572
|
+
if (isPaymentRequiredResponse(response) && autoSign) {
|
|
390
573
|
const paymentHeaders = await handlePaymentRequired(response, client);
|
|
391
574
|
const newInit = mergePaymentHeaders(init, paymentHeaders);
|
|
392
575
|
return await fetch(url, newInit);
|
|
@@ -416,15 +599,12 @@ var isV2Settlement = (response) => "status" in response;
|
|
|
416
599
|
|
|
417
600
|
// src/index.ts
|
|
418
601
|
import {
|
|
419
|
-
V1_HEADERS as
|
|
602
|
+
V1_HEADERS as V1_HEADERS3,
|
|
603
|
+
V2_HEADERS as V2_HEADERS3,
|
|
420
604
|
encodePaymentV1 as encodePaymentV13,
|
|
421
605
|
decodePaymentV1,
|
|
422
606
|
encodeSettlementV1,
|
|
423
607
|
decodeSettlementV1,
|
|
424
|
-
V2_HEADERS as V2_HEADERS2,
|
|
425
|
-
isCAIP2ChainId,
|
|
426
|
-
isCAIPAssetId,
|
|
427
|
-
isAddress,
|
|
428
608
|
encodePaymentV2 as encodePaymentV23,
|
|
429
609
|
decodePaymentV2,
|
|
430
610
|
encodeSettlementV2,
|
|
@@ -434,9 +614,9 @@ import {
|
|
|
434
614
|
getPaymentVersion,
|
|
435
615
|
getRequirementsVersion,
|
|
436
616
|
getSettlementVersion,
|
|
437
|
-
getPaymentHeaderName,
|
|
617
|
+
getPaymentHeaderName as getPaymentHeaderName2,
|
|
438
618
|
getPaymentResponseHeaderName,
|
|
439
|
-
getPaymentRequiredHeaderName,
|
|
619
|
+
getPaymentRequiredHeaderName as getPaymentRequiredHeaderName2,
|
|
440
620
|
isSettlementSuccessful,
|
|
441
621
|
getTxHash,
|
|
442
622
|
NETWORKS,
|
|
@@ -444,6 +624,15 @@ import {
|
|
|
444
624
|
getNetworkByChainId,
|
|
445
625
|
getMainnets,
|
|
446
626
|
getTestnets,
|
|
627
|
+
isX402V1Payload,
|
|
628
|
+
isX402V2Payload,
|
|
629
|
+
isX402V1Requirements as isX402V1Requirements3,
|
|
630
|
+
isX402V2Requirements as isX402V2Requirements3,
|
|
631
|
+
isX402V1Settlement,
|
|
632
|
+
isX402V2Settlement,
|
|
633
|
+
combineSignatureV2 as combineSignatureV22,
|
|
634
|
+
parseSignatureV2,
|
|
635
|
+
createNonce as createNonce2,
|
|
447
636
|
EIP712_TYPES as EIP712_TYPES2,
|
|
448
637
|
USDC_DOMAIN as USDC_DOMAIN2,
|
|
449
638
|
createEIP712Domain as createEIP712Domain2,
|
|
@@ -456,21 +645,28 @@ export {
|
|
|
456
645
|
EIP712_TYPES,
|
|
457
646
|
NETWORKS,
|
|
458
647
|
USDC_DOMAIN,
|
|
459
|
-
|
|
460
|
-
|
|
648
|
+
V1_HEADERS3 as V1_HEADERS,
|
|
649
|
+
V2_HEADERS3 as V2_HEADERS,
|
|
461
650
|
adjustVForChainId,
|
|
651
|
+
combineSignatureV22 as combineSignatureV2,
|
|
462
652
|
concatenateSignature,
|
|
463
653
|
createEIP712Domain2 as createCoreEIP712Domain,
|
|
464
654
|
createTransferWithAuthorization2 as createCoreTransferWithAuthorization,
|
|
465
655
|
createEIP712Domain,
|
|
466
656
|
createFetchWithX402,
|
|
657
|
+
createNonce2 as createNonce,
|
|
658
|
+
createPaymentHeader,
|
|
467
659
|
createTransferWithAuthorization,
|
|
468
660
|
createX402Client,
|
|
469
661
|
createX402Transport,
|
|
662
|
+
createX402V1Payment,
|
|
663
|
+
createX402V2Payment,
|
|
470
664
|
decodePaymentV1,
|
|
471
665
|
decodePaymentV2,
|
|
472
666
|
decodeSettlementV1,
|
|
473
667
|
decodeSettlementV2,
|
|
668
|
+
detectVersionFromObject,
|
|
669
|
+
detectX402Version,
|
|
474
670
|
encodePaymentV13 as encodePaymentV1,
|
|
475
671
|
encodePaymentV23 as encodePaymentV2,
|
|
476
672
|
encodeSettlementV1,
|
|
@@ -478,17 +674,17 @@ export {
|
|
|
478
674
|
getMainnets,
|
|
479
675
|
getNetworkByChainId,
|
|
480
676
|
getNetworkConfig2 as getNetworkConfig,
|
|
481
|
-
|
|
482
|
-
|
|
677
|
+
getPaymentHeader,
|
|
678
|
+
getPaymentHeaderName2 as getPaymentHeaderName,
|
|
679
|
+
getPaymentRequiredHeader,
|
|
680
|
+
getPaymentRequiredHeaderName2 as getPaymentRequiredHeaderName,
|
|
483
681
|
getPaymentResponseHeaderName,
|
|
484
682
|
getPaymentVersion,
|
|
485
683
|
getRequirementsVersion,
|
|
486
684
|
getSettlementVersion,
|
|
487
685
|
getTestnets,
|
|
488
686
|
getTxHash,
|
|
489
|
-
|
|
490
|
-
isCAIP2ChainId,
|
|
491
|
-
isCAIPAssetId,
|
|
687
|
+
isPaymentRequiredResponse,
|
|
492
688
|
isSettlementSuccessful,
|
|
493
689
|
isV1,
|
|
494
690
|
isV1Requirements,
|
|
@@ -496,7 +692,18 @@ export {
|
|
|
496
692
|
isV2,
|
|
497
693
|
isV2Requirements,
|
|
498
694
|
isV2Settlement,
|
|
695
|
+
isX402V1Payload,
|
|
696
|
+
isX402V1Requirements3 as isX402V1Requirements,
|
|
697
|
+
isX402V1Settlement,
|
|
698
|
+
isX402V2Payload,
|
|
699
|
+
isX402V2Requirements3 as isX402V2Requirements,
|
|
700
|
+
isX402V2Settlement,
|
|
701
|
+
parsePaymentRequired,
|
|
702
|
+
parsePaymentRequiredFromBody,
|
|
703
|
+
parsePaymentRequiredFromHeader,
|
|
499
704
|
parseSignature,
|
|
705
|
+
parseSignatureV2,
|
|
706
|
+
selectSchemeRequirements,
|
|
500
707
|
signTypedData,
|
|
501
708
|
signWithPrivateKey,
|
|
502
709
|
validateTransferWithAuthorization2 as validateCoreTransferWithAuthorization,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@armory-sh/client-web3",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Sawyer Cutler <sawyer@dirtroad.dev>",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"directory": "packages/client-web3"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@armory-sh/base": "^0.2.
|
|
30
|
+
"@armory-sh/base": "^0.2.12",
|
|
31
31
|
"web3": "4.16.0",
|
|
32
32
|
"web3-types": "1.10.0"
|
|
33
33
|
},
|