@zyfai/sdk 0.1.26 → 0.1.27

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/README.md CHANGED
@@ -28,8 +28,6 @@ pnpm add @zyfai/sdk viem
28
28
  ## Prerequisites
29
29
 
30
30
  1. **API Key**: Single API key for both Execution API (Safe deployment, transactions, session keys) and Data API (earnings, opportunities, analytics)
31
- 2. **Bundler API Key**: Required for Safe deployment. Get it from:
32
- - [Pimlico](https://www.pimlico.io/) (Recommended)
33
31
 
34
32
  **Get your API key from [ZyFAI Dashboard](https://sdk.zyf.ai)**
35
33
 
@@ -44,8 +42,7 @@ import { ZyfaiSDK } from "@zyfai/sdk";
44
42
 
45
43
  // Option 1: Full configuration object
46
44
  const sdk = new ZyfaiSDK({
47
- apiKey: "your-api-key", // API key for both Execution API and Data API
48
- bundlerApiKey: "your-bundler-api-key", // Required for Safe deployment
45
+ apiKey: "your-api-key",
49
46
  environment: "production", // or 'staging' (default: 'production')
50
47
  });
51
48
 
@@ -55,11 +52,10 @@ const sdk = new ZyfaiSDK("your-api-key");
55
52
 
56
53
  **Configuration Options:**
57
54
 
58
- | Option | Required | Description |
59
- | --------------- | -------- | ---------------------------------------------------------------------------------------------------- |
60
- | `apiKey` | Yes | API key for both Execution API and Data API (Safe deployment, transactions, session keys, analytics) |
61
- | `bundlerApiKey` | No\* | Pimlico API key for Safe deployment (\*required for `deploySafe`) |
62
- | `environment` | No | `"production"` or `"staging"` (default: `"production"`) |
55
+ | Option | Required | Description |
56
+ | ------------- | -------- | ---------------------------------------------------------------------------------------------------- |
57
+ | `apiKey` | Yes | API key for both Execution API and Data API (Safe deployment, transactions, session keys, analytics) |
58
+ | `environment` | No | `"production"` or `"staging"` (default: `"production"`) |
63
59
 
64
60
  ### Connect Account
65
61
 
@@ -124,16 +120,11 @@ const result = await sdk.deploySafe(userAddress, 8453);
124
120
  if (result.success) {
125
121
  console.log("Safe Address:", result.safeAddress);
126
122
  console.log("Status:", result.status); // 'deployed' | 'failed'
127
-
128
- if (result.alreadyDeployed) {
129
- console.log("Safe was already deployed - no action needed");
130
- } else {
131
- console.log("Transaction Hash:", result.txHash);
132
- }
123
+ console.log("Transaction Hash:", result.txHash);
133
124
  }
134
125
  ```
135
126
 
136
- **Note:** The SDK proactively checks if the Safe is already deployed before attempting deployment. If it exists, it returns `alreadyDeployed: true` without making any transactions.
127
+ **Note:** The SDK proactively checks if the Safe is already deployed before attempting deployment. If it exists, it returns early without making any transactions.
137
128
 
138
129
  ### 2. Multi-Chain Support
139
130
 
@@ -178,8 +169,7 @@ new ZyfaiSDK(config: SDKConfig | string)
178
169
  - If an object is provided:
179
170
  - `apiKey` (string): Your ZyFAI API key (required)
180
171
  - `environment` ('production' | 'staging', optional): API environment (default: 'production')
181
- - `bundlerApiKey` (string, optional): Bundler API key for Safe deployment (required for deploySafe)
182
- - `rpcUrls` (object, optional): Custom RPC URLs per chain to avoid rate limiting
172
+ - `rpcUrls` (object, optional): Custom RPC URLs per chain to avoid rate limiting (optional, only needed for local operations like `getSmartWalletAddress`)
183
173
  - `8453` (string, optional): Base Mainnet RPC URL
184
174
  - `42161` (string, optional): Arbitrum One RPC URL
185
175
  - `9745` (string, optional): Plasma Mainnet RPC URL
@@ -193,14 +183,12 @@ const sdk = new ZyfaiSDK("your-api-key");
193
183
  // Option 2: Object initialization (full configuration)
194
184
  const sdk = new ZyfaiSDK({
195
185
  apiKey: "your-api-key",
196
- bundlerApiKey: "your-bundler-api-key",
197
186
  environment: "production",
198
187
  });
199
188
 
200
189
  // Option 3: With custom RPC URLs (recommended to avoid rate limiting)
201
190
  const sdk = new ZyfaiSDK({
202
191
  apiKey: "your-api-key",
203
- bundlerApiKey: "your-bundler-api-key",
204
192
  environment: "production",
205
193
  rpcUrls: {
206
194
  8453: "https://base-mainnet.g.alchemy.com/v2/YOUR_API_KEY", // Base
@@ -283,7 +271,7 @@ Get the Smart Wallet (Safe) address for a user.
283
271
 
284
272
  ##### `deploySafe(userAddress: string, chainId: SupportedChainId): Promise<DeploySafeResponse>`
285
273
 
286
- Deploy a Safe smart wallet for a user.
274
+ Deploy a Safe smart wallet for a user. **Deployment is handled by the backend API**, which manages all RPC calls and bundler interactions. This avoids rate limiting issues.
287
275
 
288
276
  **Parameters:**
289
277
 
@@ -298,10 +286,14 @@ Deploy a Safe smart wallet for a user.
298
286
  safeAddress: Address;
299
287
  txHash: string;
300
288
  status: "deployed" | "failed";
301
- alreadyDeployed?: boolean; // True if the Safe was already deployed (no new deployment needed)
302
289
  }
303
290
  ```
304
291
 
292
+ **Note:**
293
+
294
+ - User must be authenticated (automatically done via `connectAccount()`)
295
+ - Backend handles all RPC calls, avoiding rate limiting
296
+
305
297
  ##### `addWalletToSdk(walletAddress: string): Promise<AddWalletToSdkResponse>`
306
298
 
307
299
  Add a wallet address to the SDK API key's allowedWallets list. This endpoint requires SDK API key authentication (API key starting with "zyfai\_").
@@ -710,7 +702,6 @@ import { ZyfaiSDK } from "@zyfai/sdk";
710
702
  async function main() {
711
703
  const sdk = new ZyfaiSDK({
712
704
  apiKey: process.env.ZYFAI_API_KEY!,
713
- bundlerApiKey: process.env.BUNDLER_API_KEY!,
714
705
  });
715
706
 
716
707
  // Connect account (automatically authenticates via SIWE)
@@ -865,9 +856,6 @@ For running the examples, set up the following environment variables:
865
856
  # Required: API key (used for both Execution API and Data API)
866
857
  ZYFAI_API_KEY=your-api-key
867
858
 
868
- # Required for Safe deployment: Bundler API key (e.g., Pimlico)
869
- BUNDLER_API_KEY=your-pimlico-api-key
870
-
871
859
  # Required for examples: Private key for signing transactions
872
860
  # WARNING: Never commit your private key to version control!
873
861
  PRIVATE_KEY=0x...
package/dist/index.d.mts CHANGED
@@ -14,7 +14,6 @@ interface RpcUrlsConfig {
14
14
  interface SDKConfig {
15
15
  apiKey: string;
16
16
  environment?: Environment;
17
- bundlerApiKey?: string;
18
17
  rpcUrls?: RpcUrlsConfig;
19
18
  }
20
19
  interface DeploySafeResponse {
@@ -22,8 +21,6 @@ interface DeploySafeResponse {
22
21
  safeAddress: Address;
23
22
  txHash: string;
24
23
  status: "deployed" | "failed";
25
- /** True if the Safe was already deployed (no new deployment needed) */
26
- alreadyDeployed?: boolean;
27
24
  }
28
25
  /** @internal */
29
26
  interface UpdateUserProfileRequest {
@@ -414,7 +411,6 @@ declare class ZyfaiSDK {
414
411
  private httpClient;
415
412
  private signer;
416
413
  private walletClient;
417
- private bundlerApiKey?;
418
414
  private authenticatedUserId;
419
415
  private hasActiveSessionKey;
420
416
  private environment;
package/dist/index.d.ts CHANGED
@@ -14,7 +14,6 @@ interface RpcUrlsConfig {
14
14
  interface SDKConfig {
15
15
  apiKey: string;
16
16
  environment?: Environment;
17
- bundlerApiKey?: string;
18
17
  rpcUrls?: RpcUrlsConfig;
19
18
  }
20
19
  interface DeploySafeResponse {
@@ -22,8 +21,6 @@ interface DeploySafeResponse {
22
21
  safeAddress: Address;
23
22
  txHash: string;
24
23
  status: "deployed" | "failed";
25
- /** True if the Safe was already deployed (no new deployment needed) */
26
- alreadyDeployed?: boolean;
27
24
  }
28
25
  /** @internal */
29
26
  interface UpdateUserProfileRequest {
@@ -414,7 +411,6 @@ declare class ZyfaiSDK {
414
411
  private httpClient;
415
412
  private signer;
416
413
  private walletClient;
417
- private bundlerApiKey?;
418
414
  private authenticatedUserId;
419
415
  private hasActiveSessionKey;
420
416
  private environment;
package/dist/index.js CHANGED
@@ -61,6 +61,8 @@ var ENDPOINTS = {
61
61
  USER_ME: "/users/me",
62
62
  USER_WITHDRAW: "/users/withdraw",
63
63
  PARTIAL_WITHDRAW: "/users/partial-withdraw",
64
+ // Safe Deployment (single endpoint)
65
+ SAFE_DEPLOY: "/users/safe-deploy",
64
66
  // Session Keys
65
67
  SESSION_KEYS_CONFIG: "/session-keys/config",
66
68
  SESSION_KEYS_ADD: "/session-keys/add",
@@ -433,120 +435,18 @@ var isSupportedChain = (chainId) => {
433
435
  var getSupportedChainIds = () => {
434
436
  return Object.keys(CHAINS).map(Number);
435
437
  };
436
- var getBundlerUrl = (chainId, bundlerApiKey, bundlerProvider = "pimlico") => {
437
- if (!bundlerApiKey) {
438
- throw new Error("Bundler API key is required for Safe deployment");
439
- }
440
- switch (bundlerProvider) {
441
- case "pimlico":
442
- return `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${bundlerApiKey}`;
443
- case "custom":
444
- return bundlerApiKey;
445
- default:
446
- throw new Error(`Unsupported bundler provider: ${bundlerProvider}`);
447
- }
448
- };
449
438
 
450
439
  // src/utils/safe-account.ts
451
440
  var import_module_sdk = require("@rhinestone/module-sdk");
452
- var import_permissionless = require("permissionless");
453
- var import_erc7579 = require("permissionless/actions/erc7579");
454
- var import_pimlico = require("permissionless/clients/pimlico");
455
441
  var import_accounts = require("permissionless/accounts");
456
442
  var import_viem3 = require("viem");
457
443
  var import_account_abstraction = require("viem/account-abstraction");
458
- var import_actions = require("permissionless/actions");
459
- var import_account_abstraction2 = require("viem/account-abstraction");
460
444
  var SAFE_7579_ADDRESS = "0x7579EE8307284F293B1927136486880611F20002";
461
445
  var ERC7579_LAUNCHPAD_ADDRESS = "0x7579011aB74c46090561ea277Ba79D510c6C00ff";
462
446
  var ACCOUNT_SALTS = {
463
447
  staging: "zyfai-staging",
464
448
  production: "zyfai"
465
449
  };
466
- var MODULE_TYPE_IDS = {
467
- validator: 1n,
468
- executor: 2n,
469
- fallback: 3n,
470
- hook: 4n
471
- };
472
- var SMART_SESSIONS_FALLBACK = {
473
- module: "0x12cae64c42f362e7d5a847c2d33388373f629177",
474
- address: "0x12cae64c42f362e7d5a847c2d33388373f629177",
475
- type: "fallback",
476
- selector: (0, import_viem3.encodeAbiParameters)(
477
- [{ name: "functionSignature", type: "bytes4" }],
478
- ["0x84b0196e"]
479
- ),
480
- initData: "0x84b0196e00000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"
481
- };
482
- var INTENT_EXECUTOR = {
483
- address: "0x00000000005aD9ce1f5035FD62CA96CEf16AdAAF",
484
- type: "executor",
485
- initData: "0x"
486
- };
487
- var PROXY_EXECUTOR = {
488
- address: "0xF659d30D4EB88B06A909F20839D8959Bd77d8790",
489
- type: "executor",
490
- initData: "0x"
491
- };
492
- var getModulesToInstall = (chainId) => {
493
- const smartSessions = (0, import_module_sdk.getSmartSessionsValidator)({});
494
- const accountLockerHook = (0, import_module_sdk.getAccountLockerHook)({ isOmniMode: true });
495
- const accountLockerSourceExecutor = (0, import_module_sdk.getAccountLockerSourceExecutor)();
496
- const accountLockerTargetExecutor = (0, import_module_sdk.getAccountLockerTargetExecutor)();
497
- if (chainId === 9745) {
498
- return [
499
- smartSessions,
500
- SMART_SESSIONS_FALLBACK,
501
- INTENT_EXECUTOR,
502
- PROXY_EXECUTOR
503
- ];
504
- }
505
- return [
506
- smartSessions,
507
- {
508
- ...accountLockerHook,
509
- type: "executor"
510
- },
511
- accountLockerSourceExecutor,
512
- accountLockerTargetExecutor,
513
- SMART_SESSIONS_FALLBACK,
514
- INTENT_EXECUTOR,
515
- PROXY_EXECUTOR
516
- ];
517
- };
518
- var getInstallModuleCallData = (module2) => {
519
- return (0, import_viem3.encodeFunctionData)({
520
- abi: [
521
- {
522
- type: "function",
523
- name: "installModule",
524
- inputs: [
525
- {
526
- type: "uint256",
527
- name: "moduleTypeId"
528
- },
529
- {
530
- type: "address",
531
- name: "module"
532
- },
533
- {
534
- type: "bytes",
535
- name: "initData"
536
- }
537
- ],
538
- outputs: [],
539
- stateMutability: "nonpayable"
540
- }
541
- ],
542
- functionName: "installModule",
543
- args: [
544
- MODULE_TYPE_IDS[module2.type],
545
- module2.address,
546
- module2.initData || "0x"
547
- ]
548
- });
549
- };
550
450
  var getSafeAccount = async (config) => {
551
451
  const {
552
452
  owner,
@@ -579,10 +479,13 @@ var getSafeAccount = async (config) => {
579
479
  threshold: 1
580
480
  });
581
481
  const saltHex = (0, import_viem3.fromHex)((0, import_viem3.toHex)(effectiveSalt), "bigint");
482
+ const tempOwner = {
483
+ address: formattedEffectiveAddress,
484
+ type: "json-rpc"
485
+ };
582
486
  const safeAccount = await (0, import_accounts.toSafeSmartAccount)({
583
487
  client: publicClient,
584
- owners: [owner.account],
585
- // Pass the owner object with address and signMessage capability
488
+ owners: [tempOwner],
586
489
  version: "1.4.1",
587
490
  entryPoint: {
588
491
  address: import_account_abstraction.entryPoint07Address,
@@ -652,100 +555,41 @@ var getAccountType = async (address, publicClient) => {
652
555
  return "Unknown";
653
556
  }
654
557
  };
655
- var getSmartAccountClient = async (config) => {
656
- const { chain, bundlerUrl } = config;
657
- const safeAccount = await getSafeAccount(config);
658
- const bundlerClient = (0, import_pimlico.createPimlicoClient)({
659
- transport: (0, import_viem3.http)(bundlerUrl),
660
- entryPoint: {
661
- address: import_account_abstraction.entryPoint07Address,
662
- version: "0.7"
663
- }
664
- });
665
- const smartAccountClient = (0, import_permissionless.createSmartAccountClient)({
666
- account: safeAccount,
667
- chain,
668
- bundlerTransport: (0, import_viem3.http)(bundlerUrl),
669
- paymaster: bundlerClient,
670
- userOperation: {
671
- estimateFeesPerGas: async () => {
672
- return (await bundlerClient.getUserOperationGasPrice()).fast;
673
- }
674
- }
675
- }).extend((0, import_erc7579.erc7579Actions)());
676
- return smartAccountClient;
677
- };
678
558
  var deploySafeAccount = async (config) => {
679
559
  try {
680
- const { owner, publicClient, chainId } = config;
560
+ const { owner, httpClient, chainId } = config;
681
561
  if (!owner || !owner.account) {
682
562
  throw new Error(
683
563
  "Wallet not connected. Please connect your wallet first."
684
564
  );
685
565
  }
686
- const safeAddress = await getDeterministicSafeAddress(config);
687
- const isDeployed = await isSafeDeployed(safeAddress, publicClient);
688
- if (isDeployed) {
689
- return {
690
- safeAddress,
691
- isDeployed: true
692
- };
693
- }
694
- const modulesToInstall = getModulesToInstall(chainId);
695
- const installCalls = modulesToInstall.map((module2) => ({
696
- to: safeAddress,
697
- data: getInstallModuleCallData(module2)
698
- }));
699
- const smartAccountClient = await getSmartAccountClient(config);
700
- const safeAccount = await getSafeAccount(config);
701
- const ownableValidator = (0, import_module_sdk.getOwnableValidator)({
702
- owners: [owner.account.address],
703
- threshold: 1
704
- });
705
- const nonce = await (0, import_actions.getAccountNonce)(publicClient, {
706
- address: safeAddress,
707
- entryPointAddress: import_account_abstraction.entryPoint07Address,
708
- key: BigInt(
709
- (0, import_viem3.pad)(ownableValidator.address, {
710
- dir: "right",
711
- size: 24
712
- }) || 0
713
- )
714
- });
715
- const userOperation = await smartAccountClient.prepareUserOperation({
716
- account: safeAccount,
717
- calls: installCalls,
718
- nonce
719
- });
720
- const userOpHashToSign = (0, import_account_abstraction2.getUserOperationHash)({
721
- chainId,
722
- entryPointAddress: import_account_abstraction.entryPoint07Address,
723
- entryPointVersion: "0.7",
724
- userOperation
725
- });
726
- if (!owner.account) {
727
- throw new Error("Owner account is required for signing");
566
+ const prepareResponse = await httpClient.post(
567
+ `${ENDPOINTS.SAFE_DEPLOY}?chainId=${chainId}`
568
+ );
569
+ if (!prepareResponse.userOpHashToSign) {
570
+ throw new Error(
571
+ "Backend did not return userOpHashToSign. Response: " + JSON.stringify(prepareResponse)
572
+ );
728
573
  }
729
- userOperation.signature = await owner.signMessage({
574
+ const userOpHashToSign = prepareResponse.userOpHashToSign;
575
+ const userOpSignature = await owner.signMessage({
730
576
  account: owner.account,
731
577
  message: { raw: userOpHashToSign }
732
578
  });
733
- const userOpHash = await smartAccountClient.sendUserOperation(
734
- userOperation
579
+ const deployResponse = await httpClient.post(
580
+ `${ENDPOINTS.SAFE_DEPLOY}?chainId=${chainId}`,
581
+ { userOpSignature }
735
582
  );
736
- try {
737
- const transaction = await smartAccountClient.waitForUserOperationReceipt({
738
- hash: userOpHash
739
- });
740
- return {
741
- safeAddress,
742
- txHash: transaction.receipt.transactionHash,
743
- isDeployed: true
744
- };
745
- } catch (error) {
746
- console.error("Transaction failed:", error);
747
- throw new Error("Failed to execute transaction");
583
+ if (!deployResponse.success) {
584
+ throw new Error(
585
+ `Safe deployment failed: ${JSON.stringify(deployResponse)}`
586
+ );
748
587
  }
588
+ return {
589
+ safeAddress: deployResponse.safeAddress || "0x",
590
+ txHash: deployResponse.txHash,
591
+ isDeployed: true
592
+ };
749
593
  } catch (error) {
750
594
  throw new Error(
751
595
  `Failed to deploy Safe account: ${error.message}`
@@ -799,24 +643,20 @@ var signSessionKey = async (config, sessions, allPublicClients, signingParams) =
799
643
  // src/core/ZyfaiSDK.ts
800
644
  var import_siwe = require("siwe");
801
645
  var ZyfaiSDK = class {
802
- // Optional custom RPC URLs per chain
803
646
  constructor(config) {
804
647
  this.signer = null;
805
648
  this.walletClient = null;
806
649
  this.authenticatedUserId = null;
807
- // If non-null, user is authenticated
808
650
  this.hasActiveSessionKey = false;
809
651
  this.currentProvider = null;
810
- // Store reference to current provider for event handling
811
652
  this.currentChainId = null;
812
653
  const sdkConfig = typeof config === "string" ? { apiKey: config } : config;
813
- const { apiKey, environment, bundlerApiKey, rpcUrls } = sdkConfig;
654
+ const { apiKey, environment, rpcUrls } = sdkConfig;
814
655
  if (!apiKey) {
815
656
  throw new Error("API key is required");
816
657
  }
817
658
  this.environment = environment || "production";
818
659
  this.httpClient = new HttpClient(apiKey, this.environment);
819
- this.bundlerApiKey = bundlerApiKey;
820
660
  this.rpcUrls = rpcUrls;
821
661
  }
822
662
  /**
@@ -1193,11 +1033,7 @@ var ZyfaiSDK = class {
1193
1033
  if (!isSupportedChain(chainId)) {
1194
1034
  throw new Error(`Unsupported chain ID: ${chainId}`);
1195
1035
  }
1196
- if (!this.bundlerApiKey) {
1197
- throw new Error(
1198
- "Bundler API key is required for Safe deployment. Please provide bundlerApiKey in SDK configuration."
1199
- );
1200
- }
1036
+ await this.authenticateUser();
1201
1037
  const walletClient = this.getWalletClient(chainId);
1202
1038
  const chainConfig = getChainConfig(chainId, this.rpcUrls);
1203
1039
  const safeAddress = await getDeterministicSafeAddress({
@@ -1211,45 +1047,34 @@ var ZyfaiSDK = class {
1211
1047
  safeAddress,
1212
1048
  chainConfig.publicClient
1213
1049
  );
1050
+ if (!alreadyDeployed) {
1051
+ const accountType = await getAccountType(
1052
+ userAddress,
1053
+ chainConfig.publicClient
1054
+ );
1055
+ if (accountType !== "EOA") {
1056
+ throw new Error(
1057
+ `Address ${userAddress} is not an EOA. Only EOA addresses can deploy Safe smart wallets.`
1058
+ );
1059
+ }
1060
+ }
1214
1061
  if (alreadyDeployed) {
1215
1062
  return {
1216
1063
  success: true,
1217
1064
  safeAddress,
1218
1065
  txHash: "0x0",
1219
- status: "deployed",
1220
- alreadyDeployed: true
1066
+ status: "deployed"
1221
1067
  };
1222
1068
  }
1223
- const accountType = await getAccountType(
1224
- userAddress,
1225
- chainConfig.publicClient
1226
- );
1227
- if (accountType !== "EOA") {
1228
- throw new Error(
1229
- `Address ${userAddress} is not an EOA. Only EOA addresses can deploy Safe smart wallets.`
1230
- );
1231
- }
1232
- const bundlerUrl = getBundlerUrl(chainId, this.bundlerApiKey);
1233
1069
  const deploymentResult = await deploySafeAccount({
1234
1070
  owner: walletClient,
1235
1071
  safeOwnerAddress: userAddress,
1236
1072
  chain: chainConfig.chain,
1237
1073
  publicClient: chainConfig.publicClient,
1238
- bundlerUrl,
1239
1074
  environment: this.environment,
1240
- chainId
1075
+ chainId,
1076
+ httpClient: this.httpClient
1241
1077
  });
1242
- try {
1243
- await this.updateUserProfile({
1244
- smartWallet: deploymentResult.safeAddress,
1245
- chains: [chainId]
1246
- });
1247
- } catch (updateError) {
1248
- console.warn(
1249
- "Failed to update user profile after Safe deployment:",
1250
- updateError.message
1251
- );
1252
- }
1253
1078
  try {
1254
1079
  await this.initializeUser(deploymentResult.safeAddress, chainId);
1255
1080
  } catch (initError) {
package/dist/index.mjs CHANGED
@@ -20,6 +20,8 @@ var ENDPOINTS = {
20
20
  USER_ME: "/users/me",
21
21
  USER_WITHDRAW: "/users/withdraw",
22
22
  PARTIAL_WITHDRAW: "/users/partial-withdraw",
23
+ // Safe Deployment (single endpoint)
24
+ SAFE_DEPLOY: "/users/safe-deploy",
23
25
  // Session Keys
24
26
  SESSION_KEYS_CONFIG: "/session-keys/config",
25
27
  SESSION_KEYS_ADD: "/session-keys/add",
@@ -320,7 +322,7 @@ import { privateKeyToAccount } from "viem/accounts";
320
322
  import {
321
323
  createWalletClient,
322
324
  custom,
323
- http as http3,
325
+ http as http2,
324
326
  getAddress as getAddress2
325
327
  } from "viem";
326
328
 
@@ -397,19 +399,6 @@ var isSupportedChain = (chainId) => {
397
399
  var getSupportedChainIds = () => {
398
400
  return Object.keys(CHAINS).map(Number);
399
401
  };
400
- var getBundlerUrl = (chainId, bundlerApiKey, bundlerProvider = "pimlico") => {
401
- if (!bundlerApiKey) {
402
- throw new Error("Bundler API key is required for Safe deployment");
403
- }
404
- switch (bundlerProvider) {
405
- case "pimlico":
406
- return `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${bundlerApiKey}`;
407
- case "custom":
408
- return bundlerApiKey;
409
- default:
410
- throw new Error(`Unsupported bundler provider: ${bundlerProvider}`);
411
- }
412
- };
413
402
 
414
403
  // src/utils/safe-account.ts
415
404
  import {
@@ -418,120 +407,23 @@ import {
418
407
  getAccount,
419
408
  getEnableSessionDetails,
420
409
  getPermissionId,
421
- getSessionNonce,
422
- getSmartSessionsValidator,
423
- getAccountLockerHook,
424
- getAccountLockerSourceExecutor,
425
- getAccountLockerTargetExecutor
410
+ getSessionNonce
426
411
  } from "@rhinestone/module-sdk";
427
- import { createSmartAccountClient } from "permissionless";
428
- import { erc7579Actions } from "permissionless/actions/erc7579";
429
- import { createPimlicoClient } from "permissionless/clients/pimlico";
430
412
  import { toSafeSmartAccount } from "permissionless/accounts";
431
413
  import {
432
- http as http2,
433
414
  getAddress,
434
- encodeFunctionData,
435
415
  fromHex,
436
- toHex,
437
- encodeAbiParameters,
438
- pad
416
+ toHex
439
417
  } from "viem";
440
418
  import {
441
419
  entryPoint07Address
442
420
  } from "viem/account-abstraction";
443
- import { getAccountNonce } from "permissionless/actions";
444
- import { getUserOperationHash } from "viem/account-abstraction";
445
421
  var SAFE_7579_ADDRESS = "0x7579EE8307284F293B1927136486880611F20002";
446
422
  var ERC7579_LAUNCHPAD_ADDRESS = "0x7579011aB74c46090561ea277Ba79D510c6C00ff";
447
423
  var ACCOUNT_SALTS = {
448
424
  staging: "zyfai-staging",
449
425
  production: "zyfai"
450
426
  };
451
- var MODULE_TYPE_IDS = {
452
- validator: 1n,
453
- executor: 2n,
454
- fallback: 3n,
455
- hook: 4n
456
- };
457
- var SMART_SESSIONS_FALLBACK = {
458
- module: "0x12cae64c42f362e7d5a847c2d33388373f629177",
459
- address: "0x12cae64c42f362e7d5a847c2d33388373f629177",
460
- type: "fallback",
461
- selector: encodeAbiParameters(
462
- [{ name: "functionSignature", type: "bytes4" }],
463
- ["0x84b0196e"]
464
- ),
465
- initData: "0x84b0196e00000000000000000000000000000000000000000000000000000000fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"
466
- };
467
- var INTENT_EXECUTOR = {
468
- address: "0x00000000005aD9ce1f5035FD62CA96CEf16AdAAF",
469
- type: "executor",
470
- initData: "0x"
471
- };
472
- var PROXY_EXECUTOR = {
473
- address: "0xF659d30D4EB88B06A909F20839D8959Bd77d8790",
474
- type: "executor",
475
- initData: "0x"
476
- };
477
- var getModulesToInstall = (chainId) => {
478
- const smartSessions = getSmartSessionsValidator({});
479
- const accountLockerHook = getAccountLockerHook({ isOmniMode: true });
480
- const accountLockerSourceExecutor = getAccountLockerSourceExecutor();
481
- const accountLockerTargetExecutor = getAccountLockerTargetExecutor();
482
- if (chainId === 9745) {
483
- return [
484
- smartSessions,
485
- SMART_SESSIONS_FALLBACK,
486
- INTENT_EXECUTOR,
487
- PROXY_EXECUTOR
488
- ];
489
- }
490
- return [
491
- smartSessions,
492
- {
493
- ...accountLockerHook,
494
- type: "executor"
495
- },
496
- accountLockerSourceExecutor,
497
- accountLockerTargetExecutor,
498
- SMART_SESSIONS_FALLBACK,
499
- INTENT_EXECUTOR,
500
- PROXY_EXECUTOR
501
- ];
502
- };
503
- var getInstallModuleCallData = (module) => {
504
- return encodeFunctionData({
505
- abi: [
506
- {
507
- type: "function",
508
- name: "installModule",
509
- inputs: [
510
- {
511
- type: "uint256",
512
- name: "moduleTypeId"
513
- },
514
- {
515
- type: "address",
516
- name: "module"
517
- },
518
- {
519
- type: "bytes",
520
- name: "initData"
521
- }
522
- ],
523
- outputs: [],
524
- stateMutability: "nonpayable"
525
- }
526
- ],
527
- functionName: "installModule",
528
- args: [
529
- MODULE_TYPE_IDS[module.type],
530
- module.address,
531
- module.initData || "0x"
532
- ]
533
- });
534
- };
535
427
  var getSafeAccount = async (config) => {
536
428
  const {
537
429
  owner,
@@ -564,10 +456,13 @@ var getSafeAccount = async (config) => {
564
456
  threshold: 1
565
457
  });
566
458
  const saltHex = fromHex(toHex(effectiveSalt), "bigint");
459
+ const tempOwner = {
460
+ address: formattedEffectiveAddress,
461
+ type: "json-rpc"
462
+ };
567
463
  const safeAccount = await toSafeSmartAccount({
568
464
  client: publicClient,
569
- owners: [owner.account],
570
- // Pass the owner object with address and signMessage capability
465
+ owners: [tempOwner],
571
466
  version: "1.4.1",
572
467
  entryPoint: {
573
468
  address: entryPoint07Address,
@@ -637,100 +532,41 @@ var getAccountType = async (address, publicClient) => {
637
532
  return "Unknown";
638
533
  }
639
534
  };
640
- var getSmartAccountClient = async (config) => {
641
- const { chain, bundlerUrl } = config;
642
- const safeAccount = await getSafeAccount(config);
643
- const bundlerClient = createPimlicoClient({
644
- transport: http2(bundlerUrl),
645
- entryPoint: {
646
- address: entryPoint07Address,
647
- version: "0.7"
648
- }
649
- });
650
- const smartAccountClient = createSmartAccountClient({
651
- account: safeAccount,
652
- chain,
653
- bundlerTransport: http2(bundlerUrl),
654
- paymaster: bundlerClient,
655
- userOperation: {
656
- estimateFeesPerGas: async () => {
657
- return (await bundlerClient.getUserOperationGasPrice()).fast;
658
- }
659
- }
660
- }).extend(erc7579Actions());
661
- return smartAccountClient;
662
- };
663
535
  var deploySafeAccount = async (config) => {
664
536
  try {
665
- const { owner, publicClient, chainId } = config;
537
+ const { owner, httpClient, chainId } = config;
666
538
  if (!owner || !owner.account) {
667
539
  throw new Error(
668
540
  "Wallet not connected. Please connect your wallet first."
669
541
  );
670
542
  }
671
- const safeAddress = await getDeterministicSafeAddress(config);
672
- const isDeployed = await isSafeDeployed(safeAddress, publicClient);
673
- if (isDeployed) {
674
- return {
675
- safeAddress,
676
- isDeployed: true
677
- };
678
- }
679
- const modulesToInstall = getModulesToInstall(chainId);
680
- const installCalls = modulesToInstall.map((module) => ({
681
- to: safeAddress,
682
- data: getInstallModuleCallData(module)
683
- }));
684
- const smartAccountClient = await getSmartAccountClient(config);
685
- const safeAccount = await getSafeAccount(config);
686
- const ownableValidator = getOwnableValidator({
687
- owners: [owner.account.address],
688
- threshold: 1
689
- });
690
- const nonce = await getAccountNonce(publicClient, {
691
- address: safeAddress,
692
- entryPointAddress: entryPoint07Address,
693
- key: BigInt(
694
- pad(ownableValidator.address, {
695
- dir: "right",
696
- size: 24
697
- }) || 0
698
- )
699
- });
700
- const userOperation = await smartAccountClient.prepareUserOperation({
701
- account: safeAccount,
702
- calls: installCalls,
703
- nonce
704
- });
705
- const userOpHashToSign = getUserOperationHash({
706
- chainId,
707
- entryPointAddress: entryPoint07Address,
708
- entryPointVersion: "0.7",
709
- userOperation
710
- });
711
- if (!owner.account) {
712
- throw new Error("Owner account is required for signing");
543
+ const prepareResponse = await httpClient.post(
544
+ `${ENDPOINTS.SAFE_DEPLOY}?chainId=${chainId}`
545
+ );
546
+ if (!prepareResponse.userOpHashToSign) {
547
+ throw new Error(
548
+ "Backend did not return userOpHashToSign. Response: " + JSON.stringify(prepareResponse)
549
+ );
713
550
  }
714
- userOperation.signature = await owner.signMessage({
551
+ const userOpHashToSign = prepareResponse.userOpHashToSign;
552
+ const userOpSignature = await owner.signMessage({
715
553
  account: owner.account,
716
554
  message: { raw: userOpHashToSign }
717
555
  });
718
- const userOpHash = await smartAccountClient.sendUserOperation(
719
- userOperation
556
+ const deployResponse = await httpClient.post(
557
+ `${ENDPOINTS.SAFE_DEPLOY}?chainId=${chainId}`,
558
+ { userOpSignature }
720
559
  );
721
- try {
722
- const transaction = await smartAccountClient.waitForUserOperationReceipt({
723
- hash: userOpHash
724
- });
725
- return {
726
- safeAddress,
727
- txHash: transaction.receipt.transactionHash,
728
- isDeployed: true
729
- };
730
- } catch (error) {
731
- console.error("Transaction failed:", error);
732
- throw new Error("Failed to execute transaction");
560
+ if (!deployResponse.success) {
561
+ throw new Error(
562
+ `Safe deployment failed: ${JSON.stringify(deployResponse)}`
563
+ );
733
564
  }
565
+ return {
566
+ safeAddress: deployResponse.safeAddress || "0x",
567
+ txHash: deployResponse.txHash,
568
+ isDeployed: true
569
+ };
734
570
  } catch (error) {
735
571
  throw new Error(
736
572
  `Failed to deploy Safe account: ${error.message}`
@@ -784,24 +620,20 @@ var signSessionKey = async (config, sessions, allPublicClients, signingParams) =
784
620
  // src/core/ZyfaiSDK.ts
785
621
  import { SiweMessage } from "siwe";
786
622
  var ZyfaiSDK = class {
787
- // Optional custom RPC URLs per chain
788
623
  constructor(config) {
789
624
  this.signer = null;
790
625
  this.walletClient = null;
791
626
  this.authenticatedUserId = null;
792
- // If non-null, user is authenticated
793
627
  this.hasActiveSessionKey = false;
794
628
  this.currentProvider = null;
795
- // Store reference to current provider for event handling
796
629
  this.currentChainId = null;
797
630
  const sdkConfig = typeof config === "string" ? { apiKey: config } : config;
798
- const { apiKey, environment, bundlerApiKey, rpcUrls } = sdkConfig;
631
+ const { apiKey, environment, rpcUrls } = sdkConfig;
799
632
  if (!apiKey) {
800
633
  throw new Error("API key is required");
801
634
  }
802
635
  this.environment = environment || "production";
803
636
  this.httpClient = new HttpClient(apiKey, this.environment);
804
- this.bundlerApiKey = bundlerApiKey;
805
637
  this.rpcUrls = rpcUrls;
806
638
  }
807
639
  /**
@@ -1021,7 +853,7 @@ var ZyfaiSDK = class {
1021
853
  this.walletClient = createWalletClient({
1022
854
  account: this.signer,
1023
855
  chain: chainConfig.chain,
1024
- transport: http3(chainConfig.rpcUrl)
856
+ transport: http2(chainConfig.rpcUrl)
1025
857
  });
1026
858
  connectedAddress = this.signer.address;
1027
859
  this.currentProvider = null;
@@ -1107,7 +939,7 @@ var ZyfaiSDK = class {
1107
939
  return createWalletClient({
1108
940
  account: this.signer,
1109
941
  chain: targetChainConfig.chain,
1110
- transport: http3(targetChainConfig.rpcUrl)
942
+ transport: http2(targetChainConfig.rpcUrl)
1111
943
  });
1112
944
  } else {
1113
945
  if (!this.walletClient) {
@@ -1178,11 +1010,7 @@ var ZyfaiSDK = class {
1178
1010
  if (!isSupportedChain(chainId)) {
1179
1011
  throw new Error(`Unsupported chain ID: ${chainId}`);
1180
1012
  }
1181
- if (!this.bundlerApiKey) {
1182
- throw new Error(
1183
- "Bundler API key is required for Safe deployment. Please provide bundlerApiKey in SDK configuration."
1184
- );
1185
- }
1013
+ await this.authenticateUser();
1186
1014
  const walletClient = this.getWalletClient(chainId);
1187
1015
  const chainConfig = getChainConfig(chainId, this.rpcUrls);
1188
1016
  const safeAddress = await getDeterministicSafeAddress({
@@ -1196,45 +1024,34 @@ var ZyfaiSDK = class {
1196
1024
  safeAddress,
1197
1025
  chainConfig.publicClient
1198
1026
  );
1027
+ if (!alreadyDeployed) {
1028
+ const accountType = await getAccountType(
1029
+ userAddress,
1030
+ chainConfig.publicClient
1031
+ );
1032
+ if (accountType !== "EOA") {
1033
+ throw new Error(
1034
+ `Address ${userAddress} is not an EOA. Only EOA addresses can deploy Safe smart wallets.`
1035
+ );
1036
+ }
1037
+ }
1199
1038
  if (alreadyDeployed) {
1200
1039
  return {
1201
1040
  success: true,
1202
1041
  safeAddress,
1203
1042
  txHash: "0x0",
1204
- status: "deployed",
1205
- alreadyDeployed: true
1043
+ status: "deployed"
1206
1044
  };
1207
1045
  }
1208
- const accountType = await getAccountType(
1209
- userAddress,
1210
- chainConfig.publicClient
1211
- );
1212
- if (accountType !== "EOA") {
1213
- throw new Error(
1214
- `Address ${userAddress} is not an EOA. Only EOA addresses can deploy Safe smart wallets.`
1215
- );
1216
- }
1217
- const bundlerUrl = getBundlerUrl(chainId, this.bundlerApiKey);
1218
1046
  const deploymentResult = await deploySafeAccount({
1219
1047
  owner: walletClient,
1220
1048
  safeOwnerAddress: userAddress,
1221
1049
  chain: chainConfig.chain,
1222
1050
  publicClient: chainConfig.publicClient,
1223
- bundlerUrl,
1224
1051
  environment: this.environment,
1225
- chainId
1052
+ chainId,
1053
+ httpClient: this.httpClient
1226
1054
  });
1227
- try {
1228
- await this.updateUserProfile({
1229
- smartWallet: deploymentResult.safeAddress,
1230
- chains: [chainId]
1231
- });
1232
- } catch (updateError) {
1233
- console.warn(
1234
- "Failed to update user profile after Safe deployment:",
1235
- updateError.message
1236
- );
1237
- }
1238
1055
  try {
1239
1056
  await this.initializeUser(deploymentResult.safeAddress, chainId);
1240
1057
  } catch (initError) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zyfai/sdk",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
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",