@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.
- package/.eslintrc.cjs +29 -0
- package/.github/workflows/ci.yml +31 -0
- package/.prettierignore +3 -0
- package/.prettierrc +6 -0
- package/LICENSE +21 -0
- package/README.md +444 -0
- package/dist/abi/core4mica.json +1605 -0
- package/dist/abi/erc20.json +187 -0
- package/dist/auth.d.ts +62 -0
- package/dist/auth.js +255 -0
- package/dist/bls.d.ts +5 -0
- package/dist/bls.js +58 -0
- package/dist/client.d.ts +58 -0
- package/dist/client.js +245 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.js +123 -0
- package/dist/contract.d.ts +33 -0
- package/dist/contract.js +134 -0
- package/dist/errors.d.ts +43 -0
- package/dist/errors.js +62 -0
- package/dist/guarantee.d.ts +3 -0
- package/dist/guarantee.js +66 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +28 -0
- package/dist/models.d.ts +150 -0
- package/dist/models.js +176 -0
- package/dist/rpc.d.ts +37 -0
- package/dist/rpc.js +163 -0
- package/dist/signing.d.ts +106 -0
- package/dist/signing.js +223 -0
- package/dist/src/abi/core4mica.json +1605 -0
- package/dist/src/abi/erc20.json +187 -0
- package/dist/src/bls.d.ts +5 -0
- package/dist/src/bls.js +45 -0
- package/dist/src/client.d.ts +55 -0
- package/dist/src/client.js +214 -0
- package/dist/src/config.d.ts +21 -0
- package/dist/src/config.js +76 -0
- package/dist/src/contract.d.ts +33 -0
- package/dist/src/contract.js +121 -0
- package/dist/src/errors.d.ts +17 -0
- package/dist/src/errors.js +31 -0
- package/dist/src/guarantee.d.ts +3 -0
- package/dist/src/guarantee.js +66 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.js +27 -0
- package/dist/src/models.d.ts +119 -0
- package/dist/src/models.js +132 -0
- package/dist/src/rpc.d.ts +30 -0
- package/dist/src/rpc.js +122 -0
- package/dist/src/signing.d.ts +16 -0
- package/dist/src/signing.js +102 -0
- package/dist/src/utils.d.ts +8 -0
- package/dist/src/utils.js +78 -0
- package/dist/src/x402.d.ts +77 -0
- package/dist/src/x402.js +214 -0
- package/dist/tests/config.test.d.ts +1 -0
- package/dist/tests/config.test.js +26 -0
- package/dist/tests/guarantee.test.d.ts +1 -0
- package/dist/tests/guarantee.test.js +26 -0
- package/dist/tests/rpc.test.d.ts +1 -0
- package/dist/tests/rpc.test.js +44 -0
- package/dist/tests/signing.test.d.ts +1 -0
- package/dist/tests/signing.test.js +25 -0
- package/dist/tests/utils.test.d.ts +1 -0
- package/dist/tests/utils.test.js +25 -0
- package/dist/tests/x402.test.d.ts +1 -0
- package/dist/tests/x402.test.js +65 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +78 -0
- package/dist/x402/index.d.ts +22 -0
- package/dist/x402/index.js +136 -0
- package/dist/x402/models.d.ts +80 -0
- package/dist/x402/models.js +2 -0
- package/dist/x402.d.ts +78 -0
- package/dist/x402.js +231 -0
- package/eslint.config.mjs +22 -0
- package/package.json +31 -0
- package/src/abi/core4mica.json +1605 -0
- package/src/abi/erc20.json +187 -0
- package/src/auth.ts +325 -0
- package/src/bls.ts +76 -0
- package/src/client.ts +347 -0
- package/src/config.ts +149 -0
- package/src/contract.ts +194 -0
- package/src/errors.ts +40 -0
- package/src/guarantee.ts +96 -0
- package/src/index.ts +12 -0
- package/src/models.ts +309 -0
- package/src/rpc.ts +225 -0
- package/src/signing.ts +330 -0
- package/src/utils.ts +75 -0
- package/src/x402/index.ts +192 -0
- package/src/x402/models.ts +94 -0
- package/tests/auth.integration.test.ts +97 -0
- package/tests/auth.test.ts +292 -0
- package/tests/config.test.ts +56 -0
- package/tests/guarantee.test.ts +26 -0
- package/tests/rpc.test.ts +76 -0
- package/tests/signing.test.ts +52 -0
- package/tests/utils.test.ts +35 -0
- package/tests/x402.test.ts +152 -0
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +15 -0
- package/vitest.config.ts +12 -0
package/dist/x402.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.X402Flow = exports.X402SettledPayment = exports.X402SignedPayment = exports.X402PaymentEnvelope = exports.TabResponse = exports.PaymentRequirementsExtra = exports.PaymentRequirements = void 0;
|
|
4
|
+
const models_1 = require("./models");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
const errors_1 = require("./errors");
|
|
7
|
+
const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
8
|
+
class PaymentRequirements {
|
|
9
|
+
constructor(scheme, network, maxAmountRequired, payTo, asset, extra, resource, description, mimeType, outputSchema, maxTimeoutSeconds) {
|
|
10
|
+
this.scheme = scheme;
|
|
11
|
+
this.network = network;
|
|
12
|
+
this.maxAmountRequired = maxAmountRequired;
|
|
13
|
+
this.payTo = payTo;
|
|
14
|
+
this.asset = asset;
|
|
15
|
+
this.extra = extra;
|
|
16
|
+
this.resource = resource;
|
|
17
|
+
this.description = description;
|
|
18
|
+
this.mimeType = mimeType;
|
|
19
|
+
this.outputSchema = outputSchema;
|
|
20
|
+
this.maxTimeoutSeconds = maxTimeoutSeconds;
|
|
21
|
+
}
|
|
22
|
+
static fromRaw(raw) {
|
|
23
|
+
const pick = (keys, defaultValue) => {
|
|
24
|
+
for (const key of keys) {
|
|
25
|
+
const value = raw[key];
|
|
26
|
+
if (value !== undefined && value !== null)
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
return defaultValue;
|
|
30
|
+
};
|
|
31
|
+
const amount = pick(['maxAmountRequired', 'max_amount_required']);
|
|
32
|
+
const payTo = pick(['payTo', 'pay_to']);
|
|
33
|
+
const asset = pick(['asset', 'assetAddress', 'asset_address']);
|
|
34
|
+
const scheme = pick(['scheme']);
|
|
35
|
+
const network = pick(['network']);
|
|
36
|
+
if (!amount || !payTo || !asset || !scheme || !network) {
|
|
37
|
+
const missing = [
|
|
38
|
+
['scheme', scheme],
|
|
39
|
+
['network', network],
|
|
40
|
+
['maxAmountRequired', amount],
|
|
41
|
+
['payTo', payTo],
|
|
42
|
+
['asset', asset],
|
|
43
|
+
]
|
|
44
|
+
.filter(([, value]) => !value)
|
|
45
|
+
.map(([key]) => key)
|
|
46
|
+
.join(', ');
|
|
47
|
+
throw new errors_1.X402Error(`payment requirements missing fields: ${missing}`);
|
|
48
|
+
}
|
|
49
|
+
return new PaymentRequirements(String(scheme), String(network), String(amount), String(payTo), String(asset), pick(['extra'], {}) ?? {}, pick(['resource']), pick(['description']), pick(['mimeType', 'mime_type']), pick(['outputSchema', 'output_schema']), pick(['maxTimeoutSeconds', 'max_timeout_seconds']));
|
|
50
|
+
}
|
|
51
|
+
toPayload() {
|
|
52
|
+
const extraPayload = { ...(this.extra ?? {}) };
|
|
53
|
+
if ('tab_endpoint' in extraPayload && !('tabEndpoint' in extraPayload)) {
|
|
54
|
+
extraPayload['tabEndpoint'] = extraPayload['tab_endpoint'];
|
|
55
|
+
delete extraPayload['tab_endpoint'];
|
|
56
|
+
}
|
|
57
|
+
const payload = {
|
|
58
|
+
scheme: this.scheme,
|
|
59
|
+
network: this.network,
|
|
60
|
+
maxAmountRequired: this.maxAmountRequired,
|
|
61
|
+
payTo: this.payTo,
|
|
62
|
+
asset: this.asset,
|
|
63
|
+
extra: extraPayload,
|
|
64
|
+
};
|
|
65
|
+
if (this.resource !== undefined)
|
|
66
|
+
payload.resource = this.resource;
|
|
67
|
+
if (this.description !== undefined)
|
|
68
|
+
payload.description = this.description;
|
|
69
|
+
if (this.mimeType !== undefined)
|
|
70
|
+
payload.mimeType = this.mimeType;
|
|
71
|
+
if (this.outputSchema !== undefined)
|
|
72
|
+
payload.outputSchema = this.outputSchema;
|
|
73
|
+
if (this.maxTimeoutSeconds !== undefined)
|
|
74
|
+
payload.maxTimeoutSeconds = this.maxTimeoutSeconds;
|
|
75
|
+
return payload;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.PaymentRequirements = PaymentRequirements;
|
|
79
|
+
class PaymentRequirementsExtra {
|
|
80
|
+
constructor(tabEndpoint) {
|
|
81
|
+
this.tabEndpoint = tabEndpoint;
|
|
82
|
+
}
|
|
83
|
+
static fromRaw(raw) {
|
|
84
|
+
const tabEndpointRaw = (isRecord(raw) ? raw.tabEndpoint : undefined) ??
|
|
85
|
+
(isRecord(raw) ? raw.tab_endpoint : undefined);
|
|
86
|
+
const tabEndpoint = typeof tabEndpointRaw === 'string'
|
|
87
|
+
? tabEndpointRaw
|
|
88
|
+
: tabEndpointRaw === null
|
|
89
|
+
? null
|
|
90
|
+
: undefined;
|
|
91
|
+
return new PaymentRequirementsExtra(tabEndpoint);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.PaymentRequirementsExtra = PaymentRequirementsExtra;
|
|
95
|
+
class TabResponse {
|
|
96
|
+
constructor(tabId, userAddress, nextReqId) {
|
|
97
|
+
this.tabId = tabId;
|
|
98
|
+
this.userAddress = userAddress;
|
|
99
|
+
this.nextReqId = nextReqId;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.TabResponse = TabResponse;
|
|
103
|
+
class X402PaymentEnvelope {
|
|
104
|
+
constructor(x402Version, scheme, network, payload) {
|
|
105
|
+
this.x402Version = x402Version;
|
|
106
|
+
this.scheme = scheme;
|
|
107
|
+
this.network = network;
|
|
108
|
+
this.payload = payload;
|
|
109
|
+
}
|
|
110
|
+
toPayload() {
|
|
111
|
+
return {
|
|
112
|
+
x402Version: this.x402Version,
|
|
113
|
+
scheme: this.scheme,
|
|
114
|
+
network: this.network,
|
|
115
|
+
payload: this.payload,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.X402PaymentEnvelope = X402PaymentEnvelope;
|
|
120
|
+
class X402SignedPayment {
|
|
121
|
+
constructor(header, claims, signature) {
|
|
122
|
+
this.header = header;
|
|
123
|
+
this.claims = claims;
|
|
124
|
+
this.signature = signature;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.X402SignedPayment = X402SignedPayment;
|
|
128
|
+
class X402SettledPayment {
|
|
129
|
+
constructor(payment, settlement) {
|
|
130
|
+
this.payment = payment;
|
|
131
|
+
this.settlement = settlement;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.X402SettledPayment = X402SettledPayment;
|
|
135
|
+
class X402Flow {
|
|
136
|
+
constructor(signer, fetchFn = fetch) {
|
|
137
|
+
this.signer = signer;
|
|
138
|
+
this.fetchFn = fetchFn;
|
|
139
|
+
}
|
|
140
|
+
static fromClient(client) {
|
|
141
|
+
return new X402Flow(client.user);
|
|
142
|
+
}
|
|
143
|
+
async signPayment(paymentRequirements, userAddress, existingTabId, existingReqId) {
|
|
144
|
+
X402Flow.validateScheme(paymentRequirements.scheme);
|
|
145
|
+
let tab;
|
|
146
|
+
if (existingTabId !== undefined && existingTabId !== null) {
|
|
147
|
+
tab = new TabResponse(String(existingTabId), userAddress, existingReqId !== undefined && existingReqId !== null ? String(existingReqId) : undefined);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
tab = await this.requestTab(paymentRequirements, userAddress);
|
|
151
|
+
}
|
|
152
|
+
const claims = this.buildClaims(paymentRequirements, tab, userAddress);
|
|
153
|
+
const signature = await this.signer.signPayment(claims, models_1.SigningScheme.EIP712);
|
|
154
|
+
const envelope = X402Flow.buildEnvelope(paymentRequirements, claims, signature);
|
|
155
|
+
const payload = envelope.toPayload();
|
|
156
|
+
payload.x402Version ?? (payload.x402Version = envelope.x402Version);
|
|
157
|
+
const header = Buffer.from(JSON.stringify(payload)).toString('base64');
|
|
158
|
+
return new X402SignedPayment(header, claims, signature);
|
|
159
|
+
}
|
|
160
|
+
async settlePayment(payment, paymentRequirements, facilitatorUrl) {
|
|
161
|
+
const url = `${facilitatorUrl.replace(/\/$/, '')}/settle`;
|
|
162
|
+
const response = await this.fetchFn(url, {
|
|
163
|
+
method: 'POST',
|
|
164
|
+
headers: { 'content-type': 'application/json' },
|
|
165
|
+
body: JSON.stringify({
|
|
166
|
+
x402Version: 1,
|
|
167
|
+
paymentHeader: payment.header,
|
|
168
|
+
paymentRequirements: paymentRequirements.toPayload(),
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
171
|
+
const data = await response.text();
|
|
172
|
+
if (!response.ok) {
|
|
173
|
+
throw new errors_1.X402Error(`settlement failed with status ${response.status}: ${data}`);
|
|
174
|
+
}
|
|
175
|
+
const settlement = data ? JSON.parse(data) : {};
|
|
176
|
+
return new X402SettledPayment(payment, settlement);
|
|
177
|
+
}
|
|
178
|
+
async requestTab(paymentRequirements, userAddress) {
|
|
179
|
+
const extra = PaymentRequirementsExtra.fromRaw(paymentRequirements.extra);
|
|
180
|
+
if (!extra.tabEndpoint) {
|
|
181
|
+
throw new errors_1.X402Error('missing tabEndpoint in paymentRequirements.extra');
|
|
182
|
+
}
|
|
183
|
+
const resp = await this.fetchFn(extra.tabEndpoint, {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
headers: { 'content-type': 'application/json' },
|
|
186
|
+
body: JSON.stringify({
|
|
187
|
+
userAddress,
|
|
188
|
+
paymentRequirements: paymentRequirements.toPayload(),
|
|
189
|
+
}),
|
|
190
|
+
});
|
|
191
|
+
if (!resp.ok) {
|
|
192
|
+
const text = await resp.text();
|
|
193
|
+
throw new errors_1.X402Error(`tab resolution failed: ${resp.status} ${text}`);
|
|
194
|
+
}
|
|
195
|
+
const body = await resp.json();
|
|
196
|
+
return new TabResponse(body.tabId ?? body.tab_id, body.userAddress ?? body.user_address, body.nextReqId ?? body.next_req_id ?? body.reqId ?? body.req_id);
|
|
197
|
+
}
|
|
198
|
+
buildClaims(requirements, tab, userAddress) {
|
|
199
|
+
const tabId = (0, utils_1.parseU256)(tab.tabId);
|
|
200
|
+
const reqId = tab.nextReqId !== undefined && tab.nextReqId !== null ? (0, utils_1.parseU256)(tab.nextReqId) : 0n;
|
|
201
|
+
const amount = (0, utils_1.parseU256)(requirements.maxAmountRequired);
|
|
202
|
+
if (tab.userAddress.toLowerCase() !== userAddress.toLowerCase()) {
|
|
203
|
+
throw new errors_1.X402Error(`user mismatch in paymentRequirements: found ${tab.userAddress}, expected ${userAddress}`);
|
|
204
|
+
}
|
|
205
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
206
|
+
return models_1.PaymentGuaranteeRequestClaims.new(userAddress, (0, utils_1.normalizeAddress)(requirements.payTo), tabId, amount, timestamp, requirements.asset, reqId);
|
|
207
|
+
}
|
|
208
|
+
static validateScheme(scheme) {
|
|
209
|
+
if (!scheme.toLowerCase().includes('4mica')) {
|
|
210
|
+
throw new errors_1.X402Error(`invalid scheme: ${scheme}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
static buildEnvelope(paymentRequirements, claims, signature) {
|
|
214
|
+
const payload = {
|
|
215
|
+
claims: {
|
|
216
|
+
version: 'v1',
|
|
217
|
+
user_address: claims.userAddress,
|
|
218
|
+
recipient_address: claims.recipientAddress,
|
|
219
|
+
tab_id: `0x${claims.tabId.toString(16)}`,
|
|
220
|
+
req_id: `0x${claims.reqId.toString(16)}`,
|
|
221
|
+
amount: `0x${claims.amount.toString(16)}`,
|
|
222
|
+
asset_address: claims.assetAddress,
|
|
223
|
+
timestamp: claims.timestamp,
|
|
224
|
+
},
|
|
225
|
+
signature: signature.signature,
|
|
226
|
+
scheme: signature.scheme,
|
|
227
|
+
};
|
|
228
|
+
return new X402PaymentEnvelope(1, paymentRequirements.scheme, paymentRequirements.network, payload);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
exports.X402Flow = X402Flow;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import globals from "globals";
|
|
3
|
+
import tseslint from "typescript-eslint";
|
|
4
|
+
import pluginReact from "eslint-plugin-react";
|
|
5
|
+
import { defineConfig } from "eslint/config";
|
|
6
|
+
|
|
7
|
+
export default defineConfig([
|
|
8
|
+
{
|
|
9
|
+
ignores: ["dist/**", "coverage/**", "node_modules/**", "*.cjs"],
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
|
|
13
|
+
plugins: { js },
|
|
14
|
+
extends: ["js/recommended"],
|
|
15
|
+
languageOptions: { globals: globals.browser },
|
|
16
|
+
},
|
|
17
|
+
tseslint.configs.recommended,
|
|
18
|
+
pluginReact.configs.flat.recommended,
|
|
19
|
+
{
|
|
20
|
+
settings: { react: { version: "detect" } },
|
|
21
|
+
},
|
|
22
|
+
]);
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@4mica/sdk",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "TypeScript SDK for interacting with the 4Mica payment network",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc -p tsconfig.build.json",
|
|
9
|
+
"test": "vitest run",
|
|
10
|
+
"lint": "eslint . --ext .ts",
|
|
11
|
+
"fmt": "prettier --check \"{src,tests}/**/*.{ts,js,json}\""
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"ethers": "^6.16.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@eslint/js": "^9.39.2",
|
|
18
|
+
"@types/node": "^25.2.0",
|
|
19
|
+
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
20
|
+
"@typescript-eslint/parser": "^8.54.0",
|
|
21
|
+
"eslint": "^9.39.2",
|
|
22
|
+
"eslint-config-prettier": "^10.1.8",
|
|
23
|
+
"eslint-plugin-react": "^7.37.5",
|
|
24
|
+
"globals": "^17.3.0",
|
|
25
|
+
"prettier": "^3.8.1",
|
|
26
|
+
"typescript": "^5.3.3",
|
|
27
|
+
"typescript-eslint": "^8.54.0",
|
|
28
|
+
"vitest": "^4.0.17"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT"
|
|
31
|
+
}
|