@barzkit/sdk 0.1.2 → 0.1.4

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
@@ -1,4 +1,4 @@
1
- import { http, createPublicClient } from 'viem';
1
+ import { getAddress, parseUnits, encodeFunctionData, http, createPublicClient } from 'viem';
2
2
  import { privateKeyToAccount } from 'viem/accounts';
3
3
  import { entryPoint06Address } from 'viem/account-abstraction';
4
4
  import { createSmartAccountClient } from 'permissionless';
@@ -86,6 +86,12 @@ var TransactionError = class extends BarzKitError {
86
86
  this.txHash = txHash;
87
87
  }
88
88
  };
89
+ var X402Error = class extends BarzKitError {
90
+ constructor(message) {
91
+ super(message, "X402_ERROR");
92
+ this.name = "X402Error";
93
+ }
94
+ };
89
95
  var BundlerError = class extends BarzKitError {
90
96
  constructor(message) {
91
97
  super(message, "BUNDLER_ERROR");
@@ -316,6 +322,362 @@ var ERC20_ABI = [
316
322
  type: "function"
317
323
  }
318
324
  ];
325
+ var ETH_SENTINEL = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
326
+ var TOKEN_DECIMALS = {
327
+ ETH: 18,
328
+ WETH: 18,
329
+ USDC: 6,
330
+ USDT: 6,
331
+ DAI: 18,
332
+ USDbC: 6
333
+ };
334
+ function resolveToken(symbolOrAddress, chain) {
335
+ if (symbolOrAddress.startsWith("0x")) {
336
+ return getAddress(symbolOrAddress);
337
+ }
338
+ const symbol = symbolOrAddress.toUpperCase();
339
+ if (symbol === "ETH") {
340
+ return ETH_SENTINEL;
341
+ }
342
+ const chainTokens = TOKENS[chain];
343
+ if (!chainTokens) {
344
+ throw new BarzKitError(
345
+ `No tokens configured for chain "${chain}".`,
346
+ "UNSUPPORTED_CHAIN"
347
+ );
348
+ }
349
+ const address = chainTokens[symbol];
350
+ if (!address) {
351
+ throw new BarzKitError(
352
+ `Unknown token "${symbolOrAddress}" on ${chain}. Available: ${Object.keys(chainTokens).join(", ")}`,
353
+ "UNKNOWN_TOKEN"
354
+ );
355
+ }
356
+ return address;
357
+ }
358
+ function getTokenDecimals(symbol) {
359
+ return TOKEN_DECIMALS[symbol.toUpperCase()] ?? null;
360
+ }
361
+ function isNativeETH(token) {
362
+ if (token.toUpperCase() === "ETH") return true;
363
+ if (token.startsWith("0x")) {
364
+ return token.toLowerCase() === ETH_SENTINEL.toLowerCase();
365
+ }
366
+ return false;
367
+ }
368
+
369
+ // src/actions/swap.ts
370
+ var UNISWAP_V3_ROUTER = {
371
+ sepolia: "0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E"
372
+ };
373
+ var SWAP_ROUTER_ABI = [
374
+ {
375
+ inputs: [
376
+ {
377
+ components: [
378
+ { name: "tokenIn", type: "address" },
379
+ { name: "tokenOut", type: "address" },
380
+ { name: "fee", type: "uint24" },
381
+ { name: "recipient", type: "address" },
382
+ { name: "amountIn", type: "uint256" },
383
+ { name: "amountOutMinimum", type: "uint256" },
384
+ { name: "sqrtPriceLimitX96", type: "uint160" }
385
+ ],
386
+ name: "params",
387
+ type: "tuple"
388
+ }
389
+ ],
390
+ name: "exactInputSingle",
391
+ outputs: [{ name: "amountOut", type: "uint256" }],
392
+ stateMutability: "payable",
393
+ type: "function"
394
+ }
395
+ ];
396
+ function buildSwapTransactions(params, chain, account) {
397
+ const router = UNISWAP_V3_ROUTER[chain];
398
+ if (!router) {
399
+ throw new BarzKitError(
400
+ `Uniswap V3 is not available on "${chain}". Supported: ${Object.keys(UNISWAP_V3_ROUTER).join(", ")}`,
401
+ "UNSUPPORTED_CHAIN"
402
+ );
403
+ }
404
+ const tokenIn = resolveToken(params.from, chain);
405
+ const tokenOut = resolveToken(params.to, chain);
406
+ if (tokenIn.toLowerCase() === tokenOut.toLowerCase()) {
407
+ throw new BarzKitError(
408
+ `Cannot swap a token to itself ("${params.from}" \u2192 "${params.to}").`,
409
+ "INVALID_SWAP"
410
+ );
411
+ }
412
+ const fromIsETH = isNativeETH(params.from);
413
+ const fromSymbol = params.from.startsWith("0x") ? null : params.from;
414
+ const decimals = fromSymbol ? getTokenDecimals(fromSymbol) ?? 18 : 18;
415
+ const amountIn = parseUnits(params.amount, decimals);
416
+ const routerTokenIn = fromIsETH ? resolveToken("WETH", chain) : tokenIn;
417
+ const routerTokenOut = isNativeETH(params.to) ? resolveToken("WETH", chain) : tokenOut;
418
+ const fee = params.fee ?? 3e3;
419
+ const amountOutMinimum = 0n;
420
+ const swapData = encodeFunctionData({
421
+ abi: SWAP_ROUTER_ABI,
422
+ functionName: "exactInputSingle",
423
+ args: [
424
+ {
425
+ tokenIn: routerTokenIn,
426
+ tokenOut: routerTokenOut,
427
+ fee,
428
+ recipient: account,
429
+ amountIn,
430
+ amountOutMinimum,
431
+ sqrtPriceLimitX96: 0n
432
+ }
433
+ ]
434
+ });
435
+ const txs = [];
436
+ if (fromIsETH) {
437
+ txs.push({
438
+ to: router,
439
+ value: amountIn,
440
+ data: swapData
441
+ });
442
+ } else {
443
+ const approveData = encodeFunctionData({
444
+ abi: ERC20_ABI,
445
+ functionName: "approve",
446
+ args: [router, amountIn]
447
+ });
448
+ txs.push({
449
+ to: tokenIn,
450
+ data: approveData
451
+ });
452
+ txs.push({
453
+ to: router,
454
+ data: swapData
455
+ });
456
+ }
457
+ return txs;
458
+ }
459
+ function getSwapTokenAddresses(params, chain) {
460
+ const addresses = [];
461
+ const tokenIn = resolveToken(params.from, chain);
462
+ const tokenOut = resolveToken(params.to, chain);
463
+ if (tokenIn !== ETH_SENTINEL) addresses.push(tokenIn);
464
+ if (tokenOut !== ETH_SENTINEL) addresses.push(tokenOut);
465
+ return addresses;
466
+ }
467
+ var AAVE_V3_POOL = {
468
+ sepolia: "0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951"
469
+ };
470
+ var AAVE_POOL_ABI = [
471
+ {
472
+ inputs: [
473
+ { name: "asset", type: "address" },
474
+ { name: "amount", type: "uint256" },
475
+ { name: "onBehalfOf", type: "address" },
476
+ { name: "referralCode", type: "uint16" }
477
+ ],
478
+ name: "supply",
479
+ outputs: [],
480
+ stateMutability: "nonpayable",
481
+ type: "function"
482
+ }
483
+ ];
484
+ function buildLendTransactions(params, chain, account) {
485
+ if (params.protocol !== "aave") {
486
+ throw new BarzKitError(
487
+ `Unknown lending protocol "${params.protocol}". Supported: aave`,
488
+ "UNKNOWN_PROTOCOL"
489
+ );
490
+ }
491
+ const pool = AAVE_V3_POOL[chain];
492
+ if (!pool) {
493
+ throw new BarzKitError(
494
+ `Aave V3 is not available on "${chain}". Supported: ${Object.keys(AAVE_V3_POOL).join(", ")}`,
495
+ "UNSUPPORTED_CHAIN"
496
+ );
497
+ }
498
+ if (isNativeETH(params.token)) {
499
+ throw new BarzKitError(
500
+ "Cannot supply native ETH to Aave. Wrap to WETH first, then supply WETH.",
501
+ "NATIVE_ETH_NOT_SUPPORTED"
502
+ );
503
+ }
504
+ const tokenAddress = resolveToken(params.token, chain);
505
+ const tokenSymbol = params.token.startsWith("0x") ? null : params.token;
506
+ const decimals = tokenSymbol ? getTokenDecimals(tokenSymbol) ?? 18 : 18;
507
+ const amount = parseUnits(params.amount, decimals);
508
+ const approveData = encodeFunctionData({
509
+ abi: ERC20_ABI,
510
+ functionName: "approve",
511
+ args: [pool, amount]
512
+ });
513
+ const supplyData = encodeFunctionData({
514
+ abi: AAVE_POOL_ABI,
515
+ functionName: "supply",
516
+ args: [tokenAddress, amount, account, 0]
517
+ });
518
+ return [
519
+ {
520
+ to: tokenAddress,
521
+ data: approveData
522
+ },
523
+ {
524
+ to: pool,
525
+ data: supplyData
526
+ }
527
+ ];
528
+ }
529
+ function getLendTokenAddresses(params, chain) {
530
+ const tokenAddress = resolveToken(params.token, chain);
531
+ return [tokenAddress];
532
+ }
533
+ var HEADER_AMOUNT = "x-payment-amount";
534
+ var HEADER_ADDRESS = "x-payment-address";
535
+ var HEADER_TOKEN = "x-payment-token";
536
+ var HEADER_NETWORK = "x-payment-network";
537
+ var HEADER_PROOF = "X-Payment-Proof";
538
+ function parsePaymentRequired(response) {
539
+ const amount = response.headers.get(HEADER_AMOUNT);
540
+ const address = response.headers.get(HEADER_ADDRESS);
541
+ const token = response.headers.get(HEADER_TOKEN);
542
+ const network = response.headers.get(HEADER_NETWORK);
543
+ if (!amount || !address || !token || !network) {
544
+ const missing = [
545
+ !amount && "X-Payment-Amount",
546
+ !address && "X-Payment-Address",
547
+ !token && "X-Payment-Token",
548
+ !network && "X-Payment-Network"
549
+ ].filter(Boolean);
550
+ throw new X402Error(`402 response missing required headers: ${missing.join(", ")}`);
551
+ }
552
+ const parsed = BigInt(amount);
553
+ if (parsed <= 0n) {
554
+ throw new X402Error(`Invalid payment amount: ${amount}`);
555
+ }
556
+ return {
557
+ amount: parsed,
558
+ address,
559
+ token,
560
+ network
561
+ };
562
+ }
563
+ function validateDomain(url, allowedDomains) {
564
+ if (!allowedDomains || allowedDomains.length === 0) return;
565
+ const hostname = new URL(url).hostname;
566
+ if (!allowedDomains.includes(hostname)) {
567
+ throw new X402Error(
568
+ `Domain "${hostname}" is not in the allowed list. Allowed: ${allowedDomains.join(", ")}`
569
+ );
570
+ }
571
+ }
572
+ var X402Manager = class {
573
+ _config = null;
574
+ _dailySpent = 0n;
575
+ _windowStart = 0;
576
+ get enabled() {
577
+ return this._config !== null;
578
+ }
579
+ get config() {
580
+ return this._config;
581
+ }
582
+ get dailySpent() {
583
+ this.resetIfExpired();
584
+ return this._dailySpent;
585
+ }
586
+ enable(config) {
587
+ this._config = config;
588
+ this._dailySpent = 0n;
589
+ this._windowStart = Date.now();
590
+ }
591
+ validatePayment(amount) {
592
+ if (!this._config) throw new X402Error("x402 not enabled");
593
+ this.resetIfExpired();
594
+ const maxPerRequest = parseHumanAmountToAtomic(this._config.maxPaymentPerRequest);
595
+ if (amount > maxPerRequest) {
596
+ throw new PermissionError(
597
+ `Payment amount ${amount} exceeds per-request limit of ${maxPerRequest} (${this._config.maxPaymentPerRequest})`
598
+ );
599
+ }
600
+ const maxDaily = parseHumanAmountToAtomic(this._config.maxDailyPayments);
601
+ if (this._dailySpent + amount > maxDaily) {
602
+ throw new PermissionError(
603
+ `Payment would exceed daily limit of ${maxDaily} (${this._config.maxDailyPayments}). Already spent: ${this._dailySpent}`
604
+ );
605
+ }
606
+ }
607
+ recordPayment(amount) {
608
+ this.resetIfExpired();
609
+ this._dailySpent += amount;
610
+ }
611
+ resetIfExpired() {
612
+ const now = Date.now();
613
+ const elapsed = now - this._windowStart;
614
+ const DAY_MS = 24 * 60 * 60 * 1e3;
615
+ if (elapsed >= DAY_MS) {
616
+ this._dailySpent = 0n;
617
+ this._windowStart = now;
618
+ }
619
+ }
620
+ };
621
+ function buildPaymentTransaction(payment) {
622
+ const data = encodeFunctionData({
623
+ abi: ERC20_ABI,
624
+ functionName: "transfer",
625
+ args: [payment.address, payment.amount]
626
+ });
627
+ return {
628
+ to: payment.token,
629
+ data
630
+ };
631
+ }
632
+ function createFetchWithPayment(manager, sendTransaction) {
633
+ return async function fetchWithPayment(url, options) {
634
+ if (!manager.enabled) {
635
+ throw new X402Error("x402 not enabled. Call agent.enableX402() first.");
636
+ }
637
+ const config = manager.config;
638
+ validateDomain(url, config.allowedDomains);
639
+ const response = await fetch(url, options);
640
+ if (response.status !== 402) {
641
+ return response;
642
+ }
643
+ const payment = parsePaymentRequired(response);
644
+ manager.validatePayment(payment.amount);
645
+ const txHash = await sendTransaction(buildPaymentTransaction(payment));
646
+ manager.recordPayment(payment.amount);
647
+ return fetch(url, {
648
+ ...options,
649
+ headers: {
650
+ ...options?.headers,
651
+ [HEADER_PROOF]: txHash
652
+ }
653
+ });
654
+ };
655
+ }
656
+ function parseHumanAmountToAtomic(input) {
657
+ const parts = input.trim().split(/\s+/);
658
+ if (parts.length !== 2) {
659
+ throw new X402Error(`Invalid amount format: "${input}". Expected "0.01 USDC".`);
660
+ }
661
+ const [numStr, unit] = parts;
662
+ const upper = unit.toUpperCase();
663
+ let decimals;
664
+ switch (upper) {
665
+ case "USDC":
666
+ case "USDT":
667
+ decimals = 6;
668
+ break;
669
+ case "DAI":
670
+ case "ETH":
671
+ case "WETH":
672
+ decimals = 18;
673
+ break;
674
+ default:
675
+ throw new X402Error(`Unknown token unit in amount: "${unit}"`);
676
+ }
677
+ const [whole, frac = ""] = numStr.split(".");
678
+ const padded = frac.padEnd(decimals, "0").slice(0, decimals);
679
+ return BigInt(whole + padded);
680
+ }
319
681
 
320
682
  // src/core/account.ts
321
683
  async function createBarzAgent(config) {
@@ -351,7 +713,30 @@ async function createBarzAgent(config) {
351
713
  console.warn("\u26A0\uFE0F Using Base mainnet \u2014 real funds at risk");
352
714
  }
353
715
  const permissionManager = new PermissionManager(config.permissions);
716
+ const x402Manager = new X402Manager();
354
717
  let frozen = false;
718
+ async function executeBatch(txs) {
719
+ for (const tx of txs) {
720
+ permissionManager.validate(tx);
721
+ }
722
+ try {
723
+ const userOpHash = await smartAccountClient.sendUserOperation({
724
+ calls: txs.map((tx) => ({
725
+ to: tx.to,
726
+ value: tx.value ?? 0n,
727
+ data: tx.data ?? "0x"
728
+ }))
729
+ });
730
+ const receipt = await smartAccountClient.waitForUserOperationReceipt({
731
+ hash: userOpHash
732
+ });
733
+ const totalValue = txs.reduce((sum, tx) => sum + (tx.value ?? 0n), 0n);
734
+ if (totalValue > 0n) permissionManager.recordSpend(totalValue);
735
+ return receipt.receipt.transactionHash;
736
+ } catch (error) {
737
+ throw humanizeError(error);
738
+ }
739
+ }
355
740
  const agent = {
356
741
  address: smartAccount.address,
357
742
  chain: config.chain,
@@ -379,26 +764,7 @@ async function createBarzAgent(config) {
379
764
  "BATCH_EMPTY"
380
765
  );
381
766
  }
382
- for (const tx of txs) {
383
- permissionManager.validate(tx);
384
- }
385
- try {
386
- const userOpHash = await smartAccountClient.sendUserOperation({
387
- calls: txs.map((tx) => ({
388
- to: tx.to,
389
- value: tx.value ?? 0n,
390
- data: tx.data ?? "0x"
391
- }))
392
- });
393
- const receipt = await smartAccountClient.waitForUserOperationReceipt({
394
- hash: userOpHash
395
- });
396
- const totalValue = txs.reduce((sum, tx) => sum + (tx.value ?? 0n), 0n);
397
- if (totalValue > 0n) permissionManager.recordSpend(totalValue);
398
- return receipt.receipt.transactionHash;
399
- } catch (error) {
400
- throw humanizeError(error);
401
- }
767
+ return executeBatch(txs);
402
768
  },
403
769
  async getBalance(token) {
404
770
  try {
@@ -432,6 +798,24 @@ async function createBarzAgent(config) {
432
798
  );
433
799
  }
434
800
  },
801
+ async swap(params) {
802
+ if (frozen) throw new FrozenError();
803
+ const tokenAddresses = getSwapTokenAddresses(params, config.chain);
804
+ validateTokenPermissions(tokenAddresses, permissionManager.permissions);
805
+ const txs = buildSwapTransactions(params, config.chain, smartAccount.address);
806
+ return executeBatch(txs);
807
+ },
808
+ async lend(params) {
809
+ if (frozen) throw new FrozenError();
810
+ const tokenAddresses = getLendTokenAddresses(params, config.chain);
811
+ validateTokenPermissions(tokenAddresses, permissionManager.permissions);
812
+ const txs = buildLendTransactions(params, config.chain, smartAccount.address);
813
+ return executeBatch(txs);
814
+ },
815
+ enableX402(x402Config) {
816
+ x402Manager.enable(x402Config);
817
+ },
818
+ fetchWithPayment: null,
435
819
  getExplorerUrl(hash) {
436
820
  return `${chainConfig.explorerUrl}/tx/${hash}`;
437
821
  },
@@ -453,6 +837,10 @@ async function createBarzAgent(config) {
453
837
  return !frozen;
454
838
  }
455
839
  };
