@nibgate/sdk 0.2.7 → 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/SKILL.md +19 -0
- package/dist/nibgate.js +165 -30
- package/dist/nibgate.js.map +3 -3
- package/dist/nibgate.min.js +2 -2
- package/dist/nibgate.min.js.map +4 -4
- package/package.json +6 -2
- package/src/browser/evm-gateway.js +19 -4
- package/src/browser/gateway.js +4 -33
- package/src/browser/index.js +1 -1
- package/src/browser/schemes/batch-scheme.js +161 -0
- package/src/index.d.ts +8 -1
package/SKILL.md
CHANGED
|
@@ -42,6 +42,25 @@ If the widget loads after app code, the SDK queues events and flushes them when
|
|
|
42
42
|
|
|
43
43
|
**Adblocker bypass:** The widget now sends events to `/api/hub/evt` instead of `/api/hub/track` to avoid adblocker filter lists that target the word "track". The old `/api/hub/track` endpoint is preserved for backward compatibility. If you self-host the backend, ensure both routes are registered.
|
|
44
44
|
|
|
45
|
+
**Hosted mode (zero backend code):** If you don't want to run your own access route, mark premium content with a data attribute and the widget handles everything:
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<div data-nibgate-premium="0.01" data-nibgate-recipient="0xYourWallet">
|
|
49
|
+
<p>Teaser text shown to everyone...</p>
|
|
50
|
+
<div data-nibgate-unlock-card>
|
|
51
|
+
<span data-nibgate-wallet-label>No wallet</span>
|
|
52
|
+
<button data-nibgate-connect>Connect</button>
|
|
53
|
+
<button data-nibgate-unlock-btn>Unlock for 0.01 USDC</button>
|
|
54
|
+
<p data-nibgate-status></p>
|
|
55
|
+
</div>
|
|
56
|
+
<div data-nibgate-unlocked hidden>
|
|
57
|
+
Full premium content here...
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
No SDK import, no server access route, no env vars. The default wallet is set once in the Nibgate dashboard — or override per-post with `data-nibgate-recipient`. Custom price per-post: change `data-nibgate-premium="0.05"`.
|
|
63
|
+
|
|
45
64
|
---
|
|
46
65
|
|
|
47
66
|
## 2. Resource Shape (Minimal)
|
package/dist/nibgate.js
CHANGED
|
@@ -33,32 +33,156 @@ var Nibgate = (() => {
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
// src/browser/schemes/batch-scheme.js
|
|
37
|
+
function supportsBatching(requirements) {
|
|
38
|
+
const extra = requirements.extra;
|
|
39
|
+
if (!extra) return false;
|
|
40
|
+
return extra.name === CIRCLE_BATCHING_NAME && extra.version === CIRCLE_BATCHING_VERSION;
|
|
41
|
+
}
|
|
42
|
+
function getVerifyingContract(requirements) {
|
|
43
|
+
if (!supportsBatching(requirements)) return void 0;
|
|
44
|
+
return requirements.extra?.verifyingContract;
|
|
45
|
+
}
|
|
46
|
+
function createNonce() {
|
|
47
|
+
const bytes = new Uint8Array(32);
|
|
48
|
+
crypto.getRandomValues(bytes);
|
|
49
|
+
return "0x" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
50
|
+
}
|
|
51
|
+
function getAddress(value) {
|
|
52
|
+
return value && typeof value === "string" && value.startsWith("0x") ? value.toLowerCase() : value;
|
|
53
|
+
}
|
|
54
|
+
var CIRCLE_BATCHING_NAME, CIRCLE_BATCHING_VERSION, GATEWAY_MIN_AUTH_VALIDITY_SECONDS, GATEWAY_AUTH_VALIDITY_WINDOW_SECONDS, authorizationTypes, BatchEvmScheme;
|
|
55
|
+
var init_batch_scheme = __esm({
|
|
56
|
+
"src/browser/schemes/batch-scheme.js"() {
|
|
57
|
+
CIRCLE_BATCHING_NAME = "GatewayWalletBatched";
|
|
58
|
+
CIRCLE_BATCHING_VERSION = "1";
|
|
59
|
+
GATEWAY_MIN_AUTH_VALIDITY_SECONDS = 7 * 24 * 60 * 60;
|
|
60
|
+
GATEWAY_AUTH_VALIDITY_WINDOW_SECONDS = GATEWAY_MIN_AUTH_VALIDITY_SECONDS + 100;
|
|
61
|
+
authorizationTypes = {
|
|
62
|
+
TransferWithAuthorization: [
|
|
63
|
+
{ name: "from", type: "address" },
|
|
64
|
+
{ name: "to", type: "address" },
|
|
65
|
+
{ name: "value", type: "uint256" },
|
|
66
|
+
{ name: "validAfter", type: "uint256" },
|
|
67
|
+
{ name: "validBefore", type: "uint256" },
|
|
68
|
+
{ name: "nonce", type: "bytes32" }
|
|
69
|
+
]
|
|
70
|
+
};
|
|
71
|
+
BatchEvmScheme = class {
|
|
72
|
+
constructor(signer) {
|
|
73
|
+
this.signer = signer;
|
|
74
|
+
this.scheme = CIRCLE_BATCHING_NAME;
|
|
75
|
+
this._beforeHooks = [];
|
|
76
|
+
this._afterHooks = [];
|
|
77
|
+
this._failureHooks = [];
|
|
78
|
+
}
|
|
79
|
+
onBeforePaymentCreation(hook) {
|
|
80
|
+
this._beforeHooks.push(hook);
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
onAfterPaymentCreation(hook) {
|
|
84
|
+
this._afterHooks.push(hook);
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
onPaymentCreationFailure(hook) {
|
|
88
|
+
this._failureHooks.push(hook);
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
async createPaymentPayload(x402Version, paymentRequirements) {
|
|
92
|
+
const context = {
|
|
93
|
+
paymentRequired: { x402Version, accepts: [paymentRequirements] },
|
|
94
|
+
selectedRequirements: paymentRequirements
|
|
95
|
+
};
|
|
96
|
+
for (const hook of this._beforeHooks) {
|
|
97
|
+
const result = await hook(context);
|
|
98
|
+
if (result && result.abort) {
|
|
99
|
+
throw new Error("Payment creation aborted: " + result.reason);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
if (!supportsBatching(paymentRequirements)) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
'BatchEvmScheme can only handle Circle batching options. Expected extra.name="' + CIRCLE_BATCHING_NAME + '" and extra.version="' + CIRCLE_BATCHING_VERSION + '"'
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
const verifyingContract = getVerifyingContract(paymentRequirements);
|
|
109
|
+
if (!verifyingContract) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
"Circle batching option missing extra.verifyingContract (GatewayWallet address)"
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
const nonce = createNonce();
|
|
115
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
116
|
+
const validityWindowSeconds = Math.max(
|
|
117
|
+
paymentRequirements.maxTimeoutSeconds || 0,
|
|
118
|
+
GATEWAY_AUTH_VALIDITY_WINDOW_SECONDS
|
|
119
|
+
);
|
|
120
|
+
const authorization = {
|
|
121
|
+
from: getAddress(this.signer.address),
|
|
122
|
+
to: getAddress(paymentRequirements.payTo),
|
|
123
|
+
value: paymentRequirements.amount,
|
|
124
|
+
validAfter: String(now - 600),
|
|
125
|
+
validBefore: String(now + validityWindowSeconds),
|
|
126
|
+
nonce
|
|
127
|
+
};
|
|
128
|
+
const signature = await this.signAuthorization(authorization, paymentRequirements, verifyingContract);
|
|
129
|
+
const payload = { authorization, signature };
|
|
130
|
+
const result = { x402Version, payload };
|
|
131
|
+
const createdContext = { ...context, paymentPayload: result };
|
|
132
|
+
for (const hook of this._afterHooks) {
|
|
133
|
+
try {
|
|
134
|
+
await hook(createdContext);
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
const failureContext = { ...context, error };
|
|
141
|
+
for (const hook of this._failureHooks) {
|
|
142
|
+
const result = await hook(failureContext);
|
|
143
|
+
if (result && result.recovered) {
|
|
144
|
+
return { x402Version: result.payload.x402Version, payload: result.payload.payload };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async signAuthorization(authorization, requirements, verifyingContract) {
|
|
151
|
+
if (!requirements.network.startsWith("eip155:")) {
|
|
152
|
+
throw new Error('BatchEvmScheme: unsupported network format "' + requirements.network + '". Expected "eip155:<chainId>"');
|
|
153
|
+
}
|
|
154
|
+
const chainId = parseInt(requirements.network.split(":")[1], 10);
|
|
155
|
+
const domain = {
|
|
156
|
+
name: CIRCLE_BATCHING_NAME,
|
|
157
|
+
version: CIRCLE_BATCHING_VERSION,
|
|
158
|
+
chainId,
|
|
159
|
+
verifyingContract: getAddress(verifyingContract)
|
|
160
|
+
};
|
|
161
|
+
const message = {
|
|
162
|
+
from: getAddress(authorization.from),
|
|
163
|
+
to: getAddress(authorization.to),
|
|
164
|
+
value: BigInt(authorization.value),
|
|
165
|
+
validAfter: BigInt(authorization.validAfter),
|
|
166
|
+
validBefore: BigInt(authorization.validBefore),
|
|
167
|
+
nonce: authorization.nonce
|
|
168
|
+
};
|
|
169
|
+
const typedData = {
|
|
170
|
+
domain,
|
|
171
|
+
types: authorizationTypes,
|
|
172
|
+
primaryType: "TransferWithAuthorization",
|
|
173
|
+
message
|
|
174
|
+
};
|
|
175
|
+
return this.signer.signTypedData(typedData);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
36
181
|
// src/browser/gateway.js
|
|
37
182
|
var gateway_exports = {};
|
|
38
183
|
__export(gateway_exports, {
|
|
39
184
|
createCircleGatewayBrowserAdapter: () => createCircleGatewayBrowserAdapter
|
|
40
185
|
});
|
|
41
|
-
async function getCircleClient(options = {}) {
|
|
42
|
-
if (cachedCircleModule) return cachedCircleModule;
|
|
43
|
-
if (options.clientModule) {
|
|
44
|
-
cachedCircleModule = options.clientModule;
|
|
45
|
-
return cachedCircleModule;
|
|
46
|
-
}
|
|
47
|
-
const moduleUrl = options.clientModuleUrl || "@circle-fin/x402-batching/client";
|
|
48
|
-
try {
|
|
49
|
-
cachedCircleModule = await import(moduleUrl);
|
|
50
|
-
return cachedCircleModule;
|
|
51
|
-
} catch (error) {
|
|
52
|
-
if (!options.clientModuleUrl) {
|
|
53
|
-
throw new Error(
|
|
54
|
-
`Could not load @circle-fin/x402-batching/client from node_modules. Provide a clientModuleUrl option (e.g. "https://esm.sh/@circle-fin/x402-batching@3.2.0/client?bundle") to load from CDN. Original error: ${error.message}`
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
throw new Error(
|
|
58
|
-
`Could not load Circle Gateway client from CDN: ${error.message}. Check that ${options.clientModuleUrl} is reachable.`
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
186
|
function encodeBase64(value) {
|
|
63
187
|
const text = typeof value === "string" ? value : stringifyJson(value);
|
|
64
188
|
if (typeof Buffer !== "undefined") return Buffer.from(text).toString("base64");
|
|
@@ -73,9 +197,7 @@ var Nibgate = (() => {
|
|
|
73
197
|
if (!signer?.address || typeof signer.signTypedData !== "function") {
|
|
74
198
|
throw new Error("Circle Gateway browser adapter requires an EVM signer with address and signTypedData.");
|
|
75
199
|
}
|
|
76
|
-
const
|
|
77
|
-
const { BatchEvmScheme } = circleClientModule;
|
|
78
|
-
const scheme = new BatchEvmScheme(signer);
|
|
200
|
+
const scheme = options.clientModule?.BatchEvmScheme ? new options.clientModule.BatchEvmScheme(signer) : new BatchEvmScheme(signer);
|
|
79
201
|
const network = options.network || options.chainId && `eip155:${options.chainId}` || "eip155:5042002";
|
|
80
202
|
function parsePaymentRequired(input) {
|
|
81
203
|
if (input && typeof input === "object") return input;
|
|
@@ -131,11 +253,10 @@ var Nibgate = (() => {
|
|
|
131
253
|
}
|
|
132
254
|
};
|
|
133
255
|
}
|
|
134
|
-
var cachedCircleModule;
|
|
135
256
|
var init_gateway = __esm({
|
|
136
257
|
"src/browser/gateway.js"() {
|
|
258
|
+
init_batch_scheme();
|
|
137
259
|
init_json();
|
|
138
|
-
cachedCircleModule = null;
|
|
139
260
|
}
|
|
140
261
|
});
|
|
141
262
|
|
|
@@ -158,6 +279,7 @@ var Nibgate = (() => {
|
|
|
158
279
|
createCircleGatewayBrowserAdapter: () => createCircleGatewayBrowserAdapter2,
|
|
159
280
|
createEvmGatewayUnlock: () => createEvmGatewayUnlock,
|
|
160
281
|
createGate: () => createGate,
|
|
282
|
+
createHostedUnlock: () => createHostedUnlock,
|
|
161
283
|
createNibgate: () => createNibgate,
|
|
162
284
|
createNibgateContentSettings: () => createNibgateContentSettings,
|
|
163
285
|
createOnchainRating: () => createOnchainRating,
|
|
@@ -803,11 +925,15 @@ var Nibgate = (() => {
|
|
|
803
925
|
const gateway = await Promise.resolve().then(() => (init_gateway(), gateway_exports));
|
|
804
926
|
return gateway.createCircleGatewayBrowserAdapter(options);
|
|
805
927
|
}
|
|
806
|
-
var
|
|
928
|
+
var HOSTED_PAY_URL = "https://api.nibgate.xyz/api/hub/pay";
|
|
929
|
+
function resolveAccessPath(resource, options) {
|
|
930
|
+
if (options.hosted || options.accessPath === "hosted") return HOSTED_PAY_URL;
|
|
931
|
+
return options.accessPath || resource.accessPath || "/api/nibgate/access";
|
|
932
|
+
}
|
|
807
933
|
function createEvmGatewayUnlock(resource, options = {}) {
|
|
808
934
|
const item = createGate(resource, options.gateOptions || {});
|
|
809
935
|
const win = browserWindow();
|
|
810
|
-
const accessPath =
|
|
936
|
+
const accessPath = resolveAccessPath(item.resource, options);
|
|
811
937
|
const source = options.source || "nibgate-evm-gateway";
|
|
812
938
|
const network = options.network || "eip155:5042002";
|
|
813
939
|
const statusTarget = typeof options.status === "string" ? win?.document.querySelector(options.status) : options.status;
|
|
@@ -897,8 +1023,7 @@ var Nibgate = (() => {
|
|
|
897
1023
|
address: walletAddress,
|
|
898
1024
|
signTypedData: (typedData) => evm.request({ method: "eth_signTypedData_v4", params: [walletAddress, stringifyJson(typedData)] })
|
|
899
1025
|
},
|
|
900
|
-
clientModule: options.circleClientModule
|
|
901
|
-
clientModuleUrl: options.circleClientModuleUrl || DEFAULT_CIRCLE_CDN
|
|
1026
|
+
clientModule: options.circleClientModule
|
|
902
1027
|
});
|
|
903
1028
|
return gatewayWallet.pay(input);
|
|
904
1029
|
}
|
|
@@ -915,6 +1040,9 @@ var Nibgate = (() => {
|
|
|
915
1040
|
challengeMessage: options.challengeMessage || "Gateway payment required. Connect your wallet to continue...",
|
|
916
1041
|
paymentMessage: options.paymentMessage || "Approve the Gateway payment proof in your wallet...",
|
|
917
1042
|
successMessage: options.successMessage || `Unlocked ${item.resource.title || "content"}.`,
|
|
1043
|
+
method: options.method,
|
|
1044
|
+
headers: options.headers,
|
|
1045
|
+
body: options.body,
|
|
918
1046
|
checkout,
|
|
919
1047
|
onStatus: setStatus
|
|
920
1048
|
});
|
|
@@ -960,6 +1088,13 @@ var Nibgate = (() => {
|
|
|
960
1088
|
if (options.autoMount !== false) mount();
|
|
961
1089
|
return controller;
|
|
962
1090
|
}
|
|
1091
|
+
function createHostedUnlock(resource, options = {}) {
|
|
1092
|
+
return createEvmGatewayUnlock(resource, {
|
|
1093
|
+
...options,
|
|
1094
|
+
hosted: true,
|
|
1095
|
+
noWalletMessage: options.noWalletMessage || "Install MetaMask or another EVM wallet to unlock premium content."
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
963
1098
|
|
|
964
1099
|
// src/browser/reputation.js
|
|
965
1100
|
var RATE_CONTENT_SELECTOR = "0xc62fad09";
|