@sodax/dapp-kit 2.0.0-rc.11 → 2.0.0-rc.13
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 +5 -5
- package/dist/index.d.ts +282 -8
- package/dist/index.mjs +290 -47
- package/package.json +2 -2
- package/src/hooks/_mutationContract.test.ts +6 -1
- package/src/hooks/bitcoin/resolveBtcReadAddress.test.ts +54 -0
- package/src/hooks/bitcoin/resolveBtcReadAddress.ts +21 -0
- package/src/hooks/bitcoin/useFundTradingWallet.ts +1 -1
- package/src/hooks/bitcoin/useRadfiAuth.ts +2 -2
- package/src/hooks/bitcoin/useRadfiWithdraw.ts +2 -2
- package/src/hooks/bitcoin/useRenewUtxos.ts +2 -2
- package/src/hooks/bitcoin/useTradingWallet.ts +1 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/leverageYield/index.ts +9 -0
- package/src/hooks/leverageYield/useLeverageYieldDeposit.ts +40 -0
- package/src/hooks/leverageYield/useLeverageYieldEffectiveApr.ts +42 -0
- package/src/hooks/leverageYield/useLeverageYieldNotifySolver.ts +34 -0
- package/src/hooks/leverageYield/useLeverageYieldPosition.ts +40 -0
- package/src/hooks/leverageYield/useLeverageYieldPreviewRedeem.ts +43 -0
- package/src/hooks/leverageYield/useLeverageYieldShareBalances.ts +74 -0
- package/src/hooks/leverageYield/useLeverageYieldTotalAssets.ts +36 -0
- package/src/hooks/leverageYield/useLeverageYieldVaultSwap.ts +50 -0
- package/src/hooks/leverageYield/useLeverageYieldWithdraw.ts +40 -0
- package/src/hooks/mm/useATokensBalances.ts +7 -1
- package/src/hooks/mm/useUserFormattedSummary.ts +5 -1
- package/src/hooks/mm/useUserReservesData.ts +5 -1
- package/src/hooks/shared/index.ts +3 -0
- package/src/hooks/shared/useGetUserHubWalletAddress.ts +5 -1
- package/src/hooks/shared/useNearStorageCheck.ts +45 -0
- package/src/hooks/shared/useNearStorageGate.ts +60 -0
- package/src/hooks/shared/useRegisterNearStorage.ts +46 -0
- package/src/providers/SodaxProvider.tsx +2 -3
- package/src/utils/index.ts +1 -0
- package/src/utils/nearStorageGate.test.ts +46 -0
- package/src/utils/nearStorageGate.ts +39 -0
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useMutation, useQuery, useQueryClient, QueryClient, MutationCache } from '@tanstack/react-query';
|
|
1
|
+
import { useMutation, useQuery, useQueryClient, useQueries, QueryClient, MutationCache } from '@tanstack/react-query';
|
|
2
2
|
import { createContext, useCallback, useContext, useState, useRef, useEffect, useMemo } from 'react';
|
|
3
3
|
import { ChainKeys, RadfiApiError, normalizePsbtToBase64, ClService, Sodax } from '@sodax/sdk';
|
|
4
4
|
export * from '@sodax/sdk';
|
|
@@ -140,59 +140,76 @@ function useRequestTrustline(token) {
|
|
|
140
140
|
);
|
|
141
141
|
return { requestTrustline, isLoading, isRequested, error, data };
|
|
142
142
|
}
|
|
143
|
-
function
|
|
143
|
+
function useNearStorageCheck({
|
|
144
144
|
params,
|
|
145
145
|
queryOptions
|
|
146
146
|
} = {}) {
|
|
147
147
|
const { sodax } = useSodaxContext();
|
|
148
|
-
const
|
|
149
|
-
const
|
|
148
|
+
const token = params?.token;
|
|
149
|
+
const accountId = params?.accountId;
|
|
150
|
+
const chainId = params?.chainId;
|
|
150
151
|
return useQuery({
|
|
151
|
-
queryKey: ["shared", "
|
|
152
|
+
queryKey: ["shared", "nearStorageCheck", chainId, token, accountId],
|
|
152
153
|
queryFn: async () => {
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return await sodax.hubProvider.getUserHubWalletAddress(spokeAddress, spokeChainId);
|
|
154
|
+
if (chainId !== ChainKeys.NEAR_MAINNET) return true;
|
|
155
|
+
if (!token || !accountId) return false;
|
|
156
|
+
return sodax.spoke.near.isStorageRegistered(token, accountId);
|
|
157
157
|
},
|
|
158
|
-
enabled: !!
|
|
159
|
-
refetchInterval: false,
|
|
158
|
+
enabled: !!token && !!accountId,
|
|
160
159
|
...queryOptions
|
|
161
160
|
});
|
|
162
161
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
// Pair symbol + address: readable in devtools, unique on-chain (symbol alone
|
|
167
|
-
// can collide — e.g. scam tokens copying legitimate ticker).
|
|
168
|
-
queryKey: ["shared", "xBalances", xChainId, xTokens.map((x) => [x.symbol, x.address]), address],
|
|
169
|
-
queryFn: async () => {
|
|
170
|
-
if (!xService) return {};
|
|
171
|
-
return xService.getBalances(address, xTokens);
|
|
172
|
-
},
|
|
173
|
-
enabled: !!xService && !!address && xTokens.length > 0,
|
|
174
|
-
refetchInterval: REFETCH_INTERVAL_MS
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
function useXBalances({
|
|
178
|
-
params,
|
|
179
|
-
queryOptions
|
|
162
|
+
function useRegisterNearStorage({
|
|
163
|
+
mutationOptions
|
|
180
164
|
} = {}) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
165
|
+
const { sodax } = useSodaxContext();
|
|
166
|
+
const queryClient = useQueryClient();
|
|
167
|
+
return useSafeMutation({
|
|
168
|
+
mutationKey: ["shared", "registerNearStorage"],
|
|
169
|
+
...mutationOptions,
|
|
170
|
+
mutationFn: async ({ token, accountId, walletProvider, deposit }) => sodax.spoke.near.registerStorage({ token, accountId, walletProvider, deposit, raw: false }),
|
|
171
|
+
onSuccess: async (data, vars, ctx) => {
|
|
172
|
+
queryClient.invalidateQueries({
|
|
173
|
+
queryKey: ["shared", "nearStorageCheck", ChainKeys.NEAR_MAINNET, vars.token, vars.accountId]
|
|
174
|
+
});
|
|
175
|
+
await mutationOptions?.onSuccess?.(data, vars, ctx);
|
|
176
|
+
}
|
|
189
177
|
});
|
|
190
178
|
}
|
|
179
|
+
function resolveNearStorageGate(chainKey, check) {
|
|
180
|
+
const isNear = chainKey === ChainKeys.NEAR_MAINNET;
|
|
181
|
+
const isChecking = check.isLoading;
|
|
182
|
+
const isRegistered = check.data;
|
|
183
|
+
return {
|
|
184
|
+
isNear,
|
|
185
|
+
needsRegistration: isNear && !isChecking && isRegistered === false,
|
|
186
|
+
blocksAction: isNear && (isChecking || isRegistered === false)
|
|
187
|
+
};
|
|
188
|
+
}
|
|
191
189
|
|
|
192
|
-
// src/hooks/
|
|
193
|
-
function
|
|
194
|
-
|
|
195
|
-
|
|
190
|
+
// src/hooks/shared/useNearStorageGate.ts
|
|
191
|
+
function useNearStorageGate({
|
|
192
|
+
dstChainKey,
|
|
193
|
+
token,
|
|
194
|
+
accountId,
|
|
195
|
+
walletProvider
|
|
196
|
+
}) {
|
|
197
|
+
const nearWalletProvider = walletProvider?.chainType === "NEAR" ? walletProvider : void 0;
|
|
198
|
+
const storageCheck = useNearStorageCheck({ params: { token, accountId, chainId: dstChainKey } });
|
|
199
|
+
const { mutateAsyncSafe: register, isPending: isRegistering } = useRegisterNearStorage();
|
|
200
|
+
const { isNear, needsRegistration, blocksAction } = resolveNearStorageGate(dstChainKey, storageCheck);
|
|
201
|
+
const registerStorage = async () => {
|
|
202
|
+
if (!nearWalletProvider || !token || !accountId) return void 0;
|
|
203
|
+
return register({ token, accountId, walletProvider: nearWalletProvider });
|
|
204
|
+
};
|
|
205
|
+
return {
|
|
206
|
+
isNear,
|
|
207
|
+
needsRegistration,
|
|
208
|
+
blocksAction,
|
|
209
|
+
isChecking: storageCheck.isLoading,
|
|
210
|
+
isRegistering,
|
|
211
|
+
registerStorage
|
|
212
|
+
};
|
|
196
213
|
}
|
|
197
214
|
var SESSION_KEY = (address) => `radfi_session_${address}`;
|
|
198
215
|
function saveRadfiSession(address, session) {
|
|
@@ -254,7 +271,7 @@ function useRadfiAuth({
|
|
|
254
271
|
clearRadfiSession(walletAddress);
|
|
255
272
|
}
|
|
256
273
|
throw new Error(
|
|
257
|
-
"This wallet is already registered with
|
|
274
|
+
"This wallet is already registered with Bound Exchange from another session. Please clear your browser storage for this site and try again, or wait for the previous session to expire."
|
|
258
275
|
);
|
|
259
276
|
}
|
|
260
277
|
throw err;
|
|
@@ -262,6 +279,71 @@ function useRadfiAuth({
|
|
|
262
279
|
}
|
|
263
280
|
});
|
|
264
281
|
}
|
|
282
|
+
|
|
283
|
+
// src/hooks/bitcoin/resolveBtcReadAddress.ts
|
|
284
|
+
function resolveBtcReadAddress(chainKey, address) {
|
|
285
|
+
if (chainKey !== ChainKeys.BITCOIN_MAINNET) return address;
|
|
286
|
+
return loadRadfiSession(address)?.tradingAddress ?? address;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/hooks/shared/useGetUserHubWalletAddress.ts
|
|
290
|
+
function useGetUserHubWalletAddress({
|
|
291
|
+
params,
|
|
292
|
+
queryOptions
|
|
293
|
+
} = {}) {
|
|
294
|
+
const { sodax } = useSodaxContext();
|
|
295
|
+
const spokeChainId = params?.spokeChainId;
|
|
296
|
+
const spokeAddress = params?.spokeAddress;
|
|
297
|
+
return useQuery({
|
|
298
|
+
queryKey: ["shared", "userHubWalletAddress", spokeChainId, spokeAddress],
|
|
299
|
+
queryFn: async () => {
|
|
300
|
+
if (!spokeChainId || !spokeAddress) {
|
|
301
|
+
throw new Error("Spoke chain id and address are required");
|
|
302
|
+
}
|
|
303
|
+
return await sodax.hubProvider.getUserHubWalletAddress(
|
|
304
|
+
resolveBtcReadAddress(spokeChainId, spokeAddress),
|
|
305
|
+
spokeChainId
|
|
306
|
+
);
|
|
307
|
+
},
|
|
308
|
+
enabled: !!spokeChainId && !!spokeAddress,
|
|
309
|
+
refetchInterval: false,
|
|
310
|
+
...queryOptions
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
var REFETCH_INTERVAL_MS = 5e3;
|
|
314
|
+
function getXBalancesQueryOptions({ xService, xChainId, xTokens, address }) {
|
|
315
|
+
return {
|
|
316
|
+
// Pair symbol + address: readable in devtools, unique on-chain (symbol alone
|
|
317
|
+
// can collide — e.g. scam tokens copying legitimate ticker).
|
|
318
|
+
queryKey: ["shared", "xBalances", xChainId, xTokens.map((x) => [x.symbol, x.address]), address],
|
|
319
|
+
queryFn: async () => {
|
|
320
|
+
if (!xService) return {};
|
|
321
|
+
return xService.getBalances(address, xTokens);
|
|
322
|
+
},
|
|
323
|
+
enabled: !!xService && !!address && xTokens.length > 0,
|
|
324
|
+
refetchInterval: REFETCH_INTERVAL_MS
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
function useXBalances({
|
|
328
|
+
params,
|
|
329
|
+
queryOptions
|
|
330
|
+
} = {}) {
|
|
331
|
+
return useQuery({
|
|
332
|
+
...getXBalancesQueryOptions({
|
|
333
|
+
xService: params?.xService,
|
|
334
|
+
xChainId: params?.xChainId,
|
|
335
|
+
xTokens: params?.xTokens ?? [],
|
|
336
|
+
address: params?.address
|
|
337
|
+
}),
|
|
338
|
+
...queryOptions
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/hooks/provider/useHubProvider.ts
|
|
343
|
+
function useHubProvider() {
|
|
344
|
+
const { sodax } = useSodaxContext();
|
|
345
|
+
return sodax.hubProvider;
|
|
346
|
+
}
|
|
265
347
|
var REFRESH_INTERVAL = 5 * 60 * 1e3;
|
|
266
348
|
function useRadfiSession(walletProvider) {
|
|
267
349
|
const { sodax } = useSodaxContext();
|
|
@@ -432,7 +514,7 @@ function useRenewUtxos({
|
|
|
432
514
|
const session = loadRadfiSession(userAddress);
|
|
433
515
|
const accessToken = session?.accessToken || radfi.accessToken;
|
|
434
516
|
if (!accessToken) {
|
|
435
|
-
throw new Error("
|
|
517
|
+
throw new Error("Bound Exchange authentication required. Please login first.");
|
|
436
518
|
}
|
|
437
519
|
const buildResult = await radfi.buildRenewUtxoTransaction({ userAddress, txIdVouts }, accessToken);
|
|
438
520
|
const signedTx = await walletProvider.signTransaction(buildResult.base64Psbt, false);
|
|
@@ -460,7 +542,7 @@ function useRadfiWithdraw({
|
|
|
460
542
|
const session = loadRadfiSession(userAddress);
|
|
461
543
|
const accessToken = session?.accessToken || radfi.accessToken;
|
|
462
544
|
if (!accessToken) {
|
|
463
|
-
throw new Error("
|
|
545
|
+
throw new Error("Bound Exchange authentication required. Please login first.");
|
|
464
546
|
}
|
|
465
547
|
const buildResult = await radfi.withdrawToUser({ userAddress, amount, tokenId, withdrawTo }, accessToken);
|
|
466
548
|
const signedTx = await walletProvider.signTransaction(buildResult.base64Psbt, false);
|
|
@@ -581,7 +663,10 @@ function useUserReservesData({
|
|
|
581
663
|
if (!spokeChainKey || !userAddress) {
|
|
582
664
|
throw new Error("spokeChainKey and userAddress are required");
|
|
583
665
|
}
|
|
584
|
-
return sodax.moneyMarket.data.getUserReservesData(
|
|
666
|
+
return sodax.moneyMarket.data.getUserReservesData(
|
|
667
|
+
spokeChainKey,
|
|
668
|
+
resolveBtcReadAddress(spokeChainKey, userAddress)
|
|
669
|
+
);
|
|
585
670
|
},
|
|
586
671
|
enabled: !!spokeChainKey && !!userAddress,
|
|
587
672
|
refetchInterval: 5e3,
|
|
@@ -707,7 +792,10 @@ function useATokensBalances({
|
|
|
707
792
|
throw new Error(`Invalid aToken address: ${aToken}`);
|
|
708
793
|
}
|
|
709
794
|
}
|
|
710
|
-
const hubWalletAddress = await sodax.hubProvider.getUserHubWalletAddress(
|
|
795
|
+
const hubWalletAddress = await sodax.hubProvider.getUserHubWalletAddress(
|
|
796
|
+
resolveBtcReadAddress(spokeChainKey, userAddress),
|
|
797
|
+
spokeChainKey
|
|
798
|
+
);
|
|
711
799
|
return sodax.moneyMarket.data.getATokensBalances(aTokens, hubWalletAddress);
|
|
712
800
|
},
|
|
713
801
|
enabled: aTokens.length > 0 && !!spokeChainKey && !!userAddress,
|
|
@@ -742,7 +830,10 @@ function useUserFormattedSummary({
|
|
|
742
830
|
}
|
|
743
831
|
const [reserves, userReserves] = await Promise.all([
|
|
744
832
|
sodax.moneyMarket.data.getReservesHumanized(),
|
|
745
|
-
sodax.moneyMarket.data.getUserReservesHumanized(
|
|
833
|
+
sodax.moneyMarket.data.getUserReservesHumanized(
|
|
834
|
+
spokeChainKey,
|
|
835
|
+
resolveBtcReadAddress(spokeChainKey, userAddress)
|
|
836
|
+
)
|
|
746
837
|
]);
|
|
747
838
|
const formattedReserves = sodax.moneyMarket.data.formatReservesUSD(
|
|
748
839
|
sodax.moneyMarket.data.buildReserveDataWithPrice(reserves)
|
|
@@ -2482,6 +2573,158 @@ function useClaimRewards({
|
|
|
2482
2573
|
}
|
|
2483
2574
|
});
|
|
2484
2575
|
}
|
|
2576
|
+
function useLeverageYieldEffectiveApr({
|
|
2577
|
+
params,
|
|
2578
|
+
queryOptions
|
|
2579
|
+
} = {}) {
|
|
2580
|
+
const { sodax } = useSodaxContext();
|
|
2581
|
+
const vault = params?.vault;
|
|
2582
|
+
return useQuery({
|
|
2583
|
+
queryKey: ["leverageYield", "effectiveApr", vault],
|
|
2584
|
+
queryFn: async () => {
|
|
2585
|
+
if (!vault) throw new Error("vault is required");
|
|
2586
|
+
const result = await sodax.leverageYield.getEffectiveApr(vault);
|
|
2587
|
+
if (!result.ok) throw result.error;
|
|
2588
|
+
return result.value;
|
|
2589
|
+
},
|
|
2590
|
+
enabled: !!vault,
|
|
2591
|
+
refetchInterval: 6e4,
|
|
2592
|
+
...queryOptions
|
|
2593
|
+
});
|
|
2594
|
+
}
|
|
2595
|
+
function useLeverageYieldPosition({
|
|
2596
|
+
params,
|
|
2597
|
+
queryOptions
|
|
2598
|
+
} = {}) {
|
|
2599
|
+
const { sodax } = useSodaxContext();
|
|
2600
|
+
const vault = params?.vault;
|
|
2601
|
+
return useQuery({
|
|
2602
|
+
queryKey: ["leverageYield", "position", vault],
|
|
2603
|
+
queryFn: async () => {
|
|
2604
|
+
if (!vault) throw new Error("vault is required");
|
|
2605
|
+
const result = await sodax.leverageYield.getPosition(vault);
|
|
2606
|
+
if (!result.ok) throw result.error;
|
|
2607
|
+
return result.value;
|
|
2608
|
+
},
|
|
2609
|
+
enabled: !!vault,
|
|
2610
|
+
refetchInterval: 3e4,
|
|
2611
|
+
...queryOptions
|
|
2612
|
+
});
|
|
2613
|
+
}
|
|
2614
|
+
function useLeverageYieldTotalAssets({
|
|
2615
|
+
params,
|
|
2616
|
+
queryOptions
|
|
2617
|
+
} = {}) {
|
|
2618
|
+
const { sodax } = useSodaxContext();
|
|
2619
|
+
const vault = params?.vault;
|
|
2620
|
+
return useQuery({
|
|
2621
|
+
queryKey: ["leverageYield", "totalAssets", vault],
|
|
2622
|
+
queryFn: async () => {
|
|
2623
|
+
if (!vault) throw new Error("vault is required");
|
|
2624
|
+
const result = await sodax.leverageYield.getTotalAssets(vault);
|
|
2625
|
+
if (!result.ok) throw result.error;
|
|
2626
|
+
return result.value;
|
|
2627
|
+
},
|
|
2628
|
+
enabled: !!vault,
|
|
2629
|
+
refetchInterval: 6e4,
|
|
2630
|
+
...queryOptions
|
|
2631
|
+
});
|
|
2632
|
+
}
|
|
2633
|
+
function useLeverageYieldPreviewRedeem({
|
|
2634
|
+
params,
|
|
2635
|
+
queryOptions
|
|
2636
|
+
} = {}) {
|
|
2637
|
+
const { sodax } = useSodaxContext();
|
|
2638
|
+
const vault = params?.vault;
|
|
2639
|
+
const shares = params?.shares;
|
|
2640
|
+
return useQuery({
|
|
2641
|
+
queryKey: ["leverageYield", "previewRedeem", vault, shares?.toString()],
|
|
2642
|
+
queryFn: async () => {
|
|
2643
|
+
if (!vault || shares === void 0) throw new Error("vault and shares are required");
|
|
2644
|
+
const result = await sodax.leverageYield.previewRedeem(vault, shares);
|
|
2645
|
+
if (!result.ok) throw result.error;
|
|
2646
|
+
return result.value;
|
|
2647
|
+
},
|
|
2648
|
+
enabled: !!vault && shares !== void 0,
|
|
2649
|
+
refetchInterval: 6e4,
|
|
2650
|
+
...queryOptions
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
function useLeverageYieldShareBalances({
|
|
2654
|
+
params,
|
|
2655
|
+
queryOptions
|
|
2656
|
+
} = {}) {
|
|
2657
|
+
const { sodax } = useSodaxContext();
|
|
2658
|
+
const vault = params?.vault;
|
|
2659
|
+
const holders = params?.holders;
|
|
2660
|
+
return useQueries({
|
|
2661
|
+
queries: (holders ?? []).map(({ chainKey, address }) => ({
|
|
2662
|
+
queryKey: ["leverageYield", "shareBalance", vault, chainKey, address],
|
|
2663
|
+
refetchInterval: 15e3,
|
|
2664
|
+
...queryOptions,
|
|
2665
|
+
enabled: !!vault,
|
|
2666
|
+
queryFn: async () => {
|
|
2667
|
+
if (!vault) throw new Error("vault is required");
|
|
2668
|
+
const holder = await sodax.hubProvider.getUserHubWalletAddress(address, chainKey);
|
|
2669
|
+
const result = await sodax.leverageYield.getShareBalance(vault, holder);
|
|
2670
|
+
if (!result.ok) throw result.error;
|
|
2671
|
+
return { chainKey, holder, shares: result.value };
|
|
2672
|
+
}
|
|
2673
|
+
}))
|
|
2674
|
+
});
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
// src/hooks/leverageYield/useLeverageYieldDeposit.ts
|
|
2678
|
+
function useLeverageYieldDeposit({
|
|
2679
|
+
mutationOptions
|
|
2680
|
+
} = {}) {
|
|
2681
|
+
const { sodax } = useSodaxContext();
|
|
2682
|
+
return useSafeMutation({
|
|
2683
|
+
mutationKey: ["leverageYield", "deposit"],
|
|
2684
|
+
...mutationOptions,
|
|
2685
|
+
mutationFn: async (vars) => unwrapResult(await sodax.leverageYield.deposit(vars))
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
// src/hooks/leverageYield/useLeverageYieldNotifySolver.ts
|
|
2690
|
+
function useLeverageYieldNotifySolver({
|
|
2691
|
+
mutationOptions
|
|
2692
|
+
} = {}) {
|
|
2693
|
+
const { sodax } = useSodaxContext();
|
|
2694
|
+
return useSafeMutation({
|
|
2695
|
+
mutationKey: ["leverageYield", "notifySolver"],
|
|
2696
|
+
...mutationOptions,
|
|
2697
|
+
mutationFn: async (vars) => unwrapResult(await sodax.leverageYield.notifySolver(vars))
|
|
2698
|
+
});
|
|
2699
|
+
}
|
|
2700
|
+
function useLeverageYieldVaultSwap({
|
|
2701
|
+
mutationOptions
|
|
2702
|
+
} = {}) {
|
|
2703
|
+
const { sodax } = useSodaxContext();
|
|
2704
|
+
const queryClient = useQueryClient();
|
|
2705
|
+
return useSafeMutation({
|
|
2706
|
+
mutationKey: ["leverageYield", "vaultSwap"],
|
|
2707
|
+
...mutationOptions,
|
|
2708
|
+
mutationFn: async (vars) => unwrapResult(await sodax.leverageYield.vaultSwap({ ...vars, raw: false })),
|
|
2709
|
+
onSuccess: async (data, vars, ctx) => {
|
|
2710
|
+
queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", vars.params.srcChainKey] });
|
|
2711
|
+
queryClient.invalidateQueries({ queryKey: ["shared", "xBalances", vars.params.dstChainKey] });
|
|
2712
|
+
await mutationOptions?.onSuccess?.(data, vars, ctx);
|
|
2713
|
+
}
|
|
2714
|
+
});
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
// src/hooks/leverageYield/useLeverageYieldWithdraw.ts
|
|
2718
|
+
function useLeverageYieldWithdraw({
|
|
2719
|
+
mutationOptions
|
|
2720
|
+
} = {}) {
|
|
2721
|
+
const { sodax } = useSodaxContext();
|
|
2722
|
+
return useSafeMutation({
|
|
2723
|
+
mutationKey: ["leverageYield", "withdraw"],
|
|
2724
|
+
...mutationOptions,
|
|
2725
|
+
mutationFn: async (vars) => unwrapResult(await sodax.leverageYield.withdraw(vars))
|
|
2726
|
+
});
|
|
2727
|
+
}
|
|
2485
2728
|
var SodaxProvider = ({ children, config }) => {
|
|
2486
2729
|
const sodax = useMemo(() => new Sodax(config), [config]);
|
|
2487
2730
|
const contextValue = useMemo(() => ({ sodax }), [sodax]);
|
|
@@ -2513,4 +2756,4 @@ function createSodaxQueryClient({
|
|
|
2513
2756
|
});
|
|
2514
2757
|
}
|
|
2515
2758
|
|
|
2516
|
-
export { SodaxProvider, clearRadfiSession, createDecreaseLiquidityParamsProps, createDepositParamsProps, createSodaxQueryClient, createSupplyLiquidityParamsProps, createWithdrawParamsProps, getXBalancesQueryOptions, loadRadfiSession, saveRadfiSession, toResult, unwrapResult, useAToken, useATokensBalances, useApproveToken, useBackendAllMoneyMarketAssets, useBackendAllMoneyMarketBorrowers, useBackendIntentByHash, useBackendIntentByTxHash, useBackendMoneyMarketAsset, useBackendMoneyMarketAssetBorrowers, useBackendMoneyMarketAssetSuppliers, useBackendMoneyMarketPosition, useBackendOrderbook, useBackendSubmitSwapTx, useBackendSubmitSwapTxStatus, useBackendUserIntents, useBitcoinBalance, useBorrow, useBridge, useBridgeAllowance, useBridgeApprove, useCancelLimitOrder, useCancelSwap, useCancelUnstake, useClaim, useClaimRewards, useConvertedAssets, useCreateDecreaseLiquidityParams, useCreateDepositParams, useCreateLimitOrder, useCreateSupplyLiquidityParams, useCreateWithdrawParams, useDecreaseLiquidity, useDeriveUserWalletAddress, useDexAllowance, useDexApprove, useDexDeposit, useDexWithdraw, useEstimateGas, useExpiredUtxos, useFeeClaimSwap, useFetchAssetsBalances, useFundTradingWallet, useGetAutoSwapPreferences, useGetBridgeableAmount, useGetBridgeableTokens, useGetUserHubWalletAddress, useHubAssetBalances, useHubProvider, useInstantUnstake, useInstantUnstakeAllowance, useInstantUnstakeApprove, useInstantUnstakeRatio, useIsTokenApproved, useLiquidityAmounts, useMMAllowance, useMMApprove, useMigrateBaln, useMigrateIcxToSoda, useMigratebnUSD, useMigrationAllowance, useMigrationApprove, usePoolBalances, usePoolData, usePools, usePositionInfo, useQuote, useRadfiAuth, useRadfiSession, useRadfiWithdraw, useRenewUtxos, useRepay, useRequestTrustline, useReservesData, useReservesHumanized, useReservesList, useReservesUsdFormat, useRevertMigrateSodaToIcx, useSafeMutation, useSetSwapPreference, useSodaxContext, useStake, useStakeAllowance, useStakeApprove, useStakeRatio, useStakingConfig, useStakingInfo, useStatus, useStellarTrustlineCheck, useSupply, useSupplyLiquidity, useSwap, useSwapAllowance, useSwapApprove, useTradingWallet, useTradingWalletBalance, useUnstake, useUnstakeAllowance, useUnstakeApprove, useUnstakingInfo, useUnstakingInfoWithPenalty, useUserFormattedSummary, useUserReservesData, useWithdraw, useWithdrawHubAsset, useXBalances };
|
|
2759
|
+
export { SodaxProvider, clearRadfiSession, createDecreaseLiquidityParamsProps, createDepositParamsProps, createSodaxQueryClient, createSupplyLiquidityParamsProps, createWithdrawParamsProps, getXBalancesQueryOptions, loadRadfiSession, resolveNearStorageGate, saveRadfiSession, toResult, unwrapResult, useAToken, useATokensBalances, useApproveToken, useBackendAllMoneyMarketAssets, useBackendAllMoneyMarketBorrowers, useBackendIntentByHash, useBackendIntentByTxHash, useBackendMoneyMarketAsset, useBackendMoneyMarketAssetBorrowers, useBackendMoneyMarketAssetSuppliers, useBackendMoneyMarketPosition, useBackendOrderbook, useBackendSubmitSwapTx, useBackendSubmitSwapTxStatus, useBackendUserIntents, useBitcoinBalance, useBorrow, useBridge, useBridgeAllowance, useBridgeApprove, useCancelLimitOrder, useCancelSwap, useCancelUnstake, useClaim, useClaimRewards, useConvertedAssets, useCreateDecreaseLiquidityParams, useCreateDepositParams, useCreateLimitOrder, useCreateSupplyLiquidityParams, useCreateWithdrawParams, useDecreaseLiquidity, useDeriveUserWalletAddress, useDexAllowance, useDexApprove, useDexDeposit, useDexWithdraw, useEstimateGas, useExpiredUtxos, useFeeClaimSwap, useFetchAssetsBalances, useFundTradingWallet, useGetAutoSwapPreferences, useGetBridgeableAmount, useGetBridgeableTokens, useGetUserHubWalletAddress, useHubAssetBalances, useHubProvider, useInstantUnstake, useInstantUnstakeAllowance, useInstantUnstakeApprove, useInstantUnstakeRatio, useIsTokenApproved, useLeverageYieldDeposit, useLeverageYieldEffectiveApr, useLeverageYieldNotifySolver, useLeverageYieldPosition, useLeverageYieldPreviewRedeem, useLeverageYieldShareBalances, useLeverageYieldTotalAssets, useLeverageYieldVaultSwap, useLeverageYieldWithdraw, useLiquidityAmounts, useMMAllowance, useMMApprove, useMigrateBaln, useMigrateIcxToSoda, useMigratebnUSD, useMigrationAllowance, useMigrationApprove, useNearStorageCheck, useNearStorageGate, usePoolBalances, usePoolData, usePools, usePositionInfo, useQuote, useRadfiAuth, useRadfiSession, useRadfiWithdraw, useRegisterNearStorage, useRenewUtxos, useRepay, useRequestTrustline, useReservesData, useReservesHumanized, useReservesList, useReservesUsdFormat, useRevertMigrateSodaToIcx, useSafeMutation, useSetSwapPreference, useSodaxContext, useStake, useStakeAllowance, useStakeApprove, useStakeRatio, useStakingConfig, useStakingInfo, useStatus, useStellarTrustlineCheck, useSupply, useSupplyLiquidity, useSwap, useSwapAllowance, useSwapApprove, useTradingWallet, useTradingWalletBalance, useUnstake, useUnstakeAllowance, useUnstakeApprove, useUnstakingInfo, useUnstakingInfoWithPenalty, useUserFormattedSummary, useUserReservesData, useWithdraw, useWithdrawHubAsset, useXBalances };
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
7
|
"license": "MIT",
|
|
8
|
-
"version": "2.0.0-rc.
|
|
8
|
+
"version": "2.0.0-rc.13",
|
|
9
9
|
"description": "React hooks for building dApps on the SODAX cross-chain DeFi platform",
|
|
10
10
|
"keywords": [
|
|
11
11
|
"sodax",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"viem": "2.29.2",
|
|
37
|
-
"@sodax/sdk": "2.0.0-rc.
|
|
37
|
+
"@sodax/sdk": "2.0.0-rc.13"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@arethetypeswrong/cli": "0.17.4",
|
|
@@ -7,7 +7,7 @@ const HOOKS_DIR = resolve(fileURLToPath(import.meta.url), '..');
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Manifest of every mutation hook. `nativeThrow: true` marks hooks whose underlying SDK methods
|
|
10
|
-
* throw natively (
|
|
10
|
+
* throw natively (Bound Exchange APIs) and so don't need `unwrapResult`.
|
|
11
11
|
*
|
|
12
12
|
* To add a new mutation hook, add its path here. The friction is intentional — it forces explicit
|
|
13
13
|
* registration so the contract is enforced from day one.
|
|
@@ -26,6 +26,10 @@ const HOOKS: Array<{ path: string; nativeThrow?: true }> = [
|
|
|
26
26
|
{ path: 'dex/useDexDeposit.ts' },
|
|
27
27
|
{ path: 'dex/useDexWithdraw.ts' },
|
|
28
28
|
{ path: 'dex/useSupplyLiquidity.ts' },
|
|
29
|
+
{ path: 'leverageYield/useLeverageYieldDeposit.ts' },
|
|
30
|
+
{ path: 'leverageYield/useLeverageYieldNotifySolver.ts' },
|
|
31
|
+
{ path: 'leverageYield/useLeverageYieldVaultSwap.ts' },
|
|
32
|
+
{ path: 'leverageYield/useLeverageYieldWithdraw.ts' },
|
|
29
33
|
{ path: 'migrate/useMigrateBaln.ts' },
|
|
30
34
|
{ path: 'migrate/useMigrateIcxToSoda.ts' },
|
|
31
35
|
{ path: 'migrate/useMigratebnUSD.ts' },
|
|
@@ -41,6 +45,7 @@ const HOOKS: Array<{ path: string; nativeThrow?: true }> = [
|
|
|
41
45
|
{ path: 'partner/useSetSwapPreference.ts' },
|
|
42
46
|
{ path: 'recovery/useWithdrawHubAsset.ts' },
|
|
43
47
|
{ path: 'shared/useEstimateGas.ts' },
|
|
48
|
+
{ path: 'shared/useRegisterNearStorage.ts', nativeThrow: true },
|
|
44
49
|
{ path: 'staking/useCancelUnstake.ts' },
|
|
45
50
|
{ path: 'staking/useClaim.ts' },
|
|
46
51
|
{ path: 'staking/useInstantUnstake.ts' },
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ChainKeys } from '@sodax/sdk';
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import type { RadfiSession } from './useRadfiAuth.js';
|
|
4
|
+
import { loadRadfiSession } from './useRadfiAuth.js';
|
|
5
|
+
import { resolveBtcReadAddress } from './resolveBtcReadAddress.js';
|
|
6
|
+
|
|
7
|
+
// resolveBtcReadAddress reads the Bound Exchange session via loadRadfiSession (localStorage-backed).
|
|
8
|
+
// The dapp-kit test env is `node` (no localStorage), so mock the session lookup to drive each branch.
|
|
9
|
+
vi.mock('./useRadfiAuth.js', () => ({
|
|
10
|
+
loadRadfiSession: vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const mockLoadRadfiSession = vi.mocked(loadRadfiSession);
|
|
14
|
+
|
|
15
|
+
const PERSONAL_ADDRESS = 'bc1qpersonal00000000000000000000000000000';
|
|
16
|
+
const TRADING_ADDRESS = 'bc1qtrading000000000000000000000000000000';
|
|
17
|
+
|
|
18
|
+
const session = (tradingAddress: string): RadfiSession => ({
|
|
19
|
+
accessToken: 'access',
|
|
20
|
+
refreshToken: 'refresh',
|
|
21
|
+
tradingAddress,
|
|
22
|
+
publicKey: 'pub',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('resolveBtcReadAddress', () => {
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
mockLoadRadfiSession.mockReset();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('passes the address through unchanged for non-Bitcoin chains (no session lookup)', () => {
|
|
31
|
+
const result = resolveBtcReadAddress(ChainKeys.ARBITRUM_MAINNET, PERSONAL_ADDRESS);
|
|
32
|
+
|
|
33
|
+
expect(result).toBe(PERSONAL_ADDRESS);
|
|
34
|
+
expect(mockLoadRadfiSession).not.toHaveBeenCalled();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('returns the trading address from the local session for Bitcoin', () => {
|
|
38
|
+
mockLoadRadfiSession.mockReturnValue(session(TRADING_ADDRESS));
|
|
39
|
+
|
|
40
|
+
const result = resolveBtcReadAddress(ChainKeys.BITCOIN_MAINNET, PERSONAL_ADDRESS);
|
|
41
|
+
|
|
42
|
+
expect(result).toBe(TRADING_ADDRESS);
|
|
43
|
+
// The session is keyed by the personal address.
|
|
44
|
+
expect(mockLoadRadfiSession).toHaveBeenCalledWith(PERSONAL_ADDRESS);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('falls back to the personal address for Bitcoin when there is no session', () => {
|
|
48
|
+
mockLoadRadfiSession.mockReturnValue(null);
|
|
49
|
+
|
|
50
|
+
const result = resolveBtcReadAddress(ChainKeys.BITCOIN_MAINNET, PERSONAL_ADDRESS);
|
|
51
|
+
|
|
52
|
+
expect(result).toBe(PERSONAL_ADDRESS);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ChainKeys, type SpokeChainKey } from '@sodax/sdk';
|
|
2
|
+
import { loadRadfiSession } from './useRadfiAuth.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a spoke address for READ queries (positions, balances, hub-wallet display).
|
|
6
|
+
*
|
|
7
|
+
* Bitcoin positions live under the Bound Exchange trading-wallet-derived hub wallet, so for Bitcoin we
|
|
8
|
+
* resolve to the trading address from the locally-persisted Bound Exchange session. This is intentionally
|
|
9
|
+
* a LOCAL lookup (no network call): a transient Bound Exchange API outage can never make a real position
|
|
10
|
+
* read as empty, and reads never throw "Trading wallet not found". When there is no session, the
|
|
11
|
+
* user has no Bitcoin position to show, so the original (personal) address is returned — deriving
|
|
12
|
+
* an empty hub wallet, which is correct. Non-Bitcoin chains pass through unchanged.
|
|
13
|
+
*
|
|
14
|
+
* The WRITE path resolves the trading address authoritatively in the SDK
|
|
15
|
+
* (`getEffectiveWalletAddress`, called from the money-market create*Intent flows); reads
|
|
16
|
+
* deliberately use this lighter, fail-safe path.
|
|
17
|
+
*/
|
|
18
|
+
export function resolveBtcReadAddress(chainKey: SpokeChainKey, address: string): string {
|
|
19
|
+
if (chainKey !== ChainKeys.BITCOIN_MAINNET) return address;
|
|
20
|
+
return loadRadfiSession(address)?.tradingAddress ?? address;
|
|
21
|
+
}
|
|
@@ -11,7 +11,7 @@ export type UseFundTradingWalletVars = {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* React hook for funding the user's
|
|
14
|
+
* React hook for funding the user's Bound Exchange trading wallet from their personal Bitcoin wallet.
|
|
15
15
|
* Pure mutation: pass `{ amount, walletProvider }` to `mutate({...})`. Returns the broadcast tx
|
|
16
16
|
* id on success.
|
|
17
17
|
*/
|
|
@@ -45,7 +45,7 @@ export function clearRadfiSession(address: string): void {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
* React hook for authenticating with
|
|
48
|
+
* React hook for authenticating with Bound Exchange via BIP322-signed message. Pure mutation: pass
|
|
49
49
|
* `{ walletProvider }` to `mutate({...})`. The hook itself takes no arguments other than the
|
|
50
50
|
* structural `mutationOptions` slot.
|
|
51
51
|
*/
|
|
@@ -95,7 +95,7 @@ export function useRadfiAuth({
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
throw new Error(
|
|
98
|
-
'This wallet is already registered with
|
|
98
|
+
'This wallet is already registered with Bound Exchange from another session. ' +
|
|
99
99
|
'Please clear your browser storage for this site and try again, ' +
|
|
100
100
|
'or wait for the previous session to expire.',
|
|
101
101
|
);
|
|
@@ -19,7 +19,7 @@ type WithdrawResult = {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* React hook for withdrawing BTC from the user's
|
|
22
|
+
* React hook for withdrawing BTC from the user's Bound Exchange trading wallet back to their personal
|
|
23
23
|
* Bitcoin wallet. Pure mutation: pass all inputs (including the wallet provider) to
|
|
24
24
|
* `mutate({...})`.
|
|
25
25
|
*/
|
|
@@ -44,7 +44,7 @@ export function useRadfiWithdraw({
|
|
|
44
44
|
const accessToken = session?.accessToken || radfi.accessToken;
|
|
45
45
|
|
|
46
46
|
if (!accessToken) {
|
|
47
|
-
throw new Error('
|
|
47
|
+
throw new Error('Bound Exchange authentication required. Please login first.');
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const buildResult = await radfi.withdrawToUser({ userAddress, amount, tokenId, withdrawTo }, accessToken);
|
|
@@ -12,7 +12,7 @@ export type UseRenewUtxosVars = {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* React hook for renewing expired UTXOs in the user's
|
|
15
|
+
* React hook for renewing expired UTXOs in the user's Bound Exchange trading wallet. Pure mutation: pass
|
|
16
16
|
* `{ txIdVouts, walletProvider }` to `mutate({...})`.
|
|
17
17
|
*/
|
|
18
18
|
export function useRenewUtxos({
|
|
@@ -32,7 +32,7 @@ export function useRenewUtxos({
|
|
|
32
32
|
const accessToken = session?.accessToken || radfi.accessToken;
|
|
33
33
|
|
|
34
34
|
if (!accessToken) {
|
|
35
|
-
throw new Error('
|
|
35
|
+
throw new Error('Bound Exchange authentication required. Please login first.');
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
const buildResult = await radfi.buildRenewUtxoTransaction({ userAddress, txIdVouts }, accessToken);
|
|
@@ -5,7 +5,7 @@ type UseTradingWalletReturn = {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Returns the
|
|
8
|
+
* Returns the Bound Exchange trading wallet address from the persisted session.
|
|
9
9
|
* Trading wallet is created automatically during authentication — no API call needed.
|
|
10
10
|
*/
|
|
11
11
|
export function useTradingWallet(walletAddress: string | undefined): UseTradingWalletReturn {
|
package/src/hooks/index.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './useLeverageYieldEffectiveApr.js';
|
|
2
|
+
export * from './useLeverageYieldPosition.js';
|
|
3
|
+
export * from './useLeverageYieldTotalAssets.js';
|
|
4
|
+
export * from './useLeverageYieldPreviewRedeem.js';
|
|
5
|
+
export * from './useLeverageYieldShareBalances.js';
|
|
6
|
+
export * from './useLeverageYieldDeposit.js';
|
|
7
|
+
export * from './useLeverageYieldNotifySolver.js';
|
|
8
|
+
export * from './useLeverageYieldVaultSwap.js';
|
|
9
|
+
export * from './useLeverageYieldWithdraw.js';
|