@4mica/sdk 1.1.0 → 1.2.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 +85 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,12 +25,30 @@ yarn add @4mica/sdk
|
|
|
25
25
|
|
|
26
26
|
Node.js 18+ is required.
|
|
27
27
|
|
|
28
|
+
## Networks
|
|
29
|
+
|
|
30
|
+
| Shorthand | CAIP-2 | Core API URL |
|
|
31
|
+
| ------------------ | ----------------- | ----------------------------------------- |
|
|
32
|
+
| `ethereum-sepolia` | `eip155:11155111` | `https://ethereum.sepolia.api.4mica.xyz/` |
|
|
33
|
+
| `base-sepolia` | `eip155:84532` | `https://base.sepolia.api.4mica.xyz/` |
|
|
34
|
+
|
|
35
|
+
The default network is Ethereum Sepolia. Use `.network()` or the `4MICA_NETWORK` environment
|
|
36
|
+
variable to switch networks.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { NETWORKS } from '@4mica/sdk';
|
|
40
|
+
|
|
41
|
+
console.log(NETWORKS['base-sepolia'].caip2); // "eip155:84532"
|
|
42
|
+
console.log(NETWORKS['base-sepolia'].rpcUrl); // "https://base.sepolia.api.4mica.xyz/"
|
|
43
|
+
```
|
|
44
|
+
|
|
28
45
|
## Initialization and Configuration
|
|
29
46
|
|
|
30
47
|
The SDK requires a signing key and can use sensible defaults for the rest:
|
|
31
48
|
|
|
32
49
|
- `walletPrivateKey` (**required** unless `signer` is provided): private key used for signing
|
|
33
|
-
- `
|
|
50
|
+
- `network` (optional): select a network by shorthand or CAIP-2 id. Defaults to `ethereum-sepolia`.
|
|
51
|
+
- `rpcUrl` (optional): override the core API URL directly (for self-hosted deployments).
|
|
34
52
|
- `ethereumHttpRpcUrl` (optional): Ethereum JSON-RPC endpoint; fetched from core if omitted
|
|
35
53
|
- `contractAddress` (optional): Core4Mica contract address; fetched from core if omitted
|
|
36
54
|
- `adminApiKey` (optional): API key for admin RPCs
|
|
@@ -45,12 +63,12 @@ The SDK requires a signing key and can use sensible defaults for the rest:
|
|
|
45
63
|
### 1) Using ConfigBuilder
|
|
46
64
|
|
|
47
65
|
```ts
|
|
48
|
-
import { Client, ConfigBuilder } from
|
|
66
|
+
import { Client, ConfigBuilder } from '@4mica/sdk';
|
|
49
67
|
|
|
50
68
|
async function main() {
|
|
51
69
|
const cfg = new ConfigBuilder()
|
|
52
|
-
.
|
|
53
|
-
.walletPrivateKey(
|
|
70
|
+
.network('base-sepolia') // or "ethereum-sepolia" (default)
|
|
71
|
+
.walletPrivateKey('0x...')
|
|
54
72
|
.build();
|
|
55
73
|
|
|
56
74
|
const client = await Client.new(cfg);
|
|
@@ -68,12 +86,14 @@ Set environment variables (example `.env`):
|
|
|
68
86
|
|
|
69
87
|
```bash
|
|
70
88
|
4MICA_WALLET_PRIVATE_KEY="0x..."
|
|
71
|
-
|
|
89
|
+
4MICA_NETWORK="base-sepolia" # shorthand or CAIP-2 id
|
|
90
|
+
# or override URL directly:
|
|
91
|
+
# 4MICA_RPC_URL="https://base.sepolia.api.4mica.xyz/"
|
|
72
92
|
4MICA_ETHEREUM_HTTP_RPC_URL="http://localhost:8545"
|
|
73
93
|
4MICA_CONTRACT_ADDRESS="0x..."
|
|
74
94
|
4MICA_ADMIN_API_KEY="ak_..."
|
|
75
95
|
4MICA_BEARER_TOKEN="Bearer <access_token>"
|
|
76
|
-
4MICA_AUTH_URL="https://api.4mica.xyz/"
|
|
96
|
+
4MICA_AUTH_URL="https://ethereum.sepolia.api.4mica.xyz/"
|
|
77
97
|
4MICA_AUTH_REFRESH_MARGIN_SECS="60"
|
|
78
98
|
```
|
|
79
99
|
|
|
@@ -81,13 +101,13 @@ If you want to set them inline for a single command, use `env` since most shells
|
|
|
81
101
|
variable names that start with a digit:
|
|
82
102
|
|
|
83
103
|
```bash
|
|
84
|
-
env 4MICA_WALLET_PRIVATE_KEY="0x..."
|
|
104
|
+
env 4MICA_WALLET_PRIVATE_KEY="0x..." 4MICA_NETWORK="base-sepolia" node app.js
|
|
85
105
|
```
|
|
86
106
|
|
|
87
107
|
Then in code:
|
|
88
108
|
|
|
89
109
|
```ts
|
|
90
|
-
import { Client, ConfigBuilder } from
|
|
110
|
+
import { Client, ConfigBuilder } from '@4mica/sdk';
|
|
91
111
|
|
|
92
112
|
const cfg = new ConfigBuilder().fromEnv().build();
|
|
93
113
|
const client = await Client.new(cfg);
|
|
@@ -100,8 +120,8 @@ If you want to integrate with a custom signer (hardware wallet, remote signer, e
|
|
|
100
120
|
SIWE auth.
|
|
101
121
|
|
|
102
122
|
```ts
|
|
103
|
-
import { Client, ConfigBuilder } from
|
|
104
|
-
import { privateKeyToAccount } from
|
|
123
|
+
import { Client, ConfigBuilder } from '@4mica/sdk';
|
|
124
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
105
125
|
|
|
106
126
|
const signer = privateKeyToAccount(process.env.PAYER_KEY as `0x${string}`);
|
|
107
127
|
const cfg = new ConfigBuilder().signer(signer).build();
|
|
@@ -113,11 +133,11 @@ const client = await Client.new(cfg);
|
|
|
113
133
|
Enable automatic SIWE auth refresh, or pass a static bearer token:
|
|
114
134
|
|
|
115
135
|
```ts
|
|
116
|
-
import { Client, ConfigBuilder } from
|
|
136
|
+
import { Client, ConfigBuilder } from '@4mica/sdk';
|
|
117
137
|
|
|
118
138
|
const cfg = new ConfigBuilder()
|
|
119
|
-
.walletPrivateKey(
|
|
120
|
-
.rpcUrl(
|
|
139
|
+
.walletPrivateKey('0x...')
|
|
140
|
+
.rpcUrl('https://api.4mica.xyz/')
|
|
121
141
|
.enableAuth()
|
|
122
142
|
.build();
|
|
123
143
|
|
|
@@ -129,8 +149,8 @@ Or use a static token:
|
|
|
129
149
|
|
|
130
150
|
```ts
|
|
131
151
|
const cfg = new ConfigBuilder()
|
|
132
|
-
.walletPrivateKey(
|
|
133
|
-
.bearerToken(
|
|
152
|
+
.walletPrivateKey('0x...')
|
|
153
|
+
.bearerToken('Bearer <access_token>')
|
|
134
154
|
.build();
|
|
135
155
|
```
|
|
136
156
|
|
|
@@ -147,6 +167,7 @@ The SDK exposes three main entry points:
|
|
|
147
167
|
### End-to-end Example (Base Sepolia + x402 v2)
|
|
148
168
|
|
|
149
169
|
See `examples/base-sepolia-x402-facilitator-e2e.ts` for a full flow in the `examples` folder:
|
|
170
|
+
|
|
150
171
|
- deposit collateral
|
|
151
172
|
- create/get a tab via facilitator
|
|
152
173
|
- issue and verify V1 + V2 guarantees
|
|
@@ -169,6 +190,7 @@ X-PAYMENT header (and optional `/settle` call) that the facilitator will accept.
|
|
|
169
190
|
#### What the SDK expects from `paymentRequirements`
|
|
170
191
|
|
|
171
192
|
At minimum you need:
|
|
193
|
+
|
|
172
194
|
- `scheme` and `network` (scheme must include `4mica`, e.g. `4mica-credit`)
|
|
173
195
|
- `payTo` (recipient address), `asset`, and `maxAmountRequired` (v1) or `amount` (v2)
|
|
174
196
|
- `extra.tabEndpoint` for tab resolution
|
|
@@ -180,8 +202,8 @@ At minimum you need:
|
|
|
180
202
|
Version 1 returns payment requirements in the JSON response body:
|
|
181
203
|
|
|
182
204
|
```ts
|
|
183
|
-
import { Client, ConfigBuilder, X402Flow } from
|
|
184
|
-
import type { PaymentRequirementsV1 } from
|
|
205
|
+
import { Client, ConfigBuilder, X402Flow } from '@4mica/sdk';
|
|
206
|
+
import type { PaymentRequirementsV1 } from '@4mica/sdk';
|
|
185
207
|
|
|
186
208
|
type ResourceResponse = {
|
|
187
209
|
x402Version: number;
|
|
@@ -189,23 +211,23 @@ type ResourceResponse = {
|
|
|
189
211
|
error?: string;
|
|
190
212
|
};
|
|
191
213
|
|
|
192
|
-
const cfg = new ConfigBuilder().walletPrivateKey(
|
|
214
|
+
const cfg = new ConfigBuilder().walletPrivateKey('0x...').build();
|
|
193
215
|
const client = await Client.new(cfg);
|
|
194
216
|
const flow = X402Flow.fromClient(client);
|
|
195
217
|
|
|
196
218
|
// 1) GET the protected endpoint and parse JSON body
|
|
197
|
-
const res = await fetch(
|
|
219
|
+
const res = await fetch('https://resource-url/resource');
|
|
198
220
|
const body = (await res.json()) as ResourceResponse;
|
|
199
221
|
|
|
200
222
|
// 2) Select a payment option
|
|
201
223
|
const requirements = body.accepts[0];
|
|
202
224
|
|
|
203
225
|
// 3) Build the X-PAYMENT header with the SDK
|
|
204
|
-
const payment = await flow.signPayment(requirements,
|
|
226
|
+
const payment = await flow.signPayment(requirements, '0xUser');
|
|
205
227
|
|
|
206
228
|
// 4) Call the protected resource with the header
|
|
207
|
-
await fetch(
|
|
208
|
-
headers: {
|
|
229
|
+
await fetch('https://resource-url/resource', {
|
|
230
|
+
headers: { 'X-PAYMENT': payment.header },
|
|
209
231
|
});
|
|
210
232
|
|
|
211
233
|
await client.aclose();
|
|
@@ -216,31 +238,31 @@ await client.aclose();
|
|
|
216
238
|
Version 2 uses the `payment-required` header (base64-encoded) instead of a JSON response body:
|
|
217
239
|
|
|
218
240
|
```ts
|
|
219
|
-
import { Client, ConfigBuilder, X402Flow } from
|
|
220
|
-
import type { X402PaymentRequired, PaymentRequirementsV2 } from
|
|
241
|
+
import { Client, ConfigBuilder, X402Flow } from '@4mica/sdk';
|
|
242
|
+
import type { X402PaymentRequired, PaymentRequirementsV2 } from '@4mica/sdk';
|
|
221
243
|
|
|
222
|
-
const cfg = new ConfigBuilder().walletPrivateKey(
|
|
244
|
+
const cfg = new ConfigBuilder().walletPrivateKey('0x...').build();
|
|
223
245
|
const client = await Client.new(cfg);
|
|
224
246
|
const flow = X402Flow.fromClient(client);
|
|
225
247
|
|
|
226
248
|
// 1) GET the protected endpoint and extract payment-required header
|
|
227
|
-
const res = await fetch(
|
|
228
|
-
const header = res.headers.get(
|
|
229
|
-
if (!header) throw new Error(
|
|
249
|
+
const res = await fetch('https://resource-url/resource');
|
|
250
|
+
const header = res.headers.get('payment-required');
|
|
251
|
+
if (!header) throw new Error('Missing payment-required header');
|
|
230
252
|
|
|
231
253
|
// 2) Decode the header
|
|
232
|
-
const decoded = Buffer.from(header,
|
|
254
|
+
const decoded = Buffer.from(header, 'base64').toString('utf8');
|
|
233
255
|
const paymentRequired = JSON.parse(decoded) as X402PaymentRequired;
|
|
234
256
|
|
|
235
257
|
// 3) Select a payment option
|
|
236
258
|
const accepted = paymentRequired.accepts[0] as PaymentRequirementsV2;
|
|
237
259
|
|
|
238
260
|
// 4) Build the PAYMENT-SIGNATURE header with the SDK
|
|
239
|
-
const signed = await flow.signPaymentV2(paymentRequired, accepted,
|
|
261
|
+
const signed = await flow.signPaymentV2(paymentRequired, accepted, '0xUser');
|
|
240
262
|
|
|
241
263
|
// 5) Call the protected resource with the header
|
|
242
|
-
await fetch(
|
|
243
|
-
headers: {
|
|
264
|
+
await fetch('https://resource-url/resource', {
|
|
265
|
+
headers: { 'PAYMENT-SIGNATURE': signed.header },
|
|
244
266
|
});
|
|
245
267
|
|
|
246
268
|
await client.aclose();
|
|
@@ -252,8 +274,8 @@ If your resource server proxies to the facilitator, you can reuse the SDK to set
|
|
|
252
274
|
verifying:
|
|
253
275
|
|
|
254
276
|
```ts
|
|
255
|
-
import { Client, ConfigBuilder, X402Flow } from
|
|
256
|
-
import type { PaymentRequirementsV1, X402SignedPayment } from
|
|
277
|
+
import { Client, ConfigBuilder, X402Flow } from '@4mica/sdk';
|
|
278
|
+
import type { PaymentRequirementsV1, X402SignedPayment } from '@4mica/sdk';
|
|
257
279
|
|
|
258
280
|
async function settle(
|
|
259
281
|
facilitatorUrl: string,
|
|
@@ -266,13 +288,14 @@ async function settle(
|
|
|
266
288
|
const flow = X402Flow.fromClient(core);
|
|
267
289
|
|
|
268
290
|
const settled = await flow.settlePayment(payment, paymentRequirements, facilitatorUrl);
|
|
269
|
-
console.log(
|
|
291
|
+
console.log('settlement result:', settled.settlement);
|
|
270
292
|
|
|
271
293
|
await core.aclose();
|
|
272
294
|
}
|
|
273
295
|
```
|
|
274
296
|
|
|
275
297
|
Notes:
|
|
298
|
+
|
|
276
299
|
- `signPayment` and `signPaymentV2` always use EIP-712 signing and will error if the scheme is not 4mica.
|
|
277
300
|
- `UserClient.signPayment` supports `SigningScheme.EIP712` (default) and `SigningScheme.EIP191`.
|
|
278
301
|
- `settlePayment` only hits `/settle`; resource servers should still call `/verify` first when enforcing access.
|
|
@@ -331,11 +354,17 @@ import {
|
|
|
331
354
|
computeValidationSubjectHash,
|
|
332
355
|
computeValidationRequestHash,
|
|
333
356
|
SigningScheme,
|
|
334
|
-
} from
|
|
357
|
+
} from '@4mica/sdk';
|
|
335
358
|
|
|
336
359
|
// 1) Build base V1 claims first
|
|
337
360
|
const baseClaims = PaymentGuaranteeRequestClaims.new(
|
|
338
|
-
userAddress,
|
|
361
|
+
userAddress,
|
|
362
|
+
recipientAddress,
|
|
363
|
+
tabId,
|
|
364
|
+
amount,
|
|
365
|
+
timestamp,
|
|
366
|
+
erc20Token,
|
|
367
|
+
reqId
|
|
339
368
|
);
|
|
340
369
|
|
|
341
370
|
// 2) Compute canonical hashes
|
|
@@ -343,14 +372,14 @@ const validationSubjectHash = computeValidationSubjectHash(baseClaims);
|
|
|
343
372
|
|
|
344
373
|
const partialV2 = new PaymentGuaranteeRequestClaimsV2({
|
|
345
374
|
...baseClaims,
|
|
346
|
-
validationRegistryAddress:
|
|
347
|
-
validationRequestHash:
|
|
375
|
+
validationRegistryAddress: '0x...',
|
|
376
|
+
validationRequestHash: '0x' + '00'.repeat(32), // placeholder
|
|
348
377
|
validationChainId: 1,
|
|
349
|
-
validatorAddress:
|
|
378
|
+
validatorAddress: '0x...',
|
|
350
379
|
validatorAgentId: 1n,
|
|
351
|
-
minValidationScore: 80,
|
|
380
|
+
minValidationScore: 80, // 1–100
|
|
352
381
|
validationSubjectHash,
|
|
353
|
-
requiredValidationTag:
|
|
382
|
+
requiredValidationTag: 'my-tag',
|
|
354
383
|
});
|
|
355
384
|
|
|
356
385
|
const validationRequestHash = computeValidationRequestHash(partialV2);
|
|
@@ -367,21 +396,25 @@ All SDK errors extend `FourMicaError`. Import individual error classes to distin
|
|
|
367
396
|
|
|
368
397
|
```ts
|
|
369
398
|
import {
|
|
370
|
-
ConfigError,
|
|
371
|
-
RpcError,
|
|
372
|
-
SigningError,
|
|
373
|
-
ContractError,
|
|
374
|
-
VerificationError,
|
|
375
|
-
X402Error,
|
|
376
|
-
AuthError,
|
|
399
|
+
ConfigError, // invalid ConfigBuilder input
|
|
400
|
+
RpcError, // 4Mica core service error (has .status and .body)
|
|
401
|
+
SigningError, // signing scheme unsupported or address mismatch
|
|
402
|
+
ContractError, // on-chain call failed or unexpected result
|
|
403
|
+
VerificationError, // BLS certificate decode/domain mismatch
|
|
404
|
+
X402Error, // x402 flow error (bad scheme, tab resolution, settlement)
|
|
405
|
+
AuthError, // base class for all auth errors
|
|
377
406
|
AuthMissingConfigError, // auth not configured when login() is called
|
|
378
|
-
} from
|
|
407
|
+
} from '@4mica/sdk';
|
|
379
408
|
|
|
380
409
|
try {
|
|
381
410
|
await client.recipient.remunerate(cert);
|
|
382
411
|
} catch (err) {
|
|
383
|
-
if (err instanceof VerificationError) {
|
|
384
|
-
|
|
412
|
+
if (err instanceof VerificationError) {
|
|
413
|
+
/* bad cert */
|
|
414
|
+
}
|
|
415
|
+
if (err instanceof ContractError) {
|
|
416
|
+
/* on-chain failure */
|
|
417
|
+
}
|
|
385
418
|
}
|
|
386
419
|
```
|
|
387
420
|
|