@zyfai/sdk 0.1.2 → 0.1.3
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 +30 -6
- package/dist/index.d.mts +18 -4
- package/dist/index.d.ts +18 -4
- package/dist/index.js +87 -47
- package/dist/index.mjs +90 -51
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -229,25 +229,31 @@ The SDK automatically fetches optimal session configuration from ZyFAI API:
|
|
|
229
229
|
```typescript
|
|
230
230
|
// SDK automatically:
|
|
231
231
|
// 1. Authenticates via SIWE (creates user record if needed)
|
|
232
|
-
// 2.
|
|
233
|
-
// 3.
|
|
232
|
+
// 2. Checks if user already has an active session key (returns early if so)
|
|
233
|
+
// 3. Calculates the deterministic Safe address
|
|
234
234
|
// 4. Retrieves personalized config via /session-keys/config
|
|
235
235
|
// 5. Signs the session key
|
|
236
236
|
// 6. Calls /session-keys/add so the session becomes active immediately
|
|
237
237
|
|
|
238
238
|
const result = await sdk.createSessionKey(userAddress, 42161);
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
// Check if session key already existed
|
|
241
|
+
if (result.alreadyActive) {
|
|
242
|
+
console.log("Session key already active:", result.message);
|
|
243
|
+
} else {
|
|
244
|
+
console.log("Session created:", result.signature);
|
|
245
|
+
console.log("Safe address:", result.sessionKeyAddress);
|
|
246
|
+
console.log("Activation ID:", result.sessionActivation?.id);
|
|
247
|
+
}
|
|
242
248
|
console.log("User ID:", result.userId);
|
|
243
|
-
console.log("Activation ID:", result.sessionActivation?.id);
|
|
244
249
|
```
|
|
245
250
|
|
|
246
251
|
**Important**:
|
|
247
252
|
|
|
248
253
|
- `createSessionKey` requires SIWE authentication (prompts wallet signature on first call)
|
|
254
|
+
- The SDK proactively checks if the user already has an active session key (from login response) and returns early without requiring any signature if one exists
|
|
249
255
|
- The user record must have `smartWallet` and `chainId` set (automatically handled after calling `deploySafe` or `updateUserProfile`)
|
|
250
|
-
-
|
|
256
|
+
- When `alreadyActive` is `true`, `sessionKeyAddress` and `signature` are not available in the response
|
|
251
257
|
|
|
252
258
|
### 4. Deposit Funds
|
|
253
259
|
|
|
@@ -713,6 +719,24 @@ Make sure to call `connectAccount()` before calling other methods that require s
|
|
|
713
719
|
|
|
714
720
|
Check that the chain ID is in the supported chains list: Arbitrum (42161), Base (8453), or Plasma (9745).
|
|
715
721
|
|
|
722
|
+
### SIWE Authentication Issues in Browser
|
|
723
|
+
|
|
724
|
+
The SDK automatically detects browser vs Node.js environments for SIWE authentication:
|
|
725
|
+
- **Browser**: Uses `window.location.origin` for the SIWE message domain/uri to match the browser's automatic `Origin` header
|
|
726
|
+
- **Node.js**: Uses the API endpoint URL
|
|
727
|
+
|
|
728
|
+
If you encounter SIWE authentication failures in a browser, ensure:
|
|
729
|
+
1. Your frontend origin is allowed by the API's CORS configuration
|
|
730
|
+
2. You're using the correct `environment` setting (`staging` or `production`)
|
|
731
|
+
|
|
732
|
+
### Session Key Already Exists
|
|
733
|
+
|
|
734
|
+
If `createSessionKey` returns `{ alreadyActive: true }`, the user already has an active session key. This is not an error - the SDK proactively checks before attempting to create a new one.
|
|
735
|
+
|
|
736
|
+
### Data API CORS Errors
|
|
737
|
+
|
|
738
|
+
Some Data API endpoints may require server-side CORS configuration. If you see CORS errors for endpoints like `onchain-earnings`, `calculate-onchain-earnings`, or `opportunities`, contact ZyFAI support to ensure your origin is whitelisted.
|
|
739
|
+
|
|
716
740
|
## Contributing
|
|
717
741
|
|
|
718
742
|
Contributions are welcome! Please open an issue or submit a pull request.
|
package/dist/index.d.mts
CHANGED
|
@@ -52,11 +52,17 @@ interface AddSessionKeyResponse {
|
|
|
52
52
|
}
|
|
53
53
|
interface SessionKeyResponse {
|
|
54
54
|
success: boolean;
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
/** Session key address (not available when alreadyActive is true) */
|
|
56
|
+
sessionKeyAddress?: Address;
|
|
57
|
+
/** Signature (not available when alreadyActive is true) */
|
|
58
|
+
signature?: Hex;
|
|
57
59
|
sessionNonces?: bigint[];
|
|
58
60
|
userId?: string;
|
|
59
61
|
sessionActivation?: AddSessionKeyResponse;
|
|
62
|
+
/** Message when session key already exists */
|
|
63
|
+
message?: string;
|
|
64
|
+
/** True if a session key was already active for this user */
|
|
65
|
+
alreadyActive?: boolean;
|
|
60
66
|
}
|
|
61
67
|
interface SmartWalletResponse {
|
|
62
68
|
address: Address;
|
|
@@ -117,7 +123,6 @@ interface PositionSlot {
|
|
|
117
123
|
interface PositionsResponse {
|
|
118
124
|
success: boolean;
|
|
119
125
|
userAddress: string;
|
|
120
|
-
totalValueUsd: number;
|
|
121
126
|
positions: Position[];
|
|
122
127
|
}
|
|
123
128
|
interface UserDetails {
|
|
@@ -142,10 +147,18 @@ interface UserDetailsResponse {
|
|
|
142
147
|
success: boolean;
|
|
143
148
|
user: UserDetails;
|
|
144
149
|
}
|
|
150
|
+
interface TVLBreakdown {
|
|
151
|
+
chain_id: number;
|
|
152
|
+
protocol_id: string | null;
|
|
153
|
+
protocol_name: string | null;
|
|
154
|
+
pool: string | null;
|
|
155
|
+
total_balance: number;
|
|
156
|
+
}
|
|
145
157
|
interface TVLResponse {
|
|
146
158
|
success: boolean;
|
|
147
159
|
totalTvl: number;
|
|
148
|
-
byChain?: Record<
|
|
160
|
+
byChain?: Record<number, number>;
|
|
161
|
+
breakdown?: TVLBreakdown[];
|
|
149
162
|
}
|
|
150
163
|
interface VolumeResponse {
|
|
151
164
|
success: boolean;
|
|
@@ -397,6 +410,7 @@ declare class ZyfaiSDK {
|
|
|
397
410
|
private bundlerApiKey?;
|
|
398
411
|
private isAuthenticated;
|
|
399
412
|
private authenticatedUserId;
|
|
413
|
+
private hasActiveSessionKey;
|
|
400
414
|
private environment;
|
|
401
415
|
constructor(config: SDKConfig | string);
|
|
402
416
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -52,11 +52,17 @@ interface AddSessionKeyResponse {
|
|
|
52
52
|
}
|
|
53
53
|
interface SessionKeyResponse {
|
|
54
54
|
success: boolean;
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
/** Session key address (not available when alreadyActive is true) */
|
|
56
|
+
sessionKeyAddress?: Address;
|
|
57
|
+
/** Signature (not available when alreadyActive is true) */
|
|
58
|
+
signature?: Hex;
|
|
57
59
|
sessionNonces?: bigint[];
|
|
58
60
|
userId?: string;
|
|
59
61
|
sessionActivation?: AddSessionKeyResponse;
|
|
62
|
+
/** Message when session key already exists */
|
|
63
|
+
message?: string;
|
|
64
|
+
/** True if a session key was already active for this user */
|
|
65
|
+
alreadyActive?: boolean;
|
|
60
66
|
}
|
|
61
67
|
interface SmartWalletResponse {
|
|
62
68
|
address: Address;
|
|
@@ -117,7 +123,6 @@ interface PositionSlot {
|
|
|
117
123
|
interface PositionsResponse {
|
|
118
124
|
success: boolean;
|
|
119
125
|
userAddress: string;
|
|
120
|
-
totalValueUsd: number;
|
|
121
126
|
positions: Position[];
|
|
122
127
|
}
|
|
123
128
|
interface UserDetails {
|
|
@@ -142,10 +147,18 @@ interface UserDetailsResponse {
|
|
|
142
147
|
success: boolean;
|
|
143
148
|
user: UserDetails;
|
|
144
149
|
}
|
|
150
|
+
interface TVLBreakdown {
|
|
151
|
+
chain_id: number;
|
|
152
|
+
protocol_id: string | null;
|
|
153
|
+
protocol_name: string | null;
|
|
154
|
+
pool: string | null;
|
|
155
|
+
total_balance: number;
|
|
156
|
+
}
|
|
145
157
|
interface TVLResponse {
|
|
146
158
|
success: boolean;
|
|
147
159
|
totalTvl: number;
|
|
148
|
-
byChain?: Record<
|
|
160
|
+
byChain?: Record<number, number>;
|
|
161
|
+
breakdown?: TVLBreakdown[];
|
|
149
162
|
}
|
|
150
163
|
interface VolumeResponse {
|
|
151
164
|
success: boolean;
|
|
@@ -397,6 +410,7 @@ declare class ZyfaiSDK {
|
|
|
397
410
|
private bundlerApiKey?;
|
|
398
411
|
private isAuthenticated;
|
|
399
412
|
private authenticatedUserId;
|
|
413
|
+
private hasActiveSessionKey;
|
|
400
414
|
private environment;
|
|
401
415
|
constructor(config: SDKConfig | string);
|
|
402
416
|
/**
|
package/dist/index.js
CHANGED
|
@@ -87,7 +87,7 @@ var DATA_ENDPOINTS = {
|
|
|
87
87
|
// Portfolio
|
|
88
88
|
DEBANK_PORTFOLIO_MULTICHAIN: (address) => `/debank/portfolio/multichain/${address}`,
|
|
89
89
|
// Opportunities
|
|
90
|
-
OPPORTUNITIES_SAFE: (chainId) => chainId ? `/opportunities/
|
|
90
|
+
OPPORTUNITIES_SAFE: (chainId) => chainId ? `/opportunities/safe?chainId=${chainId}` : "/opportunities/safe",
|
|
91
91
|
OPPORTUNITIES_DEGEN: (chainId) => chainId ? `/opportunities/degen-strategies?chainId=${chainId}` : "/opportunities/degen-strategies",
|
|
92
92
|
// APY History
|
|
93
93
|
DAILY_APY_HISTORY_WEIGHTED: (walletAddress, days) => `/daily-apy-history/weighted/${walletAddress}${days ? `?days=${days}` : ""}`,
|
|
@@ -116,8 +116,7 @@ var HttpClient = class {
|
|
|
116
116
|
baseURL: `${endpoint}${API_VERSION}`,
|
|
117
117
|
headers: {
|
|
118
118
|
"Content-Type": "application/json",
|
|
119
|
-
"X-API-Key": this.apiKey
|
|
120
|
-
Origin: this.origin
|
|
119
|
+
"X-API-Key": this.apiKey
|
|
121
120
|
},
|
|
122
121
|
timeout: 3e4
|
|
123
122
|
});
|
|
@@ -149,7 +148,6 @@ var HttpClient = class {
|
|
|
149
148
|
this.client.interceptors.request.use(
|
|
150
149
|
(config) => {
|
|
151
150
|
config.headers["X-API-Key"] = this.apiKey;
|
|
152
|
-
config.headers["Origin"] = this.origin;
|
|
153
151
|
if (this.authToken) {
|
|
154
152
|
config.headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
155
153
|
}
|
|
@@ -218,6 +216,9 @@ var HttpClient = class {
|
|
|
218
216
|
this.dataClient.interceptors.request.use(
|
|
219
217
|
(config) => {
|
|
220
218
|
config.headers["X-API-Key"] = this.dataApiKey;
|
|
219
|
+
if (this.authToken) {
|
|
220
|
+
config.headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
221
|
+
}
|
|
221
222
|
return config;
|
|
222
223
|
},
|
|
223
224
|
(error) => Promise.reject(error)
|
|
@@ -404,7 +405,6 @@ var getBundlerUrl = (chainId, bundlerApiKey, bundlerProvider = "pimlico") => {
|
|
|
404
405
|
// src/utils/safe-account.ts
|
|
405
406
|
var import_module_sdk = require("@rhinestone/module-sdk");
|
|
406
407
|
var import_permissionless = require("permissionless");
|
|
407
|
-
var import_actions = require("permissionless/actions");
|
|
408
408
|
var import_erc7579 = require("permissionless/actions/erc7579");
|
|
409
409
|
var import_pimlico = require("permissionless/clients/pimlico");
|
|
410
410
|
var import_accounts = require("permissionless/accounts");
|
|
@@ -412,27 +412,36 @@ var import_viem3 = require("viem");
|
|
|
412
412
|
var import_account_abstraction = require("viem/account-abstraction");
|
|
413
413
|
var SAFE_7579_ADDRESS = "0x7579EE8307284F293B1927136486880611F20002";
|
|
414
414
|
var ERC7579_LAUNCHPAD_ADDRESS = "0x7579011aB74c46090561ea277Ba79D510c6C00ff";
|
|
415
|
-
var
|
|
415
|
+
var ACCOUNT_SALTS = {
|
|
416
|
+
staging: "zyfai-staging",
|
|
417
|
+
production: "zyfai-production"
|
|
418
|
+
};
|
|
416
419
|
var getSafeAccount = async (config) => {
|
|
417
420
|
const {
|
|
418
421
|
owner,
|
|
419
422
|
safeOwnerAddress,
|
|
420
423
|
publicClient,
|
|
421
|
-
|
|
424
|
+
environment = "production"
|
|
422
425
|
} = config;
|
|
426
|
+
const effectiveSalt = ACCOUNT_SALTS[environment];
|
|
423
427
|
if (!owner || !owner.account) {
|
|
424
428
|
throw new Error("Wallet not connected. Please connect your wallet first.");
|
|
425
429
|
}
|
|
426
|
-
const
|
|
430
|
+
const signerAddress = owner.account.address;
|
|
431
|
+
if (safeOwnerAddress && safeOwnerAddress.toLowerCase() !== signerAddress.toLowerCase()) {
|
|
432
|
+
throw new Error(
|
|
433
|
+
`Connected wallet address (${signerAddress}) must match the Safe owner address (${safeOwnerAddress}). Please connect with the correct wallet.`
|
|
434
|
+
);
|
|
435
|
+
}
|
|
427
436
|
const ownableValidator = (0, import_module_sdk.getOwnableValidator)({
|
|
428
|
-
owners: [
|
|
437
|
+
owners: [signerAddress],
|
|
429
438
|
threshold: 1
|
|
430
439
|
});
|
|
431
|
-
const saltHex = (0, import_viem3.fromHex)((0, import_viem3.toHex)(
|
|
440
|
+
const saltHex = (0, import_viem3.fromHex)((0, import_viem3.toHex)(effectiveSalt), "bigint");
|
|
432
441
|
const safeAccount = await (0, import_accounts.toSafeSmartAccount)({
|
|
433
442
|
client: publicClient,
|
|
434
443
|
owners: [owner.account],
|
|
435
|
-
//
|
|
444
|
+
// Pass the account object with address and signMessage capability
|
|
436
445
|
version: "1.4.1",
|
|
437
446
|
entryPoint: {
|
|
438
447
|
address: import_account_abstraction.entryPoint07Address,
|
|
@@ -565,8 +574,8 @@ var deploySafeAccount = async (config) => {
|
|
|
565
574
|
);
|
|
566
575
|
}
|
|
567
576
|
};
|
|
568
|
-
var signSessionKey = async (config, sessions) => {
|
|
569
|
-
const { owner, publicClient
|
|
577
|
+
var signSessionKey = async (config, sessions, allPublicClients) => {
|
|
578
|
+
const { owner, publicClient } = config;
|
|
570
579
|
if (!owner || !owner.account) {
|
|
571
580
|
throw new Error("Wallet not connected. Please connect your wallet first.");
|
|
572
581
|
}
|
|
@@ -575,21 +584,24 @@ var signSessionKey = async (config, sessions) => {
|
|
|
575
584
|
address: safeAccount.address,
|
|
576
585
|
type: "safe"
|
|
577
586
|
});
|
|
587
|
+
const clients = allPublicClients || [publicClient];
|
|
578
588
|
const sessionNonces = await Promise.all(
|
|
579
|
-
sessions.map(
|
|
580
|
-
|
|
581
|
-
|
|
589
|
+
sessions.map((session) => {
|
|
590
|
+
const sessionChainId = Number(session.chainId);
|
|
591
|
+
const client = clients.find((c) => c.chain?.id === sessionChainId) || publicClient;
|
|
592
|
+
return (0, import_module_sdk.getSessionNonce)({
|
|
593
|
+
client,
|
|
582
594
|
account,
|
|
583
595
|
permissionId: (0, import_module_sdk.getPermissionId)({
|
|
584
596
|
session
|
|
585
597
|
})
|
|
586
|
-
})
|
|
587
|
-
)
|
|
598
|
+
});
|
|
599
|
+
})
|
|
588
600
|
);
|
|
589
601
|
const sessionDetails = await (0, import_module_sdk.getEnableSessionDetails)({
|
|
590
602
|
sessions,
|
|
591
603
|
account,
|
|
592
|
-
clients
|
|
604
|
+
clients,
|
|
593
605
|
permitGenericPolicy: true,
|
|
594
606
|
sessionNonces
|
|
595
607
|
});
|
|
@@ -614,6 +626,8 @@ var ZyfaiSDK = class {
|
|
|
614
626
|
this.walletClient = null;
|
|
615
627
|
this.isAuthenticated = false;
|
|
616
628
|
this.authenticatedUserId = null;
|
|
629
|
+
// Stored from login response
|
|
630
|
+
this.hasActiveSessionKey = false;
|
|
617
631
|
const sdkConfig = typeof config === "string" ? { apiKey: config } : config;
|
|
618
632
|
const { apiKey, dataApiKey, environment, bundlerApiKey } = sdkConfig;
|
|
619
633
|
if (!apiKey) {
|
|
@@ -636,11 +650,19 @@ var ZyfaiSDK = class {
|
|
|
636
650
|
return;
|
|
637
651
|
}
|
|
638
652
|
const walletClient = this.getWalletClient();
|
|
639
|
-
const userAddress = walletClient.account.address;
|
|
653
|
+
const userAddress = (0, import_viem4.getAddress)(walletClient.account.address);
|
|
640
654
|
const chainId = walletClient.chain?.id || 8453;
|
|
641
655
|
const challengeResponse = await this.httpClient.post(ENDPOINTS.AUTH_CHALLENGE, {});
|
|
642
|
-
|
|
643
|
-
|
|
656
|
+
let uri;
|
|
657
|
+
let domain;
|
|
658
|
+
const globalWindow = typeof globalThis !== "undefined" ? globalThis.window : void 0;
|
|
659
|
+
if (globalWindow?.location?.origin) {
|
|
660
|
+
uri = globalWindow.location.origin;
|
|
661
|
+
domain = globalWindow.location.host;
|
|
662
|
+
} else {
|
|
663
|
+
uri = API_ENDPOINTS[this.environment];
|
|
664
|
+
domain = API_ENDPOINTS[this.environment].split("//")[1];
|
|
665
|
+
}
|
|
644
666
|
const messageObj = new import_siwe.SiweMessage({
|
|
645
667
|
address: userAddress,
|
|
646
668
|
chainId,
|
|
@@ -661,11 +683,6 @@ var ZyfaiSDK = class {
|
|
|
661
683
|
{
|
|
662
684
|
message: messageObj,
|
|
663
685
|
signature
|
|
664
|
-
},
|
|
665
|
-
{
|
|
666
|
-
headers: {
|
|
667
|
-
Origin: uri
|
|
668
|
-
}
|
|
669
686
|
}
|
|
670
687
|
);
|
|
671
688
|
const authToken = loginResponse.accessToken || loginResponse.token;
|
|
@@ -674,6 +691,7 @@ var ZyfaiSDK = class {
|
|
|
674
691
|
}
|
|
675
692
|
this.httpClient.setAuthToken(authToken);
|
|
676
693
|
this.authenticatedUserId = loginResponse.userId || null;
|
|
694
|
+
this.hasActiveSessionKey = loginResponse.hasActiveSessionKey || false;
|
|
677
695
|
this.isAuthenticated = true;
|
|
678
696
|
} catch (error) {
|
|
679
697
|
throw new Error(
|
|
@@ -823,7 +841,8 @@ var ZyfaiSDK = class {
|
|
|
823
841
|
owner: walletClient,
|
|
824
842
|
safeOwnerAddress: userAddress,
|
|
825
843
|
chain: chainConfig.chain,
|
|
826
|
-
publicClient: chainConfig.publicClient
|
|
844
|
+
publicClient: chainConfig.publicClient,
|
|
845
|
+
environment: this.environment
|
|
827
846
|
});
|
|
828
847
|
const isDeployed = await isSafeDeployed(
|
|
829
848
|
safeAddress,
|
|
@@ -871,7 +890,8 @@ var ZyfaiSDK = class {
|
|
|
871
890
|
safeOwnerAddress: userAddress,
|
|
872
891
|
chain: chainConfig.chain,
|
|
873
892
|
publicClient: chainConfig.publicClient,
|
|
874
|
-
bundlerUrl
|
|
893
|
+
bundlerUrl,
|
|
894
|
+
environment: this.environment
|
|
875
895
|
});
|
|
876
896
|
try {
|
|
877
897
|
await this.updateUserProfile({
|
|
@@ -918,14 +938,14 @@ var ZyfaiSDK = class {
|
|
|
918
938
|
"User ID not available. Please ensure authentication completed successfully."
|
|
919
939
|
);
|
|
920
940
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
941
|
+
if (this.hasActiveSessionKey) {
|
|
942
|
+
return {
|
|
943
|
+
success: true,
|
|
944
|
+
userId: this.authenticatedUserId,
|
|
945
|
+
message: "Session key already exists and is active",
|
|
946
|
+
alreadyActive: true
|
|
947
|
+
};
|
|
948
|
+
}
|
|
929
949
|
const sessionConfig = await this.httpClient.get(
|
|
930
950
|
ENDPOINTS.SESSION_KEYS_CONFIG
|
|
931
951
|
);
|
|
@@ -941,10 +961,14 @@ var ZyfaiSDK = class {
|
|
|
941
961
|
chainId,
|
|
942
962
|
sessions
|
|
943
963
|
);
|
|
964
|
+
if (!signatureResult.signature) {
|
|
965
|
+
throw new Error("Failed to obtain session key signature");
|
|
966
|
+
}
|
|
944
967
|
const activation = await this.activateSessionKey(
|
|
945
968
|
signatureResult.signature,
|
|
946
969
|
signatureResult.sessionNonces
|
|
947
970
|
);
|
|
971
|
+
this.hasActiveSessionKey = true;
|
|
948
972
|
return {
|
|
949
973
|
...signatureResult,
|
|
950
974
|
userId: this.authenticatedUserId,
|
|
@@ -982,20 +1006,27 @@ var ZyfaiSDK = class {
|
|
|
982
1006
|
`Invalid account type for ${userAddress}. Must be an EOA.`
|
|
983
1007
|
);
|
|
984
1008
|
}
|
|
1009
|
+
const sessionChainIds = [
|
|
1010
|
+
...new Set(sessions.map((s) => Number(s.chainId)))
|
|
1011
|
+
];
|
|
1012
|
+
const allPublicClients = sessionChainIds.filter(isSupportedChain).map((id) => getChainConfig(id).publicClient);
|
|
985
1013
|
const { signature, sessionNonces } = await signSessionKey(
|
|
986
1014
|
{
|
|
987
1015
|
owner: walletClient,
|
|
988
1016
|
safeOwnerAddress: userAddress,
|
|
989
1017
|
chain: chainConfig.chain,
|
|
990
|
-
publicClient: chainConfig.publicClient
|
|
1018
|
+
publicClient: chainConfig.publicClient,
|
|
1019
|
+
environment: this.environment
|
|
991
1020
|
},
|
|
992
|
-
sessions
|
|
1021
|
+
sessions,
|
|
1022
|
+
allPublicClients
|
|
993
1023
|
);
|
|
994
1024
|
const safeAddress = await getDeterministicSafeAddress({
|
|
995
1025
|
owner: walletClient,
|
|
996
1026
|
safeOwnerAddress: userAddress,
|
|
997
1027
|
chain: chainConfig.chain,
|
|
998
|
-
publicClient: chainConfig.publicClient
|
|
1028
|
+
publicClient: chainConfig.publicClient,
|
|
1029
|
+
environment: this.environment
|
|
999
1030
|
});
|
|
1000
1031
|
return {
|
|
1001
1032
|
success: true,
|
|
@@ -1081,7 +1112,8 @@ var ZyfaiSDK = class {
|
|
|
1081
1112
|
owner: walletClient,
|
|
1082
1113
|
safeOwnerAddress: userAddress,
|
|
1083
1114
|
chain: chainConfig.chain,
|
|
1084
|
-
publicClient: chainConfig.publicClient
|
|
1115
|
+
publicClient: chainConfig.publicClient,
|
|
1116
|
+
environment: this.environment
|
|
1085
1117
|
});
|
|
1086
1118
|
const isDeployed = await isSafeDeployed(
|
|
1087
1119
|
safeAddress,
|
|
@@ -1156,7 +1188,8 @@ var ZyfaiSDK = class {
|
|
|
1156
1188
|
owner: walletClient,
|
|
1157
1189
|
safeOwnerAddress: userAddress,
|
|
1158
1190
|
chain: chainConfig.chain,
|
|
1159
|
-
publicClient: chainConfig.publicClient
|
|
1191
|
+
publicClient: chainConfig.publicClient,
|
|
1192
|
+
environment: this.environment
|
|
1160
1193
|
});
|
|
1161
1194
|
const isDeployed = await isSafeDeployed(
|
|
1162
1195
|
safeAddress,
|
|
@@ -1256,7 +1289,8 @@ var ZyfaiSDK = class {
|
|
|
1256
1289
|
owner: walletClient,
|
|
1257
1290
|
safeOwnerAddress: userAddress,
|
|
1258
1291
|
chain: chainConfig.chain,
|
|
1259
|
-
publicClient: chainConfig.publicClient
|
|
1292
|
+
publicClient: chainConfig.publicClient,
|
|
1293
|
+
environment: this.environment
|
|
1260
1294
|
});
|
|
1261
1295
|
const response = await this.httpClient.get(
|
|
1262
1296
|
ENDPOINTS.DATA_POSITION(safeAddress)
|
|
@@ -1264,8 +1298,6 @@ var ZyfaiSDK = class {
|
|
|
1264
1298
|
return {
|
|
1265
1299
|
success: true,
|
|
1266
1300
|
userAddress,
|
|
1267
|
-
totalValueUsd: 0,
|
|
1268
|
-
// API doesn't return this yet
|
|
1269
1301
|
positions: response ? [response] : []
|
|
1270
1302
|
};
|
|
1271
1303
|
} catch (error) {
|
|
@@ -1337,10 +1369,18 @@ var ZyfaiSDK = class {
|
|
|
1337
1369
|
async getTVL() {
|
|
1338
1370
|
try {
|
|
1339
1371
|
const response = await this.httpClient.get(ENDPOINTS.DATA_TVL);
|
|
1372
|
+
const byChain = {};
|
|
1373
|
+
for (const key of Object.keys(response)) {
|
|
1374
|
+
const numKey = parseInt(key, 10);
|
|
1375
|
+
if (!isNaN(numKey) && typeof response[key] === "number") {
|
|
1376
|
+
byChain[numKey] = response[key];
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1340
1379
|
return {
|
|
1341
1380
|
success: true,
|
|
1342
|
-
totalTvl: response.totalTvl || response.tvl || 0,
|
|
1343
|
-
byChain
|
|
1381
|
+
totalTvl: response.total || response.totalTvl || response.tvl || 0,
|
|
1382
|
+
byChain,
|
|
1383
|
+
breakdown: response.breakdown
|
|
1344
1384
|
};
|
|
1345
1385
|
} catch (error) {
|
|
1346
1386
|
throw new Error(`Failed to get TVL: ${error.message}`);
|
package/dist/index.mjs
CHANGED
|
@@ -48,7 +48,7 @@ var DATA_ENDPOINTS = {
|
|
|
48
48
|
// Portfolio
|
|
49
49
|
DEBANK_PORTFOLIO_MULTICHAIN: (address) => `/debank/portfolio/multichain/${address}`,
|
|
50
50
|
// Opportunities
|
|
51
|
-
OPPORTUNITIES_SAFE: (chainId) => chainId ? `/opportunities/
|
|
51
|
+
OPPORTUNITIES_SAFE: (chainId) => chainId ? `/opportunities/safe?chainId=${chainId}` : "/opportunities/safe",
|
|
52
52
|
OPPORTUNITIES_DEGEN: (chainId) => chainId ? `/opportunities/degen-strategies?chainId=${chainId}` : "/opportunities/degen-strategies",
|
|
53
53
|
// APY History
|
|
54
54
|
DAILY_APY_HISTORY_WEIGHTED: (walletAddress, days) => `/daily-apy-history/weighted/${walletAddress}${days ? `?days=${days}` : ""}`,
|
|
@@ -77,8 +77,7 @@ var HttpClient = class {
|
|
|
77
77
|
baseURL: `${endpoint}${API_VERSION}`,
|
|
78
78
|
headers: {
|
|
79
79
|
"Content-Type": "application/json",
|
|
80
|
-
"X-API-Key": this.apiKey
|
|
81
|
-
Origin: this.origin
|
|
80
|
+
"X-API-Key": this.apiKey
|
|
82
81
|
},
|
|
83
82
|
timeout: 3e4
|
|
84
83
|
});
|
|
@@ -110,7 +109,6 @@ var HttpClient = class {
|
|
|
110
109
|
this.client.interceptors.request.use(
|
|
111
110
|
(config) => {
|
|
112
111
|
config.headers["X-API-Key"] = this.apiKey;
|
|
113
|
-
config.headers["Origin"] = this.origin;
|
|
114
112
|
if (this.authToken) {
|
|
115
113
|
config.headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
116
114
|
}
|
|
@@ -179,6 +177,9 @@ var HttpClient = class {
|
|
|
179
177
|
this.dataClient.interceptors.request.use(
|
|
180
178
|
(config) => {
|
|
181
179
|
config.headers["X-API-Key"] = this.dataApiKey;
|
|
180
|
+
if (this.authToken) {
|
|
181
|
+
config.headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
182
|
+
}
|
|
182
183
|
return config;
|
|
183
184
|
},
|
|
184
185
|
(error) => Promise.reject(error)
|
|
@@ -293,7 +294,8 @@ import { privateKeyToAccount } from "viem/accounts";
|
|
|
293
294
|
import {
|
|
294
295
|
createWalletClient,
|
|
295
296
|
custom,
|
|
296
|
-
http as http3
|
|
297
|
+
http as http3,
|
|
298
|
+
getAddress
|
|
297
299
|
} from "viem";
|
|
298
300
|
|
|
299
301
|
// src/config/chains.ts
|
|
@@ -376,43 +378,49 @@ import {
|
|
|
376
378
|
getSessionNonce
|
|
377
379
|
} from "@rhinestone/module-sdk";
|
|
378
380
|
import { createSmartAccountClient } from "permissionless";
|
|
379
|
-
import { getAccountNonce } from "permissionless/actions";
|
|
380
381
|
import { erc7579Actions } from "permissionless/actions/erc7579";
|
|
381
382
|
import { createPimlicoClient } from "permissionless/clients/pimlico";
|
|
382
383
|
import { toSafeSmartAccount } from "permissionless/accounts";
|
|
383
384
|
import {
|
|
384
385
|
http as http2,
|
|
385
386
|
fromHex,
|
|
386
|
-
pad,
|
|
387
387
|
toHex
|
|
388
388
|
} from "viem";
|
|
389
389
|
import {
|
|
390
|
-
entryPoint07Address
|
|
391
|
-
getUserOperationHash
|
|
390
|
+
entryPoint07Address
|
|
392
391
|
} from "viem/account-abstraction";
|
|
393
392
|
var SAFE_7579_ADDRESS = "0x7579EE8307284F293B1927136486880611F20002";
|
|
394
393
|
var ERC7579_LAUNCHPAD_ADDRESS = "0x7579011aB74c46090561ea277Ba79D510c6C00ff";
|
|
395
|
-
var
|
|
394
|
+
var ACCOUNT_SALTS = {
|
|
395
|
+
staging: "zyfai-staging",
|
|
396
|
+
production: "zyfai-production"
|
|
397
|
+
};
|
|
396
398
|
var getSafeAccount = async (config) => {
|
|
397
399
|
const {
|
|
398
400
|
owner,
|
|
399
401
|
safeOwnerAddress,
|
|
400
402
|
publicClient,
|
|
401
|
-
|
|
403
|
+
environment = "production"
|
|
402
404
|
} = config;
|
|
405
|
+
const effectiveSalt = ACCOUNT_SALTS[environment];
|
|
403
406
|
if (!owner || !owner.account) {
|
|
404
407
|
throw new Error("Wallet not connected. Please connect your wallet first.");
|
|
405
408
|
}
|
|
406
|
-
const
|
|
409
|
+
const signerAddress = owner.account.address;
|
|
410
|
+
if (safeOwnerAddress && safeOwnerAddress.toLowerCase() !== signerAddress.toLowerCase()) {
|
|
411
|
+
throw new Error(
|
|
412
|
+
`Connected wallet address (${signerAddress}) must match the Safe owner address (${safeOwnerAddress}). Please connect with the correct wallet.`
|
|
413
|
+
);
|
|
414
|
+
}
|
|
407
415
|
const ownableValidator = getOwnableValidator({
|
|
408
|
-
owners: [
|
|
416
|
+
owners: [signerAddress],
|
|
409
417
|
threshold: 1
|
|
410
418
|
});
|
|
411
|
-
const saltHex = fromHex(toHex(
|
|
419
|
+
const saltHex = fromHex(toHex(effectiveSalt), "bigint");
|
|
412
420
|
const safeAccount = await toSafeSmartAccount({
|
|
413
421
|
client: publicClient,
|
|
414
422
|
owners: [owner.account],
|
|
415
|
-
//
|
|
423
|
+
// Pass the account object with address and signMessage capability
|
|
416
424
|
version: "1.4.1",
|
|
417
425
|
entryPoint: {
|
|
418
426
|
address: entryPoint07Address,
|
|
@@ -545,8 +553,8 @@ var deploySafeAccount = async (config) => {
|
|
|
545
553
|
);
|
|
546
554
|
}
|
|
547
555
|
};
|
|
548
|
-
var signSessionKey = async (config, sessions) => {
|
|
549
|
-
const { owner, publicClient
|
|
556
|
+
var signSessionKey = async (config, sessions, allPublicClients) => {
|
|
557
|
+
const { owner, publicClient } = config;
|
|
550
558
|
if (!owner || !owner.account) {
|
|
551
559
|
throw new Error("Wallet not connected. Please connect your wallet first.");
|
|
552
560
|
}
|
|
@@ -555,21 +563,24 @@ var signSessionKey = async (config, sessions) => {
|
|
|
555
563
|
address: safeAccount.address,
|
|
556
564
|
type: "safe"
|
|
557
565
|
});
|
|
566
|
+
const clients = allPublicClients || [publicClient];
|
|
558
567
|
const sessionNonces = await Promise.all(
|
|
559
|
-
sessions.map(
|
|
560
|
-
(session)
|
|
561
|
-
|
|
568
|
+
sessions.map((session) => {
|
|
569
|
+
const sessionChainId = Number(session.chainId);
|
|
570
|
+
const client = clients.find((c) => c.chain?.id === sessionChainId) || publicClient;
|
|
571
|
+
return getSessionNonce({
|
|
572
|
+
client,
|
|
562
573
|
account,
|
|
563
574
|
permissionId: getPermissionId({
|
|
564
575
|
session
|
|
565
576
|
})
|
|
566
|
-
})
|
|
567
|
-
)
|
|
577
|
+
});
|
|
578
|
+
})
|
|
568
579
|
);
|
|
569
580
|
const sessionDetails = await getEnableSessionDetails({
|
|
570
581
|
sessions,
|
|
571
582
|
account,
|
|
572
|
-
clients
|
|
583
|
+
clients,
|
|
573
584
|
permitGenericPolicy: true,
|
|
574
585
|
sessionNonces
|
|
575
586
|
});
|
|
@@ -594,6 +605,8 @@ var ZyfaiSDK = class {
|
|
|
594
605
|
this.walletClient = null;
|
|
595
606
|
this.isAuthenticated = false;
|
|
596
607
|
this.authenticatedUserId = null;
|
|
608
|
+
// Stored from login response
|
|
609
|
+
this.hasActiveSessionKey = false;
|
|
597
610
|
const sdkConfig = typeof config === "string" ? { apiKey: config } : config;
|
|
598
611
|
const { apiKey, dataApiKey, environment, bundlerApiKey } = sdkConfig;
|
|
599
612
|
if (!apiKey) {
|
|
@@ -616,11 +629,19 @@ var ZyfaiSDK = class {
|
|
|
616
629
|
return;
|
|
617
630
|
}
|
|
618
631
|
const walletClient = this.getWalletClient();
|
|
619
|
-
const userAddress = walletClient.account.address;
|
|
632
|
+
const userAddress = getAddress(walletClient.account.address);
|
|
620
633
|
const chainId = walletClient.chain?.id || 8453;
|
|
621
634
|
const challengeResponse = await this.httpClient.post(ENDPOINTS.AUTH_CHALLENGE, {});
|
|
622
|
-
|
|
623
|
-
|
|
635
|
+
let uri;
|
|
636
|
+
let domain;
|
|
637
|
+
const globalWindow = typeof globalThis !== "undefined" ? globalThis.window : void 0;
|
|
638
|
+
if (globalWindow?.location?.origin) {
|
|
639
|
+
uri = globalWindow.location.origin;
|
|
640
|
+
domain = globalWindow.location.host;
|
|
641
|
+
} else {
|
|
642
|
+
uri = API_ENDPOINTS[this.environment];
|
|
643
|
+
domain = API_ENDPOINTS[this.environment].split("//")[1];
|
|
644
|
+
}
|
|
624
645
|
const messageObj = new SiweMessage({
|
|
625
646
|
address: userAddress,
|
|
626
647
|
chainId,
|
|
@@ -641,11 +662,6 @@ var ZyfaiSDK = class {
|
|
|
641
662
|
{
|
|
642
663
|
message: messageObj,
|
|
643
664
|
signature
|
|
644
|
-
},
|
|
645
|
-
{
|
|
646
|
-
headers: {
|
|
647
|
-
Origin: uri
|
|
648
|
-
}
|
|
649
665
|
}
|
|
650
666
|
);
|
|
651
667
|
const authToken = loginResponse.accessToken || loginResponse.token;
|
|
@@ -654,6 +670,7 @@ var ZyfaiSDK = class {
|
|
|
654
670
|
}
|
|
655
671
|
this.httpClient.setAuthToken(authToken);
|
|
656
672
|
this.authenticatedUserId = loginResponse.userId || null;
|
|
673
|
+
this.hasActiveSessionKey = loginResponse.hasActiveSessionKey || false;
|
|
657
674
|
this.isAuthenticated = true;
|
|
658
675
|
} catch (error) {
|
|
659
676
|
throw new Error(
|
|
@@ -803,7 +820,8 @@ var ZyfaiSDK = class {
|
|
|
803
820
|
owner: walletClient,
|
|
804
821
|
safeOwnerAddress: userAddress,
|
|
805
822
|
chain: chainConfig.chain,
|
|
806
|
-
publicClient: chainConfig.publicClient
|
|
823
|
+
publicClient: chainConfig.publicClient,
|
|
824
|
+
environment: this.environment
|
|
807
825
|
});
|
|
808
826
|
const isDeployed = await isSafeDeployed(
|
|
809
827
|
safeAddress,
|
|
@@ -851,7 +869,8 @@ var ZyfaiSDK = class {
|
|
|
851
869
|
safeOwnerAddress: userAddress,
|
|
852
870
|
chain: chainConfig.chain,
|
|
853
871
|
publicClient: chainConfig.publicClient,
|
|
854
|
-
bundlerUrl
|
|
872
|
+
bundlerUrl,
|
|
873
|
+
environment: this.environment
|
|
855
874
|
});
|
|
856
875
|
try {
|
|
857
876
|
await this.updateUserProfile({
|
|
@@ -898,14 +917,14 @@ var ZyfaiSDK = class {
|
|
|
898
917
|
"User ID not available. Please ensure authentication completed successfully."
|
|
899
918
|
);
|
|
900
919
|
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
}
|
|
920
|
+
if (this.hasActiveSessionKey) {
|
|
921
|
+
return {
|
|
922
|
+
success: true,
|
|
923
|
+
userId: this.authenticatedUserId,
|
|
924
|
+
message: "Session key already exists and is active",
|
|
925
|
+
alreadyActive: true
|
|
926
|
+
};
|
|
927
|
+
}
|
|
909
928
|
const sessionConfig = await this.httpClient.get(
|
|
910
929
|
ENDPOINTS.SESSION_KEYS_CONFIG
|
|
911
930
|
);
|
|
@@ -921,10 +940,14 @@ var ZyfaiSDK = class {
|
|
|
921
940
|
chainId,
|
|
922
941
|
sessions
|
|
923
942
|
);
|
|
943
|
+
if (!signatureResult.signature) {
|
|
944
|
+
throw new Error("Failed to obtain session key signature");
|
|
945
|
+
}
|
|
924
946
|
const activation = await this.activateSessionKey(
|
|
925
947
|
signatureResult.signature,
|
|
926
948
|
signatureResult.sessionNonces
|
|
927
949
|
);
|
|
950
|
+
this.hasActiveSessionKey = true;
|
|
928
951
|
return {
|
|
929
952
|
...signatureResult,
|
|
930
953
|
userId: this.authenticatedUserId,
|
|
@@ -962,20 +985,27 @@ var ZyfaiSDK = class {
|
|
|
962
985
|
`Invalid account type for ${userAddress}. Must be an EOA.`
|
|
963
986
|
);
|
|
964
987
|
}
|
|
988
|
+
const sessionChainIds = [
|
|
989
|
+
...new Set(sessions.map((s) => Number(s.chainId)))
|
|
990
|
+
];
|
|
991
|
+
const allPublicClients = sessionChainIds.filter(isSupportedChain).map((id) => getChainConfig(id).publicClient);
|
|
965
992
|
const { signature, sessionNonces } = await signSessionKey(
|
|
966
993
|
{
|
|
967
994
|
owner: walletClient,
|
|
968
995
|
safeOwnerAddress: userAddress,
|
|
969
996
|
chain: chainConfig.chain,
|
|
970
|
-
publicClient: chainConfig.publicClient
|
|
997
|
+
publicClient: chainConfig.publicClient,
|
|
998
|
+
environment: this.environment
|
|
971
999
|
},
|
|
972
|
-
sessions
|
|
1000
|
+
sessions,
|
|
1001
|
+
allPublicClients
|
|
973
1002
|
);
|
|
974
1003
|
const safeAddress = await getDeterministicSafeAddress({
|
|
975
1004
|
owner: walletClient,
|
|
976
1005
|
safeOwnerAddress: userAddress,
|
|
977
1006
|
chain: chainConfig.chain,
|
|
978
|
-
publicClient: chainConfig.publicClient
|
|
1007
|
+
publicClient: chainConfig.publicClient,
|
|
1008
|
+
environment: this.environment
|
|
979
1009
|
});
|
|
980
1010
|
return {
|
|
981
1011
|
success: true,
|
|
@@ -1061,7 +1091,8 @@ var ZyfaiSDK = class {
|
|
|
1061
1091
|
owner: walletClient,
|
|
1062
1092
|
safeOwnerAddress: userAddress,
|
|
1063
1093
|
chain: chainConfig.chain,
|
|
1064
|
-
publicClient: chainConfig.publicClient
|
|
1094
|
+
publicClient: chainConfig.publicClient,
|
|
1095
|
+
environment: this.environment
|
|
1065
1096
|
});
|
|
1066
1097
|
const isDeployed = await isSafeDeployed(
|
|
1067
1098
|
safeAddress,
|
|
@@ -1136,7 +1167,8 @@ var ZyfaiSDK = class {
|
|
|
1136
1167
|
owner: walletClient,
|
|
1137
1168
|
safeOwnerAddress: userAddress,
|
|
1138
1169
|
chain: chainConfig.chain,
|
|
1139
|
-
publicClient: chainConfig.publicClient
|
|
1170
|
+
publicClient: chainConfig.publicClient,
|
|
1171
|
+
environment: this.environment
|
|
1140
1172
|
});
|
|
1141
1173
|
const isDeployed = await isSafeDeployed(
|
|
1142
1174
|
safeAddress,
|
|
@@ -1236,7 +1268,8 @@ var ZyfaiSDK = class {
|
|
|
1236
1268
|
owner: walletClient,
|
|
1237
1269
|
safeOwnerAddress: userAddress,
|
|
1238
1270
|
chain: chainConfig.chain,
|
|
1239
|
-
publicClient: chainConfig.publicClient
|
|
1271
|
+
publicClient: chainConfig.publicClient,
|
|
1272
|
+
environment: this.environment
|
|
1240
1273
|
});
|
|
1241
1274
|
const response = await this.httpClient.get(
|
|
1242
1275
|
ENDPOINTS.DATA_POSITION(safeAddress)
|
|
@@ -1244,8 +1277,6 @@ var ZyfaiSDK = class {
|
|
|
1244
1277
|
return {
|
|
1245
1278
|
success: true,
|
|
1246
1279
|
userAddress,
|
|
1247
|
-
totalValueUsd: 0,
|
|
1248
|
-
// API doesn't return this yet
|
|
1249
1280
|
positions: response ? [response] : []
|
|
1250
1281
|
};
|
|
1251
1282
|
} catch (error) {
|
|
@@ -1317,10 +1348,18 @@ var ZyfaiSDK = class {
|
|
|
1317
1348
|
async getTVL() {
|
|
1318
1349
|
try {
|
|
1319
1350
|
const response = await this.httpClient.get(ENDPOINTS.DATA_TVL);
|
|
1351
|
+
const byChain = {};
|
|
1352
|
+
for (const key of Object.keys(response)) {
|
|
1353
|
+
const numKey = parseInt(key, 10);
|
|
1354
|
+
if (!isNaN(numKey) && typeof response[key] === "number") {
|
|
1355
|
+
byChain[numKey] = response[key];
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1320
1358
|
return {
|
|
1321
1359
|
success: true,
|
|
1322
|
-
totalTvl: response.totalTvl || response.tvl || 0,
|
|
1323
|
-
byChain
|
|
1360
|
+
totalTvl: response.total || response.totalTvl || response.tvl || 0,
|
|
1361
|
+
byChain,
|
|
1362
|
+
breakdown: response.breakdown
|
|
1324
1363
|
};
|
|
1325
1364
|
} catch (error) {
|
|
1326
1365
|
throw new Error(`Failed to get TVL: ${error.message}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zyfai/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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",
|