rujira.ui 1.0.11 → 1.0.14

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.
Files changed (61) hide show
  1. package/lib/cjs/assets/tokens/brune.png +0 -0
  2. package/lib/cjs/components/buttons/TxButton.js +10 -6
  3. package/lib/cjs/components/icons/IconDenom.js +3 -0
  4. package/lib/cjs/components/inputs/DecimalInput.js +7 -4
  5. package/lib/cjs/components/inputs/DenomInput.js +2 -1
  6. package/lib/cjs/components/inputs/Numeric.js +6 -7
  7. package/lib/cjs/components/slider/Slider.js +6 -0
  8. package/lib/cjs/helpers/index.js +1 -20
  9. package/lib/cjs/i18n/locales/de/strategies.json +1 -1
  10. package/lib/cjs/i18n/locales/en/strategies.json +1 -1
  11. package/lib/cjs/index.js +1 -0
  12. package/lib/cjs/utils/fiat.js +52 -0
  13. package/lib/cjs/utils/fiat.test.js +96 -0
  14. package/lib/esm/assets/tokens/brune.png +0 -0
  15. package/lib/esm/components/buttons/TxButton.d.ts +6 -0
  16. package/lib/esm/components/buttons/TxButton.d.ts.map +1 -1
  17. package/lib/esm/components/buttons/TxButton.js +10 -6
  18. package/lib/esm/components/icons/IconDenom.d.ts.map +1 -1
  19. package/lib/esm/components/icons/IconDenom.js +4 -0
  20. package/lib/esm/components/inputs/DecimalInput.d.ts +1 -0
  21. package/lib/esm/components/inputs/DecimalInput.d.ts.map +1 -1
  22. package/lib/esm/components/inputs/DecimalInput.js +7 -4
  23. package/lib/esm/components/inputs/DenomInput.d.ts.map +1 -1
  24. package/lib/esm/components/inputs/DenomInput.js +2 -1
  25. package/lib/esm/components/inputs/Numeric.d.ts +4 -1
  26. package/lib/esm/components/inputs/Numeric.d.ts.map +1 -1
  27. package/lib/esm/components/inputs/Numeric.js +6 -7
  28. package/lib/esm/components/slider/Slider.d.ts.map +1 -1
  29. package/lib/esm/components/slider/Slider.js +6 -0
  30. package/lib/esm/helpers/index.d.ts +0 -14
  31. package/lib/esm/helpers/index.d.ts.map +1 -1
  32. package/lib/esm/helpers/index.js +0 -17
  33. package/lib/esm/i18n/locales/de/strategies.json +1 -1
  34. package/lib/esm/i18n/locales/en/strategies.json +1 -1
  35. package/lib/esm/index.d.ts +1 -0
  36. package/lib/esm/index.d.ts.map +1 -1
  37. package/lib/esm/index.js +1 -0
  38. package/lib/esm/utils/fiat.d.ts +30 -0
  39. package/lib/esm/utils/fiat.d.ts.map +1 -0
  40. package/lib/esm/utils/fiat.js +46 -0
  41. package/lib/esm/utils/fiat.test.d.ts +2 -0
  42. package/lib/esm/utils/fiat.test.d.ts.map +1 -0
  43. package/lib/esm/utils/fiat.test.js +94 -0
  44. package/package.json +8 -9
  45. package/src/assets/tokens/brune.png +0 -0
  46. package/src/components/buttons/TxButton.tsx +18 -5
  47. package/src/components/icons/IconDenom.tsx +7 -1
  48. package/src/components/inputs/DecimalInput.tsx +8 -4
  49. package/src/components/inputs/DenomInput.tsx +2 -1
  50. package/src/components/inputs/Numeric.tsx +34 -24
  51. package/src/components/slider/Slider.tsx +6 -0
  52. package/src/helpers/index.ts +0 -32
  53. package/src/i18n/locales/de/strategies.json +1 -1
  54. package/src/i18n/locales/en/strategies.json +1 -1
  55. package/src/index.ts +2 -0
  56. package/src/scss/base/_filters.scss +10 -1
  57. package/src/scss/components/_button.scss +30 -0
  58. package/src/scss/components/_numeric-input.scss +10 -2
  59. package/src/scss/components/_slider.scss +10 -0
  60. package/src/{helpers/index.test.ts → utils/fiat.test.ts} +72 -3
  61. package/src/utils/fiat.ts +71 -0
