@turtleclub/hooks 0.4.0 → 0.5.0-beta.1

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.
Files changed (49) hide show
  1. package/dist/index.cjs +875 -202
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +839 -194
  4. package/dist/index.js.map +1 -1
  5. package/package.json +3 -3
  6. package/src/v2/balance/api.ts +24 -0
  7. package/src/v2/balance/constants.ts +14 -0
  8. package/src/v2/balance/hooks/useBalance.ts +120 -0
  9. package/src/v2/balance/hooks/useEnsoBalances.ts +72 -0
  10. package/src/v2/balance/hooks/useGetOnChainBalance.ts +84 -0
  11. package/src/v2/balance/hooks/usePortfolioBalance.ts +107 -0
  12. package/src/v2/balance/hooks/useTokenBalance.ts +68 -0
  13. package/src/v2/balance/index.ts +10 -1
  14. package/src/v2/balance/queries.ts +10 -0
  15. package/src/v2/balance/schema.ts +49 -0
  16. package/src/v2/balance/types.ts +29 -0
  17. package/src/v2/balance/utils.ts +96 -0
  18. package/src/v2/earn-deposits/api.ts +1 -1
  19. package/src/v2/earn-membership/api.ts +5 -5
  20. package/src/v2/earn-opportunities/api.ts +1 -1
  21. package/src/v2/earn-route/api.ts +2 -2
  22. package/src/v2/enso-balances/api.ts +2 -3
  23. package/src/v2/enso-balances/hooks.ts +1 -2
  24. package/src/v2/index.ts +13 -4
  25. package/src/v2/lib/api-client.ts +101 -62
  26. package/src/v2/lib/turtle-provider.tsx +48 -0
  27. package/src/v2/opportunities/api.ts +2 -2
  28. package/src/v2/products/api.ts +31 -77
  29. package/src/v2/products/hooks.ts +6 -13
  30. package/src/v2/products/queries.ts +6 -6
  31. package/src/v2/products/schema.ts +1 -1
  32. package/src/v2/schemas/shared.ts +11 -9
  33. package/src/v2/supported-chains/api.ts +22 -0
  34. package/src/v2/supported-chains/hooks.ts +37 -0
  35. package/src/v2/supported-chains/index.ts +4 -0
  36. package/src/v2/supported-chains/queries.ts +9 -0
  37. package/src/v2/supported-chains/schema.ts +10 -0
  38. package/src/v2/supported-tokens/api.ts +32 -0
  39. package/src/v2/supported-tokens/hooks.ts +63 -0
  40. package/src/v2/supported-tokens/index.ts +4 -0
  41. package/src/v2/supported-tokens/queries.ts +10 -0
  42. package/src/v2/supported-tokens/schema.ts +26 -0
  43. package/src/v2/swap/index.ts +2 -0
  44. package/src/v2/swap/route-processor.ts +78 -0
  45. package/src/v2/swap/useSwapRoute.ts +82 -0
  46. package/src/v2/widget/api.ts +1 -1
  47. package/src/v2/balance/useTokenBalance.ts +0 -47
  48. package/src/v2/lib/auth-provider.tsx +0 -30
  49. package/src/v2/lib/authenticated-api-client.ts +0 -22
package/dist/index.cjs CHANGED
@@ -20,12 +20,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- AuthProvider: () => AuthProvider,
23
+ ApiError: () => ApiError,
24
+ BalanceSourcePriority: () => BalanceSourcePriority,
25
+ TurtleProvider: () => TurtleProvider,
26
+ apiClient: () => apiClient,
24
27
  approveStep: () => approveStep,
25
28
  asset: () => asset,
26
29
  assetData: () => assetData,
27
30
  assetERC20Data: () => assetERC20Data,
28
31
  assetImageData: () => assetImageData,
32
+ balanceQueries: () => balanceQueries,
29
33
  balanceTokenSchema: () => balanceTokenSchema,
30
34
  chainSchema: () => chainSchema,
31
35
  checkMembership: () => checkMembership,
