@scaffold-hbar-ui/debug-contracts 1.0.0 → 1.0.1

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 (98) hide show
  1. package/LICENSE +21 -0
  2. package/dist/esm/Contract.js +22 -0
  3. package/dist/esm/components/Collapsible.js +9 -0
  4. package/dist/esm/components/ContractInput.js +56 -0
  5. package/dist/esm/components/ContractReadMethods.js +23 -0
  6. package/dist/esm/components/ContractVariables.js +23 -0
  7. package/dist/esm/components/ContractWriteMethods.js +23 -0
  8. package/dist/esm/components/DisplayVariable.js +34 -0
  9. package/dist/esm/components/InheritanceTooltip.js +4 -0
  10. package/dist/esm/components/ReadOnlyFunctionForm.js +48 -0
  11. package/dist/esm/components/Tooltip.js +37 -0
  12. package/dist/esm/components/Tuple.js +21 -0
  13. package/dist/esm/components/TupleArray.js +73 -0
  14. package/dist/esm/components/TxReceipt.js +13 -0
  15. package/dist/esm/components/WriteOnlyFunctionForm.js +59 -0
  16. package/dist/esm/components/inputs/Bytes32Input.js +13 -0
  17. package/dist/esm/components/inputs/BytesInput.js +10 -0
  18. package/dist/esm/components/inputs/DecimalMultiplierButtons.js +58 -0
  19. package/dist/esm/components/inputs/IntegerInput.js +17 -0
  20. package/dist/esm/components/inputs/index.js +4 -0
  21. package/dist/esm/contexts/ContractConfigContext.js +13 -0
  22. package/dist/esm/hooks/useAnimationConfig.js +16 -0
  23. package/dist/esm/hooks/useCopyToClipboard.js +17 -0
  24. package/dist/esm/hooks/useTransactor.js +86 -0
  25. package/dist/esm/index.js +4 -0
  26. package/dist/esm/types.js +1 -0
  27. package/dist/esm/utils/common.js +42 -0
  28. package/dist/esm/utils/contracts.js +186 -0
  29. package/dist/esm/utils/getParsedError.js +25 -0
  30. package/dist/esm/utils/inputs.js +100 -0
  31. package/dist/esm/utils/networks.js +46 -0
  32. package/dist/esm/utils/notification.js +45 -0
  33. package/dist/esm/utils/utilsDisplay.js +68 -0
  34. package/dist/types/Contract.d.ts +12 -0
  35. package/dist/types/Contract.d.ts.map +1 -0
  36. package/dist/types/components/Collapsible.d.ts +9 -0
  37. package/dist/types/components/Collapsible.d.ts.map +1 -0
  38. package/dist/types/components/ContractInput.d.ts +14 -0
  39. package/dist/types/components/ContractInput.d.ts.map +1 -0
  40. package/dist/types/components/ContractReadMethods.d.ts +8 -0
  41. package/dist/types/components/ContractReadMethods.d.ts.map +1 -0
  42. package/dist/types/components/ContractVariables.d.ts +10 -0
  43. package/dist/types/components/ContractVariables.d.ts.map +1 -0
  44. package/dist/types/components/ContractWriteMethods.d.ts +9 -0
  45. package/dist/types/components/ContractWriteMethods.d.ts.map +1 -0
  46. package/dist/types/components/DisplayVariable.d.ts +11 -0
  47. package/dist/types/components/DisplayVariable.d.ts.map +1 -0
  48. package/dist/types/components/InheritanceTooltip.d.ts +4 -0
  49. package/dist/types/components/InheritanceTooltip.d.ts.map +1 -0
  50. package/dist/types/components/ReadOnlyFunctionForm.d.ts +11 -0
  51. package/dist/types/components/ReadOnlyFunctionForm.d.ts.map +1 -0
  52. package/dist/types/components/Tooltip.d.ts +12 -0
  53. package/dist/types/components/Tooltip.d.ts.map +1 -0
  54. package/dist/types/components/Tuple.d.ts +11 -0
  55. package/dist/types/components/Tuple.d.ts.map +1 -0
  56. package/dist/types/components/TupleArray.d.ts +13 -0
  57. package/dist/types/components/TupleArray.d.ts.map +1 -0
  58. package/dist/types/components/TxReceipt.d.ts +5 -0
  59. package/dist/types/components/TxReceipt.d.ts.map +1 -0
  60. package/dist/types/components/WriteOnlyFunctionForm.d.ts +12 -0
  61. package/dist/types/components/WriteOnlyFunctionForm.d.ts.map +1 -0
  62. package/dist/types/components/inputs/Bytes32Input.d.ts +3 -0
  63. package/dist/types/components/inputs/Bytes32Input.d.ts.map +1 -0
  64. package/dist/types/components/inputs/BytesInput.d.ts +3 -0
  65. package/dist/types/components/inputs/BytesInput.d.ts.map +1 -0
  66. package/dist/types/components/inputs/DecimalMultiplierButtons.d.ts +7 -0
  67. package/dist/types/components/inputs/DecimalMultiplierButtons.d.ts.map +1 -0
  68. package/dist/types/components/inputs/IntegerInput.d.ts +7 -0
  69. package/dist/types/components/inputs/IntegerInput.d.ts.map +1 -0
  70. package/dist/types/components/inputs/index.d.ts +5 -0
  71. package/dist/types/components/inputs/index.d.ts.map +1 -0
  72. package/dist/types/contexts/ContractConfigContext.d.ts +13 -0
  73. package/dist/types/contexts/ContractConfigContext.d.ts.map +1 -0
  74. package/dist/types/hooks/useAnimationConfig.d.ts +4 -0
  75. package/dist/types/hooks/useAnimationConfig.d.ts.map +1 -0
  76. package/dist/types/hooks/useCopyToClipboard.d.ts +5 -0
  77. package/dist/types/hooks/useCopyToClipboard.d.ts.map +1 -0
  78. package/dist/types/hooks/useTransactor.d.ts +16 -0
  79. package/dist/types/hooks/useTransactor.d.ts.map +1 -0
  80. package/dist/types/index.d.ts +5 -0
  81. package/dist/types/index.d.ts.map +1 -0
  82. package/dist/types/types.d.ts +16 -0
  83. package/dist/types/types.d.ts.map +1 -0
  84. package/dist/types/utils/common.d.ts +5 -0
  85. package/dist/types/utils/common.d.ts.map +1 -0
  86. package/dist/types/utils/contracts.d.ts +29 -0
  87. package/dist/types/utils/contracts.d.ts.map +1 -0
  88. package/dist/types/utils/getParsedError.d.ts +7 -0
  89. package/dist/types/utils/getParsedError.d.ts.map +1 -0
  90. package/dist/types/utils/inputs.d.ts +78 -0
  91. package/dist/types/utils/inputs.d.ts.map +1 -0
  92. package/dist/types/utils/networks.d.ts +12 -0
  93. package/dist/types/utils/networks.d.ts.map +1 -0
  94. package/dist/types/utils/notification.d.ts +17 -0
  95. package/dist/types/utils/notification.d.ts.map +1 -0
  96. package/dist/types/utils/utilsDisplay.d.ts +14 -0
  97. package/dist/types/utils/utilsDisplay.d.ts.map +1 -0
  98. package/package.json +20 -20
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 buidler-labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Address, Balance } from "@scaffold-hbar-ui/components";
3
+ import { useMemo, useReducer } from "react";
4
+ import { extractChain } from "viem";
5
+ import { ContractVariables } from "./components/ContractVariables";
6
+ import { ContractReadMethods } from "./components/ContractReadMethods";
7
+ import { ContractWriteMethods } from "./components/ContractWriteMethods";
8
+ import { Toaster } from "react-hot-toast";
9
+ import * as chains from "viem/chains";
10
+ import { ContractConfigProvider } from "./contexts/ContractConfigContext";
11
+ import { NETWORKS_EXTRA_DATA } from "./utils/common";
12
+ export const Contract = ({ contractName, contract, chainId, blockExplorerAddressLink }) => {
13
+ const [refreshDisplayVariables, triggerRefreshDisplayVariables] = useReducer((value) => !value, false);
14
+ const chain = extractChain({
15
+ chains: Object.values(chains),
16
+ id: chainId,
17
+ });
18
+ const balanceStyle = useMemo(() => ({
19
+ fontSize: "0.75rem",
20
+ }), []);
21
+ return (_jsxs(ContractConfigProvider, { config: { blockExplorerAddressLink, chain, chainId }, children: [_jsx("div", { className: "w-full max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 my-0 font-sans", children: _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-8 lg:gap-10", children: [_jsxs("div", { className: "lg:col-span-1 flex flex-col min-w-0", children: [_jsx("div", { className: "bg-sui-base-100 border-sui-primary-subtle border shadow-md shadow-sui-primary-subtle rounded-3xl px-6 lg:px-8 mb-6 space-y-1 py-4", children: _jsx("div", { className: "flex", children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("span", { className: "font-bold", children: contractName }), _jsx(Address, { address: contract.address, size: "base", chain: chain, blockExplorerAddressLink: blockExplorerAddressLink }), _jsxs("div", { className: "flex gap-1 items-center mt-1", children: [_jsx("span", { className: "font-bold text-sm", children: "Balance:" }), contract.address && (_jsx(Balance, { address: contract.address, style: balanceStyle }))] }), _jsxs("p", { className: "my-0 text-sm", children: [_jsx("span", { className: "font-bold", children: "Network" }), ":", " ", _jsx("span", { style: { color: NETWORKS_EXTRA_DATA[chainId]?.color }, children: chain.id === 31_337 ? "Localhost" : chain.name })] })] }) }) }), _jsx("div", { className: "bg-sui-primary-subtle rounded-3xl px-6 lg:px-8 py-4 shadow-lg shadow-sui-primary-subtle overflow-y-auto", children: _jsx(ContractVariables, { refreshDisplayVariables: refreshDisplayVariables, contract: contract }) })] }), _jsxs("div", { className: "lg:col-span-2 flex flex-col gap-6 min-w-0", children: [_jsx("div", { className: "z-10", children: _jsxs("div", { className: "bg-sui-base-100 rounded-3xl shadow-md shadow-sui-primary-subtle border border-sui-primary-subtle flex flex-col mt-10 relative", children: [_jsx("div", { className: "h-[5rem] w-[5.5rem] bg-sui-primary-subtle absolute self-start rounded-[22px] -top-[38px] -left-[1px] -z-10 py-[0.65rem] shadow-lg shadow-sui-primary-subtle", children: _jsx("div", { className: "flex items-center justify-center space-x-2", children: _jsx("p", { className: "my-0 text-sm", children: "Read" }) }) }), _jsx("div", { className: "p-5 divide-y divide-sui-primary-subtle", children: _jsx(ContractReadMethods, { contract: contract }) })] }) }), _jsx("div", { className: "z-10", children: _jsxs("div", { className: "bg-sui-base-100 rounded-3xl shadow-md shadow-sui-primary-subtle border border-sui-primary-subtle flex flex-col mt-10 relative", children: [_jsx("div", { className: "h-[5rem] w-[5.5rem] bg-sui-primary-subtle absolute self-start rounded-[22px] -top-[38px] -left-[1px] -z-10 py-[0.65rem] shadow-lg shadow-sui-primary-subtle", children: _jsx("div", { className: "flex items-center justify-center space-x-2", children: _jsx("p", { className: "my-0 text-sm", children: "Write" }) }) }), _jsx("div", { className: "p-5 divide-y divide-sui-primary-subtle", children: _jsx(ContractWriteMethods, { contract: contract, onChange: triggerRefreshDisplayVariables }) })] }) })] })] }) }), _jsx(Toaster, {})] }));
22
+ };
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ export const Collapsible = ({ title, children, className = "" }) => {
4
+ const [isOpen, setIsOpen] = useState(false);
5
+ const toggleOpen = () => {
6
+ setIsOpen(!isOpen);
7
+ };
8
+ return (_jsxs("div", { className: `border-2 border-sui-primary-subtle rounded-2xl ${className}`, children: [_jsxs("button", { type: "button", onClick: toggleOpen, className: `w-full flex items-center justify-between p-3 text-left bg-sui-primary-neutral hover:brightness-90 transition-colors rounded-t-[14px] focus:outline-none border-sui-primary-subtle focus:ring-2 focus:ring-sui-primary ${isOpen ? "rounded-b-none" : "rounded-b-[14px]"}`, children: [_jsx("span", { className: "text-sui-primary-content/50 text-sm", children: title }), _jsx("svg", { className: `w-4 h-4 transition-transform duration-200 ${isOpen ? "rotate-90" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })] }), isOpen && _jsx("div", { className: "p-4 border-t border-sui-primary-subtle/80", children: children })] }));
9
+ };
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback } from "react";
4
+ import { BaseInput, HederaAddressInput } from "@scaffold-hbar-ui/components";
5
+ import { Bytes32Input } from "./inputs/Bytes32Input";
6
+ import { BytesInput } from "./inputs/BytesInput";
7
+ import { IntegerInput } from "./inputs/IntegerInput";
8
+ import { Tuple } from "./Tuple";
9
+ import { TupleArray } from "./TupleArray";
10
+ import { useContractConfig } from "../contexts/ContractConfigContext";
11
+ /** Hedera chain IDs – use HederaAddressInput (0x or 0.0.x) on these. Includes local fork (31337). */
12
+ const HEDERA_CHAIN_IDS = new Set([295, 296, 31337]);
13
+ /**
14
+ * Generic Input component to handle input's based on their function param type
15
+ */
16
+ export const ContractInput = ({ setForm, form, stateObjectKey, paramType }) => {
17
+ const { chainId } = useContractConfig();
18
+ const onAddressChange = useCallback((value) => {
19
+ setForm((prev) => ({ ...prev, [stateObjectKey]: value }));
20
+ }, [setForm, stateObjectKey]);
21
+ const inputProps = {
22
+ name: stateObjectKey,
23
+ value: form?.[stateObjectKey],
24
+ placeholder: paramType.name ? `${paramType.type} ${paramType.name}` : paramType.type,
25
+ onChange: onAddressChange,
26
+ };
27
+ const renderInput = () => {
28
+ switch (paramType.type) {
29
+ case "address":
30
+ if (HEDERA_CHAIN_IDS.has(chainId)) {
31
+ return (_jsx(HederaAddressInput, { name: inputProps.name, value: String(inputProps.value ?? ""), placeholder: inputProps.placeholder, onChange: onAddressChange, chainId: chainId }));
32
+ }
33
+ return (_jsx(BaseInput, { name: inputProps.name, value: String(inputProps.value ?? ""), placeholder: inputProps.placeholder, onChange: onAddressChange }));
34
+ case "bytes32":
35
+ return _jsx(Bytes32Input, { ...inputProps });
36
+ case "bytes":
37
+ return _jsx(BytesInput, { ...inputProps });
38
+ case "string":
39
+ return _jsx(BaseInput, { ...inputProps });
40
+ case "tuple":
41
+ return (_jsx(Tuple, { setParentForm: setForm, parentForm: form, abiTupleParameter: paramType, parentStateObjectKey: stateObjectKey }));
42
+ default:
43
+ // Handling 'int' types and 'tuple[]' types
44
+ if (paramType.type.includes("int") && !paramType.type.includes("[")) {
45
+ return (_jsx(IntegerInput, { ...inputProps, variant: paramType.type }));
46
+ }
47
+ else if (paramType.type.startsWith("tuple[")) {
48
+ return (_jsx(TupleArray, { setParentForm: setForm, parentForm: form, abiTupleParameter: paramType, parentStateObjectKey: stateObjectKey }));
49
+ }
50
+ else {
51
+ return _jsx(BaseInput, { ...inputProps });
52
+ }
53
+ }
54
+ };
55
+ return (_jsxs("div", { className: "flex flex-col gap-1.5 w-full", children: [_jsxs("div", { className: "flex items-center ml-2", children: [paramType.name && _jsx("span", { className: "text-xs font-medium mr-2 leading-none", children: paramType.name }), _jsx("span", { className: "block text-xs font-extralight leading-none", children: paramType.type })] }), renderInput()] }));
56
+ };
@@ -0,0 +1,23 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { ReadOnlyFunctionForm } from "./ReadOnlyFunctionForm";
3
+ export const ContractReadMethods = ({ contract, }) => {
4
+ if (!contract) {
5
+ return null;
6
+ }
7
+ const functionsToDisplay = (contract.abi || []).filter((part) => part.type === "function")
8
+ .filter((fn) => {
9
+ const isQueryableWithParams = (fn.stateMutability === "view" || fn.stateMutability === "pure") && fn.inputs.length > 0;
10
+ return isQueryableWithParams;
11
+ })
12
+ .map((fn) => {
13
+ return {
14
+ fn,
15
+ inheritedFrom: contract?.inheritedFunctions?.[fn.name],
16
+ };
17
+ })
18
+ .sort((a, b) => (b.inheritedFrom ? b.inheritedFrom.localeCompare(a.inheritedFrom) : 1));
19
+ if (!functionsToDisplay.length) {
20
+ return _jsx(_Fragment, { children: "No read methods" });
21
+ }
22
+ return (_jsx(_Fragment, { children: functionsToDisplay.map(({ fn, inheritedFrom }) => (_jsx(ReadOnlyFunctionForm, { abi: contract.abi, contractAddress: contract.address, abiFunction: fn, inheritedFrom: inheritedFrom }, fn.name))) }));
23
+ };
@@ -0,0 +1,23 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { DisplayVariable } from "./DisplayVariable";
3
+ export const ContractVariables = ({ refreshDisplayVariables, contract, }) => {
4
+ if (!contract) {
5
+ return null;
6
+ }
7
+ const functionsToDisplay = contract.abi.filter((part) => part.type === "function")
8
+ .filter((fn) => {
9
+ const isQueryableWithNoParams = (fn.stateMutability === "view" || fn.stateMutability === "pure") && fn.inputs.length === 0;
10
+ return isQueryableWithNoParams;
11
+ })
12
+ .map((fn) => {
13
+ return {
14
+ fn,
15
+ inheritedFrom: contract?.inheritedFunctions?.[fn.name],
16
+ };
17
+ })
18
+ .sort((a, b) => (b.inheritedFrom ? b.inheritedFrom.localeCompare(a.inheritedFrom) : 1));
19
+ if (!functionsToDisplay.length) {
20
+ return _jsx(_Fragment, { children: "No contract variables" });
21
+ }
22
+ return (_jsx(_Fragment, { children: functionsToDisplay.map(({ fn, inheritedFrom }) => (_jsx(DisplayVariable, { abi: contract.abi, abiFunction: fn, contractAddress: contract.address, refreshDisplayVariables: refreshDisplayVariables, inheritedFrom: inheritedFrom }, fn.name))) }));
23
+ };
@@ -0,0 +1,23 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { WriteOnlyFunctionForm } from "./WriteOnlyFunctionForm";
3
+ export const ContractWriteMethods = ({ onChange, contract, }) => {
4
+ if (!contract) {
5
+ return null;
6
+ }
7
+ const functionsToDisplay = contract.abi.filter((part) => part.type === "function")
8
+ .filter((fn) => {
9
+ const isWriteableFunction = fn.stateMutability !== "view" && fn.stateMutability !== "pure";
10
+ return isWriteableFunction;
11
+ })
12
+ .map((fn) => {
13
+ return {
14
+ fn,
15
+ inheritedFrom: contract?.inheritedFunctions?.[fn.name],
16
+ };
17
+ })
18
+ .sort((a, b) => (b.inheritedFrom ? b.inheritedFrom.localeCompare(a.inheritedFrom) : 1));
19
+ if (!functionsToDisplay.length) {
20
+ return _jsx(_Fragment, { children: "No write methods" });
21
+ }
22
+ return (_jsx(_Fragment, { children: functionsToDisplay.map(({ fn, inheritedFrom }, idx) => (_jsx(WriteOnlyFunctionForm, { abi: contract.abi, abiFunction: fn, onChange: onChange, contractAddress: contract.address, inheritedFrom: inheritedFrom }, `${fn.name}-${idx}}`))) }));
23
+ };
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useReadContract } from "wagmi";
3
+ import { useAnimationConfig } from "../hooks/useAnimationConfig";
4
+ import { useEffect } from "react";
5
+ import { getParsedError } from "../utils/getParsedError";
6
+ import { ArrowPathIcon } from "@heroicons/react/24/outline";
7
+ import { InheritanceTooltip } from "./InheritanceTooltip";
8
+ import { displayTxResult } from "../utils/utilsDisplay";
9
+ import { notification } from "../utils/notification";
10
+ import { useContractConfig } from "../contexts/ContractConfigContext";
11
+ export const DisplayVariable = ({ contractAddress, abiFunction, refreshDisplayVariables, abi, inheritedFrom, }) => {
12
+ const { chainId } = useContractConfig();
13
+ const { data: result, isFetching, refetch, error, } = useReadContract({
14
+ address: contractAddress,
15
+ functionName: abiFunction.name,
16
+ abi: abi,
17
+ chainId: chainId,
18
+ query: {
19
+ retry: false,
20
+ },
21
+ });
22
+ const { showAnimation } = useAnimationConfig(result);
23
+ useEffect(() => {
24
+ refetch();
25
+ }, [refetch, refreshDisplayVariables]);
26
+ useEffect(() => {
27
+ if (error) {
28
+ const parsedError = getParsedError(error);
29
+ console.log("The parsedError is:", parsedError);
30
+ notification.error(parsedError);
31
+ }
32
+ }, [error]);
33
+ return (_jsxs("div", { className: "space-y-1 pb-2", children: [_jsxs("div", { className: "flex items-center", children: [_jsx("h3", { className: "font-medium text-lg mb-0 break-all", children: abiFunction.name }), _jsx("button", { className: "p-1 text-sui-primary-content/60 hover:text-sui-primary-content dark:hover:bg-sui-primary-neutral hover:bg-sui-primary transition-colors duration-200 cursor-pointer ml-1 rounded-full", onClick: async () => await refetch(), children: isFetching ? (_jsx("div", { className: "w-3 h-3 border border-sui-primary-subtle border-t-transparent rounded-full animate-spin" })) : (_jsx(ArrowPathIcon, { className: "h-3 w-3 cursor-pointer", "aria-hidden": "true" })) }), _jsx(InheritanceTooltip, { inheritedFrom: inheritedFrom })] }), _jsx("div", { className: "text-sui-primary-content/80 flex flex-col items-start", children: _jsx("div", { children: _jsx("div", { className: `block transition ${showAnimation ? "bg-[#e2d563] rounded-xs animate-pulse-fast" : "bg-transparent"}`, children: displayTxResult(result, "base") }) }) })] }));
34
+ };
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { InformationCircleIcon } from "@heroicons/react/20/solid";
3
+ import { Tooltip } from "./Tooltip";
4
+ export const InheritanceTooltip = ({ inheritedFrom }) => (_jsx(_Fragment, { children: inheritedFrom && (_jsx(Tooltip, { content: `Inherited from: ${inheritedFrom}`, position: "top", className: "px-2", children: _jsx(InformationCircleIcon, { className: "h-4 w-4 text-sui-primary cursor-help", "aria-hidden": "true" }) })) }));
@@ -0,0 +1,48 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { InheritanceTooltip } from "./InheritanceTooltip";
5
+ import { useConfig } from "wagmi";
6
+ import { readContract } from "wagmi/actions";
7
+ import { getFunctionInputKey, getInitialFormState, getParsedContractFunctionArgs, resolveHederaNativeAccountIdsInForm, transformAbiFunction, } from "../utils/contracts";
8
+ import { displayTxResult } from "../utils/utilsDisplay";
9
+ import { getParsedError } from "../utils/getParsedError";
10
+ import { ContractInput } from "./ContractInput";
11
+ import { notification } from "../utils/notification";
12
+ import { useContractConfig } from "../contexts/ContractConfigContext";
13
+ export const ReadOnlyFunctionForm = ({ contractAddress, abiFunction, inheritedFrom, abi, }) => {
14
+ const { chainId } = useContractConfig();
15
+ const config = useConfig();
16
+ const [form, setForm] = useState(() => getInitialFormState(abiFunction));
17
+ const [result, setResult] = useState();
18
+ const [isFetching, setIsFetching] = useState(false);
19
+ const transformedFunction = transformAbiFunction(abiFunction);
20
+ const inputElements = transformedFunction.inputs.map((input, inputIndex) => {
21
+ const key = getFunctionInputKey(abiFunction.name, input, inputIndex);
22
+ return (_jsx(ContractInput, { setForm: setForm, form: form, stateObjectKey: key, paramType: input }, key));
23
+ });
24
+ const handleRead = async () => {
25
+ setIsFetching(true);
26
+ try {
27
+ const resolvedForm = await resolveHederaNativeAccountIdsInForm(form, abiFunction, chainId);
28
+ const args = getParsedContractFunctionArgs(resolvedForm);
29
+ const data = await readContract(config, {
30
+ address: contractAddress,
31
+ abi,
32
+ functionName: abiFunction.name,
33
+ args,
34
+ chainId,
35
+ });
36
+ setResult(data);
37
+ }
38
+ catch (error) {
39
+ const parsedError = getParsedError(error);
40
+ console.log("The parsedError is:", parsedError);
41
+ notification.error(parsedError);
42
+ }
43
+ finally {
44
+ setIsFetching(false);
45
+ }
46
+ };
47
+ return (_jsxs("div", { className: "flex flex-col gap-3 py-5 first:pt-0 last:pb-1", children: [_jsxs("div", { className: "font-medium my-0 break-words", children: [abiFunction.name, _jsx(InheritanceTooltip, { inheritedFrom: inheritedFrom })] }), inputElements, _jsxs("div", { className: "flex flex-col md:flex-row justify-between gap-2 flex-wrap", children: [_jsx("div", { className: "grow w-full md:max-w-[80%]", children: result !== null && result !== undefined && (_jsxs("div", { className: "bg-sui-primary-subtle rounded-3xl text-sm px-4 py-1.5 break-words overflow-auto", children: [_jsx("p", { className: "font-bold m-0 mb-1", children: "Result:" }), _jsx("pre", { className: "whitespace-pre-wrap break-words", children: displayTxResult(result, "sm") })] })) }), _jsxs("button", { className: "btn-dc btn-dc-secondary btn-sm self-end md:self-start", onClick: handleRead, disabled: isFetching, children: [isFetching && _jsx("span", { className: "loading-dc loading-dc-spinner loading-xs" }), "Read \uD83D\uDCE1"] })] })] }));
48
+ };
@@ -0,0 +1,37 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ const getPositionClasses = (position) => {
3
+ switch (position) {
4
+ case "top":
5
+ return "bottom-full left-1/2 transform -translate-x-1/2 mb-2";
6
+ case "bottom":
7
+ return "top-full left-1/2 transform -translate-x-1/2 mt-2";
8
+ case "left":
9
+ return "right-full top-1/2 transform -translate-y-1/2 mr-2";
10
+ case "right":
11
+ return "left-full top-1/2 transform -translate-y-1/2 ml-2";
12
+ default:
13
+ return "bottom-full left-1/2 transform -translate-x-1/2 mb-2";
14
+ }
15
+ };
16
+ const getArrowClasses = (position) => {
17
+ switch (position) {
18
+ case "top":
19
+ return "absolute top-full left-1/2 transform -translate-x-1/2 border-4 border-transparent border-t-sui-primary";
20
+ case "bottom":
21
+ return "absolute bottom-full left-1/2 transform -translate-x-1/2 border-4 border-transparent border-b-sui-primary";
22
+ case "left":
23
+ return "absolute left-full top-1/2 transform -translate-y-1/2 border-4 border-transparent border-l-sui-primary";
24
+ case "right":
25
+ return "absolute right-full top-1/2 transform -translate-y-1/2 border-4 border-transparent border-r-sui-primary";
26
+ default:
27
+ return "absolute top-full left-1/2 transform -translate-x-1/2 border-4 border-transparent border-t-sui-primary";
28
+ }
29
+ };
30
+ export const Tooltip = ({ children, content, position = "top", show = true, className = "", contentClassName = "", }) => {
31
+ if (!show || !content) {
32
+ return _jsx(_Fragment, { children: children });
33
+ }
34
+ const positionClasses = getPositionClasses(position);
35
+ const arrowClasses = getArrowClasses(position);
36
+ return (_jsxs("div", { className: `relative group inline-block ${className}`, children: [children, _jsxs("div", { className: `absolute ${positionClasses} px-2 py-1 text-sm rounded-md whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-10 bg-sui-primary text-sui-primary-content ${contentClassName}`, children: [content, _jsx("div", { className: arrowClasses })] })] }));
37
+ };
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { ContractInput } from "./ContractInput";
4
+ import { Collapsible } from "./Collapsible";
5
+ import { getFunctionInputKey, getInitialTupleFormState } from "../utils/contracts";
6
+ import { replacer } from "../utils/utilsDisplay";
7
+ export const Tuple = ({ abiTupleParameter, setParentForm, parentStateObjectKey }) => {
8
+ const [form, setForm] = useState(() => getInitialTupleFormState(abiTupleParameter));
9
+ useEffect(() => {
10
+ const values = Object.values(form);
11
+ const argsStruct = {};
12
+ abiTupleParameter.components.forEach((component, componentIndex) => {
13
+ argsStruct[component.name || `input_${componentIndex}_`] = values[componentIndex];
14
+ });
15
+ setParentForm((parentForm) => ({ ...parentForm, [parentStateObjectKey]: JSON.stringify(argsStruct, replacer) }));
16
+ }, [JSON.stringify(form, replacer)]);
17
+ return (_jsx(Collapsible, { title: abiTupleParameter.internalType || "tuple", children: _jsx("div", { className: "flex flex-col space-y-4 border-l-2 border-sui-primary-subtle/80 pl-4 ml-3", children: abiTupleParameter?.components?.map((param, index) => {
18
+ const key = getFunctionInputKey(abiTupleParameter.name || "tuple", param, index);
19
+ return (_jsx(ContractInput, { setForm: setForm, form: form, stateObjectKey: key, paramType: param }, key));
20
+ }) }) }));
21
+ };
@@ -0,0 +1,73 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { ContractInput } from "./ContractInput";
4
+ import { Collapsible } from "./Collapsible";
5
+ import { getFunctionInputKey, getInitialTupleArrayFormState } from "../utils/contracts";
6
+ import { replacer } from "../utils/utilsDisplay";
7
+ export const TupleArray = ({ abiTupleParameter, setParentForm, parentStateObjectKey }) => {
8
+ const [form, setForm] = useState(() => getInitialTupleArrayFormState(abiTupleParameter));
9
+ const [additionalInputs, setAdditionalInputs] = useState([
10
+ abiTupleParameter.components,
11
+ ]);
12
+ const depth = (abiTupleParameter.type.match(/\[\]/g) || []).length;
13
+ useEffect(() => {
14
+ // Extract and group fields based on index prefix
15
+ const groupedFields = Object.keys(form).reduce((acc, key) => {
16
+ const [indexPrefix, ...restArray] = key.split("_");
17
+ const componentName = restArray.join("_");
18
+ if (!acc[indexPrefix]) {
19
+ acc[indexPrefix] = {};
20
+ }
21
+ acc[indexPrefix][componentName] = form[key];
22
+ return acc;
23
+ }, {});
24
+ let argsArray = [];
25
+ Object.keys(groupedFields).forEach((key) => {
26
+ const currentKeyValues = Object.values(groupedFields[key]);
27
+ const argsStruct = {};
28
+ abiTupleParameter.components.forEach((component, componentIndex) => {
29
+ argsStruct[component.name || `input_${componentIndex}_`] = currentKeyValues[componentIndex];
30
+ });
31
+ argsArray.push(argsStruct);
32
+ });
33
+ if (depth > 1) {
34
+ argsArray = argsArray.map((args) => {
35
+ return args[abiTupleParameter.components[0].name || "tuple"];
36
+ });
37
+ }
38
+ setParentForm((parentForm) => {
39
+ return { ...parentForm, [parentStateObjectKey]: JSON.stringify(argsArray, replacer) };
40
+ });
41
+ }, [JSON.stringify(form, replacer)]);
42
+ const addInput = () => {
43
+ setAdditionalInputs((previousValue) => {
44
+ const newAdditionalInputs = [...previousValue, abiTupleParameter.components];
45
+ // Add the new inputs to the form
46
+ setForm((form) => {
47
+ const newForm = { ...form };
48
+ abiTupleParameter.components.forEach((component, componentIndex) => {
49
+ const key = getFunctionInputKey(`${newAdditionalInputs.length - 1}_${abiTupleParameter.name || "tuple"}`, component, componentIndex);
50
+ newForm[key] = "";
51
+ });
52
+ return newForm;
53
+ });
54
+ return newAdditionalInputs;
55
+ });
56
+ };
57
+ const removeInput = () => {
58
+ // Remove the last inputs from the form
59
+ setForm((form) => {
60
+ const newForm = { ...form };
61
+ abiTupleParameter.components.forEach((component, componentIndex) => {
62
+ const key = getFunctionInputKey(`${additionalInputs.length - 1}_${abiTupleParameter.name || "tuple"}`, component, componentIndex);
63
+ delete newForm[key];
64
+ });
65
+ return newForm;
66
+ });
67
+ setAdditionalInputs((inputs) => inputs.slice(0, -1));
68
+ };
69
+ return (_jsx(Collapsible, { title: abiTupleParameter.internalType || "tuple-array", children: _jsxs("div", { className: "ml-3 flex flex-col space-y-2 border-l-2 border-sui-primary-subtle/70 pl-4", children: [additionalInputs.map((additionalInput, additionalIndex) => (_jsxs("div", { className: "space-y-1", children: [_jsx("span", { className: "inline-block bg-sui-primary-subtle text-sui-primary-content text-xs px-2 py-1 rounded", children: depth > 1 ? `${additionalIndex}` : `tuple[${additionalIndex}]` }), _jsx("div", { className: "space-y-4", children: additionalInput.map((param, index) => {
70
+ const key = getFunctionInputKey(`${additionalIndex}_${abiTupleParameter.name || "tuple"}`, param, index);
71
+ return (_jsx(ContractInput, { setForm: setForm, form: form, stateObjectKey: key, paramType: param }, key));
72
+ }) })] }, additionalIndex))), _jsxs("div", { className: "flex space-x-2", children: [_jsx("button", { className: "btn-dc btn-dc-secondary min-h-9 min-w-9", onClick: addInput, children: "+" }), additionalInputs.length > 0 && (_jsx("button", { className: "btn-dc btn-dc-secondary min-h-9 min-w-9", onClick: removeInput, children: "-" }))] })] }) }));
73
+ };
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { CheckCircleIcon, DocumentDuplicateIcon, ChevronDownIcon } from "@heroicons/react/24/outline";
4
+ import { useCopyToClipboard } from "../hooks/useCopyToClipboard";
5
+ import { ObjectFieldDisplay, replacer } from "../utils/utilsDisplay";
6
+ export const TxReceipt = ({ txResult }) => {
7
+ const [isExpanded, setIsExpanded] = useState(false);
8
+ const { copyToClipboard: copyTxResultToClipboard, isCopiedToClipboard: isTxResultCopiedToClipboard } = useCopyToClipboard();
9
+ const toggleExpanded = () => {
10
+ setIsExpanded(!isExpanded);
11
+ };
12
+ return (_jsxs("div", { className: `text-sm bg-sui-primary-subtle ${isExpanded ? "rounded-t-3xl" : "rounded-3xl"} min-h-0 py-0`, children: [_jsxs("div", { className: "flex items-center", children: [_jsx("div", { className: "mt-1 pl-2 flex-shrink-0", children: isTxResultCopiedToClipboard ? (_jsx(CheckCircleIcon, { className: "ml-1.5 text-xl font-normal text-sui-primary-content h-5 w-5 cursor-pointer", "aria-hidden": "true" })) : (_jsx(DocumentDuplicateIcon, { className: "ml-1.5 text-xl font-normal h-5 w-5 cursor-pointer", "aria-hidden": "true", onClick: () => copyTxResultToClipboard(JSON.stringify(txResult, replacer, 2)) })) }), _jsxs("div", { className: "flex-1 flex items-center justify-between cursor-pointer py-1.5 pl-1 pr-4", onClick: toggleExpanded, children: [_jsx("strong", { className: "text-sm", children: "Transaction Receipt" }), _jsx(ChevronDownIcon, { className: `h-4 w-4 transition-transform duration-200 flex-shrink-0 ${isExpanded ? "rotate-180" : ""}` })] })] }), _jsx("div", { className: `overflow-auto transition-all duration-300 ease-in-out rounded-b-3xl ${isExpanded ? "max-h-96 opacity-100" : "max-h-0 opacity-0"}`, children: _jsx("div", { className: "overflow-auto bg-sui-primary-subtle rounded-b-3xl border-t border-gray-300/20", children: _jsx("pre", { className: "text-xs p-4 whitespace-pre-wrap break-words rounded-b-3xl", children: Object.entries(txResult).map(([k, v]) => (_jsx(ObjectFieldDisplay, { name: k, value: v, size: "xs", leftPad: false }, k))) }) }) })] }));
13
+ };
@@ -0,0 +1,59 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { InheritanceTooltip } from "./InheritanceTooltip";
5
+ import { useAccount, useConfig, useWaitForTransactionReceipt, useWriteContract } from "wagmi";
6
+ import { getFunctionInputKey, getInitialFormState, getParsedContractFunctionArgs, resolveHederaNativeAccountIdsInForm, simulateContractWriteAndNotifyError, transformAbiFunction, } from "../utils/contracts";
7
+ import { useTransactor } from "../hooks/useTransactor";
8
+ import { ContractInput } from "./ContractInput";
9
+ import { IntegerInput } from "./inputs/IntegerInput";
10
+ import { TxReceipt } from "./TxReceipt";
11
+ import { Tooltip } from "./Tooltip";
12
+ import { useContractConfig } from "../contexts/ContractConfigContext";
13
+ export const WriteOnlyFunctionForm = ({ abi, abiFunction, onChange, contractAddress, inheritedFrom, }) => {
14
+ const { chainId } = useContractConfig();
15
+ const [form, setForm] = useState(() => getInitialFormState(abiFunction));
16
+ const [txValue, setTxValue] = useState("");
17
+ const { chain } = useAccount();
18
+ const writeTxn = useTransactor();
19
+ const writeDisabled = !chain || chain?.id !== chainId;
20
+ const { data: result, isPending, writeContractAsync } = useWriteContract();
21
+ const wagmiConfig = useConfig();
22
+ const handleWrite = async () => {
23
+ if (writeContractAsync) {
24
+ try {
25
+ setDisplayedTxResult(undefined);
26
+ const resolvedForm = await resolveHederaNativeAccountIdsInForm(form, abiFunction, chainId);
27
+ const writeContractObj = {
28
+ address: contractAddress,
29
+ functionName: abiFunction.name,
30
+ abi: abi,
31
+ args: getParsedContractFunctionArgs(resolvedForm),
32
+ value: BigInt(txValue),
33
+ };
34
+ await simulateContractWriteAndNotifyError({ wagmiConfig, writeContractParams: writeContractObj });
35
+ const makeWriteWithParams = () => writeContractAsync(writeContractObj);
36
+ await writeTxn(makeWriteWithParams);
37
+ onChange();
38
+ }
39
+ catch (e) {
40
+ console.error("⚡️ ~ file: WriteOnlyFunctionForm.tsx:handleWrite ~ error", e);
41
+ }
42
+ }
43
+ };
44
+ const [displayedTxResult, setDisplayedTxResult] = useState();
45
+ const { data: txResult } = useWaitForTransactionReceipt({
46
+ hash: result,
47
+ });
48
+ useEffect(() => {
49
+ setDisplayedTxResult(txResult);
50
+ }, [txResult]);
51
+ // TODO use `useMemo` to optimize also update in ReadOnlyFunctionForm
52
+ const transformedFunction = transformAbiFunction(abiFunction);
53
+ const inputs = transformedFunction.inputs.map((input, inputIndex) => {
54
+ const key = getFunctionInputKey(abiFunction.name, input, inputIndex);
55
+ return (_jsx(ContractInput, { setForm: setForm, form: form, stateObjectKey: key, paramType: input }, key));
56
+ });
57
+ const zeroInputs = inputs.length === 0 && abiFunction.stateMutability !== "payable";
58
+ return (_jsxs("div", { className: "py-5 space-y-3 first:pt-0 last:pb-1", children: [_jsxs("div", { className: `flex gap-3 ${zeroInputs ? "flex-row justify-between items-center" : "flex-col"}`, children: [_jsxs("div", { className: "font-medium my-0 break-words", children: [abiFunction.name, _jsx(InheritanceTooltip, { inheritedFrom: inheritedFrom })] }), inputs, abiFunction.stateMutability === "payable" ? (_jsxs("div", { className: "flex flex-col gap-1.5 w-full", children: [_jsxs("div", { className: "flex items-center ml-2", children: [_jsx("span", { className: "text-xs font-medium mr-2 leading-none", children: "payable value" }), _jsx("span", { className: "block text-xs font-extralight leading-none", children: "wei" })] }), _jsx(IntegerInput, { value: txValue, onChange: setTxValue, placeholder: "value (wei)" })] })) : null, _jsxs("div", { className: "flex justify-between gap-2 min-w-0", children: [!zeroInputs && (_jsx("div", { className: "flex-1 min-w-0 max-w-11/12 overflow-auto", children: displayedTxResult ? _jsx(TxReceipt, { txResult: displayedTxResult }) : null })), _jsx("div", { className: "flex flex-shrink-0 self-start", children: _jsx(Tooltip, { content: "Wallet not connected or in the wrong network", position: "bottom", show: writeDisabled, children: _jsxs("button", { className: "btn-dc btn-dc-secondary btn-sm", disabled: writeDisabled || isPending, onClick: handleWrite, children: [isPending && _jsx("span", { className: "loading-dc loading-dc-spinner mr-1" }), "Send \uD83D\uDCB8"] }) }) })] })] }), zeroInputs && txResult ? (_jsx("div", { className: "w-full min-w-0 overflow-auto", children: _jsx(TxReceipt, { txResult: txResult }) })) : null] }));
59
+ };
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback } from "react";
3
+ import { hexToString, isHex, stringToHex } from "viem";
4
+ import { BaseInput } from "@scaffold-hbar-ui/components";
5
+ export const Bytes32Input = ({ value, onChange, name, placeholder, disabled }) => {
6
+ const convertStringToBytes32 = useCallback(() => {
7
+ if (!value) {
8
+ return;
9
+ }
10
+ onChange(isHex(value) ? hexToString(value, { size: 32 }) : stringToHex(value, { size: 32 }));
11
+ }, [onChange, value]);
12
+ return (_jsx(BaseInput, { name: name, value: value, placeholder: placeholder, onChange: onChange, disabled: disabled, suffix: _jsx("button", { className: "self-center cursor-pointer text-xl font-semibold px-4 text-sui-accent", onClick: convertStringToBytes32, type: "button", children: "#" }) }));
13
+ };
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback } from "react";
3
+ import { bytesToString, isHex, toBytes, toHex } from "viem";
4
+ import { BaseInput } from "@scaffold-hbar-ui/components";
5
+ export const BytesInput = ({ value, onChange, name, placeholder, disabled }) => {
6
+ const convertStringToBytes = useCallback(() => {
7
+ onChange(isHex(value) ? bytesToString(toBytes(value)) : toHex(toBytes(value)));
8
+ }, [onChange, value]);
9
+ return (_jsx(BaseInput, { name: name, value: value, placeholder: placeholder, onChange: onChange, disabled: disabled, suffix: _jsx("button", { className: "self-center cursor-pointer font-semibold px-4 text-sui-accent", onClick: convertStringToBytes, type: "button", children: "#" }) }));
10
+ };
@@ -0,0 +1,58 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback } from "react";
3
+ import { parseEther } from "viem";
4
+ import { Tooltip } from "../Tooltip";
5
+ /**
6
+ * Returns the "base" value by stripping trailing zeros from integers.
7
+ * So clicking ×1e8 then ×1e18 (or vice versa) applies only the last clicked factor.
8
+ * e.g. "1000000000000000000" → "1", "100000000" → "1". Values with decimals are unchanged.
9
+ */
10
+ function normalizeToBase(value) {
11
+ const trimmed = value.trim();
12
+ if (!trimmed || trimmed === "0")
13
+ return trimmed;
14
+ if (trimmed.includes("."))
15
+ return trimmed;
16
+ try {
17
+ const big = BigInt(trimmed);
18
+ if (big === 0n)
19
+ return "0";
20
+ const s = big.toString();
21
+ const withoutTrailing = s.replace(/0+$/, "");
22
+ if (withoutTrailing === s)
23
+ return s;
24
+ const trailingCount = s.length - withoutTrailing.length;
25
+ return (big / BigInt(10 ** trailingCount)).toString();
26
+ }
27
+ catch {
28
+ return trimmed;
29
+ }
30
+ }
31
+ /** Multiplies a decimal string by 1e8 (Hedera 8 decimals) without float precision loss */
32
+ function multiplyValueBy1e8(value) {
33
+ const parts = value.trim().split(".");
34
+ const intPart = parts[0] || "0";
35
+ const fracPart = (parts[1] || "").padEnd(8, "0").slice(0, 8);
36
+ const result = BigInt(intPart) * BigInt(10 ** 8) + BigInt(fracPart || "0");
37
+ return result.toString();
38
+ }
39
+ export const DecimalMultiplierButtons = ({ value, onChange, disabled = false }) => {
40
+ const multiplyBy1e18 = useCallback(() => {
41
+ if (!value)
42
+ return;
43
+ const base = normalizeToBase(value);
44
+ onChange(parseEther(base).toString());
45
+ }, [onChange, value]);
46
+ const multiplyBy1e8 = useCallback(() => {
47
+ if (!value)
48
+ return;
49
+ const base = normalizeToBase(value);
50
+ onChange(multiplyValueBy1e8(base));
51
+ }, [onChange, value]);
52
+ const buttonClass = [
53
+ "h-full rounded px-2 py-0.5 text-xs font-medium transition-colors",
54
+ "text-sui-base-content/55 hover:text-sui-base-content hover:bg-sui-primary-subtle",
55
+ disabled ? "cursor-not-allowed opacity-40" : "cursor-pointer",
56
+ ].join(" ");
57
+ return (_jsxs("div", { className: "flex items-center gap-0", children: [_jsx(Tooltip, { content: "Multiply by 1e8 (tinybars)", position: "top", children: _jsx("button", { className: buttonClass, onClick: multiplyBy1e8, disabled: disabled, type: "button", children: "\u00D71e8" }) }), _jsx("span", { className: "text-sui-base-content/40 text-xs px-0.5 select-none", "aria-hidden": true, children: "|" }), _jsx(Tooltip, { content: "Multiply by 1e18 (wei)", position: "top", children: _jsx("button", { className: buttonClass, onClick: multiplyBy1e18, disabled: disabled, type: "button", children: "\u00D71e18" }) })] }));
58
+ };
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { IntegerVariant, isValidInteger } from "../../utils/inputs";
4
+ import { BaseInput } from "@scaffold-hbar-ui/components";
5
+ import { DecimalMultiplierButtons } from "./DecimalMultiplierButtons";
6
+ export const IntegerInput = ({ value, onChange, name, placeholder, disabled, variant = IntegerVariant.UINT256, }) => {
7
+ const [inputError, setInputError] = useState(false);
8
+ useEffect(() => {
9
+ if (isValidInteger(variant, value)) {
10
+ setInputError(false);
11
+ }
12
+ else {
13
+ setInputError(true);
14
+ }
15
+ }, [value, variant]);
16
+ return (_jsx(BaseInput, { name: name, value: value, placeholder: placeholder, error: inputError, onChange: onChange, disabled: disabled, suffix: !inputError && (_jsx(DecimalMultiplierButtons, { value: value, onChange: onChange, disabled: disabled })) }));
17
+ };
@@ -0,0 +1,4 @@
1
+ export { Bytes32Input } from "./Bytes32Input";
2
+ export { BytesInput } from "./BytesInput";
3
+ export { DecimalMultiplierButtons } from "./DecimalMultiplierButtons";
4
+ export { IntegerInput } from "./IntegerInput";
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from "react";
3
+ const ContractConfigContext = createContext(undefined);
4
+ export const ContractConfigProvider = ({ children, config }) => {
5
+ return _jsx(ContractConfigContext.Provider, { value: config, children: children });
6
+ };
7
+ export const useContractConfig = () => {
8
+ const context = useContext(ContractConfigContext);
9
+ if (context === undefined) {
10
+ throw new Error("useContractConfig must be used within a ContractConfigProvider");
11
+ }
12
+ return context;
13
+ };