@chipi-stack/chipi-react 14.15.1 → 14.17.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/dist/index.js CHANGED
@@ -1340,6 +1340,22 @@ function useSignTypedData() {
1340
1340
  reset: mutation.reset
1341
1341
  };
1342
1342
  }
1343
+ function useExternalSignerExecute() {
1344
+ const { chipiSDK } = useChipiContext();
1345
+ const mutation = reactQuery.useMutation({
1346
+ mutationFn: async (input) => chipiSDK.executeWithExternalSigner(input)
1347
+ });
1348
+ return {
1349
+ execute: mutation.mutate,
1350
+ executeAsync: mutation.mutateAsync,
1351
+ data: mutation.data,
1352
+ isLoading: mutation.isPending,
1353
+ isError: mutation.isError,
1354
+ error: mutation.error,
1355
+ isSuccess: mutation.isSuccess,
1356
+ reset: mutation.reset
1357
+ };
1358
+ }
1343
1359
 
1344
1360
  // ../node_modules/.pnpm/@starknet-io+types-js@0.9.2/node_modules/@starknet-io/types-js/dist/esm/index.js
1345
1361
  var esm_exports = {};
@@ -20418,6 +20434,41 @@ function hexEq(a, b) {
20418
20434
  return a === b;
20419
20435
  }
20420
20436
  }