@@ -48,20 +52,33 @@ __export(index_exports, {
48
52
  earnRouteResponseSchema: () => earnRouteResponseSchema,
49
53
  ensoBalancesQueries: () => ensoBalancesQueries,
50
54
  ensoStep: () => ensoStep,
55
+ filterBalancesByChains: () => filterBalancesByChains,
56
+ filterExcludedTokens: () => filterExcludedTokens,
57
+ filterNonZeroBalances: () => filterNonZeroBalances,
51
58
  geocheckQueryOptions: () => geocheckQueryOptions,
52
59
  getDeposits: () => getDeposits,
53
60
  getEarnOpportunities: () => getEarnOpportunities,
54
61
  getEarnRoute: () => getEarnRoute,
55
62
  getOpportunities: () => getOpportunities,
56
63
  getOpportunityById: () => getOpportunityById,
64
+ getPortfolioBalance: () => getPortfolioBalance,
57
65
  getProducts: () => getProducts,
66
+ getSourcePriority: () => getSourcePriority,
67
+ getSupportedChains: () => getSupportedChains,
68
+ getSupportedTokens: () => getSupportedTokens,
58
69
  getWalletBalances: () => getWalletBalances,
59
70
  getWidgetOpportunities: () => getWidgetOpportunities,
60
71
  incentiveSchema: () => incentiveSchema,
61
72
  lendingConfigSchema: () => lendingConfigSchema,
73
+ mergeBalancesByPriority: () => mergeBalancesByPriority,
62
74
  opportunitiesQueries: () => opportunitiesQueries,
63
75
  opportunitySchema: () => opportunitySchema,
64
76
  organizationSchema: () => organizationSchema,
77
+ portfolioBalanceResponseSchema: () => portfolioBalanceResponseSchema,
78
+ portfolioHoldingsSchema: () => portfolioHoldingsSchema,
79
+ portfolioTokenSchema: () => portfolioTokenSchema,
80
+ portfolioWalletSchema: () => portfolioWalletSchema,
81
+ processRouteDetails: () => processRouteDetails,
65
82
  productSchema: () => productSchema,
66
83
  productsQueries: () => productsQueries,
67
84
  queries: () => queries,
@@ -69,9 +86,14 @@ __export(index_exports, {
69
86
  routerStep: () => routerStep,
70
87
  routerSubstep: () => routerSubstep,
71
88
  stepTx: () => stepTx,
89
+ supportedChainsQueries: () => supportedChainsQueries,
90
+ supportedChainsResponseSchema: () => supportedChainsResponseSchema,
91
+ supportedTokenSchema: () => supportedTokenSchema,
92
+ supportedTokensQueries: () => supportedTokensQueries,
93
+ supportedTokensResponseSchema: () => supportedTokensResponseSchema,
72
94
  swapSubstep: () => swapSubstep,
73
95
  tokenSchema: () => tokenSchema,
74
- useAuthToken: () => useAuthToken,
96
+ useBalance: () => useBalance,
75
97
  useCheckMembership: () => useCheckMembership,
76
98
  useCreateMembership: () => useCreateMembership,
77
99
  useCreateMembershipAgreement: () => useCreateMembershipAgreement,
@@ -80,12 +102,18 @@ __export(index_exports, {
80
102
  useDeposits: () => useDeposits,
81
103
  useEarnOpportunities: () => useEarnOpportunities,
82
104
  useEarnRoute: () => useEarnRoute,
105
+ useEnsoBalances: () => useEnsoBalances,
83
106
  useGeocheck: () => useGeocheck,
107
+ useGetOnChainBalance: () => useGetOnChainBalance,
84
108
  useMultiChainBalances: () => useMultiChainBalances,
85
109
  useOpportunities: () => useOpportunities,
86
110
  useOpportunity: () => useOpportunity,
111
+ usePortfolioBalance: () => usePortfolioBalance,
87
112
  useProduct: () => useProduct,
88
113
  useProducts: () => useProducts,
114
+ useSupportedChains: () => useSupportedChains,
115
+ useSupportedTokens: () => useSupportedTokens,
116
+ useSwapRoute: () => useSwapRoute,
89
117
  useTokenBalance: () => useTokenBalance,
90
118
  useUpdateProduct: () => useUpdateProduct,
91
119
  useUploadProductLogo: () => useUploadProductLogo,
@@ -100,14 +128,14 @@ __export(index_exports, {
100
128
  module.exports = __toCommonJS(index_exports);
101
129
 
102
130
  // src/v2/index.ts
103
- var import_query_key_factory9 = require("@lukemorales/query-key-factory");
131
+ var import_query_key_factory12 = require("@lukemorales/query-key-factory");
104
132
 
105
133
  // src/v2/opportunities/queries.ts
106
134
  var import_query_key_factory = require("@lukemorales/query-key-factory");
107
135
 
108
136
  // src/v2/lib/api-client.ts
109
- var API_BASE_URL = "https://api.turtle.xyz";
110
- var EARN_BASE_URL = "https://earn.turtle.xyz";
137
+ var API_BASE_URL = "https://api.turtle.club";
138
+ var EARN_BASE_URL = "https://earn.turtle.club";
111
139
  var ApiError = class extends Error {
112
140
  constructor(message, status, response) {
113
141
  super(message);
@@ -116,81 +144,110 @@ var ApiError = class extends Error {
116
144
  this.name = "ApiError";
117
145
  }
118
146
  };
119
- var getEarnUrl = () => {
120
- return process.env.NEXT_PUBLIC_EARN_URL || process.env.VITE_EARN_URL || EARN_BASE_URL;
121
- };
122
- var getApiUrl = () => {
123
- return process.env.NEXT_PUBLIC_API_URL || process.env.VITE_API_URL || API_BASE_URL;
124
- };
125
- async function apiClient(endpoint, options) {
126
- const { debug, domain = "api", baseUrl, ...fetchOptions } = options ?? {};
127
- const resolvedBaseUrl = domain === "earn" ? getEarnUrl() : domain === "custom" ? baseUrl : getApiUrl();
128
- const url = `${resolvedBaseUrl}${endpoint}`;
129
- if (debug) {
130
- console.log("[API Request]", {
131
- method: fetchOptions.method ?? "GET",
132
- url,
133
- headers: fetchOptions.headers,
134
- body: fetchOptions.body
135
- });
147
+ var ApiClient = class _ApiClient {
148
+ static instance;
149
+ config = {};
150
+ static getInstance() {
151
+ if (!_ApiClient.instance) {
152
+ _ApiClient.instance = new _ApiClient();
153
+ }
154
+ return _ApiClient.instance;
136
155
  }
137
- try {
138
- const response = await fetch(url, {
139
- headers: {
140
- "Content-Type": "application/json",
141
- ...fetchOptions.headers
142
- },
143
- ...fetchOptions
144
- });
145
- const data = await response.json();
156
+ configure(config) {
157
+ this.config = { ...this.config, ...config };
158
+ }
159
+ getConfig() {
160
+ return this.config;
161
+ }
162
+ getBaseUrl(domain) {
163
+ if (domain === "earn") {
164
+ return this.config.earnUrl || EARN_BASE_URL;
165
+ }
166
+ return this.config.apiUrl || API_BASE_URL;
167
+ }
168
+ async fetch(endpoint, options) {
169
+ const { domain = "api", debug = this.config.debug, body, ...fetchOptions } = options ?? {};
170
+ const baseUrl = this.getBaseUrl(domain);
171
+ const url = `${baseUrl}${endpoint}`;
172
+ const token = this.config.getToken?.();
173
+ const isFormData = body instanceof FormData;
174
+ const headers = {
175
+ // Don't set Content-Type for FormData - browser sets it with boundary
176
+ ...!isFormData && { "Content-Type": "application/json" },
177
+ ...fetchOptions.headers
178
+ };
179
+ if (token) {
180
+ headers["Authorization"] = `Bearer ${token}`;
181
+ }
146
182
  if (debug) {
147
- console.log("[API Response]", {
148
- status: response.status,
149
- ok: response.ok,
150
- data
183
+ console.log("[ApiClient Request]", {
184
+ method: fetchOptions.method ?? "GET",
185
+ url,
186
+ headers,
187
+ body: isFormData ? "[FormData]" : body
151
188
  });
152
189
  }
153
- if (!response.ok) {
190
+ try {
191
+ const response = await fetch(url, {
192
+ ...fetchOptions,
193
+ headers,
194
+ body: body ? isFormData ? body : JSON.stringify(body) : void 0
195
+ });
196
+ const data = await response.json();
197
+ if (debug) {
198
+ console.log("[ApiClient Response]", {
199
+ status: response.status,
200
+ ok: response.ok,
201
+ data
202
+ });
203
+ }
204
+ if (!response.ok) {
205
+ throw new ApiError(
206
+ `API error: ${response.status} ${response.statusText}`,
207
+ response.status,
208
+ data
209
+ );
210
+ }
211
+ return data;
212
+ } catch (error) {
213
+ if (debug) {
214
+ console.error("[ApiClient Error]", error);
215
+ }
216
+ if (error instanceof ApiError) {
217
+ throw error;
218
+ }
154
219
  throw new ApiError(
155
- `API error: ${response.status} ${response.statusText}`,
156
- response.status,
157
- data
220
+ error instanceof Error ? error.message : "Unknown error",
221
+ 0,
222
+ error
158
223
  );
159
224
  }
160
- return data;
161
- } catch (error) {
162
- if (debug) {
163
- console.error("[API Error]", error);
164
- }
165
- if (error instanceof ApiError) {
166
- throw error;
167
- }
168
- throw new ApiError(error instanceof Error ? error.message : "Unknown error", 0, error);
169
225
  }
170
- }
226
+ };
227
+ var apiClient = ApiClient.getInstance();
171
228
 
172
229
  // src/v2/schemas/shared.ts
173
230
  var import_zod = require("zod");
174
231
  var chainSchema = import_zod.z.object({
175
- id: import_zod.z.string(),
232
+ id: import_zod.z.string().optional(),
176
233
  name: import_zod.z.string(),
177
- slug: import_zod.z.string(),
234
+ slug: import_zod.z.string().optional(),
178
235
  chainId: import_zod.z.string(),
179
- logoUrl: import_zod.z.string(),
180
- ecosystem: import_zod.z.string(),
181
- status: import_zod.z.string(),
182
- explorerUrl: import_zod.z.string()
236
+ logoUrl: import_zod.z.string().optional(),
237
+ ecosystem: import_zod.z.string().optional(),
238
+ status: import_zod.z.string().optional(),
239
+ explorerUrl: import_zod.z.string().optional()
183
240
  });
184
241
  var tokenSchema = import_zod.z.object({
185
- id: import_zod.z.string(),
242
+ id: import_zod.z.string().optional(),
186
243
  name: import_zod.z.string(),
187
244
  symbol: import_zod.z.string(),
188
245
  address: import_zod.z.string(),
189
246
  chain: chainSchema,
190
247
  decimals: import_zod.z.number(),
191
- logoUrl: import_zod.z.string(),
248
+ logoUrl: import_zod.z.string().optional(),
192
249
  isNative: import_zod.z.boolean(),
193
- priceUsd: import_zod.z.number()
250
+ priceUsd: import_zod.z.number().optional()
194
251
  });
195
252
  var organizationSchema = import_zod.z.object({
196
253
  id: import_zod.z.string(),
@@ -229,6 +286,8 @@ var vaultConfigSchema = import_zod.z.object({
229
286
  providerMetadata: import_zod.z.any().optional().nullable(),
230
287
  performanceFee: import_zod.z.number().min(0).max(100).optional().nullable(),
231
288
  managementFee: import_zod.z.number().min(0).max(100).optional().nullable(),
289
+ depositFee: import_zod.z.number().min(0).max(100).optional().nullable(),
290
+ withdrawalFee: import_zod.z.number().min(0).max(100).optional().nullable(),
232
291
  createdAt: import_zod.z.string().datetime().optional(),
233
292
  updatedAt: import_zod.z.string().datetime().optional()
234
293
  });
@@ -321,7 +380,7 @@ async function getOpportunities(filters, options) {
321
380
  if (filters?.productId) params.append("productId", filters.productId);
322
381
  const queryString = params.toString();
323
382
  const endpoint = `/turtle/opportunities${queryString ? `?${queryString}` : ""}`;
324
- const data = await apiClient(endpoint, {
383
+ const data = await apiClient.fetch(endpoint, {
325
384
  method: "GET",
326
385
  debug: options?.debug
327
386
  });
@@ -333,7 +392,7 @@ async function getOpportunities(filters, options) {
333
392
  return result.data;
334
393
  }
335
394
  async function getOpportunityById(id, options) {
336
- const data = await apiClient(`/turtle/opportunities/${id}`, {
395
+ const data = await apiClient.fetch(`/turtle/opportunities/${id}`, {
337
396
  method: "GET",
338
397
  debug: options?.debug
339
398
  });
@@ -388,7 +447,7 @@ var earnOpportunitiesResponseSchema = import_zod3.z.object({
388
447
 
389
448
  // src/v2/earn-opportunities/api.ts
390
449
  async function getEarnOpportunities(context) {
391
- const data = await apiClient("/v1/opportunities/", {
450
+ const data = await apiClient.fetch("/v1/opportunities/", {
392
451
  method: "GET",
393
452
  domain: "earn",
394
453
  signal: context?.signal
@@ -505,7 +564,7 @@ async function getEarnRoute(params, context) {
505
564
  ...params.referral_code && { referral_code: params.referral_code },
506
565
  ...params.id && { id: params.id }
507
566
  });
508
- const response = await apiClient(`/v1/route/?${searchParams.toString()}`, {
567
+ const response = await apiClient.fetch(`/v1/route/?${searchParams.toString()}`, {
509
568
  method: "GET",
510
569
  domain: "earn",
511
570
  signal: context?.signal
@@ -564,7 +623,7 @@ async function checkMembership(params, context) {
564
623
  address: params.address,
565
624
  ...params.walletEcosystem && { walletEcosystem: params.walletEcosystem }
566
625
  });
567
- const data = await apiClient(`/v1/membership/?${searchParams.toString()}`, {
626
+ const data = await apiClient.fetch(`/v1/membership/?${searchParams.toString()}`, {
568
627
  method: "GET",
569
628
  domain: "earn",
570
629
  signal: context?.signal
@@ -577,10 +636,10 @@ async function checkMembership(params, context) {
577
636
  return result.data;
578
637
  }
579
638
  async function createMembershipAgreement(request) {
580
- const data = await apiClient("/v1/membership/agreement", {
639
+ const data = await apiClient.fetch("/v1/membership/agreement", {
581
640
  method: "POST",
582
641
  domain: "earn",
583
- body: JSON.stringify(request)
642
+ body: request
584
643
  });
585
644
  const result = createAgreementResponseSchema.safeParse(data);
586
645
  if (result.success === false) {
@@ -590,10 +649,10 @@ async function createMembershipAgreement(request) {
590
649
  return result.data;
591
650
  }
592
651
  async function createMembership(request) {
593
- const data = await apiClient("/v1/membership/", {
652
+ const data = await apiClient.fetch("/v1/membership/", {
594
653
  method: "POST",
595
654
  domain: "earn",
596
- body: JSON.stringify(request)
655
+ body: request
597
656
  });
598
657
  const result = createMembershipResponseSchema.safeParse(data);
599
658
  if (result.success === false) {
@@ -645,7 +704,7 @@ async function getDeposits(params, context) {
645
704
  });
646
705
  const queryString = searchParams.toString();
647
706
  const endpoint = `/v1/deposit/${distributor_id}${queryString ? `?${queryString}` : ""}`;
648
- const data = await apiClient(endpoint, {
707
+ const data = await apiClient.fetch(endpoint, {
649
708
  method: "GET",
650
709
  domain: "earn",
651
710
  signal: context?.signal
@@ -669,21 +728,10 @@ var earnDepositsQueries = (0, import_query_key_factory5.createQueryKeys)("earnDe
669
728
  // src/v2/products/queries.ts
670
729
  var import_query_key_factory6 = require("@lukemorales/query-key-factory");
671
730
 
672
- // src/v2/lib/authenticated-api-client.ts
673
- async function authenticatedApiClient(endpoint, options, token = null) {
674
- return apiClient(endpoint, {
675
- ...options,
676
- headers: {
677
- ...token && { Authorization: `Bearer ${token}` },
678
- ...options?.headers
679
- }
680
- });
681
- }
682
-
683
731
  // src/v2/products/schema.ts
684
732
  var import_zod7 = require("zod");
685
733
  var ProductTypeSchema = import_zod7.z.enum(["deal", "campaign"]);
686
- var ProductStatusSchema = import_zod7.z.enum(["active", "paused", "ended"]);
734
+ var ProductStatusSchema = import_zod7.z.enum(["draft", "active", "paused", "ended"]);
687
735
  var TurtleOrganizationSchema = import_zod7.z.object({
688
736
  id: import_zod7.z.string().uuid(),
689
737
  name: import_zod7.z.string(),
@@ -753,19 +801,14 @@ var ProductFiltersSchema = import_zod7.z.object({
753
801
  });
754
802
 
755
803
  // src/v2/products/api.ts
756
- async function getProducts(filters, token) {
804
+ async function getProducts(filters) {
757
805
  const params = new URLSearchParams();
758
806
  if (filters?.organizationId) params.append("organizationId", filters.organizationId);
759
807
  const queryString = params.toString();
760
808
  const endpoint = `/admin/products${queryString ? `?${queryString}` : ""}`;
761
- console.log("endpoint", endpoint, filters);
762
- const data = await authenticatedApiClient(
763
- endpoint,
764
- {
765
- method: "GET"
766
- },
767
- token
768
- );
809
+ const data = await apiClient.fetch(endpoint, {
810
+ method: "GET"
811
+ });
769
812
  const result = ProductsResponseSchema.safeParse(data);
770
813
  if (result.success === false) {
771
814
  console.log("[ZOD ERROR]", result.error);
@@ -773,16 +816,11 @@ async function getProducts(filters, token) {
773
816
  }
774
817
  return result.data;
775
818
  }
776
- async function getProduct(id, token) {
819
+ async function getProduct(id) {
777
820
  const endpoint = `/admin/products/${id}`;
778
- const data = await authenticatedApiClient(
779
- endpoint,
780
- {
781
- method: "GET"
782
- },
783
- token
784
- );
785
- console.log(data);
821
+ const data = await apiClient.fetch(endpoint, {
822
+ method: "GET"
823
+ });
786
824
  const result = ProductResponseSchema.safeParse(data);
787
825
  if (result.success === false) {
788
826
  console.log("[ZOD ERROR]", result.error);
@@ -790,19 +828,12 @@ async function getProduct(id, token) {
790
828
  }
791
829
  return result.data;
792
830
  }
793
- async function createProduct(input, token) {
831
+ async function createProduct(input) {
794
832
  const endpoint = `/admin/products`;
795
- const data = await authenticatedApiClient(
796
- endpoint,
797
- {
798
- method: "POST",
799
- body: JSON.stringify(input),
800
- headers: {
801
- "Content-Type": "application/json"
802
- }
803
- },
804
- token
805
- );
833
+ const data = await apiClient.fetch(endpoint, {
834
+ method: "POST",
835
+ body: input
836
+ });
806
837
  const result = CreateProductResponseSchema.safeParse(data);
807
838
  if (result.success === false) {
808
839
  console.log("[ZOD ERROR]", result.error);
@@ -810,19 +841,12 @@ async function createProduct(input, token) {
810
841
  }
811
842
  return result.data;
812
843
  }
813
- async function updateProduct(input, token) {
844
+ async function updateProduct(input) {
814
845
  const endpoint = `/admin/products/${input.product.id}`;
815
- const data = await authenticatedApiClient(
816
- endpoint,
817
- {
818
- method: "PUT",
819
- body: JSON.stringify(input),
820
- headers: {
821
- "Content-Type": "application/json"
822
- }
823
- },
824
- token
825
- );
846
+ const data = await apiClient.fetch(endpoint, {
847
+ method: "PUT",
848
+ body: input
849
+ });
826
850
  const result = UpdateProductResponseSchema.safeParse(data);
827
851
  if (result.success === false) {
828
852
  console.log("[ZOD ERROR]", result.error);
@@ -830,36 +854,28 @@ async function updateProduct(input, token) {
830
854
  }
831
855
  return result.data;
832
856
  }
833
- async function deleteProduct(id, token) {
857
+ async function deleteProduct(id) {
834
858
  const endpoint = `/admin/products/${id}`;
835
- const data = await authenticatedApiClient(
836
- endpoint,
837
- {
838
- method: "DELETE",
839
- headers: {
840
- "Content-Type": "application/json"
841
- }
842
- },
843
- token
844
- );
859
+ const data = await apiClient.fetch(endpoint, {
860
+ method: "DELETE"
861
+ });
845
862
  const result = ProductResponseSchema.safeParse(data);
846
863
  if (result.success === false) {
847
864
  console.log("[ZOD ERROR]", result.error);
848
865
  throw new Error(`Failed to delete product: ${result.error.message}`);
849
866
  }
850
867
  }
851
- async function uploadProductLogo({ file, filename }, token) {
868
+ async function uploadProductLogo({
869
+ file,
870
+ filename
871
+ }) {
852
872
  const endpoint = `/admin/products/upload-logo`;
853
873
  const formData = new FormData();
854
874
  formData.append("file", file, filename || file.name);
855
- const data = await authenticatedApiClient(
856
- endpoint,
857
- {
858
- method: "POST",
859
- body: formData
860
- },
861
- token
862
- );
875
+ const data = await apiClient.fetch(endpoint, {
876
+ method: "POST",
877
+ body: formData
878
+ });
863
879
  const result = UploadProductLogoResponseSchema.safeParse(data);
864
880
  if (result.success === false) {
865
881
  console.log("[ZOD ERROR]", result.error);
@@ -871,17 +887,17 @@ async function uploadProductLogo({ file, filename }, token) {
871
887
  // src/v2/products/queries.ts
872
888
  var productsQueries = (0, import_query_key_factory6.createQueryKeys)("products", {
873
889
  // Get all products (no filters)
874
- all: (token) => ({
890
+ all: () => ({
875
891
  queryKey: ["all"],
876
- queryFn: () => getProducts(void 0, token)
892
+ queryFn: () => getProducts()
877
893
  }),
878
- list: (filters, token) => ({
894
+ list: (filters) => ({
879
895
  queryKey: [{ filters }],
880
- queryFn: () => getProducts(filters, token)
896
+ queryFn: () => getProducts(filters)
881
897
  }),
882
- byId: (id, token) => ({
898
+ byId: (id) => ({
883
899
  queryKey: [id],
884
- queryFn: () => getProduct(id, token)
900
+ queryFn: () => getProduct(id)
885
901
  })
886
902
  });
887
903
 
@@ -915,10 +931,9 @@ async function getWalletBalances(params, context) {
915
931
  chain: chain.toString()
916
932
  });
917
933
  const endpoint = `/v1/api/wallet_balances?${searchParams.toString()}`;
918
- const data = await apiClient(endpoint, {
934
+ const data = await apiClient.fetch(endpoint, {
919
935
  method: "GET",
920
- domain: "custom",
921
- baseUrl: "https://earn.turtle.vision",
936
+ domain: "earn",
922
937
  signal: context?.signal
923
938
  });
924
939
  const result = walletBalancesResponseSchema.safeParse(data);
@@ -949,7 +964,7 @@ var widgetOpportunitiesResponseSchema = import_zod9.z.object({
949
964
 
950
965
  // src/v2/widget/api.ts
951
966
  async function getWidgetOpportunities(distributorId, context) {
952
- const data = await apiClient(`/widget/${distributorId}/opportunities`, {
967
+ const data = await apiClient.fetch(`/widget/${distributorId}/opportunities`, {
953
968
  method: "GET",
954
969
  signal: context?.signal
955
970
  });
@@ -969,6 +984,80 @@ var widgetQueries = (0, import_query_key_factory8.createQueryKeys)("widget", {
969
984
  })
970
985
  });
971
986
 
987
+ // src/v2/supported-chains/queries.ts
988
+ var import_query_key_factory9 = require("@lukemorales/query-key-factory");
989
+
990
+ // src/v2/supported-chains/schema.ts
991
+ var import_zod10 = require("zod");
992
+ var supportedChainsResponseSchema = import_zod10.z.object({
993
+ chains: import_zod10.z.array(chainSchema)
994
+ });
995
+
996
+ // src/v2/supported-chains/api.ts
997
+ async function getSupportedChains(options) {
998
+ const data = await apiClient.fetch("/turtle/chains", {
999
+ method: "GET",
1000
+ debug: options?.debug
1001
+ });
1002
+ const result = supportedChainsResponseSchema.safeParse(data);
1003
+ if (result.success === false) {
1004
+ console.log("[ZOD ERROR]", result.error);
1005
+ throw new Error(`Failed to parse supported chains: ${result.error.message}`);
1006
+ }
1007
+ return result.data;
1008
+ }
1009
+
1010
+ // src/v2/supported-chains/queries.ts
1011
+ var supportedChainsQueries = (0, import_query_key_factory9.createQueryKeys)("supportedChains", {
1012
+ all: {
1013
+ queryKey: null,
1014
+ queryFn: () => getSupportedChains()
1015
+ }
1016
+ });
1017
+
1018
+ // src/v2/supported-tokens/queries.ts
1019
+ var import_query_key_factory10 = require("@lukemorales/query-key-factory");
1020
+
1021
+ // src/v2/supported-tokens/schema.ts
1022
+ var import_zod11 = require("zod");
1023
+ var supportedTokenSchema = tokenSchema.extend({
1024
+ active: import_zod11.z.boolean()
1025
+ });
1026
+ var supportedTokensResponseSchema = import_zod11.z.object({
1027
+ tokens: import_zod11.z.array(supportedTokenSchema),
1028
+ total: import_zod11.z.number().optional(),
1029
+ limit: import_zod11.z.number().optional(),
1030
+ page: import_zod11.z.number().optional()
1031
+ });
1032
+
1033
+ // src/v2/supported-tokens/api.ts
1034
+ async function getSupportedTokens(filters, options) {
1035
+ const params = new URLSearchParams();
1036
+ if (filters?.page !== void 0) params.append("page", filters.page.toString());
1037
+ if (filters?.limit !== void 0) params.append("limit", filters.limit.toString());
1038
+ if (filters?.search) params.append("search", filters.search);
1039
+ const queryString = params.toString();
1040
+ const endpoint = `/turtle/tokens${queryString ? `?${queryString}` : ""}`;
1041
+ const data = await apiClient.fetch(endpoint, {
1042
+ method: "GET",
1043
+ debug: options?.debug
1044
+ });
1045
+ const result = supportedTokensResponseSchema.safeParse(data);
1046
+ if (result.success === false) {
1047
+ console.log("[ZOD ERROR]", result.error);
1048
+ throw new Error(`Failed to parse supported tokens: ${result.error.message}`);
1049
+ }
1050
+ return result.data;
1051
+ }
1052
+
1053
+ // src/v2/supported-tokens/queries.ts
1054
+ var supportedTokensQueries = (0, import_query_key_factory10.createQueryKeys)("supportedTokens", {
1055
+ list: (filters) => ({
1056
+ queryKey: [filters],
1057
+ queryFn: () => getSupportedTokens(filters)
1058
+ })
1059
+ });
1060
+
972
1061
  // src/v2/earn-opportunities/hooks.ts
973
1062
  var import_react_query = require("@tanstack/react-query");
974
1063
 
@@ -1120,84 +1209,76 @@ function useOpportunity({ id, ...options }) {
1120
1209
 
1121
1210
  // src/v2/products/hooks.ts
1122
1211
  var import_react_query7 = require("@tanstack/react-query");
1123
-
1124
- // src/v2/lib/auth-provider.tsx
1125
- var import_react2 = require("react");
1126
- var import_jsx_runtime = require("react/jsx-runtime");
1127
- var AuthContext = (0, import_react2.createContext)(null);
1128
- function AuthProvider({ getToken, children }) {
1129
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AuthContext.Provider, { value: { getToken }, children });
1130
- }
1131
- function useAuthToken() {
1132
- const context = (0, import_react2.useContext)(AuthContext);
1133
- if (!context) {
1134
- throw new Error("useAuthToken must be used within AuthProvider");
1135
- }
1136
- return context;
1137
- }
1138
-
1139
- // src/v2/products/hooks.ts
1140
1212
  function useProducts({ filters, enabled = true }) {
1141
- const { getToken } = useAuthToken();
1142
1213
  return (0, import_react_query7.useQuery)({
1143
- ...productsQueries.list(filters, getToken()),
1214
+ ...productsQueries.list(filters),
1144
1215
  ...queryDefaults,
1145
1216
  enabled
1146
1217
  });
1147
1218
  }
1148
1219
  function useProduct({ id, enabled = true }) {
1149
- const { getToken } = useAuthToken();
1150
1220
  return (0, import_react_query7.useQuery)({
1151
- ...productsQueries.byId(id, getToken()),
1221
+ ...productsQueries.byId(id),
1152
1222
  ...queryDefaults,
1153
1223
  enabled
1154
1224
  });
1155
1225
  }
1156
1226
  function useCreateProduct(options) {
1157
- const { getToken } = useAuthToken();
1158
1227
  return (0, import_react_query7.useMutation)({
1159
- mutationFn: (input) => createProduct(input, getToken()),
1228
+ mutationFn: (input) => createProduct(input),
1160
1229
  ...options
1161
1230
  });
1162
1231
  }
1163
1232
  function useUpdateProduct(options) {
1164
- const { getToken } = useAuthToken();
1165
1233
  return (0, import_react_query7.useMutation)({
1166
- mutationFn: (input) => updateProduct(input, getToken()),
1234
+ mutationFn: (input) => updateProduct(input),
1167
1235
  ...options
1168
1236
  });
1169
1237
  }
1170
1238
  function useDeleteProduct(options) {
1171
- const { getToken } = useAuthToken();
1172
1239
  return (0, import_react_query7.useMutation)({
1173
- mutationFn: (id) => deleteProduct(id, getToken()),
1240
+ mutationFn: (id) => deleteProduct(id),
1174
1241
  ...options
1175
1242
  });
1176
1243
  }
1177
1244
  function useUploadProductLogo(options) {
1178
- const { getToken } = useAuthToken();
1179
1245
  return (0, import_react_query7.useMutation)({
1180
- mutationFn: (request) => uploadProductLogo(request, getToken()),
1246
+ mutationFn: (request) => uploadProductLogo(request),
1181
1247
  ...options
1182
1248
  });
1183
1249
  }
1184
1250
 
1185
- // src/v2/balance/useTokenBalance.ts
1186
- var import_react3 = require("react");
1251
+ // src/v2/balance/hooks/useTokenBalance.ts
1252
+ var import_react2 = require("react");
1187
1253
  var import_viem = require("viem");
1188
1254
  var import_utils2 = require("@turtleclub/utils");
1189
- function useTokenBalance({ token, amount, setAmount }) {
1190
- const usdValue = (0, import_react3.useMemo)(() => (0, import_utils2.calculateUsdValue)(amount, token?.price), [amount, token?.price]);
1191
- const hasInsufficientBalance = (0, import_react3.useMemo)(
1192
- () => (0, import_utils2.checkInsufficientBalance)(token, amount),
1193
- [token, amount]
1255
+ function checkInsufficientBalance(tokenBalance, amount) {
1256
+ if (!tokenBalance || !amount) return false;
1257
+ try {
1258
+ const balance = BigInt(tokenBalance.amount);
1259
+ const amountBigInt = (0, import_viem.parseUnits)(amount, tokenBalance.token.decimals);
1260
+ return balance < amountBigInt;
1261
+ } catch (error) {
1262
+ console.error("[Balance Check Error]", error, { tokenBalance, amount });
1263
+ return true;
1264
+ }
1265
+ }
1266
+ function useTokenBalance({ tokenBalance, amount, setAmount }) {
1267
+ const token = tokenBalance?.token;
1268
+ const usdValue = (0, import_react2.useMemo)(
1269
+ () => (0, import_utils2.calculateUsdValue)(amount, token?.priceUsd),
1270
+ [amount, token?.priceUsd]
1271
+ );
1272
+ const hasInsufficientBalance = (0, import_react2.useMemo)(
1273
+ () => checkInsufficientBalance(tokenBalance, amount),
1274
+ [tokenBalance, amount]
1194
1275
  );
1195
- const handleMaxClick = (0, import_react3.useCallback)(() => {
1196
- if (!token) return;
1197
- const maxAmount = (0, import_utils2.calculateMaxAmount)(token.balance, token.decimals);
1276
+ const handleMaxClick = (0, import_react2.useCallback)(() => {
1277
+ if (!tokenBalance?.token || !tokenBalance.amount) return;
1278
+ const maxAmount = (0, import_utils2.calculateMaxAmount)(tokenBalance.amount, tokenBalance.token.decimals);
1198
1279
  setAmount(maxAmount);
1199
- }, [token, setAmount]);
1200
- const amountBigInt = (0, import_react3.useMemo)(() => {
1280
+ }, [tokenBalance, setAmount]);
1281
+ const amountBigInt = (0, import_react2.useMemo)(() => {
1201
1282
  if (!token || !amount) return void 0;
1202
1283
  try {
1203
1284
  return (0, import_viem.parseUnits)(amount, token.decimals);
@@ -1213,10 +1294,454 @@ function useTokenBalance({ token, amount, setAmount }) {
1213
1294
  };
1214
1295
  }
1215
1296
 
1216
- // src/v2/widget/hooks.ts
1297
+ // src/v2/balance/types.ts
1298
+ var BalanceSourcePriority = /* @__PURE__ */ ((BalanceSourcePriority2) => {
1299
+ BalanceSourcePriority2[BalanceSourcePriority2["ONCHAIN"] = 1] = "ONCHAIN";
1300
+ BalanceSourcePriority2[BalanceSourcePriority2["PORTFOLIO"] = 2] = "PORTFOLIO";
1301
+ BalanceSourcePriority2[BalanceSourcePriority2["ENSO"] = 3] = "ENSO";
1302
+ return BalanceSourcePriority2;
1303
+ })(BalanceSourcePriority || {});
1304
+
1305
+ // src/v2/balance/schema.ts
1306
+ var import_zod12 = require("zod");
1307
+ var portfolioTokenSchema = import_zod12.z.object({
1308
+ id: import_zod12.z.string(),
1309
+ address: import_zod12.z.string(),
1310
+ name: import_zod12.z.string(),
1311
+ symbol: import_zod12.z.string(),
1312
+ decimals: import_zod12.z.number(),
1313
+ isNative: import_zod12.z.boolean(),
1314
+ logoUrl: import_zod12.z.string().nullable(),
1315
+ amount: import_zod12.z.string(),
1316
+ // Portfolio-specific field (decimal format)
1317
+ price: import_zod12.z.string().nullable().transform((val) => val ? parseFloat(val) : null),
1318
+ // Portfolio-specific field
1319
+ // Chain with optional fields to match Portfolio API response
1320
+ chain: chainSchema.partial().required({ chainId: true })
1321
+ });
1322
+ var portfolioWalletSchema = import_zod12.z.object({
1323
+ id: import_zod12.z.string().optional(),
1324
+ address: import_zod12.z.string(),
1325
+ blockchain: import_zod12.z.string().optional(),
1326
+ tokens: import_zod12.z.array(portfolioTokenSchema)
1327
+ });
1328
+ var portfolioHoldingsSchema = import_zod12.z.object({
1329
+ wallets: import_zod12.z.array(portfolioWalletSchema)
1330
+ });
1331
+ var portfolioBalanceResponseSchema = import_zod12.z.object({
1332
+ portfolio: import_zod12.z.object({
1333
+ holdings: portfolioHoldingsSchema
1334
+ })
1335
+ });
1336
+
1337
+ // src/v2/balance/api.ts
1338
+ async function getPortfolioBalance(address, options) {
1339
+ const data = await apiClient.fetch(`/wallet/${address}/balance`, {
1340
+ method: "GET",
1341
+ domain: "api",
1342
+ debug: options?.debug
1343
+ });
1344
+ const result = portfolioBalanceResponseSchema.safeParse(data);
1345
+ if (result.success === false) {
1346
+ console.log("[ZOD ERROR]", result.error);
1347
+ throw new Error(`Failed to parse portfolio balance: ${result.error.message}`);
1348
+ }
1349
+ return result.data;
1350
+ }
1351
+
1352
+ // src/v2/balance/queries.ts
1353
+ var import_query_key_factory11 = require("@lukemorales/query-key-factory");
1354
+ var balanceQueries = (0, import_query_key_factory11.createQueryKeys)("balance", {
1355
+ // Portfolio balance by address
1356
+ portfolio: (address) => ({
1357
+ queryKey: [address],
1358
+ queryFn: () => getPortfolioBalance(address)
1359
+ })
1360
+ });
1361
+
1362
+ // src/v2/balance/hooks/useBalance.ts
1363
+ var import_react7 = require("react");
1364
+
1365
+ // src/v2/balance/hooks/useGetOnChainBalance.ts
1366
+ var import_wagmi = require("wagmi");
1367
+ var import_viem2 = require("viem");
1368
+ var import_react3 = require("react");
1369
+ function useGetOnChainBalance({
1370
+ tokens,
1371
+ chainId,
1372
+ address,
1373
+ enabled = true
1374
+ }) {
1375
+ const contracts = (0, import_react3.useMemo)(() => {
1376
+ if (!address || !enabled) return [];
1377
+ return tokens.map((token) => ({
1378
+ address: token.address,
1379
+ abi: import_viem2.erc20Abi,
1380
+ functionName: "balanceOf",
1381
+ args: [address],
1382
+ chainId
1383
+ }));
1384
+ }, [tokens, address, chainId, enabled]);
1385
+ const {
1386
+ data: results,
1387
+ isLoading,
1388
+ error,
1389
+ refetch
1390
+ } = (0, import_wagmi.useReadContracts)({
1391
+ contracts,
1392
+ query: {
1393
+ ...queryDefaults,
1394
+ enabled: enabled && !!address && tokens.length > 0,
1395
+ refetchInterval: 5e3,
1396
+ refetchOnWindowFocus: true
1397
+ }
1398
+ });
1399
+ const balances = (0, import_react3.useMemo)(() => {
1400
+ if (!results || results.length === 0) return [];
1401
+ return tokens.map((token, index) => {
1402
+ const result = results[index];
1403
+ let balance = "0";
1404
+ if (result?.status === "success" && typeof result.result === "bigint") {
1405
+ balance = result.result.toString();
1406
+ }
1407
+ return {
1408
+ token,
1409
+ amount: balance,
1410
+ source: "onchain"
1411
+ };
1412
+ });
1413
+ }, [results, tokens]);
1414
+ return {
1415
+ balances,
1416
+ isLoading,
1417
+ error: error ? error : null,
1418
+ refetch
1419
+ };
1420
+ }
1421
+
1422
+ // src/v2/balance/hooks/usePortfolioBalance.ts
1423
+ var import_react_query10 = require("@tanstack/react-query");
1424
+ var import_react5 = require("react");
1425
+
1426
+ // src/v2/supported-chains/hooks.ts
1217
1427
  var import_react_query8 = require("@tanstack/react-query");
1428
+ function useSupportedChains() {
1429
+ const { data, isLoading, error, refetch } = (0, import_react_query8.useQuery)({
1430
+ ...supportedChainsQueries.all,
1431
+ ...queryDefaults
1432
+ });
1433
+ const chains = data?.chains ?? [];
1434
+ return {
1435
+ chains,
1436
+ getChainByChainId: (chainId) => chains.find((chain) => chain.chainId === chainId),
1437
+ isLoading,
1438
+ error: error ? error : null,
1439
+ refetch
1440
+ };
1441
+ }
1442
+
1443
+ // src/v2/supported-tokens/hooks.ts
1444
+ var import_react_query9 = require("@tanstack/react-query");
1445
+ var import_react4 = require("react");
1446
+ function useSupportedTokens({
1447
+ page = 0,
1448
+ limit = 20,
1449
+ search,
1450
+ enabled = true
1451
+ } = {}) {
1452
+ const { data, isLoading, error, refetch } = (0, import_react_query9.useQuery)({
1453
+ ...supportedTokensQueries.list({ page, limit, search }),
1454
+ ...queryDefaults,
1455
+ staleTime: 5 * 60 * 1e3,
1456
+ // 1 minutes
1457
+ gcTime: 10 * 60 * 1e3,
1458
+ // 10 minutes
1459
+ refetchOnWindowFocus: false,
1460
+ // Don't refetch on tab focus
1461
+ enabled
1462
+ });
1463
+ const tokens = (0, import_react4.useMemo)(() => {
1464
+ if (!data?.tokens) return [];
1465
+ return data.tokens.map(({ active, ...token }) => token);
1466
+ }, [data?.tokens]);
1467
+ return {
1468
+ tokens,
1469
+ total: data?.total ?? 0,
1470
+ limit: data?.limit ?? limit,
1471
+ page: data?.page ?? page,
1472
+ isLoading,
1473
+ error: error ? error : null,
1474
+ refetch,
1475
+ getTokenById: (id) => tokens.find((token) => token.id === id),
1476
+ getToken: (address, chainId) => tokens.find(
1477
+ (token) => token.address.toLowerCase() === address.toLowerCase() && token.chain.chainId === chainId
1478
+ )
1479
+ };
1480
+ }
1481
+
1482
+ // src/v2/balance/hooks/usePortfolioBalance.ts
1483
+ var import_viem3 = require("viem");
1484
+ function usePortfolioBalance({
1485
+ address
1486
+ }) {
1487
+ const { getChainByChainId } = useSupportedChains();
1488
+ const { getToken } = useSupportedTokens();
1489
+ const {
1490
+ data: portfolioData,
1491
+ isLoading,
1492
+ error,
1493
+ refetch
1494
+ } = (0, import_react_query10.useQuery)({
1495
+ ...balanceQueries.portfolio(address || ""),
1496
+ ...queryDefaults,
1497
+ enabled: !!address,
1498
+ refetchInterval: 1 * 60 * 1e3
1499
+ // 1 minute
1500
+ });
1501
+ const balances = (0, import_react5.useMemo)(() => {
1502
+ if (!portfolioData) return [];
1503
+ const tokenBalances = [];
1504
+ portfolioData.portfolio.holdings.wallets.forEach((wallet) => {
1505
+ wallet.tokens.forEach((portfolioToken) => {
1506
+ const chainId = portfolioToken.chain.chainId;
1507
+ const supportedToken = getToken(portfolioToken.address, chainId);
1508
+ const token = supportedToken ?? {
1509
+ id: portfolioToken.id,
1510
+ symbol: portfolioToken.symbol,
1511
+ name: portfolioToken.name,
1512
+ address: portfolioToken.address,
1513
+ decimals: portfolioToken.decimals,
1514
+ logoUrl: portfolioToken.logoUrl ?? void 0,
1515
+ chain: getChainByChainId(chainId) ?? {
1516
+ id: portfolioToken.chain.id,
1517
+ chainId: portfolioToken.chain.chainId,
1518
+ name: portfolioToken.chain.name ?? "",
1519
+ slug: portfolioToken.chain.slug,
1520
+ status: portfolioToken.chain.status,
1521
+ logoUrl: portfolioToken.chain.logoUrl,
1522
+ ecosystem: portfolioToken.chain.ecosystem,
1523
+ explorerUrl: portfolioToken.chain.explorerUrl
1524
+ },
1525
+ isNative: portfolioToken.isNative,
1526
+ priceUsd: portfolioToken.price ?? void 0
1527
+ };
1528
+ let amountInWei;
1529
+ try {
1530
+ amountInWei = (0, import_viem3.parseUnits)(portfolioToken.amount, token.decimals).toString();
1531
+ } catch (error2) {
1532
+ console.error("[Portfolio Balance] Failed to parse amount:", {
1533
+ amount: portfolioToken.amount,
1534
+ decimals: token.decimals,
1535
+ error: error2
1536
+ });
1537
+ amountInWei = "0";
1538
+ }
1539
+ tokenBalances.push({
1540
+ token,
1541
+ amount: amountInWei,
1542
+ source: "portfolio"
1543
+ });
1544
+ });
1545
+ });
1546
+ return tokenBalances;
1547
+ }, [portfolioData, getChainByChainId, getToken]);
1548
+ return {
1549
+ balances,
1550
+ isLoading,
1551
+ error: error ? error : null,
1552
+ refetch
1553
+ };
1554
+ }
1555
+
1556
+ // src/v2/balance/hooks/useEnsoBalances.ts
1557
+ var import_react6 = require("react");
1558
+ function useEnsoBalances({
1559
+ address,
1560
+ chainIds
1561
+ }) {
1562
+ const { getToken, isLoading: isSupportedTokensLoading } = useSupportedTokens();
1563
+ const {
1564
+ balances: ensoBalances,
1565
+ isLoading,
1566
+ error,
1567
+ refetchAll
1568
+ } = useMultiChainBalances({
1569
+ chainIds,
1570
+ address
1571
+ });
1572
+ const balances = (0, import_react6.useMemo)(() => {
1573
+ if (isSupportedTokensLoading || isLoading) return [];
1574
+ const tokenBalances = [];
1575
+ Object.entries(ensoBalances).forEach(([, chainBalances]) => {
1576
+ chainBalances.forEach((walletBalance) => {
1577
+ const chainId = walletBalance.token.chain.toString();
1578
+ const token = getToken(walletBalance.token.address, chainId);
1579
+ if (!token) return;
1580
+ tokenBalances.push({
1581
+ token,
1582
+ amount: walletBalance.amount,
1583
+ source: "enso"
1584
+ });
1585
+ });
1586
+ });
1587
+ return tokenBalances;
1588
+ }, [ensoBalances, getToken, isSupportedTokensLoading, isLoading]);
1589
+ return {
1590
+ balances,
1591
+ isLoading,
1592
+ error,
1593
+ refetch: refetchAll
1594
+ };
1595
+ }
1596
+
1597
+ // src/v2/balance/utils.ts
1598
+ var import_viem4 = require("viem");
1599
+
1600
+ // src/v2/balance/constants.ts
1601
+ var EXCLUDE_TOKEN_ADDRESS_LIST = [
1602
+ "0x7b5a0182e400b241b317e781a4e9dedfc1429822",
1603
+ // kpdUSDC
1604
+ "0x48c03b6ffd0008460f8657db1037c7e09deedfcb",
1605
+ // kpdUSDT
1606
+ "0x92c82f5f771f6a44cfa09357dd0575b81bf5f728",
1607
+ // kpdWBTC
1608
+ "0xcc6a16be713f6a714f68b0e1f4914fd3db15fbef",
1609
+ // kpdWETH
1610
+ "0xf470eb50b4a60c9b069f7fd6032532b8f5cc014d",
1611
+ // kpdUSDC
1612
+ "0xa5dab32dbe68e6fa784e1e50e4f620a0477d3896",
1613
+ // kpdUSDT
1614
+ "0xe1ac97e2616ad80f69f705ff007a4bbb3655544a",
1615
+ // kpdWBTC
1616
+ "0x77570cfecf83bc6bb08e2cd9e8537aea9f97ea2f"
1617
+ // kpdWETH
1618
+ ];
1619
+
1620
+ // src/v2/balance/utils.ts
1621
+ function getSourcePriority(source) {
1622
+ switch (source) {
1623
+ case "onchain":
1624
+ return 1 /* ONCHAIN */;
1625
+ case "portfolio":
1626
+ return 2 /* PORTFOLIO */;
1627
+ case "enso":
1628
+ return 3 /* ENSO */;
1629
+ }
1630
+ }
1631
+ function mergeBalancesByPriority(sources, receiptToken) {
1632
+ const balanceMap = /* @__PURE__ */ new Map();
1633
+ sources.forEach((sourceBalances) => {
1634
+ sourceBalances.forEach((balance) => {
1635
+ const key = `${balance.token.chain}-${balance.token.address.toLowerCase()}`;
1636
+ if (receiptToken) {
1637
+ const receiptKey = `${receiptToken.chain}-${receiptToken.address.toLowerCase()}`;
1638
+ if (key === receiptKey) {
1639
+ return;
1640
+ }
1641
+ }
1642
+ const existingBalance = balanceMap.get(key);
1643
+ if (!existingBalance || getSourcePriority(balance.source) < getSourcePriority(existingBalance.source)) {
1644
+ balanceMap.set(key, balance);
1645
+ }
1646
+ });
1647
+ });
1648
+ return Array.from(balanceMap.values()).sort((a, b) => {
1649
+ if (a.token.chain.chainId !== b.token.chain.chainId) {
1650
+ return Number(a.token.chain.chainId) - Number(b.token.chain.chainId);
1651
+ }
1652
+ const balanceA = Number((0, import_viem4.formatUnits)(BigInt(a.amount), a.token.decimals));
1653
+ const balanceB = Number((0, import_viem4.formatUnits)(BigInt(b.amount), b.token.decimals));
1654
+ return balanceB - balanceA;
1655
+ });
1656
+ }
1657
+ function filterBalancesByChains(balances, chainIds) {
1658
+ const chainSet = new Set(chainIds);
1659
+ return balances.filter((balance) => chainSet.has(Number(balance.token.chain.chainId)));
1660
+ }
1661
+ function filterNonZeroBalances(balances) {
1662
+ return balances.filter((balance) => {
1663
+ const amount = BigInt(balance.amount);
1664
+ return amount > 0n;
1665
+ });
1666
+ }
1667
+ function filterExcludedTokens(balances) {
1668
+ const excludedSet = new Set(EXCLUDE_TOKEN_ADDRESS_LIST.map((addr) => addr.toLowerCase()));
1669
+ return balances.filter((balance) => {
1670
+ const tokenAddress = balance.token.address.toLowerCase();
1671
+ return !excludedSet.has(tokenAddress);
1672
+ });
1673
+ }
1674
+
1675
+ // src/v2/balance/hooks/useBalance.ts
1676
+ function useBalance({
1677
+ address,
1678
+ chainIds,
1679
+ depositOpportunity
1680
+ }) {
1681
+ const {
1682
+ balances: onChainBalances,
1683
+ isLoading: isOnChainLoading,
1684
+ error: onChainError,
1685
+ refetch: refetchOnChain
1686
+ } = useGetOnChainBalance({
1687
+ tokens: depositOpportunity?.depositTokens || [],
1688
+ chainId: depositOpportunity?.depositTokens[0] ? Number(depositOpportunity.depositTokens[0].chain.chainId) : chainIds[0],
1689
+ address,
1690
+ enabled: !!depositOpportunity && !!address
1691
+ });
1692
+ const {
1693
+ balances: portfolioBalances,
1694
+ isLoading: isPortfolioLoading,
1695
+ error: portfolioError,
1696
+ refetch: refetchPortfolio
1697
+ } = usePortfolioBalance({
1698
+ address
1699
+ });
1700
+ const {
1701
+ balances: ensoBalances,
1702
+ isLoading: isEnsoLoading,
1703
+ error: ensoError,
1704
+ refetch: refetchEnso
1705
+ } = useEnsoBalances({
1706
+ address,
1707
+ chainIds
1708
+ });
1709
+ const consolidatedBalances = (0, import_react7.useMemo)(() => {
1710
+ const sources = [];
1711
+ if (depositOpportunity && onChainBalances.length > 0) {
1712
+ sources.push(onChainBalances);
1713
+ }
1714
+ if (portfolioBalances.length > 0) {
1715
+ sources.push(portfolioBalances);
1716
+ }
1717
+ if (ensoBalances.length > 0) {
1718
+ sources.push(ensoBalances);
1719
+ }
1720
+ const merged = mergeBalancesByPriority(sources, depositOpportunity?.receiptToken);
1721
+ const nonZero = filterNonZeroBalances(merged);
1722
+ return filterExcludedTokens(nonZero);
1723
+ }, [onChainBalances, portfolioBalances, ensoBalances, depositOpportunity]);
1724
+ const isLoading = isOnChainLoading || isPortfolioLoading || isEnsoLoading;
1725
+ const error = onChainError || portfolioError || ensoError;
1726
+ const refetchAll = () => {
1727
+ if (depositOpportunity) {
1728
+ refetchOnChain();
1729
+ }
1730
+ refetchPortfolio();
1731
+ refetchEnso();
1732
+ };
1733
+ return {
1734
+ balances: consolidatedBalances,
1735
+ isLoading,
1736
+ error,
1737
+ refetchAll
1738
+ };
1739
+ }
1740
+
1741
+ // src/v2/widget/hooks.ts
1742
+ var import_react_query11 = require("@tanstack/react-query");
1218
1743
  function useWidgetOpportunities(distributorId) {
1219
- return (0, import_react_query8.useQuery)({
1744
+ return (0, import_react_query11.useQuery)({
1220
1745
  ...widgetQueries.opportunities(distributorId),
1221
1746
  ...queryDefaults,
1222
1747
  select: (data) => {
@@ -1240,7 +1765,7 @@ function useWidgetOpportunities(distributorId) {
1240
1765
  }
1241
1766
 
1242
1767
  // src/v2/geocheck/useGeocheck.ts
1243
- var import_react_query9 = require("@tanstack/react-query");
1768
+ var import_react_query12 = require("@tanstack/react-query");
1244
1769
  var INDEXER_ENDPOINT = "https://api.turtle.xyz";
1245
1770
  async function fetchGeocheck() {
1246
1771
  const url = `${INDEXER_ENDPOINT}/turtle/geocheck`;
@@ -1256,13 +1781,13 @@ async function fetchGeocheck() {
1256
1781
  }
1257
1782
  return data;
1258
1783
  }
1259
- var geocheckQueryOptions = () => (0, import_react_query9.queryOptions)({
1784
+ var geocheckQueryOptions = () => (0, import_react_query12.queryOptions)({
1260
1785
  queryKey: ["geocheck"],
1261
1786
  queryFn: fetchGeocheck
1262
1787
  });
1263
1788
  function useGeocheck(options = {}) {
1264
1789
  const { enabled = true, staleTime, gcTime } = options;
1265
- return (0, import_react_query9.useQuery)({
1790
+ return (0, import_react_query12.useQuery)({
1266
1791
  ...geocheckQueryOptions(),
1267
1792
  enabled,
1268
1793
  staleTime: staleTime ?? 5 * 60 * 1e3,
@@ -1274,8 +1799,126 @@ function useGeocheck(options = {}) {
1274
1799
  });
1275
1800
  }
1276
1801
 
1802
+ // src/v2/swap/useSwapRoute.ts
1803
+ var import_react8 = require("react");
1804
+
1805
+ // src/v2/swap/route-processor.ts
1806
+ var import_utils4 = require("@turtleclub/utils");
1807
+ var parseEnsoStep = (kind, fromToken, toToken) => {
1808
+ return {
1809
+ in: {
1810
+ icon: fromToken.logos[0],
1811
+ symbol: fromToken.symbol
1812
+ },
1813
+ out: {
1814
+ icon: toToken.logos[0],
1815
+ symbol: toToken.symbol
1816
+ },
1817
+ amount: void 0,
1818
+ type: kind
1819
+ };
1820
+ };
1821
+ var parseApproveStep = (token, amount) => {
1822
+ return {
1823
+ in: {
1824
+ icon: token.logos[0],
1825
+ symbol: token.symbol
1826
+ },
1827
+ out: null,
1828
+ amount: (0, import_utils4.formatToken)(amount, { decimals: token.decimals }, true, false, 4),
1829
+ type: "approve"
1830
+ };
1831
+ };
1832
+ function processRouteDetails(data) {
1833
+ if (!data) return [];
1834
+ const tokens = [];
1835
+ data.steps.forEach((step) => {
1836
+ if (step.kind === "approve" && step.token) {
1837
+ const approveStep2 = parseApproveStep(step.token, step.amount);
1838
+ tokens.push(approveStep2);
1839
+ }
1840
+ if (step.kind === "enso" && step.substeps) {
1841
+ step.substeps.forEach((substep) => {
1842
+ const fromToken = substep.from[0];
1843
+ const toToken = substep.to[0];
1844
+ const step2 = parseEnsoStep(substep.kind, fromToken, toToken);
1845
+ tokens.push(step2);
1846
+ });
1847
+ }
1848
+ });
1849
+ return tokens;
1850
+ }
1851
+
1852
+ // src/v2/swap/useSwapRoute.ts
1853
+ var NATIVE_TOKEN_ADDRESS = "0x0000000000000000000000000000000000000000";
1854
+ var ENSO_NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
1855
+ function toEnsoTokenAddress(address) {
1856
+ if (address.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase()) {
1857
+ return ENSO_NATIVE_TOKEN_ADDRESS;
1858
+ }
1859
+ return address;
1860
+ }
1861
+ function useSwapRoute(isEnabled, userAddress, distributorId, options, referralCode) {
1862
+ const params = (0, import_react8.useMemo)(() => {
1863
+ if (!options || !isEnabled) return void 0;
1864
+ return {
1865
+ ...options,
1866
+ user: userAddress,
1867
+ distributor_id: distributorId,
1868
+ referral_code: referralCode
1869
+ };
1870
+ }, [options, userAddress, distributorId, referralCode]);
1871
+ const hasRequiredParams = (0, import_react8.useMemo)(() => {
1872
+ return !!(userAddress && params?.token_in && params?.token_out && params?.amount && params?.chain && params?.slippage && params?.distributor_id);
1873
+ }, [options]);
1874
+ const { data, isLoading, error } = useEarnRoute({
1875
+ params: {
1876
+ user: params?.user ?? "",
1877
+ chain: params?.chain ?? 1,
1878
+ slippage: params?.slippage ?? 1,
1879
+ distributor_id: params?.distributor_id ?? "",
1880
+ referral_code: params?.referral_code ?? "",
1881
+ id: params?.id ?? "",
1882
+ amount: params?.amount ?? "",
1883
+ token_in: toEnsoTokenAddress(params?.token_in ?? "") ?? "",
1884
+ token_out: toEnsoTokenAddress(options?.token_out ?? "") ?? ""
1885
+ },
1886
+ enabled: hasRequiredParams && isEnabled
1887
+ });
1888
+ const routeDetails = (0, import_react8.useMemo)(() => processRouteDetails(data ?? null), [data]);
1889
+ return {
1890
+ fetchedRoute: data ?? null,
1891
+ outputAmount: data?.amount_out ?? void 0,
1892
+ isReady: hasRequiredParams && data && !isLoading,
1893
+ hasRequiredParams,
1894
+ routeDetails,
1895
+ routeError: error,
1896
+ isLoadingRoute: isLoading
1897
+ };
1898
+ }
1899
+
1900
+ // src/v2/lib/turtle-provider.tsx
1901
+ var import_react9 = require("react");
1902
+ var import_jsx_runtime = require("react/jsx-runtime");
1903
+ function TurtleProvider({
1904
+ children,
1905
+ apiUrl,
1906
+ earnUrl,
1907
+ getToken,
1908
+ debug
1909
+ }) {
1910
+ const config = (0, import_react9.useMemo)(
1911
+ () => ({ apiUrl, earnUrl, getToken, debug }),
1912
+ [apiUrl, earnUrl, getToken, debug]
1913
+ );
1914
+ (0, import_react9.useEffect)(() => {
1915
+ apiClient.configure(config);
1916
+ }, [config]);
1917
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
1918
+ }
1919
+
1277
1920
  // src/v2/index.ts
1278
- var queries = (0, import_query_key_factory9.mergeQueryKeys)(
1921
+ var queries = (0, import_query_key_factory12.mergeQueryKeys)(
1279
1922
  opportunitiesQueries,
1280
1923
  earnOpportunitiesQueries,
1281
1924
  earnRouteQueries,
@@ -1283,16 +1926,22 @@ var queries = (0, import_query_key_factory9.mergeQueryKeys)(
1283
1926
  earnDepositsQueries,
1284
1927
  productsQueries,
1285
1928
  ensoBalancesQueries,
1286
- widgetQueries
1929
+ widgetQueries,
1930
+ supportedChainsQueries,
1931
+ supportedTokensQueries
1287
1932
  );
1288
1933
  // Annotate the CommonJS export names for ESM import in node:
1289
1934
  0 && (module.exports = {
1290
- AuthProvider,
1935
+ ApiError,
1936
+ BalanceSourcePriority,
1937
+ TurtleProvider,
1938
+ apiClient,
1291
1939
  approveStep,
1292
1940
  asset,
1293
1941
  assetData,
1294
1942
  assetERC20Data,
1295
1943
  assetImageData,
1944
+ balanceQueries,
1296
1945
  balanceTokenSchema,
1297
1946
  chainSchema,
1298
1947
  checkMembership,
@@ -1315,20 +1964,33 @@ var queries = (0, import_query_key_factory9.mergeQueryKeys)(
1315
1964
  earnRouteResponseSchema,
1316
1965
  ensoBalancesQueries,
1317
1966
  ensoStep,
1967
+ filterBalancesByChains,
1968
+ filterExcludedTokens,
1969
+ filterNonZeroBalances,
1318
1970
  geocheckQueryOptions,
1319
1971
  getDeposits,
1320
1972
  getEarnOpportunities,
1321
1973
  getEarnRoute,
1322
1974
  getOpportunities,
1323
1975
  getOpportunityById,
1976
+ getPortfolioBalance,
1324
1977
  getProducts,
1978
+ getSourcePriority,
1979
+ getSupportedChains,
1980
+ getSupportedTokens,
1325
1981
  getWalletBalances,
1326
1982
  getWidgetOpportunities,
1327
1983
  incentiveSchema,
1328
1984
  lendingConfigSchema,
1985
+ mergeBalancesByPriority,
1329
1986
  opportunitiesQueries,
1330
1987
  opportunitySchema,
1331
1988
  organizationSchema,
1989
+ portfolioBalanceResponseSchema,
1990
+ portfolioHoldingsSchema,
1991
+ portfolioTokenSchema,
1992
+ portfolioWalletSchema,
1993
+ processRouteDetails,
1332
1994
  productSchema,
1333
1995
  productsQueries,
1334
1996
  queries,
@@ -1336,9 +1998,14 @@ var queries = (0, import_query_key_factory9.mergeQueryKeys)(
1336
1998
  routerStep,
1337
1999
  routerSubstep,
1338
2000
  stepTx,
2001
+ supportedChainsQueries,
2002
+ supportedChainsResponseSchema,
2003
+ supportedTokenSchema,
2004
+ supportedTokensQueries,
2005
+ supportedTokensResponseSchema,
1339
2006
  swapSubstep,
1340
2007
  tokenSchema,
1341
- useAuthToken,
2008
+ useBalance,
1342
2009
  useCheckMembership,
1343
2010
  useCreateMembership,
1344
2011
  useCreateMembershipAgreement,
@@ -1347,12 +2014,18 @@ var queries = (0, import_query_key_factory9.mergeQueryKeys)(
1347
2014
  useDeposits,
1348
2015
  useEarnOpportunities,
1349
2016
  useEarnRoute,
2017
+ useEnsoBalances,
1350
2018
  useGeocheck,
2019
+ useGetOnChainBalance,
1351
2020
  useMultiChainBalances,
1352
2021
  useOpportunities,
1353
2022
  useOpportunity,
2023
+ usePortfolioBalance,
1354
2024
  useProduct,
1355
2025
  useProducts,
2026
+ useSupportedChains,
2027
+ useSupportedTokens,
2028
+ useSwapRoute,
1356
2029
  useTokenBalance,
1357
2030
  useUpdateProduct,
1358
2031
  useUploadProductLogo,