@zyfai/sdk 0.2.37 → 0.2.39

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.mjs CHANGED
@@ -4,6 +4,7 @@ import axios from "axios";
4
4
  // src/config/endpoints.ts
5
5
  var API_ENDPOINT = "https://api.zyf.ai";
6
6
  var DATA_API_ENDPOINT = "https://defiapi.zyf.ai";
7
+ var WS_ENDPOINT = "wss://defiapi.zyf.ai/ws/events";
7
8
  var API_VERSION = "/api/v1";
8
9
  var DATA_API_VERSION = "/api/v2";
9
10
  var ENDPOINTS = {
@@ -37,6 +38,21 @@ var ENDPOINTS = {
37
38
  SDK_TVL: "/data/sdk-tvl",
38
39
  // Agent Identity Registry
39
40
  AGENT_TOKEN_URI: "/users/me/agent-token-uri",
41
+ // Simulation
42
+ SIMULATE_BEST_POSITIONS: (params) => {
43
+ const networks = Array.isArray(params.networks) ? params.networks.join(",") : params.networks;
44
+ const query = [
45
+ `amount=${params.amount}`,
46
+ `token=${params.token}`,
47
+ `networks=${networks}`,
48
+ `strategy=${params.strategy}`
49
+ ];
50
+ if (params.minSplit !== void 0) query.push(`minSplit=${params.minSplit}`);
51
+ if (params.protocols?.length) query.push(`protocols=${params.protocols.join(",")}`);
52
+ if (params.pools?.length) query.push(`pools=${params.pools.join(",")}`);
53
+ if (params.userPositions?.length) query.push(`userPositions=${encodeURIComponent(JSON.stringify(params.userPositions))}`);
54
+ return `/simulate/best-positions?${query.join("&")}`;
55
+ },
40
56
  // Customization
41
57
  CUSTOMIZE_BATCH: "/customization/customize-batch",
42
58
  CUSTOMIZATION_POOLS: (protocolId, strategy) => `/customization/pools?protocolId=${protocolId}${strategy ? `&strategy=${strategy}` : ""}`,
@@ -433,6 +449,20 @@ var VAULT_ABI = [
433
449
  ];
434
450
  var VAULT_ADDRESS = "0xD580071c47d4a667858B5FafAb85BC9C609beC5D";
435
451
 
452
+ // src/config/constants.ts
453
+ var MIN_PORTFOLIO_BALANCE = {
454
+ 1: {
455
+ USDC: 10000n * 10n ** 6n,
456
+ // 10,000 USDC (6 decimals)
457
+ WETH: 5n * 10n ** 18n
458
+ // 5 WETH (18 decimals)
459
+ }
460
+ };
461
+ var formatMinPortfolioLabel = (raw, decimals, symbol) => {
462
+ const whole = raw / 10n ** BigInt(decimals);
463
+ return `${whole.toString()} ${symbol}`;
464
+ };
465
+
436
466
  // src/core/ZyfaiSDK.ts
437
467
  import { privateKeyToAccount } from "viem/accounts";
438
468
  import {
@@ -445,35 +475,14 @@ import {
445
475
 
446
476
  // src/config/chains.ts
447
477
  import { createPublicClient, http } from "viem";
448
- import { arbitrum, base } from "viem/chains";
449
- import { defineChain } from "viem";
450
- var plasma = defineChain({
451
- id: 9745,
452
- name: "Plasma",
453
- nativeCurrency: {
454
- decimals: 18,
455
- name: "Plasma",
456
- symbol: "PLSM"
457
- },
458
- rpcUrls: {
459
- default: {
460
- http: ["https://rpc.plasma.to"]
461
- }
462
- },
463
- blockExplorers: {
464
- default: {
465
- name: "Plasma Explorer",
466
- url: "https://explorer.plasma.io"
467
- }
468
- }
469
- });
478
+ import { arbitrum, base, mainnet } from "viem/chains";
470
479
  var DEFAULT_TOKEN_ADDRESSES = {
480
+ 1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
481
+ // USDC on Ethereum Mainnet
471
482
  8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
472
483
  // USDC on Base
473
- 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
484
+ 42161: "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
474
485
  // USDC on Arbitrum
475
- 9745: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb"
476
- // USDT on Plasma
477
486
  };
478
487
  var ASSET_CONFIGS = {
479
488
  USDC: {
@@ -484,25 +493,23 @@ var ASSET_CONFIGS = {
484
493
  decimals: 6,
485
494
  tokenSymbols: ["USDC", "USDC.e", "USDT", "USDT0"],
486
495
  tokenSymbolsByChainId: {
496
+ 1: "USDC",
487
497
  8453: "USDC",
488
498
  42161: "USDC",
489
- 9745: "USDT0",
490
499
  146: "USDC.e",
491
- 1: "USDC"
500
+ 59144: "USDC"
492
501
  },
493
502
  addresses: {
503
+ 1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
504
+ // Ethereum
494
505
  8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
495
506
  // Base
496
507
  42161: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
497
508
  // Arbitrum
498
- 9745: "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb",
499
- // Plasma
500
509
  146: "0x29219dd400f2bf60e5a23d13be72b486d4038894",
501
510
  // Sonic
502
- 59144: "0x176211869ca2b568f2a7d4ee941e073a821ee1ff",
511
+ 59144: "0x176211869ca2b568f2a7d4ee941e073a821ee1ff"
503
512
  // Linea
504
- 1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
505
- // Ethereum
506
513
  },
507
514
  enabled: true
508
515
  },
@@ -514,26 +521,23 @@ var ASSET_CONFIGS = {
514
521
  decimals: 18,
515
522
  tokenSymbols: ["WETH", "ETH"],
516
523
  tokenSymbolsByChainId: {
524
+ 1: "WETH",
517
525
  8453: "WETH",
518
526
  42161: "WETH",
519
- 9745: "WETH",
520
527
  146: "WETH",
521
- 59144: "WETH",
522
- 1: "WETH"
528
+ 59144: "WETH"
523
529
  },
524
530
  addresses: {
531
+ 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
532
+ // Ethereum
525
533
  8453: "0x4200000000000000000000000000000000000006",
526
534
  // Base
527
535
  42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
528
536
  // Arbitrum
529
- 9745: "0x4200000000000000000000000000000000000006",
530
- // Plasma
531
537
  146: "0x039e64f90d4199560e7533692f69448878db85c7",
532
538
  // Sonic
533
- 59144: "0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f",
539
+ 59144: "0xe5d7c2a44ffddf6b295a15c148167daaaf5cf34f"
534
540
  // Linea
535
- 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
536
- // Ethereum
537
541
  },
538
542
  enabled: true
539
543
  }
@@ -548,14 +552,14 @@ var getDefaultTokenAddress = (chainId, asset) => {
548
552
  return address;
549
553
  };
550
554
  var DEFAULT_RPC_URLS = {
555
+ 1: "https://mainnet.infura.io/v3/8e6cdd06e30d40ac9990bf61bed3a3d0",
551
556
  8453: "https://mainnet.base.org",
552
- 42161: "https://arb1.arbitrum.io/rpc",
553
- 9745: "https://rpc.plasma.to"
557
+ 42161: "https://arb1.arbitrum.io/rpc"
554
558
  };
555
559
  var CHAINS = {
560
+ 1: mainnet,
556
561
  8453: base,
557
- 42161: arbitrum,
558
- 9745: plasma
562
+ 42161: arbitrum
559
563
  };
560
564
  var getChainConfig = (chainId, rpcUrls) => {
561
565
  const chain = CHAINS[chainId];
@@ -1776,22 +1780,30 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1776
1780
  * Deposit funds from EOA to Safe smart wallet
1777
1781
  * Transfers tokens from the connected wallet to the user's Safe and logs the deposit
1778
1782
  *
1779
- * Token is automatically selected based on chain:
1780
- * - Base (8453) and Arbitrum (42161): USDC
1781
- * - Plasma (9745): USDT
1783
+ * Token is automatically selected based on chain (USDC by default):
1784
+ * - Ethereum Mainnet (1), Base (8453), Arbitrum (42161)
1785
+ *
1786
+ * Minimum portfolio balance enforced (Safe balance + deposit amount),
1787
+ * configured per-chain in `MIN_PORTFOLIO_BALANCE`:
1788
+ * - Ethereum Mainnet (1): 5 USDC (test threshold)
1789
+ * - Base (8453) & Arbitrum (42161): no minimum
1790
+ *
1791
+ * If no minimum is configured for a given (chainId, asset) pair,
1792
+ * the check is skipped.
1782
1793
  *
1783
1794
  * @param userAddress - User's address (owner of the Safe)
1784
1795
  * @param chainId - Target chain ID
1785
1796
  * @param amount - Amount in least decimal units (e.g., "100000000" for 100 USDC with 6 decimals)
1797
+ * @param asset - Optional asset symbol ("USDC" or "WETH"). Defaults to "USDC".
1786
1798
  * @returns Deposit response with transaction hash
1787
1799
  *
1788
1800
  * @example
1789
1801
  * ```typescript
1790
- * // Deposit 100 USDC (6 decimals) to Safe on Base
1802
+ * // Deposit 10,000 USDC (6 decimals) to Safe on Base
1791
1803
  * const result = await sdk.depositFunds(
1792
1804
  * "0xUser...",
1793
1805
  * 8453,
1794
- * "100000000" // 100 USDC = 100 * 10^6
1806
+ * "10000000000" // 10,000 USDC = 10_000 * 10^6
1795
1807
  * );
1796
1808
  * ```
1797
1809
  */
@@ -1806,7 +1818,15 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1806
1818
  if (!amount || isNaN(Number(amount)) || Number(amount) <= 0) {
1807
1819
  throw new Error("Valid amount is required");
1808
1820
  }
1809
- const token = getDefaultTokenAddress(chainId, asset);
1821
+ const assetSymbol = (asset || "USDC").toUpperCase();
1822
+ const assetConfig = ASSET_CONFIGS[assetSymbol];
1823
+ if (!assetConfig) {
1824
+ throw new Error(
1825
+ `Unsupported asset: ${assetSymbol}. Supported: USDC, WETH.`
1826
+ );
1827
+ }
1828
+ const minRequired = MIN_PORTFOLIO_BALANCE[chainId]?.[assetSymbol];
1829
+ const token = getDefaultTokenAddress(chainId, assetSymbol);
1810
1830
  const walletClient = this.getWalletClient();
1811
1831
  const chainConfig = getChainConfig(chainId, this.rpcUrls);
1812
1832
  const safeAddress = await getDeterministicSafeAddress({
@@ -1824,6 +1844,25 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1824
1844
  );
1825
1845
  }
1826
1846
  const amountBigInt = BigInt(amount);
1847
+ if (minRequired !== void 0) {
1848
+ const currentSafeBalance = await chainConfig.publicClient.readContract({
1849
+ address: token,
1850
+ abi: ERC20_ABI,
1851
+ functionName: "balanceOf",
1852
+ args: [safeAddress]
1853
+ });
1854
+ const totalAfterDeposit = currentSafeBalance + amountBigInt;
1855
+ if (totalAfterDeposit < minRequired) {
1856
+ const minLabel = formatMinPortfolioLabel(
1857
+ minRequired,
1858
+ assetConfig.decimals,
1859
+ assetSymbol
1860
+ );
1861
+ throw new Error(
1862
+ `Minimum portfolio balance not met on chain ${chainId}. Total portfolio must be at least ${minLabel}. Current Safe balance: ${currentSafeBalance.toString()}, deposit amount: ${amountBigInt.toString()}, total after deposit: ${totalAfterDeposit.toString()} (raw units, ${assetConfig.decimals} decimals).`
1863
+ );
1864
+ }
1865
+ }
1827
1866
  const txHash = await walletClient.writeContract({
1828
1867
  address: token,
1829
1868
  abi: ERC20_ABI,
@@ -1871,8 +1910,7 @@ var _ZyfaiSDK = class _ZyfaiSDK {
1871
1910
  * - Need more control over transaction execution
1872
1911
  *
1873
1912
  * Token is automatically selected based on chain if not provided:
1874
- * - Base (8453) and Arbitrum (42161): USDC
1875
- * - Plasma (9745): USDT
1913
+ * - Ethereum Mainnet (1), Base (8453), Arbitrum (42161): USDC by default
1876
1914
  *
1877
1915
  * @param chainId - Chain ID where the deposit was made
1878
1916
  * @param txHash - Transaction hash of the deposit
@@ -3049,6 +3087,54 @@ var _ZyfaiSDK = class _ZyfaiSDK {
3049
3087
  );
3050
3088
  }
3051
3089
  }
3090
+ // ============================================================================
3091
+ // Simulation
3092
+ // ============================================================================
3093
+ /**
3094
+ * Simulate the best yield positions for a given amount, splitting it across
3095
+ * the top-ranked pools (up to minSplit positions) for the requested chains/strategy.
3096
+ *
3097
+ * Returns ready-to-execute calldata (approve + deposit) per position. The deposit
3098
+ * calldata contains a "<RECEIVER>" placeholder that must be replaced with the
3099
+ * actual receiving address (e.g. the user's Safe) before sending the transaction.
3100
+ *
3101
+ * @param params - Simulation parameters
3102
+ * @returns Chain-keyed map of simulated positions with calldata
3103
+ *
3104
+ * @example
3105
+ * ```typescript
3106
+ * const result = await sdk.simulateBestPositions({
3107
+ * amount: 3000,
3108
+ * token: "USDC",
3109
+ * networks: 8453,
3110
+ * strategy: "conservative",
3111
+ * minSplit: 3,
3112
+ * protocols: ["aave", "compound", "morpho"],
3113
+ * });
3114
+ * console.log(result.data["8453"]);
3115
+ * ```
3116
+ */
3117
+ async simulateBestPositions(params) {
3118
+ try {
3119
+ if (!isValidPublicStrategy(params.strategy)) {
3120
+ throw new Error(
3121
+ `Invalid strategy: ${params.strategy}. Must be "conservative" or "aggressive".`
3122
+ );
3123
+ }
3124
+ const internalStrategy = toInternalStrategy(params.strategy);
3125
+ const response = await this.httpClient.get(
3126
+ ENDPOINTS.SIMULATE_BEST_POSITIONS({
3127
+ ...params,
3128
+ strategy: internalStrategy
3129
+ })
3130
+ );
3131
+ return response;
3132
+ } catch (error) {
3133
+ throw new Error(
3134
+ `Failed to simulate best positions: ${error.message}`
3135
+ );
3136
+ }
3137
+ }
3052
3138
  /**
3053
3139
  * Get currently selected pools for a protocol on a specific chain.
3054
3140
  *
@@ -3491,6 +3577,78 @@ var _ZyfaiSDK = class _ZyfaiSDK {
3491
3577
  symbol: tokenSymbol
3492
3578
  };
3493
3579
  }
3580
+ // ============================================================================
3581
+ // WebSocket Event Stream
3582
+ // ============================================================================
3583
+ /**
3584
+ * Subscribe to real-time Zyfai risk and liquidity events via WebSocket.
3585
+ *
3586
+ * Events: depeg, liquidity_trap, liquidity_restored, pool_status_change,
3587
+ * new_collateral_detected, liquidity_drop.
3588
+ *
3589
+ * @param handlers - Callback per event type, plus onError
3590
+ * @param filters - Optional protocol/pool filter sent to the server on subscribe
3591
+ * @returns Cleanup function — call it to close the connection
3592
+ *
3593
+ * @example
3594
+ * ```typescript
3595
+ * const unsubscribe = sdk.subscribeToEvents(
3596
+ * {
3597
+ * onDepeg: (data) => console.log("Depeg detected:", data.token, data.severity),
3598
+ * onLiquidityDrop: (data) => console.log("Liquidity drop:", data.pool, data.dropPercent),
3599
+ * },
3600
+ * { protocols: ["morpho"], pools: ["gauntlet usdc core"] }
3601
+ * );
3602
+ *
3603
+ * // Later, close the connection:
3604
+ * unsubscribe();
3605
+ * ```
3606
+ */
3607
+ subscribeToEvents(handlers, filters) {
3608
+ let ws = null;
3609
+ let closed = false;
3610
+ const connect = () => {
3611
+ ws = new globalThis.WebSocket(WS_ENDPOINT);
3612
+ ws.onopen = () => {
3613
+ const msg = { type: "subscribe" };
3614
+ if (filters && (filters.chains?.length || filters.protocols?.length || filters.pools?.length)) {
3615
+ msg.filters = filters;
3616
+ }
3617
+ ws.send(JSON.stringify(msg));
3618
+ };
3619
+ ws.onmessage = (event) => {
3620
+ try {
3621
+ const msg = JSON.parse(event.data);
3622
+ if (msg.type !== "event") return;
3623
+ switch (msg.eventType) {
3624
+ case "depeg":
3625
+ handlers.onDepeg?.(msg.data);
3626
+ break;
3627
+ case "new_collateral_detected":
3628
+ handlers.onNewCollateralDetected?.(msg.data);
3629
+ break;
3630
+ case "liquidity_drop":
3631
+ handlers.onLiquidityDrop?.(msg.data);
3632
+ break;
3633
+ }
3634
+ } catch {
3635
+ }
3636
+ };
3637
+ ws.onerror = (error) => {
3638
+ handlers.onError?.(error);
3639
+ };
3640
+ ws.onclose = () => {
3641
+ if (!closed) {
3642
+ setTimeout(connect, 3e3);
3643
+ }
3644
+ };
3645
+ };
3646
+ connect();
3647
+ return () => {
3648
+ closed = true;
3649
+ ws?.close();
3650
+ };
3651
+ }
3494
3652
  };
3495
3653
  // ============================================================================
3496
3654
  // Agent Identity Registry
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zyfai/sdk",
3
- "version": "0.2.37",
3
+ "version": "0.2.39",
4
4
  "description": "TypeScript SDK for Zyfai Yield Optimization Engine - Deploy Safe smart wallets, manage session keys, and interact with DeFi protocols",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",