Binary file
@@ -36,7 +36,7 @@ const separateClasses = (className) => {
36
36
  const TWO_STEP_ALLOWANCE_CHAINS = new Set([rujira_js_1.BSC, rujira_js_1.AVAX]);
37
37
  const TxButton = (props) => {
38
38
  const { t } = (0, i18n_1.useTranslation)("common");
39
- const { accountProvider, msg, onSuccess, onError, SimulationComponent = DefaultSimulationComponent, children, toastOpts = {}, className, hideSimulation, onClick, ...rest } = props;
39
+ const { accountProvider, msg, onSuccess, onError, onSimulation, SimulationComponent = DefaultSimulationComponent, children, toastOpts = {}, className, hideSimulation, animateSimulation = true, onClick, ...rest } = props;
40
40
  const [simulation, setSimulation] = (0, react_1.useState)();
41
41
  const [simulationError, setSimulationError] = (0, react_1.useState)();
42
42
  const [isSimulating, setIsSimulating] = (0, react_1.useState)(false);
@@ -49,9 +49,11 @@ const TxButton = (props) => {
49
49
  const doSimulate = (msg) => {
50
50
  setSimulationError(undefined);
51
51
  setSimulation(undefined);
52
+ onSimulation?.({ simulation: undefined, status: "none" });
52
53
  if (msg) {
53
54
  const seq = ++simulationSeqRef.current;
54
55
  setIsSimulating(true);
56
+ onSimulation?.({ simulation: undefined, status: "running" });
55
57
  const signer = accountProvider.signer(msg.account.address);
56
58
  signer
57
59
  .simulate(msg)
@@ -61,6 +63,7 @@ const TxButton = (props) => {
61
63
  return;
62
64
  setIsSimulating(false);
63
65
  setSimulation(sim);
66
+ onSimulation?.({ simulation: sim, status: "completed" });
64
67
  })
65
68
  .catch((err) => {
66
69
  //ignore stale simulation errors
@@ -70,6 +73,7 @@ const TxButton = (props) => {
70
73
  console.error(err);
71
74
  setSimulationError(err);
72
75
  onError && onError(new rujira_js_1.TxError(msg, err, "simulation"));
76
+ onSimulation?.({ simulation: undefined, status: "failed" });
73
77
  });
74
78
  }
75
79
  };
@@ -181,7 +185,7 @@ const TxButton = (props) => {
181
185
  ? t("waiting")
182
186
  : isRejected
183
187
  ? t("rejected")
184
- : isSimulating
188
+ : isSimulating && animateSimulation
185
189
  ? t("simulating")
186
190
  : rest.label}`,
187
191
  }),
@@ -190,13 +194,13 @@ const TxButton = (props) => {
190
194
  "button--grey": disabled &&
191
195
  !isSimulating &&
192
196
  !stylingClasses.includes("transparent"),
193
- "button--waiting": (isSigning || isRejected || isSuccess || isSimulating) &&
197
+ "button--waiting": (isSigning || isRejected || isSuccess || (isSimulating && animateSimulation)) &&
194
198
  !className?.includes("transparent"),
195
199
  "button--success": isSuccess,
196
200
  "button--rejected": isRejected,
197
- "button--simulating": isSimulating,
201
+ "button--simulating": isSimulating && animateSimulation,
198
202
  }), style: {
199
- cursor: isSigning || isSuccess || isRejected || isSimulating
203
+ cursor: isSigning || isSuccess || isRejected || (isSimulating && animateSimulation)
200
204
  ? "default"
201
205
  : undefined,
202
206
  ...rest.style,
@@ -205,7 +209,7 @@ const TxButton = (props) => {
205
209
  : undefined, children: [(isSuccess ||
206
210
  isRejected ||
207
211
  isSigning ||
208
- isSimulating ||
212
+ (isSimulating && animateSimulation) ||
209
213
  !!simulationError) &&
210
214
  !className?.includes("transparent") && ((0, jsx_runtime_1.jsx)("img", { className: "w-4 h-4 filter-white big", src: isSimulating
211
215
  ? txsimulate_gif_1.default
@@ -55,6 +55,7 @@ const bch_png_1 = require("../../assets/tokens/bch.png");
55
55
  const bfit_png_1 = require("../../assets/tokens/bfit.png");
56
56
  const bnb_png_1 = require("../../assets/tokens/bnb.png");
57
57
  const btc_png_1 = require("../../assets/tokens/btc.png");
58
+ const brune_png_1 = require("../../assets/tokens/brune.png");
58
59
  const busd_png_1 = require("../../assets/tokens/busd.png");
59
60
  const cbbtc_png_1 = require("../../assets/tokens/cbbtc.png");
60
61
  const cheq_png_1 = require("../../assets/tokens/cheq.png");
@@ -243,6 +244,7 @@ const getSRC = (src, ghost) => {
243
244
  bch: bch_png_1.default,
244
245
  bfit: bfit_png_1.default,
245
246
  bnb: bnb_png_1.default,
247
+ brune: brune_png_1.default,
246
248
  btc: btc_png_1.default,
247
249
  btcb: btc_png_1.default,
248
250
  busd: busd_png_1.default,
@@ -378,6 +380,7 @@ const getSRC = (src, ghost) => {
378
380
  xdefi: xdefi_png_1.default,
379
381
  xrp: xrp_png_1.default,
380
382
  xrune: xrune_png_1.default,
383
+ xusk: usk_png_1.default,
381
384
  yfi: yfi_png_1.default,
382
385
  yfuzn: fuzn_png_1.default,
383
386
  yieldeth: yieldeth_png_1.default,
@@ -29,19 +29,22 @@ const format = (v, decimals) => {
29
29
  const lead = str.slice(0, -decimals);
30
30
  return (0, react_number_format_1.numericFormatter)(`${lead.length ? lead : "0"}.${str.slice(-decimals)}`, { decimalScale: decimals });
31
31
  };
32
- const DecimalInput = ({ amount, decimals = 8, onAmountchange, disabled, ...rest }) => {
32
+ const DecimalInput = ({ amount, decimals = 8, onAmountchange, disabled, allowNegative = false, ...rest }) => {
33
33
  const formatted = format(amount, decimals);
34
34
  const trimmed = formatted.replace(trim, "");
35
35
  const [previousValue, setPreviousValue] = (0, react_1.useState)(trimmed);
36
36
  const [previousAmount, setPreviousAmount] = (0, react_1.useState)(amount);
37
- const onValueChange = (0, react_1.useCallback)((values) => {
37
+ const onValueChange = (0, react_1.useCallback)((values, sourceInfo) => {
38
38
  // `values.value` is always a dot separated decimal
39
39
  const intValue = parseFixed(values.value, decimals);
40
40
  setPreviousValue(values.value);
41
41
  setPreviousAmount(intValue);
42
- onAmountchange(intValue);
42
+ // Only propagate changes triggered by the user, not programmatic value prop updates
43
+ if (sourceInfo.source === "event") {
44
+ onAmountchange(intValue);
45
+ }
43
46
  }, [decimals]);
44
- return ((0, jsx_runtime_1.jsx)(react_number_format_1.NumericFormat, { allowNegative: false, decimalScale: decimals, decimalSeparator: decimal, thousandSeparator: group, disabled: disabled, placeholder: "0", maxLength: 35, value:
47
+ return ((0, jsx_runtime_1.jsx)(react_number_format_1.NumericFormat, { allowNegative: allowNegative, decimalScale: decimals, decimalSeparator: decimal, thousandSeparator: group, disabled: disabled, placeholder: "0", maxLength: 35, value:
45
48
  // Unique case if decimal digits are deleted up to the separator, causing
46
49
  // we want to retain the decimal separator in the input
47
50
  // Secondly don't change the input value to `trimmed` if the value hassn't changed
@@ -5,6 +5,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const clsx_1 = require("clsx");
6
6
  const react_1 = require("react");
7
7
  const helpers_1 = require("../../helpers");
8
+ const fiat_1 = require("../../utils/fiat");
8
9
  const i18n_1 = require("../../i18n");
9
10
  const Button_1 = require("../buttons/Button");
10
11
  const IconDenom_1 = require("../icons/IconDenom");
@@ -15,7 +16,7 @@ const DenomInput = ({ symbol, amount, decimals, onChangeAmount, disabled, readOn
15
16
  const { t } = (0, i18n_1.useTranslation)("common");
16
17
  const input = (0, react_1.useRef)(null);
17
18
  const id = (0, react_1.useMemo)(() => (0, helpers_1.uuidv4)(), []);
18
- const fiatAmount = (0, react_1.useMemo)(() => (0, helpers_1.toFiatAmount)(fiat?.price, amount), [fiat?.price, amount]);
19
+ const fiatAmount = (0, react_1.useMemo)(() => (0, fiat_1.toFiatAmount)(fiat?.price, amount), [fiat?.price, amount]);
19
20
  return ((0, jsx_runtime_1.jsxs)("div", { className: (0, clsx_1.default)({
20
21
  "denom-select condensed": true,
21
22
  "denom-select--full": full,
@@ -5,14 +5,15 @@ const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const clsx_1 = require("clsx");
6
6
  const react_1 = require("react");
7
7
  const helpers_1 = require("../../helpers");
8
+ const fiat_1 = require("../../utils/fiat");
8
9
  const i18n_1 = require("../../i18n");
9
10
  const DecimalInput_1 = require("./DecimalInput");
10
11
  const Fiat_1 = require("../numbers/Fiat");
11
- const Numeric = ({ label, amount, decimals = 8, onChangeAmount, onBlur, onFocus, focused, disabled, full, className, children, icon, fiat, }) => {
12
+ const Numeric = ({ label, amount, decimals = 8, onChangeAmount, onBlur, onFocus, focused, disabled, full, className, children, icon, fiat, allowNegative, prefix, suffix, }) => {
12
13
  const { t } = (0, i18n_1.useTranslation)("common");
13
14
  const input = (0, react_1.useRef)(null);
14
15
  const id = (0, react_1.useMemo)(() => (0, helpers_1.uuidv4)(), []);
15
- const fiatAmount = (0, react_1.useMemo)(() => (0, helpers_1.toFiatAmount)(fiat?.price, amount), [fiat?.price, amount]);
16
+ const fiatAmount = (0, react_1.useMemo)(() => (0, fiat_1.toFiatAmount)(fiat?.price, amount), [fiat?.price, amount]);
16
17
  (0, react_1.useEffect)(() => {
17
18
  if (focused && !disabled) {
18
19
  input.current?.focus();
@@ -26,11 +27,9 @@ const Numeric = ({ label, amount, decimals = 8, onChangeAmount, onBlur, onFocus,
26
27
  }), onClick: () => {
27
28
  if (!disabled)
28
29
  input.current?.focus();
29
- }, children: [label && (0, jsx_runtime_1.jsx)("label", { children: t(label) }), children, (0, jsx_runtime_1.jsxs)("div", { className: (0, clsx_1.default)("flex dir-c grow ai-e", {
30
+ }, children: [label && (0, jsx_runtime_1.jsx)("label", { children: t(label) }), children, (0, jsx_runtime_1.jsxs)("div", { className: (0, clsx_1.default)("numeric-input__content", {
30
31
  "mt-1 mb-1": fiatAmount != undefined && fiatAmount > 0n,
31
- "py-2": fiatAmount === 0n
32
- }), children: [(0, jsx_runtime_1.jsx)(DecimalInput_1.DecimalInput, { getInputRef: input, decimals: decimals, disabled: disabled, amount: amount, onAmountchange: onChangeAmount, onBlur: onBlur, onFocus: onFocus, id: id }), fiatAmount != undefined &&
33
- fiatAmount > 0n &&
34
- ((0, jsx_runtime_1.jsx)("div", { className: "flex fs-13 color-grey", children: (0, jsx_runtime_1.jsx)(Fiat_1.Fiat, { amount: fiatAmount, symbol: fiat.symbol, decimals: 8, className: "numeric-input__value color-grey fs-12" }) }))] }), icon] }));
32
+ "py-2": fiatAmount === 0n,
33
+ }), children: [(0, jsx_runtime_1.jsx)(DecimalInput_1.DecimalInput, { getInputRef: input, decimals: decimals, disabled: disabled, amount: amount, onAmountchange: onChangeAmount, onBlur: onBlur, onFocus: onFocus, id: id, allowNegative: allowNegative, prefix: prefix, suffix: suffix }), fiatAmount != undefined && fiatAmount > 0n && ((0, jsx_runtime_1.jsx)("div", { className: "flex fs-13 color-grey", children: (0, jsx_runtime_1.jsx)(Fiat_1.Fiat, { amount: fiatAmount, symbol: fiat.symbol, decimals: 8, className: "numeric-input__value color-grey fs-12" }) }))] }), icon] }));
35
34
  };
36
35
  exports.Numeric = Numeric;
@@ -3,6 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Slider = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_slider_1 = require("react-slider");
6
+ const _consoleError = console.error;
7
+ console.error = (...args) => {
8
+ if (typeof args[0] === "string" && args[0].includes('"key" prop is being spread'))
9
+ return;
10
+ _consoleError(...args);
11
+ };
6
12
  const Slider = (props) => {
7
13
  return ((0, jsx_runtime_1.jsx)(react_slider_1.default, { ...props, className: "slider grow", trackClassName: "slider__track", thumbClassName: "slider__thumb" }));
8
14
  };
@@ -1,25 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.bigIntToDecimalString = exports.floatToSubscript = exports.compress = exports.classApr = exports.formatApr = exports.nFormatterInt = exports.nFormatter = exports.uuidv4 = exports.isEmpty = exports.whatDecimalSeparator = exports.getLang = exports.toFiatDisplay = exports.toFiatAmount = void 0;
4
- const FIAT_SCALE = 10n ** 12n;
5
- const toFiatAmount = (price, amount) => {
6
- return price != null ? (price * amount) / FIAT_SCALE : undefined;
7
- };
8
- exports.toFiatAmount = toFiatAmount;
9
- /**
10
- * Formats a fiat bigint amount as a display string.
11
- * @param amount - The bigint value to format.
12
- * @param options
13
- * @param options.decimals - Decimal places in the bigint representation. @default 8
14
- * @param options.rounding - Visible decimal digits in the output. @default 2
15
- */
16
- const toFiatDisplay = (amount, options) => {
17
- const { decimals: useDecimals = 8, rounding: useRounding = 2 } = options ?? {};
18
- const dec = amount % BigInt(10 ** useDecimals);
19
- const int = BigInt(Math.floor(Number(amount - dec) / 10 ** useDecimals));
20
- return `${(int || "0").toLocaleString()}${(0, exports.whatDecimalSeparator)()}${dec.toString().padStart(useDecimals, "0").substring(0, useRounding)}`;
21
- };
22
- exports.toFiatDisplay = toFiatDisplay;
3
+ exports.bigIntToDecimalString = exports.floatToSubscript = exports.compress = exports.classApr = exports.formatApr = exports.nFormatterInt = exports.nFormatter = exports.uuidv4 = exports.isEmpty = exports.whatDecimalSeparator = exports.getLang = void 0;
23
4
  const getLang = () => {
24
5
  if (navigator.languages != undefined)
25
6
  return navigator.languages[0];
@@ -41,7 +41,7 @@
41
41
  "totalLiquidity": "Gesamtliquidität",
42
42
  "lockedLiquidity": "Gesperrte Liquidität",
43
43
  "sevenDayRoi": "7-Tage-ROI",
44
- "sevenDayApr": "7-Tage-APR",
44
+ "sevenDayApr": "APR (7-Tage)",
45
45
  "sharpeRatio": "Sharpe-Ratio",
46
46
  "risk": "Risiko",
47
47
  "interestRateCurve": "Zinskurve",
@@ -41,7 +41,7 @@
41
41
  "totalLiquidity": "Total Liquidity",
42
42
  "lockedLiquidity": "Locked Liquidity",
43
43
  "sevenDayRoi": "7-day ROI",
44
- "sevenDayApr": "7-day APR",
44
+ "sevenDayApr": "APR (7-day)",
45
45
  "sharpeRatio": "Sharpe Ratio",
46
46
  "risk": "Risk",
47
47
  "interestRateCurve": "Interest Rate Curve",
package/lib/cjs/index.js CHANGED
@@ -69,5 +69,6 @@ __exportStar(require("./hooks/useWindowSize"), exports);
69
69
  __exportStar(require("./context/GlobalModal"), exports);
70
70
  __exportStar(require("./i18n"), exports);
71
71
  __exportStar(require("./helpers"), exports);
72
+ __exportStar(require("./utils/fiat"), exports);
72
73
  __exportStar(require("./wallets"), exports);
73
74
  exports.wallets = require("./wallets");
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fiatize = exports.toFiatDisplay = exports.toFiatAmount = void 0;
4
+ const helpers_1 = require("../helpers");
5
+ const FIAT_SCALE = 10n ** 12n;
6
+ const toFiatAmount = (price, amount) => {
7
+ return price != null ? (price * amount) / FIAT_SCALE : undefined;
8
+ };
9
+ exports.toFiatAmount = toFiatAmount;
10
+ /**
11
+ * Formats a fiat bigint amount as a display string.
12
+ * @param amount - The bigint value to format.
13
+ * @param options
14
+ * @param options.decimals - Decimal places in the bigint representation. @default 8
15
+ * @param options.rounding - Visible decimal digits in the output. @default 2
16
+ */
17
+ const toFiatDisplay = (amount, options) => {
18
+ const { decimals: useDecimals = 8, rounding: useRounding = 2 } = options ?? {};
19
+ const dec = amount % BigInt(10 ** useDecimals);
20
+ const int = BigInt(Math.floor(Number(amount - dec) / 10 ** useDecimals));
21
+ return `${(int || "0").toLocaleString()}${(0, helpers_1.whatDecimalSeparator)()}${dec.toString().padStart(useDecimals, "0").substring(0, useRounding)}`;
22
+ };
23
+ exports.toFiatDisplay = toFiatDisplay;
24
+ /**
25
+ * A util that will combine text and asset values into a single display string. Use this
26
+ * when you want to add the fiat value of 1 or more assets to a component label, or other text.
27
+ * It will internally sum up the total values for you, and return a string with a fiat value
28
+ * appended, provided all price values are valid and non-zero. If any price value is
29
+ * invalid, then the original label will be returned with no fiat value appended as we cannot
30
+ * compute an accurate fiat value.
31
+ * @param label The label text.
32
+ * @param assets The assets to sum up, format and append to the supplied label.
33
+ * @param symbol The fiat symbol to use.
34
+ */
35
+ const fiatize = (label, assets, symbol = "$") => {
36
+ if (!assets || assets.length === 0)
37
+ return label;
38
+ let fiatTotal = 0n;
39
+ let incomplete = false;
40
+ for (const asset of assets) {
41
+ // 0n is currently considered an invalid price for the purposes of this function.
42
+ if (!asset.price) {
43
+ incomplete = true;
44
+ break;
45
+ }
46
+ fiatTotal = fiatTotal + ((0, exports.toFiatAmount)(asset.price, asset.amount) ?? 0n);
47
+ }
48
+ if (incomplete || fiatTotal === 0n)
49
+ return label;
50
+ return `${label} (${symbol}${(0, exports.toFiatDisplay)(fiatTotal)})`;
51
+ };
52
+ exports.fiatize = fiatize;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const fiat_1 = require("./fiat");
5
+ // $1.00 in 1e12 price scale; 1 unit in 8-decimal amount scale → fiat = $1.00
6
+ const PRICE_1 = 1000000000000n;
7
+ const AMOUNT_1 = 100000000n;
8
+ (0, vitest_1.test)("fiatize returns label when assets array is empty", () => {
9
+ (0, vitest_1.expect)((0, fiat_1.fiatize)("Buy", [])).toEqual("Buy");
10
+ });
11
+ (0, vitest_1.test)("fiatize returns label when all prices are null", () => {
12
+ (0, vitest_1.expect)((0, fiat_1.fiatize)("Buy", [{ price: null, amount: AMOUNT_1 }])).toEqual("Buy");
13
+ });
14
+ (0, vitest_1.test)("fiatize returns label when all prices are undefined", () => {
15
+ (0, vitest_1.expect)((0, fiat_1.fiatize)("Buy", [{ price: undefined, amount: AMOUNT_1 }])).toEqual("Buy");
16
+ });
17
+ (0, vitest_1.test)("fiatize returns label when amount is zero", () => {
18
+ (0, vitest_1.expect)((0, fiat_1.fiatize)("Buy", [{ price: PRICE_1, amount: 0n }])).toEqual("Buy");
19
+ });
20
+ (0, vitest_1.test)("fiatize returns label when price is zero", () => {
21
+ (0, vitest_1.expect)((0, fiat_1.fiatize)("Buy", [{ price: 0n, amount: AMOUNT_1 }])).toEqual("Buy");
22
+ });
23
+ (0, vitest_1.test)("fiatize appends formatted fiat for a single positive asset", () => {
24
+ (0, vitest_1.expect)((0, fiat_1.fiatize)("Buy", [{ price: PRICE_1, amount: AMOUNT_1 }])).toEqual("Buy ($1.00)");
25
+ });
26
+ (0, vitest_1.test)("fiatize sums multiple assets", () => {
27
+ (0, vitest_1.expect)((0, fiat_1.fiatize)("Buy", [
28
+ { price: PRICE_1, amount: AMOUNT_1 },
29
+ { price: PRICE_1, amount: AMOUNT_1 },
30
+ ])).toEqual("Buy ($2.00)");
31
+ });
32
+ (0, vitest_1.test)("fiatize returns label when 1 or more prices are unavailable - null price 1st", () => {
33
+ (0, vitest_1.expect)((0, fiat_1.fiatize)("Buy", [
34
+ { price: null, amount: AMOUNT_1 },
35
+ { price: PRICE_1, amount: AMOUNT_1 },
36
+ ])).toEqual("Buy");
37
+ });
38
+ (0, vitest_1.test)("fiatize returns label when 1 or more prices are unavailable - null price 2nd", () => {
39
+ (0, vitest_1.expect)((0, fiat_1.fiatize)("Buy", [
40
+ { price: PRICE_1, amount: AMOUNT_1 },
41
+ { price: null, amount: AMOUNT_1 },
42
+ ])).toEqual("Buy");
43
+ });
44
+ (0, vitest_1.test)("fiatize uses custom symbol", () => {
45
+ (0, vitest_1.expect)((0, fiat_1.fiatize)("Buy", [{ price: PRICE_1, amount: AMOUNT_1 }], "€")).toEqual("Buy (€1.00)");
46
+ });
47
+ (0, vitest_1.test)("fiatize uses default $ symbol when none provided", () => {
48
+ (0, vitest_1.expect)((0, fiat_1.fiatize)("Buy", [{ price: PRICE_1, amount: AMOUNT_1 }])).toContain("$");
49
+ });
50
+ // toFiatAmount
51
+ const TEST_PRICE = 75248900000000000n; //$75,248.90 in 1e12
52
+ (0, vitest_1.test)("toFiatAmount returns undefined for undefined price", () => {
53
+ (0, vitest_1.expect)((0, fiat_1.toFiatAmount)(undefined, 100n)).toEqual(undefined);
54
+ });
55
+ (0, vitest_1.test)("toFiatAmount returns undefined for null price", () => {
56
+ (0, vitest_1.expect)((0, fiat_1.toFiatAmount)(null, 100n)).toEqual(undefined);
57
+ });
58
+ (0, vitest_1.test)("toFiatAmount returns 0n for 0n price and non-zero amount", () => {
59
+ (0, vitest_1.expect)((0, fiat_1.toFiatAmount)(0n, 100000000n)).toEqual(0n);
60
+ });
61
+ (0, vitest_1.test)("toFiatAmount returns 0n for non-zero price with 0n amount", () => {
62
+ (0, vitest_1.expect)((0, fiat_1.toFiatAmount)(TEST_PRICE, 0n)).toEqual(0n);
63
+ });
64
+ (0, vitest_1.test)("toFiatAmount returns correct amount when price and amount are supplied", () => {
65
+ (0, vitest_1.expect)((0, fiat_1.toFiatAmount)(TEST_PRICE, 100000000n)).toEqual(7524890000000n);
66
+ });
67
+ // toFiatDisplay
68
+ const TEST_FIAT_AMOUNT = 123456789n;
69
+ (0, vitest_1.test)("toFiatDisplay returns correctly when no options are supplied", () => {
70
+ (0, vitest_1.expect)((0, fiat_1.toFiatDisplay)(TEST_FIAT_AMOUNT)).toEqual("1.23");
71
+ });
72
+ (0, vitest_1.test)("toFiatDisplay returns correctly with empty options", () => {
73
+ (0, vitest_1.expect)((0, fiat_1.toFiatDisplay)(TEST_FIAT_AMOUNT, {})).toEqual("1.23");
74
+ });
75
+ (0, vitest_1.test)("toFiatDisplay returns correctly with rounding only option", () => {
76
+ (0, vitest_1.expect)((0, fiat_1.toFiatDisplay)(TEST_FIAT_AMOUNT, { rounding: 3 })).toEqual("1.234");
77
+ });
78
+ (0, vitest_1.test)("toFiatDisplay returns correctly with decimals only option", () => {
79
+ (0, vitest_1.expect)((0, fiat_1.toFiatDisplay)(TEST_FIAT_AMOUNT, { decimals: 7 })).toEqual("12.34");
80
+ });
81
+ (0, vitest_1.test)("toFiatDisplay returns correctly with decimals and rounding option", () => {
82
+ (0, vitest_1.expect)((0, fiat_1.toFiatDisplay)(TEST_FIAT_AMOUNT, { decimals: 7, rounding: 4 })).toEqual("12.3456");
83
+ });
84
+ (0, vitest_1.test)("toFiatDisplay returns correctly for zero amount", () => {
85
+ (0, vitest_1.expect)((0, fiat_1.toFiatDisplay)(0n)).toEqual("0.00");
86
+ });
87
+ (0, vitest_1.test)("toFiatDisplay clamps rounding to available decimal digits", () => {
88
+ // rounding:10 exceeds decimals:8, substring returns all 8 available digits
89
+ (0, vitest_1.expect)((0, fiat_1.toFiatDisplay)(TEST_FIAT_AMOUNT, { rounding: 10 })).toEqual("1.23456789");
90
+ });
91
+ // integration
92
+ (0, vitest_1.test)("toFiatDisplay correctly formats the output of toFiatAmount", () => {
93
+ // $1.00 price in 1e12 scale, 1 unit in 8 decimal scale → fiat = 100_000_000n → "$1.00"
94
+ const fiat = (0, fiat_1.toFiatAmount)(1000000000000n, 100000000n);
95
+ (0, vitest_1.expect)((0, fiat_1.toFiatDisplay)(fiat)).toEqual("1.00");
96
+ });
Binary file
@@ -3,6 +3,10 @@ import { Renderable } from "react-hot-toast";
3
3
  import { Msg, Simulation, TxResult } from "rujira.js";
4
4
  import { AccountProvider } from "../../wallets";
5
5
  import { ButtonProps } from "./Button";
6
+ export type SimulationState = {
7
+ simulation: Simulation | undefined;
8
+ status: "none" | "running" | "completed" | "failed";
9
+ };
6
10
  export type TxButtonProps<T extends ElementType = "button"> = ButtonProps<T> & {
7
11
  accountProvider: AccountProvider;
8
12
  msg: Msg | null;
@@ -12,7 +16,9 @@ export type TxButtonProps<T extends ElementType = "button"> = ButtonProps<T> & {
12
16
  }>;
13
17
  onError?: (err: any) => void;
14
18
  onSuccess?: (res: TxResult) => void;
19
+ onSimulation?: (simulationState: SimulationState) => void;
15
20
  hideSimulation?: boolean;
21
+ animateSimulation?: boolean;
16
22
  toastOpts?: {
17
23
  loading?: Renderable;
18
24
  success?: Renderable | ((res: TxResult) => Renderable);
@@ -1 +1 @@
1
- {"version":3,"file":"TxButton.d.ts","sourceRoot":"","sources":["../../../../src/components/buttons/TxButton.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,EAAE,EAMH,MAAM,OAAO,CAAC;AACf,OAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,EAIL,GAAG,EAIH,UAAU,EAEV,QAAQ,EAGT,MAAM,WAAW,CAAC;AAQnB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAIhD,OAAO,EAAU,WAAW,EAAE,MAAM,UAAU,CAAC;AAgB/C,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,WAAW,GAAG,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IAC7E,eAAe,EAAE,eAAe,CAAC;IACjC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IAChB,mBAAmB,CAAC,EAAE,WAAW,CAAC;QAChC,UAAU,CAAC,EAAE,UAAU,CAAC;QACxB,KAAK,CAAC,EAAE,KAAK,CAAC;KACf,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;IAC7B,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE;QACV,OAAO,CAAC,EAAE,UAAU,CAAC;QACrB,OAAO,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,EAAE,QAAQ,KAAK,UAAU,CAAC,CAAC;QACvD,KAAK,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC;KACjD,CAAC;CACH,CAAC;AAOF,eAAO,MAAM,QAAQ,EAAE,EAAE,CAAC,aAAa,CA6QtC,CAAC;AAEF,eAAO,MAAM,WAAW,+CAOvB,CAAC;AACF,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB"}
1
+ {"version":3,"file":"TxButton.d.ts","sourceRoot":"","sources":["../../../../src/components/buttons/TxButton.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,EAAE,EAMH,MAAM,OAAO,CAAC;AACf,OAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,EAIL,GAAG,EAIH,UAAU,EAEV,QAAQ,EAGT,MAAM,WAAW,CAAC;AAQnB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAIhD,OAAO,EAAU,WAAW,EAAE,MAAM,UAAU,CAAC;AAgB/C,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,EAAE,UAAU,GAAG,SAAS,CAAC;IACnC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;CACrD,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,WAAW,GAAG,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IAC7E,eAAe,EAAE,eAAe,CAAC;IACjC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IAChB,mBAAmB,CAAC,EAAE,WAAW,CAAC;QAChC,UAAU,CAAC,EAAE,UAAU,CAAC;QACxB,KAAK,CAAC,EAAE,KAAK,CAAC;KACf,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;IAC7B,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,CAAC,eAAe,EAAE,eAAe,KAAK,IAAI,CAAC;IAC1D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE;QACV,OAAO,CAAC,EAAE,UAAU,CAAC;QACrB,OAAO,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,EAAE,QAAQ,KAAK,UAAU,CAAC,CAAC;QACvD,KAAK,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC;KACjD,CAAC;CACH,CAAC;AAOF,eAAO,MAAM,QAAQ,EAAE,EAAE,CAAC,aAAa,CAmRtC,CAAC;AAEF,eAAO,MAAM,WAAW,+CAOvB,CAAC;AACF,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB"}
@@ -33,7 +33,7 @@ const separateClasses = (className) => {
33
33
  const TWO_STEP_ALLOWANCE_CHAINS = new Set([BSC, AVAX]);
34
34
  export const TxButton = (props) => {
35
35
  const { t } = useTranslation("common");
36
- const { accountProvider, msg, onSuccess, onError, SimulationComponent = DefaultSimulationComponent, children, toastOpts = {}, className, hideSimulation, onClick, ...rest } = props;
36
+ const { accountProvider, msg, onSuccess, onError, onSimulation, SimulationComponent = DefaultSimulationComponent, children, toastOpts = {}, className, hideSimulation, animateSimulation = true, onClick, ...rest } = props;
37
37
  const [simulation, setSimulation] = useState();
38
38
  const [simulationError, setSimulationError] = useState();
39
39
  const [isSimulating, setIsSimulating] = useState(false);
@@ -46,9 +46,11 @@ export const TxButton = (props) => {
46
46
  const doSimulate = (msg) => {
47
47
  setSimulationError(undefined);
48
48
  setSimulation(undefined);
49
+ onSimulation?.({ simulation: undefined, status: "none" });
49
50
  if (msg) {
50
51
  const seq = ++simulationSeqRef.current;
51
52
  setIsSimulating(true);
53
+ onSimulation?.({ simulation: undefined, status: "running" });
52
54
  const signer = accountProvider.signer(msg.account.address);
53
55
  signer
54
56
  .simulate(msg)
@@ -58,6 +60,7 @@ export const TxButton = (props) => {
58
60
  return;
59
61
  setIsSimulating(false);
60
62
  setSimulation(sim);
63
+ onSimulation?.({ simulation: sim, status: "completed" });
61
64
  })
62
65
  .catch((err) => {
63
66
  //ignore stale simulation errors
@@ -67,6 +70,7 @@ export const TxButton = (props) => {
67
70
  console.error(err);
68
71
  setSimulationError(err);
69
72
  onError && onError(new TxError(msg, err, "simulation"));
73
+ onSimulation?.({ simulation: undefined, status: "failed" });
70
74
  });
71
75
  }
72
76
  };
@@ -178,7 +182,7 @@ export const TxButton = (props) => {
178
182
  ? t("waiting")
179
183
  : isRejected
180
184
  ? t("rejected")
181
- : isSimulating
185
+ : isSimulating && animateSimulation
182
186
  ? t("simulating")
183
187
  : rest.label}`,
184
188
  }),
@@ -187,13 +191,13 @@ export const TxButton = (props) => {
187
191
  "button--grey": disabled &&
188
192
  !isSimulating &&
189
193
  !stylingClasses.includes("transparent"),
190
- "button--waiting": (isSigning || isRejected || isSuccess || isSimulating) &&
194
+ "button--waiting": (isSigning || isRejected || isSuccess || (isSimulating && animateSimulation)) &&
191
195
  !className?.includes("transparent"),
192
196
  "button--success": isSuccess,
193
197
  "button--rejected": isRejected,
194
- "button--simulating": isSimulating,
198
+ "button--simulating": isSimulating && animateSimulation,
195
199
  }), style: {
196
- cursor: isSigning || isSuccess || isRejected || isSimulating
200
+ cursor: isSigning || isSuccess || isRejected || (isSimulating && animateSimulation)
197
201
  ? "default"
198
202
  : undefined,
199
203
  ...rest.style,
@@ -202,7 +206,7 @@ export const TxButton = (props) => {
202
206
  : undefined, children: [(isSuccess ||
203
207
  isRejected ||
204
208
  isSigning ||
205
- isSimulating ||
209
+ (isSimulating && animateSimulation) ||
206
210
  !!simulationError) &&
207
211
  !className?.includes("transparent") && (_jsx("img", { className: "w-4 h-4 filter-white big", src: isSimulating
208
212
  ? txSimulate
@@ -1 +1 @@
1
- {"version":3,"file":"IconDenom.d.ts","sourceRoot":"","sources":["../../../../src/components/icons/IconDenom.tsx"],"names":[],"mappings":"AAmaA,eAAO,MAAM,SAAS,6DACG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,6CAQ7D,CAAC"}
1
+ {"version":3,"file":"IconDenom.d.ts","sourceRoot":"","sources":["../../../../src/components/icons/IconDenom.tsx"],"names":[],"mappings":"AAyaA,eAAO,MAAM,SAAS,6DACG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,6CAQ7D,CAAC"}