@chipi-stack/backend 14.13.0 → 14.14.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
@@ -89,7 +89,7 @@ function hashCall(call) {
89
89
  return poseidonSpan([
90
90
  CALL_TYPE_HASH_REV1,
91
91
  toBigInt(call.contractAddress),
92
- toBigInt(starknet.hash.getSelectorFromName(call.entrypoint)),
92
+ toBigInt(starknet.hash.getSelector(call.entrypoint)),
93
93
  calldataHash
94
94
  ]);
95
95
  }
@@ -572,7 +572,7 @@ function serializeExecuteFromOutsideV2Calldata(params) {
572
572
  out.push(BigInt(oe.calls.length));
573
573
  for (const call of oe.calls) {
574
574
  out.push(BigInt(call.contractAddress));
575
- out.push(BigInt(starknet.hash.getSelectorFromName(call.entrypoint)));
575
+ out.push(BigInt(starknet.hash.getSelector(call.entrypoint)));
576
576
  const callData = Array.isArray(call.calldata) ? call.calldata : [];
577
577
  out.push(BigInt(callData.length));
578
578
  for (const datum of callData) {
@@ -1952,14 +1952,14 @@ var ChipiTransactions = class {
1952
1952
  /**
1953
1953
  * Get on-chain transaction status
1954
1954
  */
1955
- async getTransactionStatus(hash9, bearerToken) {
1955
+ async getTransactionStatus(hash11, bearerToken) {
1956
1956
  return this.client.get({
1957
- endpoint: `${shared.API_ENDPOINTS.TRANSACTIONS}/${hash9}/status`,
1957
+ endpoint: `${shared.API_ENDPOINTS.TRANSACTIONS}/${hash11}/status`,
1958
1958
  bearerToken
1959
1959
  });
1960
1960
  }
1961
1961
  /**
1962
- * Sync on-chain transfers from Voyager into the DB (read-through cache).
1962
+ * Sync on-chain transfers (Starknet Transfer events) into the DB (read-through cache).
1963
1963
  * Discovers transactions not made through the SDK (e.g., external USDC receives).
1964
1964
  * After sync, getTransactionList returns the newly discovered transactions.
1965
1965
  */
@@ -2124,6 +2124,144 @@ var ChipiSkus = class {
2124
2124
 
2125
2125
  // src/sessions.ts
2126
2126
  init_encryption2();
2127
+ var FIELD_PRIME = 2n ** 251n + 17n * 2n ** 192n + 1n;
2128
+ var HEX_FELT = /^0x[0-9a-fA-F]{1,64}$/;
2129
+ var DECIMAL_FELT = /^\d+$/;
2130
+ var CAIRO_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
2131
+ function normalizeEntrypoints(entrypoints) {
2132
+ return entrypoints.map((entry) => {
2133
+ const value = String(entry).trim();
2134
+ if (HEX_FELT.test(value) || DECIMAL_FELT.test(value)) {
2135
+ const felt = BigInt(value);
2136
+ if (felt >= FIELD_PRIME) {
2137
+ throw new shared.ChipiSessionError(
2138
+ `Session entrypoint ${value} is not a valid felt (>= field prime).`,
2139
+ shared.SESSION_ERRORS.SESSION_ENTRYPOINT_NOT_ALLOWED
2140
+ );
2141
+ }
2142
+ return starknet.num.toHex(felt);
2143
+ }
2144
+ if (CAIRO_IDENTIFIER.test(value)) {
2145
+ return starknet.hash.getSelectorFromName(value);
2146
+ }
2147
+ throw new shared.ChipiSessionError(
2148
+ `Session entrypoint "${value}" is neither a selector (0x\u2026) nor a function name.`,
2149
+ shared.SESSION_ERRORS.SESSION_ENTRYPOINT_NOT_ALLOWED
2150
+ );
2151
+ });
2152
+ }
2153
+ function decodeSessionData(result, nowSeconds) {
2154
+ const validUntil = Number(BigInt(result[0]));
2155
+ const maxCalls = Number(BigInt(result[1]));
2156
+ const callsUsed = Number(BigInt(result[2]));
2157
+ const remainingCalls = Math.max(maxCalls - callsUsed, 0);
2158
+ const isActive = validUntil !== 0 && nowSeconds <= validUntil && remainingCalls > 0;
2159
+ return { isActive, validUntil, remainingCalls, allowedEntrypoints: [] };
2160
+ }
2161
+ var U128_MASK = (1n << 128n) - 1n;
2162
+ var U256_MAX = (1n << 256n) - 1n;
2163
+ var U64_MAX = (1n << 64n) - 1n;
2164
+ function splitU256(value) {
2165
+ return [starknet.num.toHex(value & U128_MASK), starknet.num.toHex(value >> 128n)];
2166
+ }
2167
+ function buildAddSessionKeyCall(walletAddress, sessionConfig) {
2168
+ const allowedEntrypoints = normalizeEntrypoints(sessionConfig.allowedEntrypoints);
2169
+ return {
2170
+ contractAddress: walletAddress,
2171
+ entrypoint: shared.SESSION_ENTRYPOINTS.ADD_OR_UPDATE,
2172
+ calldata: [
2173
+ sessionConfig.sessionPublicKey,
2174
+ starknet.num.toHex(sessionConfig.validUntil),
2175
+ starknet.num.toHex(sessionConfig.maxCalls),
2176
+ starknet.num.toHex(allowedEntrypoints.length),
2177
+ ...allowedEntrypoints
2178
+ ]
2179
+ };
2180
+ }
2181
+ function buildRevokeSessionKeyCall(walletAddress, sessionPublicKey) {
2182
+ return {
2183
+ contractAddress: walletAddress,
2184
+ entrypoint: shared.SESSION_ENTRYPOINTS.REVOKE,
2185
+ calldata: [sessionPublicKey]
2186
+ };
2187
+ }
2188
+ function validateSpendingPolicyConfig(config) {
2189
+ const { token, maxPerCall, maxPerWindow, windowSeconds } = config;
2190
+ if (!token || token.trim().length === 0) {
2191
+ throw new shared.ChipiSessionError(
2192
+ `Invalid token address for spending policy: "${token}"`,
2193
+ shared.SESSION_ERRORS.INVALID_SPENDING_POLICY
2194
+ );
2195
+ }
2196
+ if (typeof windowSeconds !== "number" || !Number.isFinite(windowSeconds) || !Number.isInteger(windowSeconds) || windowSeconds <= 0 || BigInt(windowSeconds) > U64_MAX) {
2197
+ throw new shared.ChipiSessionError(
2198
+ `Invalid window duration: ${windowSeconds}. Must be a positive integer within u64 range.`,
2199
+ shared.SESSION_ERRORS.INVALID_SPENDING_POLICY
2200
+ );
2201
+ }
2202
+ if (maxPerCall < 0n || maxPerCall > U256_MAX) {
2203
+ throw new shared.ChipiSessionError(
2204
+ `Invalid maxPerCall: must be >= 0 and <= 2^256 - 1`,
2205
+ shared.SESSION_ERRORS.INVALID_SPENDING_POLICY
2206
+ );
2207
+ }
2208
+ if (maxPerWindow < 0n || maxPerWindow > U256_MAX) {
2209
+ throw new shared.ChipiSessionError(
2210
+ `Invalid maxPerWindow: must be >= 0 and <= 2^256 - 1`,
2211
+ shared.SESSION_ERRORS.INVALID_SPENDING_POLICY
2212
+ );
2213
+ }
2214
+ if (maxPerWindow > 0n && maxPerCall > maxPerWindow) {
2215
+ throw new shared.ChipiSessionError(
2216
+ `Invalid spending policy: maxPerCall (${maxPerCall}) cannot exceed maxPerWindow (${maxPerWindow})`,
2217
+ shared.SESSION_ERRORS.INVALID_SPENDING_POLICY
2218
+ );
2219
+ }
2220
+ }
2221
+ function buildSetSpendingPolicyCall(walletAddress, config) {
2222
+ validateSpendingPolicyConfig(config);
2223
+ const [maxPerCallLow, maxPerCallHigh] = splitU256(config.maxPerCall);
2224
+ const [maxPerWindowLow, maxPerWindowHigh] = splitU256(config.maxPerWindow);
2225
+ return {
2226
+ contractAddress: walletAddress,
2227
+ entrypoint: shared.SESSION_ENTRYPOINTS.SET_SPENDING_POLICY,
2228
+ calldata: [
2229
+ config.sessionPublicKey,
2230
+ config.token,
2231
+ maxPerCallLow,
2232
+ maxPerCallHigh,
2233
+ maxPerWindowLow,
2234
+ maxPerWindowHigh,
2235
+ starknet.num.toHex(config.windowSeconds)
2236
+ ]
2237
+ };
2238
+ }
2239
+ function buildRemoveSpendingPolicyCall(walletAddress, sessionPublicKey, token) {
2240
+ if (!token || token.trim().length === 0) {
2241
+ throw new shared.ChipiSessionError(
2242
+ `Invalid token address for spending policy removal: "${token}"`,
2243
+ shared.SESSION_ERRORS.INVALID_SPENDING_POLICY
2244
+ );
2245
+ }
2246
+ return {
2247
+ contractAddress: walletAddress,
2248
+ entrypoint: shared.SESSION_ENTRYPOINTS.REMOVE_SPENDING_POLICY,
2249
+ calldata: [sessionPublicKey, token]
2250
+ };
2251
+ }
2252
+ function buildSessionSetupCalls(walletAddress, sessionConfig, spendingPolicies = []) {
2253
+ return [
2254
+ buildAddSessionKeyCall(walletAddress, sessionConfig),
2255
+ ...spendingPolicies.map(
2256
+ (policy) => buildSetSpendingPolicyCall(walletAddress, {
2257
+ ...policy,
2258
+ sessionPublicKey: sessionConfig.sessionPublicKey
2259
+ })
2260
+ )
2261
+ ];
2262
+ }
2263
+
2264
+ // src/sessions.ts
2127
2265
  var ChipiSessions = class {
2128
2266
  constructor(client) {
2129
2267
  this.client = client;
@@ -2263,25 +2401,12 @@ var ChipiSessions = class {
2263
2401
  maxCalls: sessionConfig.maxCalls,
2264
2402
  allowedEntrypoints: sessionConfig.allowedEntrypoints.length
2265
2403
  });
2266
- const calldata = [
2267
- sessionConfig.sessionPublicKey,
2268
- starknet.num.toHex(sessionConfig.validUntil),
2269
- starknet.num.toHex(sessionConfig.maxCalls),
2270
- // Array: first element is length, then elements
2271
- starknet.num.toHex(sessionConfig.allowedEntrypoints.length),
2272
- ...sessionConfig.allowedEntrypoints
2273
- ];
2404
+ const addCall = buildAddSessionKeyCall(wallet.publicKey, sessionConfig);
2274
2405
  const txHash = await executePaymasterTransaction({
2275
2406
  params: {
2276
2407
  encryptKey,
2277
2408
  wallet,
2278
- calls: [
2279
- {
2280
- contractAddress: wallet.publicKey,
2281
- entrypoint: shared.SESSION_ENTRYPOINTS.ADD_OR_UPDATE,
2282
- calldata
2283
- }
2284
- ],
2409
+ calls: [addCall],
2285
2410
  saveToDatabase: false
2286
2411
  // Don't record session management txs
2287
2412
  },
@@ -2305,6 +2430,54 @@ var ChipiSessions = class {
2305
2430
  throw error;
2306
2431
  }
2307
2432
  }
2433
+ /**
2434
+ * Register a session and its spending policies in ONE owner-signed transaction.
2435
+ *
2436
+ * Equivalent to `addSessionKeyToContract` followed by one `setSpendingPolicy`
2437
+ * per token, but the owner signs once (one passkey prompt) and the session
2438
+ * never exists on-chain without its caps.
2439
+ *
2440
+ * @example
2441
+ * ```typescript
2442
+ * const txHash = await sdk.sessions.setupSession({
2443
+ * encryptKey,
2444
+ * wallet,
2445
+ * sessionConfig: {
2446
+ * sessionPublicKey: session.publicKey,
2447
+ * validUntil: session.validUntil,
2448
+ * maxCalls: 500,
2449
+ * allowedEntrypoints: ["transfer", "approve"],
2450
+ * },
2451
+ * spendingPolicies: [
2452
+ * { token: USDC, maxPerCall: 5_000_000n, maxPerWindow: 20_000_000n, windowSeconds: 86400 },
2453
+ * ],
2454
+ * }, bearerToken);
2455
+ * ```
2456
+ */
2457
+ async setupSession(params, bearerToken) {
2458
+ const { encryptKey, wallet, sessionConfig, spendingPolicies = [] } = params;
2459
+ this.validateWalletSupportsSessions(wallet.walletType, "Setup", wallet.publicKey);
2460
+ const calls = buildSessionSetupCalls(wallet.publicKey, sessionConfig, spendingPolicies);
2461
+ console.log("[ChipiSDK:Session:Setup] Registering session with spending policies", {
2462
+ walletAddress: wallet.publicKey.slice(0, 15) + "...",
2463
+ sessionPubKey: sessionConfig.sessionPublicKey.slice(0, 15) + "...",
2464
+ validUntil: new Date(sessionConfig.validUntil * 1e3).toISOString(),
2465
+ maxCalls: sessionConfig.maxCalls,
2466
+ allowedEntrypoints: sessionConfig.allowedEntrypoints.length,
2467
+ spendingPolicies: spendingPolicies.length
2468
+ });
2469
+ return executePaymasterTransaction({
2470
+ params: {
2471
+ encryptKey,
2472
+ wallet,
2473
+ calls,
2474
+ saveToDatabase: false
2475
+ // Don't record session management txs
2476
+ },
2477
+ bearerToken,
2478
+ client: this.client
2479
+ });
2480
+ }
2308
2481
  /**
2309
2482
  * Revoke a session key from the smart contract.
2310
2483
  *
@@ -2338,13 +2511,7 @@ var ChipiSessions = class {
2338
2511
  params: {
2339
2512
  encryptKey,
2340
2513
  wallet,
2341
- calls: [
2342
- {
2343
- contractAddress: wallet.publicKey,
2344
- entrypoint: shared.SESSION_ENTRYPOINTS.REVOKE,
2345
- calldata: [sessionPublicKey]
2346
- }
2347
- ],
2514
+ calls: [buildRevokeSessionKeyCall(wallet.publicKey, sessionPublicKey)],
2348
2515
  saveToDatabase: false
2349
2516
  // Don't record session management txs
2350
2517
  },
@@ -2419,11 +2586,10 @@ var ChipiSessions = class {
2419
2586
  allowedEntrypoints: []
2420
2587
  };
2421
2588
  }
2422
- const isActive = resultArray[0] === "0x1";
2423
- const validUntil = Number(BigInt(resultArray[1]));
2424
- const remainingCalls = Number(BigInt(resultArray[2]));
2425
- const entrypointsLen = Number(BigInt(resultArray[3]));
2426
- const allowedEntrypoints = resultArray.slice(4, 4 + entrypointsLen);
2589
+ const { isActive, validUntil, remainingCalls, allowedEntrypoints } = decodeSessionData(
2590
+ resultArray,
2591
+ Math.floor(Date.now() / 1e3)
2592
+ );
2427
2593
  const sessionData = {
2428
2594
  isActive,
2429
2595
  validUntil,
@@ -2434,7 +2600,8 @@ var ChipiSessions = class {
2434
2600
  isActive,
2435
2601
  validUntil: new Date(validUntil * 1e3).toISOString(),
2436
2602
  remainingCalls,
2437
- allowedEntrypointsCount: allowedEntrypoints.length
2603
+ // The struct carries the whitelist length, not the selectors.
2604
+ allowedEntrypointsLen: Number(BigInt(resultArray[3]))
2438
2605
  });
2439
2606
  return sessionData;
2440
2607
  } catch (error) {
@@ -2534,13 +2701,6 @@ var ChipiSessions = class {
2534
2701
  // Wraps ISessionSpendingPolicy contract interface.
2535
2702
  // Spending policies enforce per-token limits on ERC-20 operations
2536
2703
  // (transfer, approve, increase_allowance) executed by session keys.
2537
- /**
2538
- * Split a bigint into Cairo u256 representation (low u128, high u128).
2539
- */
2540
- splitU256(value) {
2541
- const mask = (1n << 128n) - 1n;
2542
- return [starknet.num.toHex(value & mask), starknet.num.toHex(value >> 128n)];
2543
- }
2544
2704
  /**
2545
2705
  * Reconstruct a bigint from Cairo u256 (low, high) hex strings.
2546
2706
  */
@@ -2585,38 +2745,7 @@ var ChipiSessions = class {
2585
2745
  shared.SESSION_ERRORS.INVALID_WALLET_TYPE_FOR_SESSION
2586
2746
  );
2587
2747
  }
2588
- const U256_MAX = (1n << 256n) - 1n;
2589
- const U64_MAX = (1n << 64n) - 1n;
2590
- if (!token || token.trim().length === 0) {
2591
- throw new shared.ChipiSessionError(
2592
- `Invalid token address for spending policy: "${token}"`,
2593
- shared.SESSION_ERRORS.INVALID_SPENDING_POLICY
2594
- );
2595
- }
2596
- if (typeof windowSeconds !== "number" || !Number.isFinite(windowSeconds) || !Number.isInteger(windowSeconds) || windowSeconds <= 0 || BigInt(windowSeconds) > U64_MAX) {
2597
- throw new shared.ChipiSessionError(
2598
- `Invalid window duration: ${windowSeconds}. Must be a positive integer within u64 range.`,
2599
- shared.SESSION_ERRORS.INVALID_SPENDING_POLICY
2600
- );
2601
- }
2602
- if (maxPerCall < 0n || maxPerCall > U256_MAX) {
2603
- throw new shared.ChipiSessionError(
2604
- `Invalid maxPerCall: must be >= 0 and <= 2^256 - 1`,
2605
- shared.SESSION_ERRORS.INVALID_SPENDING_POLICY
2606
- );
2607
- }
2608
- if (maxPerWindow < 0n || maxPerWindow > U256_MAX) {
2609
- throw new shared.ChipiSessionError(
2610
- `Invalid maxPerWindow: must be >= 0 and <= 2^256 - 1`,
2611
- shared.SESSION_ERRORS.INVALID_SPENDING_POLICY
2612
- );
2613
- }
2614
- if (maxPerWindow > 0n && maxPerCall > maxPerWindow) {
2615
- throw new shared.ChipiSessionError(
2616
- `Invalid spending policy: maxPerCall (${maxPerCall}) cannot exceed maxPerWindow (${maxPerWindow})`,
2617
- shared.SESSION_ERRORS.INVALID_SPENDING_POLICY
2618
- );
2619
- }
2748
+ validateSpendingPolicyConfig(spendingPolicyConfig);
2620
2749
  try {
2621
2750
  console.log("[ChipiSDK:Session:SetSpendingPolicy] Setting spending policy", {
2622
2751
  walletAddress: wallet.publicKey.slice(0, 15) + "...",
@@ -2626,28 +2755,12 @@ var ChipiSessions = class {
2626
2755
  maxPerWindow: maxPerWindow.toString(),
2627
2756
  windowSeconds
2628
2757
  });
2629
- const [maxPerCallLow, maxPerCallHigh] = this.splitU256(maxPerCall);
2630
- const [maxPerWindowLow, maxPerWindowHigh] = this.splitU256(maxPerWindow);
2631
- const calldata = [
2632
- sessionPublicKey,
2633
- token,
2634
- maxPerCallLow,
2635
- maxPerCallHigh,
2636
- maxPerWindowLow,
2637
- maxPerWindowHigh,
2638
- starknet.num.toHex(windowSeconds)
2639
- ];
2758
+ const setPolicyCall = buildSetSpendingPolicyCall(wallet.publicKey, spendingPolicyConfig);
2640
2759
  const txHash = await executePaymasterTransaction({
2641
2760
  params: {
2642
2761
  encryptKey,
2643
2762
  wallet,
2644
- calls: [
2645
- {
2646
- contractAddress: wallet.publicKey,
2647
- entrypoint: shared.SESSION_ENTRYPOINTS.SET_SPENDING_POLICY,
2648
- calldata
2649
- }
2650
- ],
2763
+ calls: [setPolicyCall],
2651
2764
  saveToDatabase: false
2652
2765
  },
2653
2766
  bearerToken,
@@ -2814,18 +2927,12 @@ var ChipiSessions = class {
2814
2927
  sessionPubKey: sessionPublicKey.slice(0, 15) + "...",
2815
2928
  token: token.slice(0, 15) + "..."
2816
2929
  });
2817
- const calldata = [sessionPublicKey, token];
2930
+ const removeCall = buildRemoveSpendingPolicyCall(wallet.publicKey, sessionPublicKey, token);
2818
2931
  const txHash = await executePaymasterTransaction({
2819
2932
  params: {
2820
2933
  encryptKey,
2821
2934
  wallet,
2822
- calls: [
2823
- {
2824
- contractAddress: wallet.publicKey,
2825
- entrypoint: shared.SESSION_ENTRYPOINTS.REMOVE_SPENDING_POLICY,
2826
- calldata
2827
- }
2828
- ],
2935
+ calls: [removeCall],
2829
2936
  saveToDatabase: false
2830
2937
  },
2831
2938
  bearerToken,
@@ -3006,6 +3113,105 @@ var ChipiTreasury = class {
3006
3113
  };
3007
3114
  }
3008
3115
  };
3116
+ var AI_BASE = "/ai";
3117
+ var THINK_ACTIONS = /* @__PURE__ */ new Set(["swap", "supply", "withdraw", "hold"]);
3118
+ function isThinkDecision(decision) {
3119
+ if (!decision || typeof decision !== "object") return false;
3120
+ const d = decision;
3121
+ if (typeof d.action !== "string" || !THINK_ACTIONS.has(d.action)) return false;
3122
+ if (typeof d.reason !== "string") return false;
3123
+ if (d.amount !== void 0 && (typeof d.amount !== "number" || !Number.isFinite(d.amount)))
3124
+ return false;
3125
+ if (d.confidence !== void 0 && typeof d.confidence !== "number") return false;
3126
+ if (d.from !== void 0 && typeof d.from !== "string") return false;
3127
+ if (d.to !== void 0 && typeof d.to !== "string") return false;
3128
+ return true;
3129
+ }
3130
+ var ChipiAi = class {
3131
+ constructor(client, apiSecretKey) {
3132
+ this.client = client;
3133
+ this.apiSecretKey = apiSecretKey;
3134
+ }
3135
+ secret() {
3136
+ const key = this.apiSecretKey;
3137
+ if (!key || !key.startsWith("sk_")) {
3138
+ throw new shared.ChipiAuthError(
3139
+ "sdk.ai needs a secret key (sk_): configure the SDK with apiSecretKey and call it from a server, never the browser."
3140
+ );
3141
+ }
3142
+ return key;
3143
+ }
3144
+ /**
3145
+ * `GET /v1/ai/signals`: technical signals per token, market sentiment and
3146
+ * DeFi yields. Free up to a monthly quota per organization, then billed per call.
3147
+ */
3148
+ async signals(params = {}) {
3149
+ const query = {};
3150
+ if (params.tokens?.length) query.tokens = params.tokens.join(",");
3151
+ if (params.chain) query.chain = params.chain;
3152
+ return this.client.get({
3153
+ endpoint: `${AI_BASE}/signals`,
3154
+ params: query,
3155
+ bearerToken: this.secret()
3156
+ });
3157
+ }
3158
+ /**
3159
+ * `POST /v1/ai/think`: one DeFi decision for a portfolio, with the latest
3160
+ * signals and your risk parameters injected into the prompt.
3161
+ *
3162
+ * The decision is advice, not an order: check it with `isThinkDecision` and
3163
+ * enforce your own limits before executing anything.
3164
+ */
3165
+ async think(params) {
3166
+ return this.client.post({
3167
+ endpoint: `${AI_BASE}/think`,
3168
+ bearerToken: this.secret(),
3169
+ body: params
3170
+ });
3171
+ }
3172
+ /**
3173
+ * `POST /v1/ai/execute`: unsigned AVNU swap calls (approve + swap) for
3174
+ * `walletAddress`. Nothing is submitted; sign the calls with a session key
3175
+ * or the owner's key.
3176
+ */
3177
+ async execute(params) {
3178
+ return this.client.post({
3179
+ endpoint: `${AI_BASE}/execute`,
3180
+ bearerToken: this.secret(),
3181
+ body: params
3182
+ });
3183
+ }
3184
+ /** `POST /v1/ai/chat`: general-purpose chat, billed per token. */
3185
+ async chat(params) {
3186
+ return this.client.post({
3187
+ endpoint: `${AI_BASE}/chat`,
3188
+ bearerToken: this.secret(),
3189
+ body: params
3190
+ });
3191
+ }
3192
+ /**
3193
+ * `GET /v1/ai/prices`: USD prices and 1h / 24h / 7d changes, refreshed about
3194
+ * every 30 seconds. No credits are charged, but like every `sdk.ai` method it
3195
+ * needs the SDK's secret key (`sk_`).
3196
+ */
3197
+ async prices(params = {}) {
3198
+ const query = {};
3199
+ if (params.tokens?.length) query.tokens = params.tokens.join(",");
3200
+ if (params.chain) query.chain = params.chain;
3201
+ return this.client.get({
3202
+ endpoint: `${AI_BASE}/prices`,
3203
+ params: query,
3204
+ bearerToken: this.secret()
3205
+ });
3206
+ }
3207
+ /** `GET /v1/ai/models`: available models, prices and limits. */
3208
+ async models() {
3209
+ return this.client.get({
3210
+ endpoint: `${AI_BASE}/models`,
3211
+ bearerToken: this.secret()
3212
+ });
3213
+ }
3214
+ };
3009
3215
  var InMemoryNonceStore = class {
3010
3216
  constructor() {
3011
3217
  this.used = /* @__PURE__ */ new Set();
@@ -3323,6 +3529,7 @@ var ChipiSDK = class {
3323
3529
  this.sessions = new ChipiSessions(this.client);
3324
3530
  this.exchanges = new ChipiExchanges(this.client);
3325
3531
  this.treasury = new ChipiTreasury(this.client, config.apiSecretKey);
3532
+ this.ai = new ChipiAi(this.client, config.apiSecretKey);
3326
3533
  this.executeTransaction = this.executeTransaction.bind(this);
3327
3534
  this.executeTransactionWithSession = this.executeTransactionWithSession.bind(this);
3328
3535
  this.transfer = this.transfer.bind(this);
@@ -3586,8 +3793,8 @@ var ChipiSDK = class {
3586
3793
  /**
3587
3794
  * Get on-chain transaction status (supports polling for confirmation)
3588
3795
  */
3589
- async getTransactionStatus(hash9, bearerToken) {
3590
- return this.transactions.getTransactionStatus(hash9, this.resolveBearerToken(bearerToken));
3796
+ async getTransactionStatus(hash11, bearerToken) {
3797
+ return this.transactions.getTransactionStatus(hash11, this.resolveBearerToken(bearerToken));
3591
3798
  }
3592
3799
  /**
3593
3800
  * Prepare a wallet upgrade (e.g., READY → CHIPI or CHIPI v29 → v33).
@@ -4002,6 +4209,79 @@ function x402Middleware(config) {
4002
4209
  next();
4003
4210
  };
4004
4211
  }
4212
+ var InMemoryTxHashStore = class {
4213
+ constructor() {
4214
+ this.used = /* @__PURE__ */ new Set();
4215
+ }
4216
+ consume(transactionHash) {
4217
+ if (this.used.has(transactionHash)) return false;
4218
+ this.used.add(transactionHash);
4219
+ return true;
4220
+ }
4221
+ };
4222
+ var TRANSFER_SELECTOR = BigInt(starknet.hash.getSelectorFromName("Transfer"));
4223
+ function toBigInt2(value) {
4224
+ if (value === void 0) return void 0;
4225
+ try {
4226
+ return BigInt(value);
4227
+ } catch {
4228
+ return void 0;
4229
+ }
4230
+ }
4231
+ function decodeTransfer(event) {
4232
+ if (toBigInt2(event.keys[0]) !== TRANSFER_SELECTOR) return void 0;
4233
+ const [from, to, low, high] = event.keys.length >= 3 ? [event.keys[1], event.keys[2], event.data[0], event.data[1]] : [event.data[0], event.data[1], event.data[2], event.data[3]];
4234
+ const values = [from, to, low, high].map(toBigInt2);
4235
+ if (values.some((v) => v === void 0)) return void 0;
4236
+ const [f, t, l, h] = values;
4237
+ return { from: f, to: t, amount: l + (h << 128n) };
4238
+ }
4239
+ var SessionTxVerifier = class {
4240
+ constructor(config = {}) {
4241
+ const { txHashStore, getReceipt, ...receiptOptions } = config;
4242
+ this.store = txHashStore ?? new InMemoryTxHashStore();
4243
+ this.getReceipt = getReceipt ?? waitForTransaction;
4244
+ this.receiptOptions = receiptOptions;
4245
+ }
4246
+ async verifyTransfer(params) {
4247
+ const { transactionHash, token, from, to, minAmount } = params;
4248
+ const receipt = await this.getReceipt(transactionHash, this.receiptOptions);
4249
+ if (!receipt.success) {
4250
+ return { valid: false, transactionHash, reason: "REVERTED", detail: receipt.revertReason };
4251
+ }
4252
+ const tokenAddress = BigInt(token);
4253
+ const fromAddress = BigInt(from);
4254
+ const toAddress = BigInt(to);
4255
+ let best;
4256
+ for (const event of receipt.events) {
4257
+ if (toBigInt2(event.from_address) !== tokenAddress) continue;
4258
+ const transfer = decodeTransfer(event);
4259
+ if (!transfer || transfer.from !== fromAddress || transfer.to !== toAddress) continue;
4260
+ if (best === void 0 || transfer.amount > best) best = transfer.amount;
4261
+ }
4262
+ if (best === void 0) {
4263
+ return { valid: false, transactionHash, reason: "TRANSFER_NOT_FOUND" };
4264
+ }
4265
+ if (best < minAmount) {
4266
+ return {
4267
+ valid: false,
4268
+ transactionHash,
4269
+ reason: "AMOUNT_TOO_LOW",
4270
+ detail: `paid ${best}, expected at least ${minAmount}`
4271
+ };
4272
+ }
4273
+ const key = "0x" + BigInt(transactionHash).toString(16);
4274
+ if (!await this.store.consume(key)) {
4275
+ return { valid: false, transactionHash, reason: "ALREADY_USED" };
4276
+ }
4277
+ return {
4278
+ valid: true,
4279
+ transactionHash,
4280
+ amount: best,
4281
+ ...receipt.blockNumber !== void 0 ? { blockNumber: receipt.blockNumber } : {}
4282
+ };
4283
+ }
4284
+ };
4005
4285
 
4006
4286
  // src/shhh/index.ts
4007
4287
  init_snip12_hash();
@@ -4407,7 +4687,7 @@ var JWT_APPLE_SUB_KIND_TAG = "JWT_ES256_APPLE_SUB";
4407
4687
  var JWT_APPLE_SUB_KIND_TAG_FELT = BigInt(starknet.shortString.encodeShortString(JWT_APPLE_SUB_KIND_TAG));
4408
4688
  var APPLE_ISSUER = "https://appleid.apple.com";
4409
4689
  var NONCE_B64URL_LEN = 43;
4410
- var U128_MASK = (1n << 128n) - 1n;
4690
+ var U128_MASK2 = (1n << 128n) - 1n;
4411
4691
  function assertBytes3(name, b, len) {
4412
4692
  if (b.length !== len) {
4413
4693
  throw new Error(`${name} must be exactly ${len} bytes; got ${b.length}.`);
@@ -4417,7 +4697,7 @@ function bytesBeToU2563(b) {
4417
4697
  assertBytes3("secp256r1 32B value", b, 32);
4418
4698
  let x = 0n;
4419
4699
  for (let i = 0; i < b.length; i++) x = x << 8n | BigInt(b[i]);
4420
- return { low: x & U128_MASK, high: x >> 128n };
4700
+ return { low: x & U128_MASK2, high: x >> 128n };
4421
4701
  }
4422
4702
  function jwtAppleSubHash(sub) {
4423
4703
  const bytes = new TextEncoder().encode(sub);
@@ -4552,9 +4832,9 @@ function buildEnvelopePayload(fields) {
4552
4832
  BigInt(fields.issOffset),
4553
4833
  BigInt(fields.subOffset),
4554
4834
  BigInt(fields.subLen),
4555
- fields.r & U128_MASK,
4835
+ fields.r & U128_MASK2,
4556
4836
  fields.r >> 128n,
4557
- fields.s & U128_MASK,
4837
+ fields.s & U128_MASK2,
4558
4838
  fields.s >> 128n,
4559
4839
  BigInt(fields.yParity)
4560
4840
  ];
@@ -4914,6 +5194,7 @@ Object.defineProperty(exports, "Currency", {
4914
5194
  });
4915
5195
  exports.APPLE_ISSUER = APPLE_ISSUER;
4916
5196
  exports.BOOTSTRAP_DOMAIN_SEPARATOR = BOOTSTRAP_DOMAIN_SEPARATOR;
5197
+ exports.ChipiAi = ChipiAi;
4917
5198
  exports.ChipiBrowserSDK = ChipiBrowserSDK;
4918
5199
  exports.ChipiClient = ChipiClient;
4919
5200
  exports.ChipiPaymasterAdapter = ChipiPaymasterAdapter;
@@ -4932,12 +5213,14 @@ exports.OP_ROTATE_OWNER = OP_ROTATE_OWNER;
4932
5213
  exports.OP_SET_THRESHOLD = OP_SET_THRESHOLD;
4933
5214
  exports.ROLE_GUARDIAN = ROLE_GUARDIAN;
4934
5215
  exports.ROLE_OWNER = ROLE_OWNER;
5216
+ exports.SessionTxVerifier = SessionTxVerifier;
4935
5217
  exports.ShhhAccount = ShhhAccount;
4936
5218
  exports.TIMELOCK_SECONDS = TIMELOCK_SECONDS;
4937
5219
  exports.VIEW_SELECTORS = VIEW_SELECTORS;
4938
5220
  exports.X402Facilitator = X402Facilitator;
4939
5221
  exports.X402ShhhClient = X402ShhhClient;
4940
5222
  exports.assertValidV2Snip12Envelope = assertValidV2Snip12Envelope;
5223
+ exports.buildAddSessionKeyCall = buildAddSessionKeyCall;
4941
5224
  exports.buildBootstrapSignedCall = buildBootstrapSignedCall;
4942
5225
  exports.buildCancelPendingOpCall = buildCancelPendingOpCall;
4943
5226
  exports.buildCancelRecoveryCall = buildCancelRecoveryCall;
@@ -4959,6 +5242,10 @@ exports.buildProposeAddOwnerCall = buildProposeAddOwnerCall;
4959
5242
  exports.buildProposeRemoveOwnerCall = buildProposeRemoveOwnerCall;
4960
5243
  exports.buildProposeRotateOwnerCall = buildProposeRotateOwnerCall;
4961
5244
  exports.buildProposeSetThresholdCall = buildProposeSetThresholdCall;
5245
+ exports.buildRemoveSpendingPolicyCall = buildRemoveSpendingPolicyCall;
5246
+ exports.buildRevokeSessionKeyCall = buildRevokeSessionKeyCall;
5247
+ exports.buildSessionSetupCalls = buildSessionSetupCalls;
5248
+ exports.buildSetSpendingPolicyCall = buildSetSpendingPolicyCall;
4962
5249
  exports.buildStarkEnvelope = buildStarkEnvelope;
4963
5250
  exports.buildThresholdEnvelope = buildThresholdEnvelope;
4964
5251
  exports.buildWebAuthnEnvelopeFromAssertion = buildWebAuthnEnvelopeFromAssertion;
@@ -4984,6 +5271,7 @@ exports.hashCalls = hashCalls;
4984
5271
  exports.hashOutsideExecutionStruct = hashOutsideExecutionStruct;
4985
5272
  exports.hashStarknetDomain = hashStarknetDomain;
4986
5273
  exports.isReadyToExecute = isReadyToExecute;
5274
+ exports.isThinkDecision = isThinkDecision;
4987
5275
  exports.jwtAppleSubHash = jwtAppleSubHash;
4988
5276
  exports.jwtAppleSubOwnerId = jwtAppleSubOwnerId;
4989
5277
  exports.jwtAppleSubPubkeyFelts = jwtAppleSubPubkeyFelts;
@@ -4991,6 +5279,7 @@ exports.labelToFelt = labelToFelt;
4991
5279
  exports.locateSubClaim = locateSubClaim;
4992
5280
  exports.messageHashToNonceB64url = messageHashToNonceB64url;
4993
5281
  exports.migrateChipiWalletToShhh = migrateChipiWalletToShhh;
5282
+ exports.normalizeEntrypoints = normalizeEntrypoints;
4994
5283
  exports.ownerCommitment = ownerCommitment;
4995
5284
  exports.parseAppleIdToken = parseAppleIdToken;
4996
5285
  exports.parseDerEcdsaSignature = parseDerEcdsaSignature;
@@ -5006,6 +5295,7 @@ exports.snip12HashToHexAsciiBytes = snip12HashToHexAsciiBytes;
5006
5295
  exports.starkOwnerId = starkOwnerId;
5007
5296
  exports.starkPubkeyFelts = starkPubkeyFelts;
5008
5297
  exports.submitViaAccount = submitViaAccount;
5298
+ exports.validateSpendingPolicyConfig = validateSpendingPolicyConfig;
5009
5299
  exports.waitForTransaction = waitForTransaction;
5010
5300
  exports.webauthnOwnerId = webauthnOwnerId;
5011
5301
  exports.webauthnOwnerPubkeyParam = webauthnOwnerPubkeyParam;