840
+ agent.fetchWithPayment = createFetchWithPayment(
841
+ x402Manager,
842
+ (tx) => agent.sendTransaction(tx)
843
+ );
456
844
  return agent;
457
845
  }
458
846
  function validateConfig(config) {
@@ -472,7 +860,18 @@ function validateConfig(config) {
472
860
  throw new ConfigError('Missing "pimlico.apiKey". Get a free key at https://dashboard.pimlico.io');
473
861
  }
474
862
  }
863
+ function validateTokenPermissions(tokenAddresses, permissions) {
864
+ if (!permissions.allowedTokens || permissions.allowedTokens.length === 0) return;
865
+ const allowed = permissions.allowedTokens.map((a) => a.toLowerCase());
866
+ for (const token of tokenAddresses) {
867
+ if (!allowed.includes(token.toLowerCase())) {
868
+ throw new PermissionError(
869
+ `Token ${token} is not in the allowed list. Allowed: ${permissions.allowedTokens.join(", ")}`
870
+ );
871
+ }
872
+ }
873
+ }
475
874
 
476
- export { BarzKitError, BundlerError, CHAIN_CONFIGS, ConfigError, ERC20_ABI, FrozenError, PermissionError, TOKENS, TransactionError, createBarzAgent, getChainConfig };
875
+ export { AAVE_V3_POOL, BarzKitError, BundlerError, CHAIN_CONFIGS, ConfigError, ERC20_ABI, ETH_SENTINEL, FrozenError, PermissionError, TOKENS, TransactionError, UNISWAP_V3_ROUTER, X402Error, X402Manager, buildLendTransactions, buildPaymentTransaction, buildSwapTransactions, createBarzAgent, createFetchWithPayment, getChainConfig, getLendTokenAddresses, getSwapTokenAddresses, getTokenDecimals, isNativeETH, parsePaymentRequired, resolveToken, validateDomain };
477
876
  //# sourceMappingURL=index.js.map
478
877
  //# sourceMappingURL=index.js.map