@sodax/dapp-kit 2.0.0-rc.11 → 2.0.0-rc.12
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 +100 -8
- package/dist/index.mjs +137 -46
- package/package.json +2 -2
- package/src/hooks/_mutationContract.test.ts +2 -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/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/README.md
CHANGED
|
@@ -10,7 +10,7 @@ High-level React hooks library for dApp developers. Wraps `@sodax/sdk` with Reac
|
|
|
10
10
|
- **Staking** — `useStake`, `useUnstake`, `useInstantUnstake`, `useClaim`, `useCancelUnstake`, approval hooks, info/config/ratio queries
|
|
11
11
|
- **DEX** — `useDexDeposit`, `useDexWithdraw`, `useSupplyLiquidity`, `useDecreaseLiquidity`, `useClaimRewards`, pool/position queries, param builders
|
|
12
12
|
- **Migration** — `useMigrateIcxToSoda`, `useRevertMigrateSodaToIcx`, `useMigratebnUSD`, `useMigrateBaln`, `useMigrationApprove`, `useMigrationAllowance`
|
|
13
|
-
- **Bitcoin (
|
|
13
|
+
- **Bitcoin (Bound Exchange)** — `useRadfiAuth`, `useRadfiSession`, `useTradingWallet`, `useTradingWalletBalance`, `useBitcoinBalance`, `useFundTradingWallet`, `useRadfiWithdraw`, `useExpiredUtxos`, `useRenewUtxos`
|
|
14
14
|
- **Partner** — `useFetchAssetsBalances`, `useGetAutoSwapPreferences`, `useIsTokenApproved`, `useApproveToken`, `useSetSwapPreference`, `useFeeClaimSwap`
|
|
15
15
|
- **Recovery** — `useHubAssetBalances`, `useWithdrawHubAsset`
|
|
16
16
|
- **Backend Queries** — Intent tracking, orderbook, money market position queries
|
|
@@ -213,13 +213,13 @@ function SwapButton({ intentParams }: { intentParams: CreateIntentParams }) {
|
|
|
213
213
|
- [`useMigrationApprove()`](src/hooks/migrate/useMigrationApprove.ts) — Approve token spending before migration
|
|
214
214
|
- [`useMigrationAllowance()`](src/hooks/migrate/useMigrationAllowance.ts) — Check if approval is needed
|
|
215
215
|
|
|
216
|
-
### Bitcoin (
|
|
216
|
+
### Bitcoin (Bound Exchange) Hooks
|
|
217
217
|
|
|
218
|
-
- [`useRadfiSession()`](src/hooks/bitcoin/useRadfiSession.ts) — Manage
|
|
219
|
-
- [`useRadfiAuth()`](src/hooks/bitcoin/useRadfiAuth.ts) — Authenticate with
|
|
218
|
+
- [`useRadfiSession()`](src/hooks/bitcoin/useRadfiSession.ts) — Manage Bound Exchange session (login, auto-refresh)
|
|
219
|
+
- [`useRadfiAuth()`](src/hooks/bitcoin/useRadfiAuth.ts) — Authenticate with Bound Exchange via BIP322 signing
|
|
220
220
|
- [`useTradingWallet()`](src/hooks/bitcoin/useTradingWallet.ts) — Get trading wallet address from persisted session
|
|
221
221
|
- [`useBitcoinBalance()`](src/hooks/bitcoin/useBitcoinBalance.ts) — BTC balance for any address
|
|
222
|
-
- [`useTradingWalletBalance()`](src/hooks/bitcoin/useTradingWalletBalance.ts) — Trading wallet balance from
|
|
222
|
+
- [`useTradingWalletBalance()`](src/hooks/bitcoin/useTradingWalletBalance.ts) — Trading wallet balance from Bound Exchange API
|
|
223
223
|
- [`useFundTradingWallet()`](src/hooks/bitcoin/useFundTradingWallet.ts) — Fund trading wallet from personal wallet
|
|
224
224
|
- [`useRadfiWithdraw()`](src/hooks/bitcoin/useRadfiWithdraw.ts) — Withdraw from trading wallet
|
|
225
225
|
- [`useExpiredUtxos()`](src/hooks/bitcoin/useExpiredUtxos.ts) — Fetch expired UTXOs (polls every 60s)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { UseMutationOptions, UseQueryOptions, UseMutationResult, MutateOptions, UseQueryResult, QueryClientConfig, QueryClient } from '@tanstack/react-query';
|
|
2
|
-
import { Result, Sodax, SpokeChainKey, GetEstimateGasReturnType, EstimateGasParams, IStellarWalletProvider, StellarChainKey, IXServiceBase, XToken, EvmHubProvider, IBitcoinWalletProvider, RadfiWalletBalance, RadfiUtxo, MoneyMarketBorrowActionParams, TxHashPair, MoneyMarketRepayActionParams, MoneyMarketSupplyActionParams, MoneyMarketWithdrawActionParams, UserReserveData, AggregatedReserveData, BaseCurrencyInfo, ReservesDataHumanized, Address as Address$1, MoneyMarketParams, MoneyMarketApproveActionParams, TxReturnType, Erc20Token, ChainKey, ReserveDataHumanized, FormatReserveUSDResponse, FormatUserSummaryResponse, SolverIntentQuoteResponse, SolverErrorResponse, SolverIntentQuoteRequest, SwapActionParams, SwapResponse, SolverIntentStatusResponse, Hex, CreateIntentParams, CreateLimitOrderParams,
|
|
2
|
+
import { Result, Sodax, SpokeChainKey, GetEstimateGasReturnType, EstimateGasParams, IStellarWalletProvider, StellarChainKey, INearWalletProvider, GetWalletProviderType, IXServiceBase, XToken, EvmHubProvider, IBitcoinWalletProvider, RadfiWalletBalance, RadfiUtxo, MoneyMarketBorrowActionParams, TxHashPair, MoneyMarketRepayActionParams, MoneyMarketSupplyActionParams, MoneyMarketWithdrawActionParams, UserReserveData, AggregatedReserveData, BaseCurrencyInfo, ReservesDataHumanized, Address as Address$1, MoneyMarketParams, MoneyMarketApproveActionParams, TxReturnType, Erc20Token, ChainKey, ReserveDataHumanized, FormatReserveUSDResponse, FormatUserSummaryResponse, SolverIntentQuoteResponse, SolverErrorResponse, SolverIntentQuoteRequest, SwapActionParams, SwapResponse, SolverIntentStatusResponse, Hex, CreateIntentParams, CreateLimitOrderParams, Intent, LimitOrderActionParams, IntentResponse, UserIntentsResponse, SubmitSwapTxResponse, SubmitSwapTxRequest, RequestOverrideConfig, SubmitSwapTxStatusResponse, OrderbookResponse, MoneyMarketPosition, MoneyMarketAsset, MoneyMarketAssetBorrowers, MoneyMarketAssetSuppliers, MoneyMarketBorrowers, BridgeParams, CreateBridgeIntentParams, BridgeLimit, StakeAction, StakeParams, UnstakeAction, ClaimAction, CancelUnstakeAction, StakingInfo, UnstakingInfo, UnstakeRequestWithPenalty, StakingConfig, InstantUnstakeAction, UnstakeParams, InstantUnstakeParams, PartnerFeeClaimAssetBalance, AutoSwapPreferences, FeeTokenApproveParams, FeeTokenApproveAction, HubChainKey, SetSwapPreferenceAction, PartnerFeeClaimSwapAction, IntentAutoSwapResult, HubAssetBalance, WithdrawHubAssetAction, IcxMigrateAction, IcxRevertMigrationAction, UnifiedBnUSDMigrateAction, BalnMigrateAction, MigrationAction, IcxMigrateParams, IcxCreateRevertMigrationParams, UnifiedBnUSDMigrateParams, BalnMigrateParams, PoolKey, PoolData, ClPositionInfo, AssetDepositAction, AssetWithdrawAction, CreateAssetDepositParams, ClSupplyParams, ClIncreaseLiquidityParams, ClLiquidityDecreaseLiquidityAction, ClDecreaseLiquidityParams, CreateAssetWithdrawParams, PoolSpokeAssets, DestinationParamsType, ClLiquidityClaimRewardsAction, SodaxOptions } from '@sodax/sdk';
|
|
3
3
|
export * from '@sodax/sdk';
|
|
4
4
|
import { Address } from 'viem';
|
|
5
5
|
import { ReactNode, ReactElement } from 'react';
|
|
@@ -151,6 +151,72 @@ declare function useRequestTrustline(token: string | undefined): {
|
|
|
151
151
|
data: string | null;
|
|
152
152
|
};
|
|
153
153
|
|
|
154
|
+
type UseNearStorageCheckParams = ReadHookParams<boolean, {
|
|
155
|
+
token: string | undefined;
|
|
156
|
+
accountId: string | undefined;
|
|
157
|
+
chainId: SpokeChainKey | undefined;
|
|
158
|
+
}>;
|
|
159
|
+
/**
|
|
160
|
+
* Whether `accountId` is NEP-141 storage-registered for `token` on NEAR — the receive-side
|
|
161
|
+
* prerequisite for any leg that delivers a token to the user on NEAR (swap output on NEAR, bridge
|
|
162
|
+
* into NEAR, money-market borrow/withdraw to NEAR). NEAR's analogue of a Stellar trustline.
|
|
163
|
+
*
|
|
164
|
+
* Returns `true` (no gate) when `chainId` is not NEAR, mirroring how the Stellar trustline check
|
|
165
|
+
* short-circuits off-chain. Native NEAR is not a NEP-141 token, so the SDK reports it as registered.
|
|
166
|
+
*
|
|
167
|
+
* Pair with {@link useRegisterNearStorage} to perform the one-time `storage_deposit` when this
|
|
168
|
+
* resolves to `false`.
|
|
169
|
+
*/
|
|
170
|
+
declare function useNearStorageCheck({ params, queryOptions, }?: UseNearStorageCheckParams): UseQueryResult<boolean, Error>;
|
|
171
|
+
|
|
172
|
+
type UseRegisterNearStorageVars = {
|
|
173
|
+
token: string;
|
|
174
|
+
accountId: string;
|
|
175
|
+
walletProvider: INearWalletProvider;
|
|
176
|
+
/** Storage bond override; defaults to the SDK's `NEAR_STORAGE_DEPOSIT` (0.00125 NEAR). */
|
|
177
|
+
deposit?: bigint;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Submit a NEP-141 `storage_deposit` so `accountId` can receive `token` on NEAR. NEAR's analogue of
|
|
181
|
+
* requesting a Stellar trustline; pair with {@link useNearStorageCheck} and call this when the check
|
|
182
|
+
* resolves to `false`. The recipient's NEAR wallet signs the registration tx.
|
|
183
|
+
*
|
|
184
|
+
* Pure mutation: pass `{ token, accountId, walletProvider }` to `mutate(...)`. The hook itself only
|
|
185
|
+
* takes the structural `mutationOptions` slot. `registerStorage` throws natively (no `Result<T>`),
|
|
186
|
+
* so this hook is registered as `nativeThrow` in the mutation contract. On success it invalidates
|
|
187
|
+
* the matching {@link useNearStorageCheck} query.
|
|
188
|
+
*/
|
|
189
|
+
declare function useRegisterNearStorage({ mutationOptions, }?: MutationHookParams<string, UseRegisterNearStorageVars>): SafeUseMutationResult<string, Error, UseRegisterNearStorageVars>;
|
|
190
|
+
|
|
191
|
+
interface UseNearStorageGateParams {
|
|
192
|
+
/** Destination chain the token is delivered on. */
|
|
193
|
+
dstChainKey: SpokeChainKey;
|
|
194
|
+
/** Destination token address the recipient must be registered for. */
|
|
195
|
+
token: string | undefined;
|
|
196
|
+
/** Recipient NEAR account id. */
|
|
197
|
+
accountId: string | undefined;
|
|
198
|
+
/** Destination wallet provider; only consumed when it is the NEAR provider. */
|
|
199
|
+
walletProvider: GetWalletProviderType<SpokeChainKey> | undefined;
|
|
200
|
+
}
|
|
201
|
+
interface NearStorageGate {
|
|
202
|
+
/** Destination is NEAR. */
|
|
203
|
+
isNear: boolean;
|
|
204
|
+
/** Destination is NEAR and the recipient is not yet storage-registered for `token`. */
|
|
205
|
+
needsRegistration: boolean;
|
|
206
|
+
/** The downstream action must stay disabled while the gate is unresolved or unmet. */
|
|
207
|
+
blocksAction: boolean;
|
|
208
|
+
/** The registration-status query is actively fetching. */
|
|
209
|
+
isChecking: boolean;
|
|
210
|
+
/** A `storage_deposit` tx is in flight. */
|
|
211
|
+
isRegistering: boolean;
|
|
212
|
+
/** Submit the `storage_deposit`; resolves to the SDK `Result` or `undefined` if inputs are missing. */
|
|
213
|
+
registerStorage: () => Promise<Result<string> | undefined>;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* NEP-141 storage-registration gate for flows that deliver a token to a user on NEAR.
|
|
217
|
+
*/
|
|
218
|
+
declare function useNearStorageGate({ dstChainKey, token, accountId, walletProvider, }: UseNearStorageGateParams): NearStorageGate;
|
|
219
|
+
|
|
154
220
|
type UseGetUserHubWalletAddressParams = ReadHookParams<Address, {
|
|
155
221
|
spokeChainId?: SpokeChainKey;
|
|
156
222
|
spokeAddress?: string;
|
|
@@ -228,7 +294,7 @@ declare function saveRadfiSession(address: string, session: RadfiSession): void;
|
|
|
228
294
|
declare function loadRadfiSession(address: string): RadfiSession | null;
|
|
229
295
|
declare function clearRadfiSession(address: string): void;
|
|
230
296
|
/**
|
|
231
|
-
* React hook for authenticating with
|
|
297
|
+
* React hook for authenticating with Bound Exchange via BIP322-signed message. Pure mutation: pass
|
|
232
298
|
* `{ walletProvider }` to `mutate({...})`. The hook itself takes no arguments other than the
|
|
233
299
|
* structural `mutationOptions` slot.
|
|
234
300
|
*/
|
|
@@ -248,7 +314,7 @@ type UseFundTradingWalletVars = {
|
|
|
248
314
|
walletProvider: IBitcoinWalletProvider;
|
|
249
315
|
};
|
|
250
316
|
/**
|
|
251
|
-
* React hook for funding the user's
|
|
317
|
+
* React hook for funding the user's Bound Exchange trading wallet from their personal Bitcoin wallet.
|
|
252
318
|
* Pure mutation: pass `{ amount, walletProvider }` to `mutate({...})`. Returns the broadcast tx
|
|
253
319
|
* id on success.
|
|
254
320
|
*/
|
|
@@ -271,7 +337,7 @@ type UseTradingWalletReturn = {
|
|
|
271
337
|
tradingAddress: string | undefined;
|
|
272
338
|
};
|
|
273
339
|
/**
|
|
274
|
-
* Returns the
|
|
340
|
+
* Returns the Bound Exchange trading wallet address from the persisted session.
|
|
275
341
|
* Trading wallet is created automatically during authentication — no API call needed.
|
|
276
342
|
*/
|
|
277
343
|
declare function useTradingWallet(walletAddress: string | undefined): UseTradingWalletReturn;
|
|
@@ -293,7 +359,7 @@ type UseRenewUtxosVars = {
|
|
|
293
359
|
walletProvider: IBitcoinWalletProvider;
|
|
294
360
|
};
|
|
295
361
|
/**
|
|
296
|
-
* React hook for renewing expired UTXOs in the user's
|
|
362
|
+
* React hook for renewing expired UTXOs in the user's Bound Exchange trading wallet. Pure mutation: pass
|
|
297
363
|
* `{ txIdVouts, walletProvider }` to `mutate({...})`.
|
|
298
364
|
*/
|
|
299
365
|
declare function useRenewUtxos({ mutationOptions, }?: MutationHookParams<string, UseRenewUtxosVars>): SafeUseMutationResult<string, Error, UseRenewUtxosVars>;
|
|
@@ -309,7 +375,7 @@ type WithdrawResult = {
|
|
|
309
375
|
fee: number;
|
|
310
376
|
};
|
|
311
377
|
/**
|
|
312
|
-
* React hook for withdrawing BTC from the user's
|
|
378
|
+
* React hook for withdrawing BTC from the user's Bound Exchange trading wallet back to their personal
|
|
313
379
|
* Bitcoin wallet. Pure mutation: pass all inputs (including the wallet provider) to
|
|
314
380
|
* `mutate({...})`.
|
|
315
381
|
*/
|
|
@@ -1489,7 +1555,7 @@ interface SodaxProviderProps {
|
|
|
1489
1555
|
* <SodaxProvider config={config}>...</SodaxProvider>
|
|
1490
1556
|
* ```
|
|
1491
1557
|
*/
|
|
1492
|
-
config?:
|
|
1558
|
+
config?: SodaxOptions;
|
|
1493
1559
|
}
|
|
1494
1560
|
/** Root provider for `@sodax/dapp-kit`. Must be paired with `QueryClientProvider`. */
|
|
1495
1561
|
declare const SodaxProvider: ({ children, config }: SodaxProviderProps) => ReactElement;
|
|
@@ -1554,4 +1620,30 @@ type CreateSodaxQueryClientOptions = {
|
|
|
1554
1620
|
*/
|
|
1555
1621
|
declare function createSodaxQueryClient({ onMutationError, config, }?: CreateSodaxQueryClientOptions): QueryClient;
|
|
1556
1622
|
|
|
1557
|
-
|
|
1623
|
+
/** The subset of a `useNearStorageCheck` result the gate reads. A full `UseQueryResult<boolean>` satisfies it. */
|
|
1624
|
+
type NearStorageCheckResult = Pick<UseQueryResult<boolean>, 'isLoading' | 'data'>;
|
|
1625
|
+
/** UI gate state for NEP-141 storage registration on NEAR. */
|
|
1626
|
+
interface NearStorageGateState {
|
|
1627
|
+
/** Destination is NEAR — the only chain with a NEP-141 storage-registration prerequisite. */
|
|
1628
|
+
isNear: boolean;
|
|
1629
|
+
/** Show the "register storage" action: the check resolved and the recipient is not registered. */
|
|
1630
|
+
needsRegistration: boolean;
|
|
1631
|
+
/**
|
|
1632
|
+
* Keep the downstream action (swap/bridge/borrow/withdraw) disabled: the NEAR gate is unresolved
|
|
1633
|
+
* (still checking) or unmet (needs registration).
|
|
1634
|
+
*/
|
|
1635
|
+
blocksAction: boolean;
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Derives the NEP-141 storage-registration UI gate state for a flow that delivers a token to a user
|
|
1639
|
+
* on NEAR — NEAR's analogue of the Stellar trustline gate. Unwrapped (no hook): pass the destination
|
|
1640
|
+
* `chainKey` and the `useNearStorageCheck` result; the util owns the `=== NEAR` test and reads
|
|
1641
|
+
* `isLoading` (NOT `isPending`, which stays `true` for a disabled query and would block forever).
|
|
1642
|
+
*
|
|
1643
|
+
* `blocksAction` deliberately also covers the in-flight check window so the action can't be
|
|
1644
|
+
* triggered before registration status is known; `needsRegistration` only flips once the check has
|
|
1645
|
+
* resolved (so the "register" button isn't shown speculatively while still checking).
|
|
1646
|
+
*/
|
|
1647
|
+
declare function resolveNearStorageGate(chainKey: SpokeChainKey, check: NearStorageCheckResult): NearStorageGateState;
|
|
1648
|
+
|
|
1649
|
+
export { type ATokenData, type BackendPaginationParams, type CreateSodaxQueryClientOptions, type DecreaseLiquidityParamsCore, type DepositParamsCore, type MutationHookOptions, type MutationHookParams, type NearStorageCheckResult, type NearStorageGateState, type RadfiSession, type ReadHookParams, type ReadQueryOptions, type ReserveUsdFormat, type SafeUseMutationResult, SodaxProvider, type UseATokenParams, type UseATokensBalancesParams, type UseApproveTokenVars, type UseBitcoinBalanceParams, type UseBorrowVars, type UseBridgeAllowanceParams, type UseBridgeApproveVars, type UseBridgeVars, type UseClaimRewardsVars, type UseCreateDecreaseLiquidityParamsProps, type UseCreateDepositParamsProps, type UseCreateLimitOrderVars, type UseCreateSupplyLiquidityParamsProps, type UseCreateSupplyLiquidityParamsResult, type UseCreateWithdrawParamsProps, type UseDecreaseLiquidityVars, type UseDeriveUserWalletAddressParams, type UseDexAllowanceParams, type UseDexApproveVars, type UseDexDepositVars, type UseDexWithdrawVars, type UseExpiredUtxosParams, type UseFeeClaimSwapVars, type UseFetchAssetsBalancesParams, type UseFundTradingWalletVars, type UseGetAutoSwapPreferencesParams, type UseGetBridgeableAmountParams, type UseGetBridgeableTokensParams, type UseGetUserHubWalletAddressParams, type UseHubAssetBalancesParams, type UseIsTokenApprovedParams, type UseLiquidityAmountsResult, type UseMMAllowanceParams, type UseMMApproveVars, type UseMigrateBalnVars, type UseMigrateIcxToSodaVars, type UseMigratebnUSDVars, type UseMigrationAllowanceInputs, type UseMigrationAllowanceParams, type UseMigrationApproveVars, type UseNearStorageCheckParams, type UsePoolBalancesParams, type UsePoolBalancesResponse, type UsePoolDataParams, type UsePoolsParams, type UsePositionInfoParams, type UsePositionInfoResponse, type UseQuoteParams, type UseRadfiAuthVars, type UseRadfiSessionReturn, type UseRadfiWithdrawVars, type UseRegisterNearStorageVars, type UseRenewUtxosVars, type UseRepayVars, type UseReservesDataParams, type UseReservesHumanizedParams, type UseReservesListParams, type UseReservesUsdFormatParams, type UseRevertMigrateSodaToIcxVars, type UseSetSwapPreferenceVars, type UseSetSwapPreferenceVarsHub, type UseStatusParams, type UseStellarTrustlineCheckParams, type UseSupplyLiquidityVars, type UseSupplyVars, type UseSwapAllowanceParams, type UseSwapApproveVars, type UseSwapVars, type UseTradingWalletBalanceParams, type UseUserFormattedSummaryParams, type UseUserReservesDataParams, type UseWithdrawHubAssetVars, type UseWithdrawVars, type UseXBalancesParams, type WithdrawParamsCore, type XBalancesInputs, 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, 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/dist/index.mjs
CHANGED
|
@@ -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)
|
|
@@ -2513,4 +2604,4 @@ function createSodaxQueryClient({
|
|
|
2513
2604
|
});
|
|
2514
2605
|
}
|
|
2515
2606
|
|
|
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 };
|
|
2607
|
+
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, 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.12",
|
|
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.12"
|
|
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.
|
|
@@ -41,6 +41,7 @@ const HOOKS: Array<{ path: string; nativeThrow?: true }> = [
|
|
|
41
41
|
{ path: 'partner/useSetSwapPreference.ts' },
|
|
42
42
|
{ path: 'recovery/useWithdrawHubAsset.ts' },
|
|
43
43
|
{ path: 'shared/useEstimateGas.ts' },
|
|
44
|
+
{ path: 'shared/useRegisterNearStorage.ts', nativeThrow: true },
|
|
44
45
|
{ path: 'staking/useCancelUnstake.ts' },
|
|
45
46
|
{ path: 'staking/useClaim.ts' },
|
|
46
47
|
{ 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 {
|
|
@@ -2,6 +2,7 @@ import type { SpokeChainKey } from '@sodax/sdk';
|
|
|
2
2
|
import { useQuery, type UseQueryResult } from '@tanstack/react-query';
|
|
3
3
|
import { type Address, isAddress } from 'viem';
|
|
4
4
|
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
5
|
+
import { resolveBtcReadAddress } from '../bitcoin/resolveBtcReadAddress.js';
|
|
5
6
|
import type { ReadHookParams } from '../shared/types.js';
|
|
6
7
|
|
|
7
8
|
export type UseATokensBalancesParams = ReadHookParams<
|
|
@@ -42,7 +43,12 @@ export function useATokensBalances({
|
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
// Bitcoin positions live under the trading-wallet-derived hub wallet; resolve it locally
|
|
47
|
+
// (no network) so a Bound Exchange outage never reads a real position as empty.
|
|
48
|
+
const hubWalletAddress = await sodax.hubProvider.getUserHubWalletAddress(
|
|
49
|
+
resolveBtcReadAddress(spokeChainKey, userAddress),
|
|
50
|
+
spokeChainKey,
|
|
51
|
+
);
|
|
46
52
|
return sodax.moneyMarket.data.getATokensBalances(aTokens, hubWalletAddress);
|
|
47
53
|
},
|
|
48
54
|
enabled: aTokens.length > 0 && !!spokeChainKey && !!userAddress,
|
|
@@ -2,6 +2,7 @@ import type { FormatReserveUSDResponse, FormatUserSummaryResponse } from '@sodax
|
|
|
2
2
|
import type { SpokeChainKey } from '@sodax/sdk';
|
|
3
3
|
import { useQuery, type UseQueryResult } from '@tanstack/react-query';
|
|
4
4
|
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
5
|
+
import { resolveBtcReadAddress } from '../bitcoin/resolveBtcReadAddress.js';
|
|
5
6
|
import type { ReadHookParams } from '../shared/types.js';
|
|
6
7
|
|
|
7
8
|
export type UseUserFormattedSummaryParams = ReadHookParams<
|
|
@@ -36,7 +37,10 @@ export function useUserFormattedSummary({
|
|
|
36
37
|
|
|
37
38
|
const [reserves, userReserves] = await Promise.all([
|
|
38
39
|
sodax.moneyMarket.data.getReservesHumanized(),
|
|
39
|
-
sodax.moneyMarket.data.getUserReservesHumanized(
|
|
40
|
+
sodax.moneyMarket.data.getUserReservesHumanized(
|
|
41
|
+
spokeChainKey,
|
|
42
|
+
resolveBtcReadAddress(spokeChainKey, userAddress),
|
|
43
|
+
),
|
|
40
44
|
]);
|
|
41
45
|
const formattedReserves = sodax.moneyMarket.data.formatReservesUSD(
|
|
42
46
|
sodax.moneyMarket.data.buildReserveDataWithPrice(reserves),
|
|
@@ -2,6 +2,7 @@ import type { UserReserveData } from '@sodax/sdk';
|
|
|
2
2
|
import type { SpokeChainKey } from '@sodax/sdk';
|
|
3
3
|
import { useQuery, type UseQueryResult } from '@tanstack/react-query';
|
|
4
4
|
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
5
|
+
import { resolveBtcReadAddress } from '../bitcoin/resolveBtcReadAddress.js';
|
|
5
6
|
import type { ReadHookParams } from '../shared/types.js';
|
|
6
7
|
|
|
7
8
|
export type UseUserReservesDataParams = ReadHookParams<
|
|
@@ -30,7 +31,10 @@ export function useUserReservesData({
|
|
|
30
31
|
if (!spokeChainKey || !userAddress) {
|
|
31
32
|
throw new Error('spokeChainKey and userAddress are required');
|
|
32
33
|
}
|
|
33
|
-
return sodax.moneyMarket.data.getUserReservesData(
|
|
34
|
+
return sodax.moneyMarket.data.getUserReservesData(
|
|
35
|
+
spokeChainKey,
|
|
36
|
+
resolveBtcReadAddress(spokeChainKey, userAddress),
|
|
37
|
+
);
|
|
34
38
|
},
|
|
35
39
|
enabled: !!spokeChainKey && !!userAddress,
|
|
36
40
|
refetchInterval: 5000,
|
|
@@ -6,5 +6,8 @@ export * from './useEstimateGas.js';
|
|
|
6
6
|
export * from './useDeriveUserWalletAddress.js';
|
|
7
7
|
export * from './useStellarTrustlineCheck.js';
|
|
8
8
|
export * from './useRequestTrustline.js';
|
|
9
|
+
export * from './useNearStorageCheck.js';
|
|
10
|
+
export * from './useRegisterNearStorage.js';
|
|
11
|
+
export * from './useNearStorageGate.js';
|
|
9
12
|
export * from './useGetUserHubWalletAddress.js';
|
|
10
13
|
export * from './useXBalances.js';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SpokeChainKey } from '@sodax/sdk';
|
|
2
2
|
import { useQuery, type UseQueryResult } from '@tanstack/react-query';
|
|
3
3
|
import { useSodaxContext } from './useSodaxContext.js';
|
|
4
|
+
import { resolveBtcReadAddress } from '../bitcoin/resolveBtcReadAddress.js';
|
|
4
5
|
import type { Address } from 'viem';
|
|
5
6
|
import type { ReadHookParams } from './types.js';
|
|
6
7
|
|
|
@@ -44,7 +45,10 @@ export function useGetUserHubWalletAddress({
|
|
|
44
45
|
if (!spokeChainId || !spokeAddress) {
|
|
45
46
|
throw new Error('Spoke chain id and address are required');
|
|
46
47
|
}
|
|
47
|
-
return await sodax.hubProvider.getUserHubWalletAddress(
|
|
48
|
+
return await sodax.hubProvider.getUserHubWalletAddress(
|
|
49
|
+
resolveBtcReadAddress(spokeChainId, spokeAddress),
|
|
50
|
+
spokeChainId,
|
|
51
|
+
);
|
|
48
52
|
},
|
|
49
53
|
enabled: !!spokeChainId && !!spokeAddress,
|
|
50
54
|
refetchInterval: false,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ChainKeys, type SpokeChainKey } from '@sodax/sdk';
|
|
2
|
+
import { useQuery, type UseQueryResult } from '@tanstack/react-query';
|
|
3
|
+
import { useSodaxContext } from './useSodaxContext.js';
|
|
4
|
+
import type { ReadHookParams } from './types.js';
|
|
5
|
+
|
|
6
|
+
export type UseNearStorageCheckParams = ReadHookParams<
|
|
7
|
+
boolean,
|
|
8
|
+
{
|
|
9
|
+
token: string | undefined;
|
|
10
|
+
accountId: string | undefined;
|
|
11
|
+
chainId: SpokeChainKey | undefined;
|
|
12
|
+
}
|
|
13
|
+
>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Whether `accountId` is NEP-141 storage-registered for `token` on NEAR — the receive-side
|
|
17
|
+
* prerequisite for any leg that delivers a token to the user on NEAR (swap output on NEAR, bridge
|
|
18
|
+
* into NEAR, money-market borrow/withdraw to NEAR). NEAR's analogue of a Stellar trustline.
|
|
19
|
+
*
|
|
20
|
+
* Returns `true` (no gate) when `chainId` is not NEAR, mirroring how the Stellar trustline check
|
|
21
|
+
* short-circuits off-chain. Native NEAR is not a NEP-141 token, so the SDK reports it as registered.
|
|
22
|
+
*
|
|
23
|
+
* Pair with {@link useRegisterNearStorage} to perform the one-time `storage_deposit` when this
|
|
24
|
+
* resolves to `false`.
|
|
25
|
+
*/
|
|
26
|
+
export function useNearStorageCheck({
|
|
27
|
+
params,
|
|
28
|
+
queryOptions,
|
|
29
|
+
}: UseNearStorageCheckParams = {}): UseQueryResult<boolean, Error> {
|
|
30
|
+
const { sodax } = useSodaxContext();
|
|
31
|
+
const token = params?.token;
|
|
32
|
+
const accountId = params?.accountId;
|
|
33
|
+
const chainId = params?.chainId;
|
|
34
|
+
|
|
35
|
+
return useQuery<boolean, Error>({
|
|
36
|
+
queryKey: ['shared', 'nearStorageCheck', chainId, token, accountId],
|
|
37
|
+
queryFn: async () => {
|
|
38
|
+
if (chainId !== ChainKeys.NEAR_MAINNET) return true;
|
|
39
|
+
if (!token || !accountId) return false;
|
|
40
|
+
return sodax.spoke.near.isStorageRegistered(token, accountId);
|
|
41
|
+
},
|
|
42
|
+
enabled: !!token && !!accountId,
|
|
43
|
+
...queryOptions,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { GetWalletProviderType, INearWalletProvider, Result, SpokeChainKey } from '@sodax/sdk';
|
|
2
|
+
import { resolveNearStorageGate } from '../../utils/nearStorageGate.js';
|
|
3
|
+
import { useNearStorageCheck } from './useNearStorageCheck.js';
|
|
4
|
+
import { useRegisterNearStorage } from './useRegisterNearStorage.js';
|
|
5
|
+
|
|
6
|
+
interface UseNearStorageGateParams {
|
|
7
|
+
/** Destination chain the token is delivered on. */
|
|
8
|
+
dstChainKey: SpokeChainKey;
|
|
9
|
+
/** Destination token address the recipient must be registered for. */
|
|
10
|
+
token: string | undefined;
|
|
11
|
+
/** Recipient NEAR account id. */
|
|
12
|
+
accountId: string | undefined;
|
|
13
|
+
/** Destination wallet provider; only consumed when it is the NEAR provider. */
|
|
14
|
+
walletProvider: GetWalletProviderType<SpokeChainKey> | undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface NearStorageGate {
|
|
18
|
+
/** Destination is NEAR. */
|
|
19
|
+
isNear: boolean;
|
|
20
|
+
/** Destination is NEAR and the recipient is not yet storage-registered for `token`. */
|
|
21
|
+
needsRegistration: boolean;
|
|
22
|
+
/** The downstream action must stay disabled while the gate is unresolved or unmet. */
|
|
23
|
+
blocksAction: boolean;
|
|
24
|
+
/** The registration-status query is actively fetching. */
|
|
25
|
+
isChecking: boolean;
|
|
26
|
+
/** A `storage_deposit` tx is in flight. */
|
|
27
|
+
isRegistering: boolean;
|
|
28
|
+
/** Submit the `storage_deposit`; resolves to the SDK `Result` or `undefined` if inputs are missing. */
|
|
29
|
+
registerStorage: () => Promise<Result<string> | undefined>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* NEP-141 storage-registration gate for flows that deliver a token to a user on NEAR.
|
|
34
|
+
*/
|
|
35
|
+
export function useNearStorageGate({
|
|
36
|
+
dstChainKey,
|
|
37
|
+
token,
|
|
38
|
+
accountId,
|
|
39
|
+
walletProvider,
|
|
40
|
+
}: UseNearStorageGateParams): NearStorageGate {
|
|
41
|
+
const nearWalletProvider = walletProvider?.chainType === 'NEAR' ? (walletProvider as INearWalletProvider) : undefined;
|
|
42
|
+
|
|
43
|
+
const storageCheck = useNearStorageCheck({ params: { token, accountId, chainId: dstChainKey } });
|
|
44
|
+
const { mutateAsyncSafe: register, isPending: isRegistering } = useRegisterNearStorage();
|
|
45
|
+
const { isNear, needsRegistration, blocksAction } = resolveNearStorageGate(dstChainKey, storageCheck);
|
|
46
|
+
|
|
47
|
+
const registerStorage = async (): Promise<Result<string> | undefined> => {
|
|
48
|
+
if (!nearWalletProvider || !token || !accountId) return undefined;
|
|
49
|
+
return register({ token, accountId, walletProvider: nearWalletProvider });
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
isNear,
|
|
54
|
+
needsRegistration,
|
|
55
|
+
blocksAction,
|
|
56
|
+
isChecking: storageCheck.isLoading,
|
|
57
|
+
isRegistering,
|
|
58
|
+
registerStorage,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ChainKeys, type INearWalletProvider } from '@sodax/sdk';
|
|
2
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
3
|
+
import { useSodaxContext } from './useSodaxContext.js';
|
|
4
|
+
import type { MutationHookParams } from './types.js';
|
|
5
|
+
import { useSafeMutation, type SafeUseMutationResult } from './useSafeMutation.js';
|
|
6
|
+
|
|
7
|
+
export type UseRegisterNearStorageVars = {
|
|
8
|
+
token: string;
|
|
9
|
+
accountId: string;
|
|
10
|
+
walletProvider: INearWalletProvider;
|
|
11
|
+
/** Storage bond override; defaults to the SDK's `NEAR_STORAGE_DEPOSIT` (0.00125 NEAR). */
|
|
12
|
+
deposit?: bigint;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Submit a NEP-141 `storage_deposit` so `accountId` can receive `token` on NEAR. NEAR's analogue of
|
|
17
|
+
* requesting a Stellar trustline; pair with {@link useNearStorageCheck} and call this when the check
|
|
18
|
+
* resolves to `false`. The recipient's NEAR wallet signs the registration tx.
|
|
19
|
+
*
|
|
20
|
+
* Pure mutation: pass `{ token, accountId, walletProvider }` to `mutate(...)`. The hook itself only
|
|
21
|
+
* takes the structural `mutationOptions` slot. `registerStorage` throws natively (no `Result<T>`),
|
|
22
|
+
* so this hook is registered as `nativeThrow` in the mutation contract. On success it invalidates
|
|
23
|
+
* the matching {@link useNearStorageCheck} query.
|
|
24
|
+
*/
|
|
25
|
+
export function useRegisterNearStorage({
|
|
26
|
+
mutationOptions,
|
|
27
|
+
}: MutationHookParams<string, UseRegisterNearStorageVars> = {}): SafeUseMutationResult<
|
|
28
|
+
string,
|
|
29
|
+
Error,
|
|
30
|
+
UseRegisterNearStorageVars
|
|
31
|
+
> {
|
|
32
|
+
const { sodax } = useSodaxContext();
|
|
33
|
+
const queryClient = useQueryClient();
|
|
34
|
+
return useSafeMutation<string, Error, UseRegisterNearStorageVars>({
|
|
35
|
+
mutationKey: ['shared', 'registerNearStorage'],
|
|
36
|
+
...mutationOptions,
|
|
37
|
+
mutationFn: async ({ token, accountId, walletProvider, deposit }) =>
|
|
38
|
+
sodax.spoke.near.registerStorage({ token, accountId, walletProvider, deposit, raw: false }),
|
|
39
|
+
onSuccess: async (data, vars, ctx) => {
|
|
40
|
+
queryClient.invalidateQueries({
|
|
41
|
+
queryKey: ['shared', 'nearStorageCheck', ChainKeys.NEAR_MAINNET, vars.token, vars.accountId],
|
|
42
|
+
});
|
|
43
|
+
await mutationOptions?.onSuccess?.(data, vars, ctx);
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { useMemo, type ReactNode, type ReactElement } from 'react';
|
|
2
|
-
import { Sodax, type
|
|
2
|
+
import { Sodax, type SodaxOptions } from '@sodax/sdk';
|
|
3
3
|
import { SodaxContext } from '@/contexts/index.js';
|
|
4
|
-
import type { DeepPartial } from '@sodax/sdk';
|
|
5
4
|
|
|
6
5
|
interface SodaxProviderProps {
|
|
7
6
|
children: ReactNode;
|
|
@@ -17,7 +16,7 @@ interface SodaxProviderProps {
|
|
|
17
16
|
* <SodaxProvider config={config}>...</SodaxProvider>
|
|
18
17
|
* ```
|
|
19
18
|
*/
|
|
20
|
-
config?:
|
|
19
|
+
config?: SodaxOptions;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
/** Root provider for `@sodax/dapp-kit`. Must be paired with `QueryClientProvider`. */
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ChainKeys } from '@sodax/sdk';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { resolveNearStorageGate } from './nearStorageGate.js';
|
|
4
|
+
|
|
5
|
+
describe('resolveNearStorageGate', () => {
|
|
6
|
+
it('destination not NEAR → never gates', () => {
|
|
7
|
+
expect(resolveNearStorageGate(ChainKeys.POLYGON_MAINNET, { isLoading: false, data: false })).toEqual({
|
|
8
|
+
isNear: false,
|
|
9
|
+
needsRegistration: false,
|
|
10
|
+
blocksAction: false,
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('NEAR, check in flight → blocks action but does not yet prompt registration', () => {
|
|
15
|
+
expect(resolveNearStorageGate(ChainKeys.NEAR_MAINNET, { isLoading: true, data: undefined })).toEqual({
|
|
16
|
+
isNear: true,
|
|
17
|
+
needsRegistration: false,
|
|
18
|
+
blocksAction: true,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('NEAR, check resolved, not registered → prompt registration and block action', () => {
|
|
23
|
+
expect(resolveNearStorageGate(ChainKeys.NEAR_MAINNET, { isLoading: false, data: false })).toEqual({
|
|
24
|
+
isNear: true,
|
|
25
|
+
needsRegistration: true,
|
|
26
|
+
blocksAction: true,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('NEAR, check resolved, registered → no gate', () => {
|
|
31
|
+
expect(resolveNearStorageGate(ChainKeys.NEAR_MAINNET, { isLoading: false, data: true })).toEqual({
|
|
32
|
+
isNear: true,
|
|
33
|
+
needsRegistration: false,
|
|
34
|
+
blocksAction: false,
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('NEAR, query disabled (unresolved, not checking) → does not block', () => {
|
|
39
|
+
// isLoading is false for a disabled query; data stays undefined → action must not be blocked.
|
|
40
|
+
expect(resolveNearStorageGate(ChainKeys.NEAR_MAINNET, { isLoading: false, data: undefined })).toEqual({
|
|
41
|
+
isNear: true,
|
|
42
|
+
needsRegistration: false,
|
|
43
|
+
blocksAction: false,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ChainKeys, type SpokeChainKey } from '@sodax/sdk';
|
|
2
|
+
import type { UseQueryResult } from '@tanstack/react-query';
|
|
3
|
+
|
|
4
|
+
/** The subset of a `useNearStorageCheck` result the gate reads. A full `UseQueryResult<boolean>` satisfies it. */
|
|
5
|
+
export type NearStorageCheckResult = Pick<UseQueryResult<boolean>, 'isLoading' | 'data'>;
|
|
6
|
+
|
|
7
|
+
/** UI gate state for NEP-141 storage registration on NEAR. */
|
|
8
|
+
export interface NearStorageGateState {
|
|
9
|
+
/** Destination is NEAR — the only chain with a NEP-141 storage-registration prerequisite. */
|
|
10
|
+
isNear: boolean;
|
|
11
|
+
/** Show the "register storage" action: the check resolved and the recipient is not registered. */
|
|
12
|
+
needsRegistration: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Keep the downstream action (swap/bridge/borrow/withdraw) disabled: the NEAR gate is unresolved
|
|
15
|
+
* (still checking) or unmet (needs registration).
|
|
16
|
+
*/
|
|
17
|
+
blocksAction: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Derives the NEP-141 storage-registration UI gate state for a flow that delivers a token to a user
|
|
22
|
+
* on NEAR — NEAR's analogue of the Stellar trustline gate. Unwrapped (no hook): pass the destination
|
|
23
|
+
* `chainKey` and the `useNearStorageCheck` result; the util owns the `=== NEAR` test and reads
|
|
24
|
+
* `isLoading` (NOT `isPending`, which stays `true` for a disabled query and would block forever).
|
|
25
|
+
*
|
|
26
|
+
* `blocksAction` deliberately also covers the in-flight check window so the action can't be
|
|
27
|
+
* triggered before registration status is known; `needsRegistration` only flips once the check has
|
|
28
|
+
* resolved (so the "register" button isn't shown speculatively while still checking).
|
|
29
|
+
*/
|
|
30
|
+
export function resolveNearStorageGate(chainKey: SpokeChainKey, check: NearStorageCheckResult): NearStorageGateState {
|
|
31
|
+
const isNear = chainKey === ChainKeys.NEAR_MAINNET;
|
|
32
|
+
const isChecking = check.isLoading;
|
|
33
|
+
const isRegistered = check.data;
|
|
34
|
+
return {
|
|
35
|
+
isNear,
|
|
36
|
+
needsRegistration: isNear && !isChecking && isRegistered === false,
|
|
37
|
+
blocksAction: isNear && (isChecking || isRegistered === false),
|
|
38
|
+
};
|
|
39
|
+
}
|