@orderly.network/ui-transfer 3.1.9 → 3.1.10

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/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { i18n, useTranslation, Trans } from '@orderly.network/i18n';
2
- import { registerSimpleDialog, registerSimpleSheet, Tabs, TabPanel, ArrowDownSquareFillIcon, ArrowUpSquareFillIcon, ExtensionSlot, ExtensionPositionEnum, toast, Box, Flex, textVariants, Text, cn, Button, useDocumentDirection, Spinner, Select, Input, inputFormatter, useScreen, modal, Tooltip, Tips, AlertDialog, EditIcon, ArrowLeftRightIcon, WalletIcon, DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuPortal, DropdownMenuContent, ScrollArea, ChainIcon, Badge, CaretUpIcon, CaretDownIcon, SimpleDialog, CloseRoundFillIcon, ExclamationFillIcon, TokenIcon, WarningIcon, SelectItem, ChevronRightIcon, Checkbox, CloseIcon } from '@orderly.network/ui';
3
- import { forwardRef, useState, isValidElement, useMemo, useEffect, useCallback, useRef } from 'react';
4
- import { useConfig, useWalletConnector, useLocalStorage, useConvert, useSwapQuote as useSwapQuote$1, useComputedLTV, useAccount, useAppStore, useTransfer, useSubAccountDataObserver, useSubAccountMaxWithdrawal, useMainTokenStore, useEventEmitter, usePositionStream, useIndexPricesStream, useOrderlyContext, useDeposit, useAssetsHistory, useChains, useMemoizedFn, useWithdraw, useWalletTopic, useQuery, useSWR, useTokenInfo, useTokensInfo, useHoldingStream, useInternalTransfer, useDebouncedCallback, usePrivateQuery, useTransferHistory, useBalanceTopic } from '@orderly.network/hooks';
5
- import { Decimal, zero, praseChainIdToNumber, int2hex, toNonExponential, formatWithPrecision, isSolana } from '@orderly.network/utils';
2
+ import { registerSimpleDialog, registerSimpleSheet, Tabs, TabPanel, ArrowDownSquareFillIcon, ArrowUpSquareFillIcon, useModal, ExtensionSlot, ExtensionPositionEnum, toast, Box, Text, Flex, cn, Button, textVariants, ExclamationFillIcon, ArrowLeftRightIcon, WalletIcon, DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuPortal, DropdownMenuContent, ScrollArea, useDocumentDirection, Spinner, Select, Input, inputFormatter, useScreen, modal, Tooltip, ToastTile, CheckedCircleFillIcon, Tips, AlertDialog, EditIcon, ChainIcon, Badge, CaretUpIcon, CaretDownIcon, SimpleDialog, CloseRoundFillIcon, TokenIcon, WarningIcon, SelectItem, ChevronRightIcon, Checkbox, CloseIcon } from '@orderly.network/ui';
3
+ import { forwardRef, useState, isValidElement, useMemo, useCallback, useEffect, useRef } from 'react';
4
+ import { useConfig, useAccount, useAppStore, useTransfer, useSubAccountDataObserver, useSubAccountMaxWithdrawal, useSessionStorage, useEventEmitter, useWalletConnector, useLocalStorage, usePrivateQuery, useConvert, useSwapQuote as useSwapQuote$1, useComputedLTV, useOrderlyContext, useIndexPricesStream, useDeposit, useAssetsHistory, useChains, useMemoizedFn, useWithdraw, useWalletTopic, usePositionStream, useMainTokenStore, useSWR, useTokenInfo, useQuery, useTokensInfo, useHoldingStream, useInternalTransfer, useDebouncedCallback, useTransferHistory, useBalanceTopic } from '@orderly.network/hooks';
5
+ import { zero, Decimal, praseChainIdToNumber, int2hex, toNonExponential, formatWithPrecision, isSolana } from '@orderly.network/utils';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
  import { AccountStatusEnum, ChainNamespace, AssetHistoryStatusEnum, AssetHistorySideEnum, EMPTY_OBJECT, isNativeTokenChecker, ABSTRACT_CHAIN_ID_MAP, nativeTokenAddress } from '@orderly.network/types';
8
8
  import { AuthGuard, useAuthGuard } from '@orderly.network/ui-connector';
@@ -1526,6 +1526,24 @@ var QuantityInput = (props) => {
1526
1526
  hintMessage && message
1527
1527
  ] });
1528
1528
  };
1529
+
1530
+ // src/components/slippage/slippage.utils.ts
1531
+ var getSlippageRiskLevel = (value, min, recommendedValue) => {
1532
+ if (value === void 0 || !Number.isFinite(value)) {
1533
+ return void 0;
1534
+ }
1535
+ if (value < min) {
1536
+ return "minimum";
1537
+ }
1538
+ if (value < recommendedValue) {
1539
+ return "low";
1540
+ }
1541
+ if (value > recommendedValue) {
1542
+ return "high";
1543
+ }
1544
+ return void 0;
1545
+ };
1546
+ var shouldDisableSlippageSave = (value, riskLevel) => !value || riskLevel === "minimum";
1529
1547
  var options = [0.5, 1, 2];
