@sodax/dapp-kit 2.0.0-rc.2 → 2.0.0-rc.20
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 +29 -74
- package/dist/index.d.ts +780 -58
- package/dist/index.mjs +841 -143
- package/package.json +31 -19
- package/src/hooks/_mutationContract.test.ts +15 -2
- package/src/hooks/backend/index.ts +1 -5
- package/src/hooks/bitcoin/index.ts +2 -0
- package/src/hooks/bitcoin/resolveBtcReadAddress.test.ts +54 -0
- package/src/hooks/bitcoin/resolveBtcReadAddress.ts +21 -0
- package/src/hooks/bitcoin/useBitcoinTradingSetup.ts +45 -0
- package/src/hooks/bitcoin/useEnsureRadfiAccessToken.ts +57 -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 +2 -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/partner/index.ts +4 -0
- package/src/hooks/partner/useFeeClaimWithdraw.ts +88 -0
- package/src/hooks/partner/useGetIntentDetails.ts +39 -0
- package/src/hooks/partner/useGetUserIntent.ts +35 -0
- package/src/hooks/partner/usePartnerCancelIntent.ts +52 -0
- package/src/hooks/shared/index.ts +4 -0
- package/src/hooks/shared/isUserRejectedError.test.ts +42 -0
- package/src/hooks/shared/isUserRejectedError.ts +30 -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/hooks/swapsApi/index.ts +41 -0
- package/src/hooks/swapsApi/isTerminalSwapIntentStatus.test.ts +24 -0
- package/src/hooks/swapsApi/isTerminalSwapIntentStatus.ts +11 -0
- package/src/hooks/swapsApi/useSwapsApiAllowance.ts +40 -0
- package/src/hooks/swapsApi/useSwapsApiApprove.ts +42 -0
- package/src/hooks/swapsApi/useSwapsApiCancelIntent.ts +41 -0
- package/src/hooks/swapsApi/useSwapsApiCreateIntent.ts +41 -0
- package/src/hooks/swapsApi/useSwapsApiCreateLimitOrder.ts +41 -0
- package/src/hooks/swapsApi/useSwapsApiDeadline.ts +36 -0
- package/src/hooks/swapsApi/useSwapsApiEstimateGas.ts +40 -0
- package/src/hooks/swapsApi/useSwapsApiFilledIntent.ts +41 -0
- package/src/hooks/swapsApi/useSwapsApiIntent.ts +40 -0
- package/src/hooks/swapsApi/useSwapsApiIntentExtraData.ts +41 -0
- package/src/hooks/swapsApi/useSwapsApiIntentHash.ts +40 -0
- package/src/hooks/swapsApi/useSwapsApiIntentPacket.ts +43 -0
- package/src/hooks/swapsApi/useSwapsApiPartnerFee.ts +40 -0
- package/src/hooks/swapsApi/useSwapsApiQuote.ts +55 -0
- package/src/hooks/swapsApi/useSwapsApiSolverFee.ts +40 -0
- package/src/hooks/swapsApi/useSwapsApiStatus.ts +45 -0
- package/src/hooks/swapsApi/useSwapsApiSubmitIntent.ts +43 -0
- package/src/hooks/{backend/useBackendSubmitSwapTx.ts → swapsApi/useSwapsApiSubmitTx.ts} +16 -17
- package/src/hooks/swapsApi/useSwapsApiSubmitTxStatus.ts +55 -0
- package/src/hooks/swapsApi/useSwapsApiTokens.ts +34 -0
- package/src/hooks/swapsApi/useSwapsApiTokensByChain.ts +40 -0
- package/src/providers/SodaxProvider.tsx +17 -14
- package/src/utils/index.ts +1 -0
- package/src/utils/nearStorageGate.test.ts +46 -0
- package/src/utils/nearStorageGate.ts +39 -0
- package/ai-exported/AGENTS.md +0 -134
- package/ai-exported/integration/README.md +0 -49
- package/ai-exported/integration/ai-rules.md +0 -79
- package/ai-exported/integration/architecture.md +0 -274
- package/ai-exported/integration/features/README.md +0 -29
- package/ai-exported/integration/features/auxiliary-services.md +0 -169
- package/ai-exported/integration/features/bitcoin.md +0 -87
- package/ai-exported/integration/features/bridge.md +0 -91
- package/ai-exported/integration/features/dex.md +0 -152
- package/ai-exported/integration/features/migration.md +0 -118
- package/ai-exported/integration/features/money-market.md +0 -116
- package/ai-exported/integration/features/staking.md +0 -123
- package/ai-exported/integration/features/swap.md +0 -101
- package/ai-exported/integration/quickstart.md +0 -187
- package/ai-exported/integration/recipes/README.md +0 -136
- package/ai-exported/integration/recipes/backend-queries.md +0 -157
- package/ai-exported/integration/recipes/bitcoin.md +0 -193
- package/ai-exported/integration/recipes/bridge.md +0 -174
- package/ai-exported/integration/recipes/dex.md +0 -204
- package/ai-exported/integration/recipes/invalidations.md +0 -115
- package/ai-exported/integration/recipes/migration.md +0 -212
- package/ai-exported/integration/recipes/money-market.md +0 -206
- package/ai-exported/integration/recipes/mutation-error-handling.md +0 -118
- package/ai-exported/integration/recipes/observability.md +0 -93
- package/ai-exported/integration/recipes/setup.md +0 -144
- package/ai-exported/integration/recipes/staking.md +0 -202
- package/ai-exported/integration/recipes/swap.md +0 -272
- package/ai-exported/integration/recipes/wallet-connectivity.md +0 -101
- package/ai-exported/integration/reference/README.md +0 -12
- package/ai-exported/integration/reference/glossary.md +0 -188
- package/ai-exported/integration/reference/hooks-index.md +0 -190
- package/ai-exported/integration/reference/public-api.md +0 -110
- package/ai-exported/integration/reference/querykey-conventions.md +0 -179
- package/ai-exported/migration/README.md +0 -60
- package/ai-exported/migration/ai-rules.md +0 -81
- package/ai-exported/migration/breaking-changes/hook-signatures.md +0 -233
- package/ai-exported/migration/breaking-changes/querykey-conventions.md +0 -108
- package/ai-exported/migration/breaking-changes/result-handling.md +0 -211
- package/ai-exported/migration/breaking-changes/sdk-leakage.md +0 -165
- package/ai-exported/migration/checklist.md +0 -89
- package/ai-exported/migration/features/README.md +0 -34
- package/ai-exported/migration/features/auxiliary-services.md +0 -114
- package/ai-exported/migration/features/bitcoin.md +0 -88
- package/ai-exported/migration/features/bridge.md +0 -123
- package/ai-exported/migration/features/dex.md +0 -101
- package/ai-exported/migration/features/migration.md +0 -120
- package/ai-exported/migration/features/money-market.md +0 -97
- package/ai-exported/migration/features/staking.md +0 -109
- package/ai-exported/migration/features/swap.md +0 -118
- package/ai-exported/migration/recipes.md +0 -188
- package/ai-exported/migration/reference/README.md +0 -15
- package/ai-exported/migration/reference/deleted-hooks.md +0 -110
- package/ai-exported/migration/reference/error-shape-crosswalk.md +0 -144
- package/ai-exported/migration/reference/renamed-hooks.md +0 -66
- package/dist/index.cjs +0 -2642
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -1550
- package/dist/index.mjs.map +0 -1
- package/src/hooks/backend/useBackendSubmitSwapTxStatus.ts +0 -59
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useQuery, type UseQueryResult } from '@tanstack/react-query';
|
|
2
|
+
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
3
|
+
import type { Address } from '@sodax/sdk';
|
|
4
|
+
import type { ReadHookParams } from '../shared/types.js';
|
|
5
|
+
|
|
6
|
+
export type UseLeverageYieldPreviewRedeemParams = ReadHookParams<
|
|
7
|
+
bigint,
|
|
8
|
+
{ vault: Address | undefined; shares: bigint | undefined }
|
|
9
|
+
>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Reads the assets received for redeeming `shares` of a leverage-yield vault (ERC-4626
|
|
13
|
+
* `previewRedeem`). Passing `10n ** 18n` (one share) yields the per-share price that creeps
|
|
14
|
+
* up as the vault accrues interest. Moves slowly, so the default refresh is 60s.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const { data: sharePrice } = useLeverageYieldPreviewRedeem({
|
|
19
|
+
* params: { vault: vault.vault, shares: 10n ** 18n },
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export function useLeverageYieldPreviewRedeem({
|
|
24
|
+
params,
|
|
25
|
+
queryOptions,
|
|
26
|
+
}: UseLeverageYieldPreviewRedeemParams = {}): UseQueryResult<bigint, Error> {
|
|
27
|
+
const { sodax } = useSodaxContext();
|
|
28
|
+
const vault = params?.vault;
|
|
29
|
+
const shares = params?.shares;
|
|
30
|
+
|
|
31
|
+
return useQuery<bigint, Error>({
|
|
32
|
+
queryKey: ['leverageYield', 'previewRedeem', vault, shares?.toString()],
|
|
33
|
+
queryFn: async () => {
|
|
34
|
+
if (!vault || shares === undefined) throw new Error('vault and shares are required');
|
|
35
|
+
const result = await sodax.leverageYield.previewRedeem(vault, shares);
|
|
36
|
+
if (!result.ok) throw result.error;
|
|
37
|
+
return result.value;
|
|
38
|
+
},
|
|
39
|
+
enabled: !!vault && shares !== undefined,
|
|
40
|
+
refetchInterval: 60_000,
|
|
41
|
+
...queryOptions,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { useQueries, type UseQueryResult } from '@tanstack/react-query';
|
|
2
|
+
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
3
|
+
import type { Address, SpokeChainKey } from '@sodax/sdk';
|
|
4
|
+
import type { ReadHookParams } from '../shared/types.js';
|
|
5
|
+
|
|
6
|
+
/** A single `(chain, hub-wallet holder, share balance)` row produced by {@link useLeverageYieldShareBalances}. */
|
|
7
|
+
export type LeverageYieldShareHolding = {
|
|
8
|
+
chainKey: SpokeChainKey;
|
|
9
|
+
holder: Address;
|
|
10
|
+
shares: bigint;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** A `(chainKey, EOA address)` pair the user controls — one per spoke chain they may hold shares under. */
|
|
14
|
+
export type LeverageYieldShareHolder = {
|
|
15
|
+
chainKey: SpokeChainKey;
|
|
16
|
+
address: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type UseLeverageYieldShareBalancesParams = ReadHookParams<
|
|
20
|
+
LeverageYieldShareHolding,
|
|
21
|
+
{ vault: Address | undefined; holders: readonly LeverageYieldShareHolder[] | undefined }
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Reads a user's leverage-vault share (`lsoda*`) balances across every chain they may hold a
|
|
26
|
+
* position under — one query per holder, fanned out via `useQueries`.
|
|
27
|
+
*
|
|
28
|
+
* For each `(chainKey, address)` the holder is resolved to the address that actually owns the
|
|
29
|
+
* shares: the user's **derived hub wallet** on every chain, including the hub itself. Leverage
|
|
30
|
+
* deposits always deliver `lsoda*` to `getUserHubWalletAddress(srcAddress, srcChainKey)` (the
|
|
31
|
+
* CREATE3 user-router on Sonic for a hub-sourced deposit, the per-spoke hub wallet otherwise) —
|
|
32
|
+
* never the bare EOA — so a hub-chain deposit's shares live in the router, not the signing EOA.
|
|
33
|
+
* Resolving the holder the same way the deposit does is what makes a Sonic-sourced position show
|
|
34
|
+
* up here instead of reading a stale zero off the EOA.
|
|
35
|
+
*
|
|
36
|
+
* Returns the raw `useQueries` result array (15s refresh per query); callers aggregate as needed
|
|
37
|
+
* (e.g. sum `shares` for a headline total, or pick the row for the active chain).
|
|
38
|
+
*
|
|
39
|
+
* `useQueries` has no top-level options slot, so `queryOptions` is spread into every individual
|
|
40
|
+
* query config (and applies uniformly to each holder's query).
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const balances = useLeverageYieldShareBalances({ params: { vault: vault.vault, holders } });
|
|
45
|
+
* const total = balances.reduce((acc, q) => acc + (q.data?.shares ?? 0n), 0n);
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export function useLeverageYieldShareBalances({
|
|
49
|
+
params,
|
|
50
|
+
queryOptions,
|
|
51
|
+
}: UseLeverageYieldShareBalancesParams = {}): UseQueryResult<LeverageYieldShareHolding, Error>[] {
|
|
52
|
+
const { sodax } = useSodaxContext();
|
|
53
|
+
const vault = params?.vault;
|
|
54
|
+
const holders = params?.holders;
|
|
55
|
+
|
|
56
|
+
return useQueries({
|
|
57
|
+
queries: (holders ?? []).map(({ chainKey, address }) => ({
|
|
58
|
+
queryKey: ['leverageYield', 'shareBalance', vault, chainKey, address] as const,
|
|
59
|
+
refetchInterval: 15_000,
|
|
60
|
+
...queryOptions,
|
|
61
|
+
enabled: !!vault,
|
|
62
|
+
queryFn: async (): Promise<LeverageYieldShareHolding> => {
|
|
63
|
+
if (!vault) throw new Error('vault is required');
|
|
64
|
+
// Always the derived hub wallet — on the hub chain too. A leverage deposit delivers
|
|
65
|
+
// shares to getUserHubWalletAddress(...), the CREATE3 router on Sonic, never the EOA,
|
|
66
|
+
// so reading balanceOf(EOA) for the hub chain would always come back zero.
|
|
67
|
+
const holder = await sodax.hubProvider.getUserHubWalletAddress(address, chainKey);
|
|
68
|
+
const result = await sodax.leverageYield.getShareBalance(vault, holder);
|
|
69
|
+
if (!result.ok) throw result.error;
|
|
70
|
+
return { chainKey, holder, shares: result.value };
|
|
71
|
+
},
|
|
72
|
+
})),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useQuery, type UseQueryResult } from '@tanstack/react-query';
|
|
2
|
+
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
3
|
+
import type { Address } from '@sodax/sdk';
|
|
4
|
+
import type { ReadHookParams } from '../shared/types.js';
|
|
5
|
+
|
|
6
|
+
export type UseLeverageYieldTotalAssetsParams = ReadHookParams<bigint, { vault: Address | undefined }>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reads a leverage-yield vault's total underlying assets (vault-asset units, 18 decimals) —
|
|
10
|
+
* i.e. its TVL. Moves slowly, so the default refresh is 60s.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const { data: tvl } = useLeverageYieldTotalAssets({ params: { vault: vault.vault } });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function useLeverageYieldTotalAssets({
|
|
18
|
+
params,
|
|
19
|
+
queryOptions,
|
|
20
|
+
}: UseLeverageYieldTotalAssetsParams = {}): UseQueryResult<bigint, Error> {
|
|
21
|
+
const { sodax } = useSodaxContext();
|
|
22
|
+
const vault = params?.vault;
|
|
23
|
+
|
|
24
|
+
return useQuery<bigint, Error>({
|
|
25
|
+
queryKey: ['leverageYield', 'totalAssets', vault],
|
|
26
|
+
queryFn: async () => {
|
|
27
|
+
if (!vault) throw new Error('vault is required');
|
|
28
|
+
const result = await sodax.leverageYield.getTotalAssets(vault);
|
|
29
|
+
if (!result.ok) throw result.error;
|
|
30
|
+
return result.value;
|
|
31
|
+
},
|
|
32
|
+
enabled: !!vault,
|
|
33
|
+
refetchInterval: 60_000,
|
|
34
|
+
...queryOptions,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
2
|
+
import type { SpokeChainKey, VaultSwapActionParams, VaultSwapResponse } from '@sodax/sdk';
|
|
3
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
4
|
+
import type { MutationHookParams } from '../shared/types.js';
|
|
5
|
+
import { useSafeMutation, type SafeUseMutationResult } from '../shared/useSafeMutation.js';
|
|
6
|
+
import { unwrapResult } from '../shared/unwrapResult.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Mutation variables for {@link useLeverageYieldVaultSwap}. Generic over `K extends SpokeChainKey`
|
|
10
|
+
* (defaults to the full union). Sophisticated callers can lock K at the hook call site to narrow
|
|
11
|
+
* the `walletProvider` and `params.srcChainKey` types.
|
|
12
|
+
*/
|
|
13
|
+
export type UseLeverageYieldVaultSwapVars<K extends SpokeChainKey = SpokeChainKey> = Omit<
|
|
14
|
+
VaultSwapActionParams<K, false>,
|
|
15
|
+
'raw'
|
|
16
|
+
>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* React hook for executing an end-to-end leverage-yield vault swap (deposit or withdraw).
|
|
20
|
+
*
|
|
21
|
+
* Spread a `LeverageYieldSwapPayload` built by {@link useLeverageYieldDeposit} /
|
|
22
|
+
* {@link useLeverageYieldWithdraw} into `mutate` alongside the wallet provider:
|
|
23
|
+
* `vaultSwap({ ...payload, walletProvider })`. Runs `sodax.leverageYield.vaultSwap` —
|
|
24
|
+
* create intent → verify → relay → notify solver — so vault flows never touch the
|
|
25
|
+
* generic swap surface.
|
|
26
|
+
*
|
|
27
|
+
* Throws on SDK failure so React Query's native error model engages (`isError`, `error`,
|
|
28
|
+
* `onError`, `retry`). Returns the unwrapped `VaultSwapResponse` on success.
|
|
29
|
+
*/
|
|
30
|
+
export function useLeverageYieldVaultSwap<K extends SpokeChainKey = SpokeChainKey>({
|
|
31
|
+
mutationOptions,
|
|
32
|
+
}: MutationHookParams<VaultSwapResponse, UseLeverageYieldVaultSwapVars<K>> = {}): SafeUseMutationResult<
|
|
33
|
+
VaultSwapResponse,
|
|
34
|
+
Error,
|
|
35
|
+
UseLeverageYieldVaultSwapVars<K>
|
|
36
|
+
> {
|
|
37
|
+
const { sodax } = useSodaxContext();
|
|
38
|
+
const queryClient = useQueryClient();
|
|
39
|
+
|
|
40
|
+
return useSafeMutation<VaultSwapResponse, Error, UseLeverageYieldVaultSwapVars<K>>({
|
|
41
|
+
mutationKey: ['leverageYield', 'vaultSwap'],
|
|
42
|
+
...mutationOptions,
|
|
43
|
+
mutationFn: async vars => unwrapResult(await sodax.leverageYield.vaultSwap({ ...vars, raw: false })),
|
|
44
|
+
onSuccess: async (data, vars, ctx) => {
|
|
45
|
+
queryClient.invalidateQueries({ queryKey: ['shared', 'xBalances', vars.params.srcChainKey] });
|
|
46
|
+
queryClient.invalidateQueries({ queryKey: ['shared', 'xBalances', vars.params.dstChainKey] });
|
|
47
|
+
await mutationOptions?.onSuccess?.(data, vars, ctx);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
2
|
+
import type { LeverageYieldSwapPayload, LeverageYieldSwapWithdrawParams } from '@sodax/sdk';
|
|
3
|
+
import type { MutationHookParams } from '../shared/types.js';
|
|
4
|
+
import { useSafeMutation, type SafeUseMutationResult } from '../shared/useSafeMutation.js';
|
|
5
|
+
import { unwrapResult } from '../shared/unwrapResult.js';
|
|
6
|
+
|
|
7
|
+
/** Mutation variables for {@link useLeverageYieldWithdraw} — the swap-style withdraw inputs. */
|
|
8
|
+
export type UseLeverageYieldWithdrawVars = LeverageYieldSwapWithdrawParams;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Builds the `LeverageYieldSwapPayload` for a leverage-yield withdraw (`lsoda*` shares → any token).
|
|
12
|
+
* The returned payload carries `hubWalletSwap: true`; spread it into
|
|
13
|
+
* {@link useLeverageYieldVaultSwap}, which authorises the hub wallet to spend the shares via a
|
|
14
|
+
* `Connection.sendMessage`.
|
|
15
|
+
*
|
|
16
|
+
* This is a builder, not an executor. Throws on SDK failure so React Query's error model engages;
|
|
17
|
+
* returns the unwrapped `LeverageYieldSwapPayload` on success.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const { mutateAsyncSafe: buildWithdraw } = useLeverageYieldWithdraw();
|
|
22
|
+
* const built = await buildWithdraw({ vault, srcChainKey, srcAddress, dstChainKey, outputToken, inputAmount, minOutputAmount });
|
|
23
|
+
* if (built.ok) vaultSwap({ ...built.value, walletProvider });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function useLeverageYieldWithdraw({
|
|
27
|
+
mutationOptions,
|
|
28
|
+
}: MutationHookParams<LeverageYieldSwapPayload, UseLeverageYieldWithdrawVars> = {}): SafeUseMutationResult<
|
|
29
|
+
LeverageYieldSwapPayload,
|
|
30
|
+
Error,
|
|
31
|
+
UseLeverageYieldWithdrawVars
|
|
32
|
+
> {
|
|
33
|
+
const { sodax } = useSodaxContext();
|
|
34
|
+
|
|
35
|
+
return useSafeMutation<LeverageYieldSwapPayload, Error, UseLeverageYieldWithdrawVars>({
|
|
36
|
+
mutationKey: ['leverageYield', 'withdraw'],
|
|
37
|
+
...mutationOptions,
|
|
38
|
+
mutationFn: async vars => unwrapResult(await sodax.leverageYield.withdraw(vars)),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -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,
|
|
@@ -4,3 +4,7 @@ export * from './useIsTokenApproved.js';
|
|
|
4
4
|
export * from './useApproveToken.js';
|
|
5
5
|
export * from './useSetSwapPreference.js';
|
|
6
6
|
export * from './useFeeClaimSwap.js';
|
|
7
|
+
export * from './usePartnerCancelIntent.js';
|
|
8
|
+
export * from './useGetUserIntent.js';
|
|
9
|
+
export * from './useGetIntentDetails.js';
|
|
10
|
+
export * from './useFeeClaimWithdraw.js';
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// packages/dapp-kit/src/hooks/partner/useFeeClaimWithdraw.ts
|
|
2
|
+
import {
|
|
3
|
+
ChainKeys,
|
|
4
|
+
type Address,
|
|
5
|
+
type BridgeParams,
|
|
6
|
+
type GetWalletProviderType,
|
|
7
|
+
type HubChainKey,
|
|
8
|
+
type SpokeChainKey,
|
|
9
|
+
type TxHashPair,
|
|
10
|
+
} from '@sodax/sdk';
|
|
11
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
12
|
+
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
13
|
+
import type { MutationHookParams } from '../shared/types.js';
|
|
14
|
+
import { useSafeMutation, type SafeUseMutationResult } from '../shared/useSafeMutation.js';
|
|
15
|
+
import { unwrapResult } from '../shared/unwrapResult.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Mutation variables for {@link useFeeClaimWithdraw}. Maps a partner fee balance to a
|
|
19
|
+
* Sonic-sourced bridge: `feeToken` is the wrapped hub-asset address on Sonic
|
|
20
|
+
* (`PartnerFeeClaimAssetBalance.address`); `dstToken` is the original token address on
|
|
21
|
+
* `dstChainKey` (`PartnerFeeClaimAssetBalance.originalAddress`, or `feeToken` for same-chain
|
|
22
|
+
* delivery to a Sonic address).
|
|
23
|
+
*/
|
|
24
|
+
export type UseFeeClaimWithdrawVars = {
|
|
25
|
+
params: {
|
|
26
|
+
srcAddress: string;
|
|
27
|
+
feeToken: Address;
|
|
28
|
+
amount: bigint;
|
|
29
|
+
dstChainKey: SpokeChainKey;
|
|
30
|
+
dstToken: string;
|
|
31
|
+
recipient: string;
|
|
32
|
+
};
|
|
33
|
+
walletProvider: GetWalletProviderType<HubChainKey>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* React hook to withdraw a partner fee token directly to the partner's wallet **without a swap**.
|
|
38
|
+
*
|
|
39
|
+
* For fees the partner wants to keep as-is (the desired output equals the fee token, e.g. BTC→BTC),
|
|
40
|
+
* an auto-swap is impossible — the solver rejects a same-token swap. This bridges the wrapped fee
|
|
41
|
+
* token from Sonic to its native chain (or transfers it on Sonic when `dstChainKey` is Sonic),
|
|
42
|
+
* bypassing the solver entirely.
|
|
43
|
+
*
|
|
44
|
+
* Bridging from Sonic pulls the token via the partner's hub-wallet router, which requires a prior
|
|
45
|
+
* allowance to that spender — a different approval than the ProtocolIntents one used by the swap
|
|
46
|
+
* claim. Use `useBridgeAllowance` / `useBridgeApprove` (with the same mapped bridge params) first.
|
|
47
|
+
*
|
|
48
|
+
* Throws on SDK failure so React Query's native error model engages. Returns the `TxHashPair`.
|
|
49
|
+
*/
|
|
50
|
+
export function useFeeClaimWithdraw({
|
|
51
|
+
mutationOptions,
|
|
52
|
+
}: MutationHookParams<TxHashPair, UseFeeClaimWithdrawVars> = {}): SafeUseMutationResult<
|
|
53
|
+
TxHashPair,
|
|
54
|
+
Error,
|
|
55
|
+
UseFeeClaimWithdrawVars
|
|
56
|
+
> {
|
|
57
|
+
const { sodax } = useSodaxContext();
|
|
58
|
+
const queryClient = useQueryClient();
|
|
59
|
+
|
|
60
|
+
return useSafeMutation<TxHashPair, Error, UseFeeClaimWithdrawVars>({
|
|
61
|
+
mutationKey: ['partner', 'feeClaimWithdraw'],
|
|
62
|
+
...mutationOptions,
|
|
63
|
+
mutationFn: async ({ params, walletProvider }) => {
|
|
64
|
+
const bridgeParams = {
|
|
65
|
+
params: {
|
|
66
|
+
srcChainKey: ChainKeys.SONIC_MAINNET,
|
|
67
|
+
srcAddress: params.srcAddress,
|
|
68
|
+
srcToken: params.feeToken,
|
|
69
|
+
amount: params.amount,
|
|
70
|
+
dstChainKey: params.dstChainKey,
|
|
71
|
+
dstToken: params.dstToken,
|
|
72
|
+
recipient: params.recipient,
|
|
73
|
+
},
|
|
74
|
+
raw: false,
|
|
75
|
+
walletProvider,
|
|
76
|
+
} satisfies BridgeParams<HubChainKey, false>;
|
|
77
|
+
|
|
78
|
+
return unwrapResult(await sodax.bridge.bridge(bridgeParams));
|
|
79
|
+
},
|
|
80
|
+
onSuccess: async (data, vars, ctx) => {
|
|
81
|
+
queryClient.invalidateQueries({
|
|
82
|
+
queryKey: ['partner', 'feeClaim', 'assetsBalances', vars.params.srcAddress],
|
|
83
|
+
});
|
|
84
|
+
queryClient.invalidateQueries({ queryKey: ['shared', 'xBalances', vars.params.dstChainKey] });
|
|
85
|
+
await mutationOptions?.onSuccess?.(data, vars, ctx);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// packages/dapp-kit/src/hooks/partner/useGetIntentDetails.ts
|
|
2
|
+
import type { Hex, Intent } from '@sodax/sdk';
|
|
3
|
+
import { useQuery, type UseQueryResult } from '@tanstack/react-query';
|
|
4
|
+
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
5
|
+
import type { ReadHookParams } from '../shared/types.js';
|
|
6
|
+
|
|
7
|
+
const ZERO_HASH = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
|
8
|
+
|
|
9
|
+
export type UseGetIntentDetailsParams = ReadHookParams<Intent, { intentHash: Hex | undefined }>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* React hook to fetch the full {@link Intent} details for a ProtocolIntents intent hash.
|
|
13
|
+
*
|
|
14
|
+
* Pair with {@link useGetUserIntent} to display a stuck intent (e.g. its locked `inputAmount`)
|
|
15
|
+
* before recovering it. Disabled when the hash is missing or the zero hash (no intent). Throws
|
|
16
|
+
* on `!ok`.
|
|
17
|
+
*/
|
|
18
|
+
export function useGetIntentDetails({
|
|
19
|
+
params,
|
|
20
|
+
queryOptions,
|
|
21
|
+
}: UseGetIntentDetailsParams = {}): UseQueryResult<Intent, Error> {
|
|
22
|
+
const { sodax } = useSodaxContext();
|
|
23
|
+
const intentHash = params?.intentHash;
|
|
24
|
+
const hasIntent = !!intentHash && intentHash !== ZERO_HASH;
|
|
25
|
+
|
|
26
|
+
return useQuery<Intent, Error>({
|
|
27
|
+
queryKey: ['partner', 'feeClaim', 'intentDetails', intentHash],
|
|
28
|
+
queryFn: async () => {
|
|
29
|
+
if (!intentHash) {
|
|
30
|
+
throw new Error('intentHash is required');
|
|
31
|
+
}
|
|
32
|
+
const result = await sodax.partners.feeClaim.getIntentDetails(intentHash);
|
|
33
|
+
if (!result.ok) throw result.error;
|
|
34
|
+
return result.value;
|
|
35
|
+
},
|
|
36
|
+
enabled: hasIntent,
|
|
37
|
+
...queryOptions,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// packages/dapp-kit/src/hooks/partner/useGetUserIntent.ts
|
|
2
|
+
import type { GetUserIntentParams, Hex } from '@sodax/sdk';
|
|
3
|
+
import { useQuery, type UseQueryResult } from '@tanstack/react-query';
|
|
4
|
+
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
5
|
+
import type { ReadHookParams } from '../shared/types.js';
|
|
6
|
+
|
|
7
|
+
export type UseGetUserIntentParams = ReadHookParams<Hex, Partial<GetUserIntentParams>>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* React hook to look up the stored intent hash for a partner's `(user, fromToken, toToken)` pair.
|
|
11
|
+
*
|
|
12
|
+
* A non-zero hash means an open auto-swap intent exists for that token pair (e.g. an unfilled
|
|
13
|
+
* same-token claim) and can be recovered via {@link usePartnerCancelIntent}. Disabled until all
|
|
14
|
+
* three inputs are present. Throws on `!ok`.
|
|
15
|
+
*/
|
|
16
|
+
export function useGetUserIntent({ params, queryOptions }: UseGetUserIntentParams = {}): UseQueryResult<Hex, Error> {
|
|
17
|
+
const { sodax } = useSodaxContext();
|
|
18
|
+
const user = params?.user;
|
|
19
|
+
const fromToken = params?.fromToken;
|
|
20
|
+
const toToken = params?.toToken;
|
|
21
|
+
|
|
22
|
+
return useQuery<Hex, Error>({
|
|
23
|
+
queryKey: ['partner', 'feeClaim', 'userIntent', user, fromToken, toToken],
|
|
24
|
+
queryFn: async () => {
|
|
25
|
+
if (!user || !fromToken || !toToken) {
|
|
26
|
+
throw new Error('user, fromToken and toToken are required');
|
|
27
|
+
}
|
|
28
|
+
const result = await sodax.partners.feeClaim.getUserIntent({ user, fromToken, toToken });
|
|
29
|
+
if (!result.ok) throw result.error;
|
|
30
|
+
return result.value;
|
|
31
|
+
},
|
|
32
|
+
enabled: !!user && !!fromToken && !!toToken,
|
|
33
|
+
...queryOptions,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// packages/dapp-kit/src/hooks/partner/usePartnerCancelIntent.ts
|
|
2
|
+
import type { HubChainKey, PartnerFeeClaimCancelAction, TxReturnType } from '@sodax/sdk';
|
|
3
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
4
|
+
import { useSodaxContext } from '../shared/useSodaxContext.js';
|
|
5
|
+
import type { MutationHookParams } from '../shared/types.js';
|
|
6
|
+
import { useSafeMutation, type SafeUseMutationResult } from '../shared/useSafeMutation.js';
|
|
7
|
+
import { unwrapResult } from '../shared/unwrapResult.js';
|
|
8
|
+
|
|
9
|
+
export type UsePartnerCancelIntentVars = Omit<PartnerFeeClaimCancelAction<HubChainKey, false>, 'raw'>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* React hook to cancel a stuck partner fee-claim auto-swap intent and recover the locked tokens.
|
|
13
|
+
*
|
|
14
|
+
* Calls `ProtocolIntents.cancelIntent(fromToken, toToken)` — the only authorized cancel path for
|
|
15
|
+
* partner auto-swap intents (the generic swap-cancel reverts because ProtocolIntents, not the
|
|
16
|
+
* partner, is the intent creator). The contract cancels the intent and refunds the input amount to
|
|
17
|
+
* the partner. Use this to recover funds from an unfillable same-token claim.
|
|
18
|
+
*
|
|
19
|
+
* Throws on SDK failure so React Query's native error model engages. Returns the transaction hash.
|
|
20
|
+
*/
|
|
21
|
+
export function usePartnerCancelIntent({
|
|
22
|
+
mutationOptions,
|
|
23
|
+
}: MutationHookParams<TxReturnType<HubChainKey, false>, UsePartnerCancelIntentVars> = {}): SafeUseMutationResult<
|
|
24
|
+
TxReturnType<HubChainKey, false>,
|
|
25
|
+
Error,
|
|
26
|
+
UsePartnerCancelIntentVars
|
|
27
|
+
> {
|
|
28
|
+
const { sodax } = useSodaxContext();
|
|
29
|
+
const queryClient = useQueryClient();
|
|
30
|
+
|
|
31
|
+
return useSafeMutation<TxReturnType<HubChainKey, false>, Error, UsePartnerCancelIntentVars>({
|
|
32
|
+
mutationKey: ['partner', 'cancelIntent'],
|
|
33
|
+
...mutationOptions,
|
|
34
|
+
mutationFn: async vars => unwrapResult(await sodax.partners.feeClaim.cancelIntent<false>({ ...vars, raw: false })),
|
|
35
|
+
onSuccess: async (data, vars, ctx) => {
|
|
36
|
+
queryClient.invalidateQueries({
|
|
37
|
+
queryKey: ['partner', 'feeClaim', 'assetsBalances', vars.params.srcAddress],
|
|
38
|
+
});
|
|
39
|
+
queryClient.invalidateQueries({
|
|
40
|
+
queryKey: [
|
|
41
|
+
'partner',
|
|
42
|
+
'feeClaim',
|
|
43
|
+
'userIntent',
|
|
44
|
+
vars.params.srcAddress,
|
|
45
|
+
vars.params.fromToken,
|
|
46
|
+
vars.params.toToken,
|
|
47
|
+
],
|
|
48
|
+
});
|
|
49
|
+
await mutationOptions?.onSuccess?.(data, vars, ctx);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './isUserRejectedError.js';
|
|
1
2
|
export * from './types.js';
|
|
2
3
|
export * from './unwrapResult.js';
|
|
3
4
|
export * from './useSafeMutation.js';
|
|
@@ -6,5 +7,8 @@ export * from './useEstimateGas.js';
|
|
|
6
7
|
export * from './useDeriveUserWalletAddress.js';
|
|
7
8
|
export * from './useStellarTrustlineCheck.js';
|
|
8
9
|
export * from './useRequestTrustline.js';
|
|
10
|
+
export * from './useNearStorageCheck.js';
|
|
11
|
+
export * from './useRegisterNearStorage.js';
|
|
12
|
+
export * from './useNearStorageGate.js';
|
|
9
13
|
export * from './useGetUserHubWalletAddress.js';
|
|
10
14
|
export * from './useXBalances.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SodaxError } from '@sodax/sdk';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { isUserRejectedError } from './isUserRejectedError.js';
|
|
4
|
+
|
|
5
|
+
describe('isUserRejectedError', () => {
|
|
6
|
+
it('matches SodaxError with code USER_REJECTED', () => {
|
|
7
|
+
const err = new SodaxError('USER_REJECTED', 'User rejected the request', { feature: 'swap' });
|
|
8
|
+
expect(isUserRejectedError(err)).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('does not match SodaxError with any other code', () => {
|
|
12
|
+
const cases: SodaxError[] = [
|
|
13
|
+
new SodaxError('INTENT_CREATION_FAILED', 'sim revert', { feature: 'swap' }),
|
|
14
|
+
new SodaxError('VALIDATION_FAILED', 'user rejected by invariant', { feature: 'staking' }),
|
|
15
|
+
new SodaxError('APPROVE_FAILED', 'allowance', { feature: 'moneyMarket' }),
|
|
16
|
+
new SodaxError('EXECUTION_FAILED', 'orchestrator', { feature: 'bridge' }),
|
|
17
|
+
new SodaxError('UNKNOWN', 'fallback', { feature: 'dex' }),
|
|
18
|
+
];
|
|
19
|
+
for (const err of cases) expect(isUserRejectedError(err)).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('does not match a plain Error whose message contains rejection prose', () => {
|
|
23
|
+
// The predicate trusts SDK classification; it does NOT re-scan message content. A bare
|
|
24
|
+
// Error from outside the SDK boundary (e.g. a non-canonical throw in a queryFn) is not a
|
|
25
|
+
// canonical USER_REJECTED — it should fall through to the generic failure UI.
|
|
26
|
+
expect(isUserRejectedError(new Error('User rejected the request'))).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('does not match viem-shaped objects or wallet-library shapes', () => {
|
|
30
|
+
expect(isUserRejectedError({ name: 'UserRejectedRequestError', message: 'denied' })).toBe(false);
|
|
31
|
+
expect(isUserRejectedError({ code: 4001, message: 'reject' })).toBe(false);
|
|
32
|
+
expect(isUserRejectedError({ code: 'ACTION_REJECTED' })).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('does not match non-error values', () => {
|
|
36
|
+
expect(isUserRejectedError(undefined)).toBe(false);
|
|
37
|
+
expect(isUserRejectedError(null)).toBe(false);
|
|
38
|
+
expect(isUserRejectedError('User rejected the request')).toBe(false);
|
|
39
|
+
expect(isUserRejectedError({})).toBe(false);
|
|
40
|
+
expect(isUserRejectedError(0)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { isSodaxError } from '@sodax/sdk';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Predicate for the canonical "user cancelled the wallet prompt" outcome.
|
|
5
|
+
*
|
|
6
|
+
* The SDK classifies wallet rejection at the source: `intentCreationFailed` / `approveFailed`
|
|
7
|
+
* wrappers emit `SodaxError<'USER_REJECTED'>` whenever the underlying cause matches a known
|
|
8
|
+
* rejection shape (viem `4001` / `UserRejectedRequestError`, ICON Hana `CANCEL_SIGNING`,
|
|
9
|
+
* Solana / Sui / Stellar / Stacks / NEAR / Injective name + message patterns). Consumers
|
|
10
|
+
* branch on this code to render a "Cancelled" UI instead of a "Failed" toast — user
|
|
11
|
+
* cancellation is a normal flow, not a failure.
|
|
12
|
+
*
|
|
13
|
+
* Use after `mutateAsyncSafe` (or inside an `onError` / `catch`):
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* const result = await swap.mutateAsyncSafe({ params, walletProvider });
|
|
17
|
+
* if (!result.ok) {
|
|
18
|
+
* if (isUserRejectedError(result.error)) return; // silent — user cancelled
|
|
19
|
+
* toast.error(getErrorMessage(result.error)); // real failure
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* The check trusts the SDK's classification — it does NOT re-scan message content. Errors
|
|
24
|
+
* raised outside the SDK boundary (e.g. a non-SodaxError thrown from a `queryFn`) will not
|
|
25
|
+
* match, which is intentional: only the SDK's `USER_REJECTED` is the canonical "cancelled"
|
|
26
|
+
* signal.
|
|
27
|
+
*/
|
|
28
|
+
export function isUserRejectedError(error: unknown): boolean {
|
|
29
|
+
return isSodaxError(error) && error.code === 'USER_REJECTED';
|
|
30
|
+
}
|
|
@@ -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,
|