@nibgate/sdk 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@nibgate/sdk",
3
- "version": "0.2.8",
3
+ "version": "0.2.9",
4
4
  "description": "Framework-agnostic browser and server package for creator-owned gated content, unlock events, and receipts.",
5
5
  "type": "module",
6
- "sideEffects": ["./src/browser/client.js", "./src/browser/events.js", "./src/browser/index.js"],
6
+ "sideEffects": [
7
+ "./src/browser/client.js",
8
+ "./src/browser/events.js",
9
+ "./src/browser/index.js"
10
+ ],
7
11
  "main": "./src/index.js",
8
12
  "module": "./src/index.js",
9
13
  "types": "./src/index.d.ts",
@@ -11,7 +11,6 @@ export async function createCircleGatewayBrowserAdapter(options = {}) {
11
11
  return gateway.createCircleGatewayBrowserAdapter(options);
12
12
  }
13
13
 
14
- const DEFAULT_CIRCLE_CDN = 'https://esm.sh/@circle-fin/x402-batching@3.2.0/client?bundle';
15
14
  const HOSTED_PAY_URL = 'https://api.nibgate.xyz/api/hub/pay';
16
15
 
17
16
  function resolveAccessPath(resource, options) {
@@ -121,8 +120,7 @@ export function createEvmGatewayUnlock(resource, options = {}) {
121
120
  address: walletAddress,
122
121
  signTypedData: (typedData) => evm.request({ method: 'eth_signTypedData_v4', params: [walletAddress, stringifyJson(typedData)] })
123
122
  },
124
- clientModule: options.circleClientModule,
125
- clientModuleUrl: options.circleClientModuleUrl || DEFAULT_CIRCLE_CDN
123
+ clientModule: options.circleClientModule
126
124
  });
127
125
  return gatewayWallet.pay(input);
128
126
  }
@@ -139,6 +137,9 @@ export function createEvmGatewayUnlock(resource, options = {}) {
139
137
  challengeMessage: options.challengeMessage || 'Gateway payment required. Connect your wallet to continue...',
140
138
  paymentMessage: options.paymentMessage || 'Approve the Gateway payment proof in your wallet...',
141
139
  successMessage: options.successMessage || `Unlocked ${item.resource.title || 'content'}.`,
140
+ method: options.method,
141
+ headers: options.headers,
142
+ body: options.body,
142
143
  checkout, onStatus: setStatus
143
144
  });
