@4mica/sdk 0.5.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.
Files changed (105) hide show
  1. package/.eslintrc.cjs +29 -0
  2. package/.github/workflows/ci.yml +31 -0
  3. package/.prettierignore +3 -0
  4. package/.prettierrc +6 -0
  5. package/LICENSE +21 -0
  6. package/README.md +444 -0
  7. package/dist/abi/core4mica.json +1605 -0
  8. package/dist/abi/erc20.json +187 -0
  9. package/dist/auth.d.ts +62 -0
  10. package/dist/auth.js +255 -0
  11. package/dist/bls.d.ts +5 -0
  12. package/dist/bls.js +58 -0
  13. package/dist/client.d.ts +58 -0
  14. package/dist/client.js +245 -0
  15. package/dist/config.d.ts +36 -0
  16. package/dist/config.js +123 -0
  17. package/dist/contract.d.ts +33 -0
  18. package/dist/contract.js +134 -0
  19. package/dist/errors.d.ts +43 -0
  20. package/dist/errors.js +62 -0
  21. package/dist/guarantee.d.ts +3 -0
  22. package/dist/guarantee.js +66 -0
  23. package/dist/index.d.ts +12 -0
  24. package/dist/index.js +28 -0
  25. package/dist/models.d.ts +150 -0
  26. package/dist/models.js +176 -0
  27. package/dist/rpc.d.ts +37 -0
  28. package/dist/rpc.js +163 -0
  29. package/dist/signing.d.ts +106 -0
  30. package/dist/signing.js +223 -0
  31. package/dist/src/abi/core4mica.json +1605 -0
  32. package/dist/src/abi/erc20.json +187 -0
  33. package/dist/src/bls.d.ts +5 -0
  34. package/dist/src/bls.js +45 -0
  35. package/dist/src/client.d.ts +55 -0
  36. package/dist/src/client.js +214 -0
  37. package/dist/src/config.d.ts +21 -0
  38. package/dist/src/config.js +76 -0
  39. package/dist/src/contract.d.ts +33 -0
  40. package/dist/src/contract.js +121 -0
  41. package/dist/src/errors.d.ts +17 -0
  42. package/dist/src/errors.js +31 -0
  43. package/dist/src/guarantee.d.ts +3 -0
  44. package/dist/src/guarantee.js +66 -0
  45. package/dist/src/index.d.ts +11 -0
  46. package/dist/src/index.js +27 -0
  47. package/dist/src/models.d.ts +119 -0
  48. package/dist/src/models.js +132 -0
  49. package/dist/src/rpc.d.ts +30 -0
  50. package/dist/src/rpc.js +122 -0
  51. package/dist/src/signing.d.ts +16 -0
  52. package/dist/src/signing.js +102 -0
  53. package/dist/src/utils.d.ts +8 -0
  54. package/dist/src/utils.js +78 -0
  55. package/dist/src/x402.d.ts +77 -0
  56. package/dist/src/x402.js +214 -0
  57. package/dist/tests/config.test.d.ts +1 -0
  58. package/dist/tests/config.test.js +26 -0
  59. package/dist/tests/guarantee.test.d.ts +1 -0
  60. package/dist/tests/guarantee.test.js +26 -0
  61. package/dist/tests/rpc.test.d.ts +1 -0
  62. package/dist/tests/rpc.test.js +44 -0
  63. package/dist/tests/signing.test.d.ts +1 -0
  64. package/dist/tests/signing.test.js +25 -0
  65. package/dist/tests/utils.test.d.ts +1 -0
  66. package/dist/tests/utils.test.js +25 -0
  67. package/dist/tests/x402.test.d.ts +1 -0
  68. package/dist/tests/x402.test.js +65 -0
  69. package/dist/utils.d.ts +8 -0
  70. package/dist/utils.js +78 -0
  71. package/dist/x402/index.d.ts +22 -0
  72. package/dist/x402/index.js +136 -0
  73. package/dist/x402/models.d.ts +80 -0
  74. package/dist/x402/models.js +2 -0
  75. package/dist/x402.d.ts +78 -0
  76. package/dist/x402.js +231 -0
  77. package/eslint.config.mjs +22 -0
  78. package/package.json +31 -0
  79. package/src/abi/core4mica.json +1605 -0
  80. package/src/abi/erc20.json +187 -0
  81. package/src/auth.ts +325 -0
  82. package/src/bls.ts +76 -0
  83. package/src/client.ts +347 -0
  84. package/src/config.ts +149 -0
  85. package/src/contract.ts +194 -0
  86. package/src/errors.ts +40 -0
  87. package/src/guarantee.ts +96 -0
  88. package/src/index.ts +12 -0
  89. package/src/models.ts +309 -0
  90. package/src/rpc.ts +225 -0
  91. package/src/signing.ts +330 -0
  92. package/src/utils.ts +75 -0
  93. package/src/x402/index.ts +192 -0
  94. package/src/x402/models.ts +94 -0
  95. package/tests/auth.integration.test.ts +97 -0
  96. package/tests/auth.test.ts +292 -0
  97. package/tests/config.test.ts +56 -0
  98. package/tests/guarantee.test.ts +26 -0
  99. package/tests/rpc.test.ts +76 -0
  100. package/tests/signing.test.ts +52 -0
  101. package/tests/utils.test.ts +35 -0
  102. package/tests/x402.test.ts +152 -0
  103. package/tsconfig.build.json +5 -0
  104. package/tsconfig.json +15 -0
  105. package/vitest.config.ts +12 -0
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const rpc_1 = require("../src/rpc");
5
+ const errors_1 = require("../src/errors");
6
+ (0, vitest_1.describe)('RpcProxy', () => {
7
+ (0, vitest_1.it)('round trips public params', async () => {
8
+ const params = {
9
+ publicKey: [1, 2, 3],
10
+ contractAddress: '0x1234567890abcdef1234567890abcdef12345678',
11
+ ethereumHttpRpcUrl: 'http://localhost:8545',
12
+ eip712Name: '4mica',
13
+ eip712Version: '1',
14
+ chainId: 1337,
15
+ };
16
+ const fetchMock = vitest_1.vi.fn(async (input) => {
17
+ const url = input.toString();
18
+ (0, vitest_1.expect)(url.endsWith('/core/public-params')).toBe(true);
19
+ return new Response(JSON.stringify(params), { status: 200 });
20
+ });
21
+ const proxy = new rpc_1.RpcProxy('http://example.com', undefined, fetchMock);
22
+ const got = await proxy.getPublicParams();
23
+ (0, vitest_1.expect)(got.chainId).toBe(1337);
24
+ (0, vitest_1.expect)(got.contractAddress).toBe(params.contractAddress);
25
+ (0, vitest_1.expect)(got.ethereumHttpRpcUrl).toBe(params.ethereumHttpRpcUrl);
26
+ });
27
+ (0, vitest_1.it)('surfaces api errors', async () => {
28
+ const fetchMock = vitest_1.vi.fn(async (input) => {
29
+ (0, vitest_1.expect)(input.toString()).toContain('settlementStatus=unknown');
30
+ return new Response(JSON.stringify({ error: 'invalid settlement status: unknown' }), {
31
+ status: 400,
32
+ });
33
+ });
34
+ const proxy = new rpc_1.RpcProxy('http://example.com', undefined, fetchMock);
35
+ await (0, vitest_1.expect)(proxy.listRecipientTabs('0xdeadbeef', ['unknown'])).rejects.toThrow(errors_1.RpcError);
36
+ });
37
+ (0, vitest_1.it)('returns decode error on invalid json', async () => {
38
+ const fetchMock = vitest_1.vi.fn(async () => {
39
+ return new Response('not-json', { status: 200 });
40
+ });
41
+ const proxy = new rpc_1.RpcProxy('http://example.com', undefined, fetchMock);
42
+ await (0, vitest_1.expect)(proxy.getPublicParams()).rejects.toThrow(errors_1.RpcError);
43
+ });
44
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const models_1 = require("../src/models");
5
+ const signing_1 = require("../src/signing");
6
+ const errors_1 = require("../src/errors");
7
+ function buildParams() {
8
+ return new signing_1.CorePublicParameters(new Uint8Array(), '0x0000000000000000000000000000000000000000', 'https://example.com', '4Mica', '1', 1);
9
+ }
10
+ (0, vitest_1.describe)('PaymentSigner', () => {
11
+ (0, vitest_1.it)('rejects address mismatch', async () => {
12
+ const signer = new signing_1.PaymentSigner('11'.repeat(32));
13
+ const claims = models_1.PaymentGuaranteeRequestClaims.new('0x0000000000000000000000000000000000000011', '0x0000000000000000000000000000000000000002', 1, 5, 1234, null);
14
+ await (0, vitest_1.expect)(signer.signRequest(buildParams(), claims, models_1.SigningScheme.EIP712)).rejects.toThrow(errors_1.SigningError);
15
+ });
16
+ (0, vitest_1.it)('produces eip712 signature', async () => {
17
+ const privateKey = '0x59c6995e998f97a5a0044976f7be35d5ad91c0cfa55b5cfb20b07a1c60f4c5bc';
18
+ const signer = new signing_1.PaymentSigner(privateKey);
19
+ const accountAddress = (await signer['wallet'].getAddress()).toLowerCase();
20
+ const claims = models_1.PaymentGuaranteeRequestClaims.new(accountAddress, '0x0000000000000000000000000000000000000002', 42, 123, 999, null);
21
+ const sig = await signer.signRequest(buildParams(), claims, models_1.SigningScheme.EIP712);
22
+ (0, vitest_1.expect)(sig.scheme).toBe(models_1.SigningScheme.EIP712);
23
+ (0, vitest_1.expect)(sig.signature.length).toBe(132);
24
+ });
25
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const utils_1 = require("../src/utils");
5
+ (0, vitest_1.describe)('utils', () => {
6
+ (0, vitest_1.it)('validateUrl rejects bad input', () => {
7
+ (0, vitest_1.expect)(() => (0, utils_1.validateUrl)('not-a-url')).toThrow(utils_1.ValidationError);
8
+ });
9
+ (0, vitest_1.it)('normalizePrivateKey strips prefix and lowercases', () => {
10
+ const key = (0, utils_1.normalizePrivateKey)('0xABCDEF' + '0'.repeat(58));
11
+ (0, vitest_1.expect)(key).toBe('0xabcdef' + '0'.repeat(58));
12
+ });
13
+ (0, vitest_1.it)('parseU256 accepts hex strings and serializes back', () => {
14
+ const value = (0, utils_1.parseU256)('0x10');
15
+ (0, vitest_1.expect)(value).toBe(16n);
16
+ (0, vitest_1.expect)((0, utils_1.serializeU256)(value)).toBe('0x10');
17
+ });
18
+ (0, vitest_1.it)('normalizeAddress round trips checksum', () => {
19
+ const addr = '0x0000000000000000000000000000000000000001';
20
+ (0, vitest_1.expect)((0, utils_1.normalizeAddress)(addr)).toBe('0x0000000000000000000000000000000000000001');
21
+ });
22
+ (0, vitest_1.it)('parseU256 rejects negatives', () => {
23
+ (0, vitest_1.expect)(() => (0, utils_1.parseU256)(-1)).toThrow(utils_1.ValidationError);
24
+ });
25
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const models_1 = require("../src/models");
5
+ const x402_1 = require("../src/x402");
6
+ const errors_1 = require("../src/errors");
7
+ class StubSigner {
8
+ async signPayment() {
9
+ return { signature: 'deadbeef', scheme: models_1.SigningScheme.EIP712 };
10
+ }
11
+ }
12
+ class StubX402Flow extends x402_1.X402Flow {
13
+ async requestTab() {
14
+ return new x402_1.TabResponse('2', '0x0000000000000000000000000000000000000001');
15
+ }
16
+ }
17
+ (0, vitest_1.describe)('X402Flow', () => {
18
+ (0, vitest_1.it)('rejects invalid scheme', async () => {
19
+ const flow = new StubX402Flow(new StubSigner());
20
+ const requirements = new x402_1.PaymentRequirements('http+pay', 'testnet', '1', '0x0000000000000000000000000000000000000003', '0x0000000000000000000000000000000000000000', { tabEndpoint: 'https://example.com' });
21
+ await (0, vitest_1.expect)(flow.signPayment(requirements, '0x0000000000000000000000000000000000000001')).rejects.toThrow(errors_1.X402Error);
22
+ });
23
+ (0, vitest_1.it)('builds header and payload', async () => {
24
+ const flow = new StubX402Flow(new StubSigner());
25
+ const requirements = new x402_1.PaymentRequirements('4mica+pay', 'testnet', '5', '0x0000000000000000000000000000000000000003', '0x0000000000000000000000000000000000000000', { tabEndpoint: 'https://example.com' });
26
+ const userAddress = '0x0000000000000000000000000000000000000001';
27
+ const signed = await flow.signPayment(requirements, userAddress);
28
+ const decoded = Buffer.from(signed.header, 'base64').toString('utf8');
29
+ const envelope = JSON.parse(decoded);
30
+ (0, vitest_1.expect)(envelope.x402Version).toBe(1);
31
+ (0, vitest_1.expect)(envelope.scheme).toBe('4mica+pay');
32
+ (0, vitest_1.expect)(envelope.payload.claims.tab_id).toBe('0x2');
33
+ (0, vitest_1.expect)(signed.claims.tabId).toBe(2n);
34
+ (0, vitest_1.expect)(signed.claims.amount).toBe(5n);
35
+ });
36
+ (0, vitest_1.it)('settles payment through facilitator', async () => {
37
+ const userAddress = '0x0000000000000000000000000000000000000009';
38
+ const tabEndpoint = 'http://facilitator.test/tab';
39
+ const facilitatorUrl = 'http://facilitator.test';
40
+ const requirements = new x402_1.PaymentRequirements('4mica+pay', 'testnet', '5', '0x00000000000000000000000000000000000000ff', '0x0000000000000000000000000000000000000000', { tabEndpoint });
41
+ const fetch = async (url, init) => {
42
+ const u = new URL(url);
43
+ if (u.pathname === '/tab') {
44
+ const body = JSON.parse(init?.body);
45
+ (0, vitest_1.expect)(body.userAddress).toBe(userAddress);
46
+ return new Response(JSON.stringify({ tabId: '0x1234', userAddress }), { status: 200 });
47
+ }
48
+ if (u.pathname === '/settle') {
49
+ const payload = JSON.parse(init?.body);
50
+ (0, vitest_1.expect)(payload.paymentRequirements.payTo).toBe(requirements.payTo);
51
+ return new Response(JSON.stringify({ settled: true, networkId: requirements.network }), {
52
+ status: 200,
53
+ });
54
+ }
55
+ return new Response('not found', { status: 404 });
56
+ };
57
+ const flow = new x402_1.X402Flow(new StubSigner(), fetch);
58
+ const payment = await flow.signPayment(requirements, userAddress);
59
+ (0, vitest_1.expect)(payment.claims.tabId).toBe(0x1234n);
60
+ const settled = await flow.settlePayment(payment, requirements, facilitatorUrl);
61
+ (0, vitest_1.expect)(settled.settlement.settled).toBe(true);
62
+ (0, vitest_1.expect)(settled.settlement.networkId).toBe(requirements.network);
63
+ (0, vitest_1.expect)(settled.payment.claims.recipientAddress).toBe(requirements.payTo);
64
+ });
65
+ });
@@ -0,0 +1,8 @@
1
+ export declare class ValidationError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare function validateUrl(raw: string): string;
5
+ export declare function normalizePrivateKey(raw: string): string;
6
+ export declare function normalizeAddress(raw: string): string;
7
+ export declare function parseU256(value: number | bigint | string): bigint;
8
+ export declare function serializeU256(value: number | bigint | string): string;
package/dist/utils.js ADDED
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidationError = void 0;
4
+ exports.validateUrl = validateUrl;
5
+ exports.normalizePrivateKey = normalizePrivateKey;
6
+ exports.normalizeAddress = normalizeAddress;
7
+ exports.parseU256 = parseU256;
8
+ exports.serializeU256 = serializeU256;
9
+ const ethers_1 = require("ethers");
10
+ class ValidationError extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = 'ValidationError';
14
+ }
15
+ }
16
+ exports.ValidationError = ValidationError;
17
+ function validateUrl(raw) {
18
+ try {
19
+ const url = new URL(raw);
20
+ if (!url.protocol || !url.host) {
21
+ throw new Error('missing parts');
22
+ }
23
+ return raw;
24
+ }
25
+ catch {
26
+ throw new ValidationError(`invalid URL: ${raw}`);
27
+ }
28
+ }
29
+ function normalizePrivateKey(raw) {
30
+ const key = raw.startsWith('0x') ? raw.slice(2) : raw;
31
+ if (!/^[0-9a-fA-F]{64}$/.test(key)) {
32
+ throw new ValidationError('invalid private key (expected 32 byte hex)');
33
+ }
34
+ return `0x${key.toLowerCase()}`;
35
+ }
36
+ function normalizeAddress(raw) {
37
+ const candidate = String(raw);
38
+ if ((0, ethers_1.isAddress)(candidate)) {
39
+ return (0, ethers_1.getAddress)(candidate);
40
+ }
41
+ const lower = candidate.toLowerCase();
42
+ if ((0, ethers_1.isAddress)(lower)) {
43
+ return (0, ethers_1.getAddress)(lower);
44
+ }
45
+ throw new ValidationError(`invalid address: ${raw}`);
46
+ }
47
+ function parseNumericString(raw) {
48
+ const text = raw.trim();
49
+ const n = BigInt(text);
50
+ if (n < 0n) {
51
+ throw new ValidationError('u256 cannot be negative');
52
+ }
53
+ return n;
54
+ }
55
+ function parseU256(value) {
56
+ if (typeof value === 'number') {
57
+ if (!Number.isFinite(value)) {
58
+ throw new ValidationError('invalid integer');
59
+ }
60
+ if (value < 0) {
61
+ throw new ValidationError('u256 cannot be negative');
62
+ }
63
+ return BigInt(value);
64
+ }
65
+ if (typeof value === 'bigint') {
66
+ if (value < 0) {
67
+ throw new ValidationError('u256 cannot be negative');
68
+ }
69
+ return value;
70
+ }
71
+ if (typeof value === 'string') {
72
+ return parseNumericString(value);
73
+ }
74
+ throw new ValidationError(`unsupported numeric type: ${typeof value}`);
75
+ }
76
+ function serializeU256(value) {
77
+ return `0x${parseU256(value).toString(16)}`;
78
+ }
@@ -0,0 +1,22 @@
1
+ import { PaymentGuaranteeRequestClaims, PaymentSignature, SigningScheme } from '../models';
2
+ import { PaymentRequirementsV1, TabResponse, X402SignedPayment, X402SettledPayment, PaymentRequirementsV2, X402PaymentRequired, X402ResourceInfo } from './models';
3
+ import type { FetchFn } from '../rpc';
4
+ export * from './models';
5
+ export interface FlowSigner {
6
+ signPayment(claims: PaymentGuaranteeRequestClaims, scheme: SigningScheme): Promise<PaymentSignature>;
7
+ }
8
+ export declare class X402Flow {
9
+ private signer;
10
+ private fetchFn;
11
+ constructor(signer: FlowSigner, fetchFn?: FetchFn);
12
+ static fromClient(client: {
13
+ user: FlowSigner;
14
+ }): X402Flow;
15
+ signPayment(paymentRequirements: PaymentRequirementsV1, userAddress: string): Promise<X402SignedPayment>;
16
+ signPaymentV2(paymentRequired: X402PaymentRequired, accepted: PaymentRequirementsV2, userAddress: string): Promise<X402SignedPayment>;
17
+ settlePayment(payment: X402SignedPayment, paymentRequirements: PaymentRequirementsV1, facilitatorUrl: string): Promise<X402SettledPayment>;
18
+ protected requestTab(x402Version: number, paymentRequirements: PaymentRequirementsV1 | PaymentRequirementsV2, userAddress: string, resource?: X402ResourceInfo): Promise<TabResponse>;
19
+ protected buildClaims(requirements: PaymentRequirementsV1 | PaymentRequirementsV2, tab: TabResponse, userAddress: string): PaymentGuaranteeRequestClaims;
20
+ private static validateScheme;
21
+ private static buildPaymentPayload;
22
+ }
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.X402Flow = void 0;
18
+ const models_1 = require("../models");
19
+ const utils_1 = require("../utils");
20
+ const errors_1 = require("../errors");
21
+ __exportStar(require("./models"), exports);
22
+ class X402Flow {
23
+ constructor(signer, fetchFn = fetch) {
24
+ this.signer = signer;
25
+ this.fetchFn = fetchFn;
26
+ }
27
+ static fromClient(client) {
28
+ return new X402Flow(client.user);
29
+ }
30
+ async signPayment(paymentRequirements, userAddress) {
31
+ X402Flow.validateScheme(paymentRequirements.scheme);
32
+ const tab = await this.requestTab(1, paymentRequirements, userAddress);
33
+ const claims = this.buildClaims(paymentRequirements, tab, userAddress);
34
+ const signature = await this.signer.signPayment(claims, models_1.SigningScheme.EIP712);
35
+ const paymentPayload = X402Flow.buildPaymentPayload(claims, signature);
36
+ const envelope = {
37
+ x402Version: 1,
38
+ scheme: paymentRequirements.scheme,
39
+ network: paymentRequirements.network,
40
+ payload: paymentPayload,
41
+ };
42
+ const header = Buffer.from(JSON.stringify(envelope)).toString('base64');
43
+ return { header, payload: paymentPayload, signature };
44
+ }
45
+ async signPaymentV2(paymentRequired, accepted, userAddress) {
46
+ X402Flow.validateScheme(accepted.scheme);
47
+ const tab = await this.requestTab(2, accepted, userAddress, paymentRequired.resource);
48
+ const claims = this.buildClaims(accepted, tab, userAddress);
49
+ const signature = await this.signer.signPayment(claims, models_1.SigningScheme.EIP712);
50
+ const paymentPayload = X402Flow.buildPaymentPayload(claims, signature);
51
+ const envelope = {
52
+ x402Version: 2,
53
+ accepted: accepted,
54
+ payload: paymentPayload,
55
+ resource: paymentRequired.resource,
56
+ };
57
+ const header = Buffer.from(JSON.stringify(envelope)).toString('base64');
58
+ return { header, payload: paymentPayload, signature };
59
+ }
60
+ async settlePayment(payment, paymentRequirements, facilitatorUrl) {
61
+ const url = `${facilitatorUrl.replace(/\/$/, '')}/settle`;
62
+ const response = await this.fetchFn(url, {
63
+ method: 'POST',
64
+ headers: { 'content-type': 'application/json' },
65
+ body: JSON.stringify({
66
+ x402Version: 1,
67
+ paymentHeader: payment.header,
68
+ paymentRequirements,
69
+ }),
70
+ });
71
+ const data = await response.text();
72
+ if (!response.ok) {
73
+ throw new errors_1.X402Error(`settlement failed with status ${response.status}: ${data}`);
74
+ }
75
+ const settlement = data ? JSON.parse(data) : {};
76
+ return { payment, settlement };
77
+ }
78
+ async requestTab(x402Version, paymentRequirements, userAddress, resource) {
79
+ const tabEndpoint = paymentRequirements.extra?.tabEndpoint;
80
+ if (!tabEndpoint || typeof tabEndpoint !== 'string') {
81
+ throw new errors_1.X402Error('missing tabEndpoint in paymentRequirements.extra');
82
+ }
83
+ const resp = await this.fetchFn(tabEndpoint, {
84
+ method: 'POST',
85
+ headers: { 'content-type': 'application/json' },
86
+ body: JSON.stringify({
87
+ x402Version,
88
+ userAddress,
89
+ paymentRequirements,
90
+ resource,
91
+ }),
92
+ });
93
+ if (!resp.ok) {
94
+ const text = await resp.text();
95
+ throw new errors_1.X402Error(`tab resolution failed: ${resp.status} ${text}`);
96
+ }
97
+ const body = await resp.json();
98
+ return {
99
+ tabId: body.tabId ?? body.tab_id,
100
+ userAddress: body.userAddress ?? body.user_address,
101
+ nextReqId: body.nextReqId ?? body.next_req_id ?? body.reqId ?? body.req_id,
102
+ };
103
+ }
104
+ buildClaims(requirements, tab, userAddress) {
105
+ const tabId = (0, utils_1.parseU256)(tab.tabId);
106
+ const reqId = tab.nextReqId !== undefined && tab.nextReqId !== null ? (0, utils_1.parseU256)(tab.nextReqId) : 0n;
107
+ const amount = (0, utils_1.parseU256)('maxAmountRequired' in requirements ? requirements.maxAmountRequired : requirements.amount);
108
+ if (tab.userAddress.toLowerCase() !== userAddress.toLowerCase()) {
109
+ throw new errors_1.X402Error(`user mismatch in paymentRequirements: found ${tab.userAddress}, expected ${userAddress}`);
110
+ }
111
+ const timestamp = Math.floor(Date.now() / 1000);
112
+ return models_1.PaymentGuaranteeRequestClaims.new(userAddress, (0, utils_1.normalizeAddress)(requirements.payTo), tabId, amount, timestamp, requirements.asset, reqId);
113
+ }
114
+ static validateScheme(scheme) {
115
+ if (!scheme.toLowerCase().includes('4mica')) {
116
+ throw new errors_1.X402Error(`invalid scheme: ${scheme}`);
117
+ }
118
+ }
119
+ static buildPaymentPayload(claims, signature) {
120
+ return {
121
+ claims: {
122
+ version: 'v1',
123
+ user_address: claims.userAddress,
124
+ recipient_address: claims.recipientAddress,
125
+ tab_id: `0x${claims.tabId.toString(16)}`,
126
+ req_id: `0x${claims.reqId.toString(16)}`,
127
+ amount: `0x${claims.amount.toString(16)}`,
128
+ asset_address: claims.assetAddress,
129
+ timestamp: claims.timestamp,
130
+ },
131
+ signature: signature.signature,
132
+ scheme: signature.scheme,
133
+ };
134
+ }
135
+ }
136
+ exports.X402Flow = X402Flow;
@@ -0,0 +1,80 @@
1
+ import { PaymentSignature, SigningScheme } from '../models';
2
+ export interface PaymentRequirementsV1 {
3
+ scheme: string;
4
+ network: string;
5
+ maxAmountRequired: string;
6
+ payTo: string;
7
+ asset: string;
8
+ resource?: string;
9
+ description?: string;
10
+ mimeType?: string;
11
+ outputSchema?: unknown;
12
+ maxTimeoutSeconds?: number;
13
+ extra?: PaymentRequirementsExtra;
14
+ }
15
+ export interface PaymentRequirementsV2 {
16
+ scheme: string;
17
+ network: string;
18
+ asset: string;
19
+ amount: string;
20
+ payTo: string;
21
+ maxTimeoutSeconds?: number;
22
+ extra?: PaymentRequirementsExtra;
23
+ }
24
+ export type PaymentRequirements = PaymentRequirementsV2;
25
+ export interface PaymentRequirementsExtra {
26
+ tabEndpoint?: string;
27
+ }
28
+ export interface TabResponse {
29
+ tabId: string;
30
+ userAddress: string;
31
+ nextReqId?: string;
32
+ }
33
+ export interface X402PaymentPayloadClaims {
34
+ version: string;
35
+ user_address: string;
36
+ recipient_address: string;
37
+ tab_id: string;
38
+ req_id: string;
39
+ amount: string;
40
+ timestamp: number;
41
+ asset_address: string;
42
+ }
43
+ export interface X402PaymentPayload {
44
+ claims: X402PaymentPayloadClaims;
45
+ signature: string;
46
+ scheme: SigningScheme;
47
+ }
48
+ export interface X402PaymentEnvelopeV1 {
49
+ x402Version: number;
50
+ scheme: string;
51
+ network: string;
52
+ payload: X402PaymentPayload;
53
+ }
54
+ export interface X402ResourceInfo {
55
+ url: string;
56
+ description: string;
57
+ mimeType: string;
58
+ }
59
+ export interface X402PaymentEnvelopeV2 {
60
+ x402Version: number;
61
+ accepted: PaymentRequirementsV2;
62
+ payload: X402PaymentPayload;
63
+ resource: X402ResourceInfo;
64
+ }
65
+ export interface X402SignedPayment {
66
+ header: string;
67
+ payload: X402PaymentPayload;
68
+ signature: PaymentSignature;
69
+ }
70
+ export interface X402SettledPayment {
71
+ payment: X402SignedPayment;
72
+ settlement: unknown;
73
+ }
74
+ export interface X402PaymentRequired {
75
+ x402Version: number;
76
+ error?: string;
77
+ resource: X402ResourceInfo;
78
+ accepts: PaymentRequirementsV2[];
79
+ extensions?: Record<string, unknown>;
80
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/x402.d.ts ADDED
@@ -0,0 +1,78 @@
1
+ import { PaymentGuaranteeRequestClaims, PaymentSignature, SigningScheme } from './models';
2
+ import type { FetchFn } from './rpc';
3
+ export interface PaymentRequirementsInit {
4
+ scheme: string;
5
+ network: string;
6
+ maxAmountRequired: string;
7
+ payTo: string;
8
+ asset: string;
9
+ extra?: Record<string, unknown>;
10
+ resource?: string;
11
+ description?: string;
12
+ mimeType?: string;
13
+ outputSchema?: unknown;
14
+ maxTimeoutSeconds?: number;
15
+ }
16
+ export declare class PaymentRequirements {
17
+ scheme: string;
18
+ network: string;
19
+ maxAmountRequired: string;
20
+ payTo: string;
21
+ asset: string;
22
+ extra: Record<string, unknown>;
23
+ resource?: string | undefined;
24
+ description?: string | undefined;
25
+ mimeType?: string | undefined;
26
+ outputSchema?: unknown | undefined;
27
+ maxTimeoutSeconds?: number | undefined;
28
+ constructor(scheme: string, network: string, maxAmountRequired: string, payTo: string, asset: string, extra: Record<string, unknown>, resource?: string | undefined, description?: string | undefined, mimeType?: string | undefined, outputSchema?: unknown | undefined, maxTimeoutSeconds?: number | undefined);
29
+ static fromRaw(raw: Record<string, unknown>): PaymentRequirements;
30
+ toPayload(): Record<string, unknown>;
31
+ }
32
+ export declare class PaymentRequirementsExtra {
33
+ tabEndpoint?: string | null | undefined;
34
+ constructor(tabEndpoint?: string | null | undefined);
35
+ static fromRaw(raw: Record<string, unknown> | undefined): PaymentRequirementsExtra;
36
+ }
37
+ export declare class TabResponse {
38
+ tabId: string;
39
+ userAddress: string;
40
+ nextReqId?: string | null | undefined;
41
+ constructor(tabId: string, userAddress: string, nextReqId?: string | null | undefined);
42
+ }
43
+ export declare class X402PaymentEnvelope {
44
+ x402Version: number;
45
+ scheme: string;
46
+ network: string;
47
+ payload: Record<string, unknown>;
48
+ constructor(x402Version: number, scheme: string, network: string, payload: Record<string, unknown>);
49
+ toPayload(): Record<string, unknown>;
50
+ }
51
+ export declare class X402SignedPayment {
52
+ header: string;
53
+ claims: PaymentGuaranteeRequestClaims;
54
+ signature: PaymentSignature;
55
+ constructor(header: string, claims: PaymentGuaranteeRequestClaims, signature: PaymentSignature);
56
+ }
57
+ export declare class X402SettledPayment {
58
+ payment: X402SignedPayment;
59
+ settlement: unknown;
60
+ constructor(payment: X402SignedPayment, settlement: unknown);
61
+ }
62
+ export interface FlowSigner {
63
+ signPayment(claims: PaymentGuaranteeRequestClaims, scheme: SigningScheme): Promise<PaymentSignature>;
64
+ }
65
+ export declare class X402Flow {
66
+ private signer;
67
+ private fetchFn;
68
+ constructor(signer: FlowSigner, fetchFn?: FetchFn);
69
+ static fromClient(client: {
70
+ user: FlowSigner;
71
+ }): X402Flow;
72
+ signPayment(paymentRequirements: PaymentRequirements, userAddress: string, existingTabId?: bigint | number, existingReqId?: bigint | number): Promise<X402SignedPayment>;
73
+ settlePayment(payment: X402SignedPayment, paymentRequirements: PaymentRequirements, facilitatorUrl: string): Promise<X402SettledPayment>;
74
+ protected requestTab(paymentRequirements: PaymentRequirements, userAddress: string): Promise<TabResponse>;
75
+ protected buildClaims(requirements: PaymentRequirements, tab: TabResponse, userAddress: string): PaymentGuaranteeRequestClaims;
76
+ private static validateScheme;
77
+ private static buildEnvelope;
78
+ }