20437
+ function parseCapAmount(value, field, token) {
20438
+ try {
20439
+ const v = typeof value === "bigint" ? value : BigInt(String(value).trim());
20440
+ if (v < 0n) throw new Error("negative");
20441
+ return v;
20442
+ } catch {
20443
+ throw new Error(
20444
+ `session_spend_cap_invalid: ${field} for token ${token} must be a non-negative integer in base units (got "${String(value)}")`
20445
+ );
20446
+ }
20447
+ }
20448
+ function toSpendingPolicies(caps, lifetimeSeconds) {
20449
+ return caps.map((cap) => {
20450
+ const policy = "maxPerWindow" in cap ? {
20451
+ token: cap.token,
20452
+ maxPerCall: parseCapAmount(cap.maxPerCall, "maxPerCall", cap.token),
20453
+ maxPerWindow: parseCapAmount(cap.maxPerWindow, "maxPerWindow", cap.token),
20454
+ windowSeconds: cap.windowSeconds
20455
+ } : (() => {
20456
+ const amount = parseCapAmount(cap.amount, "amount", cap.token);
20457
+ return {
20458
+ token: cap.token,
20459
+ maxPerCall: amount,
20460
+ maxPerWindow: amount,
20461
+ windowSeconds: Math.max(1, lifetimeSeconds)
20462
+ };
20463
+ })();
20464
+ if (policy.maxPerWindow === 0n) {
20465
+ throw new Error(
20466
+ `session_spend_cap_zero: the cap for token ${cap.token} resolves to maxPerWindow = 0, which means unlimited on-chain. Use a positive cap.`
20467
+ );
20468
+ }
20469
+ return policy;
20470
+ });
20471
+ }
20421
20472
  function sessionCovers(policy, calls) {
20422
20473
  if (!policy?.allowedCalls?.length) return false;
20423
20474
  return calls.every(
@@ -20432,6 +20483,10 @@ function useChipiWalletObject(config3) {
20432
20483
  const { signTypedDataAsync } = useSignTypedData();
20433
20484
  const { createSessionKeyAsync } = useCreateSessionKey();
20434
20485
  const { addSessionKeyToContractAsync } = useAddSessionKeyToContract();
20486
+ const { chipiSDK } = useChipiContext();
20487
+ const sdk = React.useRef(chipiSDK);
20488
+ sdk.current = chipiSDK;
20489
+ const warnedMissingApproval = React.useRef(false);
20435
20490
  const cfg = React.useRef(config3);
20436
20491
  cfg.current = config3;
20437
20492
  const requireWallet = () => {
@@ -20444,28 +20499,33 @@ function useChipiWalletObject(config3) {
20444
20499
  if (policy.validUntil <= nowSec()) throw new Error("session_policy_expired");
20445
20500
  const c = cfg.current;
20446
20501
  const wallet = requireWallet();
20502
+ const lifetimeSeconds = Math.max(1, policy.validUntil - nowSec());
20503
+ const spendingPolicies = policy.spendCaps?.length ? toSpendingPolicies(policy.spendCaps, lifetimeSeconds) : [];
20447
20504
  const encryptKey = await c.getEncryptKey();
20448
20505
  const session = await createSessionKeyAsync({
20449
20506
  encryptKey,
20450
- durationSeconds: Math.max(1, policy.validUntil - nowSec())
20507
+ durationSeconds: lifetimeSeconds
20451
20508
  });
20452
20509
  const bearerToken = await c.getBearerToken();
20453
- await addSessionKeyToContractAsync({
20454
- params: {
20455
- encryptKey,
20456
- wallet,
20457
- sessionConfig: {
20458
- sessionPublicKey: session.publicKey,
20459
- validUntil: policy.validUntil,
20460
- maxCalls: 1e3,
20461
- // selectors; empty = all entrypoints allowed
20462
- allowedEntrypoints: policy.allowedCalls.map(
20463
- (a) => hash_exports.getSelectorFromName(a.entrypoint)
20464
- )
20465
- }
20466
- },
20467
- bearerToken
20468
- });
20510
+ const params = {
20511
+ encryptKey,
20512
+ wallet,
20513
+ sessionConfig: {
20514
+ sessionPublicKey: session.publicKey,
20515
+ validUntil: policy.validUntil,
20516
+ maxCalls: 1e3,
20517
+ // Selectors; empty = all entrypoints allowed. On-chain this is a
20518
+ // per-selector whitelist, NOT scoped to allowedCalls' contracts.
20519
+ allowedEntrypoints: policy.allowedCalls.map(
20520
+ (a) => hash_exports.getSelectorFromName(a.entrypoint)
20521
+ )
20522
+ }
20523
+ };
20524
+ if (spendingPolicies.length > 0) {
20525
+ await sdk.current.sessions.setupSession({ ...params, spendingPolicies }, bearerToken);
20526
+ } else {
20527
+ await addSessionKeyToContractAsync({ params, bearerToken });
20528
+ }
20469
20529
  return session;
20470
20530
  },
20471
20531
  [createSessionKeyAsync, addSessionKeyToContractAsync]
@@ -20505,12 +20565,23 @@ function useChipiWalletObject(config3) {
20505
20565
  return signTypedDataAsync({ encryptKey, wallet, typedData });
20506
20566
  },
20507
20567
  getDeploymentData: async () => {
20508
- throw new Error("account_already_deployed");
20568
+ if (cfg.current.isDeployed === true) throw new Error("account_already_deployed");
20569
+ throw new Error("account_deployment_data_unavailable");
20509
20570
  },
20510
20571
  createSession: async (policy) => {
20511
20572
  await createSession(policy);
20512
20573
  },
20513
- requestApproval: (req) => cfg.current.requestApproval?.(req) ?? Promise.resolve(true)
20574
+ requestApproval: (req) => {
20575
+ const gate = cfg.current.requestApproval;
20576
+ if (gate) return gate(req);
20577
+ if (!warnedMissingApproval.current) {
20578
+ warnedMissingApproval.current = true;
20579
+ console.warn(
20580
+ "[ChipiSDK:Connect] No `requestApproval` was provided to useChipiWalletObject, so every approval (connect, execute, signTypedData, session) is denied. Pass `requestApproval` to show the user an approval UI and resolve true/false."
20581
+ );
20582
+ }
20583
+ return Promise.resolve(false);
20584
+ }
20514
20585
  }),
20515
20586
  [callAnyContractAsync, executeWithSessionAsync, signTypedDataAsync, createSession]
20516
20587
  );
@@ -21189,11 +21260,35 @@ var RpcError2 = class extends Error {
21189
21260
  var USER_REFUSED_OP = 113;
21190
21261
  var INVALID_REQUEST_PAYLOAD = 114;
21191
21262
  var ACCOUNT_ALREADY_DEPLOYED = 115;
21263
+ var NO_DEPLOYMENT_DATA = 116;
21192
21264
  var UNKNOWN_ERROR = 163;
21265
+ var MISSING_APPROVAL_WARNING = "[ChipiSDK:Connect] No `requestApproval` was provided, so every approval (connect, execute, signTypedData, session) is denied. Pass `requestApproval` to show the user an approval UI and resolve true/false.";
21266
+ function normalizeChainId(value) {
21267
+ try {
21268
+ if (typeof value === "bigint") return value;
21269
+ if (typeof value === "number") {
21270
+ return Number.isSafeInteger(value) && value >= 0 ? BigInt(value) : null;
21271
+ }
21272
+ if (typeof value !== "string" || value.length === 0) return null;
21273
+ if (/^0x[0-9a-f]+$/i.test(value) || /^[0-9]+$/.test(value)) return BigInt(value);
21274
+ return BigInt(shortString_exports.encodeShortString(value));
21275
+ } catch {
21276
+ return null;
21277
+ }
21278
+ }
21193
21279
  function createChipiWindowObject(deps) {
21194
21280
  const accountListeners = /* @__PURE__ */ new Set();
21195
21281
  const networkListeners = /* @__PURE__ */ new Set();
21196
- const approve = deps.requestApproval ?? (async () => true);
21282
+ let approved = false;
21283
+ let pendingConnect = null;
21284
+ let warnedMissingApproval = false;
21285
+ const approve = deps.requestApproval ?? (async () => {
21286
+ if (!warnedMissingApproval) {
21287
+ warnedMissingApproval = true;
21288
+ console.warn(MISSING_APPROVAL_WARNING);
21289
+ }
21290
+ return false;
21291
+ });
21197
21292
  async function requireApproval(req) {
21198
21293
  const ok = await approve(req);
21199
21294
  if (!ok) throw new RpcError2(USER_REFUSED_OP, "User refused the operation");
@@ -21201,19 +21296,27 @@ function createChipiWindowObject(deps) {
21201
21296
  const request = (async (call) => {
21202
21297
  switch (call.type) {
21203
21298
  case "wallet_requestAccounts": {
21204
- const accounts = await deps.getAccounts();
21205
- if (accounts.length === 0) {
21206
- await requireApproval({ type: "connect" });
21207
- return deps.getAccounts();
21299
+ if (call.params?.silent_mode) {
21300
+ return approved ? deps.getAccounts() : [];
21208
21301
  }
21209
- return accounts;
21302
+ if (approved) {
21303
+ const accounts = await deps.getAccounts();
21304
+ if (accounts.length > 0) return accounts;
21305
+ }
21306
+ if (!pendingConnect) {
21307
+ pendingConnect = requireApproval({ type: "connect" }).then(() => {
21308
+ approved = true;
21309
+ }).finally(() => {
21310
+ pendingConnect = null;
21311
+ });
21312
+ }
21313
+ await pendingConnect;
21314
+ return deps.getAccounts();
21210
21315
  }
21211
21316
  case "wallet_requestChainId":
21212
21317
  return deps.chainId;
21213
- case "wallet_getPermissions": {
21214
- const accounts = await deps.getAccounts();
21215
- return accounts.length > 0 ? ["accounts"] : [];
21216
- }
21318
+ case "wallet_getPermissions":
21319
+ return approved ? ["accounts"] : [];
21217
21320
  case "wallet_addInvokeTransaction": {
21218
21321
  const wireCalls = call.params?.calls ?? [];
21219
21322
  if (wireCalls.length === 0) {
@@ -21226,14 +21329,38 @@ function createChipiWindowObject(deps) {
21226
21329
  }
21227
21330
  case "wallet_signTypedData": {
21228
21331
  const typedData = call.params;
21332
+ const domainChainId = typedData?.domain?.chainId;
21333
+ if (domainChainId === void 0 || domainChainId === null) {
21334
+ throw new RpcError2(
21335
+ INVALID_REQUEST_PAYLOAD,
21336
+ `wallet_signTypedData: domain.chainId is required (the wallet chain is ${deps.chainId})`
21337
+ );
21338
+ }
21339
+ const expected = normalizeChainId(deps.chainId);
21340
+ const actual = normalizeChainId(domainChainId);
21341
+ if (actual === null || expected === null || actual !== expected) {
21342
+ throw new RpcError2(
21343
+ INVALID_REQUEST_PAYLOAD,
21344
+ `wallet_signTypedData: domain.chainId ${String(domainChainId)} does not match the wallet chain ${deps.chainId}`
21345
+ );
21346
+ }
21229
21347
  await requireApproval({ type: "signTypedData", typedData });
21230
21348
  return deps.signTypedData(typedData);
21231
21349
  }
21232
21350
  case "wallet_deploymentData":
21233
21351
  try {
21234
21352
  return await deps.getDeploymentData();
21235
- } catch {
21236
- throw new RpcError2(ACCOUNT_ALREADY_DEPLOYED, "Account already deployed");
21353
+ } catch (err2) {
21354
+ if (err2 instanceof Error && err2.message === "account_already_deployed") {
21355
+ throw new RpcError2(ACCOUNT_ALREADY_DEPLOYED, "Account already deployed");
21356
+ }
21357
+ if (err2 instanceof Error && err2.message === "account_deployment_data_unavailable") {
21358
+ throw new RpcError2(NO_DEPLOYMENT_DATA, "Account deployment data is not available");
21359
+ }
21360
+ throw new RpcError2(
21361
+ UNKNOWN_ERROR,
21362
+ err2 instanceof Error ? err2.message : "Could not read deployment data"
21363
+ );
21237
21364
  }
21238
21365
  case "wallet_supportedSpecs":
21239
21366
  return SUPPORTED_SPECS;
@@ -21550,6 +21677,7 @@ exports.useChipiWalletObject = useChipiWalletObject;
21550
21677
  exports.useCreateSessionKey = useCreateSessionKey;
21551
21678
  exports.useCreateWallet = useCreateWallet;
21552
21679
  exports.useExecuteWithSession = useExecuteWithSession;
21680
+ exports.useExternalSignerExecute = useExternalSignerExecute;
21553
21681
  exports.useGetSessionData = useGetSessionData;
21554
21682
  exports.useGetSku = useGetSku;
21555
21683
  exports.useGetSkuList = useGetSkuList;