1530
1548
  var Slippage = (props) => {
1531
1549
  const { min = 0.2, max = 10, dp = 2 } = props;
@@ -1534,10 +1552,12 @@ var Slippage = (props) => {
1534
1552
  const [open, setOpen] = useState(false);
1535
1553
  const { t } = useTranslation();
1536
1554
  useEffect(() => {
1537
- if (props.value && !options.includes(props.value)) {
1555
+ if (props.value !== void 0 && !options.includes(props.value)) {
1556
+ setValue(void 0);
1538
1557
  setCustomValue(props.value.toString());
1539
1558
  } else {
1540
1559
  setValue(props.value);
1560
+ setCustomValue("");
1541
1561
  }
1542
1562
  }, [props.value, open]);
1543
1563
  const showSlippage = () => {
@@ -1557,7 +1577,7 @@ var Slippage = (props) => {
1557
1577
  }
1558
1578
  const d = new Decimal(val);
1559
1579
  setValue(void 0);
1560
- if (d.lt(min)) {
1580
+ if (d.lt(min) && !props.riskGuidance) {
1561
1581
  setCustomValue(min.toString());
1562
1582
  } else if (d.gt(max)) {
1563
1583
  setCustomValue(max.toString());
@@ -1576,17 +1596,27 @@ var Slippage = (props) => {
1576
1596
  }
1577
1597
  return Promise.resolve(true);
1578
1598
  };
1579
- const disabled = !getValue();
1599
+ const currentValue = getValue();
1600
+ const riskLevel = props.riskGuidance ? getSlippageRiskLevel(
1601
+ currentValue,
1602
+ min,
1603
+ props.riskGuidance.recommendedValue
1604
+ ) : void 0;
1580
1605
  const errorMessage = useMemo(() => {
1581
- return props.validate?.(getValue());
1582
- }, [value, customValue, props.validate]);
1606
+ if (riskLevel === "minimum") {
1607
+ return props.riskGuidance?.minimumError;
1608
+ }
1609
+ return currentValue === void 0 ? void 0 : props.validate?.(currentValue);
1610
+ }, [currentValue, props.riskGuidance, props.validate, riskLevel]);
1611
+ const warningMessage = riskLevel === "low" ? props.riskGuidance?.lowWarning : riskLevel === "high" ? props.riskGuidance?.highWarning : void 0;
1612
+ const disabled = shouldDisableSlippageSave(currentValue, riskLevel);
1583
1613
  const content = /* @__PURE__ */ jsxs("div", { className: "oui-text-2xs", children: [
1584
1614
  /* @__PURE__ */ jsxs(Flex, { gapX: 1, itemAlign: "center", children: [
1585
1615
  /* @__PURE__ */ jsx(Text, { size: "sm", intensity: 54, children: t("transfer.slippage.slippageTolerance") }),
1586
1616
  /* @__PURE__ */ jsx(
1587
1617
  Tips,
1588
1618
  {
1589
- content: t("transfer.slippage.slippageTolerance.description"),
1619
+ content: props.riskGuidance?.tooltip ?? t("transfer.slippage.slippageTolerance.description"),
1590
1620
  title: t("common.tips"),
1591
1621
  classNamss: {
1592
1622
  trigger: "oui-mt-[2px] oui-size-4"
@@ -1631,7 +1661,17 @@ var Slippage = (props) => {
1631
1661
  }
1632
1662
  )
1633
1663
  ] }),
1634
- errorMessage && /* @__PURE__ */ jsx(Flex, { mt: 6, children: /* @__PURE__ */ jsx(
1664
+ (props.riskGuidance?.helperText || props.message) && /* @__PURE__ */ jsx(
1665
+ Text,
1666
+ {
1667
+ as: "div",
1668
+ size: "2xs",
1669
+ intensity: 54,
1670
+ className: "oui-mt-3 oui-leading-4",
1671
+ children: props.riskGuidance?.helperText || props.message
1672
+ }
1673
+ ),
1674
+ errorMessage && /* @__PURE__ */ jsx(Flex, { mt: 3, children: /* @__PURE__ */ jsx(
1635
1675
  Text,
1636
1676
  {
1637
1677
  size: "2xs",
@@ -1639,7 +1679,8 @@ var Slippage = (props) => {
1639
1679
  className: "oui-w-full oui-text-center",
1640
1680
  children: errorMessage
1641
1681
  }
1642
- ) })
1682
+ ) }),
1683
+ !errorMessage && warningMessage && /* @__PURE__ */ jsx(Flex, { mt: 3, children: /* @__PURE__ */ jsx(Text, { as: "div", size: "2xs", color: "warning", className: "oui-leading-4", children: warningMessage }) })
1643
1684
  ] });
1644
1685
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1645
1686
  /* @__PURE__ */ jsx(
@@ -7224,7 +7265,96 @@ var SwapCoin = (props) => {
7224
7265
  }
7225
7266
  );
7226
7267
  };
7268
+ var getTrackedConvertRequestForAccount = (request, accountId) => request?.accountId === accountId ? request : void 0;
7269
+ var isValidConvertAmount = (value) => {
7270
+ const amount = Number(value);
7271
+ return Number.isFinite(amount) && amount > 0;
7272
+ };
7273
+ var getConvertTargetAmount = (submittedAmount) => isValidConvertAmount(submittedAmount) ? String(submittedAmount) : "-";
7274
+ var getConvertReceivedAmount = (record) => {
7275
+ if (!record || record.status !== "completed" && record.status !== "succeeded") {
7276
+ return "-";
7277
+ }
7278
+ if (isValidConvertAmount(record.received_qty)) {
7279
+ return String(record.received_qty);
7280
+ }
7281
+ const detailAmount = record.details.reduce((total, detail) => {
7282
+ if (detail.result?.toLowerCase() !== "succeeded" || !isValidConvertAmount(detail.received_qty)) {
7283
+ return total;
7284
+ }
7285
+ return total.plus(detail.received_qty);
7286
+ }, new Decimal(0));
7287
+ return detailAmount.gt(0) ? detailAmount.toString() : "-";
7288
+ };
7289
+ var createTrackedConvertRequest = (record) => {
7290
+ const sourceEntry = Object.entries(record.converted_asset ?? {})[0];
7291
+ const sourceDetail = record.details[0];
7292
+ return {
7293
+ convertId: record.convert_id,
7294
+ previousMaxConvertId: record.convert_id - 1,
7295
+ sourceToken: sourceEntry?.[0] || sourceDetail?.converted_asset || "-",
7296
+ sourceAmount: String(
7297
+ sourceEntry?.[1] ?? sourceDetail?.converted_qty ?? "-"
7298
+ ),
7299
+ targetToken: record.received_asset || "USDC",
7300
+ targetAmount: getConvertTargetAmount(void 0)
7301
+ };
7302
+ };
7303
+ var getEffectiveConvertStatus = (record) => {
7304
+ if (record.status === "pending") {
7305
+ return "pending";
7306
+ }
7307
+ const hasFailedTransaction = record.details.some(
7308
+ (detail) => detail.result?.toLowerCase() === "failed"
7309
+ );
7310
+ if (hasFailedTransaction) {
7311
+ return "failed";
7312
+ }
7313
+ const hasOnlySucceededTransactions = record.status === "completed" && record.details.length > 0 && record.details.every(
7314
+ (detail) => detail.result?.toLowerCase() === "succeeded"
7315
+ );
7316
+ return hasOnlySucceededTransactions ? "succeeded" : record.status;
7317
+ };
7318
+ var getConvertProgressStatus = (status, isRefreshing, isDelayed) => {
7319
+ if (status !== "pending") {
7320
+ return status;
7321
+ }
7322
+ if (isRefreshing) {
7323
+ return "checking";
7324
+ }
7325
+ return isDelayed ? "delayed" : "pending";
7326
+ };
7327
+ var shouldDisplayConvertProgress = (status, isRecoveredRequest) => Boolean(status && (!isRecoveredRequest || status === "pending"));
7328
+ var recordContainsToken = (record, token) => {
7329
+ return Object.keys(record.converted_asset ?? {}).some(
7330
+ (asset) => asset.toLowerCase() === token.toLowerCase()
7331
+ ) || record.details.some(
7332
+ (detail) => detail.converted_asset?.toLowerCase() === token.toLowerCase()
7333
+ );
7334
+ };
7335
+ var findTrackedConvertRecord = (rows, request) => {
7336
+ if (request.convertId !== void 0) {
7337
+ return rows.find((record) => record.convert_id === request.convertId);
7338
+ }
7339
+ return rows.find(
7340
+ (record) => record.convert_id > request.previousMaxConvertId && record.type === "manual" && recordContainsToken(record, request.sourceToken)
7341
+ );
7342
+ };
7343
+ var findLatestPendingConvertRecord = (rows) => rows.find((record) => getEffectiveConvertStatus(record) === "pending");
7344
+ var getConvertIdFromResponse = (response) => {
7345
+ if (!response || typeof response !== "object") {
7346
+ return void 0;
7347
+ }
7348
+ const value = response;
7349
+ const convertId = value.data?.convertId ?? value.data?.convert_id ?? value.convertId ?? value.convert_id;
7350
+ const numericConvertId = Number(convertId);
7351
+ return Number.isFinite(numericConvertId) ? numericConvertId : void 0;
7352
+ };
7353
+ var normalizeConvertSlippage = (value) => {
7354
+ return Number.isFinite(value) ? Math.max(0.2, value) : 0.5;
7355
+ };
7227
7356
  var ConvertFormUI = (props) => {
7357
+ const { t } = useTranslation();
7228
7358
  const {
7229
7359
  loading,
7230
7360
  disabled,
@@ -7245,8 +7375,26 @@ var ConvertFormUI = (props) => {
7245
7375
  currentLTV,
7246
7376
  nextLTV,
7247
7377
  networkId,
7248
- balanceRevalidating
7378
+ balanceRevalidating,
7379
+ activeConvertRequest,
7380
+ isCheckingConvertStatus,
7381
+ hasConvertStatusError,
7382
+ isConvertHistoryRefreshing,
7383
+ onRefreshConvertStatus,
7384
+ isRecoveredConvertRequest
7249
7385
  } = props;
7386
+ if (activeConvertRequest && shouldDisplayConvertProgress(
7387
+ activeConvertRequest.status,
7388
+ isRecoveredConvertRequest
7389
+ )) {
7390
+ return /* @__PURE__ */ jsx(
7391
+ ConvertProgressStatus,
7392
+ {
7393
+ request: activeConvertRequest,
7394
+ isRefreshing: isConvertHistoryRefreshing
7395
+ }
7396
+ );
7397
+ }
7250
7398
  return /* @__PURE__ */ jsxs(Box, { className: textVariants({ weight: "semibold" }), children: [
7251
7399
  /* @__PURE__ */ jsxs(Box, { className: "oui-mb-6 lg:oui-mb-8", children: [
7252
7400
  /* @__PURE__ */ jsx(Box, { mt: 3, mb: 1, children: /* @__PURE__ */ jsx(
@@ -7290,7 +7438,21 @@ var ConvertFormUI = (props) => {
7290
7438
  targetSymbol: targetToken?.token
7291
7439
  }
7292
7440
  ),
7293
- /* @__PURE__ */ jsx(Slippage, { value: slippage, onValueChange: onSlippageChange }),
7441
+ /* @__PURE__ */ jsx(
7442
+ Slippage,
7443
+ {
7444
+ value: slippage,
7445
+ onValueChange: onSlippageChange,
7446
+ riskGuidance: {
7447
+ recommendedValue: 0.5,
7448
+ tooltip: t("transfer.convert.slippage.tooltip"),
7449
+ helperText: t("transfer.convert.slippage.helper"),
7450
+ minimumError: t("transfer.convert.slippage.minimumError"),
7451
+ lowWarning: t("transfer.convert.slippage.lowWarning"),
7452
+ highWarning: t("transfer.convert.slippage.highWarning")
7453
+ }
7454
+ }
7455
+ ),
7294
7456
  /* @__PURE__ */ jsx(
7295
7457
  MinimumReceived,
7296
7458
  {
@@ -7309,17 +7471,117 @@ var ConvertFormUI = (props) => {
7309
7471
  )
7310
7472
  ] })
7311
7473
  ] }),
7474
+ hasConvertStatusError && /* @__PURE__ */ jsxs(
7475
+ Flex,
7476
+ {
7477
+ itemAlign: "center",
7478
+ justify: "between",
7479
+ gap: 3,
7480
+ className: "oui-mb-4 oui-w-full",
7481
+ children: [
7482
+ /* @__PURE__ */ jsxs(Flex, { itemAlign: "center", gap: 2, children: [
7483
+ /* @__PURE__ */ jsx(ExclamationFillIcon, { className: "oui-size-4 oui-shrink-0 oui-text-warning-darken" }),
7484
+ /* @__PURE__ */ jsx(Text, { size: "xs", intensity: 54, children: t("transfer.convert.statusCheckFailed") })
7485
+ ] }),
7486
+ /* @__PURE__ */ jsx(
7487
+ Button,
7488
+ {
7489
+ size: "sm",
7490
+ variant: "outlined",
7491
+ loading: isConvertHistoryRefreshing,
7492
+ onClick: onRefreshConvertStatus,
7493
+ children: t("common.refresh")
7494
+ }
7495
+ )
7496
+ ]
7497
+ }
7498
+ ),
7312
7499
  /* @__PURE__ */ jsx(Flex, { itemAlign: "center", justify: "center", children: /* @__PURE__ */ jsx(
7313
7500
  ConvertAction,
7314
7501
  {
7315
7502
  networkId,
7316
7503
  disabled,
7317
- loading,
7504
+ loading: loading || isCheckingConvertStatus,
7318
7505
  onConvert
7319
7506
  }
7320
7507
  ) })
7321
7508
  ] });
7322
7509
  };
7510
+ var ConvertProgressStatus = ({ request, isRefreshing }) => {
7511
+ const { t } = useTranslation();
7512
+ const isPending = request.status === "pending";
7513
+ const isCompleted = request.status === "completed" || request.status === "succeeded";
7514
+ const isFailed = !isPending && !isCompleted;
7515
+ const progressStatus = getConvertProgressStatus(
7516
+ request.status,
7517
+ isRefreshing,
7518
+ request.isDelayed
7519
+ );
7520
+ const isChecking = progressStatus === "checking";
7521
+ const isDelayed = progressStatus === "delayed";
7522
+ const hasTargetAmount = isValidConvertAmount(request.targetAmount);
7523
+ const hasReceivedAmount = isValidConvertAmount(request.receivedAmount);
7524
+ const interpolation = {
7525
+ fromAmount: request.sourceAmount,
7526
+ fromToken: request.sourceToken,
7527
+ toAmount: request.targetAmount,
7528
+ toToken: request.targetToken,
7529
+ receivedAmount: request.receivedAmount
7530
+ };
7531
+ const statusLabel = isChecking ? t("transfer.convert.statusChecking") : isDelayed ? t("transfer.convert.delayed", interpolation) : isPending ? t("transfer.convert.pending") : isCompleted ? t("transfer.convert.completed") : t("transfer.convert.failed");
7532
+ const description = isDelayed ? t("transfer.convert.delayed.description") : isPending ? t(
7533
+ hasTargetAmount ? "transfer.convert.pending.description" : "transfer.convert.pending.descriptionWithoutAmount",
7534
+ interpolation
7535
+ ) : isCompleted ? t(
7536
+ hasReceivedAmount ? "transfer.convert.completed.description" : "transfer.convert.completed.descriptionWithoutAmount",
7537
+ interpolation
7538
+ ) : t("transfer.convert.failed.description");
7539
+ return /* @__PURE__ */ jsxs(
7540
+ Flex,
7541
+ {
7542
+ direction: "column",
7543
+ itemAlign: "center",
7544
+ gap: 6,
7545
+ className: "oui-w-full oui-px-4 oui-py-6",
7546
+ children: [
7547
+ /* @__PURE__ */ jsxs(Flex, { itemAlign: "center", justify: "center", gap: 3, className: "oui-w-full", children: [
7548
+ isCompleted && /* @__PURE__ */ jsx(CheckedCircleFillIcon, { className: "oui-size-4 oui-text-success" }),
7549
+ !isPending && !isCompleted && /* @__PURE__ */ jsx(ExclamationFillIcon, { className: "oui-size-4 oui-text-danger" }),
7550
+ /* @__PURE__ */ jsx(
7551
+ Text,
7552
+ {
7553
+ size: "sm",
7554
+ weight: "semibold",
7555
+ intensity: isFailed ? void 0 : 80,
7556
+ color: isFailed ? "danger" : void 0,
7557
+ className: "oui-text-center",
7558
+ children: statusLabel
7559
+ }
7560
+ ),
7561
+ isPending && !isDelayed && /* @__PURE__ */ jsx(
7562
+ Flex,
7563
+ {
7564
+ itemAlign: "center",
7565
+ justify: "center",
7566
+ className: "oui-size-4 oui-shrink-0",
7567
+ children: /* @__PURE__ */ jsx(Spinner, { size: "sm", className: "oui-block" })
7568
+ }
7569
+ )
7570
+ ] }),
7571
+ /* @__PURE__ */ jsx(
7572
+ Text,
7573
+ {
7574
+ as: "div",
7575
+ size: "xs",
7576
+ intensity: 54,
7577
+ className: "oui-w-full oui-text-center",
7578
+ children: description
7579
+ }
7580
+ )
7581
+ ]
7582
+ }
7583
+ );
7584
+ };
7323
7585
  var splitTokenBySymbol = (items) => {
7324
7586
  return items.reduce(
7325
7587
  (result, item) => {
@@ -7372,17 +7634,41 @@ var useToken3 = (options2) => {
7372
7634
  targetToken
7373
7635
  };
7374
7636
  };
7637
+ var isValidAmount = (value) => {
7638
+ const amount = Number(value);
7639
+ return Number.isFinite(amount) && amount > 0;
7640
+ };
7641
+ var getQuoteTargetAmount = (estimatedValue, estimatedAmount, decimals) => {
7642
+ if (isValidAmount(estimatedValue)) {
7643
+ return estimatedValue;
7644
+ }
7645
+ if (isValidAmount(estimatedAmount) && Number.isInteger(decimals) && typeof decimals === "number" && decimals >= 0) {
7646
+ return new Decimal(estimatedAmount).div(new Decimal(10).pow(decimals)).toString();
7647
+ }
7648
+ return "-";
7649
+ };
7375
7650
  var calculateQuoteRate = (inputAmount, estimatedUsdcValue) => new Decimal(estimatedUsdcValue).div(inputAmount).toString();
7376
7651
  var calculateMinimumReceived = (estimatedUsdcValue, slippageLimitPercent) => new Decimal(estimatedUsdcValue).mul(new Decimal(1).minus(new Decimal(slippageLimitPercent).div(100))).toString();
7377
-
7378
- // src/components/convertForm/convertForm.script.tsx
7379
7652
  var ORDERLY_CONVERT_SLIPPAGE_KEY = "orderly_convert_slippage";
7653
+ var ORDERLY_TRACKED_CONVERT_KEY = "orderly_tracked_convert";
7380
7654
  var SWAP_QUOTE_DEBOUNCE_MS = 300;
7655
+ var CONVERT_HISTORY_REFRESH_INTERVAL = 1e4;
7656
+ var CONVERT_DELAY_WARNING_TIMEOUT = 2e4;
7657
+ var CONVERT_HISTORY_URL = "/v1/asset/convert_history?page=1&size=20";
7381
7658
  var getQuoteErrorMessage = (error) => error instanceof Error ? error.message : String(error);
7382
7659
  var useConvertFormScript = (options2) => {
7383
- const { token: defaultToken, close } = options2;
7660
+ const { token: defaultToken } = options2;
7384
7661
  const { t } = useTranslation();
7385
7662
  const [loading, setLoading] = useState(false);
7663
+ const [persistedTrackedConvert, setPersistedTrackedConvert] = useSessionStorage(
7664
+ ORDERLY_TRACKED_CONVERT_KEY,
7665
+ null
7666
+ );
7667
+ const [trackedConvert, setTrackedConvert] = useState(() => persistedTrackedConvert ?? void 0);
7668
+ const [isConvertDelayed, setIsConvertDelayed] = useState(false);
7669
+ const effectiveTrackedConvert = trackedConvert ?? persistedTrackedConvert;
7670
+ const trackedRequest = effectiveTrackedConvert?.request;
7671
+ const ee = useEventEmitter();
7386
7672
  const config = useConfig();
7387
7673
  const networkId = config.get("networkId");
7388
7674
  const [quantity, setQuantity] = useState("");
@@ -7403,19 +7689,145 @@ var useConvertFormScript = (options2) => {
7403
7689
  ORDERLY_CONVERT_SLIPPAGE_KEY,
7404
7690
  0.5
7405
7691
  );
7692
+ const normalizedSlippage = normalizeConvertSlippage(Number(slippage));
7693
+ useEffect(() => {
7694
+ if (normalizedSlippage !== slippage) {
7695
+ setSlippage(normalizedSlippage);
7696
+ }
7697
+ }, [normalizedSlippage, setSlippage, slippage]);
7698
+ const { state: accountState } = useAccount();
7699
+ const canQueryConvertHistory = accountState.status >= AccountStatusEnum.EnableTrading || accountState.status === AccountStatusEnum.EnableTradingWithoutConnected;
7700
+ const currentTrackedRequest = getTrackedConvertRequestForAccount(
7701
+ trackedRequest,
7702
+ accountState.accountId
7703
+ );
7704
+ useEffect(() => {
7705
+ if (!trackedConvert && persistedTrackedConvert) {
7706
+ setTrackedConvert(persistedTrackedConvert);
7707
+ }
7708
+ }, [persistedTrackedConvert, trackedConvert]);
7709
+ useEffect(() => {
7710
+ setIsConvertDelayed(false);
7711
+ }, [accountState.accountId]);
7712
+ const {
7713
+ data: convertHistory,
7714
+ error: convertHistoryError,
7715
+ isValidating: isConvertHistoryRefreshing,
7716
+ mutate: refreshConvertHistory
7717
+ } = usePrivateQuery(CONVERT_HISTORY_URL, {
7718
+ formatter: (data) => data,
7719
+ revalidateOnFocus: true,
7720
+ refreshInterval: (latestData) => {
7721
+ const rows = latestData?.rows ?? [];
7722
+ if (currentTrackedRequest) {
7723
+ const trackedRecord2 = findTrackedConvertRecord(
7724
+ rows,
7725
+ currentTrackedRequest
7726
+ );
7727
+ return !trackedRecord2 || getEffectiveConvertStatus(trackedRecord2) === "pending" ? CONVERT_HISTORY_REFRESH_INTERVAL : 0;
7728
+ }
7729
+ return findLatestPendingConvertRecord(rows) ? CONVERT_HISTORY_REFRESH_INTERVAL : 0;
7730
+ }
7731
+ });
7732
+ const convertHistoryRows = convertHistory?.rows ?? [];
7733
+ const latestPendingRecord = useMemo(
7734
+ () => findLatestPendingConvertRecord(convertHistoryRows),
7735
+ [convertHistoryRows]
7736
+ );
7737
+ const latestPendingRequest = useMemo(() => {
7738
+ if (!latestPendingRecord) {
7739
+ return void 0;
7740
+ }
7741
+ return {
7742
+ ...createTrackedConvertRequest(latestPendingRecord),
7743
+ accountId: accountState.accountId
7744
+ };
7745
+ }, [accountState.accountId, latestPendingRecord]);
7746
+ useEffect(() => {
7747
+ if (!currentTrackedRequest && latestPendingRequest) {
7748
+ setTrackedConvert({
7749
+ request: latestPendingRequest,
7750
+ origin: "recovered"
7751
+ });
7752
+ }
7753
+ }, [currentTrackedRequest, latestPendingRequest]);
7754
+ const effectiveTrackedRequest = currentTrackedRequest ?? latestPendingRequest;
7755
+ useEffect(() => {
7756
+ const handleConvertChange = () => {
7757
+ void refreshConvertHistory();
7758
+ };
7759
+ ee.on("assetconvert:changed", handleConvertChange);
7760
+ return () => {
7761
+ ee.off("assetconvert:changed", handleConvertChange);
7762
+ };
7763
+ }, [ee, refreshConvertHistory]);
7764
+ const trackedRecord = useMemo(
7765
+ () => effectiveTrackedRequest ? findTrackedConvertRecord(convertHistoryRows, effectiveTrackedRequest) : void 0,
7766
+ [convertHistoryRows, effectiveTrackedRequest]
7767
+ );
7768
+ const activeRecord = effectiveTrackedRequest ? trackedRecord : latestPendingRecord;
7769
+ const historyStatus = effectiveTrackedRequest ? trackedRecord ? getEffectiveConvertStatus(trackedRecord) : "pending" : activeRecord ? getEffectiveConvertStatus(activeRecord) : void 0;
7770
+ const activeStatus = historyStatus;
7771
+ useEffect(() => {
7772
+ if (persistedTrackedConvert && activeStatus && activeStatus !== "pending") {
7773
+ setPersistedTrackedConvert(null);
7774
+ }
7775
+ }, [activeStatus, persistedTrackedConvert, setPersistedTrackedConvert]);
7776
+ const trackedRequestKey = effectiveTrackedRequest ? `${effectiveTrackedRequest.convertId ?? "new"}:${effectiveTrackedRequest.previousMaxConvertId}:${effectiveTrackedRequest.sourceToken}:${effectiveTrackedRequest.targetToken}` : void 0;
7777
+ useEffect(() => {
7778
+ if (!trackedRequestKey || activeStatus !== "pending") {
7779
+ return;
7780
+ }
7781
+ const timer = window.setTimeout(() => {
7782
+ setIsConvertDelayed(true);
7783
+ }, CONVERT_DELAY_WARNING_TIMEOUT);
7784
+ return () => window.clearTimeout(timer);
7785
+ }, [activeStatus, trackedRequestKey]);
7406
7786
  const { maxAmount, convert } = useConvert({ token: sourceToken?.token });
7407
7787
  const onConvert = async () => {
7408
- if (loading) {
7788
+ if (loading || activeStatus === "pending" || canQueryConvertHistory && !convertHistory) {
7409
7789
  return;
7410
7790
  }
7411
7791
  setLoading(true);
7792
+ const requestSnapshot = {
7793
+ accountId: accountState.accountId,
7794
+ previousMaxConvertId: convertHistoryRows.reduce(
7795
+ (max, record) => Math.max(max, record.convert_id),
7796
+ 0
7797
+ ),
7798
+ sourceToken: sourceToken?.token || "-",
7799
+ sourceAmount: quantity,
7800
+ targetToken: targetToken?.token || "USDC",
7801
+ targetAmount: memoizedOutAmounts
7802
+ };
7412
7803
  return convert({
7413
7804
  amount: Number(quantity),
7414
- slippage: new Decimal(slippage).div(100).toNumber()
7415
- }).then(() => {
7416
- toast.success(t("transfer.convert.completed"));
7417
- close?.();
7418
- setQuantity("");
7805
+ slippage: new Decimal(normalizedSlippage).div(100).toNumber()
7806
+ }).then((response) => {
7807
+ const nextTrackedConvert = {
7808
+ request: {
7809
+ ...requestSnapshot,
7810
+ convertId: getConvertIdFromResponse(response)
7811
+ },
7812
+ origin: "submitted"
7813
+ };
7814
+ setTrackedConvert(nextTrackedConvert);
7815
+ setPersistedTrackedConvert(nextTrackedConvert);
7816
+ toast.success(
7817
+ /* @__PURE__ */ jsx(
7818
+ ToastTile,
7819
+ {
7820
+ title: t("transfer.convert.request"),
7821
+ subtitle: t("transfer.convert.request.description", {
7822
+ fromAmount: requestSnapshot.sourceAmount,
7823
+ fromToken: requestSnapshot.sourceToken,
7824
+ toAmount: requestSnapshot.targetAmount,
7825
+ toToken: requestSnapshot.targetToken
7826
+ })
7827
+ }
7828
+ )
7829
+ );
7830
+ void refreshConvertHistory();
7419
7831
  }).catch((err) => {
7420
7832
  toast.error(
7421
7833
  err.message?.includes("user rejected") ? t("transfer.convert.failed") : err.message
@@ -7424,6 +7836,9 @@ var useConvertFormScript = (options2) => {
7424
7836
  setLoading(false);
7425
7837
  });
7426
7838
  };
7839
+ const onRefreshConvertStatus = () => {
7840
+ void refreshConvertHistory();
7841
+ };
7427
7842
  const [
7428
7843
  postQuote,
7429
7844
  { data: quoteData, reset: resetQuote, isMutating: isQuoteLoading }
@@ -7436,9 +7851,9 @@ var useConvertFormScript = (options2) => {
7436
7851
  fromToken: sourceToken.token,
7437
7852
  toToken: targetToken.token,
7438
7853
  amount: new Decimal(quantity).toNumber(),
7439
- slippage: new Decimal(slippage).div(100).toNumber()
7854
+ slippage: new Decimal(normalizedSlippage).div(100).toNumber()
7440
7855
  };
7441
- }, [quantity, slippage, sourceToken?.token, targetToken?.token]);
7856
+ }, [normalizedSlippage, quantity, sourceToken?.token, targetToken?.token]);
7442
7857
  useEffect(() => {
7443
7858
  resetQuote();
7444
7859
  if (!quoteRequest) {
@@ -7475,31 +7890,47 @@ var useConvertFormScript = (options2) => {
7475
7890
  }
7476
7891
  const fromToken = quoteData.fromToken;
7477
7892
  const toToken = quoteData.toToken;
7478
- if (!fromToken || !toToken || !fromToken.tokenAddress || !toToken.tokenAddress || !fromToken.amount || !toToken.estimatedAmount || !toToken.estimatedValue || !quoteData.gasEstimate || typeof quoteData.expiresAt !== "number") {
7893
+ if (!fromToken || !toToken || !fromToken.tokenAddress || !toToken.tokenAddress || !fromToken.amount || !quoteData.gasEstimate || typeof quoteData.expiresAt !== "number") {
7479
7894
  return false;
7480
7895
  }
7481
- return quoteData.expiresAt > Date.now();
7482
- }, [quoteData, quoteRequest]);
7896
+ const targetAmount2 = getQuoteTargetAmount(
7897
+ toToken.estimatedValue,
7898
+ toToken.estimatedAmount,
7899
+ targetToken?.decimals
7900
+ );
7901
+ return targetAmount2 !== "-" && quoteData.expiresAt > Date.now();
7902
+ }, [quoteData, quoteRequest, targetToken?.decimals]);
7483
7903
  useEffect(() => {
7484
7904
  if (quoteData && !isQuoteDataMatched) {
7485
7905
  resetQuote();
7486
7906
  }
7487
7907
  }, [isQuoteDataMatched, quoteData, resetQuote]);
7908
+ const quoteTargetAmount = useMemo(
7909
+ () => quoteData ? getQuoteTargetAmount(
7910
+ quoteData.toToken.estimatedValue,
7911
+ quoteData.toToken.estimatedAmount,
7912
+ targetToken?.decimals
7913
+ ) : "-",
7914
+ [quoteData, targetToken?.decimals]
7915
+ );
7488
7916
  const memoizedOutAmounts = useMemo(() => {
7489
7917
  if (quoteData && !isQuoteLoading && isQuoteDataMatched) {
7490
- return quoteData.toToken.estimatedValue;
7918
+ return quoteTargetAmount;
7491
7919
  }
7492
7920
  return "-";
7493
- }, [quoteData, isQuoteDataMatched, isQuoteLoading]);
7921
+ }, [isQuoteDataMatched, isQuoteLoading, quoteData, quoteTargetAmount]);
7494
7922
  const memoizedConvertRate = useMemo(() => {
7495
7923
  if (quoteData && quoteRequest && !isQuoteLoading && isQuoteDataMatched) {
7496
- return calculateQuoteRate(
7497
- quoteRequest.amount,
7498
- quoteData.toToken.estimatedValue
7499
- );
7924
+ return calculateQuoteRate(quoteRequest.amount, quoteTargetAmount);
7500
7925
  }
7501
7926
  return "-";
7502
- }, [isQuoteDataMatched, isQuoteLoading, quoteData, quoteRequest]);
7927
+ }, [
7928
+ isQuoteDataMatched,
7929
+ isQuoteLoading,
7930
+ quoteData,
7931
+ quoteRequest,
7932
+ quoteTargetAmount
7933
+ ]);
7503
7934
  const memoizedMinimumReceived = useMemo(() => {
7504
7935
  if (!quoteData || isQuoteLoading || !isQuoteDataMatched) {
7505
7936
  return "0";
@@ -7509,10 +7940,10 @@ var useConvertFormScript = (options2) => {
7509
7940
  return "0";
7510
7941
  }
7511
7942
  return calculateMinimumReceived(
7512
- quoteData.toToken.estimatedValue,
7943
+ quoteTargetAmount,
7513
7944
  effectiveSlippage.toString()
7514
7945
  );
7515
- }, [quoteData, isQuoteDataMatched, isQuoteLoading]);
7946
+ }, [quoteData, isQuoteDataMatched, isQuoteLoading, quoteTargetAmount]);
7516
7947
  useEffect(() => {
7517
7948
  if (!quoteData || !quoteRequest || !isQuoteDataMatched) {
7518
7949
  return;
@@ -7543,7 +7974,19 @@ var useConvertFormScript = (options2) => {
7543
7974
  input: Number(quantity),
7544
7975
  token: sourceToken?.token
7545
7976
  });
7546
- const disabled = !quantity || Number(quantity) === 0;
7977
+ const isCheckingConvertStatus = canQueryConvertHistory && !convertHistory && !convertHistoryError;
7978
+ const hasConvertStatusError = canQueryConvertHistory && !convertHistory && Boolean(convertHistoryError);
7979
+ const disabled = !quantity || Number(quantity) === 0 || isCheckingConvertStatus || hasConvertStatusError || activeStatus === "pending";
7980
+ const targetAmount = effectiveTrackedRequest ? getConvertTargetAmount(effectiveTrackedRequest.targetAmount) : "-";
7981
+ const activeConvertRequest = effectiveTrackedRequest ? {
7982
+ status: activeStatus,
7983
+ sourceToken: effectiveTrackedRequest.sourceToken,
7984
+ sourceAmount: effectiveTrackedRequest.sourceAmount,
7985
+ targetToken: effectiveTrackedRequest.targetToken,
7986
+ targetAmount,
7987
+ receivedAmount: trackedRecord ? getConvertReceivedAmount(trackedRecord) : targetAmount,
7988
+ isDelayed: isConvertDelayed
7989
+ } : void 0;
7547
7990
  const { hasPositions, onSettlePnl } = useSettlePnl();
7548
7991
  return {
7549
7992
  walletName,
@@ -7563,14 +8006,20 @@ var useConvertFormScript = (options2) => {
7563
8006
  hasPositions,
7564
8007
  onSettlePnl,
7565
8008
  networkId,
7566
- slippage,
7567
- onSlippageChange: setSlippage,
8009
+ slippage: normalizedSlippage,
8010
+ onSlippageChange: (value) => setSlippage(normalizeConvertSlippage(value)),
7568
8011
  convertRate: memoizedConvertRate,
7569
8012
  minimumReceived: memoizedMinimumReceived,
7570
8013
  outAmounts: memoizedOutAmounts,
7571
8014
  isQuoteLoading,
7572
8015
  currentLTV: currentLtv,
7573
- nextLTV
8016
+ nextLTV,
8017
+ activeConvertRequest,
8018
+ isRecoveredConvertRequest: Boolean(currentTrackedRequest) && effectiveTrackedConvert?.origin === "recovered" || !currentTrackedRequest && Boolean(latestPendingRequest),
8019
+ isCheckingConvertStatus,
8020
+ hasConvertStatusError,
8021
+ isConvertHistoryRefreshing,
8022
+ onRefreshConvertStatus
7574
8023
  };
7575
8024
  };
7576
8025
  var ConvertDialogId = "ConvertDialogId";
@@ -7579,12 +8028,16 @@ var ConvertFormWidget = (props) => {
7579
8028
  const state = useConvertFormScript(props);
7580
8029
  return /* @__PURE__ */ jsx(ConvertFormUI, { ...state });
7581
8030
  };
7582
- registerSimpleDialog(ConvertDialogId, ConvertFormWidget, {
8031
+ var ConvertFormModalWidget = (props) => {
8032
+ const { visible } = useModal();
8033
+ return visible ? /* @__PURE__ */ jsx(ConvertFormWidget, { ...props }) : null;
8034
+ };
8035
+ registerSimpleDialog(ConvertDialogId, ConvertFormModalWidget, {
7583
8036
  size: "md",
7584
8037
  classNames: { content: "oui-border oui-border-line-6" },
7585
8038
  title: () => i18n.t("transfer.convert")
7586
8039
  });
7587
- registerSimpleSheet(ConvertSheetId, ConvertFormWidget, {
8040
+ registerSimpleSheet(ConvertSheetId, ConvertFormModalWidget, {
7588
8041
  title: () => i18n.t("transfer.convert")
7589
8042
  });
7590
8043
  var DepositStatus = (props) => {
@@ -8480,6 +8933,6 @@ var DepositStatusWidget = (props) => {
8480
8933
  return /* @__PURE__ */ jsx(DepositStatus, { ...state, ...props });
8481
8934
  };
8482
8935
 
8483
- export { ActionButton, AvailableQuantity, BrokerWallet, ChainSelect, ConvertFormUI, ConvertFormWidget, DEPOSIT_ERROR_CODE_MAP, DepositAction, DepositAndWithdraw, DepositAndWithdrawWithDialogId, DepositAndWithdrawWithSheetId, DepositForm, DepositFormWidget, DepositStatus, DepositStatusWidget, ExchangeDivider, Fee, InjectableDepositAndWithdraw, QuantityInput, SwapCoin, TransferDialogId, TransferForm, TransferFormWidget, TransferSheetId, TransferWidget, Web3Wallet, WithdrawForm, WithdrawFormWidget, WithdrawTo, YIELD_BEARING_ASSETS, YieldBearingReminder, checkIsAccountId, feeDecimalsOffset, getDepositKnownErrorMessage, getTransferErrorMessage, getUSDCToken, getYieldBearingAsset, isYieldBearingAsset, sortTokensWithUSDCFirst, useConvertFormScript as useConvertForm, useDepositFormScript, useDepositStatusScript, useTransferFormScript, useWithdrawFormScript };
8936
+ export { ActionButton, AvailableQuantity, BrokerWallet, ChainSelect, ConvertFormUI, ConvertFormWidget, DEPOSIT_ERROR_CODE_MAP, DepositAction, DepositAndWithdraw, DepositAndWithdrawWithDialogId, DepositAndWithdrawWithSheetId, DepositForm, DepositFormWidget, DepositStatus, DepositStatusWidget, ExchangeDivider, Fee, InjectableDepositAndWithdraw, QuantityInput, SwapCoin, TransferDialogId, TransferForm, TransferFormWidget, TransferSheetId, TransferWidget, Web3Wallet, WithdrawForm, WithdrawFormWidget, WithdrawTo, YIELD_BEARING_ASSETS, YieldBearingReminder, checkIsAccountId, feeDecimalsOffset, getDepositKnownErrorMessage, getEffectiveConvertStatus, getTransferErrorMessage, getUSDCToken, getYieldBearingAsset, isYieldBearingAsset, sortTokensWithUSDCFirst, useConvertFormScript as useConvertForm, useDepositFormScript, useDepositStatusScript, useTransferFormScript, useWithdrawFormScript };
8484
8937
  //# sourceMappingURL=index.mjs.map
8485
8938
  //# sourceMappingURL=index.mjs.map