@symmio/trading-react 3.1.0 → 3.3.0
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/dist/estimated-price/use-estimated-price.d.ts +6 -3
- package/dist/estimated-price/use-estimated-price.d.ts.map +1 -1
- package/dist/estimated-price/use-estimated-price.js +12 -9
- package/dist/estimated-price/use-estimated-price.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +193 -190
- package/dist/instant-layer/get-instant-open-funding-error.d.ts +5 -0
- package/dist/instant-layer/get-instant-open-funding-error.d.ts.map +1 -0
- package/dist/instant-layer/get-instant-open-funding-error.js +15 -0
- package/dist/instant-layer/get-instant-open-funding-error.js.map +1 -0
- package/dist/instant-layer/index.d.ts +2 -0
- package/dist/instant-layer/index.d.ts.map +1 -1
- package/dist/instant-layer/index.js +18 -16
- package/dist/instant-layer/use-instant-close-fees.d.ts +46 -0
- package/dist/instant-layer/use-instant-close-fees.d.ts.map +1 -0
- package/dist/instant-layer/use-instant-close-fees.js +83 -0
- package/dist/instant-layer/use-instant-close-fees.js.map +1 -0
- package/dist/instant-layer/use-instant-open-auto.d.ts +8 -3
- package/dist/instant-layer/use-instant-open-auto.d.ts.map +1 -1
- package/dist/instant-layer/use-instant-open-auto.js.map +1 -1
- package/dist/instant-layer/use-instant-open-fees.d.ts +40 -10
- package/dist/instant-layer/use-instant-open-fees.d.ts.map +1 -1
- package/dist/instant-layer/use-instant-open-fees.js +23 -98
- package/dist/instant-layer/use-instant-open-fees.js.map +1 -1
- package/dist/instant-layer/use-instant-open-inputs.d.ts +37 -0
- package/dist/instant-layer/use-instant-open-inputs.d.ts.map +1 -0
- package/dist/instant-layer/use-instant-open-inputs.js +136 -0
- package/dist/instant-layer/use-instant-open-inputs.js.map +1 -0
- package/dist/instant-layer/use-instant-open-with-tpsl.d.ts +6 -0
- package/dist/instant-layer/use-instant-open-with-tpsl.d.ts.map +1 -1
- package/dist/instant-layer/use-instant-open-with-tpsl.js.map +1 -1
- package/dist/instant-layer/use-prepare-instant-open-params.d.ts +78 -0
- package/dist/instant-layer/use-prepare-instant-open-params.d.ts.map +1 -0
- package/dist/instant-layer/use-prepare-instant-open-params.js +38 -0
- package/dist/instant-layer/use-prepare-instant-open-params.js.map +1 -0
- package/dist/margin/use-available-instant-open-margin.d.ts +3 -2
- package/dist/margin/use-available-instant-open-margin.d.ts.map +1 -1
- package/dist/margin/use-available-instant-open-margin.js +0 -1
- package/dist/margin/use-available-instant-open-margin.js.map +1 -1
- package/dist/solvers/index.d.ts +1 -0
- package/dist/solvers/index.d.ts.map +1 -1
- package/dist/solvers/use-solver-info.d.ts +14 -0
- package/dist/solvers/use-solver-info.d.ts.map +1 -0
- package/dist/solvers/use-solver-info.js +27 -0
- package/dist/solvers/use-solver-info.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSymmioChainId as e } from "../provider/use-symmio-chain-id.js";
|
|
3
|
+
import { useSymmioConfig as ee } from "../provider/use-symmio-config.js";
|
|
4
|
+
import { useFeeForUser as te } from "../fees/use-fee-for-user.js";
|
|
5
|
+
import { useMarkets as ne } from "../markets/use-markets.js";
|
|
6
|
+
import { usePriceByName as re } from "../price-service/use-price-by-name.js";
|
|
7
|
+
import { useSolverInfo as ie } from "../solvers/use-solver-info.js";
|
|
8
|
+
import { useEstimatedPrice as t } from "../estimated-price/use-estimated-price.js";
|
|
9
|
+
import { useLockedParams as n } from "../locked-params/use-locked-params.js";
|
|
10
|
+
import { getInstantOpenFundingError as r } from "./get-instant-open-funding-error.js";
|
|
11
|
+
import { PositionType as i, calculateTradeParams as a } from "@symmio/trading-core";
|
|
12
|
+
import { useMemo as ae } from "react";
|
|
13
|
+
//#region src/instant-layer/use-instant-open-inputs.ts
|
|
14
|
+
function o(o, s, c = !0) {
|
|
15
|
+
let l = ee(o), u = e(), d = o.chainId ?? u, { subAccountAddress: f, market: p, positionType: m, initialMargin: oe, fund: h, leverage: g, solverId: _ } = o, v = h !== void 0, y = v || o.availableBalance !== void 0, b = s === "prepare" || y, se = s === "prepare" && l.getChainConfig(d).contractsVersion === "0.8.6", x = v ? h.balance : oe ?? "", S = c && x.length > 0, C = l.getSolver({
|
|
16
|
+
chainId: d,
|
|
17
|
+
solverId: _
|
|
18
|
+
}).id === "enigma", w = S ? r({
|
|
19
|
+
...o,
|
|
20
|
+
isLowcap: C
|
|
21
|
+
}) : void 0, T = S && w === void 0, E = ne({
|
|
22
|
+
config: o.config,
|
|
23
|
+
chainId: d,
|
|
24
|
+
solverId: _,
|
|
25
|
+
query: {
|
|
26
|
+
enabled: T,
|
|
27
|
+
staleTime: 3e5
|
|
28
|
+
}
|
|
29
|
+
}).data?.find((e) => e.symbolId === p.id), D = p.name ?? E?.name, O = p.pricePrecision ?? E?.pricePrecision, k = p.quantityPrecision ?? E?.quantityPrecision, A = p.hedgerFeeOpen ?? E?.hedgerFeeOpen, j = p.hedgerFeeClose ?? E?.hedgerFeeClose, M = p.minOpenSolverFeeCap ?? (E ? E.kind === "enigma" ? E.minOpenSolverFeeCap : "0" : void 0), N = p.minCloseSolverFeeCap ?? (E ? E.kind === "enigma" ? E.minCloseSolverFeeCap : "0" : void 0), ce = !se || M !== void 0 && N !== void 0, P = E?.kind === "enigma" ? E : void 0, le = p.hedgerFeeCloseEarlyRate ?? P?.hedgerFeeCloseEarlyRate ?? j, ue = p.hedgerFeeCloseEarlyThreshold ?? P?.hedgerFeeCloseEarlyThreshold ?? 0, F = p.hedgerFeeCloseStandardThreshold ?? P?.hedgerFeeCloseStandardThreshold ?? 0, I = p.minAcceptablePortionLf ?? E?.minAcceptablePortionLf, L = p.minAcceptableQuoteValue ?? E?.minAcceptableQuoteValue, R = p.maxNotionalValue ?? E?.maxNotionalValue, z = p.minNotionalValue ?? E?.minNotionalValue, B = p.maxQuantity ?? E?.maxQuantity, V = p.lotSize ?? E?.lotSize, H = !y || I !== void 0 && L !== void 0 && R !== void 0 && z !== void 0 && B !== void 0 && V !== void 0, U = o.lockedParamPercent?.cva !== void 0 && o.lockedParamPercent.lf !== void 0 && o.lockedParamPercent.partyAmm !== void 0 && o.lockedParamPercent.partyBmm !== void 0, W = n({
|
|
30
|
+
config: o.config,
|
|
31
|
+
chainId: d,
|
|
32
|
+
solverId: _,
|
|
33
|
+
symbol: D ?? "",
|
|
34
|
+
leverage: g,
|
|
35
|
+
query: {
|
|
36
|
+
enabled: T && b && !U && D !== void 0,
|
|
37
|
+
staleTime: 3e5
|
|
38
|
+
}
|
|
39
|
+
}), G = U ? o.lockedParamPercent : W.data ? {
|
|
40
|
+
cva: W.data.cva ?? "0",
|
|
41
|
+
lf: W.data.lf ?? "0",
|
|
42
|
+
partyAmm: W.data.partyAmm ?? "0",
|
|
43
|
+
partyBmm: W.data.partyBmm ?? "0"
|
|
44
|
+
} : void 0, de = !b || G !== void 0, K = ie({
|
|
45
|
+
config: o.config,
|
|
46
|
+
chainId: d,
|
|
47
|
+
solverId: _,
|
|
48
|
+
query: {
|
|
49
|
+
enabled: T && C && o.solverInfo === void 0,
|
|
50
|
+
staleTime: 3e5,
|
|
51
|
+
retry: !1
|
|
52
|
+
}
|
|
53
|
+
}), q = o.solverInfo ?? K.data ?? (K.isError ? {} : void 0), fe = !C || q !== void 0, pe = te({
|
|
54
|
+
config: o.config,
|
|
55
|
+
chainId: d,
|
|
56
|
+
user: f,
|
|
57
|
+
symbolId: BigInt(p.id),
|
|
58
|
+
query: {
|
|
59
|
+
enabled: T && o.feeRates === void 0,
|
|
60
|
+
staleTime: 3e5
|
|
61
|
+
}
|
|
62
|
+
}), J = o.feeRates ?? pe.data, me = re({
|
|
63
|
+
name: D,
|
|
64
|
+
solverId: _,
|
|
65
|
+
enabled: T && o.markPrice === void 0
|
|
66
|
+
}), Y = o.markPrice ?? me.markPrice ?? void 0, X = ae(() => !T || !C || Y === void 0 || O === void 0 || k === void 0 ? "" : a({
|
|
67
|
+
markPrice: Y,
|
|
68
|
+
slippage: 0,
|
|
69
|
+
positionType: m,
|
|
70
|
+
userInput: x,
|
|
71
|
+
inputField: "PRICE",
|
|
72
|
+
leverage: g,
|
|
73
|
+
pricePrecision: O,
|
|
74
|
+
quantityPrecision: k
|
|
75
|
+
})?.quantity ?? "", [
|
|
76
|
+
T,
|
|
77
|
+
C,
|
|
78
|
+
Y,
|
|
79
|
+
O,
|
|
80
|
+
k,
|
|
81
|
+
m,
|
|
82
|
+
x,
|
|
83
|
+
g
|
|
84
|
+
]), he = Y === void 0 ? "" : String(Number(Y) * (m === i.SHORT ? .5 : 1.5)), Z = t({
|
|
85
|
+
config: o.config,
|
|
86
|
+
chainId: d,
|
|
87
|
+
solverId: _,
|
|
88
|
+
symbolId: p.id,
|
|
89
|
+
quantity: X,
|
|
90
|
+
positionType: m,
|
|
91
|
+
entry: "open",
|
|
92
|
+
price: he,
|
|
93
|
+
query: {
|
|
94
|
+
enabled: T && C && o.estimatedOpenPrice === void 0 && X.length > 0,
|
|
95
|
+
staleTime: 3e4
|
|
96
|
+
}
|
|
97
|
+
}), ge = C && o.estimatedOpenPrice === void 0 && (Z.isDebouncing || Z.isFetching), Q = !C || o.estimatedOpenPrice !== void 0 || !ge && (Z.isFetched || Z.isError), $ = o.estimatedOpenPrice === void 0 ? Z.data?.estimatedPrice : o.estimatedOpenPrice, _e = !C || !Q ? void 0 : $ && $ !== "0" ? $ : null;
|
|
98
|
+
return {
|
|
99
|
+
config: l,
|
|
100
|
+
validationError: w,
|
|
101
|
+
ready: T && D !== void 0 && O !== void 0 && k !== void 0 && J !== void 0 && Y !== void 0 && de && (!C || A !== void 0 && j !== void 0) && H && ce && fe && Q,
|
|
102
|
+
refetchEstimatedPrice: Z.refetch,
|
|
103
|
+
estimatedOpenPriceTimestamp: Z.isPlaceholderData ? void 0 : Z.dataUpdatedAt || void 0,
|
|
104
|
+
prefills: {
|
|
105
|
+
chainId: d,
|
|
106
|
+
market: {
|
|
107
|
+
id: p.id,
|
|
108
|
+
name: D,
|
|
109
|
+
pricePrecision: O,
|
|
110
|
+
quantityPrecision: k,
|
|
111
|
+
minOpenSolverFeeCap: M,
|
|
112
|
+
minCloseSolverFeeCap: N,
|
|
113
|
+
hedgerFeeOpen: A,
|
|
114
|
+
hedgerFeeClose: j,
|
|
115
|
+
hedgerFeeCloseEarlyRate: le,
|
|
116
|
+
hedgerFeeCloseEarlyThreshold: ue,
|
|
117
|
+
hedgerFeeCloseStandardThreshold: F,
|
|
118
|
+
minAcceptablePortionLf: I,
|
|
119
|
+
minAcceptableQuoteValue: L,
|
|
120
|
+
maxNotionalValue: R,
|
|
121
|
+
minNotionalValue: z,
|
|
122
|
+
maxQuantity: B,
|
|
123
|
+
lotSize: V
|
|
124
|
+
},
|
|
125
|
+
markPrice: Y,
|
|
126
|
+
feeRates: J,
|
|
127
|
+
lockedParamPercent: G,
|
|
128
|
+
solverInfo: q,
|
|
129
|
+
estimatedOpenPrice: _e
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
134
|
+
export { o as useInstantOpenInputs };
|
|
135
|
+
|
|
136
|
+
//# sourceMappingURL=use-instant-open-inputs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-instant-open-inputs.js","names":[],"sources":["../../src/instant-layer/use-instant-open-inputs.ts"],"sourcesContent":["\"use client\";\n\nimport {\n calculateTradeParams,\n PositionType,\n type ConfigParameter,\n type GetInstantOpenFeesParameters,\n} from \"@symmio/trading-core\";\nimport { useMemo } from \"react\";\nimport { useEstimatedPrice } from \"../estimated-price/use-estimated-price\";\nimport { useFeeForUser } from \"../fees/use-fee-for-user\";\nimport { useLockedParams } from \"../locked-params/use-locked-params\";\nimport { useMarkets } from \"../markets/use-markets\";\nimport { usePriceByName } from \"../price-service/use-price-by-name\";\nimport { useSymmioChainId } from \"../provider/use-symmio-chain-id\";\nimport { useSymmioConfig } from \"../provider/use-symmio-config\";\nimport { useSolverInfo } from \"../solvers/use-solver-info\";\nimport { getInstantOpenFundingError } from \"./get-instant-open-funding-error\";\n\n/** Shared input orchestration for preparation and fee previews; calculations stay in core. */\nexport function useInstantOpenInputs(\n parameters: GetInstantOpenFeesParameters & ConfigParameter,\n purpose: \"prepare\" | \"fees\",\n queryEnabled = true,\n) {\n const config = useSymmioConfig(parameters);\n const contextChainId = useSymmioChainId();\n const chainId = parameters.chainId ?? contextChainId;\n const { subAccountAddress, market, positionType, initialMargin, fund, leverage, solverId } = parameters;\n const isFullBalance = fund !== undefined;\n const canSizeFullBalance = isFullBalance || parameters.availableBalance !== undefined;\n const needsLockedParams = purpose === \"prepare\" || canSizeFullBalance;\n const needsSolverFeeCaps = purpose === \"prepare\" && config.getChainConfig(chainId).contractsVersion === \"0.8.6\";\n /** The amount driving the sizing math — the typed margin, or the whole balance in full-balance mode. */\n const sizingInput = isFullBalance ? fund.balance : (initialMargin ?? \"\");\n const requested = queryEnabled && sizingInput.length > 0;\n\n /** The dry-run estimate exists only on lowcap (Enigma) solvers. */\n const isLowcap = config.getSolver({ chainId, solverId }).id === \"enigma\";\n const validationError = requested ? getInstantOpenFundingError({ ...parameters, isLowcap }) : undefined;\n const enabled = requested && validationError === undefined;\n\n // Market metadata — slow-moving; dedupes with any other useMarkets on the page.\n const marketsQuery = useMarkets({\n config: parameters.config,\n chainId,\n solverId,\n query: { enabled, staleTime: 300_000 },\n });\n const resolvedMarket = marketsQuery.data?.find((entry) => entry.symbolId === market.id);\n const marketName = market.name ?? resolvedMarket?.name;\n const pricePrecision = market.pricePrecision ?? resolvedMarket?.pricePrecision;\n const quantityPrecision = market.quantityPrecision ?? resolvedMarket?.quantityPrecision;\n const hedgerFeeOpen = market.hedgerFeeOpen ?? resolvedMarket?.hedgerFeeOpen;\n const hedgerFeeClose = market.hedgerFeeClose ?? resolvedMarket?.hedgerFeeClose;\n\n const minOpenSolverFeeCap =\n market.minOpenSolverFeeCap ??\n (resolvedMarket ? (resolvedMarket.kind === \"enigma\" ? resolvedMarket.minOpenSolverFeeCap : \"0\") : undefined);\n const minCloseSolverFeeCap =\n market.minCloseSolverFeeCap ??\n (resolvedMarket ? (resolvedMarket.kind === \"enigma\" ? resolvedMarket.minCloseSolverFeeCap : \"0\") : undefined);\n const capsReady = !needsSolverFeeCaps || (minOpenSolverFeeCap !== undefined && minCloseSolverFeeCap !== undefined);\n\n // Time-decaying close-fee rates ride the same `useMarkets` read — Enigma-only\n // fields on `EnigmaMarket`, so narrow on `kind` before reading.\n const resolvedEnigmaMarket = resolvedMarket?.kind === \"enigma\" ? resolvedMarket : undefined;\n const hedgerFeeCloseEarlyRate =\n market.hedgerFeeCloseEarlyRate ?? resolvedEnigmaMarket?.hedgerFeeCloseEarlyRate ?? hedgerFeeClose;\n const hedgerFeeCloseEarlyThreshold =\n market.hedgerFeeCloseEarlyThreshold ?? resolvedEnigmaMarket?.hedgerFeeCloseEarlyThreshold ?? 0;\n const hedgerFeeCloseStandardThreshold =\n market.hedgerFeeCloseStandardThreshold ?? resolvedEnigmaMarket?.hedgerFeeCloseStandardThreshold ?? 0;\n\n // Quote constraints — explicit or automatic full-balance sizing snaps to the lot grid and checks the\n // published floors; they ride the same `useMarkets` read.\n const minAcceptablePortionLf = market.minAcceptablePortionLf ?? resolvedMarket?.minAcceptablePortionLf;\n const minAcceptableQuoteValue = market.minAcceptableQuoteValue ?? resolvedMarket?.minAcceptableQuoteValue;\n const maxNotionalValue = market.maxNotionalValue ?? resolvedMarket?.maxNotionalValue;\n const minNotionalValue = market.minNotionalValue ?? resolvedMarket?.minNotionalValue;\n const maxQuantity = market.maxQuantity ?? resolvedMarket?.maxQuantity;\n const lotSize = market.lotSize ?? resolvedMarket?.lotSize;\n const constraintsReady =\n !canSizeFullBalance ||\n (minAcceptablePortionLf !== undefined &&\n minAcceptableQuoteValue !== undefined &&\n maxNotionalValue !== undefined &&\n minNotionalValue !== undefined &&\n maxQuantity !== undefined &&\n lotSize !== undefined);\n\n // Preparation always needs locks; the fee preview needs them only for balance sizing.\n const locksPrefilled =\n parameters.lockedParamPercent?.cva !== undefined &&\n parameters.lockedParamPercent.lf !== undefined &&\n parameters.lockedParamPercent.partyAmm !== undefined &&\n parameters.lockedParamPercent.partyBmm !== undefined;\n const lockedParamsQuery = useLockedParams({\n config: parameters.config,\n chainId,\n solverId,\n symbol: marketName ?? \"\",\n leverage,\n query: {\n enabled: enabled && needsLockedParams && !locksPrefilled && marketName !== undefined,\n staleTime: 300_000,\n },\n });\n const lockedParamPercent = locksPrefilled\n ? parameters.lockedParamPercent\n : lockedParamsQuery.data\n ? {\n // The resolver skips its fetch only when all four fields are present —\n // mirror its `\"0\"` defaults for fields the solver omits.\n cva: lockedParamsQuery.data.cva ?? \"0\",\n lf: lockedParamsQuery.data.lf ?? \"0\",\n partyAmm: lockedParamsQuery.data.partyAmm ?? \"0\",\n partyBmm: lockedParamsQuery.data.partyBmm ?? \"0\",\n }\n : undefined;\n const lockedParamsReady = !needsLockedParams || lockedParamPercent !== undefined;\n\n // Static solver fees follow the same cached input path as the other prefills.\n // A failed optional /info read keeps core's existing zero-static-fee fallback.\n const solverInfoQuery = useSolverInfo({\n config: parameters.config,\n chainId,\n solverId,\n query: { enabled: enabled && isLowcap && parameters.solverInfo === undefined, staleTime: 300_000, retry: false },\n });\n const solverInfo = parameters.solverInfo ?? solverInfoQuery.data ?? (solverInfoQuery.isError ? {} : undefined);\n const solverInfoReady = !isLowcap || solverInfo !== undefined;\n\n // On-chain platform fee rates — contract state, changes rarely; cached hard.\n const feeQuery = useFeeForUser({\n config: parameters.config,\n chainId,\n user: subAccountAddress,\n symbolId: BigInt(market.id),\n query: { enabled: enabled && parameters.feeRates === undefined, staleTime: 300_000 },\n });\n const feeRates = parameters.feeRates ?? feeQuery.data;\n\n // Mark price — caller's cache-hot value wins; otherwise the shared stream.\n const priceQuery = usePriceByName({\n name: marketName,\n solverId,\n enabled: enabled && parameters.markPrice === undefined,\n });\n const markPrice = parameters.markPrice ?? priceQuery.markPrice ?? undefined;\n\n // Lowcap dry-run estimate: mark-sized quantity, wide fixed request bound so the\n // solver's price gate never rejects the question. Debounced internally.\n const estimateQuantity = useMemo(() => {\n if (!enabled || !isLowcap || markPrice === undefined) return \"\";\n if (pricePrecision === undefined || quantityPrecision === undefined) return \"\";\n const sized = calculateTradeParams({\n markPrice,\n slippage: 0,\n positionType,\n userInput: sizingInput,\n inputField: \"PRICE\",\n leverage,\n pricePrecision,\n quantityPrecision,\n });\n return sized?.quantity ?? \"\";\n }, [enabled, isLowcap, markPrice, pricePrecision, quantityPrecision, positionType, sizingInput, leverage]);\n const estimateRequestPrice =\n markPrice !== undefined ? String(Number(markPrice) * (positionType === PositionType.SHORT ? 0.5 : 1.5)) : \"\";\n const estimateQuery = useEstimatedPrice({\n config: parameters.config,\n chainId,\n solverId,\n symbolId: market.id,\n quantity: estimateQuantity,\n positionType,\n entry: \"open\",\n price: estimateRequestPrice,\n query: {\n enabled: enabled && isLowcap && parameters.estimatedOpenPrice === undefined && estimateQuantity.length > 0,\n staleTime: 30_000,\n },\n });\n // undefined = pending, string = available, null = settled but unavailable.\n // Pass null through to core so a failed cached request is not fetched again.\n const estimateUpdating =\n isLowcap && parameters.estimatedOpenPrice === undefined && (estimateQuery.isDebouncing || estimateQuery.isFetching);\n const estimateSettled =\n !isLowcap ||\n parameters.estimatedOpenPrice !== undefined ||\n (!estimateUpdating && (estimateQuery.isFetched || estimateQuery.isError));\n const resolvedEstimate =\n parameters.estimatedOpenPrice !== undefined ? parameters.estimatedOpenPrice : estimateQuery.data?.estimatedPrice;\n const estimatedOpenPrice =\n !isLowcap || !estimateSettled ? undefined : resolvedEstimate && resolvedEstimate !== \"0\" ? resolvedEstimate : null;\n\n // Every prefill present → the query function is pure, zero fetches.\n const ready =\n enabled &&\n marketName !== undefined &&\n pricePrecision !== undefined &&\n quantityPrecision !== undefined &&\n feeRates !== undefined &&\n markPrice !== undefined &&\n lockedParamsReady &&\n (!isLowcap || (hedgerFeeOpen !== undefined && hedgerFeeClose !== undefined)) &&\n constraintsReady &&\n capsReady &&\n solverInfoReady &&\n estimateSettled;\n\n return {\n config,\n validationError,\n ready,\n refetchEstimatedPrice: estimateQuery.refetch,\n estimatedOpenPriceTimestamp: estimateQuery.isPlaceholderData ? undefined : estimateQuery.dataUpdatedAt || undefined,\n prefills: {\n chainId,\n market: {\n id: market.id,\n name: marketName,\n pricePrecision,\n quantityPrecision,\n minOpenSolverFeeCap,\n minCloseSolverFeeCap,\n hedgerFeeOpen,\n hedgerFeeClose,\n hedgerFeeCloseEarlyRate,\n hedgerFeeCloseEarlyThreshold,\n hedgerFeeCloseStandardThreshold,\n minAcceptablePortionLf,\n minAcceptableQuoteValue,\n maxNotionalValue,\n minNotionalValue,\n maxQuantity,\n lotSize,\n },\n markPrice,\n feeRates,\n lockedParamPercent,\n solverInfo,\n estimatedOpenPrice,\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -39,6 +39,12 @@ export type UseInstantOpenWithTpSlReturnType = UseMutationResult<UseInstantOpenW
|
|
|
39
39
|
* `tempQuoteId`. Web layer just wires inputs; all mutation logic (both legs +
|
|
40
40
|
* cache invalidation + confirming-slot write) lives in the SDK.
|
|
41
41
|
*
|
|
42
|
+
* Funding follows the `PrepareInstantOpenParameters` one-of: a typed
|
|
43
|
+
* `initialMargin`, or `fund: { mode: "full-balance", balance }` (lowcap only)
|
|
44
|
+
* to deploy the whole balance. In full-balance mode the submitted quantity is
|
|
45
|
+
* the SDK-rescaled one — take the TP/SL `quantity` from `useInstantOpenFees`
|
|
46
|
+
* (its `quantity` field) rather than re-deriving it from the typed margin.
|
|
47
|
+
*
|
|
42
48
|
* @example
|
|
43
49
|
* ```tsx
|
|
44
50
|
* const open = useInstantOpenWithTpSl();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-instant-open-with-tpsl.d.ts","sourceRoot":"","sources":["../../src/instant-layer/use-instant-open-with-tpsl.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,EACjC,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC5B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAA+B,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG5F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAMzE;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,+BAAgC,SAAQ,4BAA4B;IACnF,IAAI,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,GAAG,SAAS,CAAC,CAAC;CAC5D;AAED,sEAAsE;AACtE,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,qBAAqB,CAAC;IACnC,0DAA0D;IAC1D,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAC9B,mFAAmF;IACnF,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAChC;AAED,kDAAkD;AAClD,MAAM,MAAM,2BAA2B,GAAG,MAAM,GAAG,SAAS,GAAG,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtG,qDAAqD;AACrD,MAAM,MAAM,gCAAgC,GAAG,eAAe,CAAC;AAE/D,qDAAqD;AACrD,MAAM,MAAM,gCAAgC,GAAG,iBAAiB,CAC9D,0BAA0B,EAC1B,kBAAkB,EAClB,+BAA+B,CAChC,GAAG;IACF,+DAA+D;IAC/D,KAAK,EAAE,2BAA2B,CAAC;CACpC,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"use-instant-open-with-tpsl.d.ts","sourceRoot":"","sources":["../../src/instant-layer/use-instant-open-with-tpsl.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,EACjC,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC5B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAA+B,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG5F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAMzE;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,+BAAgC,SAAQ,4BAA4B;IACnF,IAAI,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,GAAG,SAAS,CAAC,CAAC;CAC5D;AAED,sEAAsE;AACtE,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,qBAAqB,CAAC;IACnC,0DAA0D;IAC1D,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAC9B,mFAAmF;IACnF,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAChC;AAED,kDAAkD;AAClD,MAAM,MAAM,2BAA2B,GAAG,MAAM,GAAG,SAAS,GAAG,gBAAgB,GAAG,SAAS,GAAG,OAAO,CAAC;AAEtG,qDAAqD;AACrD,MAAM,MAAM,gCAAgC,GAAG,eAAe,CAAC;AAE/D,qDAAqD;AACrD,MAAM,MAAM,gCAAgC,GAAG,iBAAiB,CAC9D,0BAA0B,EAC1B,kBAAkB,EAClB,+BAA+B,CAChC,GAAG;IACF,+DAA+D;IAC/D,KAAK,EAAE,2BAA2B,CAAC;CACpC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,GAAE,gCAAqC,GAChD,gCAAgC,CAqFlC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-instant-open-with-tpsl.js","names":[],"sources":["../../src/instant-layer/use-instant-open-with-tpsl.ts"],"sourcesContent":["\"use client\";\n\nimport {\n getInstantOpensQueryKey,\n instantOpenAutoMutationOptions,\n setQuoteTpSlMutationOptions,\n type ConfigParameter,\n type InstantOpenReturnType,\n type PrepareInstantOpenParameters,\n type SetQuoteTpSlParameters,\n type SetQuoteTpSlReturnType,\n} from \"@symmio/trading-core\";\nimport { useMutation, useQueryClient, type UseMutationResult } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\nimport { normalizeSymmError } from \"../errors/normalize-symm-error\";\nimport type { SymmioRequestError } from \"../errors/symmio-request-error\";\nimport { useSymmioChainId } from \"../provider/use-symmio-chain-id\";\nimport { useSymmioConfig } from \"../provider/use-symmio-config\";\nimport { useTpSlStore } from \"../tpsl/tpsl-store\";\nimport { invalidateAccountBalances, predicateMatch } from \"../utils\";\n\n/**\n * Parameters for {@link useInstantOpenWithTpSl}: the standard instant-open\n * inputs, plus an optional `tpsl` block that mirrors {@link SetQuoteTpSlParameters}\n * minus `quoteId` (derived from the hedger response) and `chainId` (defaulted).\n *\n * When `tpsl` is omitted (or both `tp` and `sl` are empty), the hook behaves\n * exactly like {@link useInstantOpenAuto}. When present, the SDK dispatches\n * `setQuoteTpSl` immediately after the hedger returns a `tempQuoteId` — the\n * message is signed against the (predicted) VA the caller supplies, so no\n * on-chain reconciliation wait is needed. Confirmation still comes through the\n * TP/SL WebSocket picked up by {@link useQuoteTpSl}.\n */\nexport interface UseInstantOpenWithTpSlVariables extends PrepareInstantOpenParameters {\n tpsl?: Omit<SetQuoteTpSlParameters, \"quoteId\" | \"chainId\">;\n}\n\n/** Aggregated success payload from {@link useInstantOpenWithTpSl}. */\nexport interface UseInstantOpenWithTpSlData {\n instantOpen: InstantOpenReturnType;\n /** Set when the TP/SL leg was attempted and succeeded. */\n tpsl?: SetQuoteTpSlReturnType;\n /** Set when the TP/SL leg was attempted and failed — instant open still landed. */\n tpslError?: SymmioRequestError;\n}\n\n/** Which leg the orchestrator is currently on. */\nexport type UseInstantOpenWithTpSlPhase = \"idle\" | \"opening\" | \"attaching-tpsl\" | \"success\" | \"error\";\n\n/** Parameters for {@link useInstantOpenWithTpSl}. */\nexport type UseInstantOpenWithTpSlParameters = ConfigParameter;\n\n/** Return type of {@link useInstantOpenWithTpSl}. */\nexport type UseInstantOpenWithTpSlReturnType = UseMutationResult<\n UseInstantOpenWithTpSlData,\n SymmioRequestError,\n UseInstantOpenWithTpSlVariables\n> & {\n /** Current orchestration phase. Handy for phased status UI. */\n phase: UseInstantOpenWithTpSlPhase;\n};\n\n/**\n * One-shot orchestrator: post an instant open and — if the caller pre-filled\n * a TP/SL block — immediately submit `setQuoteTpSl` using the hedger's returned\n * `tempQuoteId`. Web layer just wires inputs; all mutation logic (both legs +\n * cache invalidation + confirming-slot write) lives in the SDK.\n *\n * @example\n * ```tsx\n * const open = useInstantOpenWithTpSl();\n * open.mutate({\n * ...instantOpenParams,\n * tpsl: hasTpOrSl\n * ? { from: sessionKey, virtualAccount: predictedVa, subAccount,\n * symbolId, positionType, quantity, pricePrecision, tp, sl }\n * : undefined,\n * });\n * ```\n */\nexport function useInstantOpenWithTpSl(\n parameters: UseInstantOpenWithTpSlParameters = {},\n): UseInstantOpenWithTpSlReturnType {\n const config = useSymmioConfig(parameters);\n const chainId = useSymmioChainId();\n const queryClient = useQueryClient();\n const openBase = instantOpenAutoMutationOptions(config);\n const tpslBase = setQuoteTpSlMutationOptions(config);\n const markConfirming = useTpSlStore((state) => state.markConfirming);\n\n const [phase, setPhase] = useState<UseInstantOpenWithTpSlPhase>(\"idle\");\n\n const mutation = useMutation<UseInstantOpenWithTpSlData, SymmioRequestError, UseInstantOpenWithTpSlVariables>({\n mutationKey: [\"useInstantOpenWithTpSl\"] as const,\n mutationFn: async (variables) => {\n const resolvedChainId = variables.chainId ?? chainId;\n setPhase(\"opening\");\n let openResult: InstantOpenReturnType;\n try {\n openResult = await openBase.mutationFn({ ...variables, chainId: resolvedChainId });\n } catch (err) {\n setPhase(\"error\");\n throw normalizeSymmError(err);\n }\n\n // Refresh the instant-opens feed the moment the hedger accepts.\n const configKey = config.getChainConfigKey(resolvedChainId);\n void queryClient.invalidateQueries({ predicate: predicateMatch(getInstantOpensQueryKey, { configKey }) });\n /** The open locks margin and charges a fee — every balance read on this chain is now suspect. */\n invalidateAccountBalances(queryClient, { configKey });\n\n const wantsTpSl = Boolean(variables.tpsl && (variables.tpsl.tp || variables.tpsl.sl));\n if (!wantsTpSl || !openResult.tempQuoteId) {\n setPhase(\"success\");\n return { instantOpen: openResult };\n }\n\n const tempIdNumber = Number(openResult.tempQuoteId);\n if (!Number.isFinite(tempIdNumber)) {\n setPhase(\"success\");\n return { instantOpen: openResult };\n }\n\n setPhase(\"attaching-tpsl\");\n const quoteId = BigInt(tempIdNumber);\n try {\n const tpslResult = await tpslBase.mutationFn({\n ...(variables.tpsl as Omit<SetQuoteTpSlParameters, \"quoteId\" | \"chainId\">),\n chainId: resolvedChainId,\n quoteId,\n });\n // Mirror useSetQuoteTpSl.onSuccess: seed the confirming slot with the\n // target trigger price/type (not just the state) so useQuoteTpSl renders\n // the levels immediately — e.g. inline on the freshly-opened position row,\n // keyed by the tempQuoteId — instead of blank until the WS report lands.\n const tp = variables.tpsl!.tp;\n const sl = variables.tpsl!.sl;\n if (tp) {\n markConfirming(quoteId, \"tp\", {\n price: tp.triggerPrice,\n pricePrecision: variables.tpsl!.pricePrecision,\n priceType: tp.priceType,\n cohQuoteId: tpslResult.cohQuoteId,\n });\n }\n if (sl) {\n markConfirming(quoteId, \"sl\", {\n price: sl.triggerPrice,\n pricePrecision: variables.tpsl!.pricePrecision,\n priceType: sl.priceType,\n cohQuoteId: tpslResult.cohQuoteId,\n });\n }\n setPhase(\"success\");\n return { instantOpen: openResult, tpsl: tpslResult };\n } catch (err) {\n // Instant open landed; only the TP/SL leg failed. Surface it on the\n // return payload rather than throwing, so callers can render a partial\n // success state without losing the trade.\n setPhase(\"success\");\n return { instantOpen: openResult, tpslError: normalizeSymmError(err) };\n }\n },\n onError: () => setPhase(\"error\"),\n }) as UseMutationResult<UseInstantOpenWithTpSlData, SymmioRequestError, UseInstantOpenWithTpSlVariables>;\n\n return Object.assign(mutation, { phase });\n}\n"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"use-instant-open-with-tpsl.js","names":[],"sources":["../../src/instant-layer/use-instant-open-with-tpsl.ts"],"sourcesContent":["\"use client\";\n\nimport {\n getInstantOpensQueryKey,\n instantOpenAutoMutationOptions,\n setQuoteTpSlMutationOptions,\n type ConfigParameter,\n type InstantOpenReturnType,\n type PrepareInstantOpenParameters,\n type SetQuoteTpSlParameters,\n type SetQuoteTpSlReturnType,\n} from \"@symmio/trading-core\";\nimport { useMutation, useQueryClient, type UseMutationResult } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\nimport { normalizeSymmError } from \"../errors/normalize-symm-error\";\nimport type { SymmioRequestError } from \"../errors/symmio-request-error\";\nimport { useSymmioChainId } from \"../provider/use-symmio-chain-id\";\nimport { useSymmioConfig } from \"../provider/use-symmio-config\";\nimport { useTpSlStore } from \"../tpsl/tpsl-store\";\nimport { invalidateAccountBalances, predicateMatch } from \"../utils\";\n\n/**\n * Parameters for {@link useInstantOpenWithTpSl}: the standard instant-open\n * inputs, plus an optional `tpsl` block that mirrors {@link SetQuoteTpSlParameters}\n * minus `quoteId` (derived from the hedger response) and `chainId` (defaulted).\n *\n * When `tpsl` is omitted (or both `tp` and `sl` are empty), the hook behaves\n * exactly like {@link useInstantOpenAuto}. When present, the SDK dispatches\n * `setQuoteTpSl` immediately after the hedger returns a `tempQuoteId` — the\n * message is signed against the (predicted) VA the caller supplies, so no\n * on-chain reconciliation wait is needed. Confirmation still comes through the\n * TP/SL WebSocket picked up by {@link useQuoteTpSl}.\n */\nexport interface UseInstantOpenWithTpSlVariables extends PrepareInstantOpenParameters {\n tpsl?: Omit<SetQuoteTpSlParameters, \"quoteId\" | \"chainId\">;\n}\n\n/** Aggregated success payload from {@link useInstantOpenWithTpSl}. */\nexport interface UseInstantOpenWithTpSlData {\n instantOpen: InstantOpenReturnType;\n /** Set when the TP/SL leg was attempted and succeeded. */\n tpsl?: SetQuoteTpSlReturnType;\n /** Set when the TP/SL leg was attempted and failed — instant open still landed. */\n tpslError?: SymmioRequestError;\n}\n\n/** Which leg the orchestrator is currently on. */\nexport type UseInstantOpenWithTpSlPhase = \"idle\" | \"opening\" | \"attaching-tpsl\" | \"success\" | \"error\";\n\n/** Parameters for {@link useInstantOpenWithTpSl}. */\nexport type UseInstantOpenWithTpSlParameters = ConfigParameter;\n\n/** Return type of {@link useInstantOpenWithTpSl}. */\nexport type UseInstantOpenWithTpSlReturnType = UseMutationResult<\n UseInstantOpenWithTpSlData,\n SymmioRequestError,\n UseInstantOpenWithTpSlVariables\n> & {\n /** Current orchestration phase. Handy for phased status UI. */\n phase: UseInstantOpenWithTpSlPhase;\n};\n\n/**\n * One-shot orchestrator: post an instant open and — if the caller pre-filled\n * a TP/SL block — immediately submit `setQuoteTpSl` using the hedger's returned\n * `tempQuoteId`. Web layer just wires inputs; all mutation logic (both legs +\n * cache invalidation + confirming-slot write) lives in the SDK.\n *\n * Funding follows the `PrepareInstantOpenParameters` one-of: a typed\n * `initialMargin`, or `fund: { mode: \"full-balance\", balance }` (lowcap only)\n * to deploy the whole balance. In full-balance mode the submitted quantity is\n * the SDK-rescaled one — take the TP/SL `quantity` from `useInstantOpenFees`\n * (its `quantity` field) rather than re-deriving it from the typed margin.\n *\n * @example\n * ```tsx\n * const open = useInstantOpenWithTpSl();\n * open.mutate({\n * ...instantOpenParams,\n * tpsl: hasTpOrSl\n * ? { from: sessionKey, virtualAccount: predictedVa, subAccount,\n * symbolId, positionType, quantity, pricePrecision, tp, sl }\n * : undefined,\n * });\n * ```\n */\nexport function useInstantOpenWithTpSl(\n parameters: UseInstantOpenWithTpSlParameters = {},\n): UseInstantOpenWithTpSlReturnType {\n const config = useSymmioConfig(parameters);\n const chainId = useSymmioChainId();\n const queryClient = useQueryClient();\n const openBase = instantOpenAutoMutationOptions(config);\n const tpslBase = setQuoteTpSlMutationOptions(config);\n const markConfirming = useTpSlStore((state) => state.markConfirming);\n\n const [phase, setPhase] = useState<UseInstantOpenWithTpSlPhase>(\"idle\");\n\n const mutation = useMutation<UseInstantOpenWithTpSlData, SymmioRequestError, UseInstantOpenWithTpSlVariables>({\n mutationKey: [\"useInstantOpenWithTpSl\"] as const,\n mutationFn: async (variables) => {\n const resolvedChainId = variables.chainId ?? chainId;\n setPhase(\"opening\");\n let openResult: InstantOpenReturnType;\n try {\n openResult = await openBase.mutationFn({ ...variables, chainId: resolvedChainId });\n } catch (err) {\n setPhase(\"error\");\n throw normalizeSymmError(err);\n }\n\n // Refresh the instant-opens feed the moment the hedger accepts.\n const configKey = config.getChainConfigKey(resolvedChainId);\n void queryClient.invalidateQueries({ predicate: predicateMatch(getInstantOpensQueryKey, { configKey }) });\n /** The open locks margin and charges a fee — every balance read on this chain is now suspect. */\n invalidateAccountBalances(queryClient, { configKey });\n\n const wantsTpSl = Boolean(variables.tpsl && (variables.tpsl.tp || variables.tpsl.sl));\n if (!wantsTpSl || !openResult.tempQuoteId) {\n setPhase(\"success\");\n return { instantOpen: openResult };\n }\n\n const tempIdNumber = Number(openResult.tempQuoteId);\n if (!Number.isFinite(tempIdNumber)) {\n setPhase(\"success\");\n return { instantOpen: openResult };\n }\n\n setPhase(\"attaching-tpsl\");\n const quoteId = BigInt(tempIdNumber);\n try {\n const tpslResult = await tpslBase.mutationFn({\n ...(variables.tpsl as Omit<SetQuoteTpSlParameters, \"quoteId\" | \"chainId\">),\n chainId: resolvedChainId,\n quoteId,\n });\n // Mirror useSetQuoteTpSl.onSuccess: seed the confirming slot with the\n // target trigger price/type (not just the state) so useQuoteTpSl renders\n // the levels immediately — e.g. inline on the freshly-opened position row,\n // keyed by the tempQuoteId — instead of blank until the WS report lands.\n const tp = variables.tpsl!.tp;\n const sl = variables.tpsl!.sl;\n if (tp) {\n markConfirming(quoteId, \"tp\", {\n price: tp.triggerPrice,\n pricePrecision: variables.tpsl!.pricePrecision,\n priceType: tp.priceType,\n cohQuoteId: tpslResult.cohQuoteId,\n });\n }\n if (sl) {\n markConfirming(quoteId, \"sl\", {\n price: sl.triggerPrice,\n pricePrecision: variables.tpsl!.pricePrecision,\n priceType: sl.priceType,\n cohQuoteId: tpslResult.cohQuoteId,\n });\n }\n setPhase(\"success\");\n return { instantOpen: openResult, tpsl: tpslResult };\n } catch (err) {\n // Instant open landed; only the TP/SL leg failed. Surface it on the\n // return payload rather than throwing, so callers can render a partial\n // success state without losing the trade.\n setPhase(\"success\");\n return { instantOpen: openResult, tpslError: normalizeSymmError(err) };\n }\n },\n onError: () => setPhase(\"error\"),\n }) as UseMutationResult<UseInstantOpenWithTpSlData, SymmioRequestError, UseInstantOpenWithTpSlVariables>;\n\n return Object.assign(mutation, { phase });\n}\n"],"mappings":";;;;;;;;;;;AAsFA,SAAgB,EACd,IAA+C,CAAC,GACd;CAClC,IAAM,IAAS,EAAgB,CAAU,GACnC,IAAU,EAAiB,GAC3B,IAAc,EAAe,GAC7B,IAAW,EAA+B,CAAM,GAChD,IAAW,EAA4B,CAAM,GAC7C,IAAiB,GAAc,MAAU,EAAM,cAAc,GAE7D,CAAC,GAAO,KAAY,EAAsC,MAAM,GAEhE,IAAW,EAA6F;EAC5G,aAAa,CAAC,wBAAwB;EACtC,YAAY,OAAO,MAAc;GAC/B,IAAM,IAAkB,EAAU,WAAW;GAC7C,EAAS,SAAS;GAClB,IAAI;GACJ,IAAI;IACF,IAAa,MAAM,EAAS,WAAW;KAAE,GAAG;KAAW,SAAS;IAAgB,CAAC;GACnF,SAAS,GAAK;IAEZ,MADA,EAAS,OAAO,GACV,EAAmB,CAAG;GAC9B;GAGA,IAAM,IAAY,EAAO,kBAAkB,CAAe;GAM1D,IALA,EAAiB,kBAAkB,EAAE,WAAW,EAAe,GAAyB,EAAE,aAAU,CAAC,EAAE,CAAC,GAExG,EAA0B,GAAa,EAAE,aAAU,CAAC,GAGhD,EADsB,EAAU,SAAS,EAAU,KAAK,MAAM,EAAU,KAAK,QAC/D,CAAC,EAAW,aAE5B,OADA,EAAS,SAAS,GACX,EAAE,aAAa,EAAW;GAGnC,IAAM,IAAe,OAAO,EAAW,WAAW;GAClD,IAAI,CAAC,OAAO,SAAS,CAAY,GAE/B,OADA,EAAS,SAAS,GACX,EAAE,aAAa,EAAW;GAGnC,EAAS,gBAAgB;GACzB,IAAM,IAAU,OAAO,CAAY;GACnC,IAAI;IACF,IAAM,IAAa,MAAM,EAAS,WAAW;KAC3C,GAAI,EAAU;KACd,SAAS;KACT;IACF,CAAC,GAKK,IAAK,EAAU,KAAM,IACrB,IAAK,EAAU,KAAM;IAkB3B,OAjBI,KACF,EAAe,GAAS,MAAM;KAC5B,OAAO,EAAG;KACV,gBAAgB,EAAU,KAAM;KAChC,WAAW,EAAG;KACd,YAAY,EAAW;IACzB,CAAC,GAEC,KACF,EAAe,GAAS,MAAM;KAC5B,OAAO,EAAG;KACV,gBAAgB,EAAU,KAAM;KAChC,WAAW,EAAG;KACd,YAAY,EAAW;IACzB,CAAC,GAEH,EAAS,SAAS,GACX;KAAE,aAAa;KAAY,MAAM;IAAW;GACrD,SAAS,GAAK;IAKZ,OADA,EAAS,SAAS,GACX;KAAE,aAAa;KAAY,WAAW,EAAmB,CAAG;IAAE;GACvE;EACF;EACA,eAAe,EAAS,OAAO;CACjC,CAAC;CAED,OAAO,OAAO,OAAO,GAAU,EAAE,SAAM,CAAC;AAC1C"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ConfigParameter, PrepareInstantOpenParamsOptions, PrepareInstantOpenParamsReturnType } from '@symmio/trading-core';
|
|
2
|
+
import { UseQueryResult } from '@tanstack/react-query';
|
|
3
|
+
import { SymmioRequestError } from '../errors/symmio-request-error.js';
|
|
4
|
+
import { UseEstimatedPriceReturnType } from '../estimated-price/use-estimated-price.js';
|
|
5
|
+
/**
|
|
6
|
+
* Parameters for {@link usePrepareInstantOpenParams}: the same trade intent the
|
|
7
|
+
* submit takes (`subAccountAddress`, `from`, `market`, `positionType`,
|
|
8
|
+
* `initialMargin` **or** `fund`, optional `availableBalance` for automatic sizing,
|
|
9
|
+
* `leverage`, optional `slippage` / pre-fetched
|
|
10
|
+
* data, TanStack `query` overrides) plus an optional `config`.
|
|
11
|
+
*/
|
|
12
|
+
export type UsePrepareInstantOpenParamsParameters = PrepareInstantOpenParamsOptions & ConfigParameter;
|
|
13
|
+
/**
|
|
14
|
+
* Return type of {@link usePrepareInstantOpenParams}: the query result carrying
|
|
15
|
+
* the prepared {@link PrepareInstantOpenParamsReturnType}, plus the dry-run estimate the
|
|
16
|
+
* params were built with.
|
|
17
|
+
*/
|
|
18
|
+
export type UsePrepareInstantOpenParamsReturnType = UseQueryResult<PrepareInstantOpenParamsReturnType, SymmioRequestError> & {
|
|
19
|
+
/**
|
|
20
|
+
* The dry-run estimate the preview was built with (`null` when unavailable;
|
|
21
|
+
* `undefined` while pending or on a non-lowcap solver). Forward it as
|
|
22
|
+
* `estimatedOpenPrice` to the submit so its `margin.amount` — whose settlement leg tracks the estimate — matches this
|
|
23
|
+
* preview bit-for-bit.
|
|
24
|
+
*/
|
|
25
|
+
estimatedOpenPrice: string | null | undefined;
|
|
26
|
+
/** Arrival time of the fetched estimate in epoch milliseconds. The frontend decides when to refresh or expire it. */
|
|
27
|
+
estimatedOpenPriceTimestamp: number | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Refetch the SDK-managed estimate on the frontend's schedule. Resolves when
|
|
30
|
+
* the estimate request finishes; use isReady to await preparation. A supplied
|
|
31
|
+
* estimatedOpenPrice still takes precedence over the fetched value.
|
|
32
|
+
*/
|
|
33
|
+
refetchEstimatedPrice: UseEstimatedPriceReturnType["refetch"];
|
|
34
|
+
/** Synchronous input error. Display before query.error; dependent queries stay disabled. */
|
|
35
|
+
validationError: SymmioRequestError | undefined;
|
|
36
|
+
/** True when all inputs and the current preparation have settled; gate submission on this. */
|
|
37
|
+
isReady: boolean;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Live-preview the exact {@link PrepareInstantOpenParamsReturnType} an instant-open submit
|
|
41
|
+
* will sign — `order` (price + quantity), `lockedParam`, `margin.amount`, and
|
|
42
|
+
* `solverFeeCaps` — by running `prepareInstantOpenParams` as a read.
|
|
43
|
+
* `prepareInstantOpenParams` neither signs nor submits, so this is safe to run
|
|
44
|
+
* live; drive a "quote preview" from it and what the user sees is bit-for-bit
|
|
45
|
+
* what `sendQuote` receives, with no separate local recomputation to drift.
|
|
46
|
+
*
|
|
47
|
+
* Like {@link useInstantOpenFees}, it **pre-fetches every input through its own
|
|
48
|
+
* cached queries** (market metadata, mark price, locked params, on-chain fee
|
|
49
|
+
* rates, solver info, and the lowcap dry-run estimate) and passes them in as
|
|
50
|
+
* prefills, so the query function is pure and does no per-render network. Previous data is kept
|
|
51
|
+
* while inputs move, so a mark-price tick updates the numbers in place.
|
|
52
|
+
*
|
|
53
|
+
* For bit-for-bit parity on `margin.amount`, forward {@link
|
|
54
|
+
* UsePrepareInstantOpenParamsReturnType.estimatedOpenPrice} to the submit call
|
|
55
|
+
* (`useInstantOpenAuto` / `useInstantOpenWithTpSl`) as `estimatedOpenPrice`, so
|
|
56
|
+
* both the preview and the send provision settlement at the same estimate. The
|
|
57
|
+
* full-balance sizing decision and quantity also depend on that estimate.
|
|
58
|
+
* Gate submission on `isReady` while inputs or the estimate are settling.
|
|
59
|
+
* Display `validationError ?? error`; input errors are synchronous, including offline.
|
|
60
|
+
* A disabled hook or empty input remains idle without input validation.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```tsx
|
|
64
|
+
* const preview = usePrepareInstantOpenParams({
|
|
65
|
+
* subAccountAddress,
|
|
66
|
+
* from: sessionKey,
|
|
67
|
+
* market: { id: symbolId },
|
|
68
|
+
* positionType,
|
|
69
|
+
* initialMargin,
|
|
70
|
+
* leverage,
|
|
71
|
+
* slippage,
|
|
72
|
+
* });
|
|
73
|
+
* // preview.data?.order.quantity — the exact leveraged quantity (wei) the send signs
|
|
74
|
+
* // submit: mutate({ …, estimatedOpenPrice: preview.estimatedOpenPrice })
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare function usePrepareInstantOpenParams(parameters: UsePrepareInstantOpenParamsParameters): UsePrepareInstantOpenParamsReturnType;
|
|
78
|
+
//# sourceMappingURL=use-prepare-instant-open-params.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-prepare-instant-open-params.d.ts","sourceRoot":"","sources":["../../src/instant-layer/use-prepare-instant-open-params.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,+BAA+B,EACpC,KAAK,kCAAkC,EACxC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AAG1F;;;;;;GAMG;AACH,MAAM,MAAM,qCAAqC,GAAG,+BAA+B,GAAG,eAAe,CAAC;AAEtG;;;;GAIG;AACH,MAAM,MAAM,qCAAqC,GAAG,cAAc,CAChE,kCAAkC,EAClC,kBAAkB,CACnB,GAAG;IACF;;;;;OAKG;IACH,kBAAkB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C,qHAAqH;IACrH,2BAA2B,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD;;;;OAIG;IACH,qBAAqB,EAAE,2BAA2B,CAAC,SAAS,CAAC,CAAC;IAC9D,4FAA4F;IAC5F,eAAe,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAChD,8FAA8F;IAC9F,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,qCAAqC,GAChD,qCAAqC,CA8BvC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { normalizeSymmError as e } from "../errors/normalize-symm-error.js";
|
|
3
|
+
import { useInstantOpenInputs as t } from "./use-instant-open-inputs.js";
|
|
4
|
+
import { prepareInstantOpenParamsQueryOptions as n } from "@symmio/trading-core";
|
|
5
|
+
import { useQuery as r } from "@tanstack/react-query";
|
|
6
|
+
//#region src/instant-layer/use-prepare-instant-open-params.ts
|
|
7
|
+
function i(i) {
|
|
8
|
+
let a = t(i, "prepare", i.query?.enabled !== !1), o = n(a.config, {
|
|
9
|
+
...i,
|
|
10
|
+
...a.prefills,
|
|
11
|
+
query: {
|
|
12
|
+
...i.query,
|
|
13
|
+
enabled: a.ready,
|
|
14
|
+
placeholderData: (e) => a.validationError === void 0 ? e : void 0
|
|
15
|
+
}
|
|
16
|
+
}), s = r({
|
|
17
|
+
...o,
|
|
18
|
+
queryFn: async () => {
|
|
19
|
+
try {
|
|
20
|
+
return await o.queryFn();
|
|
21
|
+
} catch (t) {
|
|
22
|
+
throw e(t);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
...s,
|
|
28
|
+
refetchEstimatedPrice: a.refetchEstimatedPrice,
|
|
29
|
+
validationError: a.validationError,
|
|
30
|
+
estimatedOpenPrice: a.prefills.estimatedOpenPrice,
|
|
31
|
+
estimatedOpenPriceTimestamp: i.estimatedOpenPriceTimestamp ?? a.estimatedOpenPriceTimestamp,
|
|
32
|
+
isReady: a.ready && s.isSuccess && !s.isFetching && !s.isPlaceholderData
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { i as usePrepareInstantOpenParams };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=use-prepare-instant-open-params.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-prepare-instant-open-params.js","names":[],"sources":["../../src/instant-layer/use-prepare-instant-open-params.ts"],"sourcesContent":["\"use client\";\n\nimport {\n prepareInstantOpenParamsQueryOptions,\n type ConfigParameter,\n type PrepareInstantOpenParamsOptions,\n type PrepareInstantOpenParamsReturnType,\n} from \"@symmio/trading-core\";\nimport { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { normalizeSymmError } from \"../errors/normalize-symm-error\";\nimport type { SymmioRequestError } from \"../errors/symmio-request-error\";\nimport type { UseEstimatedPriceReturnType } from \"../estimated-price/use-estimated-price\";\nimport { useInstantOpenInputs } from \"./use-instant-open-inputs\";\n\n/**\n * Parameters for {@link usePrepareInstantOpenParams}: the same trade intent the\n * submit takes (`subAccountAddress`, `from`, `market`, `positionType`,\n * `initialMargin` **or** `fund`, optional `availableBalance` for automatic sizing,\n * `leverage`, optional `slippage` / pre-fetched\n * data, TanStack `query` overrides) plus an optional `config`.\n */\nexport type UsePrepareInstantOpenParamsParameters = PrepareInstantOpenParamsOptions & ConfigParameter;\n\n/**\n * Return type of {@link usePrepareInstantOpenParams}: the query result carrying\n * the prepared {@link PrepareInstantOpenParamsReturnType}, plus the dry-run estimate the\n * params were built with.\n */\nexport type UsePrepareInstantOpenParamsReturnType = UseQueryResult<\n PrepareInstantOpenParamsReturnType,\n SymmioRequestError\n> & {\n /**\n * The dry-run estimate the preview was built with (`null` when unavailable;\n * `undefined` while pending or on a non-lowcap solver). Forward it as\n * `estimatedOpenPrice` to the submit so its `margin.amount` — whose settlement leg tracks the estimate — matches this\n * preview bit-for-bit.\n */\n estimatedOpenPrice: string | null | undefined;\n /** Arrival time of the fetched estimate in epoch milliseconds. The frontend decides when to refresh or expire it. */\n estimatedOpenPriceTimestamp: number | undefined;\n /**\n * Refetch the SDK-managed estimate on the frontend's schedule. Resolves when\n * the estimate request finishes; use isReady to await preparation. A supplied\n * estimatedOpenPrice still takes precedence over the fetched value.\n */\n refetchEstimatedPrice: UseEstimatedPriceReturnType[\"refetch\"];\n /** Synchronous input error. Display before query.error; dependent queries stay disabled. */\n validationError: SymmioRequestError | undefined;\n /** True when all inputs and the current preparation have settled; gate submission on this. */\n isReady: boolean;\n};\n\n/**\n * Live-preview the exact {@link PrepareInstantOpenParamsReturnType} an instant-open submit\n * will sign — `order` (price + quantity), `lockedParam`, `margin.amount`, and\n * `solverFeeCaps` — by running `prepareInstantOpenParams` as a read.\n * `prepareInstantOpenParams` neither signs nor submits, so this is safe to run\n * live; drive a \"quote preview\" from it and what the user sees is bit-for-bit\n * what `sendQuote` receives, with no separate local recomputation to drift.\n *\n * Like {@link useInstantOpenFees}, it **pre-fetches every input through its own\n * cached queries** (market metadata, mark price, locked params, on-chain fee\n * rates, solver info, and the lowcap dry-run estimate) and passes them in as\n * prefills, so the query function is pure and does no per-render network. Previous data is kept\n * while inputs move, so a mark-price tick updates the numbers in place.\n *\n * For bit-for-bit parity on `margin.amount`, forward {@link\n * UsePrepareInstantOpenParamsReturnType.estimatedOpenPrice} to the submit call\n * (`useInstantOpenAuto` / `useInstantOpenWithTpSl`) as `estimatedOpenPrice`, so\n * both the preview and the send provision settlement at the same estimate. The\n * full-balance sizing decision and quantity also depend on that estimate.\n * Gate submission on `isReady` while inputs or the estimate are settling.\n * Display `validationError ?? error`; input errors are synchronous, including offline.\n * A disabled hook or empty input remains idle without input validation.\n *\n * @example\n * ```tsx\n * const preview = usePrepareInstantOpenParams({\n * subAccountAddress,\n * from: sessionKey,\n * market: { id: symbolId },\n * positionType,\n * initialMargin,\n * leverage,\n * slippage,\n * });\n * // preview.data?.order.quantity — the exact leveraged quantity (wei) the send signs\n * // submit: mutate({ …, estimatedOpenPrice: preview.estimatedOpenPrice })\n * ```\n */\nexport function usePrepareInstantOpenParams(\n parameters: UsePrepareInstantOpenParamsParameters,\n): UsePrepareInstantOpenParamsReturnType {\n const inputs = useInstantOpenInputs(parameters, \"prepare\", parameters.query?.enabled !== false);\n const options = prepareInstantOpenParamsQueryOptions(inputs.config, {\n ...parameters,\n ...inputs.prefills,\n query: {\n ...parameters.query,\n enabled: inputs.ready,\n placeholderData: (previous) => (inputs.validationError === undefined ? previous : undefined),\n },\n });\n const query = useQuery({\n ...options,\n queryFn: async () => {\n try {\n return await options.queryFn();\n } catch (err) {\n throw normalizeSymmError(err);\n }\n },\n }) as UseQueryResult<PrepareInstantOpenParamsReturnType, SymmioRequestError>;\n\n return {\n ...query,\n refetchEstimatedPrice: inputs.refetchEstimatedPrice,\n validationError: inputs.validationError,\n estimatedOpenPrice: inputs.prefills.estimatedOpenPrice,\n estimatedOpenPriceTimestamp: parameters.estimatedOpenPriceTimestamp ?? inputs.estimatedOpenPriceTimestamp,\n isReady: inputs.ready && query.isSuccess && !query.isFetching && !query.isPlaceholderData,\n };\n}\n"],"mappings":";;;;;;AA2FA,SAAgB,EACd,GACuC;CACvC,IAAM,IAAS,EAAqB,GAAY,WAAW,EAAW,OAAO,YAAY,EAAK,GACxF,IAAU,EAAqC,EAAO,QAAQ;EAClE,GAAG;EACH,GAAG,EAAO;EACV,OAAO;GACL,GAAG,EAAW;GACd,SAAS,EAAO;GAChB,kBAAkB,MAAc,EAAO,oBAAoB,KAAA,IAAY,IAAW,KAAA;EACpF;CACF,CAAC,GACK,IAAQ,EAAS;EACrB,GAAG;EACH,SAAS,YAAY;GACnB,IAAI;IACF,OAAO,MAAM,EAAQ,QAAQ;GAC/B,SAAS,GAAK;IACZ,MAAM,EAAmB,CAAG;GAC9B;EACF;CACF,CAAC;CAED,OAAO;EACL,GAAG;EACH,uBAAuB,EAAO;EAC9B,iBAAiB,EAAO;EACxB,oBAAoB,EAAO,SAAS;EACpC,6BAA6B,EAAW,+BAA+B,EAAO;EAC9E,SAAS,EAAO,SAAS,EAAM,aAAa,CAAC,EAAM,cAAc,CAAC,EAAM;CAC1E;AACF"}
|
|
@@ -41,9 +41,10 @@ export interface UseAvailableInstantOpenMarginReturnType {
|
|
|
41
41
|
* type:
|
|
42
42
|
*
|
|
43
43
|
* - **VA isolations** (`POSITION` / `MARKET` / `MARKET_DIRECTION`): the
|
|
44
|
-
* sub-account's available balance shaved for
|
|
44
|
+
* sub-account's available balance shaved for the open fee and — SHORT only — a
|
|
45
45
|
* worst-case slippage fill (`calculateAvailableInstantOpenMargin` over
|
|
46
|
-
* {@link useAccountBalanceOf} + {@link useFeeForUser}).
|
|
46
|
+
* {@link useAccountBalanceOf} + {@link useFeeForUser}). The close fee is
|
|
47
|
+
* charged at close, so it is not reserved here.
|
|
47
48
|
* - **`CUSTOM` (cross-margin)** — trades execute on the sub-account directly:
|
|
48
49
|
* `calculateAvailableForOrder` over the live `balanceInfoOfPartyA` snapshot
|
|
49
50
|
* and the account's SDK-computed uPnL ({@link useAccountUpnl} — Σ
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-available-instant-open-margin.d.ts","sourceRoot":"","sources":["../../src/margin/use-available-instant-open-margin.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,YAAY,EAEZ,KAAK,eAAe,EACpB,KAAK,QAAQ,EACd,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;AAI9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAqBzE,4DAA4D;AAC5D,MAAM,WAAW,uCAAwC,SAAQ,eAAe;IAC9E,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,YAAY,EAAE,YAAY,CAAC;IAC3B,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,4DAA4D;AAC5D,MAAM,WAAW,uCAAuC;IACtD,iFAAiF;IACjF,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,sHAAsH;IACtH,eAAe,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,SAAS,EAAE,OAAO,CAAC;IACnB,wDAAwD;IACxD,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACjC,qFAAqF;IACrF,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED
|
|
1
|
+
{"version":3,"file":"use-available-instant-open-margin.d.ts","sourceRoot":"","sources":["../../src/margin/use-available-instant-open-margin.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,YAAY,EAEZ,KAAK,eAAe,EACpB,KAAK,QAAQ,EACd,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;AAI9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAqBzE,4DAA4D;AAC5D,MAAM,WAAW,uCAAwC,SAAQ,eAAe;IAC9E,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,YAAY,EAAE,YAAY,CAAC;IAC3B,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,4DAA4D;AAC5D,MAAM,WAAW,uCAAuC;IACtD,iFAAiF;IACjF,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,sHAAsH;IACtH,eAAe,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,SAAS,EAAE,OAAO,CAAC;IACnB,wDAAwD;IACxD,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACjC,qFAAqF;IACrF,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,uCAAuC,GAClD,uCAAuC,CAwHzC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-available-instant-open-margin.js","names":[],"sources":["../../src/margin/use-available-instant-open-margin.ts"],"sourcesContent":["\"use client\";\n\nimport {\n calculateAvailableForOrder,\n calculateAvailableInstantOpenMargin,\n PositionType,\n SubAccountIsolationType,\n type ConfigParameter,\n type SolverId,\n} from \"@symmio/trading-core\";\nimport { useCallback, useMemo } from \"react\";\nimport { formatUnits, zeroAddress, type Address } from \"viem\";\nimport { useAccountBalanceInfo } from \"../account-layer/use-account-balance-info\";\nimport { useAccountBalanceOf } from \"../account-layer/use-account-balance-of\";\nimport { useSubAccount } from \"../account-layer/use-sub-account\";\nimport type { SymmioRequestError } from \"../errors/symmio-request-error\";\nimport { useFeeForUser } from \"../fees/use-fee-for-user\";\nimport { useSymmioConfig } from \"../provider/use-symmio-config\";\nimport { useAccountUpnl } from \"../quotes/use-account-upnl\";\n\n/** Convert a slippage percent (e.g. `5`) to an 18-decimal fraction (`5e16`). */\nfunction slippagePercentToFractionWei(slippage: number): bigint {\n return BigInt(Math.round(slippage * 1e16));\n}\n\n/**\n * **Rasa-specific policy**: the fraction of a cross-margin account's\n * `availableForOrder` a new open may spend, in basis points (90%). The\n * remaining 10% stays as a safety buffer so a fill at a slightly worse price\n * or a fee charge cannot tip the account straight into deficiency.\n *\n * Applied only when the resolved solver is Rasa — a future cross-margin solver\n * spends 100% unless its vendor mandates its own buffer.\n */\nconst RASA_SPENDABLE_BPS = 9_000n;\n\n/** Parameters for {@link useAvailableInstantOpenMargin}. */\nexport interface UseAvailableInstantOpenMarginParameters extends ConfigParameter {\n /** SubAccount to spend margin from. The hook is idle until it is set. */\n account?: Address;\n /** Market symbol id, for the fee lookup. Idle until it is set. */\n symbolId?: bigint | number;\n /** Requested leverage (integer ≥ 1). */\n leverage: number;\n /** LONG skips the slippage cap; SHORT applies it. */\n positionType: PositionType;\n /** Slippage percent, e.g. `5`. */\n slippage: number;\n /**\n * Solver whose markets and price provider value the cross-margin uPnL.\n * Defaults to the chain's default solver. The margin **model** is not chosen\n * here — it follows the sub-account's isolation type.\n */\n solverId?: SolverId;\n /** Optional chain override; defaults to the connected chain. */\n chainId?: number;\n}\n\n/** Return type of {@link useAvailableInstantOpenMargin}. */\nexport interface UseAvailableInstantOpenMarginReturnType {\n /** Spendable margin in 18-decimal wei; `undefined` until balance + fees load. */\n availableMarginWei: bigint | undefined;\n /** {@link UseAvailableInstantOpenMarginReturnType.availableMarginWei} as a decimal string; `\"0\"` when unavailable. */\n availableMargin: string;\n /** `true` while the underlying balance / fee reads are loading. */\n isLoading: boolean;\n /** First error from the balance / fee reads, if any. */\n error: SymmioRequestError | null;\n /** Refetch the underlying balance + fee reads (e.g. after an open/close settles). */\n refetch: () => Promise<void>;\n}\n\n/**\n * Maximum initial margin an instant open can spend for `account` on a market —\n * **the** margin hook. The margin model follows the sub-account's isolation\n * type:\n *\n * - **VA isolations** (`POSITION` / `MARKET` / `MARKET_DIRECTION`): the\n * sub-account's available balance shaved for fees and — SHORT only — a\n * worst-case slippage fill (`calculateAvailableInstantOpenMargin` over\n * {@link useAccountBalanceOf} + {@link useFeeForUser}).\n * - **`CUSTOM` (cross-margin)** — trades execute on the sub-account directly:\n * `calculateAvailableForOrder` over the live `balanceInfoOfPartyA` snapshot\n * and the account's SDK-computed uPnL ({@link useAccountUpnl} — Σ\n * per-position uPnL against live mark prices), clamped at zero. **On the\n * Rasa solver only**, 90% of it is spendable — a 10% buffer stays reserved\n * (see `RASA_SPENDABLE_BPS`); other cross-margin solvers spend 100%.\n *\n * Every read is live: balances refetch on on-chain settle notifications and\n * the cross-margin uPnL streams off the price socket, so the `Max` chip tracks\n * trades and the market without manual refreshes. Wire the result to the trade\n * form's `Max` chip and gate submit on it.\n *\n * @example\n * ```tsx\n * const { availableMargin, availableMarginWei } = useAvailableInstantOpenMargin({\n * account: subAccount,\n * symbolId: market.symbolId,\n * leverage,\n * positionType,\n * slippage,\n * });\n * ```\n */\nexport function useAvailableInstantOpenMargin(\n parameters: UseAvailableInstantOpenMarginParameters,\n): UseAvailableInstantOpenMarginReturnType {\n const { account, symbolId, leverage, positionType, slippage, solverId, chainId, config } = parameters;\n\n // The 10% spend buffer is a RASA policy, not a cross-margin-model rule —\n // resolved by solver id so a future cross-margin solver is unaffected.\n const resolvedConfig = useSymmioConfig(parameters);\n const isRasaSolver = resolvedConfig.getSolver({ chainId, solverId }).id === \"rasa\";\n\n // The margin model follows the sub-account's isolation: `CUSTOM` trades on\n // the sub-account directly (cross-margin); the VA isolations spend the\n // sub-account's available balance. Isolation is fixed at creation, so this\n // read caches indefinitely.\n const subAccountQuery = useSubAccount({\n account: account ?? zeroAddress,\n chainId,\n config,\n query: { enabled: Boolean(account), staleTime: Infinity },\n });\n const isolationType = account !== undefined ? subAccountQuery.data?.isolationType : undefined;\n const isolationKnown = isolationType !== undefined;\n const isCrossMargin = isolationType === SubAccountIsolationType.CUSTOM;\n\n // `live` keeps the margin fresh: balances refetch when an open/close settles\n // on-chain, so the Max chip drops without a manual refresh.\n const balanceQuery = useAccountBalanceOf({\n account,\n chainId,\n config,\n live: true,\n query: { enabled: isolationKnown && !isCrossMargin },\n });\n const feeQuery = useFeeForUser({\n user: account ?? zeroAddress,\n symbolId: symbolId ?? 0,\n chainId,\n config,\n query: { enabled: isolationKnown && !isCrossMargin && Boolean(account && symbolId !== undefined) },\n });\n const balanceInfoQuery = useAccountBalanceInfo({\n account,\n chainId,\n config,\n live: true,\n query: { enabled: isCrossMargin && Boolean(account) },\n });\n const accountUpnl = useAccountUpnl({ account, chainId, solverId, config, enabled: isCrossMargin, live: true });\n const { data: balance, isLoading: balanceLoading, error: balanceError, refetch: refetchBalance } = balanceQuery;\n const { data: fees, isLoading: feeLoading, error: feeError, refetch: refetchFees } = feeQuery;\n const {\n data: balanceInfo,\n isLoading: balanceInfoLoading,\n error: balanceInfoError,\n refetch: refetchBalanceInfo,\n } = balanceInfoQuery;\n const { upnl, isLoading: upnlLoading, refetch: refetchUpnl } = accountUpnl;\n\n const refetch = useCallback(async () => {\n if (isCrossMargin) {\n await Promise.all([refetchBalanceInfo(), refetchUpnl()]);\n return;\n }\n await Promise.all([refetchBalance(), refetchFees()]);\n }, [isCrossMargin, refetchBalance, refetchFees, refetchBalanceInfo, refetchUpnl]);\n\n return useMemo<UseAvailableInstantOpenMarginReturnType>(() => {\n const isolationLoading = Boolean(account) && !isolationKnown;\n\n if (isCrossMargin) {\n const isLoading = balanceInfoLoading || upnlLoading;\n const error = balanceInfoError ?? null;\n\n if (balanceInfo === undefined || upnl === undefined) {\n return { availableMarginWei: undefined, availableMargin: \"0\", isLoading, error, refetch };\n }\n const available = calculateAvailableForOrder({ balanceInfo, upnl });\n const clamped = available > 0n ? available : 0n;\n // Rasa-only 10% spend buffer — see RASA_SPENDABLE_BPS.\n const availableMarginWei = isRasaSolver ? (clamped * RASA_SPENDABLE_BPS) / 10_000n : clamped;\n return { availableMarginWei, availableMargin: formatUnits(availableMarginWei, 18), isLoading, error, refetch };\n }\n\n const isLoading = isolationLoading || balanceLoading || feeLoading;\n const error = balanceError ?? feeError ?? null;\n\n if (!isolationKnown || balance === undefined || fees === undefined) {\n return { availableMarginWei: undefined, availableMargin: \"0\", isLoading, error, refetch };\n }\n\n const availableMarginWei = calculateAvailableInstantOpenMargin({\n balance,\n openFee: fees.openFee,\n closeFee: fees.closeFee,\n slippageFractionWei: slippagePercentToFractionWei(slippage),\n leverage,\n positionType,\n });\n\n return { availableMarginWei, availableMargin: formatUnits(availableMarginWei, 18), isLoading, error, refetch };\n }, [\n account,\n isolationKnown,\n isCrossMargin,\n isRasaSolver,\n balance,\n balanceLoading,\n balanceError,\n fees,\n feeLoading,\n feeError,\n balanceInfo,\n balanceInfoLoading,\n balanceInfoError,\n upnl,\n upnlLoading,\n slippage,\n leverage,\n positionType,\n refetch,\n ]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-available-instant-open-margin.js","names":[],"sources":["../../src/margin/use-available-instant-open-margin.ts"],"sourcesContent":["\"use client\";\n\nimport {\n calculateAvailableForOrder,\n calculateAvailableInstantOpenMargin,\n PositionType,\n SubAccountIsolationType,\n type ConfigParameter,\n type SolverId,\n} from \"@symmio/trading-core\";\nimport { useCallback, useMemo } from \"react\";\nimport { formatUnits, zeroAddress, type Address } from \"viem\";\nimport { useAccountBalanceInfo } from \"../account-layer/use-account-balance-info\";\nimport { useAccountBalanceOf } from \"../account-layer/use-account-balance-of\";\nimport { useSubAccount } from \"../account-layer/use-sub-account\";\nimport type { SymmioRequestError } from \"../errors/symmio-request-error\";\nimport { useFeeForUser } from \"../fees/use-fee-for-user\";\nimport { useSymmioConfig } from \"../provider/use-symmio-config\";\nimport { useAccountUpnl } from \"../quotes/use-account-upnl\";\n\n/** Convert a slippage percent (e.g. `5`) to an 18-decimal fraction (`5e16`). */\nfunction slippagePercentToFractionWei(slippage: number): bigint {\n return BigInt(Math.round(slippage * 1e16));\n}\n\n/**\n * **Rasa-specific policy**: the fraction of a cross-margin account's\n * `availableForOrder` a new open may spend, in basis points (90%). The\n * remaining 10% stays as a safety buffer so a fill at a slightly worse price\n * or a fee charge cannot tip the account straight into deficiency.\n *\n * Applied only when the resolved solver is Rasa — a future cross-margin solver\n * spends 100% unless its vendor mandates its own buffer.\n */\nconst RASA_SPENDABLE_BPS = 9_000n;\n\n/** Parameters for {@link useAvailableInstantOpenMargin}. */\nexport interface UseAvailableInstantOpenMarginParameters extends ConfigParameter {\n /** SubAccount to spend margin from. The hook is idle until it is set. */\n account?: Address;\n /** Market symbol id, for the fee lookup. Idle until it is set. */\n symbolId?: bigint | number;\n /** Requested leverage (integer ≥ 1). */\n leverage: number;\n /** LONG skips the slippage cap; SHORT applies it. */\n positionType: PositionType;\n /** Slippage percent, e.g. `5`. */\n slippage: number;\n /**\n * Solver whose markets and price provider value the cross-margin uPnL.\n * Defaults to the chain's default solver. The margin **model** is not chosen\n * here — it follows the sub-account's isolation type.\n */\n solverId?: SolverId;\n /** Optional chain override; defaults to the connected chain. */\n chainId?: number;\n}\n\n/** Return type of {@link useAvailableInstantOpenMargin}. */\nexport interface UseAvailableInstantOpenMarginReturnType {\n /** Spendable margin in 18-decimal wei; `undefined` until balance + fees load. */\n availableMarginWei: bigint | undefined;\n /** {@link UseAvailableInstantOpenMarginReturnType.availableMarginWei} as a decimal string; `\"0\"` when unavailable. */\n availableMargin: string;\n /** `true` while the underlying balance / fee reads are loading. */\n isLoading: boolean;\n /** First error from the balance / fee reads, if any. */\n error: SymmioRequestError | null;\n /** Refetch the underlying balance + fee reads (e.g. after an open/close settles). */\n refetch: () => Promise<void>;\n}\n\n/**\n * Maximum initial margin an instant open can spend for `account` on a market —\n * **the** margin hook. The margin model follows the sub-account's isolation\n * type:\n *\n * - **VA isolations** (`POSITION` / `MARKET` / `MARKET_DIRECTION`): the\n * sub-account's available balance shaved for the open fee and — SHORT only — a\n * worst-case slippage fill (`calculateAvailableInstantOpenMargin` over\n * {@link useAccountBalanceOf} + {@link useFeeForUser}). The close fee is\n * charged at close, so it is not reserved here.\n * - **`CUSTOM` (cross-margin)** — trades execute on the sub-account directly:\n * `calculateAvailableForOrder` over the live `balanceInfoOfPartyA` snapshot\n * and the account's SDK-computed uPnL ({@link useAccountUpnl} — Σ\n * per-position uPnL against live mark prices), clamped at zero. **On the\n * Rasa solver only**, 90% of it is spendable — a 10% buffer stays reserved\n * (see `RASA_SPENDABLE_BPS`); other cross-margin solvers spend 100%.\n *\n * Every read is live: balances refetch on on-chain settle notifications and\n * the cross-margin uPnL streams off the price socket, so the `Max` chip tracks\n * trades and the market without manual refreshes. Wire the result to the trade\n * form's `Max` chip and gate submit on it.\n *\n * @example\n * ```tsx\n * const { availableMargin, availableMarginWei } = useAvailableInstantOpenMargin({\n * account: subAccount,\n * symbolId: market.symbolId,\n * leverage,\n * positionType,\n * slippage,\n * });\n * ```\n */\nexport function useAvailableInstantOpenMargin(\n parameters: UseAvailableInstantOpenMarginParameters,\n): UseAvailableInstantOpenMarginReturnType {\n const { account, symbolId, leverage, positionType, slippage, solverId, chainId, config } = parameters;\n\n // The 10% spend buffer is a RASA policy, not a cross-margin-model rule —\n // resolved by solver id so a future cross-margin solver is unaffected.\n const resolvedConfig = useSymmioConfig(parameters);\n const isRasaSolver = resolvedConfig.getSolver({ chainId, solverId }).id === \"rasa\";\n\n // The margin model follows the sub-account's isolation: `CUSTOM` trades on\n // the sub-account directly (cross-margin); the VA isolations spend the\n // sub-account's available balance. Isolation is fixed at creation, so this\n // read caches indefinitely.\n const subAccountQuery = useSubAccount({\n account: account ?? zeroAddress,\n chainId,\n config,\n query: { enabled: Boolean(account), staleTime: Infinity },\n });\n const isolationType = account !== undefined ? subAccountQuery.data?.isolationType : undefined;\n const isolationKnown = isolationType !== undefined;\n const isCrossMargin = isolationType === SubAccountIsolationType.CUSTOM;\n\n // `live` keeps the margin fresh: balances refetch when an open/close settles\n // on-chain, so the Max chip drops without a manual refresh.\n const balanceQuery = useAccountBalanceOf({\n account,\n chainId,\n config,\n live: true,\n query: { enabled: isolationKnown && !isCrossMargin },\n });\n const feeQuery = useFeeForUser({\n user: account ?? zeroAddress,\n symbolId: symbolId ?? 0,\n chainId,\n config,\n query: { enabled: isolationKnown && !isCrossMargin && Boolean(account && symbolId !== undefined) },\n });\n const balanceInfoQuery = useAccountBalanceInfo({\n account,\n chainId,\n config,\n live: true,\n query: { enabled: isCrossMargin && Boolean(account) },\n });\n const accountUpnl = useAccountUpnl({ account, chainId, solverId, config, enabled: isCrossMargin, live: true });\n const { data: balance, isLoading: balanceLoading, error: balanceError, refetch: refetchBalance } = balanceQuery;\n const { data: fees, isLoading: feeLoading, error: feeError, refetch: refetchFees } = feeQuery;\n const {\n data: balanceInfo,\n isLoading: balanceInfoLoading,\n error: balanceInfoError,\n refetch: refetchBalanceInfo,\n } = balanceInfoQuery;\n const { upnl, isLoading: upnlLoading, refetch: refetchUpnl } = accountUpnl;\n\n const refetch = useCallback(async () => {\n if (isCrossMargin) {\n await Promise.all([refetchBalanceInfo(), refetchUpnl()]);\n return;\n }\n await Promise.all([refetchBalance(), refetchFees()]);\n }, [isCrossMargin, refetchBalance, refetchFees, refetchBalanceInfo, refetchUpnl]);\n\n return useMemo<UseAvailableInstantOpenMarginReturnType>(() => {\n const isolationLoading = Boolean(account) && !isolationKnown;\n\n if (isCrossMargin) {\n const isLoading = balanceInfoLoading || upnlLoading;\n const error = balanceInfoError ?? null;\n\n if (balanceInfo === undefined || upnl === undefined) {\n return { availableMarginWei: undefined, availableMargin: \"0\", isLoading, error, refetch };\n }\n const available = calculateAvailableForOrder({ balanceInfo, upnl });\n const clamped = available > 0n ? available : 0n;\n // Rasa-only 10% spend buffer — see RASA_SPENDABLE_BPS.\n const availableMarginWei = isRasaSolver ? (clamped * RASA_SPENDABLE_BPS) / 10_000n : clamped;\n return { availableMarginWei, availableMargin: formatUnits(availableMarginWei, 18), isLoading, error, refetch };\n }\n\n const isLoading = isolationLoading || balanceLoading || feeLoading;\n const error = balanceError ?? feeError ?? null;\n\n if (!isolationKnown || balance === undefined || fees === undefined) {\n return { availableMarginWei: undefined, availableMargin: \"0\", isLoading, error, refetch };\n }\n\n const availableMarginWei = calculateAvailableInstantOpenMargin({\n balance,\n openFee: fees.openFee,\n // Close fee is charged at close from the position, not reserved from the\n // open budget, so it is not shaved here.\n slippageFractionWei: slippagePercentToFractionWei(slippage),\n leverage,\n positionType,\n });\n\n return { availableMarginWei, availableMargin: formatUnits(availableMarginWei, 18), isLoading, error, refetch };\n }, [\n account,\n isolationKnown,\n isCrossMargin,\n isRasaSolver,\n balance,\n balanceLoading,\n balanceError,\n fees,\n feeLoading,\n feeError,\n balanceInfo,\n balanceInfoLoading,\n balanceInfoError,\n upnl,\n upnlLoading,\n slippage,\n leverage,\n positionType,\n refetch,\n ]);\n}\n"],"mappings":"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"}
|
package/dist/solvers/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/solvers/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/solvers/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ConfigParameter, GetSolverInfoData, GetSolverInfoOptions } from '@symmio/trading-core';
|
|
2
|
+
import { UseQueryResult } from '@tanstack/react-query';
|
|
3
|
+
import { SymmioRequestError } from '../errors/symmio-request-error.js';
|
|
4
|
+
/** Parameters for {@link useSolverInfo}. */
|
|
5
|
+
export type UseSolverInfoParameters = GetSolverInfoOptions & ConfigParameter;
|
|
6
|
+
/** Return type of {@link useSolverInfo}. */
|
|
7
|
+
export type UseSolverInfoReturnType = UseQueryResult<GetSolverInfoData, SymmioRequestError>;
|
|
8
|
+
/**
|
|
9
|
+
* Read the solver's static fee configuration via the Enigma-only `/info`
|
|
10
|
+
* endpoint. Fails with `UNSUPPORTED_BY_SOLVER` when the resolved solver is not
|
|
11
|
+
* an `enigma` solver.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useSolverInfo(parameters?: UseSolverInfoParameters): UseSolverInfoReturnType;
|
|
14
|
+
//# sourceMappingURL=use-solver-info.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-solver-info.d.ts","sourceRoot":"","sources":["../../src/solvers/use-solver-info.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAIzE,4CAA4C;AAC5C,MAAM,MAAM,uBAAuB,GAAG,oBAAoB,GAAG,eAAe,CAAC;AAE7E,4CAA4C;AAC5C,MAAM,MAAM,uBAAuB,GAAG,cAAc,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAAC;AAE5F;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,UAAU,GAAE,uBAA4B,GAAG,uBAAuB,CAkB/F"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSymmioChainId as e } from "../provider/use-symmio-chain-id.js";
|
|
3
|
+
import { useSymmioConfig as t } from "../provider/use-symmio-config.js";
|
|
4
|
+
import { normalizeSymmError as n } from "../errors/normalize-symm-error.js";
|
|
5
|
+
import { getSolverInfoQueryOptions as r } from "@symmio/trading-core";
|
|
6
|
+
import { useQuery as i } from "@tanstack/react-query";
|
|
7
|
+
//#region src/solvers/use-solver-info.ts
|
|
8
|
+
function a(a = {}) {
|
|
9
|
+
let o = t(a), s = e(), c = r(o, {
|
|
10
|
+
...a,
|
|
11
|
+
chainId: a.chainId ?? s
|
|
12
|
+
});
|
|
13
|
+
return i({
|
|
14
|
+
...c,
|
|
15
|
+
queryFn: async () => {
|
|
16
|
+
try {
|
|
17
|
+
return await c.queryFn();
|
|
18
|
+
} catch (e) {
|
|
19
|
+
throw n(e);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { a as useSolverInfo };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=use-solver-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-solver-info.js","names":[],"sources":["../../src/solvers/use-solver-info.ts"],"sourcesContent":["\"use client\";\n\nimport {\n getSolverInfoQueryOptions,\n type ConfigParameter,\n type GetSolverInfoData,\n type GetSolverInfoOptions,\n} from \"@symmio/trading-core\";\nimport { useQuery, type UseQueryResult } from \"@tanstack/react-query\";\nimport { normalizeSymmError } from \"../errors/normalize-symm-error\";\nimport type { SymmioRequestError } from \"../errors/symmio-request-error\";\nimport { useSymmioChainId } from \"../provider/use-symmio-chain-id\";\nimport { useSymmioConfig } from \"../provider/use-symmio-config\";\n\n/** Parameters for {@link useSolverInfo}. */\nexport type UseSolverInfoParameters = GetSolverInfoOptions & ConfigParameter;\n\n/** Return type of {@link useSolverInfo}. */\nexport type UseSolverInfoReturnType = UseQueryResult<GetSolverInfoData, SymmioRequestError>;\n\n/**\n * Read the solver's static fee configuration via the Enigma-only `/info`\n * endpoint. Fails with `UNSUPPORTED_BY_SOLVER` when the resolved solver is not\n * an `enigma` solver.\n */\nexport function useSolverInfo(parameters: UseSolverInfoParameters = {}): UseSolverInfoReturnType {\n const config = useSymmioConfig(parameters);\n const chainId = useSymmioChainId();\n const options = getSolverInfoQueryOptions(config, {\n ...parameters,\n chainId: parameters.chainId ?? chainId,\n });\n\n return useQuery({\n ...options,\n queryFn: async () => {\n try {\n return await options.queryFn();\n } catch (err) {\n throw normalizeSymmError(err);\n }\n },\n }) as UseSolverInfoReturnType;\n}\n"],"mappings":";;;;;;;AAyBA,SAAgB,EAAc,IAAsC,CAAC,GAA4B;CAC/F,IAAM,IAAS,EAAgB,CAAU,GACnC,IAAU,EAAiB,GAC3B,IAAU,EAA0B,GAAQ;EAChD,GAAG;EACH,SAAS,EAAW,WAAW;CACjC,CAAC;CAED,OAAO,EAAS;EACd,GAAG;EACH,SAAS,YAAY;GACnB,IAAI;IACF,OAAO,MAAM,EAAQ,QAAQ;GAC/B,SAAS,GAAK;IACZ,MAAM,EAAmB,CAAG;GAC9B;EACF;CACF,CAAC;AACH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symmio/trading-react",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "React adapter for the SYMMIO SDK. Hooks, providers, and React-bound ergonomics on top of @symmio/trading-core.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/SYMM-IO/Trading-SDK/tree/main/packages/trading-react#readme",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"@tanstack/react-query": "^5.100.0",
|
|
92
92
|
"decimal.js": "^10.4.3",
|
|
93
93
|
"zustand": "^5.0.0",
|
|
94
|
-
"@symmio/trading-core": "^3.
|
|
94
|
+
"@symmio/trading-core": "^3.4.0"
|
|
95
95
|
},
|
|
96
96
|
"peerDependencies": {
|
|
97
97
|
"react": "^19.0.0",
|