UNPKG

@suiware/kit

Version:

Opinionated React components and hooks for building Sui dApps.

418 lines (408 loc) 12.3 kB
'use strict'; var utils = require('@mysten/sui/utils'); var debounce = require('lodash.debounce'); var react = require('react'); var jsxRuntime = require('react/jsx-runtime'); var themes = require('@radix-ui/themes'); var dappKit = require('@mysten/dapp-kit'); var BigNumber = require('bignumber.js'); var lucideReact = require('lucide-react'); var faucet = require('@mysten/sui/faucet'); var reactQuery = require('@tanstack/react-query'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var debounce__default = /*#__PURE__*/_interopDefault(debounce); var BigNumber__default = /*#__PURE__*/_interopDefault(BigNumber); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/helpers/suins.ts var resolveSuinsName = (suinsClient, name) => __async(null, null, function* () { const nameRecord = yield suinsClient.getNameRecord(name); return (nameRecord == null ? void 0 : nameRecord.targetAddress) || null; }); var DEBOUNCE_DELAY = 500; var AddressInput = ({ value, onChange, placeholder, className = "", disabled = false, suinsClient }) => { const [inputValue, setInputValue] = react.useState(value); const [error, setError] = react.useState(null); const debouncedResolve = react.useCallback( debounce__default.default((name) => __async(null, null, function* () { if (!suinsClient) { setError( "SuinsClient is not available. Pass it through component props to make SuiNS name resolving work." ); return; } try { const resolvedAddress = yield resolveSuinsName(suinsClient, name); if (resolvedAddress) { setError(null); onChange(resolvedAddress); } else { setError("Invalid SuiNS name"); } } catch (err) { setError("Failed to resolve SuiNS name"); } }), DEBOUNCE_DELAY), [suinsClient, onChange] ); const handleChange = (e) => __async(null, null, function* () { const newValue = e.target.value.trim(); setInputValue(newValue); if (newValue.endsWith(".sui") || newValue.startsWith("@")) { debouncedResolve(newValue); return; } if (newValue && !utils.isValidSuiAddress(newValue)) { setError("Invalid Sui address"); onChange(newValue); return; } setError(null); onChange(newValue); }); react.useEffect(() => { setInputValue(value); }, [value]); react.useEffect(() => { return () => { debouncedResolve.cancel(); }; }, [debouncedResolve]); return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `sk-address-input ${className}`, children: [ /* @__PURE__ */ jsxRuntime.jsx( "input", { type: "text", value: inputValue, onChange: handleChange, placeholder: placeholder || "Enter Sui address" + (suinsClient != null ? " or SuiNS name" : ""), disabled, className: error ? "error" : "" } ), error && /* @__PURE__ */ jsxRuntime.jsx("span", { children: error }) ] }); }; var AddressInput_default = AddressInput; var AmountInput = ({ value, onChange, placeholder, className = "", disabled = false }) => { const [inputValue, setInputValue] = react.useState(value); const [error, setError] = react.useState(null); const validateAmount = (amount) => { if (!amount) return true; if (!/^\d*\.?\d*$/.test(amount)) { return false; } const numericValue = parseFloat(amount); if (isNaN(numericValue)) { return false; } if (numericValue < 0) { return false; } return true; }; const handleChange = (e) => { const newValue = e.target.value.trim(); setInputValue(newValue); if (!newValue) { setError(null); onChange(newValue); return; } if (!validateAmount(newValue)) { setError("Invalid amount"); onChange(newValue); return; } setError(null); onChange(newValue); }; react.useEffect(() => { setInputValue(value); }, [value]); return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `sk-amount-input ${className}`, children: [ /* @__PURE__ */ jsxRuntime.jsx( "input", { type: "text", value: inputValue, onChange: handleChange, placeholder: placeholder || "Enter SUI amount", disabled, className: `${error ? "error" : ""} ${className}` } ), error && /* @__PURE__ */ jsxRuntime.jsx("span", { children: error }) ] }); }; var AmountInput_default = AmountInput; var formatAmount = (amount) => { if (amount == null) { return void 0; } let bn = new BigNumber__default.default(amount.toString()); bn = bn.shiftedBy(-9); return bn.decimalPlaces(2, BigNumber__default.default.ROUND_DOWN).toFormat(2); }; // src/hooks/useBalance.tsx var DEFAULT_REFETCH_INTERVAL = 3e3; var useBalance = ({ autoRefetch, autoRefetchInterval } = {}) => { const currentAccount = dappKit.useCurrentAccount(); const { data, refetch, error } = dappKit.useSuiClientQuery("getBalance", { owner: currentAccount == null ? void 0 : currentAccount.address }); react.useEffect(() => { if (autoRefetch == null || autoRefetch === false) { return; } const interval = setInterval( () => { if (currentAccount == null || !autoRefetch) { clearInterval(interval); return; } refetch(); }, autoRefetch && autoRefetchInterval != null ? autoRefetchInterval : DEFAULT_REFETCH_INTERVAL ); return () => { clearTimeout(interval); }; }, [refetch, autoRefetch, autoRefetchInterval, currentAccount]); return { balance: data ? formatAmount(data.totalBalance) : void 0, error, refetch: () => __async(null, null, function* () { refetch(); }) }; }; var useBalance_default = useBalance; var Balance = () => { const { balance } = useBalance_default({ autoRefetch: true }); if (balance == null) { return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {}); } return /* @__PURE__ */ jsxRuntime.jsxs(themes.Badge, { variant: "surface", color: "green", className: "sk-balance-badge", children: [ balance, " SUI" ] }); }; var Balance_default = Balance; var fundAddress = (address, network) => __async(null, null, function* () { return yield faucet.requestSuiFromFaucetV2({ host: faucet.getFaucetHost(network), recipient: address }); }); var getTestnetFaucetLink = (address) => { return `https://faucet.sui.io/?address=${address}`; }; // src/hooks/useFaucet.tsx var useFaucet = ({ onError, onSuccess }) => { const ctx = dappKit.useSuiClientContext(); const currentAccount = dappKit.useCurrentAccount(); const fund = (address) => __async(null, null, function* () { if (!["localnet", "devnet", "testnet"].includes(ctx.network)) { onError != null && onError(null, `The ${ctx.network} network does not have a faucet`); return; } const fundedAddress = address == null ? currentAccount == null ? void 0 : currentAccount.address : void 0; if (fundedAddress == null) { onError != null && onError(null, "Please connect your wallet first"); return; } if (ctx.network === "testnet") { window.open(getTestnetFaucetLink(fundedAddress), "_blank"); return; } try { const { status } = yield fundAddress( fundedAddress, ctx.network ); if (status !== "Success") { onError != null && onError( new Error(status.Failure.internal), "Cannot fund the address on this network at the moment" ); } } catch (e) { onError != null && onError(e, "Cannot fund the address"); return; } onSuccess != null && onSuccess( `The ${utils.formatAddress(fundedAddress)} address has been funded successfully` ); }); return { fund }; }; var useFaucet_default = useFaucet; var Faucet = (props) => { const { onError, onSuccess } = props; const { fund } = useFaucet_default({ onError, onSuccess }); return /* @__PURE__ */ jsxRuntime.jsxs( themes.Button, { variant: "surface", className: "sk-faucet-button", onClick: () => fund(), children: [ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.HandCoinsIcon, {}), "Faucet" ] } ); }; var Faucet_default = Faucet; // src/helpers/formatNetworkType.ts var formatNetworkType = (machineName) => { if (machineName.startsWith("sui:")) { return machineName.substring(4); } return machineName; }; // src/hooks/useNetworkType.tsx var DEFAULT_REFETCH_INTERVAL2 = 3e3; var useNetworkType = ({ autoSync, autoSyncInterval } = {}) => { const wallet = dappKit.useCurrentWallet(); const ctx = dappKit.useSuiClientContext(); const [networkType, setNetworkType] = react.useState(void 0); const synchronizeNetworkType = (wallet2, ctx2) => { var _a, _b, _c; if (!wallet2.isConnected) { setNetworkType(void 0); return; } const newNetwork = formatNetworkType( (_c = (_b = (_a = wallet2.currentWallet) == null ? void 0 : _a.accounts) == null ? void 0 : _b[0].chains) == null ? void 0 : _c[0] ); setNetworkType(newNetwork); if (newNetwork != null) { ctx2.selectNetwork(newNetwork); } }; react.useEffect(() => { synchronizeNetworkType(wallet, ctx); if (autoSync == null || autoSync === false) { return; } const interval = setInterval( () => { if (!wallet.isConnected || !autoSync) { setNetworkType(void 0); clearInterval(interval); return; } synchronizeNetworkType(wallet, ctx); }, autoSync && autoSyncInterval != null ? autoSyncInterval : DEFAULT_REFETCH_INTERVAL2 ); return () => { clearTimeout(interval); }; }, [autoSync, autoSyncInterval, wallet, ctx]); return { networkType, synchronize: () => synchronizeNetworkType(wallet, ctx) }; }; var useNetworkType_default = useNetworkType; var NetworkType = () => { const { networkType } = useNetworkType_default({ autoSync: true }); let color = "amber"; if (networkType == null) { color = "tomato"; } else if (networkType === "mainnet") { color = "green"; } return /* @__PURE__ */ jsxRuntime.jsx(themes.Badge, { color, className: "sk-network-badge", children: networkType || "disconnected" }); }; var NetworkType_default = NetworkType; var queryClient = new reactQuery.QueryClient(); var SuiProvider = ({ children, customQueryClient, customNetworkConfig, defaultNetwork, walletAutoConnect, walletStashedName, walletSlushName, themeSettings }) => { return /* @__PURE__ */ jsxRuntime.jsx(reactQuery.QueryClientProvider, { client: customQueryClient || queryClient, children: /* @__PURE__ */ jsxRuntime.jsx( dappKit.SuiClientProvider, { networks: customNetworkConfig, defaultNetwork, children: /* @__PURE__ */ jsxRuntime.jsx( dappKit.WalletProvider, { autoConnect: walletAutoConnect, theme: themeSettings, slushWallet: { name: walletSlushName || walletStashedName || "Sui Wallet" }, children } ) } ) }); }; var SuiProvider_default = SuiProvider; exports.AddressInput = AddressInput_default; exports.AmountInput = AmountInput_default; exports.Balance = Balance_default; exports.Faucet = Faucet_default; exports.NetworkType = NetworkType_default; exports.SuiProvider = SuiProvider_default; exports.useBalance = useBalance_default; exports.useFaucet = useFaucet_default; exports.useNetworkType = useNetworkType_default; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map