@4mica/sdk 0.5.5 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -4
- package/dist/abi/core4mica.d.ts +0 -28
- package/dist/abi/core4mica.js +0 -36
- package/dist/abi/erc20.d.ts +10 -0
- package/dist/abi/erc20.js +13 -0
- package/dist/bls.js +13 -2
- package/dist/client/index.d.ts +48 -1
- package/dist/client/index.js +46 -0
- package/dist/client/recipient.d.ts +108 -4
- package/dist/client/recipient.js +129 -6
- package/dist/client/user.d.ts +78 -2
- package/dist/client/user.js +83 -28
- package/dist/config.d.ts +50 -0
- package/dist/config.js +42 -1
- package/dist/contract.d.ts +9 -0
- package/dist/contract.js +64 -24
- package/dist/errors.d.ts +15 -0
- package/dist/errors.js +15 -0
- package/dist/guarantee.d.ts +22 -0
- package/dist/guarantee.js +209 -29
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/models.d.ts +108 -0
- package/dist/models.js +120 -1
- package/dist/payment.d.ts +66 -4
- package/dist/payment.js +39 -5
- package/dist/rpc.d.ts +2 -2
- package/dist/rpc.js +5 -2
- package/dist/signing.d.ts +89 -12
- package/dist/signing.js +109 -30
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +13 -5
- package/dist/validation.d.ts +32 -0
- package/dist/validation.js +82 -0
- package/dist/x402/index.d.ts +70 -2
- package/dist/x402/index.js +101 -1
- package/dist/x402/models.d.ts +8 -2
- package/package.json +3 -1
package/dist/signing.js
CHANGED
|
@@ -1,43 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PaymentSigner = exports.GUARANTEE_EIP712_TYPES = exports.
|
|
3
|
+
exports.PaymentSigner = exports.GUARANTEE_EIP712_TYPES_V2 = exports.GUARANTEE_EIP712_TYPES = exports.computeValidationRequestHash = exports.computeValidationSubjectHash = void 0;
|
|
4
4
|
exports.validateGuaranteeTypedData = validateGuaranteeTypedData;
|
|
5
5
|
exports.validateGuaranteeSigningContext = validateGuaranteeSigningContext;
|
|
6
6
|
exports.buildGuaranteeTypedData = buildGuaranteeTypedData;
|
|
7
|
+
exports.buildGuaranteeTypedDataV2 = buildGuaranteeTypedDataV2;
|
|
8
|
+
exports.encodeGuaranteeEip191V2 = encodeGuaranteeEip191V2;
|
|
7
9
|
exports.encodeGuaranteeEip191 = encodeGuaranteeEip191;
|
|
8
10
|
const viem_1 = require("viem");
|
|
11
|
+
var validation_1 = require("./validation");
|
|
12
|
+
Object.defineProperty(exports, "computeValidationSubjectHash", { enumerable: true, get: function () { return validation_1.computeValidationSubjectHash; } });
|
|
13
|
+
Object.defineProperty(exports, "computeValidationRequestHash", { enumerable: true, get: function () { return validation_1.computeValidationRequestHash; } });
|
|
9
14
|
const errors_1 = require("./errors");
|
|
10
15
|
const models_1 = require("./models");
|
|
11
16
|
const utils_1 = require("./utils");
|
|
12
17
|
const serde_1 = require("./serde");
|
|
13
|
-
class CorePublicParameters {
|
|
14
|
-
publicKey;
|
|
15
|
-
contractAddress;
|
|
16
|
-
ethereumHttpRpcUrl;
|
|
17
|
-
eip712Name;
|
|
18
|
-
eip712Version;
|
|
19
|
-
chainId;
|
|
20
|
-
constructor(publicKey, contractAddress, ethereumHttpRpcUrl, eip712Name, eip712Version, chainId) {
|
|
21
|
-
this.publicKey = publicKey;
|
|
22
|
-
this.contractAddress = contractAddress;
|
|
23
|
-
this.ethereumHttpRpcUrl = ethereumHttpRpcUrl;
|
|
24
|
-
this.eip712Name = eip712Name;
|
|
25
|
-
this.eip712Version = eip712Version;
|
|
26
|
-
this.chainId = chainId;
|
|
27
|
-
}
|
|
28
|
-
static fromRpc(payload) {
|
|
29
|
-
const pkRaw = payload.public_key ?? payload.publicKey;
|
|
30
|
-
const pk = typeof pkRaw === 'string'
|
|
31
|
-
? (0, viem_1.toBytes)(pkRaw)
|
|
32
|
-
: pkRaw instanceof Uint8Array
|
|
33
|
-
? pkRaw
|
|
34
|
-
: Array.isArray(pkRaw)
|
|
35
|
-
? Uint8Array.from(pkRaw)
|
|
36
|
-
: new Uint8Array();
|
|
37
|
-
return new CorePublicParameters(pk, String(payload.contract_address ?? payload.contractAddress ?? ''), String(payload.ethereum_http_rpc_url ?? payload.ethereumHttpRpcUrl ?? ''), (payload.eip712_name ?? payload.eip712Name ?? '4Mica'), (payload.eip712_version ?? payload.eip712Version ?? '1'), Number(payload.chain_id ?? payload.chainId));
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
exports.CorePublicParameters = CorePublicParameters;
|
|
41
18
|
exports.GUARANTEE_EIP712_TYPES = {
|
|
42
19
|
EIP712Domain: [
|
|
43
20
|
{ name: 'name', type: 'string' },
|
|
@@ -54,6 +31,30 @@ exports.GUARANTEE_EIP712_TYPES = {
|
|
|
54
31
|
{ name: 'timestamp', type: 'uint64' },
|
|
55
32
|
],
|
|
56
33
|
};
|
|
34
|
+
exports.GUARANTEE_EIP712_TYPES_V2 = {
|
|
35
|
+
EIP712Domain: [
|
|
36
|
+
{ name: 'name', type: 'string' },
|
|
37
|
+
{ name: 'version', type: 'string' },
|
|
38
|
+
{ name: 'chainId', type: 'uint256' },
|
|
39
|
+
],
|
|
40
|
+
SolGuaranteeRequestClaimsV2: [
|
|
41
|
+
{ name: 'user', type: 'address' },
|
|
42
|
+
{ name: 'recipient', type: 'address' },
|
|
43
|
+
{ name: 'tabId', type: 'uint256' },
|
|
44
|
+
{ name: 'reqId', type: 'uint256' },
|
|
45
|
+
{ name: 'amount', type: 'uint256' },
|
|
46
|
+
{ name: 'asset', type: 'address' },
|
|
47
|
+
{ name: 'timestamp', type: 'uint64' },
|
|
48
|
+
{ name: 'validationRegistryAddress', type: 'address' },
|
|
49
|
+
{ name: 'validationRequestHash', type: 'bytes32' },
|
|
50
|
+
{ name: 'validationChainId', type: 'uint256' },
|
|
51
|
+
{ name: 'validatorAddress', type: 'address' },
|
|
52
|
+
{ name: 'validatorAgentId', type: 'uint256' },
|
|
53
|
+
{ name: 'minValidationScore', type: 'uint8' },
|
|
54
|
+
{ name: 'validationSubjectHash', type: 'bytes32' },
|
|
55
|
+
{ name: 'requiredValidationTag', type: 'string' },
|
|
56
|
+
],
|
|
57
|
+
};
|
|
57
58
|
const fieldsMatch = (actual, expected) => {
|
|
58
59
|
if (!Array.isArray(actual) || actual.length !== expected.length)
|
|
59
60
|
return false;
|
|
@@ -165,6 +166,73 @@ function buildGuaranteeTypedData(params, claims) {
|
|
|
165
166
|
},
|
|
166
167
|
};
|
|
167
168
|
}
|
|
169
|
+
function buildGuaranteeTypedDataV2(params, claims) {
|
|
170
|
+
return {
|
|
171
|
+
types: exports.GUARANTEE_EIP712_TYPES_V2,
|
|
172
|
+
primaryType: 'SolGuaranteeRequestClaimsV2',
|
|
173
|
+
domain: {
|
|
174
|
+
name: params.eip712Name,
|
|
175
|
+
version: params.eip712Version,
|
|
176
|
+
chainId: params.chainId,
|
|
177
|
+
},
|
|
178
|
+
message: {
|
|
179
|
+
user: claims.userAddress,
|
|
180
|
+
recipient: claims.recipientAddress,
|
|
181
|
+
tabId: claims.tabId,
|
|
182
|
+
reqId: claims.reqId,
|
|
183
|
+
amount: claims.amount,
|
|
184
|
+
asset: claims.assetAddress,
|
|
185
|
+
timestamp: BigInt(claims.timestamp),
|
|
186
|
+
validationRegistryAddress: claims.validationRegistryAddress,
|
|
187
|
+
validationRequestHash: (0, utils_1.ensureHexPrefix)(claims.validationRequestHash),
|
|
188
|
+
validationChainId: BigInt(claims.validationChainId),
|
|
189
|
+
validatorAddress: claims.validatorAddress,
|
|
190
|
+
validatorAgentId: claims.validatorAgentId,
|
|
191
|
+
minValidationScore: claims.minValidationScore,
|
|
192
|
+
validationSubjectHash: (0, utils_1.ensureHexPrefix)(claims.validationSubjectHash),
|
|
193
|
+
requiredValidationTag: claims.requiredValidationTag,
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function encodeGuaranteeEip191V2(claims) {
|
|
198
|
+
return (0, viem_1.encodeAbiParameters)([
|
|
199
|
+
{ type: 'address' },
|
|
200
|
+
{ type: 'address' },
|
|
201
|
+
{ type: 'uint256' },
|
|
202
|
+
{ type: 'uint256' },
|
|
203
|
+
{ type: 'uint256' },
|
|
204
|
+
{ type: 'address' },
|
|
205
|
+
{ type: 'uint64' },
|
|
206
|
+
{ type: 'address' },
|
|
207
|
+
{ type: 'bytes32' },
|
|
208
|
+
{ type: 'uint256' },
|
|
209
|
+
{ type: 'address' },
|
|
210
|
+
{ type: 'uint256' },
|
|
211
|
+
{ type: 'uint8' },
|
|
212
|
+
{ type: 'bytes32' },
|
|
213
|
+
{ type: 'string' },
|
|
214
|
+
], [
|
|
215
|
+
claims.userAddress,
|
|
216
|
+
claims.recipientAddress,
|
|
217
|
+
claims.tabId,
|
|
218
|
+
claims.reqId,
|
|
219
|
+
claims.amount,
|
|
220
|
+
claims.assetAddress,
|
|
221
|
+
BigInt(claims.timestamp),
|
|
222
|
+
claims.validationRegistryAddress,
|
|
223
|
+
(claims.validationRequestHash.startsWith('0x')
|
|
224
|
+
? claims.validationRequestHash
|
|
225
|
+
: `0x${claims.validationRequestHash}`),
|
|
226
|
+
BigInt(claims.validationChainId),
|
|
227
|
+
claims.validatorAddress,
|
|
228
|
+
claims.validatorAgentId,
|
|
229
|
+
claims.minValidationScore,
|
|
230
|
+
(claims.validationSubjectHash.startsWith('0x')
|
|
231
|
+
? claims.validationSubjectHash
|
|
232
|
+
: `0x${claims.validationSubjectHash}`),
|
|
233
|
+
claims.requiredValidationTag,
|
|
234
|
+
]);
|
|
235
|
+
}
|
|
168
236
|
function encodeGuaranteeEip191(claims) {
|
|
169
237
|
const payload = (0, viem_1.encodeAbiParameters)([
|
|
170
238
|
{ type: 'address' },
|
|
@@ -193,10 +261,21 @@ class PaymentSigner {
|
|
|
193
261
|
async signRequest(params, claims, scheme = models_1.SigningScheme.EIP712) {
|
|
194
262
|
try {
|
|
195
263
|
validateGuaranteeSigningContext(params, claims, { signerAddress: this.signer.address });
|
|
264
|
+
const isV2 = claims instanceof models_1.PaymentGuaranteeRequestClaimsV2;
|
|
196
265
|
if (scheme === models_1.SigningScheme.EIP712) {
|
|
197
266
|
if (!this.signer.signTypedData) {
|
|
198
267
|
throw new errors_1.SigningError('signTypedData is not supported for this account');
|
|
199
268
|
}
|
|
269
|
+
if (isV2) {
|
|
270
|
+
const typed = buildGuaranteeTypedDataV2(params, claims);
|
|
271
|
+
const signature = await this.signer.signTypedData({
|
|
272
|
+
domain: typed.domain,
|
|
273
|
+
types: { SolGuaranteeRequestClaimsV2: typed.types.SolGuaranteeRequestClaimsV2 },
|
|
274
|
+
primaryType: typed.primaryType,
|
|
275
|
+
message: typed.message,
|
|
276
|
+
});
|
|
277
|
+
return { signature, scheme };
|
|
278
|
+
}
|
|
200
279
|
const typed = buildGuaranteeTypedData(params, claims);
|
|
201
280
|
const signature = await this.signer.signTypedData({
|
|
202
281
|
domain: typed.domain,
|
|
@@ -210,7 +289,7 @@ class PaymentSigner {
|
|
|
210
289
|
if (!this.signer.signMessage) {
|
|
211
290
|
throw new errors_1.SigningError('signMessage is not supported for this account');
|
|
212
291
|
}
|
|
213
|
-
const message = encodeGuaranteeEip191(claims);
|
|
292
|
+
const message = isV2 ? encodeGuaranteeEip191V2(claims) : encodeGuaranteeEip191(claims);
|
|
214
293
|
const signature = await this.signer.signMessage({ message });
|
|
215
294
|
return { signature, scheme };
|
|
216
295
|
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare class ValidationError extends Error {
|
|
|
5
5
|
export declare function validateUrl(raw: string): string;
|
|
6
6
|
export declare function normalizePrivateKey(raw: string): string;
|
|
7
7
|
export declare function normalizeAddress(raw: string): string;
|
|
8
|
+
export declare function ensureHexPrefix(value: string): Hex;
|
|
8
9
|
export declare function parseU256(value: number | bigint | string): bigint;
|
|
9
10
|
export declare function serializeU256(value: number | bigint | string): string;
|
|
10
11
|
export declare function hexFromBytes(bytes: Uint8Array): Hex;
|
package/dist/utils.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.ValidationError = void 0;
|
|
|
4
4
|
exports.validateUrl = validateUrl;
|
|
5
5
|
exports.normalizePrivateKey = normalizePrivateKey;
|
|
6
6
|
exports.normalizeAddress = normalizeAddress;
|
|
7
|
+
exports.ensureHexPrefix = ensureHexPrefix;
|
|
7
8
|
exports.parseU256 = parseU256;
|
|
8
9
|
exports.serializeU256 = serializeU256;
|
|
9
10
|
exports.hexFromBytes = hexFromBytes;
|
|
@@ -16,16 +17,17 @@ class ValidationError extends Error {
|
|
|
16
17
|
}
|
|
17
18
|
exports.ValidationError = ValidationError;
|
|
18
19
|
function validateUrl(raw) {
|
|
20
|
+
let url;
|
|
19
21
|
try {
|
|
20
|
-
|
|
21
|
-
if (!url.protocol || !url.host) {
|
|
22
|
-
throw new Error('missing parts');
|
|
23
|
-
}
|
|
24
|
-
return raw;
|
|
22
|
+
url = new URL(raw);
|
|
25
23
|
}
|
|
26
24
|
catch {
|
|
27
25
|
throw new ValidationError(`invalid URL: ${raw}`);
|
|
28
26
|
}
|
|
27
|
+
if (!url.protocol || !url.host) {
|
|
28
|
+
throw new ValidationError(`invalid URL: ${raw}`);
|
|
29
|
+
}
|
|
30
|
+
return raw;
|
|
29
31
|
}
|
|
30
32
|
function normalizePrivateKey(raw) {
|
|
31
33
|
const key = raw.startsWith('0x') ? raw.slice(2) : raw;
|
|
@@ -45,6 +47,9 @@ function normalizeAddress(raw) {
|
|
|
45
47
|
}
|
|
46
48
|
throw new ValidationError(`invalid address: ${raw}`);
|
|
47
49
|
}
|
|
50
|
+
function ensureHexPrefix(value) {
|
|
51
|
+
return (value.startsWith('0x') ? value : `0x${value}`);
|
|
52
|
+
}
|
|
48
53
|
function parseNumericString(raw) {
|
|
49
54
|
const text = raw.trim();
|
|
50
55
|
const n = BigInt(text);
|
|
@@ -58,6 +63,9 @@ function parseU256(value) {
|
|
|
58
63
|
if (!Number.isFinite(value)) {
|
|
59
64
|
throw new ValidationError('invalid integer');
|
|
60
65
|
}
|
|
66
|
+
if (!Number.isSafeInteger(value)) {
|
|
67
|
+
throw new ValidationError('integer value exceeds safe precision; use bigint or hex string for large values');
|
|
68
|
+
}
|
|
61
69
|
if (value < 0) {
|
|
62
70
|
throw new ValidationError('u256 cannot be negative');
|
|
63
71
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Hex } from 'viem';
|
|
2
|
+
import { PaymentGuaranteeRequestClaims, PaymentGuaranteeRequestClaimsV2 } from './models';
|
|
3
|
+
/** Binding domain string used as a prefix when hashing validation subjects. */
|
|
4
|
+
export declare const VALIDATION_SUBJECT_BINDING_DOMAIN = "4MICA_VALIDATION_SUBJECT_V1";
|
|
5
|
+
/** Binding domain string used as a prefix when hashing validation requests. */
|
|
6
|
+
export declare const VALIDATION_REQUEST_BINDING_DOMAIN = "4MICA_VALIDATION_REQUEST_V1";
|
|
7
|
+
/**
|
|
8
|
+
* Compute the `validationSubjectHash` for a V2 payment guarantee request.
|
|
9
|
+
*
|
|
10
|
+
* Binds the payment subject (tab, user, recipient, amount, asset, timestamp) to
|
|
11
|
+
* a keccak256 hash prefixed by {@link VALIDATION_SUBJECT_BINDING_DOMAIN} so that
|
|
12
|
+
* on-chain validators can verify which payment the validation refers to.
|
|
13
|
+
*
|
|
14
|
+
* @param claims - V1 base claims (the subject fields are the same for V1 and V2).
|
|
15
|
+
* @returns 32-byte keccak256 hash as a `0x`-prefixed hex string.
|
|
16
|
+
*/
|
|
17
|
+
export declare function computeValidationSubjectHash(claims: PaymentGuaranteeRequestClaims): Hex;
|
|
18
|
+
/**
|
|
19
|
+
* Compute the `validationRequestHash` for a V2 payment guarantee request.
|
|
20
|
+
*
|
|
21
|
+
* Binds the full validation policy (chain, registry, validator, score threshold,
|
|
22
|
+
* subject hash, required tag) to a keccak256 hash prefixed by
|
|
23
|
+
* {@link VALIDATION_REQUEST_BINDING_DOMAIN}. This hash is included in the V2 claims
|
|
24
|
+
* that are BLS-signed by the core service, allowing on-chain verification that the
|
|
25
|
+
* validation policy was not tampered with.
|
|
26
|
+
*
|
|
27
|
+
* @param claims - V2 claims with all validation policy fields populated.
|
|
28
|
+
* The `validationSubjectHash` field must already be computed via
|
|
29
|
+
* {@link computeValidationSubjectHash} before calling this function.
|
|
30
|
+
* @returns 32-byte keccak256 hash as a `0x`-prefixed hex string.
|
|
31
|
+
*/
|
|
32
|
+
export declare function computeValidationRequestHash(claims: PaymentGuaranteeRequestClaimsV2): Hex;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VALIDATION_REQUEST_BINDING_DOMAIN = exports.VALIDATION_SUBJECT_BINDING_DOMAIN = void 0;
|
|
4
|
+
exports.computeValidationSubjectHash = computeValidationSubjectHash;
|
|
5
|
+
exports.computeValidationRequestHash = computeValidationRequestHash;
|
|
6
|
+
const viem_1 = require("viem");
|
|
7
|
+
const utils_1 = require("./utils");
|
|
8
|
+
/** Binding domain string used as a prefix when hashing validation subjects. */
|
|
9
|
+
exports.VALIDATION_SUBJECT_BINDING_DOMAIN = '4MICA_VALIDATION_SUBJECT_V1';
|
|
10
|
+
/** Binding domain string used as a prefix when hashing validation requests. */
|
|
11
|
+
exports.VALIDATION_REQUEST_BINDING_DOMAIN = '4MICA_VALIDATION_REQUEST_V1';
|
|
12
|
+
/**
|
|
13
|
+
* Compute the `validationSubjectHash` for a V2 payment guarantee request.
|
|
14
|
+
*
|
|
15
|
+
* Binds the payment subject (tab, user, recipient, amount, asset, timestamp) to
|
|
16
|
+
* a keccak256 hash prefixed by {@link VALIDATION_SUBJECT_BINDING_DOMAIN} so that
|
|
17
|
+
* on-chain validators can verify which payment the validation refers to.
|
|
18
|
+
*
|
|
19
|
+
* @param claims - V1 base claims (the subject fields are the same for V1 and V2).
|
|
20
|
+
* @returns 32-byte keccak256 hash as a `0x`-prefixed hex string.
|
|
21
|
+
*/
|
|
22
|
+
function computeValidationSubjectHash(claims) {
|
|
23
|
+
const bindingDomain = (0, viem_1.keccak256)(new TextEncoder().encode(exports.VALIDATION_SUBJECT_BINDING_DOMAIN));
|
|
24
|
+
const encoded = (0, viem_1.encodeAbiParameters)([
|
|
25
|
+
{ type: 'bytes32' },
|
|
26
|
+
{ type: 'uint256' },
|
|
27
|
+
{ type: 'uint256' },
|
|
28
|
+
{ type: 'address' },
|
|
29
|
+
{ type: 'address' },
|
|
30
|
+
{ type: 'uint256' },
|
|
31
|
+
{ type: 'address' },
|
|
32
|
+
{ type: 'uint64' },
|
|
33
|
+
], [
|
|
34
|
+
bindingDomain,
|
|
35
|
+
claims.tabId,
|
|
36
|
+
claims.reqId,
|
|
37
|
+
claims.userAddress,
|
|
38
|
+
claims.recipientAddress,
|
|
39
|
+
claims.amount,
|
|
40
|
+
claims.assetAddress,
|
|
41
|
+
BigInt(claims.timestamp),
|
|
42
|
+
]);
|
|
43
|
+
return (0, viem_1.keccak256)(encoded);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Compute the `validationRequestHash` for a V2 payment guarantee request.
|
|
47
|
+
*
|
|
48
|
+
* Binds the full validation policy (chain, registry, validator, score threshold,
|
|
49
|
+
* subject hash, required tag) to a keccak256 hash prefixed by
|
|
50
|
+
* {@link VALIDATION_REQUEST_BINDING_DOMAIN}. This hash is included in the V2 claims
|
|
51
|
+
* that are BLS-signed by the core service, allowing on-chain verification that the
|
|
52
|
+
* validation policy was not tampered with.
|
|
53
|
+
*
|
|
54
|
+
* @param claims - V2 claims with all validation policy fields populated.
|
|
55
|
+
* The `validationSubjectHash` field must already be computed via
|
|
56
|
+
* {@link computeValidationSubjectHash} before calling this function.
|
|
57
|
+
* @returns 32-byte keccak256 hash as a `0x`-prefixed hex string.
|
|
58
|
+
*/
|
|
59
|
+
function computeValidationRequestHash(claims) {
|
|
60
|
+
const bindingDomain = (0, viem_1.keccak256)(new TextEncoder().encode(exports.VALIDATION_REQUEST_BINDING_DOMAIN));
|
|
61
|
+
const tagHash = (0, viem_1.keccak256)(new TextEncoder().encode(claims.requiredValidationTag));
|
|
62
|
+
const encoded = (0, viem_1.encodeAbiParameters)([
|
|
63
|
+
{ type: 'bytes32' },
|
|
64
|
+
{ type: 'uint256' },
|
|
65
|
+
{ type: 'address' },
|
|
66
|
+
{ type: 'address' },
|
|
67
|
+
{ type: 'uint256' },
|
|
68
|
+
{ type: 'bytes32' },
|
|
69
|
+
{ type: 'uint8' },
|
|
70
|
+
{ type: 'bytes32' },
|
|
71
|
+
], [
|
|
72
|
+
bindingDomain,
|
|
73
|
+
BigInt(claims.validationChainId),
|
|
74
|
+
claims.validationRegistryAddress,
|
|
75
|
+
claims.validatorAddress,
|
|
76
|
+
claims.validatorAgentId,
|
|
77
|
+
(0, utils_1.ensureHexPrefix)(claims.validationSubjectHash),
|
|
78
|
+
claims.minValidationScore,
|
|
79
|
+
tagHash,
|
|
80
|
+
]);
|
|
81
|
+
return (0, viem_1.keccak256)(encoded);
|
|
82
|
+
}
|
package/dist/x402/index.d.ts
CHANGED
|
@@ -1,22 +1,90 @@
|
|
|
1
|
-
import { PaymentGuaranteeRequestClaims, PaymentSignature, SigningScheme } from '../models';
|
|
1
|
+
import { PaymentGuaranteeRequestClaims, PaymentGuaranteeRequestClaimsV2, PaymentSignature, SigningScheme } from '../models';
|
|
2
2
|
import { PaymentRequirementsV1, TabResponse, X402SignedPayment, X402SettledPayment, PaymentRequirementsV2, X402PaymentRequired, X402ResourceInfo } from './models';
|
|
3
3
|
import type { FetchFn } from '../rpc';
|
|
4
4
|
export * from './models';
|
|
5
|
+
/**
|
|
6
|
+
* Minimal signing interface required by {@link X402Flow}.
|
|
7
|
+
* Implemented by {@link UserClient} from the main SDK client.
|
|
8
|
+
*/
|
|
5
9
|
export interface FlowSigner {
|
|
6
|
-
signPayment(claims: PaymentGuaranteeRequestClaims, scheme: SigningScheme): Promise<PaymentSignature>;
|
|
10
|
+
signPayment(claims: PaymentGuaranteeRequestClaims | PaymentGuaranteeRequestClaimsV2, scheme: SigningScheme): Promise<PaymentSignature>;
|
|
7
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Handles the x402 HTTP 402 payment protocol for 4Mica.
|
|
14
|
+
*
|
|
15
|
+
* Orchestrates the full client-side x402 flow: resolving a tab from the recipient's
|
|
16
|
+
* tab endpoint, building and signing payment claims (V1 or V2), and optionally settling
|
|
17
|
+
* the payment against a facilitator service.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const flow = X402Flow.fromClient(client);
|
|
22
|
+
* const signed = await flow.signPayment(paymentRequirements, userAddress);
|
|
23
|
+
* // attach signed.header as the X-PAYMENT header on the protected request
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
8
26
|
export declare class X402Flow {
|
|
9
27
|
private signer;
|
|
10
28
|
private fetchFn;
|
|
29
|
+
/**
|
|
30
|
+
* @param signer - Payment signer, typically `client.user`.
|
|
31
|
+
* @param fetchFn - HTTP fetch implementation. Defaults to global `fetch`.
|
|
32
|
+
*/
|
|
11
33
|
constructor(signer: FlowSigner, fetchFn?: FetchFn);
|
|
34
|
+
/**
|
|
35
|
+
* Convenience factory — creates an `X402Flow` from the user sub-client of a `Client`.
|
|
36
|
+
*
|
|
37
|
+
* @param client - Any object with a `.user` property implementing {@link FlowSigner}.
|
|
38
|
+
*/
|
|
12
39
|
static fromClient(client: {
|
|
13
40
|
user: FlowSigner;
|
|
14
41
|
}): X402Flow;
|
|
42
|
+
/**
|
|
43
|
+
* Sign an x402 V1 payment.
|
|
44
|
+
*
|
|
45
|
+
* Resolves a tab from `paymentRequirements.extra.tabEndpoint`, builds V1 claims,
|
|
46
|
+
* signs them with EIP-712, and returns the base64-encoded payment header together
|
|
47
|
+
* with the raw payload and signature.
|
|
48
|
+
*
|
|
49
|
+
* @param paymentRequirements - V1 payment requirements from the `402 Payment Required` response.
|
|
50
|
+
* @param userAddress - Address of the paying user.
|
|
51
|
+
* @returns Signed payment ready to attach as the `X-PAYMENT` request header.
|
|
52
|
+
* @throws {@link X402Error} if the scheme is not a 4Mica scheme or the tab endpoint fails.
|
|
53
|
+
*/
|
|
15
54
|
signPayment(paymentRequirements: PaymentRequirementsV1, userAddress: string): Promise<X402SignedPayment>;
|
|
55
|
+
/**
|
|
56
|
+
* Sign an x402 V2 payment, optionally including a V2 validation policy.
|
|
57
|
+
*
|
|
58
|
+
* If `accepted.extra` contains all required validation policy fields
|
|
59
|
+
* (`validationRegistryAddress`, `validatorAddress`, `validatorAgentId`,
|
|
60
|
+
* `minValidationScore`, `validationChainId`), the claims are built as V2 with
|
|
61
|
+
* the computed `validationSubjectHash` and `validationRequestHash`. Otherwise
|
|
62
|
+
* falls back to V1 claims.
|
|
63
|
+
*
|
|
64
|
+
* @param paymentRequired - The original `402 Payment Required` response object.
|
|
65
|
+
* @param accepted - The accepted V2 payment requirements.
|
|
66
|
+
* @param userAddress - Address of the paying user.
|
|
67
|
+
* @returns Signed payment ready to attach as the `X-PAYMENT` request header.
|
|
68
|
+
* @throws {@link X402Error} if the scheme is not a 4Mica scheme or the tab endpoint fails.
|
|
69
|
+
*/
|
|
16
70
|
signPaymentV2(paymentRequired: X402PaymentRequired, accepted: PaymentRequirementsV2, userAddress: string): Promise<X402SignedPayment>;
|
|
71
|
+
/**
|
|
72
|
+
* Submit a signed payment to a facilitator for on-chain settlement.
|
|
73
|
+
*
|
|
74
|
+
* Sends a POST request to `{facilitatorUrl}/settle` with the payment header,
|
|
75
|
+
* decoded payload, and payment requirements. Use this when the protected resource
|
|
76
|
+
* requires facilitator-confirmed settlement before granting access.
|
|
77
|
+
*
|
|
78
|
+
* @param payment - Signed payment returned by {@link signPayment} or {@link signPaymentV2}.
|
|
79
|
+
* @param paymentRequirements - V1 payment requirements included in the settlement request.
|
|
80
|
+
* @param facilitatorUrl - Base URL of the facilitator service.
|
|
81
|
+
* @returns The original payment plus the raw settlement response from the facilitator.
|
|
82
|
+
* @throws {@link X402Error} if the facilitator returns a non-2xx response.
|
|
83
|
+
*/
|
|
17
84
|
settlePayment(payment: X402SignedPayment, paymentRequirements: PaymentRequirementsV1, facilitatorUrl: string): Promise<X402SettledPayment>;
|
|
18
85
|
protected requestTab(x402Version: number, paymentRequirements: PaymentRequirementsV1 | PaymentRequirementsV2, userAddress: string, resource?: X402ResourceInfo): Promise<TabResponse>;
|
|
19
86
|
protected buildClaims(requirements: PaymentRequirementsV1 | PaymentRequirementsV2, tab: TabResponse, userAddress: string): PaymentGuaranteeRequestClaims;
|
|
87
|
+
protected buildClaimsV2(requirements: PaymentRequirementsV2, tab: TabResponse, userAddress: string): PaymentGuaranteeRequestClaimsV2;
|
|
20
88
|
private static validateScheme;
|
|
21
89
|
private static decodePaymentHeader;
|
|
22
90
|
}
|
package/dist/x402/index.js
CHANGED
|
@@ -19,17 +19,60 @@ const models_1 = require("../models");
|
|
|
19
19
|
const utils_1 = require("../utils");
|
|
20
20
|
const errors_1 = require("../errors");
|
|
21
21
|
const payment_1 = require("../payment");
|
|
22
|
+
const validation_1 = require("../validation");
|
|
22
23
|
__exportStar(require("./models"), exports);
|
|
24
|
+
function hasValidationPolicy(extra) {
|
|
25
|
+
return !!(extra?.validationRegistryAddress &&
|
|
26
|
+
extra?.validatorAddress &&
|
|
27
|
+
extra?.validatorAgentId !== undefined &&
|
|
28
|
+
extra?.minValidationScore !== undefined &&
|
|
29
|
+
extra?.validationChainId !== undefined);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Handles the x402 HTTP 402 payment protocol for 4Mica.
|
|
33
|
+
*
|
|
34
|
+
* Orchestrates the full client-side x402 flow: resolving a tab from the recipient's
|
|
35
|
+
* tab endpoint, building and signing payment claims (V1 or V2), and optionally settling
|
|
36
|
+
* the payment against a facilitator service.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const flow = X402Flow.fromClient(client);
|
|
41
|
+
* const signed = await flow.signPayment(paymentRequirements, userAddress);
|
|
42
|
+
* // attach signed.header as the X-PAYMENT header on the protected request
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
23
45
|
class X402Flow {
|
|
24
46
|
signer;
|
|
25
47
|
fetchFn;
|
|
48
|
+
/**
|
|
49
|
+
* @param signer - Payment signer, typically `client.user`.
|
|
50
|
+
* @param fetchFn - HTTP fetch implementation. Defaults to global `fetch`.
|
|
51
|
+
*/
|
|
26
52
|
constructor(signer, fetchFn = fetch) {
|
|
27
53
|
this.signer = signer;
|
|
28
54
|
this.fetchFn = fetchFn;
|
|
29
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Convenience factory — creates an `X402Flow` from the user sub-client of a `Client`.
|
|
58
|
+
*
|
|
59
|
+
* @param client - Any object with a `.user` property implementing {@link FlowSigner}.
|
|
60
|
+
*/
|
|
30
61
|
static fromClient(client) {
|
|
31
62
|
return new X402Flow(client.user);
|
|
32
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Sign an x402 V1 payment.
|
|
66
|
+
*
|
|
67
|
+
* Resolves a tab from `paymentRequirements.extra.tabEndpoint`, builds V1 claims,
|
|
68
|
+
* signs them with EIP-712, and returns the base64-encoded payment header together
|
|
69
|
+
* with the raw payload and signature.
|
|
70
|
+
*
|
|
71
|
+
* @param paymentRequirements - V1 payment requirements from the `402 Payment Required` response.
|
|
72
|
+
* @param userAddress - Address of the paying user.
|
|
73
|
+
* @returns Signed payment ready to attach as the `X-PAYMENT` request header.
|
|
74
|
+
* @throws {@link X402Error} if the scheme is not a 4Mica scheme or the tab endpoint fails.
|
|
75
|
+
*/
|
|
33
76
|
async signPayment(paymentRequirements, userAddress) {
|
|
34
77
|
X402Flow.validateScheme(paymentRequirements.scheme);
|
|
35
78
|
const tab = await this.requestTab(1, paymentRequirements, userAddress);
|
|
@@ -45,10 +88,27 @@ class X402Flow {
|
|
|
45
88
|
const header = Buffer.from(JSON.stringify(envelope)).toString('base64');
|
|
46
89
|
return { header, payload: paymentPayload, signature };
|
|
47
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Sign an x402 V2 payment, optionally including a V2 validation policy.
|
|
93
|
+
*
|
|
94
|
+
* If `accepted.extra` contains all required validation policy fields
|
|
95
|
+
* (`validationRegistryAddress`, `validatorAddress`, `validatorAgentId`,
|
|
96
|
+
* `minValidationScore`, `validationChainId`), the claims are built as V2 with
|
|
97
|
+
* the computed `validationSubjectHash` and `validationRequestHash`. Otherwise
|
|
98
|
+
* falls back to V1 claims.
|
|
99
|
+
*
|
|
100
|
+
* @param paymentRequired - The original `402 Payment Required` response object.
|
|
101
|
+
* @param accepted - The accepted V2 payment requirements.
|
|
102
|
+
* @param userAddress - Address of the paying user.
|
|
103
|
+
* @returns Signed payment ready to attach as the `X-PAYMENT` request header.
|
|
104
|
+
* @throws {@link X402Error} if the scheme is not a 4Mica scheme or the tab endpoint fails.
|
|
105
|
+
*/
|
|
48
106
|
async signPaymentV2(paymentRequired, accepted, userAddress) {
|
|
49
107
|
X402Flow.validateScheme(accepted.scheme);
|
|
50
108
|
const tab = await this.requestTab(2, accepted, userAddress, paymentRequired.resource);
|
|
51
|
-
const claims =
|
|
109
|
+
const claims = hasValidationPolicy(accepted.extra)
|
|
110
|
+
? this.buildClaimsV2(accepted, tab, userAddress)
|
|
111
|
+
: this.buildClaims(accepted, tab, userAddress);
|
|
52
112
|
const signature = await this.signer.signPayment(claims, models_1.SigningScheme.EIP712);
|
|
53
113
|
const paymentPayload = (0, payment_1.buildPaymentPayload)(claims, signature);
|
|
54
114
|
const envelope = {
|
|
@@ -60,6 +120,19 @@ class X402Flow {
|
|
|
60
120
|
const header = Buffer.from(JSON.stringify(envelope)).toString('base64');
|
|
61
121
|
return { header, payload: paymentPayload, signature };
|
|
62
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Submit a signed payment to a facilitator for on-chain settlement.
|
|
125
|
+
*
|
|
126
|
+
* Sends a POST request to `{facilitatorUrl}/settle` with the payment header,
|
|
127
|
+
* decoded payload, and payment requirements. Use this when the protected resource
|
|
128
|
+
* requires facilitator-confirmed settlement before granting access.
|
|
129
|
+
*
|
|
130
|
+
* @param payment - Signed payment returned by {@link signPayment} or {@link signPaymentV2}.
|
|
131
|
+
* @param paymentRequirements - V1 payment requirements included in the settlement request.
|
|
132
|
+
* @param facilitatorUrl - Base URL of the facilitator service.
|
|
133
|
+
* @returns The original payment plus the raw settlement response from the facilitator.
|
|
134
|
+
* @throws {@link X402Error} if the facilitator returns a non-2xx response.
|
|
135
|
+
*/
|
|
63
136
|
async settlePayment(payment, paymentRequirements, facilitatorUrl) {
|
|
64
137
|
const url = `${facilitatorUrl.replace(/\/$/, '')}/settle`;
|
|
65
138
|
const paymentPayload = X402Flow.decodePaymentHeader(payment.header);
|
|
@@ -119,6 +192,33 @@ class X402Flow {
|
|
|
119
192
|
const timestamp = Math.floor(Date.now() / 1000);
|
|
120
193
|
return models_1.PaymentGuaranteeRequestClaims.new(userAddress, (0, utils_1.normalizeAddress)(requirements.payTo), tabId, amount, timestamp, requirements.asset, reqId);
|
|
121
194
|
}
|
|
195
|
+
buildClaimsV2(requirements, tab, userAddress) {
|
|
196
|
+
const base = this.buildClaims(requirements, tab, userAddress);
|
|
197
|
+
const extra = requirements.extra;
|
|
198
|
+
const validationSubjectHash = (0, validation_1.computeValidationSubjectHash)(base);
|
|
199
|
+
const partialClaims = new models_1.PaymentGuaranteeRequestClaimsV2({
|
|
200
|
+
userAddress: base.userAddress,
|
|
201
|
+
recipientAddress: base.recipientAddress,
|
|
202
|
+
tabId: base.tabId,
|
|
203
|
+
reqId: base.reqId,
|
|
204
|
+
amount: base.amount,
|
|
205
|
+
timestamp: base.timestamp,
|
|
206
|
+
assetAddress: base.assetAddress,
|
|
207
|
+
validationRegistryAddress: extra.validationRegistryAddress,
|
|
208
|
+
validationRequestHash: '0x' + '00'.repeat(32),
|
|
209
|
+
validationChainId: extra.validationChainId,
|
|
210
|
+
validatorAddress: extra.validatorAddress,
|
|
211
|
+
validatorAgentId: (0, utils_1.parseU256)(extra.validatorAgentId),
|
|
212
|
+
minValidationScore: extra.minValidationScore,
|
|
213
|
+
validationSubjectHash,
|
|
214
|
+
requiredValidationTag: extra.requiredValidationTag ?? '',
|
|
215
|
+
});
|
|
216
|
+
const validationRequestHash = (0, validation_1.computeValidationRequestHash)(partialClaims);
|
|
217
|
+
return new models_1.PaymentGuaranteeRequestClaimsV2({
|
|
218
|
+
...partialClaims,
|
|
219
|
+
validationRequestHash,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
122
222
|
static validateScheme(scheme) {
|
|
123
223
|
if (!scheme.toLowerCase().includes('4mica')) {
|
|
124
224
|
throw new errors_1.X402Error(`invalid scheme: ${scheme}`);
|
package/dist/x402/models.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PaymentSignature } from '../models';
|
|
2
|
-
import type { PaymentPayload
|
|
2
|
+
import type { PaymentPayload } from '../payment';
|
|
3
3
|
export interface PaymentRequirementsV1 {
|
|
4
4
|
scheme: string;
|
|
5
5
|
network: string;
|
|
@@ -25,13 +25,19 @@ export interface PaymentRequirementsV2 {
|
|
|
25
25
|
export type PaymentRequirements = PaymentRequirementsV2;
|
|
26
26
|
export interface PaymentRequirementsExtra {
|
|
27
27
|
tabEndpoint?: string;
|
|
28
|
+
validationRegistryAddress?: string;
|
|
29
|
+
validationChainId?: number;
|
|
30
|
+
validatorAddress?: string;
|
|
31
|
+
validatorAgentId?: string;
|
|
32
|
+
minValidationScore?: number;
|
|
33
|
+
requiredValidationTag?: string;
|
|
28
34
|
}
|
|
29
35
|
export interface TabResponse {
|
|
30
36
|
tabId: string;
|
|
31
37
|
userAddress: string;
|
|
32
38
|
nextReqId?: string;
|
|
33
39
|
}
|
|
34
|
-
export type X402PaymentPayloadClaims =
|
|
40
|
+
export type X402PaymentPayloadClaims = PaymentPayload['claims'];
|
|
35
41
|
export type X402PaymentPayload = PaymentPayload;
|
|
36
42
|
export interface X402PaymentEnvelopeV1 {
|
|
37
43
|
x402Version: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@4mica/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "TypeScript SDK for interacting with the 4Mica payment network",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc -p tsconfig.build.json",
|
|
43
43
|
"test": "vitest run",
|
|
44
|
+
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
44
45
|
"lint": "eslint . --ext .ts",
|
|
45
46
|
"fmt": "prettier --check \"{src,tests}/**/*.{ts,js,json}\""
|
|
46
47
|
},
|
|
@@ -53,6 +54,7 @@
|
|
|
53
54
|
"@types/node": "^25.2.0",
|
|
54
55
|
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
55
56
|
"@typescript-eslint/parser": "^8.54.0",
|
|
57
|
+
"dotenv-cli": "^11.0.0",
|
|
56
58
|
"eslint": "^9.39.2",
|
|
57
59
|
"eslint-config-prettier": "^10.1.8",
|
|
58
60
|
"eslint-plugin-react": "^7.37.5",
|