Merge pull request #201 from cosmos/feat/add-generated-theme
Add new theme, tweak home and errors
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import copy from "copy-to-clipboard";
|
||||
import { Copy } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { Badge } from "./ui/badge";
|
||||
import { useToast } from "./ui/use-toast";
|
||||
|
||||
interface BadgeWithCopyProps {
|
||||
readonly name: string;
|
||||
@@ -9,13 +9,11 @@ interface BadgeWithCopyProps {
|
||||
}
|
||||
|
||||
export default function BadgeWithCopy({ name, toCopy }: BadgeWithCopyProps) {
|
||||
const { toast } = useToast();
|
||||
|
||||
return (
|
||||
<Badge
|
||||
onClick={() => {
|
||||
onClick={async () => {
|
||||
copy(toCopy);
|
||||
toast({ description: `Copied ${name} to clipboard` });
|
||||
toast(`Copied ${name} to clipboard`, { description: toCopy });
|
||||
}}
|
||||
className="max-w-md self-start truncate hover:cursor-pointer"
|
||||
>
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function Header() {
|
||||
const { chain } = useChains();
|
||||
|
||||
return (
|
||||
<header className="flex flex-row items-center justify-between gap-4 bg-fuchsia-900 px-3">
|
||||
<header className="flex w-full flex-row items-center justify-between gap-4 bg-fuchsia-900 px-3">
|
||||
<ChainConnect />
|
||||
<Link
|
||||
href={
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { toastError } from "@/lib/utils";
|
||||
import { Coin } from "@cosmjs/amino";
|
||||
import { StargateClient } from "@cosmjs/stargate";
|
||||
import { Dispatch, SetStateAction, useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useChains } from "../../../context/ChainsContext";
|
||||
import BalancePill from "./BalancePill";
|
||||
|
||||
interface BalancesListProps {
|
||||
readonly walletAddress: string;
|
||||
readonly setError: Dispatch<SetStateAction<string>>;
|
||||
}
|
||||
|
||||
export default function BalancesList({ walletAddress, setError }: BalancesListProps) {
|
||||
export default function BalancesList({ walletAddress }: BalancesListProps) {
|
||||
const { chain } = useChains();
|
||||
const [balances, setBalances] = useState<readonly Coin[]>([]);
|
||||
|
||||
@@ -24,12 +24,15 @@ export default function BalancesList({ walletAddress, setError }: BalancesListPr
|
||||
const client = await StargateClient.connect(chain.nodeAddress);
|
||||
const newBalances = await client.getAllBalances(walletAddress);
|
||||
setBalances(newBalances);
|
||||
} catch (e: unknown) {
|
||||
setError(e instanceof Error ? e.message : "Failed to get balances");
|
||||
console.error("Get balances error:", e);
|
||||
} catch (e) {
|
||||
console.error("Failed to get balances:", e);
|
||||
toastError({
|
||||
description: "Failed to get balances",
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
}
|
||||
})();
|
||||
}, [chain.nodeAddress, setError, walletAddress]);
|
||||
}, [chain.nodeAddress, walletAddress]);
|
||||
|
||||
return balances.length ? (
|
||||
<Card className="bg-fuchsia-850 w-full max-w-md border-transparent">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toastError } from "@/lib/utils";
|
||||
import { LoadingStates, WalletInfo, WalletType } from "@/types/signing";
|
||||
import { makeCosmoshubPath } from "@cosmjs/amino";
|
||||
import { toBase64 } from "@cosmjs/encoding";
|
||||
@@ -16,20 +17,17 @@ interface ButtonConnectWalletProps {
|
||||
WalletInfo | null | undefined,
|
||||
Dispatch<SetStateAction<WalletInfo | null | undefined>>,
|
||||
];
|
||||
readonly setError: Dispatch<SetStateAction<string>>;
|
||||
}
|
||||
|
||||
export default function ButtonConnectWallet({
|
||||
walletType,
|
||||
walletInfoState: [walletInfo, setWalletInfo],
|
||||
setError,
|
||||
}: ButtonConnectWalletProps) {
|
||||
const { chain } = useChains();
|
||||
const [loading, setLoading] = useState<LoadingStates>({});
|
||||
|
||||
const connectKeplr = useCallback(async () => {
|
||||
try {
|
||||
setError("");
|
||||
setLoading((oldLoading) => ({ ...oldLoading, keplr: true }));
|
||||
|
||||
await window.keplr.enable(chain.chainId);
|
||||
@@ -44,12 +42,16 @@ export default function ButtonConnectWallet({
|
||||
|
||||
setWalletInfo({ type: "Keplr", address, pubKey });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError(getConnectError(e));
|
||||
const connectError = getConnectError(e);
|
||||
console.error(connectError, e);
|
||||
toastError({
|
||||
description: connectError,
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
} finally {
|
||||
setLoading((newLoading) => ({ ...newLoading, keplr: false }));
|
||||
}
|
||||
}, [chain.chainId, setError, setWalletInfo]);
|
||||
}, [chain.chainId, setWalletInfo]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!walletInfo?.address) {
|
||||
@@ -67,7 +69,6 @@ export default function ButtonConnectWallet({
|
||||
|
||||
const connectLedger = async () => {
|
||||
try {
|
||||
setError("");
|
||||
setLoading((newLoading) => ({ ...newLoading, ledger: true }));
|
||||
|
||||
const ledgerTransport = await TransportWebUSB.create(120000, 120000);
|
||||
@@ -81,8 +82,12 @@ export default function ButtonConnectWallet({
|
||||
|
||||
setWalletInfo({ type: "Ledger", address, pubKey });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError(getConnectError(e));
|
||||
const connectError = getConnectError(e);
|
||||
console.error(connectError, e);
|
||||
toastError({
|
||||
description: connectError,
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
} finally {
|
||||
setLoading((newLoading) => ({ ...newLoading, ledger: false }));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import BadgeWithCopy from "@/components/BadgeWithCopy";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { explorerLinkAccount } from "@/lib/displayHelpers";
|
||||
import { WalletInfo } from "@/types/signing";
|
||||
import { AlertCircle, Unplug } from "lucide-react";
|
||||
import { Unplug } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
import { useChains } from "../../../context/ChainsContext";
|
||||
@@ -16,7 +15,6 @@ export default function AccountView() {
|
||||
|
||||
const walletInfoState = useState<WalletInfo | null>();
|
||||
const [walletInfo, setWalletInfo] = walletInfoState;
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const explorerLink =
|
||||
explorerLinkAccount(chain.explorerLinks.account, walletInfo?.address || "") || "";
|
||||
@@ -43,7 +41,7 @@ export default function AccountView() {
|
||||
<div className="flex flex-col gap-4">
|
||||
<BadgeWithCopy name="address" toCopy={walletInfo.address} />
|
||||
{explorerLink ? (
|
||||
<Button asChild className="self-center">
|
||||
<Button asChild variant="secondary" className="self-center">
|
||||
<a href={explorerLink} target="_blank">
|
||||
View in explorer
|
||||
</a>
|
||||
@@ -57,15 +55,9 @@ export default function AccountView() {
|
||||
</CardContent>
|
||||
) : null}
|
||||
<CardFooter className="my-8 flex w-full flex-col gap-4 p-0">
|
||||
{error ? (
|
||||
<Alert variant="destructive">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertTitle>Error</AlertTitle>
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
{walletInfo?.type ? (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
setWalletInfo(null);
|
||||
}}
|
||||
@@ -75,26 +67,14 @@ export default function AccountView() {
|
||||
</Button>
|
||||
) : (
|
||||
<div className="flex w-full flex-col gap-4">
|
||||
<ButtonConnectWallet
|
||||
walletType="Keplr"
|
||||
walletInfoState={walletInfoState}
|
||||
setError={setError}
|
||||
/>
|
||||
<ButtonConnectWallet
|
||||
walletType="Ledger"
|
||||
walletInfoState={walletInfoState}
|
||||
setError={setError}
|
||||
/>
|
||||
<ButtonConnectWallet walletType="Keplr" walletInfoState={walletInfoState} />
|
||||
<ButtonConnectWallet walletType="Ledger" walletInfoState={walletInfoState} />
|
||||
</div>
|
||||
)}
|
||||
</CardFooter>
|
||||
</Card>
|
||||
{walletInfo?.address ? (
|
||||
<BalancesList
|
||||
key={walletInfo.address}
|
||||
walletAddress={walletInfo.address}
|
||||
setError={setError}
|
||||
/>
|
||||
<BalancesList key={walletInfo.address} walletAddress={walletInfo.address} />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { loadValidators } from "@/context/ChainsContext/helpers";
|
||||
import { toastError, toastSuccess } from "@/lib/utils";
|
||||
import { EncodeObject } from "@cosmjs/proto-signing";
|
||||
import { Account, calculateFee } from "@cosmjs/stargate";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import { NextRouter, withRouter } from "next/router";
|
||||
import { useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { useChains } from "../../../context/ChainsContext";
|
||||
import { requestJson } from "../../../lib/request";
|
||||
import { exportMsgToJson, gasOfTx } from "../../../lib/txMsgHelpers";
|
||||
@@ -39,7 +41,6 @@ const CreateTxForm = ({ router, senderAddress, accountOnChain }: CreateTxFormPro
|
||||
const [memo, setMemo] = useState("");
|
||||
const [gasLimit, setGasLimit] = useState(gasOfTx([]));
|
||||
const [gasLimitError, setGasLimitError] = useState("");
|
||||
const [showCreateTxError, setShowTxError] = useState(false);
|
||||
|
||||
const addMsgType = (newMsgType: MsgTypeUrl) => {
|
||||
setMsgKeys((oldMsgKeys) => [...oldMsgKeys, crypto.randomUUID()]);
|
||||
@@ -59,9 +60,9 @@ const CreateTxForm = ({ router, senderAddress, accountOnChain }: CreateTxFormPro
|
||||
};
|
||||
|
||||
const createTx = async () => {
|
||||
try {
|
||||
setShowTxError(false);
|
||||
const loadingToastId = toast.loading("Creating transaction");
|
||||
|
||||
try {
|
||||
assert(typeof accountOnChain.accountNumber === "number", "accountNumber missing");
|
||||
assert(msgGetters.current.length, "form filled incorrectly");
|
||||
|
||||
@@ -90,13 +91,17 @@ const CreateTxForm = ({ router, senderAddress, accountOnChain }: CreateTxFormPro
|
||||
const { transactionID } = await requestJson("/api/transaction", {
|
||||
body: { dataJSON: JSON.stringify(tx) },
|
||||
});
|
||||
|
||||
toastSuccess("Transaction created with ID", transactionID);
|
||||
router.push(`/${chain.registryName}/${senderAddress}/transaction/${transactionID}`);
|
||||
} catch (error) {
|
||||
console.error("Creat transaction error:", error);
|
||||
setShowTxError(true);
|
||||
} catch (e) {
|
||||
console.error("Failed to create transaction:", e);
|
||||
toastError({
|
||||
description: "Failed to create transaction",
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
} finally {
|
||||
setProcessing(false);
|
||||
toast.dismiss(loadingToastId);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -252,13 +257,6 @@ const CreateTxForm = ({ router, senderAddress, accountOnChain }: CreateTxFormPro
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{showCreateTxError ? (
|
||||
<StackableContainer lessMargin lessPadding>
|
||||
<p className="multisig-error">
|
||||
Error when creating the transaction. See console for more details.
|
||||
</p>
|
||||
</StackableContainer>
|
||||
) : null}
|
||||
<Button
|
||||
label="Create Transaction"
|
||||
onClick={createTx}
|
||||
|
||||
@@ -1,96 +1,100 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { ChainInfo } from "@/context/ChainsContext/types";
|
||||
import { StargateClient } from "@cosmjs/stargate";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import Link from "next/link";
|
||||
import { NextRouter, withRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { useChains } from "../../context/ChainsContext";
|
||||
import { exampleAddress } from "../../lib/displayHelpers";
|
||||
import { getMultisigAccount } from "../../lib/multisigHelpers";
|
||||
import Button from "../inputs/Button";
|
||||
import Input from "../inputs/Input";
|
||||
import StackableContainer from "../layout/StackableContainer";
|
||||
|
||||
interface Props {
|
||||
const existsMultisigAccount = async (chain: ChainInfo, address: string) => {
|
||||
try {
|
||||
const client = await StargateClient.connect(chain.nodeAddress);
|
||||
const [, account] = await getMultisigAccount(address, chain.addressPrefix, client);
|
||||
return account !== null;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
interface FindMultisigFormProps {
|
||||
router: NextRouter;
|
||||
}
|
||||
|
||||
const FindMultisigForm = (props: Props) => {
|
||||
const FindMultisigForm = ({ router }: FindMultisigFormProps) => {
|
||||
const { chain } = useChains();
|
||||
const [address, setAddress] = useState("");
|
||||
const [multisigError, setMultisigError] = useState("");
|
||||
|
||||
const handleSearch = () => {
|
||||
props.router.push(`/${chain.registryName}/${address}`);
|
||||
};
|
||||
const findMultisigSchema = z.object({
|
||||
address: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "Required")
|
||||
.startsWith(chain.addressPrefix, `Invalid prefix for ${chain.chainDisplayName}`)
|
||||
.refine(async (address) => await existsMultisigAccount(chain, address), {
|
||||
message: "Multisig not found",
|
||||
}),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
(async function () {
|
||||
if (!address) {
|
||||
setMultisigError("");
|
||||
return;
|
||||
}
|
||||
const findMultisigForm = useForm<z.infer<typeof findMultisigSchema>>({
|
||||
resolver: zodResolver(findMultisigSchema),
|
||||
defaultValues: { address: "" },
|
||||
});
|
||||
|
||||
try {
|
||||
const client = await StargateClient.connect(chain.nodeAddress);
|
||||
await getMultisigAccount(address, chain.addressPrefix, client);
|
||||
setMultisigError("");
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
setMultisigError(error.message);
|
||||
} else {
|
||||
setMultisigError("Multisig error");
|
||||
}
|
||||
console.error("Multisig error:", error);
|
||||
}
|
||||
})();
|
||||
}, [address, chain.addressPrefix, chain.nodeAddress]);
|
||||
const submitFindMultisig = ({ address }: z.infer<typeof findMultisigSchema>) =>
|
||||
router.push(`/${chain.registryName}/${address}`);
|
||||
|
||||
return (
|
||||
<StackableContainer>
|
||||
<StackableContainer lessPadding>
|
||||
<p>
|
||||
Already have a multisig address? Enter it below. If it’s a valid address, you will be able
|
||||
to view its transactions and create new ones.
|
||||
</p>
|
||||
</StackableContainer>
|
||||
<StackableContainer lessPadding lessMargin>
|
||||
<Input
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAddress(e.target.value)}
|
||||
value={address}
|
||||
label="Multisig Address"
|
||||
name="address"
|
||||
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
|
||||
error={multisigError}
|
||||
/>
|
||||
<Button
|
||||
label="Use this Multisig"
|
||||
onClick={handleSearch}
|
||||
primary
|
||||
disabled={!address || !!multisigError}
|
||||
/>
|
||||
</StackableContainer>
|
||||
<StackableContainer lessPadding>
|
||||
<p className="create-help">Don't have a multisig?</p>
|
||||
<Button
|
||||
label="Create New Multisig"
|
||||
onClick={() => props.router.push(`${chain.registryName}/create`)}
|
||||
/>
|
||||
</StackableContainer>
|
||||
<style jsx>{`
|
||||
.multisig-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.error {
|
||||
color: coral;
|
||||
font-size: 0.8em;
|
||||
text-align: left;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.create-help {
|
||||
text-align: center;
|
||||
}
|
||||
`}</style>
|
||||
</StackableContainer>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Already have a {chain.chainDisplayName || "Cosmos Hub"} multisig?</CardTitle>
|
||||
<CardDescription>
|
||||
Enter its address below to view its transactions and create new ones.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...findMultisigForm}>
|
||||
<form onSubmit={findMultisigForm.handleSubmit(submitFindMultisig)} className="space-y-8">
|
||||
<FormField
|
||||
control={findMultisigForm.control}
|
||||
name="address"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Multisig address</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={`E.g. "${exampleAddress(0, chain.addressPrefix)}"`}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
<Button type="submit">Use this multisig</Button>
|
||||
<Button asChild variant="link" className="p-0 text-secondary">
|
||||
<Link href={chain.registryName ? `/${chain.registryName}/create` : ""} className="">
|
||||
I don't have a multisig
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toastError } from "@/lib/utils";
|
||||
import { StargateClient } from "@cosmjs/stargate";
|
||||
import { NextRouter, withRouter } from "next/router";
|
||||
import { useState } from "react";
|
||||
@@ -67,20 +68,20 @@ const MultiSigForm = (props: Props) => {
|
||||
return accountOnChain.pubkey.value;
|
||||
};
|
||||
|
||||
const handleKeyBlur = async (index: number, e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const handleKeyBlur = async (index: number, { target }: React.ChangeEvent<HTMLInputElement>) => {
|
||||
try {
|
||||
const tempPubkeys = [...pubkeys];
|
||||
let pubkey;
|
||||
// use pubkey
|
||||
console.log(tempPubkeys[index]);
|
||||
if (tempPubkeys[index].isPubkey) {
|
||||
pubkey = e.target.value;
|
||||
pubkey = target.value;
|
||||
if (pubkey.length !== 44) {
|
||||
throw new Error("Invalid Secp256k1 pubkey");
|
||||
}
|
||||
} else {
|
||||
// use address to fetch pubkey
|
||||
const address = e.target.value;
|
||||
const address = target.value;
|
||||
if (address.length > 0) {
|
||||
pubkey = await getPubkeyFromNode(address);
|
||||
}
|
||||
@@ -89,11 +90,10 @@ const MultiSigForm = (props: Props) => {
|
||||
tempPubkeys[index].compressedPubkey = pubkey;
|
||||
tempPubkeys[index].keyError = "";
|
||||
setPubkeys(tempPubkeys);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
} catch (e) {
|
||||
console.error("Invalid address or pubkey", e);
|
||||
const tempPubkeys = [...pubkeys];
|
||||
tempPubkeys[index].keyError = error.message;
|
||||
tempPubkeys[index].keyError = e instanceof Error ? e.message : "Invalid address or pubkey";
|
||||
setPubkeys(tempPubkeys);
|
||||
}
|
||||
};
|
||||
@@ -110,8 +110,12 @@ const MultiSigForm = (props: Props) => {
|
||||
chain.chainId,
|
||||
);
|
||||
props.router.push(`/${chain.registryName}/${multisigAddress}`);
|
||||
} catch (error) {
|
||||
console.log("Failed to creat multisig: ", error);
|
||||
} catch (e) {
|
||||
console.error("Failed to create multisig:", e);
|
||||
toastError({
|
||||
description: "Failed to create multisig",
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { toastError, toastSuccess } from "@/lib/utils";
|
||||
import { LoadingStates, SigningStatus } from "@/types/signing";
|
||||
import { MultisigThresholdPubkey, makeCosmoshubPath } from "@cosmjs/amino";
|
||||
import { createWasmAminoConverters, wasmTypes } from "@cosmjs/cosmwasm-stargate";
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import TransportWebUSB from "@ledgerhq/hw-transport-webusb";
|
||||
import { useCallback, useLayoutEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { useChains } from "../../context/ChainsContext";
|
||||
import { getConnectError } from "../../lib/errorHelpers";
|
||||
import { requestJson } from "../../lib/request";
|
||||
@@ -34,8 +36,6 @@ const TransactionSigning = (props: TransactionSigningProps) => {
|
||||
|
||||
const { chain } = useChains();
|
||||
const [walletAccount, setWalletAccount] = useState<WalletAccount>();
|
||||
const [sigError, setSigError] = useState("");
|
||||
const [connectError, setConnectError] = useState("");
|
||||
const [signing, setSigning] = useState<SigningStatus>("not_signed");
|
||||
const [walletType, setWalletType] = useState<"Keplr" | "Ledger">();
|
||||
const [ledgerSigner, setLedgerSigner] = useState({});
|
||||
@@ -68,10 +68,13 @@ const TransactionSigning = (props: TransactionSigningProps) => {
|
||||
}
|
||||
|
||||
setWalletType("Keplr");
|
||||
setConnectError("");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setConnectError(getConnectError(e));
|
||||
const connectError = getConnectError(e);
|
||||
console.error(connectError, e);
|
||||
toastError({
|
||||
description: connectError,
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
} finally {
|
||||
setLoading((newLoading) => ({ ...newLoading, keplr: false }));
|
||||
}
|
||||
@@ -124,16 +127,21 @@ const TransactionSigning = (props: TransactionSigningProps) => {
|
||||
|
||||
setLedgerSigner(offlineSigner);
|
||||
setWalletType("Ledger");
|
||||
setConnectError("");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setConnectError(getConnectError(e));
|
||||
const connectError = getConnectError(e);
|
||||
console.error(connectError, e);
|
||||
toastError({
|
||||
description: connectError,
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
} finally {
|
||||
setLoading((newLoading) => ({ ...newLoading, ledger: false }));
|
||||
}
|
||||
};
|
||||
|
||||
const signTransaction = async () => {
|
||||
const loadingToastId = toast.loading("Signing transaction");
|
||||
|
||||
try {
|
||||
setLoading((newLoading) => ({ ...newLoading, signing: true }));
|
||||
|
||||
@@ -172,21 +180,27 @@ const TransactionSigning = (props: TransactionSigningProps) => {
|
||||
);
|
||||
|
||||
if (prevSigMatch > -1) {
|
||||
setSigError("This account has already signed.");
|
||||
} else {
|
||||
const signature = {
|
||||
bodyBytes: bases64EncodedBodyBytes,
|
||||
signature: bases64EncodedSignature,
|
||||
address: signerAddress,
|
||||
};
|
||||
await requestJson(`/api/transaction/${props.transactionID}/signature`, { body: signature });
|
||||
props.addSignature(signature);
|
||||
setSigning("signed");
|
||||
throw new Error("This account has already signed");
|
||||
}
|
||||
|
||||
const signature = {
|
||||
bodyBytes: bases64EncodedBodyBytes,
|
||||
signature: bases64EncodedSignature,
|
||||
address: signerAddress,
|
||||
};
|
||||
await requestJson(`/api/transaction/${props.transactionID}/signature`, { body: signature });
|
||||
toastSuccess("Transaction signed by", signerAddress);
|
||||
props.addSignature(signature);
|
||||
setSigning("signed");
|
||||
} catch (e) {
|
||||
console.log("signing err: ", e);
|
||||
console.error("Failed to sign the tx:", e);
|
||||
toastError({
|
||||
description: "Failed to sign the tx",
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
} finally {
|
||||
setLoading((newLoading) => ({ ...newLoading, signing: false }));
|
||||
toast.dismiss(loadingToastId);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -242,20 +256,6 @@ const TransactionSigning = (props: TransactionSigningProps) => {
|
||||
)}
|
||||
</>
|
||||
) : null}
|
||||
{sigError ? (
|
||||
<StackableContainer lessPadding lessRadius lessMargin>
|
||||
<div className="signature-error">
|
||||
<p>This account has already signed this transaction</p>
|
||||
</div>
|
||||
</StackableContainer>
|
||||
) : null}
|
||||
{connectError ? (
|
||||
<StackableContainer lessPadding lessRadius lessMargin>
|
||||
<div className="signature-error">
|
||||
<p>{connectError}</p>
|
||||
</div>
|
||||
</StackableContainer>
|
||||
) : null}
|
||||
</StackableContainer>
|
||||
<style jsx>{`
|
||||
p {
|
||||
@@ -273,15 +273,6 @@ const TransactionSigning = (props: TransactionSigningProps) => {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.signature-error p {
|
||||
max-width: 550px;
|
||||
color: red;
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.signature-error p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.confirmation {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -11,7 +11,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useTheme } from "next-themes"
|
||||
import { Toaster as Sonner } from "sonner"
|
||||
|
||||
type ToasterProps = React.ComponentProps<typeof Sonner>
|
||||
|
||||
const Toaster = ({ ...props }: ToasterProps) => {
|
||||
const { theme = "system" } = useTheme()
|
||||
|
||||
return (
|
||||
<Sonner
|
||||
theme={theme as ToasterProps["theme"]}
|
||||
className="toaster group"
|
||||
toastOptions={{
|
||||
classNames: {
|
||||
toast:
|
||||
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
||||
description: "group-[.toast]:text-muted-foreground",
|
||||
actionButton:
|
||||
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
|
||||
cancelButton:
|
||||
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Toaster }
|
||||
@@ -9,7 +9,7 @@ const Switch = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SwitchPrimitives.Root
|
||||
className={cn(
|
||||
"peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
||||
"peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-muted data-[state=unchecked]:bg-input",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -17,7 +17,7 @@ const Switch = React.forwardRef<
|
||||
>
|
||||
<SwitchPrimitives.Thumb
|
||||
className={cn(
|
||||
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
|
||||
"pointer-events-none block h-5 w-5 rounded-full bg-primary shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
|
||||
)}
|
||||
/>
|
||||
</SwitchPrimitives.Root>
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
import * as React from "react"
|
||||
import * as ToastPrimitives from "@radix-ui/react-toast"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { X } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const ToastProvider = ToastPrimitives.Provider
|
||||
|
||||
const ToastViewport = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Viewport
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
|
||||
|
||||
const toastVariants = cva(
|
||||
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "border bg-background text-foreground",
|
||||
destructive:
|
||||
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const Toast = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
||||
VariantProps<typeof toastVariants>
|
||||
>(({ className, variant, ...props }, ref) => {
|
||||
return (
|
||||
<ToastPrimitives.Root
|
||||
ref={ref}
|
||||
className={cn(toastVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
Toast.displayName = ToastPrimitives.Root.displayName
|
||||
|
||||
const ToastAction = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Action>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Action
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ToastAction.displayName = ToastPrimitives.Action.displayName
|
||||
|
||||
const ToastClose = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Close>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Close
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
||||
className
|
||||
)}
|
||||
toast-close=""
|
||||
{...props}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</ToastPrimitives.Close>
|
||||
))
|
||||
ToastClose.displayName = ToastPrimitives.Close.displayName
|
||||
|
||||
const ToastTitle = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Title
|
||||
ref={ref}
|
||||
className={cn("text-sm font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ToastTitle.displayName = ToastPrimitives.Title.displayName
|
||||
|
||||
const ToastDescription = React.forwardRef<
|
||||
React.ElementRef<typeof ToastPrimitives.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm opacity-90", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
ToastDescription.displayName = ToastPrimitives.Description.displayName
|
||||
|
||||
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
|
||||
|
||||
type ToastActionElement = React.ReactElement<typeof ToastAction>
|
||||
|
||||
export {
|
||||
type ToastProps,
|
||||
type ToastActionElement,
|
||||
ToastProvider,
|
||||
ToastViewport,
|
||||
Toast,
|
||||
ToastTitle,
|
||||
ToastDescription,
|
||||
ToastClose,
|
||||
ToastAction,
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import {
|
||||
Toast,
|
||||
ToastClose,
|
||||
ToastDescription,
|
||||
ToastProvider,
|
||||
ToastTitle,
|
||||
ToastViewport,
|
||||
} from "@/components/ui/toast"
|
||||
import { useToast } from "@/components/ui/use-toast"
|
||||
|
||||
export function Toaster() {
|
||||
const { toasts } = useToast()
|
||||
|
||||
return (
|
||||
<ToastProvider >
|
||||
{toasts.map(function ({ id, title, description, action, ...props }) {
|
||||
return (
|
||||
<Toast key={id} {...props} className="bg-fuchsia-900" style={
|
||||
{
|
||||
"--accent": "0, 100%, 100%",
|
||||
"--border": "0, 100%, 100%",
|
||||
} as React.CSSProperties
|
||||
}>
|
||||
<div className="grid gap-1">
|
||||
{title && <ToastTitle>{title}</ToastTitle>}
|
||||
{description && (
|
||||
<ToastDescription>{description}</ToastDescription>
|
||||
)}
|
||||
</div>
|
||||
{action}
|
||||
<ToastClose />
|
||||
</Toast>
|
||||
)
|
||||
})}
|
||||
<ToastViewport />
|
||||
</ToastProvider>
|
||||
)
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
// Inspired by react-hot-toast library
|
||||
import * as React from "react"
|
||||
|
||||
import type {
|
||||
ToastActionElement,
|
||||
ToastProps,
|
||||
} from "@/components/ui/toast"
|
||||
|
||||
const TOAST_LIMIT = 1
|
||||
const TOAST_REMOVE_DELAY = 1000000
|
||||
|
||||
type ToasterToast = ToastProps & {
|
||||
id: string
|
||||
title?: React.ReactNode
|
||||
description?: React.ReactNode
|
||||
action?: ToastActionElement
|
||||
}
|
||||
|
||||
const actionTypes = {
|
||||
ADD_TOAST: "ADD_TOAST",
|
||||
UPDATE_TOAST: "UPDATE_TOAST",
|
||||
DISMISS_TOAST: "DISMISS_TOAST",
|
||||
REMOVE_TOAST: "REMOVE_TOAST",
|
||||
} as const
|
||||
|
||||
let count = 0
|
||||
|
||||
function genId() {
|
||||
count = (count + 1) % Number.MAX_VALUE
|
||||
return count.toString()
|
||||
}
|
||||
|
||||
type ActionType = typeof actionTypes
|
||||
|
||||
type Action =
|
||||
| {
|
||||
type: ActionType["ADD_TOAST"]
|
||||
toast: ToasterToast
|
||||
}
|
||||
| {
|
||||
type: ActionType["UPDATE_TOAST"]
|
||||
toast: Partial<ToasterToast>
|
||||
}
|
||||
| {
|
||||
type: ActionType["DISMISS_TOAST"]
|
||||
toastId?: ToasterToast["id"]
|
||||
}
|
||||
| {
|
||||
type: ActionType["REMOVE_TOAST"]
|
||||
toastId?: ToasterToast["id"]
|
||||
}
|
||||
|
||||
interface State {
|
||||
toasts: ToasterToast[]
|
||||
}
|
||||
|
||||
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
||||
|
||||
const addToRemoveQueue = (toastId: string) => {
|
||||
if (toastTimeouts.has(toastId)) {
|
||||
return
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
toastTimeouts.delete(toastId)
|
||||
dispatch({
|
||||
type: "REMOVE_TOAST",
|
||||
toastId: toastId,
|
||||
})
|
||||
}, TOAST_REMOVE_DELAY)
|
||||
|
||||
toastTimeouts.set(toastId, timeout)
|
||||
}
|
||||
|
||||
export const reducer = (state: State, action: Action): State => {
|
||||
switch (action.type) {
|
||||
case "ADD_TOAST":
|
||||
return {
|
||||
...state,
|
||||
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
||||
}
|
||||
|
||||
case "UPDATE_TOAST":
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
||||
),
|
||||
}
|
||||
|
||||
case "DISMISS_TOAST": {
|
||||
const { toastId } = action
|
||||
|
||||
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
||||
// but I'll keep it here for simplicity
|
||||
if (toastId) {
|
||||
addToRemoveQueue(toastId)
|
||||
} else {
|
||||
state.toasts.forEach((toast) => {
|
||||
addToRemoveQueue(toast.id)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.map((t) =>
|
||||
t.id === toastId || toastId === undefined
|
||||
? {
|
||||
...t,
|
||||
open: false,
|
||||
}
|
||||
: t
|
||||
),
|
||||
}
|
||||
}
|
||||
case "REMOVE_TOAST":
|
||||
if (action.toastId === undefined) {
|
||||
return {
|
||||
...state,
|
||||
toasts: [],
|
||||
}
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const listeners: Array<(state: State) => void> = []
|
||||
|
||||
let memoryState: State = { toasts: [] }
|
||||
|
||||
function dispatch(action: Action) {
|
||||
memoryState = reducer(memoryState, action)
|
||||
listeners.forEach((listener) => {
|
||||
listener(memoryState)
|
||||
})
|
||||
}
|
||||
|
||||
type Toast = Omit<ToasterToast, "id">
|
||||
|
||||
function toast({ ...props }: Toast) {
|
||||
const id = genId()
|
||||
|
||||
const update = (props: ToasterToast) =>
|
||||
dispatch({
|
||||
type: "UPDATE_TOAST",
|
||||
toast: { ...props, id },
|
||||
})
|
||||
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
|
||||
|
||||
dispatch({
|
||||
type: "ADD_TOAST",
|
||||
toast: {
|
||||
...props,
|
||||
id,
|
||||
open: true,
|
||||
onOpenChange: (open) => {
|
||||
if (!open) dismiss()
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
id: id,
|
||||
dismiss,
|
||||
update,
|
||||
}
|
||||
}
|
||||
|
||||
function useToast() {
|
||||
const [state, setState] = React.useState<State>(memoryState)
|
||||
|
||||
React.useEffect(() => {
|
||||
listeners.push(setState)
|
||||
return () => {
|
||||
const index = listeners.indexOf(setState)
|
||||
if (index > -1) {
|
||||
listeners.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}, [state])
|
||||
|
||||
return {
|
||||
...state,
|
||||
toast,
|
||||
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
||||
}
|
||||
}
|
||||
|
||||
export { useToast, toast }
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getAllValidators } from "@/lib/staking";
|
||||
import { toastError } from "@/lib/utils";
|
||||
import { ReactNode, createContext, useContext, useEffect, useReducer } from "react";
|
||||
import { emptyChain, isChainInfoFilled, setChain, setChains, setChainsError } from "./helpers";
|
||||
import { getChain, getNodeFromArray, useChainsFromRegistry } from "./service";
|
||||
@@ -99,7 +100,11 @@ export const ChainsProvider = ({ children }: ChainsProviderProps) => {
|
||||
const validators = await getAllValidators(state.chain.nodeAddress);
|
||||
dispatch({ type: "setValidatorState", payload: { validators, status: "done" } });
|
||||
} catch (e) {
|
||||
console.error(e instanceof Error ? e.message : "Failed to load validators");
|
||||
console.error("Failed to load validators:", e);
|
||||
toastError({
|
||||
description: "Failed to load validators",
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
dispatch({ type: "setValidatorState", payload: { validators: [], status: "error" } });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getChainsFromRegistry, getShaFromRegistry } from "@/lib/chainRegistry";
|
||||
import { toastError } from "@/lib/utils";
|
||||
import { StargateClient } from "@cosmjs/stargate";
|
||||
import { useEffect, useState } from "react";
|
||||
import { emptyChain, isChainInfoFilled } from "./helpers";
|
||||
@@ -56,12 +57,12 @@ export const useChainsFromRegistry = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e instanceof Error) {
|
||||
console.error(e.message);
|
||||
setChainItemsError(e.message);
|
||||
} else {
|
||||
setChainItemsError("Failed to get chains from registry");
|
||||
}
|
||||
console.error("Failed to get chains from registry:", e);
|
||||
setChainItemsError(e instanceof Error ? e.message : "Failed to get chains from registry");
|
||||
toastError({
|
||||
description: "Failed to get chains from registry",
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
}
|
||||
})();
|
||||
}, [chainItems.mainnets.size, chainItems.testnets.size]);
|
||||
|
||||
+40
-1
@@ -1,6 +1,45 @@
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import copy from "copy-to-clipboard";
|
||||
import { toast } from "sonner";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
interface ToastErrorProps {
|
||||
readonly title?: string;
|
||||
readonly description?: string;
|
||||
readonly fullError?: Error;
|
||||
}
|
||||
|
||||
export function toastError(
|
||||
{ title, description, fullError }: ToastErrorProps = { title: "An error ocurred" },
|
||||
) {
|
||||
toast.error(title, {
|
||||
description,
|
||||
action:
|
||||
fullError && fullError.message
|
||||
? { label: "Copy full error", onClick: () => copy(fullError.message) }
|
||||
: undefined,
|
||||
classNames: {
|
||||
closeButton:
|
||||
"group-[.toaster]:!bg-destructive group-[.toaster]:!text-destructive-foreground [&>svg]:!stroke-[4px]",
|
||||
},
|
||||
actionButtonStyle: {
|
||||
backgroundColor: "hsl(var(--destructive-foreground))",
|
||||
color: "hsl(var(--destructive))",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function toastSuccess(title: string, description?: string) {
|
||||
toast.success(title, {
|
||||
description,
|
||||
classNames: {
|
||||
closeButton:
|
||||
"group-[.toaster]:!bg-green-500 group-[.toaster]:!text-white [&>svg]:!stroke-[4px]",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Generated
+300
-117
@@ -44,7 +44,6 @@
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toast": "^1.1.5",
|
||||
"@radix-ui/react-toggle": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@testing-library/jest-dom": "^6.1.3",
|
||||
@@ -78,11 +77,12 @@
|
||||
"react-hook-form": "^7.47.0",
|
||||
"react-select": "^5.7.4",
|
||||
"recharts": "^2.8.0",
|
||||
"sonner": "^1.4.3",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwindcss": "3.3.3",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"typescript": "5.2.2",
|
||||
"vanilla-jsoneditor": "^0.18.3",
|
||||
"vanilla-jsoneditor": "^0.22.0",
|
||||
"zod": "^3.22.2"
|
||||
}
|
||||
},
|
||||
@@ -757,6 +757,91 @@
|
||||
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
|
||||
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="
|
||||
},
|
||||
"node_modules/@codemirror/autocomplete": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.13.0.tgz",
|
||||
"integrity": "sha512-SuDrho1klTINfbcMPnyro1ZxU9xJtwDMtb62R8TjL/tOl71IoOsvBo1a9x+hDvHhIzkTcJHy2VC+rmpGgYkRSw==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.17.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/commands": {
|
||||
"version": "6.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.3.tgz",
|
||||
"integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@lezer/common": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-json": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
||||
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@lezer/json": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language": {
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz",
|
||||
"integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.23.0",
|
||||
"@lezer/common": "^1.1.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0",
|
||||
"style-mod": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lint": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.5.0.tgz",
|
||||
"integrity": "sha512-+5YyicIaaAZKU8K43IQi8TBy6mF6giGeWAH7N96Z5LC30Wm5JMjqxOYIE9mxwMG1NbhT2mA3l9hA4uuKUM3E5g==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/search": {
|
||||
"version": "6.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.6.tgz",
|
||||
"integrity": "sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/state": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz",
|
||||
"integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A=="
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.25.1.tgz",
|
||||
"integrity": "sha512-2LXLxsQnHDdfGzDvjzAwZh2ZviNJm7im6tGpa0IONIDnFd8RZ80D2SNi8PDi6YjKcMoMRK20v6OmKIdsrwsyoQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"style-mod": "^4.1.0",
|
||||
"w3c-keyname": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@confio/ics23": {
|
||||
"version": "0.6.8",
|
||||
"resolved": "https://registry.npmjs.org/@confio/ics23/-/ics23-0.6.8.tgz",
|
||||
@@ -1143,21 +1228,33 @@
|
||||
"integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw=="
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-common-types": {
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz",
|
||||
"integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==",
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz",
|
||||
"integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==",
|
||||
"hasInstallScript": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/free-solid-svg-icons": {
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz",
|
||||
"integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==",
|
||||
"node_modules/@fortawesome/free-regular-svg-icons": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz",
|
||||
"integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.4.2"
|
||||
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/free-solid-svg-icons": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz",
|
||||
"integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -1657,6 +1754,37 @@
|
||||
"resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-6.10.1.tgz",
|
||||
"integrity": "sha512-z+ILK8Q3y+nfUl43ctCPuR4Y2bIxk/ooCQFwZxhtci1EhAtMDzMAx2W25qx8G1PPL9UUOdnUax19+F0OjXoj4w=="
|
||||
},
|
||||
"node_modules/@lezer/common": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz",
|
||||
"integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ=="
|
||||
},
|
||||
"node_modules/@lezer/highlight": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz",
|
||||
"integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/json": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.2.tgz",
|
||||
"integrity": "sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/lr": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz",
|
||||
"integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "13.4.19",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.19.tgz",
|
||||
@@ -3367,90 +3495,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-toast": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.1.5.tgz",
|
||||
"integrity": "sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/primitive": "1.0.1",
|
||||
"@radix-ui/react-collection": "1.0.3",
|
||||
"@radix-ui/react-compose-refs": "1.0.1",
|
||||
"@radix-ui/react-context": "1.0.1",
|
||||
"@radix-ui/react-dismissable-layer": "1.0.5",
|
||||
"@radix-ui/react-portal": "1.0.4",
|
||||
"@radix-ui/react-presence": "1.0.1",
|
||||
"@radix-ui/react-primitive": "1.0.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.0.1",
|
||||
"@radix-ui/react-use-controllable-state": "1.0.1",
|
||||
"@radix-ui/react-use-layout-effect": "1.0.1",
|
||||
"@radix-ui/react-visually-hidden": "1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-dismissable-layer": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz",
|
||||
"integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/primitive": "1.0.1",
|
||||
"@radix-ui/react-compose-refs": "1.0.1",
|
||||
"@radix-ui/react-primitive": "1.0.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.0.1",
|
||||
"@radix-ui/react-use-escape-keydown": "1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-portal": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz",
|
||||
"integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-primitive": "1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-toggle": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz",
|
||||
@@ -3746,6 +3790,16 @@
|
||||
"@babel/runtime": "^7.13.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@replit/codemirror-indentation-markers": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-indentation-markers/-/codemirror-indentation-markers-6.5.0.tgz",
|
||||
"integrity": "sha512-5RgeuQ6erfROi1EVI2X7G4UR+KByjb07jhYMynvpvlrV22JlnARifmKMGEUKy0pKcxBNfwbFqoUlTYHPgyZNlg==",
|
||||
"peerDependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rushstack/eslint-patch": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.3.tgz",
|
||||
@@ -3772,6 +3826,11 @@
|
||||
"@sinonjs/commons": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sphinxxxx/color-conversion": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz",
|
||||
"integrity": "sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw=="
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz",
|
||||
@@ -4009,9 +4068,9 @@
|
||||
"integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g=="
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz",
|
||||
"integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA=="
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
|
||||
},
|
||||
"node_modules/@types/graceful-fs": {
|
||||
"version": "4.1.6",
|
||||
@@ -5383,6 +5442,16 @@
|
||||
"periscopic": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/codemirror-wrapped-line-indent": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/codemirror-wrapped-line-indent/-/codemirror-wrapped-line-indent-1.0.5.tgz",
|
||||
"integrity": "sha512-T6C18nEhWb+k3JD7BhNaYFp9dzpjXM4Oq3jymSYPugZOe5nN64DNWRilkr6iox2sqaQ3PH0D4RVg+Qcv6u1OBg==",
|
||||
"peerDependencies": {
|
||||
"@codemirror/language": "^6.9.0",
|
||||
"@codemirror/state": "^6.2.1",
|
||||
"@codemirror/view": "^6.17.1"
|
||||
}
|
||||
},
|
||||
"node_modules/collect-v8-coverage": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
|
||||
@@ -5474,6 +5543,11 @@
|
||||
"resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.9.0.tgz",
|
||||
"integrity": "sha512-MN/yUe6mkJwHnCFfsNPeCfXVhyxHYW6c/xDUzrSbBycYzw++XvWDMJArXp2pLdgD6FQ8DW79vkPjeNKVrXaHeQ=="
|
||||
},
|
||||
"node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@@ -7192,10 +7266,15 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
|
||||
"integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw=="
|
||||
},
|
||||
"node_modules/immutable-json-patch": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/immutable-json-patch/-/immutable-json-patch-5.1.3.tgz",
|
||||
"integrity": "sha512-95AsF9hJTPpwtBGAnHmw57PASL672tb+vGHR5xLhH2VPuHSsLho7grjlfgQ65DIhHP+UmLCjdmuuA6L1ndJbZg=="
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/immutable-json-patch/-/immutable-json-patch-6.0.1.tgz",
|
||||
"integrity": "sha512-BHL/cXMjwFZlTOffiWNdY8ZTvNyYLrutCnWxrcKPHr5FqpAb6vsO6WWSPnVSys3+DruFN6lhHJJPHi8uELQL5g=="
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.0",
|
||||
@@ -7524,9 +7603,9 @@
|
||||
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
|
||||
},
|
||||
"node_modules/is-reference": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.1.tgz",
|
||||
"integrity": "sha512-baJJdQLiYaJdvFbJqXrcGv3WU3QCzBlUcI5QhbesIm6/xPsvmO+2CDoi/GMOFBQEQm+PXkwOPrp9KK5ozZsp2w==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
|
||||
"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
|
||||
"dependencies": {
|
||||
"@types/estree": "*"
|
||||
}
|
||||
@@ -8309,6 +8388,14 @@
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jmespath": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
|
||||
"integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -8395,6 +8482,11 @@
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||
},
|
||||
"node_modules/json-source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/json-source-map/-/json-source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg=="
|
||||
},
|
||||
"node_modules/json-stable-stringify-without-jsonify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
|
||||
@@ -8411,6 +8503,14 @@
|
||||
"json5": "lib/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonrepair": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.6.0.tgz",
|
||||
"integrity": "sha512-ZvOmoq35LhlDaf1W3uT7e17Bh2dYbln1+pdJ1KUIMkRAoUC4mvXX+dbr9Ih6dDmYvB0mdijAucyPk4xX1cEjww==",
|
||||
"bin": {
|
||||
"jsonrepair": "bin/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jsx-ast-utils": {
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
||||
@@ -8566,6 +8666,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@@ -8615,9 +8720,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.3",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz",
|
||||
"integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==",
|
||||
"version": "0.30.8",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz",
|
||||
"integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15"
|
||||
},
|
||||
@@ -8783,6 +8888,11 @@
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
|
||||
},
|
||||
"node_modules/natural-compare-lite": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz",
|
||||
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g=="
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "13.4.19",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-13.4.19.tgz",
|
||||
@@ -10201,6 +10311,22 @@
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.71.1",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz",
|
||||
"integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==",
|
||||
"dependencies": {
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
"source-map-js": ">=0.6.2 <2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"sass": "sass.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/saxes": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
|
||||
@@ -10284,6 +10410,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/sonner": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/sonner/-/sonner-1.4.3.tgz",
|
||||
"integrity": "sha512-SArYlHbkjqRuLiR0iGY2ZSr09oOrxw081ZZkQPfXrs8aZQLIBOLOdzTYxGJB5yIZ7qL56UEPmrX1YqbODwG0Lw==",
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -10499,6 +10634,11 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/style-mod": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
|
||||
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="
|
||||
},
|
||||
"node_modules/styled-jsx": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
|
||||
@@ -10589,28 +10729,37 @@
|
||||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.0.tgz",
|
||||
"integrity": "sha512-kVsdPjDbLrv74SmLSUzAsBGquMs4MPgWGkGLpH+PjOYnFOziAvENVzgJmyOCV2gntxE32aNm8/sqNKD6LbIpeQ==",
|
||||
"version": "4.2.12",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.12.tgz",
|
||||
"integrity": "sha512-d8+wsh5TfPwqVzbm4/HCXC783/KPHV60NvwitJnyTA5lWn1elhXMNWhXGCJ7PwPa8qFUnyJNIyuIRt2mT0WMug==",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15",
|
||||
"@jridgewell/trace-mapping": "^0.3.18",
|
||||
"@types/estree": "^1.0.1",
|
||||
"acorn": "^8.9.0",
|
||||
"aria-query": "^5.3.0",
|
||||
"axobject-query": "^3.2.1",
|
||||
"axobject-query": "^4.0.0",
|
||||
"code-red": "^1.0.3",
|
||||
"css-tree": "^2.3.1",
|
||||
"estree-walker": "^3.0.3",
|
||||
"is-reference": "^3.0.1",
|
||||
"locate-character": "^3.0.0",
|
||||
"magic-string": "^0.30.0",
|
||||
"magic-string": "^0.30.4",
|
||||
"periscopic": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte/node_modules/axobject-query": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
|
||||
"integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==",
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/symbol-observable": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-2.0.3.tgz",
|
||||
@@ -11057,14 +11206,35 @@
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
|
||||
},
|
||||
"node_modules/vanilla-jsoneditor": {
|
||||
"version": "0.18.3",
|
||||
"resolved": "https://registry.npmjs.org/vanilla-jsoneditor/-/vanilla-jsoneditor-0.18.3.tgz",
|
||||
"integrity": "sha512-T3noS1HVVLEhuNgVmk9/FJFhxfv9TO736UBPuj9laMqq9ywinb09VDF5isQmY5hB7a6erg2GdaUrRUEcLmZrzw==",
|
||||
"version": "0.22.0",
|
||||
"resolved": "https://registry.npmjs.org/vanilla-jsoneditor/-/vanilla-jsoneditor-0.22.0.tgz",
|
||||
"integrity": "sha512-r6AN3NAWyVFb9pH6iNIa2Q1YiSGai4PGhFN+YnR4OJzXE72hu/Xu/MUbVyGvJxchhPcWj6bIPB1Ox5KSOspmFA==",
|
||||
"dependencies": {
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||
"@codemirror/autocomplete": "^6.13.0",
|
||||
"@codemirror/commands": "^6.3.3",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/language": "^6.10.1",
|
||||
"@codemirror/lint": "^6.5.0",
|
||||
"@codemirror/search": "^6.5.6",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@codemirror/view": "^6.24.1",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.5.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.5.1",
|
||||
"@lezer/highlight": "^1.2.0",
|
||||
"@replit/codemirror-indentation-markers": "^6.5.0",
|
||||
"ajv": "^8.12.0",
|
||||
"immutable-json-patch": "^5.1.3",
|
||||
"svelte": "^4.2.0"
|
||||
"codemirror-wrapped-line-indent": "^1.0.5",
|
||||
"diff-sequences": "^29.6.3",
|
||||
"immutable-json-patch": "6.0.1",
|
||||
"jmespath": "^0.16.0",
|
||||
"json-source-map": "^0.6.1",
|
||||
"jsonrepair": "^3.6.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"memoize-one": "^6.0.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
"sass": "^1.71.1",
|
||||
"svelte": "^4.2.12",
|
||||
"vanilla-picker": "^2.12.2"
|
||||
}
|
||||
},
|
||||
"node_modules/vanilla-jsoneditor/node_modules/ajv": {
|
||||
@@ -11087,6 +11257,14 @@
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
|
||||
},
|
||||
"node_modules/vanilla-picker": {
|
||||
"version": "2.12.2",
|
||||
"resolved": "https://registry.npmjs.org/vanilla-picker/-/vanilla-picker-2.12.2.tgz",
|
||||
"integrity": "sha512-dk0gNeNL9fQFGd1VEhNDQfFlbCqAiksRh1H2tVPlavkH88n/a/y30rXi9PPKrYPTK5kEfPO4xcldt4ts/1wIAg==",
|
||||
"dependencies": {
|
||||
"@sphinxxxx/color-conversion": "^2.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/victory-vendor": {
|
||||
"version": "36.6.11",
|
||||
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.11.tgz",
|
||||
@@ -11108,6 +11286,11 @@
|
||||
"d3-timer": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
|
||||
},
|
||||
"node_modules/w3c-xmlserializer": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
|
||||
|
||||
+2
-2
@@ -49,7 +49,6 @@
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
"@radix-ui/react-tabs": "^1.0.4",
|
||||
"@radix-ui/react-toast": "^1.1.5",
|
||||
"@radix-ui/react-toggle": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@testing-library/jest-dom": "^6.1.3",
|
||||
@@ -83,11 +82,12 @@
|
||||
"react-hook-form": "^7.47.0",
|
||||
"react-select": "^5.7.4",
|
||||
"recharts": "^2.8.0",
|
||||
"sonner": "^1.4.3",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwindcss": "3.3.3",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"typescript": "5.2.2",
|
||||
"vanilla-jsoneditor": "^0.18.3",
|
||||
"vanilla-jsoneditor": "^0.22.0",
|
||||
"zod": "^3.22.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isChainInfoFilled } from "@/context/ChainsContext/helpers";
|
||||
import { toastError, toastSuccess } from "@/lib/utils";
|
||||
import { MultisigThresholdPubkey } from "@cosmjs/amino";
|
||||
import { fromBase64 } from "@cosmjs/encoding";
|
||||
import { Account, StargateClient, makeMultisignedTxBytes } from "@cosmjs/stargate";
|
||||
@@ -6,6 +7,7 @@ import { assert } from "@cosmjs/utils";
|
||||
import { GetServerSideProps } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import CompletedTransaction from "../../../../components/dataViews/CompletedTransaction";
|
||||
import ThresholdInfo from "../../../../components/dataViews/ThresholdInfo";
|
||||
import TransactionInfo from "../../../../components/dataViews/TransactionInfo";
|
||||
@@ -69,12 +71,10 @@ const TransactionPage = ({
|
||||
}) => {
|
||||
const { chain } = useChains();
|
||||
const [currentSignatures, setCurrentSignatures] = useState(signatures);
|
||||
const [broadcastError, setBroadcastError] = useState("");
|
||||
const [isBroadcasting, setIsBroadcasting] = useState(false);
|
||||
const [transactionHash, setTransactionHash] = useState(txHash);
|
||||
const [accountOnChain, setAccountOnChain] = useState<Account | null>(null);
|
||||
const [pubkey, setPubkey] = useState<MultisigThresholdPubkey>();
|
||||
const [hasAccountError, setHasAccountError] = useState(false);
|
||||
const txInfo = dbTxFromJson(transactionJSON);
|
||||
const router = useRouter();
|
||||
const multisigAddress = router.query.address?.toString();
|
||||
@@ -95,20 +95,21 @@ const TransactionPage = ({
|
||||
|
||||
setPubkey(result[0]);
|
||||
setAccountOnChain(result[1]);
|
||||
setHasAccountError(false);
|
||||
} catch (error: unknown) {
|
||||
setHasAccountError(true);
|
||||
console.error(
|
||||
error instanceof Error ? error.message : "Multisig address could not be found",
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Failed to find multisig address:", e);
|
||||
toastError({
|
||||
description: "Failed to find multisig address",
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
}
|
||||
})();
|
||||
}, [chain, multisigAddress]);
|
||||
|
||||
const broadcastTx = async () => {
|
||||
const loadingToastId = toast.loading("Broadcasting transaction");
|
||||
|
||||
try {
|
||||
setIsBroadcasting(true);
|
||||
setBroadcastError("");
|
||||
|
||||
assert(accountOnChain, "Account on chain value missing.");
|
||||
assert(
|
||||
@@ -132,11 +133,17 @@ const TransactionPage = ({
|
||||
await requestJson(`/api/transaction/${transactionID}`, {
|
||||
body: { txHash: result.transactionHash },
|
||||
});
|
||||
toastSuccess("Transaction broadcasted with hash", result.transactionHash);
|
||||
setTransactionHash(result.transactionHash);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
} catch (e) {
|
||||
console.error("Failed to broadcast tx:", e);
|
||||
toastError({
|
||||
description: "Failed to broadcast tx",
|
||||
fullError: e instanceof Error ? e : undefined,
|
||||
});
|
||||
} finally {
|
||||
setIsBroadcasting(false);
|
||||
setBroadcastError(e.toString());
|
||||
toast.dismiss(loadingToastId);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -159,13 +166,6 @@ const TransactionPage = ({
|
||||
<StackableContainer>
|
||||
<h1>{transactionHash ? "Completed Transaction" : "In Progress Transaction"}</h1>
|
||||
</StackableContainer>
|
||||
{hasAccountError ? (
|
||||
<StackableContainer>
|
||||
<div className="multisig-error">
|
||||
<p>Multisig address could not be found.</p>
|
||||
</div>
|
||||
</StackableContainer>
|
||||
) : null}
|
||||
{transactionHash ? <CompletedTransaction transactionHash={transactionHash} /> : null}
|
||||
{!transactionHash ? (
|
||||
<StackableContainer lessPadding lessMargin>
|
||||
@@ -178,7 +178,6 @@ const TransactionPage = ({
|
||||
primary
|
||||
disabled={isBroadcasting}
|
||||
/>
|
||||
{broadcastError ? <div className="broadcast-error">{broadcastError}</div> : null}
|
||||
</>
|
||||
) : null}
|
||||
{pubkey && txInfo ? (
|
||||
@@ -194,23 +193,6 @@ const TransactionPage = ({
|
||||
) : null}
|
||||
{txInfo ? <TransactionInfo tx={txInfo} /> : null}
|
||||
</StackableContainer>
|
||||
<style jsx>{`
|
||||
.broadcast-error {
|
||||
background: firebrick;
|
||||
margin: 20px auto;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
font-family: monospace;
|
||||
max-width: 475px;
|
||||
}
|
||||
.multisig-error p {
|
||||
max-width: 550px;
|
||||
color: red;
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
`}</style>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,22 +1,15 @@
|
||||
import FindMultisigForm from "@/components/forms/FindMultisigForm";
|
||||
import Page from "@/components/layout/Page";
|
||||
import StackableContainer from "@/components/layout/StackableContainer";
|
||||
import Head from "@/components/head";
|
||||
import { useChains } from "@/context/ChainsContext";
|
||||
|
||||
const MultiPage = () => {
|
||||
const { chain } = useChains();
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<StackableContainer base>
|
||||
<StackableContainer lessPadding>
|
||||
<h1 className="title">
|
||||
<span>{chain.chainDisplayName}</span> Multisig Manager
|
||||
</h1>
|
||||
</StackableContainer>
|
||||
<FindMultisigForm />
|
||||
</StackableContainer>
|
||||
</Page>
|
||||
<div className="m-4 mt-0 flex max-w-xl flex-1 flex-col justify-center gap-4">
|
||||
<Head title={`${chain.chainDisplayName || "Cosmos Hub"} Multisig Manager`} />
|
||||
<FindMultisigForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+24
-5
@@ -1,19 +1,38 @@
|
||||
import Header from "@/components/Header";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import ThemeProvider from "@/context/ThemesContext";
|
||||
import "@/styles/globals.css";
|
||||
import type { AppProps } from "next/app";
|
||||
import { ChainsProvider } from "../context/ChainsContext";
|
||||
import "@/styles/globals.css";
|
||||
|
||||
export default function MultisigApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<ChainsProvider>
|
||||
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem>
|
||||
<TooltipProvider>
|
||||
<Header />
|
||||
<Component {...pageProps} />
|
||||
<Toaster />
|
||||
<div className="flex min-h-screen flex-col items-center gap-4">
|
||||
<Header />
|
||||
<Component {...pageProps} />
|
||||
</div>
|
||||
<Toaster
|
||||
richColors
|
||||
closeButton
|
||||
duration={999999}
|
||||
/* This need to be overriden or else it doesn't apply the custom styles. A bug from shadcn probably https://github.com/shadcn-ui/ui/issues/2254 */
|
||||
toastOptions={{
|
||||
classNames: {
|
||||
toast:
|
||||
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
||||
error:
|
||||
"group-[.toaster]:!bg-destructive group-[.toaster]:!text-destructive-foreground",
|
||||
success: "group-[.toaster]:!bg-green-500 group-[.toaster]:!text-white",
|
||||
description: "group-[.toast]:text-foreground",
|
||||
actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
|
||||
cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</TooltipProvider>
|
||||
</ThemeProvider>
|
||||
</ChainsProvider>
|
||||
|
||||
+19
-58
@@ -4,66 +4,27 @@
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
|
||||
--primary: 240 5.9% 10%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 240 10% 3.9%;
|
||||
|
||||
--background: 296.76 90.24% 16.08%;
|
||||
--foreground: 0 0% 100%;
|
||||
--muted: 299.15 27.84% 50%;
|
||||
--muted-foreground: 0 0% 83.14%;
|
||||
--popover: 298.26 40.83% 33.14%;
|
||||
--popover-foreground: 0 0% 100%;
|
||||
--card: 298.26 40.83% 33.14%;
|
||||
--card-foreground: 0 0% 100%;
|
||||
--border: 297 0% 100%;
|
||||
--input: 299.15 0% 100%;
|
||||
--primary: 297 90% 16%;
|
||||
--primary-foreground: 300 100% 85.69%;
|
||||
--secondary: 300 100% 85.69%;
|
||||
--secondary-foreground: 296.76 90.24% 16.08%;
|
||||
--accent: 299.15 27.84% 50%;
|
||||
--accent-foreground: 327 0% 100%;
|
||||
--destructive: 0 84.24% 60.2%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
--ring: 357.74 0% 100%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 240 10% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
|
||||
--card: 240 10% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
|
||||
--popover: 240 10% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 240 5.9% 10%;
|
||||
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
|
||||
--muted: 240 3.7% 15.9%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
|
||||
--accent: 240 3.7% 15.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
|
||||
--destructive: 0 72% 51%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
--ring: 240 4.9% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
|
||||
Reference in New Issue
Block a user