144
145
  if (result.ok) {
@@ -191,7 +192,6 @@ export function createHostedUnlock(resource, options = {}) {
191
192
  return createEvmGatewayUnlock(resource, {
192
193
  ...options,
193
194
  hosted: true,
194
- circleClientModuleUrl: options.circleClientModuleUrl || DEFAULT_CIRCLE_CDN,
195
195
  noWalletMessage: options.noWalletMessage || 'Install MetaMask or another EVM wallet to unlock premium content.',
196
196
  });
197
197
  }
@@ -1,35 +1,6 @@
1
+ import { BatchEvmScheme } from './schemes/batch-scheme.js';
1
2
  import { stringifyJson } from './json.js';
2
3
 
3
- let cachedCircleModule = null;
4
-
5
- async function getCircleClient(options = {}) {
6
- if (cachedCircleModule) return cachedCircleModule;
7
-
8
- if (options.clientModule) {
9
- cachedCircleModule = options.clientModule;
10
- return cachedCircleModule;
11
- }
12
-
13
- const moduleUrl = options.clientModuleUrl || '@circle-fin/x402-batching/client';
14
-
15
- try {
16
- cachedCircleModule = await import(moduleUrl);
17
- return cachedCircleModule;
18
- } catch (error) {
19
- if (!options.clientModuleUrl) {
20
- throw new Error(
21
- `Could not load @circle-fin/x402-batching/client from node_modules. ` +
22
- `Provide a clientModuleUrl option (e.g. "https://esm.sh/@circle-fin/x402-batching@3.2.0/client?bundle") ` +
23
- `to load from CDN. Original error: ${error.message}`
24
- );
25
- }
26
- throw new Error(
27
- `Could not load Circle Gateway client from CDN: ${error.message}. ` +
28
- `Check that ${options.clientModuleUrl} is reachable.`
29
- );
30
- }
31
- }
32
-
33
4
  function encodeBase64(value) {
34
5
  const text = typeof value === 'string' ? value : stringifyJson(value);
35
6
  if (typeof Buffer !== 'undefined') return Buffer.from(text).toString('base64');
@@ -47,9 +18,9 @@ export async function createCircleGatewayBrowserAdapter(options = {}) {
47
18
  throw new Error('Circle Gateway browser adapter requires an EVM signer with address and signTypedData.');
48
19
  }
49
20
 
50
- const circleClientModule = await getCircleClient(options);
51
- const { BatchEvmScheme } = circleClientModule;
52
- const scheme = new BatchEvmScheme(signer);
21
+ const scheme = options.clientModule?.BatchEvmScheme
22
+ ? new options.clientModule.BatchEvmScheme(signer)
23
+ : new BatchEvmScheme(signer);
53
24
  const network = options.network || options.chainId && `eip155:${options.chainId}` || 'eip155:5042002';
54
25
 
55
26
  function parsePaymentRequired(input) {
@@ -0,0 +1,161 @@
1
+ export const CIRCLE_BATCHING_NAME = 'GatewayWalletBatched';
2
+ export const CIRCLE_BATCHING_VERSION = '1';
3
+ export const GATEWAY_MIN_AUTH_VALIDITY_SECONDS = 7 * 24 * 60 * 60;
4
+ export const GATEWAY_AUTH_VALIDITY_WINDOW_SECONDS = GATEWAY_MIN_AUTH_VALIDITY_SECONDS + 100;
5
+
6
+ const authorizationTypes = {
7
+ TransferWithAuthorization: [
8
+ { name: 'from', type: 'address' },
9
+ { name: 'to', type: 'address' },
10
+ { name: 'value', type: 'uint256' },
11
+ { name: 'validAfter', type: 'uint256' },
12
+ { name: 'validBefore', type: 'uint256' },
13
+ { name: 'nonce', type: 'bytes32' }
14
+ ]
15
+ };
16
+
17
+ function supportsBatching(requirements) {
18
+ const extra = requirements.extra;
19
+ if (!extra) return false;
20
+ return extra.name === CIRCLE_BATCHING_NAME && extra.version === CIRCLE_BATCHING_VERSION;
21
+ }
22
+
23
+ function getVerifyingContract(requirements) {
24
+ if (!supportsBatching(requirements)) return undefined;
25
+ return requirements.extra?.verifyingContract;
26
+ }
27
+
28
+ function createNonce() {
29
+ const bytes = new Uint8Array(32);
30
+ crypto.getRandomValues(bytes);
31
+ return '0x' + Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');
32
+ }
33
+
34
+ function getAddress(value) {
35
+ return value && typeof value === 'string' && value.startsWith('0x') ? value.toLowerCase() : value;
36
+ }
37
+
38
+ export class BatchEvmScheme {
39
+ constructor(signer) {
40
+ this.signer = signer;
41
+ this.scheme = CIRCLE_BATCHING_NAME;
42
+ this._beforeHooks = [];
43
+ this._afterHooks = [];
44
+ this._failureHooks = [];
45
+ }
46
+
47
+ onBeforePaymentCreation(hook) {
48
+ this._beforeHooks.push(hook);
49
+ return this;
50
+ }
51
+
52
+ onAfterPaymentCreation(hook) {
53
+ this._afterHooks.push(hook);
54
+ return this;
55
+ }
56
+
57
+ onPaymentCreationFailure(hook) {
58
+ this._failureHooks.push(hook);
59
+ return this;
60
+ }
61
+
62
+ async createPaymentPayload(x402Version, paymentRequirements) {
63
+ const context = {
64
+ paymentRequired: { x402Version, accepts: [paymentRequirements] },
65
+ selectedRequirements: paymentRequirements
66
+ };
67
+
68
+ for (const hook of this._beforeHooks) {
69
+ const result = await hook(context);
70
+ if (result && result.abort) {
71
+ throw new Error('Payment creation aborted: ' + result.reason);
72
+ }
73
+ }
74
+
75
+ try {
76
+ if (!supportsBatching(paymentRequirements)) {
77
+ throw new Error(
78
+ 'BatchEvmScheme can only handle Circle batching options. Expected extra.name="' +
79
+ CIRCLE_BATCHING_NAME + '" and extra.version="' + CIRCLE_BATCHING_VERSION + '"'
80
+ );
81
+ }
82
+
83
+ const verifyingContract = getVerifyingContract(paymentRequirements);
84
+ if (!verifyingContract) {
85
+ throw new Error(
86
+ 'Circle batching option missing extra.verifyingContract (GatewayWallet address)'
87
+ );
88
+ }
89
+
90
+ const nonce = createNonce();
91
+ const now = Math.floor(Date.now() / 1000);
92
+ const validityWindowSeconds = Math.max(
93
+ paymentRequirements.maxTimeoutSeconds || 0,
94
+ GATEWAY_AUTH_VALIDITY_WINDOW_SECONDS
95
+ );
96
+
97
+ const authorization = {
98
+ from: getAddress(this.signer.address),
99
+ to: getAddress(paymentRequirements.payTo),
100
+ value: paymentRequirements.amount,
101
+ validAfter: String(now - 600),
102
+ validBefore: String(now + validityWindowSeconds),
103
+ nonce
104
+ };
105
+
106
+ const signature = await this.signAuthorization(authorization, paymentRequirements, verifyingContract);
107
+
108
+ const payload = { authorization, signature };
109
+ const result = { x402Version, payload };
110
+
111
+ const createdContext = { ...context, paymentPayload: result };
112
+ for (const hook of this._afterHooks) {
113
+ try { await hook(createdContext); } catch {}
114
+ }
115
+
116
+ return result;
117
+ } catch (error) {
118
+ const failureContext = { ...context, error };
119
+ for (const hook of this._failureHooks) {
120
+ const result = await hook(failureContext);
121
+ if (result && result.recovered) {
122
+ return { x402Version: result.payload.x402Version, payload: result.payload.payload };
123
+ }
124
+ }
125
+ throw error;
126
+ }
127
+ }
128
+
129
+ async signAuthorization(authorization, requirements, verifyingContract) {
130
+ if (!requirements.network.startsWith('eip155:')) {
131
+ throw new Error('BatchEvmScheme: unsupported network format "' + requirements.network + '". Expected "eip155:<chainId>"');
132
+ }
133
+
134
+ const chainId = parseInt(requirements.network.split(':')[1], 10);
135
+
136
+ const domain = {
137
+ name: CIRCLE_BATCHING_NAME,
138
+ version: CIRCLE_BATCHING_VERSION,
139
+ chainId,
140
+ verifyingContract: getAddress(verifyingContract)
141
+ };
142
+
143
+ const message = {
144
+ from: getAddress(authorization.from),
145
+ to: getAddress(authorization.to),
146
+ value: BigInt(authorization.value),
147
+ validAfter: BigInt(authorization.validAfter),
148
+ validBefore: BigInt(authorization.validBefore),
149
+ nonce: authorization.nonce
150
+ };
151
+
152
+ const typedData = {
153
+ domain,
154
+ types: authorizationTypes,
155
+ primaryType: 'TransferWithAuthorization',
156
+ message
157
+ };
158
+
159
+ return this.signer.signTypedData(typedData);
160
+ }
161
+ }
package/src/index.d.ts CHANGED
@@ -217,7 +217,6 @@ export type NibgateCircleGatewayBrowserAdapterOptions = {
217
217
  network?: string;
218
218
  chainId?: number | string;
219
219
  clientModule?: Record<string, unknown>;
220
- clientModuleUrl?: string;
221
220
  };
222
221
 
223
222
  export type NibgateCircleGatewayBrowserAdapter = {
@@ -230,10 +229,15 @@ export type NibgateEvmGatewayUnlockOptions = {
230
229
  source?: string;
231
230
  hosted?: boolean;
232
231
  accessPath?: string;
232
+ method?: string;
233
+ headers?: Record<string, string>;
234
+ body?: BodyInit | null;
233
235
  network?: string;
234
236
  paymentProvider?: string;
235
237
  circleClientModule?: Record<string, unknown>;
238
+ /** @deprecated The SDK now ships a native BatchEvmScheme — no CDN needed */
236
239
  circleClientModuleUrl?: string;
240
+
237
241
  provider?: {
238
242
  request(args: { method: string; params?: unknown[] }): Promise<unknown>;
239
243
  };