@suiware/kit
Version:
Opinionated React components and hooks for building Sui dApps.
418 lines (408 loc) • 12.3 kB
JavaScript
;
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