@ottocode/sdk 0.1.203 → 0.1.205

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@ottocode/sdk",
3
- "version": "0.1.203",
3
+ "version": "0.1.205",
4
4
  "description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
5
5
  "author": "nitishxyz",
6
6
  "license": "MIT",
@@ -78,6 +78,7 @@ export type SetuProviderOptions = {
78
78
  promptCacheKey?: string;
79
79
  promptCacheRetention?: 'in_memory' | '24h';
80
80
  topupApprovalMode?: 'auto' | 'approval';
81
+ autoPayThresholdUsd?: number;
81
82
  };
82
83
 
83
84
  export type SetuAuth = {
@@ -163,6 +164,7 @@ export function createSetuFetch(
163
164
  const promptCacheKey = options.promptCacheKey;
164
165
  const promptCacheRetention = options.promptCacheRetention;
165
166
  const topupApprovalMode = options.topupApprovalMode ?? 'auto';
167
+ const autoPayThresholdUsd = options.autoPayThresholdUsd ?? 0;
166
168
 
167
169
  const baseFetch = globalThis.fetch.bind(globalThis);
168
170
 
@@ -232,39 +234,62 @@ export function createSetuFetch(
232
234
  const amountUsd =
233
235
  parseInt(requirement.maxAmountRequired, 10) / 1_000_000;
234
236
 
235
- if (topupApprovalMode === 'approval' && callbacks.onPaymentApproval) {
237
+ let walletUsdcBalance = 0;
238
+ if (autoPayThresholdUsd > 0) {
239
+ walletUsdcBalance = await getWalletUsdcBalance(walletAddress, rpcURL);
240
+ }
241
+
242
+ const canAutoPay =
243
+ autoPayThresholdUsd > 0 && walletUsdcBalance >= autoPayThresholdUsd;
244
+
245
+ const requestApproval = async () => {
246
+ if (!callbacks.onPaymentApproval) return;
236
247
  const approval = await callbacks.onPaymentApproval({
237
248
  amountUsd,
238
- currentBalance: 0,
249
+ currentBalance: walletUsdcBalance,
239
250
  });
240
-
241
251
  if (approval === 'cancel') {
242
252
  callbacks.onPaymentError?.('Payment cancelled by user');
243
253
  throw new Error('Setu: payment cancelled by user');
244
254
  }
245
-
246
255
  if (approval === 'fiat') {
247
256
  const err = new Error('Setu: fiat payment selected');
248
257
  (err as Error & { code: string }).code = 'SETU_FIAT_SELECTED';
249
258
  throw err;
250
259
  }
260
+ };
261
+
262
+ if (!canAutoPay && topupApprovalMode === 'approval') {
263
+ await requestApproval();
251
264
  }
252
265
 
253
- callbacks.onPaymentRequired?.(amountUsd, 0);
254
-
255
- const outcome = await handlePayment({
256
- requirement,
257
- keypair,
258
- rpcURL,
259
- baseURL,
260
- baseFetch,
261
- buildWalletHeaders,
262
- maxAttempts: remainingPayments,
263
- callbacks,
264
- });
265
-
266
- const newTotal = currentAttempts + outcome.attemptsUsed;
267
- globalPaymentAttempts.set(walletAddress, newTotal);
266
+ callbacks.onPaymentRequired?.(amountUsd, walletUsdcBalance);
267
+
268
+ const doPayment = async () => {
269
+ const outcome = await handlePayment({
270
+ requirement,
271
+ keypair,
272
+ rpcURL,
273
+ baseURL,
274
+ baseFetch,
275
+ buildWalletHeaders,
276
+ maxAttempts: remainingPayments,
277
+ callbacks,
278
+ });
279
+ const newTotal = currentAttempts + outcome.attemptsUsed;
280
+ globalPaymentAttempts.set(walletAddress, newTotal);
281
+ };
282
+
283
+ if (canAutoPay) {
284
+ try {
285
+ await doPayment();
286
+ } catch (_autoPayErr) {
287
+ await requestApproval();
288
+ await doPayment();
289
+ }
290
+ } else {
291
+ await doPayment();
292
+ }
268
293
  } finally {
269
294
  releaseLock();
270
295
  }
@@ -638,6 +663,48 @@ export function getPublicKeyFromPrivate(privateKey: string): string | null {
638
663
  }
639
664
  }
640
665
 
666
+ async function getWalletUsdcBalance(
667
+ walletAddress: string,
668
+ rpcUrl: string,
669
+ ): Promise<number> {
670
+ try {
671
+ const usdcMint = rpcUrl.includes('devnet')
672
+ ? USDC_MINT_DEVNET
673
+ : USDC_MINT_MAINNET;
674
+ const response = await fetch(rpcUrl, {
675
+ method: 'POST',
676
+ headers: { 'Content-Type': 'application/json' },
677
+ body: JSON.stringify({
678
+ jsonrpc: '2.0',
679
+ id: 1,
680
+ method: 'getTokenAccountsByOwner',
681
+ params: [walletAddress, { mint: usdcMint }, { encoding: 'jsonParsed' }],
682
+ }),
683
+ });
684
+ if (!response.ok) return 0;
685
+ const data = (await response.json()) as {
686
+ result?: {
687
+ value?: Array<{
688
+ account: {
689
+ data: {
690
+ parsed: {
691
+ info: { tokenAmount: { uiAmount: number } };
692
+ };
693
+ };
694
+ };
695
+ }>;
696
+ };
697
+ };
698
+ let total = 0;
699
+ for (const acct of data.result?.value ?? []) {
700
+ total += acct.account.data.parsed.info.tokenAmount.uiAmount ?? 0;
701
+ }
702
+ return total;
703
+ } catch {
704
+ return 0;
705
+ }
706
+ }
707
+
641
708
  const USDC_MINT_MAINNET = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
642
709
  const USDC_MINT_DEVNET = '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU';
643
710