diff --git a/examples/telescope-authz/components/authz/AuthzSection.tsx b/examples/telescope-authz/components/authz/AuthzSection.tsx
new file mode 100644
index 000000000..3e5ae2c6a
--- /dev/null
+++ b/examples/telescope-authz/components/authz/AuthzSection.tsx
@@ -0,0 +1,68 @@
+import { useState } from 'react';
+import { ChainName } from 'cosmos-kit';
+import { useChain } from '@cosmos-kit/react';
+import { Box, Button, Tabs, Text } from '@interchain-ui/react';
+
+import { Grants } from './Grants';
+import { GrantModal } from './GrantModal';
+
+export const AuthzSection = ({ chainName }: { chainName: ChainName }) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [activeTab, setActiveTab] = useState(0);
+ const { address } = useChain(chainName);
+
+ if (!address) {
+ return (
+
+ Please connect your wallet to view and create grants
+
+ );
+ }
+
+ return (
+
+
+ setActiveTab(tabId)}
+ attributes={{ width: '$min' }}
+ />
+
+
+
+
+
+ setIsOpen(false)}
+ chainName={chainName}
+ />
+
+ );
+};
diff --git a/examples/telescope-authz/components/authz/CustomizationField.tsx b/examples/telescope-authz/components/authz/CustomizationField.tsx
new file mode 100644
index 000000000..7021bb5da
--- /dev/null
+++ b/examples/telescope-authz/components/authz/CustomizationField.tsx
@@ -0,0 +1,132 @@
+import { Dispatch, SetStateAction, useState } from 'react';
+import { Box, NumberField, SelectButton, Text } from '@interchain-ui/react';
+
+import { useValidators } from '@/hooks';
+import { Permission, PermissionId } from '@/configs';
+import { SelectValidatorsModal } from './SelectValidatorsModal';
+import { AccessList } from './GrantModal';
+
+// ==============================================
+
+type SendCustomizationProps = {
+ value: number | undefined;
+ onChange: (value: string) => void;
+};
+
+const SendCustomization = ({ value, onChange }: SendCustomizationProps) => {
+ return (
+ {
+ // @ts-ignore
+ onChange(e.target.value);
+ }}
+ formatOptions={{
+ maximumFractionDigits: 6,
+ }}
+ />
+ );
+};
+
+// ==============================================
+
+type DelegateCustomizationProps = {
+ value: number | undefined;
+ onChange: (value: string) => void;
+ chainName: string;
+ accessList: AccessList;
+ setAccessList: Dispatch>;
+};
+
+const DelegateCustomization = ({
+ value,
+ onChange,
+ chainName,
+ accessList,
+ setAccessList,
+}: DelegateCustomizationProps) => {
+ const [isOpen, setIsOpen] = useState(false);
+
+ const { data } = useValidators(chainName);
+
+ const validatorNames = data
+ ? accessList.addresses.map(
+ (address) => data.find((v) => v.address === address)!.name
+ )
+ : [];
+
+ return (
+ <>
+ {
+ // @ts-ignore
+ onChange(e.target.value);
+ }}
+ formatOptions={{
+ maximumFractionDigits: 6,
+ }}
+ />
+ setIsOpen(true)}
+ />
+ 0 ? 'block' : 'none'}
+ mt="$2"
+ px="$2"
+ >
+
+
+ {accessList.type === 'allowList' ? 'Allow List' : 'Deny List'}
+ :
+
+ {validatorNames.join(', ')}
+
+
+ setIsOpen(false)}
+ />
+ >
+ );
+};
+
+// ==============================================
+
+type CustomizationFieldProps =
+ | ({
+ permissionType: typeof Permission['Send'];
+ } & SendCustomizationProps)
+ | ({
+ permissionType: typeof Permission['Delegate'];
+ } & DelegateCustomizationProps);
+
+export const CustomizationField = ({
+ permissionType,
+ ...rest
+}: CustomizationFieldProps): JSX.Element | null => {
+ const fields: Partial> = {
+ send:
+ permissionType === 'send' ? (
+
+ ) : null,
+ delegate:
+ permissionType === 'delegate' ? (
+
+ ) : null,
+ };
+
+ return fields[permissionType] ?? null;
+};
diff --git a/examples/telescope-authz/components/authz/GrantCard.tsx b/examples/telescope-authz/components/authz/GrantCard.tsx
new file mode 100644
index 000000000..a9e70ff1c
--- /dev/null
+++ b/examples/telescope-authz/components/authz/GrantCard.tsx
@@ -0,0 +1,185 @@
+import Link from 'next/link';
+import Image from 'next/image';
+import { useState } from 'react';
+import {
+ Box,
+ Button,
+ IconButton,
+ Stack,
+ Text,
+ TextField,
+} from '@interchain-ui/react';
+import { useChain } from '@cosmos-kit/react';
+
+import {
+ getChainLogoByChainName,
+ PrettyGrant,
+ PrettyPermission,
+} from '@/utils';
+import { useAuthzContext } from '@/context';
+import { useAuthzTx, useGrants } from '@/hooks';
+import { getCoin, permissionNameToRouteMap } from '@/configs';
+
+import styles from '@/styles/custom.module.css';
+
+type GrantCardProps = {
+ role: 'granter' | 'grantee';
+ grant: PrettyGrant;
+ chainName: string;
+ onViewDetails: () => void;
+};
+
+export const GrantCard = ({
+ role,
+ grant,
+ chainName,
+ onViewDetails,
+}: GrantCardProps) => {
+ const [isCopied, setIsCopied] = useState(false);
+ const [isRevoking, setIsRevoking] = useState(false);
+ const [revokingPermission, setRevokingPermission] =
+ useState();
+
+ const { chain } = useChain(chainName);
+ const { refetch } = useGrants(chainName);
+ const { setPermission } = useAuthzContext();
+ const { authzTx, createRevokeMsg } = useAuthzTx(chainName);
+
+ const { address, permissions } = grant;
+
+ const isGranter = role === 'granter';
+ const token = getCoin(chainName);
+
+ const copy = (text: string) => {
+ if (isCopied) return;
+
+ navigator.clipboard
+ .writeText(text)
+ .then(() => {
+ setIsCopied(true);
+ setTimeout(() => setIsCopied(false), 800);
+ })
+ .catch((error) => {
+ console.error('Failed to copy:', error);
+ });
+ };
+
+ const handleRevoke = (permission: PrettyPermission) => {
+ setIsRevoking(true);
+
+ authzTx({
+ msgs: [createRevokeMsg(permission)],
+ onSuccess: () => {
+ refetch();
+ },
+ onComplete: () => {
+ setIsRevoking(false);
+ },
+ });
+ };
+
+ return (
+
+
+
+
+ {chain.pretty_name}
+
+
+
+
+
+
+ copy(address)}
+ />
+
+
+
+
+ Permissions
+
+
+
+ {permissions.map((permission) =>
+ isGranter ? (
+
+ ) : permissionNameToRouteMap[permission.name] ? (
+
+
+
+ ) : (
+
+ )
+ )}
+
+
+
+
+ );
+};
diff --git a/examples/telescope-authz/components/authz/GrantDetailsModal.tsx b/examples/telescope-authz/components/authz/GrantDetailsModal.tsx
new file mode 100644
index 000000000..592498149
--- /dev/null
+++ b/examples/telescope-authz/components/authz/GrantDetailsModal.tsx
@@ -0,0 +1,94 @@
+import { useState } from 'react';
+import { BasicModal, Box, Button } from '@interchain-ui/react';
+
+import { useAuthzTx, useGrants } from '@/hooks';
+import { PrettyGrant, PrettyPermission } from '@/utils';
+import { PermissionDetailCard } from './PermissionDetailCard';
+
+type GrantDetailsModalProps = {
+ grant: PrettyGrant;
+ chainName: string;
+ role: 'granter' | 'grantee';
+ isOpen: boolean;
+ onClose: () => void;
+};
+
+export const GrantDetailsModal = ({
+ role,
+ grant,
+ isOpen,
+ onClose,
+ chainName,
+}: GrantDetailsModalProps) => {
+ const { permissions } = grant;
+ const isGranter = role === 'granter';
+
+ const [isRevoking, setIsRevoking] = useState(false);
+ const [revokingPermission, setRevokingPermission] =
+ useState();
+
+ const { refetch } = useGrants(chainName);
+ const { authzTx, createRevokeMsg } = useAuthzTx(chainName);
+
+ const handleRevoke = (permissions: PrettyPermission[]) => {
+ setIsRevoking(true);
+
+ authzTx({
+ msgs: permissions.map(createRevokeMsg),
+ onSuccess: () => {
+ refetch();
+ onClose();
+ },
+ onComplete: () => {
+ setIsRevoking(false);
+ setRevokingPermission(undefined);
+ },
+ });
+ };
+
+ return (
+
+
+
+ {permissions.map((permission) => (
+ {
+ handleRevoke([permission]);
+ setRevokingPermission(permission);
+ }}
+ isRevoking={
+ isRevoking && permission.name === revokingPermission?.name
+ }
+ chainName={chainName}
+ permission={permission}
+ />
+ ))}
+
+
+ {isGranter && (
+
+ )}
+
+
+ );
+};
diff --git a/examples/telescope-authz/components/authz/GrantModal.tsx b/examples/telescope-authz/components/authz/GrantModal.tsx
new file mode 100644
index 000000000..d96671def
--- /dev/null
+++ b/examples/telescope-authz/components/authz/GrantModal.tsx
@@ -0,0 +1,292 @@
+import { useState } from 'react';
+import { ChainName } from 'cosmos-kit';
+import {
+ BasicModal,
+ Box,
+ TextField,
+ Button,
+ Popover,
+ PopoverTrigger,
+ PopoverContent,
+ SelectButton,
+ ListItem,
+ Stack,
+ FieldLabel,
+} from '@interchain-ui/react';
+import { coin } from '@cosmjs/amino';
+import { useChain } from '@cosmos-kit/react';
+import { IoMdCalendar } from 'react-icons/io';
+import Calendar from 'react-calendar';
+import dayjs from 'dayjs';
+
+import {
+ getExponent,
+ PermissionId,
+ PermissionItem,
+ permissions,
+} from '@/configs';
+import { AuthorizationType } from '@/src/codegen/cosmos/staking/v1beta1/authz';
+import { GrantMsg, useAuthzTx, useGrants } from '@/hooks';
+import { getTokenByChainName, shiftDigits } from '@/utils';
+import { CustomizationField } from './CustomizationField';
+import { AddressInput } from '@/components';
+
+import styles from '@/styles/custom.module.css';
+
+export type AccessList = {
+ type: 'allowList' | 'denyList';
+ addresses: string[];
+};
+
+type GrantModalProps = {
+ isOpen: boolean;
+ onClose: () => void;
+ chainName: ChainName;
+};
+
+export const GrantModal = ({ isOpen, onClose, chainName }: GrantModalProps) => {
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
+ const [isCalendarOpen, setIsCalendarOpen] = useState(false);
+
+ const [granteeAddress, setGranteeAddress] = useState('');
+ const [addressErrorMsg, setAddressErrorMsg] = useState('');
+ const [expiryDate, setExpiryDate] = useState(null);
+ const [selectedPermission, setSelectedPermission] =
+ useState(null);
+
+ const [sendLimit, setSendLimit] = useState(undefined);
+ const [delegateLimit, setDelegateLimit] = useState(
+ undefined
+ );
+ const [accessList, setAccessList] = useState({
+ type: 'allowList',
+ addresses: [],
+ });
+
+ const [isGranting, setIsGranting] = useState(false);
+
+ const { refetch } = useGrants(chainName);
+ const { address } = useChain(chainName);
+ const { authzTx, createGrantMsg } = useAuthzTx(chainName);
+
+ const token = getTokenByChainName(chainName);
+ const exponent = getExponent(chainName);
+ const denom = token.base;
+
+ const onModalClose = () => {
+ setGranteeAddress('');
+ setExpiryDate(null);
+ setSelectedPermission(null);
+ setSendLimit(undefined);
+ setDelegateLimit(undefined);
+ setIsGranting(false);
+ setAccessList({ type: 'allowList', addresses: [] });
+ onClose();
+ };
+
+ const onGrantClick = () => {
+ if (!address || !granteeAddress || !expiryDate || !selectedPermission)
+ return;
+
+ setIsGranting(true);
+
+ const sendMsg: GrantMsg = {
+ grantType: 'send',
+ customize: sendLimit
+ ? {
+ spendLimit: [coin(shiftDigits(sendLimit, exponent), denom)],
+ }
+ : undefined,
+ };
+
+ const delegateMsg: GrantMsg = {
+ grantType: 'delegate',
+ customize:
+ delegateLimit || accessList.addresses.length > 0
+ ? {
+ authorizationType: AuthorizationType.AUTHORIZATION_TYPE_DELEGATE,
+ maxTokens: coin(shiftDigits(delegateLimit, exponent), denom),
+ [accessList.type]: { address: accessList.addresses },
+ }
+ : undefined,
+ };
+
+ const grantMsg: Record = {
+ send: sendMsg,
+ delegate: delegateMsg,
+ vote: { grantType: 'vote' },
+ 'claim-rewards': { grantType: 'claim-rewards' },
+ };
+
+ const msg = createGrantMsg({
+ grantee: granteeAddress,
+ granter: address,
+ expiration: expiryDate,
+ ...grantMsg[selectedPermission.id],
+ });
+
+ authzTx({
+ msgs: [msg],
+ onSuccess: () => {
+ refetch();
+ onModalClose();
+ },
+ onComplete: () => {
+ setIsGranting(false);
+ },
+ });
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {}}
+ />
+
+
+
+ {permissions.map((p) => (
+ {
+ setSelectedPermission(p);
+ setIsDropdownOpen(false);
+ },
+ }}
+ >
+ {p.name}
+
+ ))}
+
+
+
+
+ {selectedPermission?.id === 'send' && (
+ {
+ if (!val) {
+ setSendLimit(undefined);
+ return;
+ }
+ setSendLimit(Number(val));
+ }}
+ />
+ )}
+
+ {selectedPermission?.id === 'delegate' && (
+ {
+ if (!val) {
+ setDelegateLimit(undefined);
+ return;
+ }
+ setDelegateLimit(Number(val));
+ }}
+ />
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ {
+ if (Array.isArray(val)) {
+ setExpiryDate(val[1]);
+ return;
+ }
+ setExpiryDate(val);
+ }}
+ onClickDay={() => {
+ setIsCalendarOpen(false);
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/examples/telescope-authz/components/authz/Grants.tsx b/examples/telescope-authz/components/authz/Grants.tsx
new file mode 100644
index 000000000..76c0151ae
--- /dev/null
+++ b/examples/telescope-authz/components/authz/Grants.tsx
@@ -0,0 +1,66 @@
+import { useState } from 'react';
+import { Box, Spinner, Text } from '@interchain-ui/react';
+
+import { useGrants } from '@/hooks';
+import { PrettyGrant } from '@/utils';
+import { GrantCard } from './GrantCard';
+import { GrantDetailsModal } from './GrantDetailsModal';
+
+type GrantsProps = {
+ role: 'granter' | 'grantee';
+ chainName: string;
+};
+
+export const Grants = ({ chainName, role }: GrantsProps) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [viewingGrant, setViewingGrant] = useState();
+ const { data, isLoading } = useGrants(chainName);
+
+ const isGranter = role === 'granter';
+ const grants = isGranter ? data?.granterGrants : data?.granteeGrants;
+
+ return (
+
+ {isLoading ? (
+
+ ) : grants && grants.length > 0 ? (
+
+ {grants.map((grant) => (
+ {
+ setIsOpen(true);
+ setViewingGrant(grant);
+ }}
+ />
+ ))}
+
+ ) : (
+
+ {isGranter
+ ? "You haven't granted any permission yet"
+ : "You don't have any grants"}
+
+ )}
+
+ {viewingGrant && (
+ setIsOpen(false)}
+ />
+ )}
+
+ );
+};
diff --git a/examples/telescope-authz/components/authz/LoginInfoBanner.tsx b/examples/telescope-authz/components/authz/LoginInfoBanner.tsx
new file mode 100644
index 000000000..7644b375b
--- /dev/null
+++ b/examples/telescope-authz/components/authz/LoginInfoBanner.tsx
@@ -0,0 +1,39 @@
+import { useChain } from '@cosmos-kit/react';
+import { Box, Icon, Text } from '@interchain-ui/react';
+
+type LoginInfoBannerProps = {
+ loginAddress: string;
+ chainName: string;
+};
+
+export const LoginInfoBanner = ({
+ loginAddress,
+ chainName,
+}: LoginInfoBannerProps) => {
+ const { isWalletConnected } = useChain(chainName);
+
+ if (!isWalletConnected) return null;
+
+ return (
+
+
+
+ You are now logged in as
+
+ {loginAddress}
+
+
+
+ );
+};
diff --git a/examples/telescope-authz/components/authz/PermissionDetailCard.tsx b/examples/telescope-authz/components/authz/PermissionDetailCard.tsx
new file mode 100644
index 000000000..a58804b57
--- /dev/null
+++ b/examples/telescope-authz/components/authz/PermissionDetailCard.tsx
@@ -0,0 +1,130 @@
+import Link from 'next/link';
+import { Box, Button, Icon, Skeleton, Text } from '@interchain-ui/react';
+
+import { useValidators } from '@/hooks';
+import { permissionNameToRouteMap } from '@/configs';
+import { getAttributePairs, PrettyGrant } from '@/utils';
+import { useAuthzContext } from '@/context';
+
+type PermissionDetailCardProps = {
+ role: 'granter' | 'grantee';
+ onRevoke: () => void;
+ isRevoking: boolean;
+ chainName: string;
+ permission: PrettyGrant['permissions'][0];
+};
+
+export const PermissionDetailCard = ({
+ role,
+ onRevoke,
+ isRevoking,
+ chainName,
+ permission,
+}: PermissionDetailCardProps) => {
+ const { name, expiration, expiry, authorization } = permission;
+ const isGranter = role === 'granter';
+
+ const { setPermission } = useAuthzContext();
+ const { data, isLoading } = useValidators(chainName, { fetchLogos: false });
+ const attributes = getAttributePairs(authorization, data || []);
+
+ return (
+
+
+ {isGranter ? (
+
+
+ {name}
+
+
+
+ ) : permissionNameToRouteMap[name] ? (
+
+ setPermission(permission) }}
+ >
+
+ {name}
+
+
+
+
+ ) : (
+
+ {name}
+
+ )}
+
+
+
+ {expiration && }
+ {attributes.map((attr) => (
+
+ ))}
+
+
+ );
+};
+
+type PermissionAttributeProps = {
+ label: string;
+ value: string;
+ isLoading?: boolean;
+};
+
+const PermissionAttribute = ({
+ label,
+ value,
+ isLoading = false,
+}: PermissionAttributeProps) => {
+ return (
+
+
+ {label}
+
+ {isLoading ? (
+
+ ) : (
+
+ {value}
+
+ )}
+
+ );
+};
diff --git a/examples/telescope-authz/components/authz/SelectValidatorsModal.tsx b/examples/telescope-authz/components/authz/SelectValidatorsModal.tsx
new file mode 100644
index 000000000..04ee042ae
--- /dev/null
+++ b/examples/telescope-authz/components/authz/SelectValidatorsModal.tsx
@@ -0,0 +1,161 @@
+import { Dispatch, SetStateAction, useMemo, useState } from 'react';
+import {
+ BasicModal,
+ Box,
+ Button,
+ GovernanceRadio,
+ GovernanceRadioGroup,
+ GridColumn,
+ Spinner,
+ Stack,
+ Text,
+ ValidatorList,
+ ValidatorNameCell,
+} from '@interchain-ui/react';
+
+import { useValidators } from '@/hooks';
+import { ExtendedValidator as Validator } from '@/utils';
+import { AccessList } from './GrantModal';
+
+type SelectValidatorsModalProps = {
+ isOpen: boolean;
+ onClose: () => void;
+ chainName: string;
+ accessList: AccessList;
+ setAccessList: Dispatch>;
+};
+
+type ListType = 'allowList' | 'denyList';
+
+export const SelectValidatorsModal = ({
+ isOpen,
+ onClose,
+ chainName,
+ accessList,
+ setAccessList,
+}: SelectValidatorsModalProps) => {
+ const { data, isLoading } = useValidators(chainName);
+ const listType = accessList.type;
+
+ const columns: GridColumn[] = useMemo(() => {
+ return [
+ {
+ id: 'validator',
+ label: 'Validator',
+ width: '196px',
+ align: 'left',
+ render: (validator: Validator) => (
+
+ ),
+ },
+ {
+ id: 'action',
+ width: '126px',
+ align: 'right',
+ render: (validator: Validator) => (
+
+ {accessList.addresses.includes(validator.address) ? (
+
+ ) : (
+ <>
+
+ >
+ )}
+
+ ),
+ },
+ ];
+ }, [chainName, accessList]);
+
+ return (
+
+
+ {isLoading ? (
+
+ ) : data && data.length > 0 ? (
+
+
+ {
+ setAccessList((prev) => ({
+ ...prev,
+ type: selected as ListType,
+ }));
+ }}
+ >
+
+
+ Allow List
+
+ Deny List
+
+
+
+
+
+
+ ) : (
+
+ No Validators Found
+
+ )}
+
+
+ );
+};
diff --git a/examples/telescope-authz/components/authz/index.ts b/examples/telescope-authz/components/authz/index.ts
new file mode 100644
index 000000000..fe0758403
--- /dev/null
+++ b/examples/telescope-authz/components/authz/index.ts
@@ -0,0 +1,2 @@
+export * from './AuthzSection';
+export * from './LoginInfoBanner';
diff --git a/examples/telescope-authz/components/claim-rewards/ClaimRewardsSection.tsx b/examples/telescope-authz/components/claim-rewards/ClaimRewardsSection.tsx
new file mode 100644
index 000000000..27dea72cf
--- /dev/null
+++ b/examples/telescope-authz/components/claim-rewards/ClaimRewardsSection.tsx
@@ -0,0 +1,50 @@
+import { useChain } from '@cosmos-kit/react';
+import { ChainName } from 'cosmos-kit';
+import { Box, Spinner, Text } from '@interchain-ui/react';
+
+import { useStakingData } from '@/hooks';
+import Overview from './Overview';
+
+export const ClaimRewardsSection = ({
+ chainName,
+}: {
+ chainName: ChainName;
+}) => {
+ const { isWalletConnected } = useChain(chainName);
+ const { data, isLoading, refetch } = useStakingData(chainName);
+
+ return (
+
+ {!isWalletConnected ? (
+
+
+ Please connect the wallet
+
+
+ ) : isLoading || !data ? (
+
+
+
+ ) : (
+
+ )}
+
+ );
+};
diff --git a/examples/telescope-authz/components/claim-rewards/Overview.tsx b/examples/telescope-authz/components/claim-rewards/Overview.tsx
new file mode 100644
index 000000000..4ba470c26
--- /dev/null
+++ b/examples/telescope-authz/components/claim-rewards/Overview.tsx
@@ -0,0 +1,101 @@
+import { useState } from 'react';
+import {
+ Box,
+ StakingAssetHeader,
+ StakingClaimHeader,
+} from '@interchain-ui/react';
+import { ChainName } from 'cosmos-kit';
+
+import { getCoin } from '@/configs';
+import { Prices, useAuthzTx } from '@/hooks';
+import {
+ sum,
+ calcDollarValue,
+ isGreaterThanZero,
+ type ParsedRewards as Rewards,
+} from '@/utils';
+import { MsgWithdrawDelegatorReward } from '@/src/codegen/cosmos/distribution/v1beta1/tx';
+import { useAuthzContext } from '@/context';
+
+const Overview = ({
+ balance,
+ rewards,
+ staked,
+ updateData,
+ chainName,
+ prices,
+}: {
+ balance: string;
+ rewards: Rewards;
+ staked: string;
+ updateData: () => void;
+ chainName: ChainName;
+ prices: Prices;
+}) => {
+ const [isClaiming, setIsClaiming] = useState(false);
+
+ const { permission } = useAuthzContext();
+ const { authzTx, createExecMsg } = useAuthzTx(chainName);
+
+ const totalAmount = sum(balance, staked, rewards?.total ?? 0);
+ const coin = getCoin(chainName);
+
+ const onClaimRewardClick = () => {
+ if (!permission) return;
+
+ setIsClaiming(true);
+
+ const { grantee, granter, expiration } = permission;
+
+ const msgs = rewards.byValidators.map(({ validatorAddress }) =>
+ MsgWithdrawDelegatorReward.toProtoMsg({
+ delegatorAddress: granter,
+ validatorAddress,
+ })
+ );
+
+ authzTx({
+ msgs: [createExecMsg({ msgs, grantee })],
+ execExpiration: expiration,
+ onSuccess: () => {
+ updateData();
+ },
+ onComplete: () => {
+ setIsClaiming(false);
+ },
+ });
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default Overview;
diff --git a/examples/telescope-authz/components/claim-rewards/index.ts b/examples/telescope-authz/components/claim-rewards/index.ts
new file mode 100644
index 000000000..68a0b4a8a
--- /dev/null
+++ b/examples/telescope-authz/components/claim-rewards/index.ts
@@ -0,0 +1 @@
+export * from './ClaimRewardsSection';
diff --git a/examples/telescope-authz/components/common/AddressInput.tsx b/examples/telescope-authz/components/common/AddressInput.tsx
new file mode 100644
index 000000000..1d3bad918
--- /dev/null
+++ b/examples/telescope-authz/components/common/AddressInput.tsx
@@ -0,0 +1,63 @@
+import { useMemo } from 'react';
+import { useChain } from '@cosmos-kit/react';
+import { fromBech32 } from '@cosmjs/encoding';
+import { TextField, Text, Box, BoxProps } from '@interchain-ui/react';
+
+type AddressInputProps = {
+ chainName: string;
+ address: string;
+ onAddressChange: (address: string) => void;
+ mb?: BoxProps['mb'];
+ label?: string;
+ placeholder?: string;
+ onInvalidAddress?: (error: string) => void;
+};
+
+export const AddressInput = ({
+ chainName,
+ address,
+ onAddressChange,
+ label,
+ mb,
+ onInvalidAddress,
+ placeholder,
+}: AddressInputProps) => {
+ const { chain } = useChain(chainName);
+
+ const errorMessage = useMemo(() => {
+ let errorMsg = '';
+
+ if (!address) {
+ onInvalidAddress && onInvalidAddress(errorMsg);
+ return errorMsg;
+ }
+
+ try {
+ const res = fromBech32(address);
+ if (!address.startsWith(chain.bech32_prefix)) {
+ errorMsg = `Invalid address: Unexpected prefix (expected: ${chain.bech32_prefix}, actual: ${res.prefix})`;
+ }
+ } catch (error) {
+ errorMsg = 'Invalid address';
+ } finally {
+ onInvalidAddress && onInvalidAddress(errorMsg);
+ return errorMsg;
+ }
+ }, [address]);
+
+ return (
+
+ onAddressChange(e.target.value)}
+ label={label}
+ placeholder={placeholder}
+ attributes={{ mb: errorMessage ? '$2' : '0' }}
+ intent={errorMessage ? 'error' : 'default'}
+ />
+
+ {errorMessage && {errorMessage}}
+
+ );
+};
diff --git a/examples/telescope-authz/components/common/Footer.tsx b/examples/telescope-authz/components/common/Footer.tsx
new file mode 100644
index 000000000..6dbfd8dcd
--- /dev/null
+++ b/examples/telescope-authz/components/common/Footer.tsx
@@ -0,0 +1,166 @@
+import {
+ Box,
+ Link,
+ Text,
+ Icon,
+ Stack,
+ Divider,
+ useColorModeValue,
+} from '@interchain-ui/react';
+import { dependencies, products, Project } from '@/configs';
+
+function Product({ name, desc, link }: Project) {
+ return (
+
+
+
+ {name} →
+
+
+ {desc}
+
+
+
+ );
+}
+
+function Dependency({ name, desc, link }: Project) {
+ return (
+
+
+
+
+
+
+
+
+ {name}
+
+
+ {desc}
+
+
+
+
+ );
+}
+
+export function Footer() {
+ return (
+ <>
+
+ {products.map((product) => (
+
+ ))}
+
+
+ {dependencies.map((dependency) => (
+
+ ))}
+
+
+
+
+
+ Built with
+
+ Cosmology
+
+
+ >
+ );
+}
diff --git a/examples/telescope-authz/components/common/Header.tsx b/examples/telescope-authz/components/common/Header.tsx
new file mode 100644
index 000000000..b573280b2
--- /dev/null
+++ b/examples/telescope-authz/components/common/Header.tsx
@@ -0,0 +1,63 @@
+import {
+ Box,
+ Button,
+ Icon,
+ Text,
+ useTheme,
+ useColorModeValue,
+} from '@interchain-ui/react';
+
+const stacks = ['CosmosKit', 'Next.js'];
+
+export function Header() {
+ const { theme, setTheme } = useTheme();
+
+ const toggleColorMode = () => {
+ setTheme(theme === 'light' ? 'dark' : 'light');
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+ Create Cosmos App
+
+
+
+ Welcome to
+
+
+ {stacks.join(' + ')}
+
+
+
+ >
+ );
+}
diff --git a/examples/telescope-authz/components/common/Layout.tsx b/examples/telescope-authz/components/common/Layout.tsx
new file mode 100644
index 000000000..ff3365d5e
--- /dev/null
+++ b/examples/telescope-authz/components/common/Layout.tsx
@@ -0,0 +1,19 @@
+import Head from 'next/head';
+import { Container } from '@interchain-ui/react';
+import { Header } from './Header';
+import { Footer } from './Footer';
+
+export function Layout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+ Create Cosmos App
+
+
+
+
+ {children}
+
+
+ );
+}
diff --git a/examples/telescope-authz/components/common/index.ts b/examples/telescope-authz/components/common/index.ts
new file mode 100644
index 000000000..595791106
--- /dev/null
+++ b/examples/telescope-authz/components/common/index.ts
@@ -0,0 +1,2 @@
+export * from './Layout';
+export * from './AddressInput';
diff --git a/examples/telescope-authz/components/index.tsx b/examples/telescope-authz/components/index.tsx
new file mode 100644
index 000000000..e751a4da9
--- /dev/null
+++ b/examples/telescope-authz/components/index.tsx
@@ -0,0 +1,7 @@
+export * from './authz';
+export * from './common';
+export * from './wallet';
+export * from './staking';
+export * from './claim-rewards';
+export * from './voting';
+export * from './send';
diff --git a/examples/telescope-authz/components/send/index.ts b/examples/telescope-authz/components/send/index.ts
new file mode 100644
index 000000000..d1ab99c82
--- /dev/null
+++ b/examples/telescope-authz/components/send/index.ts
@@ -0,0 +1 @@
+export * from './send';
diff --git a/examples/telescope-authz/components/send/send.tsx b/examples/telescope-authz/components/send/send.tsx
new file mode 100644
index 000000000..72de11961
--- /dev/null
+++ b/examples/telescope-authz/components/send/send.tsx
@@ -0,0 +1,143 @@
+import { useState } from 'react';
+import { useChain } from '@cosmos-kit/react';
+import { Box, Button, Spinner, Text, TokenInput } from '@interchain-ui/react';
+import BigNumber from 'bignumber.js';
+
+import { useAuthzTx, useSendData, useToast } from '@/hooks';
+import {
+ getCoin,
+ getExponent,
+ getChainLogoByChainName,
+ shiftDigits,
+} from '@/utils';
+import { useAuthzContext } from '@/context';
+import { AddressInput } from '@/components';
+import { MsgSend } from '@/src/codegen/cosmos/bank/v1beta1/tx';
+import { SendAuthorization } from '@/src/codegen/cosmos/bank/v1beta1/authz';
+
+type SendSectionProps = {
+ chainName: string;
+};
+
+export const SendSection = ({ chainName }: SendSectionProps) => {
+ const [recipientAddress, setRecipientAddress] = useState('');
+ const [amount, setAmount] = useState(undefined);
+ const [isSending, setIsSending] = useState(false);
+ const [errorMsg, setErrorMsg] = useState('');
+
+ const { isWalletConnected } = useChain(chainName);
+ const { data, isLoading, refetch } = useSendData(chainName);
+ const { authzTx, createExecMsg } = useAuthzTx(chainName);
+ const { permission } = useAuthzContext();
+ const { toast } = useToast();
+
+ const coin = getCoin(chainName);
+ const exponent = getExponent(chainName);
+
+ const onSendClick = () => {
+ if (!amount || !recipientAddress || !permission) return;
+
+ const { grantee, granter, authorization, expiration } = permission;
+
+ const sendAmount = shiftDigits(amount, exponent);
+
+ if (SendAuthorization.is(authorization)) {
+ const limitAmount = authorization?.spendLimit?.[0]?.amount;
+ if (limitAmount && new BigNumber(sendAmount).gt(limitAmount)) {
+ toast({
+ type: 'error',
+ title: 'Amount exceeds the spending limit',
+ });
+ return;
+ }
+ }
+
+ setIsSending(true);
+
+ const msg = MsgSend.toProtoMsg({
+ amount: [
+ {
+ amount: sendAmount,
+ denom: coin.base,
+ },
+ ],
+ fromAddress: granter,
+ toAddress: recipientAddress,
+ });
+
+ authzTx({
+ msgs: [createExecMsg({ msgs: [msg], grantee })],
+ execExpiration: expiration,
+ onSuccess: () => {
+ refetch();
+ setAmount(undefined);
+ setRecipientAddress('');
+ },
+ onComplete: () => {
+ setIsSending(false);
+ },
+ });
+ };
+
+ if (!isWalletConnected) {
+ return (
+
+ Please connect the wallet
+
+ );
+ }
+
+ if (isLoading || !data) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+ Send
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/examples/telescope-authz/components/staking/AllValidators.tsx b/examples/telescope-authz/components/staking/AllValidators.tsx
new file mode 100644
index 000000000..ca21669bf
--- /dev/null
+++ b/examples/telescope-authz/components/staking/AllValidators.tsx
@@ -0,0 +1,66 @@
+import { useState } from 'react';
+import { Text } from '@interchain-ui/react';
+import { ChainName } from 'cosmos-kit';
+
+import { DelegateModal } from './DelegateModal';
+import AllValidatorsList from './AllValidatorsList';
+import { Prices, useDisclosure } from '@/hooks';
+import { type ExtendedValidator as Validator } from '@/utils';
+
+export const AllValidators = ({
+ validators,
+ balance,
+ updateData,
+ unbondingDays,
+ chainName,
+ logos,
+ prices,
+}: {
+ validators: Validator[];
+ balance: string;
+ updateData: () => void;
+ unbondingDays: string;
+ chainName: ChainName;
+ logos: {
+ [key: string]: string;
+ };
+ prices: Prices;
+}) => {
+ const delegateModalControl = useDisclosure();
+ const [selectedValidator, setSelectedValidator] = useState();
+
+ return (
+ <>
+
+ All Validators
+
+
+
+
+ {selectedValidator && (
+
+ )}
+ >
+ );
+};
diff --git a/examples/telescope-authz/components/staking/AllValidatorsList.tsx b/examples/telescope-authz/components/staking/AllValidatorsList.tsx
new file mode 100644
index 000000000..f6856d506
--- /dev/null
+++ b/examples/telescope-authz/components/staking/AllValidatorsList.tsx
@@ -0,0 +1,119 @@
+import React, { Dispatch, SetStateAction, useMemo } from 'react';
+import { ChainName } from 'cosmos-kit';
+
+import { getCoin } from '@/configs';
+import { shiftDigits, type ExtendedValidator as Validator } from '@/utils';
+import {
+ Text,
+ Button,
+ ValidatorList,
+ ValidatorNameCell,
+ ValidatorTokenAmountCell,
+ GridColumn,
+} from '@interchain-ui/react';
+
+const AllValidatorsList = ({
+ validators,
+ openModal,
+ chainName,
+ logos,
+ setSelectedValidator,
+}: {
+ validators: Validator[];
+ chainName: ChainName;
+ openModal: () => void;
+ setSelectedValidator: Dispatch>;
+ logos: {
+ [key: string]: string;
+ };
+}) => {
+ const coin = getCoin(chainName);
+
+ const columns = useMemo(() => {
+ const _columns: GridColumn[] = [
+ {
+ id: 'validator',
+ label: 'Validator',
+ width: '196px',
+ align: 'left',
+ render: (validator: Validator) => (
+
+ ),
+ },
+ {
+ id: 'voting-power',
+ label: 'Voting Power',
+ width: '196px',
+ align: 'right',
+ render: (validator: Validator) => (
+
+ ),
+ },
+ {
+ id: 'commission',
+ label: 'Commission',
+ width: '196px',
+ align: 'right',
+ render: (validator: Validator) => (
+
+ {shiftDigits(validator.commission, 2)}%
+
+ ),
+ },
+ {
+ id: 'action',
+ width: '196px',
+ align: 'right',
+ render: (validator) => (
+
+ ),
+ },
+ ];
+
+ const hasApr = !!validators[0]?.apr;
+
+ if (hasApr) {
+ _columns.splice(3, 0, {
+ id: 'apr',
+ label: 'APR',
+ width: '196px',
+ align: 'right',
+ render: (validator: Validator) => (
+ {validator.apr}%
+ ),
+ });
+ }
+
+ return _columns;
+ }, [chainName]);
+
+ return (
+
+ );
+};
+
+export default React.memo(AllValidatorsList);
diff --git a/examples/telescope-authz/components/staking/DelegateModal.tsx b/examples/telescope-authz/components/staking/DelegateModal.tsx
new file mode 100644
index 000000000..38021aa1d
--- /dev/null
+++ b/examples/telescope-authz/components/staking/DelegateModal.tsx
@@ -0,0 +1,269 @@
+import { useState } from 'react';
+import { ChainName } from 'cosmos-kit';
+import BigNumber from 'bignumber.js';
+import {
+ BasicModal,
+ StakingDelegate,
+ Box,
+ Button,
+ Callout,
+ Text,
+ NumberField,
+} from '@interchain-ui/react';
+
+import {
+ type ExtendedValidator as Validator,
+ formatValidatorMetaInfo,
+ getAssetLogoUrl,
+ isGreaterThanZero,
+ shiftDigits,
+ calcDollarValue,
+} from '@/utils';
+import { getCoin, getExponent } from '@/configs';
+import { useAuthzContext } from '@/context';
+import { Prices, useAuthzTx, UseDisclosureReturn, useToast } from '@/hooks';
+import { MsgDelegate } from '@/src/codegen/cosmos/staking/v1beta1/tx';
+import { StakeAuthorization } from '@/src/codegen/cosmos/staking/v1beta1/authz';
+
+export const DelegateModal = ({
+ balance,
+ updateData,
+ unbondingDays,
+ chainName,
+ logoUrl,
+ modalControl,
+ selectedValidator,
+ closeOuterModal,
+ prices,
+ modalTitle,
+ showDescription = true,
+}: {
+ balance: string;
+ updateData: () => void;
+ unbondingDays: string;
+ chainName: ChainName;
+ modalControl: UseDisclosureReturn;
+ selectedValidator: Validator;
+ logoUrl: string;
+ prices: Prices;
+ closeOuterModal?: () => void;
+ modalTitle?: string;
+ showDescription?: boolean;
+}) => {
+ const { isOpen, onClose } = modalControl;
+
+ const [amount, setAmount] = useState(0);
+ const [isDelegating, setIsDelegating] = useState(false);
+ const [, forceUpdate] = useState(0);
+
+ const coin = getCoin(chainName);
+ const exponent = getExponent(chainName);
+
+ const { authzTx, createExecMsg } = useAuthzTx(chainName);
+ const { permission } = useAuthzContext();
+ const { toast } = useToast();
+
+ const onModalClose = () => {
+ onClose();
+ setAmount(0);
+ setIsDelegating(false);
+ };
+
+ const onDelegateClick = async () => {
+ if (!amount || !permission) return;
+
+ const { grantee, granter, authorization, expiration } = permission;
+
+ const delegateAmount = shiftDigits(amount, exponent);
+
+ if (StakeAuthorization.is(authorization)) {
+ const maxAmount = authorization?.maxTokens?.amount;
+
+ if (maxAmount && new BigNumber(delegateAmount).gt(maxAmount)) {
+ toast({
+ type: 'error',
+ title: 'Amount exceeds max tokens',
+ });
+ return;
+ }
+
+ const allowList = authorization?.allowList?.address;
+ const denyList = authorization?.denyList?.address;
+
+ if (
+ (allowList && !allowList.includes(selectedValidator.address)) ||
+ (denyList && denyList.includes(selectedValidator.address))
+ ) {
+ toast({
+ type: 'error',
+ title: 'Unauthorized Delegation',
+ description:
+ 'You are not allowed to delegate to this validator from this account.',
+ });
+ return;
+ }
+ }
+
+ setIsDelegating(true);
+
+ const msg = MsgDelegate.toProtoMsg({
+ delegatorAddress: granter,
+ validatorAddress: selectedValidator.address,
+ amount: {
+ amount: delegateAmount,
+ denom: coin.base,
+ },
+ });
+
+ authzTx({
+ msgs: [createExecMsg({ msgs: [msg], grantee })],
+ execExpiration: expiration,
+ onSuccess: () => {
+ closeOuterModal && closeOuterModal();
+ updateData();
+ onModalClose();
+ },
+ onComplete: () => {
+ setIsDelegating(false);
+ },
+ });
+ };
+
+ const headerExtra = (
+ <>
+ {showDescription && selectedValidator.description && (
+ {selectedValidator.description}
+ )}
+ {unbondingDays && (
+
+ You will need to undelegate in order for your staked assets to be
+ liquid again. This process will take {unbondingDays} days to complete.
+
+ )}
+ >
+ );
+
+ return (
+
+
+
+ {
+ // @ts-ignore
+ const val = e.target.value;
+
+ if (!val) {
+ setAmount(undefined);
+ return;
+ }
+
+ if (new BigNumber(val).gt(balance)) {
+ setAmount(Number(balance));
+ forceUpdate((n) => n + 1);
+ return;
+ }
+
+ setAmount(Number(val));
+ }}
+ />
+
+ {
+ // if (!val) {
+ // setAmount(undefined);
+ // return;
+ // }
+
+ // const max = maxAmountAndFee?.maxAmount || balance;
+
+ // if (new BigNumber(val).gt(max)) {
+ // setAmount(Number(max));
+ // forceUpdate((n) => n + 1);
+ // return;
+ // }
+
+ // setAmount(Number(val));
+ // },
+ partials: [
+ {
+ label: '1/2',
+ onClick: () => {
+ setAmount(new BigNumber(balance).dividedBy(2).toNumber());
+ },
+ },
+ {
+ label: '1/3',
+ onClick: () => {
+ setAmount(new BigNumber(balance).dividedBy(3).toNumber());
+ },
+ },
+ {
+ label: 'Max',
+ onClick: () => setAmount(Number(balance)),
+ },
+ ],
+ }}
+ footer={
+
+ }
+ />
+
+
+ );
+};
diff --git a/examples/telescope-authz/components/staking/Overview.tsx b/examples/telescope-authz/components/staking/Overview.tsx
new file mode 100644
index 000000000..07bebbdf6
--- /dev/null
+++ b/examples/telescope-authz/components/staking/Overview.tsx
@@ -0,0 +1,96 @@
+import { useState } from 'react';
+import {
+ Box,
+ StakingAssetHeader,
+ StakingClaimHeader,
+} from '@interchain-ui/react';
+import { useChain } from '@cosmos-kit/react';
+import { ChainName } from 'cosmos-kit';
+import { cosmos } from 'interchain-query';
+
+import { getCoin } from '@/configs';
+import { Prices, useTx } from '@/hooks';
+import {
+ sum,
+ calcDollarValue,
+ isGreaterThanZero,
+ type ParsedRewards as Rewards,
+} from '@/utils';
+
+const { withdrawDelegatorReward } =
+ cosmos.distribution.v1beta1.MessageComposer.fromPartial;
+
+const Overview = ({
+ balance,
+ rewards,
+ staked,
+ updateData,
+ chainName,
+ prices,
+}: {
+ balance: string;
+ rewards: Rewards;
+ staked: string;
+ updateData: () => void;
+ chainName: ChainName;
+ prices: Prices;
+}) => {
+ const [isClaiming, setIsClaiming] = useState(false);
+ const { address } = useChain(chainName);
+ const { tx } = useTx(chainName);
+
+ const totalAmount = sum(balance, staked, rewards?.total ?? 0);
+ const coin = getCoin(chainName);
+
+ const onClaimRewardClick = async () => {
+ setIsClaiming(true);
+
+ if (!address) return;
+
+ const msgs = rewards.byValidators.map(({ validatorAddress }) =>
+ withdrawDelegatorReward({
+ delegatorAddress: address,
+ validatorAddress,
+ })
+ );
+
+ await tx(msgs, {
+ onSuccess: updateData,
+ });
+
+ setIsClaiming(false);
+ };
+
+ return (
+ <>
+
+
+
+
+ {/*
+
+ */}
+ >
+ );
+};
+
+export default Overview;
diff --git a/examples/telescope-authz/components/staking/StakingSection.tsx b/examples/telescope-authz/components/staking/StakingSection.tsx
new file mode 100644
index 000000000..d3ffc69f9
--- /dev/null
+++ b/examples/telescope-authz/components/staking/StakingSection.tsx
@@ -0,0 +1,63 @@
+import { useChain } from '@cosmos-kit/react';
+import { ChainName } from 'cosmos-kit';
+import { Box, Spinner, Text } from '@interchain-ui/react';
+
+import Overview from './Overview';
+import { AllValidators } from './AllValidators';
+import { useStakingData, useValidatorLogos } from '@/hooks';
+
+export const StakingSection = ({ chainName }: { chainName: ChainName }) => {
+ const { isWalletConnected } = useChain(chainName);
+ const { data, isLoading, refetch } = useStakingData(chainName);
+ const { data: logos, isLoading: isFetchingLogos } = useValidatorLogos(
+ chainName,
+ data?.allValidators || []
+ );
+
+ return (
+
+ {!isWalletConnected ? (
+
+
+ Please connect the wallet
+
+
+ ) : isLoading || isFetchingLogos || !data ? (
+
+
+
+ ) : (
+ <>
+
+
+
+ >
+ )}
+
+ );
+};
diff --git a/examples/telescope-authz/components/staking/index.ts b/examples/telescope-authz/components/staking/index.ts
new file mode 100644
index 000000000..4deb7bee7
--- /dev/null
+++ b/examples/telescope-authz/components/staking/index.ts
@@ -0,0 +1 @@
+export * from './StakingSection';
diff --git a/examples/telescope-authz/components/voting/Proposal.tsx b/examples/telescope-authz/components/voting/Proposal.tsx
new file mode 100644
index 000000000..f8df5d381
--- /dev/null
+++ b/examples/telescope-authz/components/voting/Proposal.tsx
@@ -0,0 +1,376 @@
+import {
+ Box,
+ Button,
+ GovernanceRadio,
+ GovernanceRadioGroup,
+ GovernanceResultCard,
+ GovernanceVoteBreakdown,
+ GovernanceVoteType,
+ Icon,
+ Stack,
+ Text,
+} from '@interchain-ui/react';
+import {
+ Proposal as IProposal,
+ ProposalStatus,
+} from 'interchain-query/cosmos/gov/v1beta1/gov';
+import {
+ exponentiate,
+ formatDate,
+ getCoin,
+ getExponent,
+ percent,
+} from '@/utils';
+import Markdown from 'react-markdown';
+import { useEffect, useState } from 'react';
+import { useAuthzTx, Votes } from '@/hooks';
+import { useAuthzContext } from '@/context';
+import { MsgVote } from '@/src/codegen/cosmos/gov/v1beta1/tx';
+
+// export declare enum VoteOption {
+// /** VOTE_OPTION_UNSPECIFIED - VOTE_OPTION_UNSPECIFIED defines a no-op vote option. */
+// VOTE_OPTION_UNSPECIFIED = 0,
+// /** VOTE_OPTION_YES - VOTE_OPTION_YES defines a yes vote option. */
+// VOTE_OPTION_YES = 1,
+// /** VOTE_OPTION_ABSTAIN - VOTE_OPTION_ABSTAIN defines an abstain vote option. */
+// VOTE_OPTION_ABSTAIN = 2,
+// /** VOTE_OPTION_NO - VOTE_OPTION_NO defines a no vote option. */
+// VOTE_OPTION_NO = 3,
+// /** VOTE_OPTION_NO_WITH_VETO - VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. */
+// VOTE_OPTION_NO_WITH_VETO = 4,
+// UNRECOGNIZED = -1
+// }
+
+const VoteTypes = ['', 'yes', 'abstain', 'no', 'noWithVeto'];
+
+export type ProposalProps = {
+ proposal: IProposal;
+ votes?: Votes;
+ quorum?: number;
+ bondedTokens?: string;
+ chainName: string;
+ onVoteSuccess?: () => void;
+};
+
+export function Proposal({
+ votes,
+ quorum,
+ proposal,
+ chainName,
+ bondedTokens,
+ onVoteSuccess = () => {},
+}: ProposalProps) {
+ const vote = votes?.[proposal.proposalId.toString()];
+
+ const [showMore, setShowMore] = useState(false);
+ const [voteType, setVoteType] = useState();
+ const [isVoting, setIsVoting] = useState(false);
+
+ const coin = getCoin(chainName);
+ const exponent = getExponent(chainName);
+
+ const { authzTx, createExecMsg } = useAuthzTx(chainName);
+ const { permission } = useAuthzContext();
+
+ const toggleShowMore = () => setShowMore((v) => !v);
+
+ useEffect(() => {
+ if (typeof vote === 'number') {
+ setVoteType(VoteTypes[vote] as GovernanceVoteType);
+ }
+ }, [vote]);
+
+ const isChanged =
+ (vote === undefined && voteType) ||
+ (typeof vote === 'number' && voteType && voteType !== VoteTypes[vote]);
+
+ const isPassed = proposal.status === ProposalStatus.PROPOSAL_STATUS_PASSED;
+
+ const isRejected =
+ proposal.status === ProposalStatus.PROPOSAL_STATUS_REJECTED;
+
+ const isDepositPeriod =
+ proposal.status === ProposalStatus.PROPOSAL_STATUS_DEPOSIT_PERIOD;
+
+ const isVotingPeriod =
+ proposal.status === ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD;
+
+ const total = proposal.finalTallyResult
+ ? Object.values(proposal.finalTallyResult).reduce(
+ (sum, val) => sum + Number(val),
+ 0
+ )
+ : 0;
+
+ const turnout = total / Number(bondedTokens);
+
+ // @ts-ignore
+ const description = proposal?.content?.description || '';
+ const renderedDescription =
+ description.length > 200
+ ? showMore
+ ? description
+ : `${description.slice(0, 200)}...`
+ : description || '';
+
+ const minStakedTokens =
+ quorum && exponentiate(quorum * Number(bondedTokens), -exponent).toFixed(6);
+
+ const timepoints = [
+ {
+ label: 'Submit Time',
+ timestamp: formatDate(proposal?.submitTime!) || '',
+ },
+ {
+ label: 'Voting Starts',
+ timestamp: isDepositPeriod
+ ? 'Not Specified Yet'
+ : formatDate(proposal.votingStartTime) || '',
+ },
+ {
+ label: 'Voting Ends',
+ timestamp: isDepositPeriod
+ ? 'Not Specified Yet'
+ : formatDate(proposal?.votingEndTime!) || '',
+ },
+ ];
+
+ function onVoteTypeChange(selected: string) {
+ setVoteType(selected as GovernanceVoteType);
+ }
+
+ function onVoteButtonClick() {
+ if (!voteType || !permission) return;
+
+ setIsVoting(true);
+
+ const { grantee, granter, expiration } = permission;
+
+ const msg = MsgVote.toProtoMsg({
+ voter: granter,
+ option: VoteTypes.indexOf(voteType),
+ proposalId: proposal.proposalId,
+ });
+
+ authzTx({
+ msgs: [createExecMsg({ msgs: [msg], grantee })],
+ execExpiration: expiration,
+ onSuccess: () => {
+ onVoteSuccess();
+ },
+ onComplete: () => {
+ setIsVoting(false);
+ },
+ });
+ }
+
+ return (
+
+
+
+ {timepoints.map((timepoint) => (
+
+
+ {timepoint.label}
+
+
+ {timepoint.timestamp}
+
+
+ ))}
+
+
+
+
+ Yes
+ No
+ No with veto
+ Abstain
+
+
+
+
+
+
+
+ Vote Details
+
+ {quorum ? (
+
+
+
+ {`Minimum of staked ${minStakedTokens} ${coin.symbol}(${
+ quorum * 100
+ }%) need to vote
+ for this proposal to pass.`}
+
+
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+ {isPassed ? (
+
+ ) : null}
+ {isRejected ? (
+
+ ) : null}
+
+
+
+
+ {/* Description */}
+
+
+ Description
+
+
+
+ {showMore ? {description} : renderedDescription}
+
+
+
+
+
+
+
+ );
+}
diff --git a/examples/telescope-authz/components/voting/Voting.tsx b/examples/telescope-authz/components/voting/Voting.tsx
new file mode 100644
index 000000000..b6115f74e
--- /dev/null
+++ b/examples/telescope-authz/components/voting/Voting.tsx
@@ -0,0 +1,166 @@
+import { useState } from 'react';
+import { useChain } from '@cosmos-kit/react';
+import {
+ Proposal as IProposal,
+ ProposalStatus,
+ TallyResult,
+ TextProposal,
+} from 'interchain-query/cosmos/gov/v1beta1/gov';
+import {
+ BasicModal,
+ Box,
+ GovernanceProposalItem,
+ Spinner,
+ Text,
+ useColorModeValue,
+} from '@interchain-ui/react';
+import { useModal, useVotingData } from '@/hooks';
+import { Proposal } from '@/components';
+import { formatDate } from '@/utils';
+
+function status(s: ProposalStatus) {
+ switch (s) {
+ case ProposalStatus.PROPOSAL_STATUS_UNSPECIFIED:
+ return 'pending';
+ case ProposalStatus.PROPOSAL_STATUS_DEPOSIT_PERIOD:
+ return 'pending';
+ case ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD:
+ return 'pending';
+ case ProposalStatus.PROPOSAL_STATUS_PASSED:
+ return 'passed';
+ case ProposalStatus.PROPOSAL_STATUS_REJECTED:
+ return 'rejected';
+ case ProposalStatus.PROPOSAL_STATUS_FAILED:
+ return 'rejected';
+ default:
+ return 'pending';
+ }
+}
+
+function votes(result: TallyResult) {
+ return {
+ yes: Number(result.yes) || 0,
+ no: Number(result.no) || 0,
+ abstain: Number(result.abstain) || 0,
+ noWithVeto: Number(result.noWithVeto) || 0,
+ };
+}
+
+export type VotingProps = {
+ chainName: string;
+};
+
+export function Voting({ chainName }: VotingProps) {
+ const { address } = useChain(chainName);
+ const [proposal, setProposal] = useState();
+ const { data, isLoading, refetch } = useVotingData(chainName);
+ const { modal, open: openModal, close: closeModal, setTitle } = useModal('');
+
+ const spinnerColor = useColorModeValue('$blackAlpha800', '$whiteAlpha900');
+
+ function onClickProposal(index: number) {
+ const proposal = data.proposals![index];
+ openModal();
+ setProposal(proposal);
+ setTitle(
+ `#${proposal.proposalId?.toString()} ${
+ (proposal.content as TextProposal)?.title
+ }`
+ );
+ }
+
+ if (!address) {
+ return (
+
+
+ Please connect to your wallet to see the proposals.
+
+
+ );
+ }
+
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+ Proposals
+
+
+
+ {data.proposals && data.proposals.length > 0 ? (
+ data.proposals.map((proposal, index) => (
+ onClickProposal(index) }}
+ >
+ {data.votes[proposal.proposalId.toString()] ? (
+
+
+ Voted
+
+
+ ) : null}
+
+
+ ))
+ ) : (
+
+ No proposals found
+
+ )}
+
+
+
+
+ {modal.title}
+
+
+ }
+ isOpen={modal.open}
+ onOpen={openModal}
+ onClose={closeModal}
+ >
+
+
+
+ );
+}
diff --git a/examples/telescope-authz/components/voting/index.ts b/examples/telescope-authz/components/voting/index.ts
new file mode 100644
index 000000000..75bcca3d8
--- /dev/null
+++ b/examples/telescope-authz/components/voting/index.ts
@@ -0,0 +1,2 @@
+export * from './Voting';
+export * from './Proposal';
\ No newline at end of file
diff --git a/examples/telescope-authz/components/wallet/Astronaut.tsx b/examples/telescope-authz/components/wallet/Astronaut.tsx
new file mode 100644
index 000000000..0704133ad
--- /dev/null
+++ b/examples/telescope-authz/components/wallet/Astronaut.tsx
@@ -0,0 +1,156 @@
+export const Astronaut = (props: any) => (
+
+);
diff --git a/examples/telescope-authz/components/wallet/Chain.tsx b/examples/telescope-authz/components/wallet/Chain.tsx
new file mode 100644
index 000000000..9397000d3
--- /dev/null
+++ b/examples/telescope-authz/components/wallet/Chain.tsx
@@ -0,0 +1,130 @@
+import * as React from 'react';
+import { chains } from 'chain-registry';
+import {
+ Box,
+ Combobox,
+ Text,
+ Stack,
+ Avatar,
+ useTheme,
+} from '@interchain-ui/react';
+
+type Option = {
+ label: string;
+ value: string;
+};
+
+export type ChainInfo = typeof chains[number];
+
+export interface ChooseChainProps {
+ chainName?: string;
+ chainInfos: ChainInfo[];
+ onChange: (selectedItem: string | undefined) => void;
+}
+
+const ChainOption = (props: Option & { iconUrl: string }) => {
+ return (
+
+ name[0]}
+ size="xs"
+ src={props.iconUrl}
+ fallbackMode="bg"
+ />
+
+
+ {props.label}
+
+
+ );
+};
+
+export const Chain = (props: ChooseChainProps) => {
+ const { chainName, chainInfos, onChange } = props;
+ const [selectedKey, setSelectedKey] = React.useState();
+ const { themeClass } = useTheme();
+
+ React.useEffect(() => {
+ // Init selected key to provided chainName
+ if (chainName && chainInfos.length > 0) {
+ const initKey = chainInfos.filter(
+ (option) => option.chain_name === chainName
+ )[0].chain_name;
+
+ return setSelectedKey(initKey);
+ }
+
+ if (!chainName) setSelectedKey(undefined);
+ }, [chainInfos, chainName]);
+
+ const chainOptions = chainInfos
+ .map((chainInfo) => ({
+ iconUrl: chainInfo.logo_URIs?.png ?? '',
+ label: chainInfo.pretty_name,
+ value: chainInfo.chain_name,
+ }))
+ .filter((chainInfo) => chainInfo.label && chainInfo.value);
+
+ return (
+
+ {
+ if (item) {
+ setSelectedKey(item);
+
+ const found =
+ chainInfos.find((options) => options.chain_name === item) ?? null;
+
+ if (found) {
+ onChange?.(found.chain_name);
+ }
+ }
+ }}
+ inputAddonStart={
+ selectedKey ? (
+ name[0]}
+ size="xs"
+ src={
+ chainOptions.find((i) => i.value === selectedKey)?.iconUrl ??
+ undefined
+ }
+ fallbackMode="bg"
+ attributes={{
+ paddingX: '$4',
+ }}
+ />
+ ) : null
+ }
+ styleProps={{
+ width: {
+ mobile: '100%',
+ mdMobile: '420px',
+ },
+ }}
+ >
+ {chainOptions.map((option) => (
+
+
+
+ ))}
+
+
+ );
+};
diff --git a/examples/telescope-authz/components/wallet/ChainCard.tsx b/examples/telescope-authz/components/wallet/ChainCard.tsx
new file mode 100644
index 000000000..7d4bdcb1c
--- /dev/null
+++ b/examples/telescope-authz/components/wallet/ChainCard.tsx
@@ -0,0 +1,43 @@
+import Image from 'next/image';
+import { Text, Box, useTheme } from '@interchain-ui/react';
+
+interface IChainCard {
+ prettyName: string;
+ icon?: string;
+}
+
+export const ChainCard = (props: IChainCard) => {
+ const { theme } = useTheme();
+ return (
+
+
+
+
+
+
+ {props.prettyName}
+
+
+ );
+};
diff --git a/examples/telescope-authz/components/wallet/Connect.tsx b/examples/telescope-authz/components/wallet/Connect.tsx
new file mode 100644
index 000000000..336cd2636
--- /dev/null
+++ b/examples/telescope-authz/components/wallet/Connect.tsx
@@ -0,0 +1,60 @@
+import { MouseEventHandler } from 'react';
+import { Button as UIButton, IconName } from '@interchain-ui/react';
+
+export type ButtonProps = {
+ text?: string;
+ icon?: IconName;
+ loading?: boolean;
+ disabled?: boolean;
+ onClick?: MouseEventHandler;
+}
+
+export type ConnectProps = Pick;
+
+function noop() {}
+
+export function Button({
+ text,
+ icon,
+ loading,
+ disabled,
+ onClick = noop
+}: ButtonProps) {
+ return (
+
+ {text}
+
+ );
+};
+
+export const ButtonConnect = ({ text = 'Connect Wallet', onClick = noop }: ConnectProps) =>
+
+
+export const ButtonConnected = ({ text = 'My Wallet', onClick = noop }: ConnectProps) =>
+
+
+export const ButtonDisconnected = ({ text = 'Connect Wallet', onClick = noop }: ConnectProps) =>
+
+
+export const ButtonConnecting = ({ text = 'Connecting ...', loading = true }: ConnectProps) =>
+
+
+export const ButtonRejected = ({ text = 'Reconnect', onClick = noop }: ConnectProps) =>
+
+
+export const ButtonError = ({ text = 'Change Wallet', onClick = noop }: ConnectProps) =>
+
+
+export const ButtonNotExist = ({ text = 'Install Wallet', onClick = noop }: ConnectProps) =>
+
\ No newline at end of file
diff --git a/examples/telescope-authz/components/wallet/User.tsx b/examples/telescope-authz/components/wallet/User.tsx
new file mode 100644
index 000000000..351a98958
--- /dev/null
+++ b/examples/telescope-authz/components/wallet/User.tsx
@@ -0,0 +1,19 @@
+import { ReactNode } from 'react';
+import { Box, Text, Stack, useColorModeValue } from '@interchain-ui/react';
+import { Astronaut } from './Astronaut';
+
+export type UserProps = {
+ name: string;
+ icon?: ReactNode;
+}
+
+export function User({ name, icon = }: UserProps) {
+ return
+
+ {icon}
+
+
+ {name}
+
+
+}
\ No newline at end of file
diff --git a/examples/telescope-authz/components/wallet/Wallet.tsx b/examples/telescope-authz/components/wallet/Wallet.tsx
new file mode 100644
index 000000000..8052a3c71
--- /dev/null
+++ b/examples/telescope-authz/components/wallet/Wallet.tsx
@@ -0,0 +1,131 @@
+import { useEffect } from 'react';
+import {
+ Box,
+ ClipboardCopyText,
+ Stack,
+ useColorModeValue,
+} from '@interchain-ui/react';
+import { WalletStatus } from 'cosmos-kit';
+import { useChain, useManager } from '@cosmos-kit/react';
+import { chains } from 'chain-registry';
+import { User } from './User';
+import { Chain } from './Chain';
+import { Warning } from './Warning';
+import {
+ ButtonConnect,
+ ButtonConnected,
+ ButtonConnecting,
+ ButtonDisconnected,
+ ButtonError,
+ ButtonNotExist,
+ ButtonRejected,
+} from './Connect';
+import { ChainCard } from './ChainCard';
+
+export const DEFAULT_CHAIN_NAME = 'cosmoshub';
+export const CHAIN_NAME_STORAGE_KEY = 'selected-chain';
+
+export type WalletProps = {
+ chainName?: string;
+ isMultiChain?: boolean;
+ onChainChange?: (chainName?: string) => void;
+};
+
+export function Wallet({
+ chainName = DEFAULT_CHAIN_NAME,
+ isMultiChain = false,
+ onChainChange = () => {},
+}: WalletProps) {
+ const {
+ chain,
+ status,
+ wallet,
+ username,
+ address,
+ message,
+ connect,
+ openView,
+ } = useChain(chainName);
+ const { getChainLogo } = useManager();
+
+ const ConnectButton = {
+ [WalletStatus.Connected]: ,
+ [WalletStatus.Connecting]: ,
+ [WalletStatus.Disconnected]: ,
+ [WalletStatus.Error]: ,
+ [WalletStatus.Rejected]: ,
+ [WalletStatus.NotExist]: ,
+ }[status] || ;
+
+ function handleChainChange(chainName?: string) {
+ onChainChange(chainName);
+ if (chainName) {
+ localStorage.setItem(CHAIN_NAME_STORAGE_KEY, chainName);
+ } else {
+ localStorage.removeItem(CHAIN_NAME_STORAGE_KEY);
+ }
+ }
+
+ useEffect(() => {
+ onChainChange(
+ localStorage.getItem(CHAIN_NAME_STORAGE_KEY) || DEFAULT_CHAIN_NAME
+ );
+ }, []);
+
+ return (
+
+
+ {isMultiChain ? (
+
+ ) : (
+
+ )}
+
+
+ {username ? : null}
+ {address ? (
+
+ ) : null}
+
+ {ConnectButton}
+
+
+ {message &&
+ [WalletStatus.Error, WalletStatus.Rejected].includes(status) ? (
+
+ ) : null}
+
+
+ );
+}
diff --git a/examples/telescope-authz/components/wallet/Warning.tsx b/examples/telescope-authz/components/wallet/Warning.tsx
new file mode 100644
index 000000000..0081ba755
--- /dev/null
+++ b/examples/telescope-authz/components/wallet/Warning.tsx
@@ -0,0 +1,32 @@
+import { ReactNode } from 'react';
+import { Box, Text, Icon, Stack, useColorModeValue } from '@interchain-ui/react';
+
+export type WarningProps = {
+ text: string;
+ icon?: ReactNode;
+}
+
+export const WarningIcon =
+
+export function Warning({ text, icon = WarningIcon }: WarningProps) {
+ return (
+
+
+ {icon}
+
+
+ {text}
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/examples/telescope-authz/components/wallet/index.ts b/examples/telescope-authz/components/wallet/index.ts
new file mode 100644
index 000000000..7df19835e
--- /dev/null
+++ b/examples/telescope-authz/components/wallet/index.ts
@@ -0,0 +1 @@
+export * from './Wallet';
diff --git a/examples/telescope-authz/configs/defaults.ts b/examples/telescope-authz/configs/defaults.ts
new file mode 100644
index 000000000..f14778924
--- /dev/null
+++ b/examples/telescope-authz/configs/defaults.ts
@@ -0,0 +1,19 @@
+import { assets } from 'chain-registry';
+import { AssetList, Asset } from '@chain-registry/types';
+
+export const defaultChainName = 'osmosis';
+
+export const getChainAssets = (chainName: string = defaultChainName) => {
+ return assets.find((chain) => chain.chain_name === chainName) as AssetList;
+};
+
+export const getCoin = (chainName: string = defaultChainName) => {
+ const chainAssets = getChainAssets(chainName);
+ return chainAssets.assets[0] as Asset;
+};
+
+export const getExponent = (chainName: string) => {
+ return getCoin(chainName).denom_units.find(
+ (unit) => unit.denom === getCoin(chainName).display
+ )?.exponent as number;
+};
diff --git a/examples/telescope-authz/configs/features.ts b/examples/telescope-authz/configs/features.ts
new file mode 100644
index 000000000..a87e4983d
--- /dev/null
+++ b/examples/telescope-authz/configs/features.ts
@@ -0,0 +1,51 @@
+export type Project = {
+ name: string;
+ desc: string;
+ link: string;
+};
+
+export const products: Project[] = [
+ {
+ name: 'CosmosKit',
+ desc: 'A wallet adapter for react with mobile WalletConnect support for the Cosmos ecosystem.',
+ link: 'https://github.com/cosmology-tech/cosmos-kit',
+ },
+ {
+ name: 'Telescope',
+ desc: 'A TypeScript Transpiler for Cosmos Protobufs to generate libraries for Cosmos blockchains.',
+ link: 'https://github.com/cosmology-tech/telescope',
+ },
+ {
+ name: 'TS Codegen',
+ desc: 'The quickest and easiest way to convert CosmWasm Contracts into dev-friendly TypeScript classes.',
+ link: 'https://github.com/CosmWasm/ts-codegen',
+ },
+ {
+ name: 'CosmWasm Academy',
+ desc: 'Master CosmWasm and build your secure, multi-chain dApp on any CosmWasm chain!',
+ link: 'https://academy.cosmwasm.com/',
+ },
+ {
+ name: 'Chain Registry',
+ desc: 'Get chain and asset list information from the npm package for the Official Cosmos chain registry.',
+ link: 'https://github.com/cosmology-tech/chain-registry',
+ },
+ {
+ name: 'Videos',
+ desc: 'How-to videos from the official Cosmology website, with learning resources for building in Cosmos.',
+ link: 'https://cosmology.zone/learn',
+ },
+];
+
+export const dependencies: Project[] = [
+ {
+ name: 'Interchain UI',
+ desc: 'Cross-framework UI Kit for Crafting dApps.',
+ link: 'https://github.com/cosmology-tech/interchain-ui',
+ },
+ {
+ name: 'Next.js',
+ desc: 'A React Framework supports hybrid static & server rendering.',
+ link: 'https://nextjs.org/',
+ },
+];
diff --git a/examples/telescope-authz/configs/index.ts b/examples/telescope-authz/configs/index.ts
new file mode 100644
index 000000000..64654ed9b
--- /dev/null
+++ b/examples/telescope-authz/configs/index.ts
@@ -0,0 +1,3 @@
+export * from './features';
+export * from './defaults';
+export * from './permissions';
diff --git a/examples/telescope-authz/configs/permissions.ts b/examples/telescope-authz/configs/permissions.ts
new file mode 100644
index 000000000..51796d13a
--- /dev/null
+++ b/examples/telescope-authz/configs/permissions.ts
@@ -0,0 +1,44 @@
+export const Permission = {
+ Vote: 'vote',
+ Send: 'send',
+ Delegate: 'delegate',
+ ClaimRewards: 'claim-rewards',
+} as const;
+
+export type PermissionId = typeof Permission[keyof typeof Permission];
+
+export type PermissionItem = {
+ id: PermissionId;
+ name: string;
+ isCustomizable: boolean;
+};
+
+export const permissions: PermissionItem[] = [
+ {
+ id: Permission.Vote,
+ name: 'Vote',
+ isCustomizable: false,
+ },
+ {
+ id: Permission.Send,
+ name: 'Send',
+ isCustomizable: true,
+ },
+ {
+ id: Permission.Delegate,
+ name: 'Delegate',
+ isCustomizable: true,
+ },
+ {
+ id: Permission.ClaimRewards,
+ name: 'Claim Rewards',
+ isCustomizable: false,
+ },
+];
+
+export const permissionNameToRouteMap: Record = {
+ Vote: '/vote',
+ Send: '/send',
+ Delegate: '/stake',
+ WithdrawDelegatorReward: '/claim-rewards',
+};
diff --git a/examples/telescope-authz/context/authz-context.tsx b/examples/telescope-authz/context/authz-context.tsx
new file mode 100644
index 000000000..ed144a28a
--- /dev/null
+++ b/examples/telescope-authz/context/authz-context.tsx
@@ -0,0 +1,35 @@
+import { createContext, useContext, useState } from 'react';
+import { PrettyPermission } from '@/utils';
+
+type AuthzProviderProps = { children: React.ReactNode };
+
+type TAuthzContext = {
+ permission: PrettyPermission | undefined;
+ setPermission: (permission: PrettyPermission) => void;
+ chainName: string | undefined;
+ setChainName: (chainName: string | undefined) => void;
+};
+
+const AuthzContext = createContext(undefined);
+
+const AuthzProvider = ({ children }: AuthzProviderProps) => {
+ const [permission, setPermission] = useState();
+ const [chainName, setChainName] = useState();
+ const value = { permission, setPermission, chainName, setChainName };
+
+ return (
+ {children}
+ );
+};
+
+const useAuthzContext = () => {
+ const context = useContext(AuthzContext);
+
+ if (context === undefined) {
+ throw new Error('useAuthzContext must be used within a AuthzProvider');
+ }
+
+ return context;
+};
+
+export { AuthzProvider, useAuthzContext };
diff --git a/examples/telescope-authz/context/index.ts b/examples/telescope-authz/context/index.ts
new file mode 100644
index 000000000..b4a06dd89
--- /dev/null
+++ b/examples/telescope-authz/context/index.ts
@@ -0,0 +1 @@
+export * from './authz-context';
diff --git a/examples/telescope-authz/hooks/index.ts b/examples/telescope-authz/hooks/index.ts
new file mode 100644
index 000000000..b0db4a978
--- /dev/null
+++ b/examples/telescope-authz/hooks/index.ts
@@ -0,0 +1,14 @@
+export * from './useTx';
+export * from './useToast';
+export * from './usePrices';
+export * from './useGrants';
+export * from './useAuthzTx';
+export * from './useValidators';
+export * from './useDisclosure';
+export * from './useStakingData';
+export * from './useValidatorLogos';
+export * from './useQueryHooks';
+export * from './useRpcQueryClient';
+export * from './useVotingData';
+export * from './useModal';
+export * from './useSendData';
diff --git a/examples/telescope-authz/hooks/useAuthzTx.ts b/examples/telescope-authz/hooks/useAuthzTx.ts
new file mode 100644
index 000000000..ea6cfb21e
--- /dev/null
+++ b/examples/telescope-authz/hooks/useAuthzTx.ts
@@ -0,0 +1,260 @@
+import { useChain } from '@cosmos-kit/react';
+import { isDeliverTxSuccess, StdFee } from '@cosmjs/stargate';
+
+import { cosmos, getSigningCosmosClient } from '@/src/codegen';
+import { MsgVote } from '@/src/codegen/cosmos/gov/v1beta1/tx';
+import { MsgWithdrawDelegatorReward } from '@/src/codegen/cosmos/distribution/v1beta1/tx';
+import { MsgGrant } from '@/src/codegen/cosmos/authz/v1beta1/tx';
+import { EncodeObject } from '@/src/codegen/types';
+import { SendAuthorization } from '@/src/codegen/cosmos/bank/v1beta1/authz';
+import {
+ AuthorizationType,
+ StakeAuthorization,
+} from '@/src/codegen/cosmos/staking/v1beta1/authz';
+import { GenericAuthorization } from '@/src/codegen/cosmos/authz/v1beta1/authz';
+
+import { getTokenByChainName, PrettyPermission } from '@/utils';
+import { Permission } from '@/configs';
+import { useToast, type CustomToast } from './useToast';
+import { coin } from '@cosmjs/amino';
+import { MsgSend } from '@/src/codegen/cosmos/bank/v1beta1/tx';
+import {
+ MsgDelegate,
+ MsgBeginRedelegate,
+ MsgUndelegate,
+} from '@/src/codegen/cosmos/staking/v1beta1/tx';
+import dayjs from 'dayjs';
+
+const { grant, revoke, exec } =
+ cosmos.authz.v1beta1.MessageComposer.fromPartial;
+
+// ==========================================
+
+export type GrantMsg =
+ | {
+ grantType: typeof Permission.Send;
+ customize?: SendAuthorization;
+ }
+ | {
+ grantType: typeof Permission.Delegate;
+ customize?: StakeAuthorization;
+ }
+ | {
+ grantType: typeof Permission.ClaimRewards | typeof Permission.Vote;
+ customize?: GenericAuthorization;
+ };
+
+type CreateGrantMsgOptions = GrantMsg & {
+ grantee: MsgGrant['grantee'];
+ granter: MsgGrant['granter'];
+ expiration?: NonNullable['expiration'];
+};
+
+const createGrantMsg = (options: CreateGrantMsgOptions) => {
+ const { grantType, customize, grantee, granter, expiration } = options;
+
+ const authz: Record = {
+ send:
+ customize ||
+ GenericAuthorization.fromPartial({
+ msg: MsgSend.typeUrl,
+ }),
+ delegate:
+ customize ||
+ GenericAuthorization.fromPartial({
+ msg: MsgDelegate.typeUrl,
+ }),
+ vote: GenericAuthorization.fromPartial({
+ msg: MsgVote.typeUrl,
+ }),
+ 'claim-rewards': GenericAuthorization.fromPartial({
+ msg: MsgWithdrawDelegatorReward.typeUrl,
+ }),
+ };
+
+ return grant({
+ granter,
+ grantee,
+ grant: {
+ authorization: authz[grantType],
+ expiration,
+ },
+ });
+};
+
+// ==========================================
+
+export const createRevokeMsg = (permission: PrettyPermission) => {
+ const { grantee, granter, authorization: authz } = permission;
+
+ let msgTypeUrl = '';
+
+ switch (true) {
+ case GenericAuthorization.is(authz):
+ msgTypeUrl = (authz as GenericAuthorization).msg;
+ break;
+
+ case SendAuthorization.is(authz):
+ msgTypeUrl = MsgSend.typeUrl;
+ break;
+
+ case StakeAuthorization.is(authz):
+ const authzType = (authz as StakeAuthorization).authorizationType;
+ if (authzType === AuthorizationType.AUTHORIZATION_TYPE_DELEGATE) {
+ msgTypeUrl = MsgDelegate.typeUrl;
+ break;
+ }
+ if (authzType === AuthorizationType.AUTHORIZATION_TYPE_UNDELEGATE) {
+ msgTypeUrl = MsgUndelegate.typeUrl;
+ break;
+ }
+ if (authzType === AuthorizationType.AUTHORIZATION_TYPE_REDELEGATE) {
+ msgTypeUrl = MsgBeginRedelegate.typeUrl;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return revoke({
+ grantee,
+ granter,
+ msgTypeUrl,
+ });
+};
+
+// ==========================================
+
+type CreateExecMsgOptions = Parameters[0];
+
+export const createExecMsg = ({ grantee, msgs }: CreateExecMsgOptions) => {
+ return exec({ grantee, msgs });
+};
+
+// ==========================================
+
+const txRaw = cosmos.tx.v1beta1.TxRaw;
+
+enum TxStatus {
+ Failed = 'Transaction Failed',
+ Successful = 'Transaction Successful',
+ Broadcasting = 'Transaction Broadcasting',
+}
+
+type AuthzTxOptions = {
+ msgs: EncodeObject[];
+ fee?: StdFee | null;
+ toast?: Partial;
+ onSuccess?: () => void;
+ onComplete?: () => void;
+ execExpiration?: Date | undefined;
+};
+
+export const useAuthzTx = (chainName: string) => {
+ const { toast } = useToast();
+ const { address, getRpcEndpoint, getOfflineSignerDirect } =
+ useChain(chainName);
+
+ const authzTx = async (options: AuthzTxOptions) => {
+ const {
+ msgs,
+ fee,
+ onSuccess,
+ onComplete,
+ execExpiration,
+ toast: customToast,
+ } = options;
+
+ if (execExpiration && dayjs().isAfter(execExpiration)) {
+ toast({
+ type: 'error',
+ title: 'Permission Expired',
+ });
+ if (onComplete) onComplete();
+ return;
+ }
+
+ if (!address) {
+ toast({
+ type: 'error',
+ title: 'Wallet not connected',
+ description: 'Please connect the wallet',
+ });
+ if (onComplete) onComplete();
+ return;
+ }
+
+ let signed: Parameters['0'];
+ let client: Awaited>;
+
+ const defaultFee: StdFee = {
+ amount: [coin('0', getTokenByChainName(chainName).base)],
+ gas: '860000',
+ };
+
+ try {
+ client = await getSigningCosmosClient({
+ rpcEndpoint: await getRpcEndpoint(),
+ signer: getOfflineSignerDirect(),
+ });
+ signed = await client.sign(address, msgs, fee || defaultFee, '');
+ } catch (e: any) {
+ console.error(e);
+ toast({
+ title: TxStatus.Failed,
+ description: e?.message || 'An unexpected error has occurred',
+ type: 'error',
+ });
+ if (onComplete) onComplete();
+ return;
+ }
+
+ let broadcastToastId: string | number;
+
+ broadcastToastId = toast({
+ title: TxStatus.Broadcasting,
+ description: 'Waiting for transaction to be included in the block',
+ type: 'loading',
+ duration: 999999,
+ });
+
+ if (client && signed) {
+ await client
+ .broadcastTx(Uint8Array.from(txRaw.encode(signed).finish()))
+ .then((res) => {
+ if (isDeliverTxSuccess(res)) {
+ if (onSuccess) onSuccess();
+
+ toast({
+ title: customToast?.title || TxStatus.Successful,
+ type: customToast?.type || 'success',
+ description: customToast?.description,
+ });
+ } else {
+ toast({
+ title: TxStatus.Failed,
+ description: res?.rawLog,
+ type: 'error',
+ duration: 10000,
+ });
+ }
+ })
+ .catch((err) => {
+ console.error(err);
+ toast({
+ title: TxStatus.Failed,
+ description: err?.message,
+ type: 'error',
+ duration: 10000,
+ });
+ })
+ .finally(() => toast.close(broadcastToastId));
+ } else {
+ toast.close(broadcastToastId);
+ }
+
+ if (onComplete) onComplete();
+ };
+
+ return { authzTx, createGrantMsg, createRevokeMsg, createExecMsg };
+};
diff --git a/examples/telescope-authz/hooks/useDisclosure.ts b/examples/telescope-authz/hooks/useDisclosure.ts
new file mode 100644
index 000000000..cb14407a5
--- /dev/null
+++ b/examples/telescope-authz/hooks/useDisclosure.ts
@@ -0,0 +1,18 @@
+import { useState } from 'react';
+
+export const useDisclosure = (initialState = false) => {
+ const [isOpen, setIsOpen] = useState(initialState);
+
+ const onClose = () => setIsOpen(false);
+ const onOpen = () => setIsOpen(true);
+ const onToggle = () => setIsOpen((prev) => !prev);
+
+ return {
+ isOpen,
+ onClose,
+ onOpen,
+ onToggle,
+ };
+};
+
+export type UseDisclosureReturn = ReturnType;
diff --git a/examples/telescope-authz/hooks/useGrants.ts b/examples/telescope-authz/hooks/useGrants.ts
new file mode 100644
index 000000000..c543f499e
--- /dev/null
+++ b/examples/telescope-authz/hooks/useGrants.ts
@@ -0,0 +1,92 @@
+import { useEffect, useMemo, useRef } from 'react';
+import { useChain } from '@cosmos-kit/react';
+import { useQuery } from '@tanstack/react-query';
+
+import { prettyGrants } from '@/utils';
+import { useRpcQueryClient } from './useRpcQueryClient';
+
+export const useGrants = (chainName: string) => {
+ const { address } = useChain(chainName);
+ const prevAddressRef = useRef(address);
+
+ const { rpcQueryClient, isLoading: isRpcQueryClientLoading } =
+ useRpcQueryClient(chainName);
+
+ const granterGrantsQuery = useQuery({
+ queryKey: ['granterGrants', address],
+ queryFn: () =>
+ rpcQueryClient?.cosmos.authz.v1beta1.granterGrants({
+ granter: address || '',
+ }),
+ enabled: !!rpcQueryClient && !!address,
+ select: (data) => data?.grants,
+ staleTime: Infinity,
+ });
+
+ const granteeGrantsQuery = useQuery({
+ queryKey: ['granteeGrants', address],
+ queryFn: () =>
+ rpcQueryClient?.cosmos.authz.v1beta1.granteeGrants({
+ grantee: address || '',
+ }),
+ enabled: !!rpcQueryClient && !!address,
+ select: (data) => data?.grants,
+ staleTime: Infinity,
+ });
+
+ const dataQueries = {
+ granterGrants: granterGrantsQuery,
+ granteeGrants: granteeGrantsQuery,
+ };
+
+ const queriesToRefetch = [
+ dataQueries.granteeGrants,
+ dataQueries.granterGrants,
+ ];
+
+ const refetch = () => {
+ queriesToRefetch.forEach((query) => query.refetch());
+ };
+
+ useEffect(() => {
+ if (prevAddressRef.current !== address) {
+ refetch();
+ prevAddressRef.current = address;
+ }
+ }, [address]);
+
+ const isInitialFetching = Object.values(dataQueries).some(
+ ({ isLoading }) => isLoading
+ );
+
+ const isRefetching = Object.values(dataQueries).some(
+ ({ isRefetching }) => isRefetching
+ );
+
+ const isLoading =
+ isRpcQueryClientLoading || isInitialFetching || isRefetching;
+
+ type DataQueries = typeof dataQueries;
+
+ type QueriesData = {
+ [Key in keyof DataQueries]: NonNullable;
+ };
+
+ const data = useMemo(() => {
+ if (isLoading) return;
+
+ const queriesData = Object.fromEntries(
+ Object.entries(dataQueries).map(([key, query]) => [key, query.data])
+ ) as QueriesData;
+
+ const { granteeGrants, granterGrants } = queriesData;
+
+ return {
+ granteeGrants: prettyGrants(granteeGrants, 'granter'),
+ granterGrants: prettyGrants(granterGrants, 'grantee'),
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isLoading]);
+
+ return { data, isLoading, refetch };
+};
diff --git a/examples/telescope-authz/hooks/useModal.ts b/examples/telescope-authz/hooks/useModal.ts
new file mode 100644
index 000000000..a0d02c107
--- /dev/null
+++ b/examples/telescope-authz/hooks/useModal.ts
@@ -0,0 +1,13 @@
+import { useState } from 'react';
+
+export function useModal(title = '') {
+ const [modal, setModal] = useState({ open: false, title });
+
+ const open = () => setModal(modal => ({ ...modal, open: true }));
+ const close = () => setModal(modal => ({ ...modal, open: false }));
+ const toggle = () => setModal(modal => ({ ...modal, open: !modal.open }));
+
+ const setTitle = (title: string) => setModal(modal => ({ ...modal, title }));
+
+ return { modal, open, close, toggle, setTitle }
+}
\ No newline at end of file
diff --git a/examples/telescope-authz/hooks/usePrices.ts b/examples/telescope-authz/hooks/usePrices.ts
new file mode 100644
index 000000000..284b0bd20
--- /dev/null
+++ b/examples/telescope-authz/hooks/usePrices.ts
@@ -0,0 +1,53 @@
+import { assets } from 'chain-registry';
+import { useQuery } from '@tanstack/react-query';
+import { AssetList } from '@chain-registry/types';
+
+type CoinGeckoId = string;
+type CoinGeckoUSD = { usd: number };
+type CoinGeckoUSDResponse = Record;
+export type Prices = Record;
+
+const handleError = (resp: Response) => {
+ if (!resp.ok) throw Error(resp.statusText);
+ return resp;
+};
+
+const getGeckoIdsFromAssets = (assets: AssetList[]) => {
+ return assets
+ .map((asset) => asset.assets[0].coingecko_id)
+ .filter(Boolean) as string[];
+};
+
+const formatPrices = (
+ prices: CoinGeckoUSDResponse,
+ assets: AssetList[]
+): Prices => {
+ return Object.entries(prices).reduce((priceHash, cur) => {
+ const assetList = assets.find(
+ (asset) => asset.assets[0].coingecko_id === cur[0]
+ )!;
+ const denom = assetList.assets[0].base;
+ return { ...priceHash, [denom]: cur[1].usd };
+ }, {});
+};
+
+const fetchPrices = async (
+ geckoIds: string[]
+): Promise => {
+ const url = `https://api.coingecko.com/api/v3/simple/price?ids=${geckoIds.join()}&vs_currencies=usd`;
+
+ return fetch(url)
+ .then(handleError)
+ .then((res) => res.json());
+};
+
+export const usePrices = () => {
+ const geckoIds = getGeckoIdsFromAssets(assets);
+
+ return useQuery({
+ queryKey: ['prices'],
+ queryFn: () => fetchPrices(geckoIds),
+ select: (data) => formatPrices(data, assets),
+ staleTime: Infinity,
+ });
+};
diff --git a/examples/telescope-authz/hooks/useQueryHooks.ts b/examples/telescope-authz/hooks/useQueryHooks.ts
new file mode 100644
index 000000000..d4f72a54b
--- /dev/null
+++ b/examples/telescope-authz/hooks/useQueryHooks.ts
@@ -0,0 +1,42 @@
+import { useChain } from '@cosmos-kit/react';
+import {
+ useRpcEndpoint,
+ useRpcClient,
+ createRpcQueryHooks,
+} from 'interchain-query';
+
+export const useQueryHooks = (chainName: string) => {
+ const { getRpcEndpoint } = useChain(chainName);
+
+ const rpcEndpointQuery = useRpcEndpoint({
+ getter: getRpcEndpoint,
+ options: {
+ staleTime: Infinity,
+ queryKeyHashFn: (queryKey) => {
+ return JSON.stringify([...queryKey, chainName]);
+ },
+ },
+ });
+
+ const rpcClientQuery = useRpcClient({
+ rpcEndpoint: rpcEndpointQuery.data || '',
+ options: {
+ enabled: Boolean(rpcEndpointQuery.data),
+ staleTime: Infinity,
+ },
+ });
+
+ const { cosmos } = createRpcQueryHooks({
+ rpc: rpcClientQuery.data,
+ });
+
+ const isReady = Boolean(rpcClientQuery.data);
+ const isFetching = rpcEndpointQuery.isFetching || rpcClientQuery.isFetching;
+
+ return {
+ cosmos,
+ isReady,
+ isFetching,
+ rpcEndpoint: rpcEndpointQuery.data,
+ };
+};
diff --git a/examples/telescope-authz/hooks/useRpcQueryClient.ts b/examples/telescope-authz/hooks/useRpcQueryClient.ts
new file mode 100644
index 000000000..d4e53ecb4
--- /dev/null
+++ b/examples/telescope-authz/hooks/useRpcQueryClient.ts
@@ -0,0 +1,21 @@
+import { useQuery } from '@tanstack/react-query';
+import { cosmos } from '@/src/codegen';
+import { useQueryHooks } from './useQueryHooks';
+
+const { createRPCQueryClient } = cosmos.ClientFactory;
+
+export const useRpcQueryClient = (chainName: string) => {
+ const { rpcEndpoint } = useQueryHooks(chainName);
+
+ const rpcQueryClientQuery = useQuery({
+ queryKey: ['rpcQueryClient', rpcEndpoint],
+ queryFn: () => createRPCQueryClient({ rpcEndpoint: rpcEndpoint || '' }),
+ enabled: Boolean(rpcEndpoint),
+ staleTime: Infinity,
+ });
+
+ return {
+ isLoading: rpcQueryClientQuery.isLoading,
+ rpcQueryClient: rpcQueryClientQuery.data,
+ };
+};
diff --git a/examples/telescope-authz/hooks/useSendData.ts b/examples/telescope-authz/hooks/useSendData.ts
new file mode 100644
index 000000000..0e89b5670
--- /dev/null
+++ b/examples/telescope-authz/hooks/useSendData.ts
@@ -0,0 +1,74 @@
+import { useMemo } from 'react';
+
+import { usePrices } from './usePrices';
+import { shiftDigits } from '@/utils';
+import { useAuthzContext } from '@/context';
+import { getCoin, getExponent } from '@/configs';
+import { useQueryHooks } from './useQueryHooks';
+
+export const useSendData = (chainName: string) => {
+ const { permission } = useAuthzContext();
+
+ const address = permission?.granter;
+
+ const coin = getCoin(chainName);
+ const exp = getExponent(chainName);
+
+ const {
+ cosmos: cosmosQuery,
+ isReady: isQueryHooksReady,
+ isFetching: isQueryHooksFetching,
+ } = useQueryHooks(chainName);
+
+ const balanceQuery = cosmosQuery.bank.v1beta1.useBalance({
+ request: {
+ address: address || '',
+ denom: coin.base,
+ },
+ options: {
+ enabled: isQueryHooksReady && !!address,
+ select: ({ balance }) => shiftDigits(balance?.amount || '0', -exp),
+ staleTime: 0,
+ },
+ });
+
+ const pricesQuery = usePrices();
+
+ const allQueries = {
+ prices: pricesQuery,
+ balance: balanceQuery,
+ };
+
+ const updatableQueries = [allQueries.balance];
+
+ const refetch = () => {
+ updatableQueries.forEach((query) => query.refetch());
+ };
+
+ const isInitialFetching = Object.values(allQueries).some(
+ ({ isLoading }) => isLoading
+ );
+
+ const isRefetching = Object.values(allQueries).some(
+ ({ isRefetching }) => isRefetching
+ );
+
+ const isLoading = isQueryHooksFetching || isInitialFetching || isRefetching;
+
+ type AllQueries = typeof allQueries;
+
+ type QueriesData = {
+ [Key in keyof AllQueries]: NonNullable;
+ };
+
+ const data = useMemo(() => {
+ if (isLoading) return;
+
+ return Object.fromEntries(
+ Object.entries(allQueries).map(([key, query]) => [key, query.data])
+ ) as QueriesData;
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isLoading]);
+
+ return { data, isLoading, refetch };
+};
diff --git a/examples/telescope-authz/hooks/useStakingData.ts b/examples/telescope-authz/hooks/useStakingData.ts
new file mode 100644
index 000000000..2bc4d424a
--- /dev/null
+++ b/examples/telescope-authz/hooks/useStakingData.ts
@@ -0,0 +1,243 @@
+import { useEffect, useMemo } from 'react';
+import BigNumber from 'bignumber.js';
+import { cosmos } from 'interchain-query';
+
+import { usePrices } from './usePrices';
+import { getCoin, getExponent } from '@/configs';
+import {
+ shiftDigits,
+ calcTotalDelegation,
+ extendValidators,
+ parseAnnualProvisions,
+ parseDelegations,
+ parseRewards,
+ parseUnbondingDays,
+ parseValidators,
+} from '@/utils';
+import { useAuthzContext } from '@/context';
+import { useQueryHooks } from './useQueryHooks';
+
+(BigInt.prototype as any).toJSON = function () {
+ return this.toString();
+};
+
+export const useStakingData = (chainName: string) => {
+ const { permission } = useAuthzContext();
+
+ const address = permission?.granter;
+
+ const coin = getCoin(chainName);
+ const exp = getExponent(chainName);
+
+ const {
+ cosmos: cosmosQuery,
+ isReady: isQueryHooksReady,
+ isFetching: isQueryHooksFetching,
+ } = useQueryHooks(chainName);
+
+ const balanceQuery = cosmosQuery.bank.v1beta1.useBalance({
+ request: {
+ address: address || '',
+ denom: coin.base,
+ },
+ options: {
+ enabled: isQueryHooksReady && !!address,
+ select: ({ balance }) => shiftDigits(balance?.amount || '0', -exp),
+ },
+ });
+
+ const myValidatorsQuery = cosmosQuery.staking.v1beta1.useDelegatorValidators({
+ request: {
+ delegatorAddr: address || '',
+ pagination: undefined,
+ },
+ options: {
+ enabled: isQueryHooksReady && !!address,
+ select: ({ validators }) => parseValidators(validators),
+ },
+ });
+
+ const rewardsQuery =
+ cosmosQuery.distribution.v1beta1.useDelegationTotalRewards({
+ request: {
+ delegatorAddress: address || '',
+ },
+ options: {
+ enabled: isQueryHooksReady && !!address,
+ select: (data) => parseRewards(data, coin.base, -exp),
+ },
+ });
+
+ const validatorsQuery = cosmosQuery.staking.v1beta1.useValidators({
+ request: {
+ status: cosmos.staking.v1beta1.bondStatusToJSON(
+ cosmos.staking.v1beta1.BondStatus.BOND_STATUS_BONDED
+ ),
+ pagination: {
+ key: new Uint8Array(),
+ offset: 0n,
+ limit: 200n,
+ countTotal: true,
+ reverse: false,
+ },
+ },
+ options: {
+ enabled: isQueryHooksReady,
+ select: ({ validators }) => {
+ const sorted = validators.sort((a, b) =>
+ new BigNumber(b.tokens).minus(a.tokens).toNumber()
+ );
+ return parseValidators(sorted);
+ },
+ },
+ });
+
+ const delegationsQuery = cosmosQuery.staking.v1beta1.useDelegatorDelegations({
+ request: {
+ delegatorAddr: address || '',
+ pagination: {
+ key: new Uint8Array(),
+ offset: 0n,
+ limit: 100n,
+ countTotal: true,
+ reverse: false,
+ },
+ },
+ options: {
+ enabled: isQueryHooksReady && !!address,
+ select: ({ delegationResponses }) =>
+ parseDelegations(delegationResponses, -exp),
+ },
+ });
+
+ const unbondingDaysQuery = cosmosQuery.staking.v1beta1.useParams({
+ options: {
+ enabled: isQueryHooksReady,
+ select: ({ params }) => parseUnbondingDays(params),
+ },
+ });
+
+ const annualProvisionsQuery = cosmosQuery.mint.v1beta1.useAnnualProvisions({
+ options: {
+ enabled: isQueryHooksReady,
+ select: parseAnnualProvisions,
+ retry: false,
+ },
+ });
+
+ const poolQuery = cosmosQuery.staking.v1beta1.usePool({
+ options: {
+ enabled: isQueryHooksReady,
+ select: ({ pool }) => pool,
+ },
+ });
+
+ const communityTaxQuery = cosmosQuery.distribution.v1beta1.useParams({
+ options: {
+ enabled: isQueryHooksReady,
+ select: ({ params }) => shiftDigits(params?.communityTax || '0', -18),
+ },
+ });
+
+ const pricesQuery = usePrices();
+
+ const allQueries = {
+ balance: balanceQuery,
+ myValidators: myValidatorsQuery,
+ rewards: rewardsQuery,
+ allValidators: validatorsQuery,
+ delegations: delegationsQuery,
+ unbondingDays: unbondingDaysQuery,
+ annualProvisions: annualProvisionsQuery,
+ pool: poolQuery,
+ communityTax: communityTaxQuery,
+ prices: pricesQuery,
+ };
+
+ const queriesWithUnchangingKeys = [
+ allQueries.unbondingDays,
+ allQueries.annualProvisions,
+ allQueries.pool,
+ allQueries.communityTax,
+ allQueries.allValidators,
+ ];
+
+ const updatableQueriesAfterMutation = [
+ allQueries.balance,
+ allQueries.myValidators,
+ allQueries.rewards,
+ allQueries.allValidators,
+ allQueries.delegations,
+ ];
+
+ useEffect(() => {
+ queriesWithUnchangingKeys.forEach((query) => query.remove());
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [chainName]);
+
+ const isInitialFetching = Object.values(allQueries).some(
+ ({ isLoading }) => isLoading
+ );
+
+ const isRefetching = Object.values(allQueries).some(
+ ({ isRefetching }) => isRefetching
+ );
+
+ const isLoading = isQueryHooksFetching || isInitialFetching || isRefetching;
+
+ type AllQueries = typeof allQueries;
+
+ type QueriesData = {
+ [Key in keyof AllQueries]: NonNullable;
+ };
+
+ const data = useMemo(() => {
+ if (isLoading) return;
+
+ const queriesData = Object.fromEntries(
+ Object.entries(allQueries).map(([key, query]) => [key, query.data])
+ ) as QueriesData;
+
+ const {
+ allValidators,
+ delegations,
+ rewards,
+ myValidators,
+ annualProvisions,
+ communityTax,
+ pool,
+ } = queriesData;
+
+ const chainMetadata = { annualProvisions, communityTax, pool };
+
+ const extendedAllValidators = extendValidators({
+ validators: allValidators,
+ delegations,
+ rewards: rewards?.byValidators,
+ chainMetadata,
+ });
+
+ const extendedMyValidators = extendValidators({
+ validators: myValidators,
+ delegations,
+ rewards: rewards?.byValidators,
+ chainMetadata,
+ });
+
+ const totalDelegated = calcTotalDelegation(delegations);
+
+ return {
+ ...queriesData,
+ allValidators: extendedAllValidators,
+ myValidators: extendedMyValidators,
+ totalDelegated,
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isLoading]);
+
+ const refetch = () => {
+ updatableQueriesAfterMutation.forEach((query) => query.refetch());
+ };
+
+ return { data, isLoading, refetch };
+};
diff --git a/examples/telescope-authz/hooks/useToast.tsx b/examples/telescope-authz/hooks/useToast.tsx
new file mode 100644
index 000000000..7fafd08e5
--- /dev/null
+++ b/examples/telescope-authz/hooks/useToast.tsx
@@ -0,0 +1,35 @@
+import { toast, Text, ToastType, Spinner } from '@interchain-ui/react';
+
+export type CustomToast = {
+ type: ToastType;
+ title: string;
+ duration?: number;
+ description?: string | JSX.Element;
+};
+
+const ToastTitle = ({ title }: { title: string }) => {
+ return (
+
+ {title}
+
+ );
+};
+
+export const useToast = () => {
+ const customToast = ({
+ type,
+ title,
+ description,
+ duration = 5000,
+ }: CustomToast) => {
+ return toast.custom(type, , {
+ duration,
+ description,
+ icon: type === 'loading' && ,
+ });
+ };
+
+ customToast.close = toast.dismiss;
+
+ return { toast: customToast };
+};
diff --git a/examples/telescope-authz/hooks/useTx.ts b/examples/telescope-authz/hooks/useTx.ts
new file mode 100644
index 000000000..5087b69ce
--- /dev/null
+++ b/examples/telescope-authz/hooks/useTx.ts
@@ -0,0 +1,115 @@
+import { cosmos } from 'interchain-query';
+import { useChain } from '@cosmos-kit/react';
+import { isDeliverTxSuccess, StdFee } from '@cosmjs/stargate';
+import { useToast, type CustomToast } from './useToast';
+
+const txRaw = cosmos.tx.v1beta1.TxRaw;
+
+interface Msg {
+ typeUrl: string;
+ value: any;
+}
+
+interface TxOptions {
+ fee?: StdFee | null;
+ toast?: Partial;
+ onSuccess?: () => void;
+}
+
+export enum TxStatus {
+ Failed = 'Transaction Failed',
+ Successful = 'Transaction Successful',
+ Broadcasting = 'Transaction Broadcasting',
+}
+
+export const useTx = (chainName: string) => {
+ const { address, getSigningStargateClient, estimateFee } =
+ useChain(chainName);
+
+ const { toast } = useToast();
+
+ const tx = async (msgs: Msg[], options: TxOptions) => {
+ if (!address) {
+ toast({
+ type: 'error',
+ title: 'Wallet not connected',
+ description: 'Please connect the wallet',
+ });
+ return;
+ }
+
+ let signed: Parameters['0'];
+ let client: Awaited>;
+
+ try {
+ let fee: StdFee;
+ if (options?.fee) {
+ fee = options.fee;
+ client = await getSigningStargateClient();
+ } else {
+ const [_fee, _client] = await Promise.all([
+ estimateFee(msgs),
+ getSigningStargateClient(),
+ ]);
+ fee = _fee;
+ client = _client;
+ }
+ signed = await client.sign(address, msgs, fee, '');
+ } catch (e: any) {
+ console.error(e);
+ toast({
+ title: TxStatus.Failed,
+ description: e?.message || 'An unexpected error has occured',
+ type: 'error',
+ });
+ return;
+ }
+
+ let broadcastToastId: string | number;
+
+ broadcastToastId = toast({
+ title: TxStatus.Broadcasting,
+ description: 'Waiting for transaction to be included in the block',
+ type: 'loading',
+ duration: 999999,
+ });
+
+ if (client && signed) {
+ await client
+ .broadcastTx(Uint8Array.from(txRaw.encode(signed).finish()))
+ .then((res) => {
+ console.log(res);
+ if (isDeliverTxSuccess(res)) {
+ if (options.onSuccess) options.onSuccess();
+
+ toast({
+ title: options.toast?.title || TxStatus.Successful,
+ type: options.toast?.type || 'success',
+ description: options.toast?.description,
+ });
+ } else {
+ toast({
+ title: TxStatus.Failed,
+ description: res?.rawLog,
+ type: 'error',
+ duration: 10000,
+ });
+ }
+ })
+ .catch((err) => {
+ console.error(err);
+ toast({
+ title: TxStatus.Failed,
+ description: err?.message,
+ type: 'error',
+ duration: 10000,
+ });
+ })
+ .finally(() => toast.close(broadcastToastId));
+ } else {
+ toast.close(broadcastToastId);
+ }
+ };
+
+ return { tx };
+};
diff --git a/examples/telescope-authz/hooks/useValidatorLogos.ts b/examples/telescope-authz/hooks/useValidatorLogos.ts
new file mode 100644
index 000000000..01deb5012
--- /dev/null
+++ b/examples/telescope-authz/hooks/useValidatorLogos.ts
@@ -0,0 +1,13 @@
+import { ExtendedValidator, getLogoUrls } from '@/utils';
+import { useQuery } from '@tanstack/react-query';
+
+export const useValidatorLogos = (
+ chainName: string,
+ validators: ExtendedValidator[]
+) => {
+ return useQuery({
+ queryKey: ['validatorLogos', chainName, validators.length],
+ queryFn: () => getLogoUrls(validators, chainName),
+ staleTime: Infinity,
+ });
+};
diff --git a/examples/telescope-authz/hooks/useValidators.ts b/examples/telescope-authz/hooks/useValidators.ts
new file mode 100644
index 000000000..b68449f5f
--- /dev/null
+++ b/examples/telescope-authz/hooks/useValidators.ts
@@ -0,0 +1,108 @@
+import { useMemo } from 'react';
+import { useQuery } from '@tanstack/react-query';
+import { cosmos } from 'interchain-query';
+import BigNumber from 'bignumber.js';
+
+import { extendValidators, getLogoUrls, parseValidators } from '@/utils';
+import { useQueryHooks } from './useQueryHooks';
+
+(BigInt.prototype as any).toJSON = function () {
+ return this.toString();
+};
+
+type UseValidatorsConfig = {
+ fetchLogos?: boolean;
+};
+
+export const useValidators = (
+ chainName: string,
+ { fetchLogos = true }: UseValidatorsConfig = {}
+) => {
+ const {
+ cosmos: cosmosQuery,
+ isReady: isQueryHooksReady,
+ isFetching: isQueryHooksFetching,
+ } = useQueryHooks(chainName);
+
+ const validatorsQuery = cosmosQuery.staking.v1beta1.useValidators({
+ request: {
+ status: cosmos.staking.v1beta1.bondStatusToJSON(
+ cosmos.staking.v1beta1.BondStatus.BOND_STATUS_BONDED
+ ),
+ pagination: {
+ key: new Uint8Array(),
+ offset: 0n,
+ limit: 500n,
+ countTotal: true,
+ reverse: false,
+ },
+ },
+ options: {
+ enabled: isQueryHooksReady,
+ select: ({ validators }) => {
+ const sorted = validators.sort((a, b) =>
+ new BigNumber(b.tokens).minus(a.tokens).toNumber()
+ );
+ return parseValidators(sorted);
+ },
+ queryKeyHashFn: (queryKey) => {
+ return JSON.stringify([...queryKey, chainName]);
+ },
+ staleTime: Infinity,
+ },
+ });
+
+ const validatorLogosQuery = useQuery({
+ queryKey: ['validatorLogos', chainName],
+ queryFn: () => getLogoUrls(validatorsQuery.data || [], chainName),
+ enabled: !!validatorsQuery.data && fetchLogos,
+ staleTime: Infinity,
+ });
+
+ const allQueries = {
+ validators: validatorsQuery,
+ validatorLogos: validatorLogosQuery,
+ };
+
+ const queriesToRefetch = [allQueries.validators];
+
+ const refetch = () => {
+ queriesToRefetch.forEach((query) => query.refetch());
+ };
+
+ const isInitialFetching = Object.values(allQueries).some(
+ ({ isFetching }) => isFetching
+ );
+
+ const isRefetching = Object.values(allQueries).some(
+ ({ isRefetching }) => isRefetching
+ );
+
+ const isLoading = isQueryHooksFetching || isInitialFetching || isRefetching;
+
+ type AllQueries = typeof allQueries;
+
+ type QueriesData = {
+ [Key in keyof AllQueries]: NonNullable;
+ };
+
+ const data = useMemo(() => {
+ if (isLoading) return;
+
+ const queriesData = Object.fromEntries(
+ Object.entries(allQueries).map(([key, query]) => [key, query.data])
+ ) as QueriesData;
+
+ const { validators, validatorLogos } = queriesData;
+
+ const extendedValidators = extendValidators({
+ validators,
+ logos: validatorLogos,
+ });
+
+ return extendedValidators;
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isLoading]);
+
+ return { data, isLoading, refetch };
+};
diff --git a/examples/telescope-authz/hooks/useVotingData.ts b/examples/telescope-authz/hooks/useVotingData.ts
new file mode 100644
index 000000000..76b2d2915
--- /dev/null
+++ b/examples/telescope-authz/hooks/useVotingData.ts
@@ -0,0 +1,153 @@
+import { useEffect, useMemo, useState } from 'react';
+import { useQueries } from '@tanstack/react-query';
+import { ProposalStatus } from 'interchain-query/cosmos/gov/v1beta1/gov';
+
+import { paginate, parseQuorum, processProposals } from '@/utils';
+import { useRpcQueryClient } from './useRpcQueryClient';
+import { useQueryHooks } from './useQueryHooks';
+import { useAuthzContext } from '@/context';
+
+(BigInt.prototype as any).toJSON = function () {
+ return this.toString();
+};
+
+export interface Votes {
+ [key: string]: number;
+}
+
+export function useVotingData(chainName: string) {
+ const [isLoading, setIsLoading] = useState(false);
+ const { permission } = useAuthzContext();
+
+ const address = permission?.granter;
+
+ const { rpcQueryClient } = useRpcQueryClient(chainName);
+ const { cosmos, isReady, isFetching } = useQueryHooks(chainName);
+
+ const proposalsQuery = cosmos.gov.v1beta1.useProposals({
+ request: {
+ voter: '',
+ depositor: '',
+ pagination: paginate(50n, true),
+ proposalStatus: ProposalStatus.PROPOSAL_STATUS_UNSPECIFIED,
+ },
+ options: {
+ enabled: isReady,
+ staleTime: Infinity,
+ select: ({ proposals }) => processProposals(proposals),
+ },
+ });
+
+ const bondedTokensQuery = cosmos.staking.v1beta1.usePool({
+ options: {
+ enabled: isReady,
+ staleTime: Infinity,
+ select: ({ pool }) => pool?.bondedTokens,
+ },
+ });
+
+ const quorumQuery = cosmos.gov.v1beta1.useParams({
+ request: { paramsType: 'tallying' },
+ options: {
+ enabled: isReady,
+ staleTime: Infinity,
+ select: ({ tallyParams }) => parseQuorum(tallyParams?.quorum),
+ },
+ });
+
+ const votedProposalsQuery = cosmos.gov.v1beta1.useProposals({
+ request: {
+ voter: address || '/', // use '/' to differentiate from proposalsQuery
+ depositor: '',
+ pagination: paginate(50n, true),
+ proposalStatus: ProposalStatus.PROPOSAL_STATUS_UNSPECIFIED,
+ },
+ options: {
+ enabled: isReady && Boolean(address),
+ select: ({ proposals }) => proposals,
+ keepPreviousData: true,
+ },
+ });
+
+ const votesQueries = useQueries({
+ queries: (votedProposalsQuery.data || []).map(({ proposalId }) => ({
+ queryKey: ['voteQuery', proposalId, address],
+ queryFn: () =>
+ rpcQueryClient?.cosmos.gov.v1beta1.vote({
+ proposalId,
+ voter: address || '',
+ }),
+ enabled:
+ Boolean(rpcQueryClient) &&
+ Boolean(address) &&
+ Boolean(votedProposalsQuery.data),
+ keepPreviousData: true,
+ })),
+ });
+
+ const singleQueries = {
+ quorum: quorumQuery,
+ proposals: proposalsQuery,
+ bondedTokens: bondedTokensQuery,
+ votedProposals: votedProposalsQuery,
+ };
+
+ const staticQueries = [
+ singleQueries.quorum,
+ singleQueries.proposals,
+ singleQueries.bondedTokens,
+ ];
+
+ const dynamicQueries = [singleQueries.votedProposals];
+
+ useEffect(() => {
+ staticQueries.forEach((query) => query.remove());
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [chainName]);
+
+ const isStaticQueriesFetching = staticQueries.some(
+ ({ isFetching }) => isFetching
+ );
+
+ const isDynamicQueriesFetching =
+ singleQueries.votedProposals.isFetching ||
+ votesQueries.some(({ isFetching }) => isFetching);
+
+ const loading =
+ isFetching || isStaticQueriesFetching || isDynamicQueriesFetching;
+
+ useEffect(() => {
+ // no loading when refetching
+ if (isFetching || isStaticQueriesFetching) setIsLoading(true);
+ if (!loading) setIsLoading(false);
+ }, [isStaticQueriesFetching, loading]);
+
+ type SingleQueries = typeof singleQueries;
+
+ type SingleQueriesData = {
+ [Key in keyof SingleQueries]: NonNullable;
+ };
+
+ const singleQueriesData = useMemo(() => {
+ if (isStaticQueriesFetching || !isReady) return;
+
+ return Object.fromEntries(
+ Object.entries(singleQueries).map(([key, query]) => [key, query.data])
+ ) as SingleQueriesData;
+ }, [isStaticQueriesFetching, isReady]);
+
+ const votes = useMemo(() => {
+ const votesEntries = votesQueries
+ .map((query) => query.data)
+ .map((data) => [data?.vote?.proposalId, data?.vote?.options[0].option]);
+
+ return Object.fromEntries(votesEntries) as Votes;
+ }, [votesQueries]);
+
+ const refetch = () => {
+ votesQueries.forEach((query) => query.remove());
+ dynamicQueries.forEach((query) => query.refetch());
+ };
+
+ return { data: { ...singleQueriesData, votes }, isLoading, refetch };
+}
diff --git a/examples/telescope-authz/next.config.js b/examples/telescope-authz/next.config.js
index 3d3bc9990..76756d4a4 100644
--- a/examples/telescope-authz/next.config.js
+++ b/examples/telescope-authz/next.config.js
@@ -2,6 +2,9 @@
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
+ images: {
+ domains: ['raw.githubusercontent.com'],
+ },
};
module.exports = nextConfig;
diff --git a/examples/telescope-authz/package.json b/examples/telescope-authz/package.json
index 49e6aedd9..4dfcd2b3b 100644
--- a/examples/telescope-authz/package.json
+++ b/examples/telescope-authz/package.json
@@ -3,6 +3,7 @@
"version": "0.0.4",
"private": true,
"scripts": {
+ "dev": "next dev",
"build": "yarn tsc -p tsconfig.json --outDir main --module commonjs",
"clean": "rimraf ./types && rimraf ./src/codegen",
"locks:remove": "rm -f yarn.lock",
@@ -42,35 +43,28 @@
},
"dependencies": {
"@chain-registry/client": "1.11.0",
- "@chain-registry/osmosis": "^1.21.0",
"@chain-registry/types": "0.17.0",
- "@chakra-ui/icons": "2.0.12",
- "@chakra-ui/react": "2.5.1",
"@cosmjs/amino": "0.32.0",
"@cosmjs/cosmwasm-stargate": "0.32.0",
"@cosmjs/proto-signing": "0.32.0",
"@cosmjs/stargate": "0.32.0",
"@cosmology/lcd": "^0.12.0",
"@cosmos-kit/react": "2.10.9",
- "@emotion/react": "11.10.6",
- "@emotion/styled": "11.10.6",
"@interchain-ui/react": "^1.21.16",
- "@tanstack/react-query": "4.16.1",
+ "@tanstack/react-query": "4.32.0",
"bignumber.js": "9.1.0",
"chain-registry": "1.20.0",
- "chakra-react-select": "^4.7.0",
"cosmos-kit": "2.8.6",
+ "dayjs": "1.11.10",
"fast-fuzzy": "^1.12.0",
- "framer-motion": "9.0.7",
- "mobx": "^6.7.0",
- "mobx-react": "^7.6.0",
+ "interchain-query": "1.10.1",
"next": "^13",
- "osmojs": "^16.5.1",
"react": "18.2.0",
+ "react-calendar": "4.8.0",
"react-dom": "18.2.0",
"react-icons": "^4.4.0",
- "react-markdown": "^8.0.7",
- "react-minimal-pie-chart": "^8.4.0"
+ "react-markdown": "9.0.1",
+ "react-no-ssr": "1.1.0"
},
"devDependencies": {
"@confio/relayer": "0.7.0",
@@ -79,10 +73,12 @@
"@protobufs/cosmos": "^0.1.0",
"@protobufs/cosmwasm": "^0.1.1",
"@protobufs/ibc": "^0.1.0",
+ "@tanstack/react-query-devtools": "4.32.0",
"@types/jest": "^29.5.0",
"@types/node": "18.11.9",
"@types/react": "18.0.25",
"@types/react-dom": "18.0.9",
+ "@types/react-no-ssr": "1.1.7",
"eslint": "8.28.0",
"eslint-config-next": "13.0.5",
"generate-lockfile": "0.0.12",
diff --git a/examples/telescope-authz/pages/_app.tsx b/examples/telescope-authz/pages/_app.tsx
new file mode 100644
index 000000000..ec1be1717
--- /dev/null
+++ b/examples/telescope-authz/pages/_app.tsx
@@ -0,0 +1,93 @@
+import '@interchain-ui/react/styles';
+import '@interchain-ui/react/globalStyles';
+import 'react-calendar/dist/Calendar.css';
+
+import React from 'react';
+import type { AppProps } from 'next/app';
+import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
+
+import { SignerOptions, wallets } from 'cosmos-kit';
+import { ChainProvider } from '@cosmos-kit/react';
+import { chains, assets } from 'chain-registry';
+import { GasPrice } from '@cosmjs/stargate';
+
+import {
+ Box,
+ Toaster,
+ useTheme,
+ useColorModeValue,
+ ThemeProvider,
+} from '@interchain-ui/react';
+import { AuthzProvider } from '@/context';
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: 2,
+ refetchOnWindowFocus: false,
+ staleTime: Infinity,
+ },
+ },
+});
+
+function CreateCosmosApp({ Component, pageProps }: AppProps) {
+ const { themeClass } = useTheme();
+
+ const signerOptions: SignerOptions = {
+ signingStargate: (_chain) => {
+ let gasPrice;
+ try {
+ const chain =
+ typeof _chain === 'string'
+ ? chains.find(({ chain_name }) => chain_name === _chain)!
+ : _chain;
+ const feeToken = chain.fees?.fee_tokens[0];
+ const fee = `${feeToken?.average_gas_price || 0.025}${feeToken?.denom}`;
+ gasPrice = GasPrice.fromString(fee);
+ } catch (error) {
+ gasPrice = GasPrice.fromString('0.025uosmo');
+ }
+ return { gasPrice };
+ },
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default CreateCosmosApp;
diff --git a/examples/telescope-authz/pages/claim-rewards.tsx b/examples/telescope-authz/pages/claim-rewards.tsx
new file mode 100644
index 000000000..bf13c0316
--- /dev/null
+++ b/examples/telescope-authz/pages/claim-rewards.tsx
@@ -0,0 +1,33 @@
+import { Divider } from '@interchain-ui/react';
+import { useAuthzContext } from '@/context';
+import {
+ Layout,
+ Wallet,
+ LoginInfoBanner,
+ ClaimRewardsSection,
+} from '@/components';
+import { withServerSideRedirect } from '@/utils';
+
+export const getServerSideProps = withServerSideRedirect({ to: '/' });
+
+export default function ClaimRewards() {
+ const { chainName, permission } = useAuthzContext();
+
+ return (
+
+
+
+ {chainName && (
+ <>
+ {permission && (
+
+ )}
+
+ >
+ )}
+
+ );
+}
diff --git a/examples/telescope-authz/pages/index.tsx b/examples/telescope-authz/pages/index.tsx
new file mode 100644
index 000000000..a18803e20
--- /dev/null
+++ b/examples/telescope-authz/pages/index.tsx
@@ -0,0 +1,26 @@
+import { useState } from 'react';
+import { Divider } from '@interchain-ui/react';
+import { ChainName } from 'cosmos-kit';
+
+import { useAuthzContext } from '@/context';
+import { Layout, Wallet, AuthzSection } from '@/components';
+
+export default function Home() {
+ const [selectedChain, setSelectedChain] = useState();
+ const { setChainName } = useAuthzContext();
+
+ return (
+
+ {
+ setSelectedChain(chainName);
+ setChainName(chainName);
+ }}
+ />
+
+ {selectedChain && }
+
+ );
+}
diff --git a/examples/telescope-authz/pages/send.tsx b/examples/telescope-authz/pages/send.tsx
new file mode 100644
index 000000000..f5dec598e
--- /dev/null
+++ b/examples/telescope-authz/pages/send.tsx
@@ -0,0 +1,28 @@
+import { Divider } from '@interchain-ui/react';
+import { useAuthzContext } from '@/context';
+import { withServerSideRedirect } from '@/utils';
+import { Layout, Wallet, LoginInfoBanner, SendSection } from '@/components';
+
+export const getServerSideProps = withServerSideRedirect({ to: '/' });
+
+export default function Send() {
+ const { chainName, permission } = useAuthzContext();
+
+ return (
+
+
+
+ {chainName && (
+ <>
+ {permission && (
+
+ )}
+
+ >
+ )}
+
+ );
+}
diff --git a/examples/telescope-authz/pages/stake.tsx b/examples/telescope-authz/pages/stake.tsx
new file mode 100644
index 000000000..b0723969d
--- /dev/null
+++ b/examples/telescope-authz/pages/stake.tsx
@@ -0,0 +1,28 @@
+import { Divider } from '@interchain-ui/react';
+import { useAuthzContext } from '@/context';
+import { withServerSideRedirect } from '@/utils';
+import { Layout, LoginInfoBanner, StakingSection, Wallet } from '@/components';
+
+export const getServerSideProps = withServerSideRedirect({ to: '/' });
+
+export default function Stake() {
+ const { chainName, permission } = useAuthzContext();
+
+ return (
+
+
+
+ {chainName && (
+ <>
+ {permission && (
+
+ )}
+
+ >
+ )}
+
+ );
+}
diff --git a/examples/telescope-authz/pages/vote.tsx b/examples/telescope-authz/pages/vote.tsx
new file mode 100644
index 000000000..24ff75e16
--- /dev/null
+++ b/examples/telescope-authz/pages/vote.tsx
@@ -0,0 +1,28 @@
+import { Divider } from '@interchain-ui/react';
+import { useAuthzContext } from '@/context';
+import { withServerSideRedirect } from '@/utils';
+import { Layout, Wallet, LoginInfoBanner, Voting } from '@/components';
+
+export const getServerSideProps = withServerSideRedirect({ to: '/' });
+
+export default function Vote() {
+ const { chainName, permission } = useAuthzContext();
+
+ return (
+
+
+
+ {chainName && (
+ <>
+ {permission && (
+
+ )}
+
+ >
+ )}
+
+ );
+}
diff --git a/examples/telescope-authz/public/image/favicon.ico b/examples/telescope-authz/public/image/favicon.ico
new file mode 100644
index 000000000..d7b1d76a3
Binary files /dev/null and b/examples/telescope-authz/public/image/favicon.ico differ
diff --git a/examples/telescope-authz/styles/custom.module.css b/examples/telescope-authz/styles/custom.module.css
new file mode 100644
index 000000000..6a1988aae
--- /dev/null
+++ b/examples/telescope-authz/styles/custom.module.css
@@ -0,0 +1,7 @@
+.customInput {
+ background-color: #eef2f8;
+}
+
+.containerSm {
+ width: 640px;
+}
diff --git a/examples/telescope-authz/tsconfig.json b/examples/telescope-authz/tsconfig.json
index 1f63d593c..5477be90c 100644
--- a/examples/telescope-authz/tsconfig.json
+++ b/examples/telescope-authz/tsconfig.json
@@ -1,21 +1,25 @@
{
"compilerOptions": {
- "baseUrl": ".",
- "rootDir": "src",
- "skipLibCheck": true,
- "declaration": true,
- "declarationDir": "./types",
- "emitDeclarationOnly": false,
- "isolatedModules": true,
+ "target": "ES2020",
+ "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
-
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
"esModuleInterop": true,
- "target": "es2022",
- "lib": ["es2022", "DOM"],
- "downlevelIteration": true,
+ "module": "esnext",
"moduleResolution": "node",
- "resolveJsonModule": true
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["*"],
+ "react": ["./node_modules/@types/react"]
+ }
},
- "include": ["src/**/*"],
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
diff --git a/examples/telescope-authz/utils/authz.ts b/examples/telescope-authz/utils/authz.ts
new file mode 100644
index 000000000..45eecebac
--- /dev/null
+++ b/examples/telescope-authz/utils/authz.ts
@@ -0,0 +1,159 @@
+import dayjs from 'dayjs';
+import { Coin } from '@cosmjs/amino';
+import { GetServerSideProps } from 'next';
+
+import {
+ GenericAuthorization,
+ GrantAuthorization,
+} from '@/src/codegen/cosmos/authz/v1beta1/authz';
+import { SendAuthorization } from '@/src/codegen/cosmos/bank/v1beta1/authz';
+import {
+ authorizationTypeToJSON,
+ StakeAuthorization,
+} from '@/src/codegen/cosmos/staking/v1beta1/authz';
+
+import { shiftDigits } from './calc';
+import { ExtendedValidator } from './staking';
+import { getExponentByDenom, getSymbolByDenom } from './chain';
+
+const capitalize = (str: string) => {
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
+};
+
+const getAuthzPrettyName = (authz: GrantAuthorization['authorization']) => {
+ switch (true) {
+ case SendAuthorization.is(authz):
+ return 'Send';
+
+ case StakeAuthorization.is(authz):
+ const type = authorizationTypeToJSON(
+ (authz as StakeAuthorization).authorizationType
+ );
+ return capitalize(type.split('_').pop()!);
+
+ case GenericAuthorization.is(authz):
+ const msg = (authz as GenericAuthorization).msg;
+ return msg.split('.').pop()!.replace('Msg', '');
+
+ default:
+ return 'Unknown';
+ }
+};
+
+export const prettyGrants = (
+ grants: GrantAuthorization[],
+ groupBy: 'granter' | 'grantee'
+) => {
+ return grants
+ .reduce((acc, grant) => {
+ const addr = grant[groupBy];
+ const isNewAddr = acc.every((item) => !item[addr]);
+ if (isNewAddr) {
+ return [...acc, { [addr]: [grant] }];
+ }
+ return acc.map((item) => {
+ if (item[addr]) {
+ return { [addr]: [...item[addr], grant] };
+ }
+ return item;
+ });
+ }, [] as { [addr: string]: GrantAuthorization[] }[])
+ .map((item) => {
+ const address = Object.keys(item)[0];
+ const grants = item[address];
+
+ const permissions = grants.map((grant) => {
+ return {
+ ...grant,
+ expiry: dayjs(grant.expiration).format('YYYY-MM-DD HH:mm:ss'),
+ name: getAuthzPrettyName(grant.authorization),
+ };
+ });
+
+ return {
+ address,
+ permissions,
+ };
+ });
+};
+
+export type PrettyGrant = ReturnType[0];
+export type PrettyPermission = PrettyGrant['permissions'][0];
+
+const formatTokenAmount = (token: Coin) => {
+ const symbol = getSymbolByDenom(token.denom);
+ const exponent = getExponentByDenom(token.denom);
+ const displayAmount = shiftDigits(token.amount, -exponent);
+ return `${displayAmount} ${symbol}`;
+};
+
+const formatValidatorsList = (
+ addresses: string[],
+ validators: ExtendedValidator[]
+) => {
+ return addresses
+ .map((address) => {
+ const validator = validators.find((v) => v.address === address);
+ return validator ? validator.name : address;
+ })
+ .join(', ');
+};
+
+export const getAttributePairs = (
+ authz: GrantAuthorization['authorization'],
+ validators: ExtendedValidator[]
+) => {
+ const pair: { label: string; value: string }[] = [];
+
+ switch (true) {
+ case SendAuthorization.is(authz):
+ const sendAuthz = authz as SendAuthorization;
+ if (sendAuthz.spendLimit) {
+ pair.push({
+ label: 'Spend Limit',
+ value: formatTokenAmount(sendAuthz.spendLimit[0]),
+ });
+ }
+
+ case StakeAuthorization.is(authz):
+ const stakeAuthz = authz as StakeAuthorization;
+ if (stakeAuthz.maxTokens) {
+ pair.push({
+ label: 'Max Tokens',
+ value: formatTokenAmount(stakeAuthz.maxTokens),
+ });
+ }
+ if (stakeAuthz.allowList) {
+ pair.push({
+ label: 'Allow List',
+ value: formatValidatorsList(stakeAuthz.allowList.address, validators),
+ });
+ }
+ if (stakeAuthz.denyList) {
+ pair.push({
+ label: 'Deny List',
+ value: formatValidatorsList(stakeAuthz.denyList.address, validators),
+ });
+ }
+
+ default:
+ break;
+ }
+
+ return pair;
+};
+
+interface RedirectOptions {
+ to: string;
+}
+
+export const withServerSideRedirect = (options: RedirectOptions) => {
+ const getServerSideProps: GetServerSideProps = async (context) => {
+ context.res.writeHead(301, { Location: options.to });
+ context.res.end();
+
+ return { props: {} };
+ };
+
+ return getServerSideProps;
+};
diff --git a/examples/telescope-authz/utils/calc.ts b/examples/telescope-authz/utils/calc.ts
new file mode 100644
index 000000000..b97d5a5c7
--- /dev/null
+++ b/examples/telescope-authz/utils/calc.ts
@@ -0,0 +1,38 @@
+import BigNumber from 'bignumber.js';
+import { Prices } from '@/hooks';
+
+export const isGreaterThanZero = (val: number | string | undefined) => {
+ return new BigNumber(val || 0).gt(0);
+};
+
+export const toNumber = (val: string, decimals: number = 6) => {
+ return new BigNumber(val).decimalPlaces(decimals).toNumber();
+};
+
+export const sum = (...args: string[]) => {
+ return args
+ .reduce((prev, cur) => prev.plus(cur), new BigNumber(0))
+ .toString();
+};
+
+export const calcDollarValue = (
+ denom: string,
+ amount: string | number,
+ prices: Prices
+) => {
+ return new BigNumber(prices[denom] || 0)
+ .times(amount)
+ .decimalPlaces(2)
+ .toNumber();
+};
+
+export const shiftDigits = (
+ num: string | number | undefined | null,
+ places: number,
+ decimalPlaces?: number
+) => {
+ return new BigNumber(num ?? 0)
+ .shiftedBy(places)
+ .decimalPlaces(decimalPlaces || 6)
+ .toString();
+};
diff --git a/examples/telescope-authz/utils/chain.ts b/examples/telescope-authz/utils/chain.ts
new file mode 100644
index 000000000..bc8173d6b
--- /dev/null
+++ b/examples/telescope-authz/utils/chain.ts
@@ -0,0 +1,44 @@
+import { assets } from 'chain-registry';
+import { Asset, AssetList, Chain } from '@chain-registry/types';
+
+export const getChainLogoByChainName = (chainName: string): string => {
+ const asset = assets.find(({ chain_name }) => chain_name === chainName)
+ ?.assets?.[0];
+ return Object.values(asset?.logo_URIs || {})?.[0] || '';
+};
+
+export const getChainLogoFromChain = (chain: Chain) => {
+ return Object.values(chain?.logo_URIs || {})?.[0] || '';
+};
+
+export const getChainAssets = (chainName: string) => {
+ return assets.find((chain) => chain.chain_name === chainName) as AssetList;
+};
+
+export const getTokenByChainName = (chainName: string) => {
+ const chainAssets = getChainAssets(chainName);
+ return chainAssets.assets[0] as Asset;
+};
+
+export const getExponentByChainName = (chainName: string) => {
+ return (
+ getTokenByChainName(chainName).denom_units.find(
+ (unit) => unit.denom === getTokenByChainName(chainName).display
+ )?.exponent || 6
+ );
+};
+
+export const getExponentByDenom = (denom: string) => {
+ const asset = assets.find((chain) => chain.assets[0].base === denom)
+ ?.assets[0];
+ const exponent = asset?.denom_units.find(
+ (unit) => unit.denom === asset.display
+ )?.exponent;
+ return exponent || 6;
+};
+
+export const getSymbolByDenom = (denom: string) => {
+ const asset = assets.find((chain) => chain.assets[0].base === denom)
+ ?.assets[0];
+ return asset?.symbol || '';
+};
diff --git a/examples/telescope-authz/utils/index.ts b/examples/telescope-authz/utils/index.ts
new file mode 100644
index 000000000..70809f7d0
--- /dev/null
+++ b/examples/telescope-authz/utils/index.ts
@@ -0,0 +1,6 @@
+export * from './calc';
+export * from './chain';
+export * from './logos';
+export * from './authz';
+export * from './staking';
+export * from './vote';
diff --git a/examples/telescope-authz/utils/logos.ts b/examples/telescope-authz/utils/logos.ts
new file mode 100644
index 000000000..a03eddd47
--- /dev/null
+++ b/examples/telescope-authz/utils/logos.ts
@@ -0,0 +1,123 @@
+import { ParsedValidator as Validator } from './staking';
+
+type ImageSource = {
+ imageSource: 'cosmostation' | 'keybase';
+};
+
+export const splitIntoChunks = (arr: any[], chunkSize: number) => {
+ const res = [];
+ for (let i = 0; i < arr.length; i += chunkSize) {
+ const chunk = arr.slice(i, i + chunkSize);
+ res.push(chunk);
+ }
+ return res;
+};
+
+export const convertChainName = (chainName: string) => {
+ if (chainName.endsWith('testnet')) {
+ return chainName.replace('testnet', '-testnet');
+ }
+
+ switch (chainName) {
+ case 'cosmoshub':
+ return 'cosmos';
+ case 'assetmantle':
+ return 'asset-mantle';
+ case 'cryptoorgchain':
+ return 'crypto-org';
+ case 'dig':
+ return 'dig-chain';
+ case 'gravitybridge':
+ return 'gravity-bridge';
+ case 'kichain':
+ return 'ki-chain';
+ case 'oraichain':
+ return 'orai-chain';
+ case 'terra':
+ return 'terra-classic';
+ default:
+ return chainName;
+ }
+};
+
+export const isUrlValid = async (url: string) => {
+ const res = await fetch(url, { method: 'HEAD' });
+ const contentType = res?.headers?.get('Content-Type') || '';
+ return contentType.startsWith('image');
+};
+
+export const getCosmostationUrl = (
+ chainName: string,
+ validatorAddr: string
+) => {
+ const cosmostationChainName = convertChainName(chainName);
+ return `https://raw.githubusercontent.com/cosmostation/chainlist/main/chain/${cosmostationChainName}/moniker/${validatorAddr}.png`;
+};
+
+export const getKeybaseUrl = (identity: string) => {
+ return `https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=${identity}&fields=pictures`;
+};
+
+export const addLogoUrlSource = async (
+ validator: Validator,
+ chainName: string
+): Promise => {
+ const url = getCosmostationUrl(chainName, validator.address);
+ const isValid = await isUrlValid(url);
+ return { ...validator, imageSource: isValid ? 'cosmostation' : 'keybase' };
+};
+
+export const getLogoUrls = async (
+ validators: Validator[],
+ chainName: string
+) => {
+ const validatorsWithImgSource = await Promise.all(
+ validators.map((validator) => addLogoUrlSource(validator, chainName))
+ );
+
+ // cosmostation urls
+ const cosmostationUrls = validatorsWithImgSource
+ .filter((validator) => validator.imageSource === 'cosmostation')
+ .map(({ address }) => {
+ return {
+ address,
+ url: getCosmostationUrl(chainName, address),
+ };
+ });
+
+ // keybase urls
+ const keybaseIdentities = validatorsWithImgSource
+ .filter((validator) => validator.imageSource === 'keybase')
+ .map(({ address, identity }) => ({
+ address,
+ identity,
+ }));
+
+ const chunkedIdentities = splitIntoChunks(keybaseIdentities, 20);
+
+ let responses: any[] = [];
+
+ for (const chunk of chunkedIdentities) {
+ const logoUrlRequests = chunk.map(({ address, identity }) => {
+ if (!identity) return { address, url: '' };
+
+ return fetch(getKeybaseUrl(identity))
+ .then((response) => response.json())
+ .then((res) => ({
+ address,
+ url: res.them?.[0]?.pictures?.primary.url || '',
+ }));
+ });
+ responses = [...responses, await Promise.all(logoUrlRequests)];
+ await new Promise((resolve) => setTimeout(resolve, 500));
+ }
+
+ const keybaseUrls = responses.flat();
+
+ const allUrls = [...cosmostationUrls, ...keybaseUrls].reduce(
+ (prev, cur) => ({ ...prev, [cur.address]: cur.url }),
+ {}
+ );
+
+ return allUrls;
+};
diff --git a/examples/telescope-authz/utils/staking.ts b/examples/telescope-authz/utils/staking.ts
new file mode 100644
index 000000000..e33220cfe
--- /dev/null
+++ b/examples/telescope-authz/utils/staking.ts
@@ -0,0 +1,190 @@
+import { QueryDelegationTotalRewardsResponse } from 'interchain-query/cosmos/distribution/v1beta1/query';
+import {
+ Pool,
+ Validator,
+} from 'interchain-query/cosmos/staking/v1beta1/staking';
+import { isGreaterThanZero, shiftDigits, toNumber } from '.';
+import { Coin, decodeCosmosSdkDecFromProto } from '@cosmjs/stargate';
+import {
+ QueryDelegatorDelegationsResponse,
+ QueryParamsResponse,
+} from 'interchain-query/cosmos/staking/v1beta1/query';
+import BigNumber from 'bignumber.js';
+import { QueryAnnualProvisionsResponse } from 'interchain-query/cosmos/mint/v1beta1/query';
+import type { Asset } from '@chain-registry/types';
+
+const DAY_TO_SECONDS = 24 * 60 * 60;
+const ZERO = '0';
+
+export const calcStakingApr = ({
+ pool,
+ commission,
+ communityTax,
+ annualProvisions,
+}: ChainMetaData & { commission: string }) => {
+ const totalSupply = new BigNumber(pool?.bondedTokens || 0).plus(
+ pool?.notBondedTokens || 0
+ );
+
+ const bondedTokensRatio = new BigNumber(pool?.bondedTokens || 0).div(
+ totalSupply
+ );
+
+ const inflation = new BigNumber(annualProvisions || 0).div(totalSupply);
+
+ const one = new BigNumber(1);
+
+ return inflation
+ .multipliedBy(one.minus(communityTax || 0))
+ .div(bondedTokensRatio)
+ .multipliedBy(one.minus(commission))
+ .shiftedBy(2)
+ .decimalPlaces(2, BigNumber.ROUND_DOWN)
+ .toString();
+};
+
+export const decodeUint8Arr = (uint8array: Uint8Array | undefined) => {
+ if (!uint8array) return '';
+ return new TextDecoder('utf-8').decode(uint8array);
+};
+
+const formatCommission = (commission: string) => {
+ return new BigNumber(commission).isLessThan(1)
+ ? commission
+ : shiftDigits(commission, -18);
+};
+
+export type ParsedValidator = ReturnType[0];
+
+export const parseValidators = (validators: Validator[]) => {
+ return validators.map((validator) => ({
+ description: validator.description?.details || '',
+ name: validator.description?.moniker || '',
+ identity: validator.description?.identity || '',
+ address: validator.operatorAddress,
+ commission: formatCommission(
+ validator.commission?.commissionRates?.rate || '0'
+ ),
+ votingPower: toNumber(shiftDigits(validator.tokens, -6, 4), 4),
+ }));
+};
+
+export type ExtendedValidator = ReturnType[0];
+
+export type ChainMetaData = {
+ annualProvisions: string;
+ communityTax: string;
+ pool: Pool;
+};
+
+export const extendValidators = ({
+ validators = [],
+ logos = {},
+ delegations = [],
+ rewards = [],
+ chainMetadata = {} as ChainMetaData,
+}: {
+ validators: ParsedValidator[];
+ logos?: Record;
+ delegations?: ParsedDelegations;
+ rewards?: ParsedRewards['byValidators'];
+ chainMetadata?: ChainMetaData;
+}) => {
+ const { annualProvisions, communityTax, pool } = chainMetadata;
+
+ return validators.map((validator) => {
+ const { address, commission } = validator;
+
+ const logo = logos[address] || '';
+
+ const delegation =
+ delegations.find(({ validatorAddress }) => validatorAddress === address)
+ ?.amount || ZERO;
+
+ const reward =
+ rewards.find(({ validatorAddress }) => validatorAddress === address)
+ ?.amount || ZERO;
+
+ const apr =
+ annualProvisions && communityTax && pool && commission
+ ? calcStakingApr({ annualProvisions, commission, communityTax, pool })
+ : null;
+
+ return { ...validator, logo, delegation, reward, apr };
+ });
+};
+
+const findAndDecodeReward = (
+ coins: Coin[],
+ denom: string,
+ exponent: number
+) => {
+ const amount = coins.find((coin) => coin.denom === denom)?.amount || ZERO;
+ const decodedAmount = decodeCosmosSdkDecFromProto(amount).toString();
+ return shiftDigits(decodedAmount, exponent);
+};
+
+export type ParsedRewards = ReturnType;
+
+export const parseRewards = (
+ { rewards, total }: QueryDelegationTotalRewardsResponse,
+ denom: string,
+ exponent: number
+) => {
+ if (!rewards || !total) return { byValidators: [], total: ZERO };
+
+ const totalReward = findAndDecodeReward(total, denom, exponent);
+
+ const rewardsParsed = rewards.map(({ reward, validatorAddress }) => ({
+ validatorAddress,
+ amount: findAndDecodeReward(reward, denom, exponent),
+ }));
+
+ return { byValidators: rewardsParsed, total: totalReward };
+};
+
+export type ParsedDelegations = ReturnType;
+
+export const parseDelegations = (
+ delegations: QueryDelegatorDelegationsResponse['delegationResponses'],
+ exponent: number
+) => {
+ if (!delegations) return [];
+ return delegations.map(({ balance, delegation }) => ({
+ validatorAddress: delegation?.validatorAddress || '',
+ amount: shiftDigits(balance?.amount || ZERO, exponent),
+ }));
+};
+
+export const calcTotalDelegation = (delegations: ParsedDelegations) => {
+ if (!delegations) return ZERO;
+
+ return delegations
+ .reduce((prev, cur) => prev.plus(cur.amount), new BigNumber(0))
+ .toString();
+};
+
+export const parseUnbondingDays = (params: QueryParamsResponse['params']) => {
+ return new BigNumber(Number(params?.unbondingTime?.seconds || 0))
+ .div(DAY_TO_SECONDS)
+ .decimalPlaces(0)
+ .toString();
+};
+
+export const parseAnnualProvisions = (data: QueryAnnualProvisionsResponse) => {
+ const res = shiftDigits(decodeUint8Arr(data?.annualProvisions), -18);
+ return isGreaterThanZero(res) ? res : null;
+};
+
+export const getAssetLogoUrl = (asset: Asset): string => {
+ return Object.values(asset?.logo_URIs || {})?.[0] || '';
+};
+
+export const formatValidatorMetaInfo = (
+ validator: ExtendedValidator
+): string => {
+ const commissionStr = `Commission ${shiftDigits(validator.commission, 2)}%`;
+ const aprStr = validator.apr ? `APR ${validator.apr}%` : '';
+
+ return [commissionStr, aprStr].filter(Boolean).join(' | ');
+};
diff --git a/examples/telescope-authz/utils/vote.ts b/examples/telescope-authz/utils/vote.ts
new file mode 100644
index 000000000..52b9ff336
--- /dev/null
+++ b/examples/telescope-authz/utils/vote.ts
@@ -0,0 +1,94 @@
+import dayjs from 'dayjs';
+import BigNumber from 'bignumber.js';
+import { Chain, Asset } from '@chain-registry/types';
+import {
+ Proposal,
+ ProposalStatus,
+} from 'interchain-query/cosmos/gov/v1beta1/gov';
+import { getChainAssets } from './chain';
+
+export function getChainLogo(chain: Chain) {
+ return chain.logo_URIs?.svg || chain.logo_URIs?.png || chain.logo_URIs?.jpeg;
+}
+
+export function formatDate(date?: Date) {
+ if (!date) return null;
+ return dayjs(date).format('YYYY-MM-DD hh:mm:ss');
+}
+
+export function paginate(limit: bigint, reverse: boolean = false) {
+ return {
+ limit,
+ reverse,
+ key: new Uint8Array(),
+ offset: 0n,
+ countTotal: true,
+ };
+}
+
+export function percent(num: number | string = 0, total: number, decimals = 2) {
+ return total
+ ? new BigNumber(num)
+ .dividedBy(total)
+ .multipliedBy(100)
+ .decimalPlaces(decimals)
+ .toNumber()
+ : 0;
+}
+
+export const getCoin = (chainName: string) => {
+ const chainAssets = getChainAssets(chainName);
+ return chainAssets.assets[0] as Asset;
+};
+
+export const getExponent = (chainName: string) => {
+ return getCoin(chainName).denom_units.find(
+ (unit) => unit.denom === getCoin(chainName).display
+ )?.exponent as number;
+};
+
+export const exponentiate = (num: number | string | undefined, exp: number) => {
+ if (!num) return 0;
+ return new BigNumber(num)
+ .multipliedBy(new BigNumber(10).exponentiatedBy(exp))
+ .toNumber();
+};
+
+export function decodeUint8Array(value?: Uint8Array) {
+ return value ? new TextDecoder('utf-8').decode(value) : '';
+}
+
+export function getTitle(value?: Uint8Array) {
+ return decodeUint8Array(value)
+ .slice(0, 250)
+ .match(/[A-Z][A-Za-z].*(?=\u0012)/)?.[0];
+}
+
+export function parseQuorum(value?: Uint8Array) {
+ const quorum = decodeUint8Array(value);
+ return new BigNumber(quorum).shiftedBy(-quorum.length).toNumber();
+}
+
+export function processProposals(proposals: Proposal[]) {
+ const sorted = proposals.sort(
+ (a, b) => Number(b.proposalId) - Number(a.proposalId)
+ );
+
+ proposals.forEach((proposal) => {
+ // @ts-ignore
+ if (!proposal.content?.title && proposal.content?.value) {
+ // @ts-ignore
+ proposal.content.title = getTitle(proposal.content?.value);
+ }
+ });
+
+ return sorted
+ .filter(
+ ({ status }) => status === ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD
+ )
+ .concat(
+ sorted.filter(
+ ({ status }) => status !== ProposalStatus.PROPOSAL_STATUS_VOTING_PERIOD
+ )
+ );
+}
diff --git a/yarn.lock b/yarn.lock
index b81519355..0e4b60ac0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2135,6 +2135,17 @@
bfs-path "^1.0.2"
cross-fetch "^3.1.5"
+"@chain-registry/client@^1.18.0":
+ version "1.19.0"
+ resolved "https://registry.npmjs.org/@chain-registry/client/-/client-1.19.0.tgz#7f40d194c942e11c413dcfeba1ac63c264e5e142"
+ integrity sha512-Nt25fi5yuo+W26jaMHauWP4on7p8EZuOB7oBgpvl3XSje8EIJtLMnQChKzd/6+J/8fkr1vpp1lFwaIkInh9K2Q==
+ dependencies:
+ "@babel/runtime" "^7.21.0"
+ "@chain-registry/types" "^0.18.0"
+ "@chain-registry/utils" "^1.18.0"
+ bfs-path "^1.0.2"
+ cross-fetch "^3.1.5"
+
"@chain-registry/cosmostation@1.26.0":
version "1.26.0"
resolved "https://registry.npmjs.org/@chain-registry/cosmostation/-/cosmostation-1.26.0.tgz#0d3967b8d320168a5c9c7b56b3352dcf3f2945b0"
@@ -2217,6 +2228,13 @@
dependencies:
"@babel/runtime" "^7.21.0"
+"@chain-registry/types@^0.18.0":
+ version "0.18.0"
+ resolved "https://registry.npmjs.org/@chain-registry/types/-/types-0.18.0.tgz#68d91054a5b36ff621248f2843f3ff62dc9ade7f"
+ integrity sha512-jRFOKOMIAcA5Qp5cXEiAPyXs13WbSP19kvcb8qm5vaRwXvHMufPd/ZxLC80yq/LB62FeZeaVYfsjorLf3M6tTw==
+ dependencies:
+ "@babel/runtime" "^7.21.0"
+
"@chain-registry/utils@1.13.1":
version "1.13.1"
resolved "https://registry.npmmirror.com/@chain-registry/utils/-/utils-1.13.1.tgz"
@@ -2277,6 +2295,16 @@
bignumber.js "9.1.1"
sha.js "^2.4.11"
+"@chain-registry/utils@^1.18.0":
+ version "1.18.0"
+ resolved "https://registry.npmjs.org/@chain-registry/utils/-/utils-1.18.0.tgz#d711efc2660b3b65077a5bee90b45ddc6af2194f"
+ integrity sha512-FAPyP1FN1jr88GfiBWnppe5S1ddgbK9TfwkyKuNSKHlk9Q+fhdC4nPM552bEz76AuFyfv7SFHg3kDuQSgYYZBw==
+ dependencies:
+ "@babel/runtime" "^7.21.0"
+ "@chain-registry/types" "^0.18.0"
+ bignumber.js "9.1.1"
+ sha.js "^2.4.11"
+
"@chakra-ui/accordion@2.1.9":
version "2.1.9"
resolved "https://registry.npmmirror.com/@chakra-ui/accordion/-/accordion-2.1.9.tgz"
@@ -3585,6 +3613,18 @@
"@cosmjs/utils" "^0.32.2"
cosmjs-types "^0.9.0"
+"@cosmjs/proto-signing@0.32.2", "@cosmjs/proto-signing@^0.32.0", "@cosmjs/proto-signing@^0.32.2":
+ version "0.32.2"
+ resolved "https://registry.npmmirror.com/@cosmjs/proto-signing/-/proto-signing-0.32.2.tgz#26ed2675978ce24078981f4c15a06c5d6b808f44"
+ integrity sha512-UV4WwkE3W3G3s7wwU9rizNcUEz2g0W8jQZS5J6/3fiN0mRPwtPKQ6EinPN9ASqcAJ7/VQH4/9EPOw7d6XQGnqw==
+ dependencies:
+ "@cosmjs/amino" "^0.32.2"
+ "@cosmjs/crypto" "^0.32.2"
+ "@cosmjs/encoding" "^0.32.2"
+ "@cosmjs/math" "^0.32.2"
+ "@cosmjs/utils" "^0.32.2"
+ cosmjs-types "^0.9.0"
+
"@cosmjs/proto-signing@^0.31.3":
version "0.31.3"
resolved "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.31.3.tgz#20440b7b96fb2cd924256a10e656fd8d4481cdcd"
@@ -4395,32 +4435,62 @@
"@cosmos-kit/core" "^2.8.9"
cosmjs-types ">=0.9.0"
-"@cosmos-kit/coin98@^2.6.10":
- version "2.6.10"
- resolved "https://registry.npmjs.org/@cosmos-kit/coin98/-/coin98-2.6.10.tgz#7f4df0f64ac47cd2a9d30a2d0ced4123e348f1e8"
- integrity sha512-bkWCtWDl18OgaMgeyRyDsasrmfCmBRqkpoltPjZ3TfQPlTIYJP667McOvi+7grxLR5Smwo/uCDCNZM6zHJtSKw==
+"@cosmos-kit/coin98-extension@^2.7.8":
+ version "2.7.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/coin98-extension/-/coin98-extension-2.7.8.tgz#13eed9c1a74b1d777780f6a9b070c6156ad4c48a"
+ integrity sha512-DrFfR3/BdFe6hgrbczf5YdOToMV4R/fmmnaPnzNbdJpDbzEApOS79+RJBIDk1XgEqCsRjoVocRD39AZtOZ+/8g==
+ dependencies:
+ "@chain-registry/keplr" "1.28.0"
+ "@cosmos-kit/core" "^2.8.8"
+
+"@cosmos-kit/coin98@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/coin98/-/coin98-2.6.5.tgz#134541b9c93a0feca1c7b727eb6b66c9005c8bfd"
+ integrity sha512-LfJ75JebBwrz3FP5/ewgnQOkaSrFJN/k8U/FOwI9Y+WJ5AcbjXr1Hq5or1bJgFy0IsiLQDSA5zPQQU7txxsClQ==
dependencies:
"@cosmos-kit/coin98-extension" "^2.7.10"
-"@cosmos-kit/compass-extension@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/compass-extension/-/compass-extension-2.6.9.tgz#994e8ce618e2f19e6cda92c6acd507a9604f3881"
- integrity sha512-r5YVzfRThbcDbU5mQdiEMGXBDrm4m8k5clPNTTpNqk3GqSbVXhQUlDQMb7fYEtZRSu/J4lB8fZwt4C9829xIZA==
+"@cosmos-kit/coin98@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/coin98/-/coin98-2.6.8.tgz#023f22fa171a41393da7fb6d5a42a9ccdb4ea38c"
+ integrity sha512-ZD6Sh7+jQ776bTI6/YWf/8qt2pD6PiRhDp7MbVPswwgncz8H6mpsb5xPNSm+zabzTka+KLPNVq6cGcCwzamh9A==
+ dependencies:
+ "@cosmos-kit/coin98-extension" "^2.7.8"
+
+"@cosmos-kit/compass-extension@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/compass-extension/-/compass-extension-2.6.5.tgz#a07c70b8b7fe85c909fb18ef7235392a37a37711"
+ integrity sha512-bvo+KQIDE+cJY26fHqVu4YkiAKMfKBjhUpugA87YsAYjLS1x+Xtj07aG5tZB2WuGFcIer3D24C6swEjQjSSN9w==
dependencies:
"@chain-registry/keplr" "1.28.0"
"@cosmos-kit/core" "^2.8.9"
-"@cosmos-kit/compass@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/compass/-/compass-2.6.9.tgz#22f8bb42260691c4fedd27673d5b3664f0958e18"
- integrity sha512-XHHV3rI6MxpiB3Ir7PHI/ft7VgFDUjNFpo1sttpBFWtW32PBe2EZ4COHpzUz9LtVwRhnkdWT6PK1oZshpKVXaA==
+"@cosmos-kit/compass-extension@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/compass-extension/-/compass-extension-2.6.8.tgz#de85b315dcf855162d217c499b6ab31d7f3f436b"
+ integrity sha512-nJUsbF5DMosTDFXEzdH07W1Y0GipEzrc2euYM4/7/kihSi3rOmw9grZCbn7Yd1NvUQDdAnNPjhAtWZ5ZAlHeDg==
+ dependencies:
+ "@chain-registry/keplr" "1.28.0"
+ "@cosmos-kit/core" "^2.8.8"
+
+"@cosmos-kit/compass@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/compass/-/compass-2.6.5.tgz#03d7377b00e6afcfe4aa6fa58bfcb32a0841a5dd"
+ integrity sha512-UROfl17KllDq3QCyi5W1u7mBifI1/BRie4xtyjhrzt23cr12Kgd1vYHeprzryMWUVwf1uG2/hjXA4JyY8LQWDA==
dependencies:
"@cosmos-kit/compass-extension" "^2.6.9"
-"@cosmos-kit/core@^2.8.9":
- version "2.8.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/core/-/core-2.8.9.tgz#3e6b40fa14c5d143065d3ec6a894e386f95ea4ad"
- integrity sha512-e0Fqbut9mqHlFS6QnLSbU8AFYPtXUcKVaMJsL1dKbfTotACsECgnAs4gzXaTFo3sTwEJrNZ6cq86qvqV85X6uw==
+"@cosmos-kit/compass@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/compass/-/compass-2.6.8.tgz#42c3e4a6f02481073279430217691c9175dd1445"
+ integrity sha512-VgLNmKHXfgGk56bmMUR1bMFGk9D9rw8wRsJxi3mLe9iemPiMFklSDCDxOJY1zZOVQT9Vgf5YCaNxx7LDpmt8+A==
+ dependencies:
+ "@cosmos-kit/compass-extension" "^2.6.8"
+
+"@cosmos-kit/core@2.8.5", "@cosmos-kit/core@^2.8.5":
+ version "2.8.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/core/-/core-2.8.5.tgz#4b2acdf8e8d90a780e1d1f4937876a960be192b3"
+ integrity sha512-kIUYIF08MOH+DPki2d54EpU0ALdS0euBUJUwcUN+oaQrDza/q8VwTPHXG4grzlxiW7w3HG36jowrbySTumoZ7g==
dependencies:
"@chain-registry/client" "^1.18.0"
"@chain-registry/types" "0.17.0"
@@ -4434,207 +4504,420 @@
events "3.3.0"
uuid "^9.0.1"
-"@cosmos-kit/cosmostation-extension@^2.7.10":
- version "2.7.10"
- resolved "https://registry.npmjs.org/@cosmos-kit/cosmostation-extension/-/cosmostation-extension-2.7.10.tgz#2120fa826c692732e7c539a35141df0f8e832876"
- integrity sha512-x0rBk4Ht6uicfZxIXRbwUi5WHrvKsI11oXbf9bYL9nRBfRTgpbO6zIW9791oTVjiJLzHbKaBQoqh1R233YcHbg==
+"@cosmos-kit/core@^2.8.8":
+ version "2.8.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/core/-/core-2.8.8.tgz#634e0849fa972efdf1ccd50e6adc033c1c861863"
+ integrity sha512-gM0yyNS4BdHh8Tb0x9o4tEV97HXSHmKKDgjrzVxUnVhxiIaHtGsCvlUAnBc3uDcsm0HF5vhM3gJX8vkk3uitDw==
+ dependencies:
+ "@chain-registry/client" "^1.18.0"
+ "@chain-registry/types" "0.17.0"
+ "@cosmjs/amino" "^0.32.2"
+ "@cosmjs/cosmwasm-stargate" "^0.32.2"
+ "@cosmjs/proto-signing" "^0.32.2"
+ "@cosmjs/stargate" "^0.32.2"
+ "@walletconnect/types" "2.11.0"
+ bowser "2.11.0"
+ cosmjs-types "^0.9.0"
+ events "3.3.0"
+ uuid "^9.0.1"
+
+"@cosmos-kit/cosmostation-extension@^2.7.5":
+ version "2.7.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/cosmostation-extension/-/cosmostation-extension-2.7.5.tgz#a12914ca3b1f2b09c252f8cd04cf1cfd4c896d0c"
+ integrity sha512-aOPILDdgBAuCRDBphZx0UKO03DZgokcwyEVfQPy8vE8qzX4wyVA38dd/pCtkWmpfCqne0abP/n8ROLVBOnLx7g==
dependencies:
"@chain-registry/cosmostation" "^1.26.0"
"@cosmos-kit/core" "^2.8.9"
cosmjs-types "^0.9.0"
-"@cosmos-kit/cosmostation-mobile@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/cosmostation-mobile/-/cosmostation-mobile-2.6.9.tgz#1c304ac12cccc05de93c9ed927243d51087426f6"
- integrity sha512-GjZetNMFrz2oLisdp14ahxftkwPz5v1OYRGvD4MLTdMSA71JUTzX8A4At4uo4M7nwG09H1yQ2/CLQy9XGVRinA==
+"@cosmos-kit/cosmostation-extension@^2.7.8":
+ version "2.7.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/cosmostation-extension/-/cosmostation-extension-2.7.8.tgz#2336eea14b9cac9e42690ac248333ba7edecfd03"
+ integrity sha512-XUvOraMwriVIv3QBZLY+FCfyxb+hGdhFmy4w3zo2GRlx+/+jVM4B74hflWjRNZUJ7H4tegGNOYp92cKXymMYMw==
+ dependencies:
+ "@chain-registry/cosmostation" "^1.26.0"
+ "@cosmos-kit/core" "^2.8.8"
+
+"@cosmos-kit/cosmostation-mobile@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/cosmostation-mobile/-/cosmostation-mobile-2.6.5.tgz#1587e66b1292f666d96b966eb6e05bb3ccda9c66"
+ integrity sha512-U/ue9sacIFlj+OhaedzsHbPdQpokEj5C9DABJ57rRHIwMGnSpmNwvfd3kn9Mbcp5cetGjzOQJGvjdJxRrS+uzQ==
dependencies:
"@chain-registry/cosmostation" "1.26.0"
"@cosmos-kit/core" "^2.8.9"
"@cosmos-kit/walletconnect" "^2.5.9"
-"@cosmos-kit/cosmostation@^2.6.10":
- version "2.6.10"
- resolved "https://registry.npmjs.org/@cosmos-kit/cosmostation/-/cosmostation-2.6.10.tgz#406d93a29c154e988f4d60425d8edbba4f0cd455"
- integrity sha512-DWR28fnkv04yr5Eyo/CS7yOiYr3cwld9lE/F2qGIYGb2Tw/eUWhKSl05+2ly8S15xoKOXT7wxXOsFIXYYUQ8/g==
+"@cosmos-kit/cosmostation-mobile@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/cosmostation-mobile/-/cosmostation-mobile-2.6.8.tgz#02dc795cf4c5e05b752bb39ac138fd3b8092ebed"
+ integrity sha512-GchATnZJ3X265f34CfYYKO5C+KVXOYHHejMXB1PszwK4FPqJ6ixOnswsX8W3ECEmdSAn9g0+4beMmEynnlJXFw==
+ dependencies:
+ "@chain-registry/cosmostation" "1.26.0"
+ "@cosmos-kit/core" "^2.8.8"
+ "@cosmos-kit/walletconnect" "^2.5.8"
+
+"@cosmos-kit/cosmostation@2.6.5", "@cosmos-kit/cosmostation@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/cosmostation/-/cosmostation-2.6.5.tgz#2a22c7871321a8374dd13d4a8aaad5ebb17914ee"
+ integrity sha512-AiggZSu05r5eilCbclme06NgDmT2QWfHzqvOEtB36BFBquNt/+89KancH5bwG+B5/YFKdhxq6HfX4bnjTD5y9w==
dependencies:
"@cosmos-kit/cosmostation-extension" "^2.7.10"
"@cosmos-kit/cosmostation-mobile" "^2.6.9"
-"@cosmos-kit/exodus-extension@^2.5.9":
- version "2.5.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/exodus-extension/-/exodus-extension-2.5.9.tgz#d898ef4e8794aa6ea9ce98f85cf8c05519b6b434"
- integrity sha512-iPFWs3pPLYcEkRfw32qwM7/iZJX5un0tDIGdqdIR0l0qJWxwU3Fsbrrk4v/qbbRzHlHtcfiohlUOR+m52zj5zA==
+"@cosmos-kit/cosmostation@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/cosmostation/-/cosmostation-2.6.8.tgz#28a1bd108bb5af80f8e0d55ea79bd26e12e39569"
+ integrity sha512-4nXUPpZUfibcFfBLoM3+LgHz+PRkv3Y1e1GlmVQ8brI14lBZvTc/5NHDU7hO9LtsAawAgswWoSac9vFpUXi1ig==
+ dependencies:
+ "@cosmos-kit/cosmostation-extension" "^2.7.8"
+ "@cosmos-kit/cosmostation-mobile" "^2.6.8"
+
+"@cosmos-kit/exodus-extension@^2.5.5":
+ version "2.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/exodus-extension/-/exodus-extension-2.5.5.tgz#162ec9db03401a592601c243936aec98aee61c49"
+ integrity sha512-DV+h68ote8MmYb07oxRt82/ImKrLnu0fJ8xuzTb+WfdIlP7Cmg8YPkgjLLkxiUEyCP4lRCa3RxXYuqds62b9bw==
dependencies:
"@cosmos-kit/core" "^2.8.9"
react-icons "4.4.0"
-"@cosmos-kit/exodus@^2.5.9":
- version "2.5.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/exodus/-/exodus-2.5.9.tgz#b265d7e42b97e5bf2debb728e244414e51db3f66"
- integrity sha512-szvol6Q5ypE8VBjrqvswnAGLfQBBqPNOqJ6cW53qRU123Y9f1PwfHwDWhtBM9UO4hnfbqgoET3ezCJlTV53zgQ==
+"@cosmos-kit/exodus-extension@^2.5.8":
+ version "2.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/exodus-extension/-/exodus-extension-2.5.8.tgz#13034db71e485cd40ede4599e480434a2723bcb5"
+ integrity sha512-ThzO20r9Z49MXznNQdqP3Tiini7t3LrXfYPfR7cOQn0Nobgsu8SgzEh6eK8SaJNnWLornKbl9PJE63jsps2BpA==
+ dependencies:
+ "@cosmos-kit/core" "^2.8.8"
+ react-icons "4.4.0"
+
+"@cosmos-kit/exodus@^2.5.5":
+ version "2.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/exodus/-/exodus-2.5.5.tgz#219449dda518cfecb77005a06ab7fb454eb6e1c1"
+ integrity sha512-AAN1TTxBhMewbwL93eipRRpr3obo40Sw+sPVXiq8zg01AmsP6eos7LhaAzdviHQ++SoeiTqkLPjiJslIQ+EKng==
dependencies:
"@cosmos-kit/exodus-extension" "^2.5.9"
-"@cosmos-kit/fin-extension@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/fin-extension/-/fin-extension-2.6.9.tgz#167e153850566a7060b3b54e060ea8e835cc1ae5"
- integrity sha512-e9hcEye/7OVln40Rh6q4rQ3NHl2/toBwnDSFDDeSCbP8sXEsboVLVT8WSkgxU8OY/iP9R8DJz49JviHnMsyn5g==
+"@cosmos-kit/exodus@^2.5.8":
+ version "2.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/exodus/-/exodus-2.5.8.tgz#c9111b651b4dd718f3dc2f7310fe1df2ca71b6e4"
+ integrity sha512-lCi/yWGnM8upt5eXKYMZn8yQeHP281eKB8UqfOpKMm/HmqJeykkOAGytue3k7RiSnd/LwTDObUUFbZd5EFi2JQ==
+ dependencies:
+ "@cosmos-kit/exodus-extension" "^2.5.8"
+
+"@cosmos-kit/fin-extension@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/fin-extension/-/fin-extension-2.6.5.tgz#8c652fed5bc8849391634d947848f0d66d4054ae"
+ integrity sha512-m16haeEBGMFFoDhn/D1hiAM/+3LgzsTIAwzpTbMRbKl5Xt0W6SLYEXBapjR0c/vUiFZ5XiHr40nWKB0wobtznQ==
dependencies:
"@chain-registry/keplr" "1.28.0"
"@cosmos-kit/core" "^2.8.9"
-"@cosmos-kit/fin@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/fin/-/fin-2.6.9.tgz#cb9319323623a66db1d8715c84db289dfbfd572b"
- integrity sha512-lbXJnWZGMqorPj2WQI4KTS51XtJbgFsoLG7aF4wyfG7euZrlBeazOr+h49WOLvIPgiMWCYascYkuHiFuurtxQw==
+"@cosmos-kit/fin-extension@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/fin-extension/-/fin-extension-2.6.8.tgz#2599caa7f25b4198465c43afe8e5dab4aa004d84"
+ integrity sha512-BUR89uMrVR9XMPJRf9GqnE4e2Km6msJkmVMc3aH93MRaywGWEVKb7HuWZdZN+LjUTln4OLgQeNYHMqtg8msNTA==
+ dependencies:
+ "@chain-registry/keplr" "1.28.0"
+ "@cosmos-kit/core" "^2.8.8"
+
+"@cosmos-kit/fin@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/fin/-/fin-2.6.5.tgz#04883dab4b7e60879aa41f11bb61638bd5bb465d"
+ integrity sha512-yf4Y5wvkVnM2D3fY2TGIks7QfVq3qkvkjQ2EjN8rCkI5PP9JTci/wM80aL8wK1nWTDyyRIB4GeruFyZf0Vn1LQ==
dependencies:
"@cosmos-kit/fin-extension" "^2.6.9"
-"@cosmos-kit/frontier-extension@^2.5.9":
- version "2.5.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/frontier-extension/-/frontier-extension-2.5.9.tgz#1082aaa35ef02f20af4f9de4d5d76068aa15191e"
- integrity sha512-YjHaxvlH1YSZj3epKXHstSNRyWdd8YZUPzpftLLuhebuaINHWk6V92MnUtXYo8UKIKOSw3UIHjZii/MieJ1STQ==
+"@cosmos-kit/fin@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/fin/-/fin-2.6.8.tgz#f466cccbc71a206dea74b147223a74e3c472a665"
+ integrity sha512-atpDQhG1clcEXfo/QBCiE5gcL2sM+4aReKJZ4QE/RLa90R4xpaIGFXnUfQ/+lqxbqKwx1knEGvqZa2XvkTukrA==
+ dependencies:
+ "@cosmos-kit/fin-extension" "^2.6.8"
+
+"@cosmos-kit/frontier-extension@^2.5.5":
+ version "2.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/frontier-extension/-/frontier-extension-2.5.5.tgz#e43abab05f99561f4b4af74c7337da83af440c5b"
+ integrity sha512-8wjirC9vTG651jB12R2BvF2Fs7+grtf3gGUNA4PQQHvaHgTNxA8iF6FouXRVTkWsgj7yNiBY6kjXdnbYkT1H9g==
dependencies:
"@cosmos-kit/core" "^2.8.9"
-"@cosmos-kit/frontier@^2.5.9":
- version "2.5.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/frontier/-/frontier-2.5.9.tgz#96bb0408038fa9a7fb24fb91a7095aa3cd5bda84"
- integrity sha512-iYneO93TK+ksNgWl3wXafKW7UGTQ/b+cSJcneGZJ3C8r4AGdjtgmcMgFgS6NcbQxB/1l0IBmwzkhyUcBzC/hjQ==
+"@cosmos-kit/frontier-extension@^2.5.8":
+ version "2.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/frontier-extension/-/frontier-extension-2.5.8.tgz#01af6fa98b93edd70734274e9d82b0a99a2acc08"
+ integrity sha512-A4RFeatLy/Y4d/n3LapmZiNQe0GNtsgeOd83WF0lBo9cwaMXrtu17tbGnLDwb6bykVNg/56zxyYLAcOZBSOzhg==
+ dependencies:
+ "@cosmos-kit/core" "^2.8.8"
+
+"@cosmos-kit/frontier@^2.5.5":
+ version "2.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/frontier/-/frontier-2.5.5.tgz#c59f5ca880d6ea87f830633935c0f19319e4d1ee"
+ integrity sha512-jf7uEdMdRLeGRUufQOzhg7hGIS57QPjv2zSg9vPrIutawIopy10W+cN7wM4Y9gLDe93K05ZMAHKHlkGHqBjFCw==
dependencies:
"@cosmos-kit/frontier-extension" "^2.5.9"
-"@cosmos-kit/keplr-extension@^2.7.9":
- version "2.7.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/keplr-extension/-/keplr-extension-2.7.9.tgz#16586c94452a5e65713cdead07446447c511fa0e"
- integrity sha512-asIjLzJjNa1mpQRohoT5zwnutk93WaQ/Y6ndb2CITfjXzM7HWrbhUzShy3FXZjHNO9CbPTjpsHNzhyScTc7fgg==
+"@cosmos-kit/frontier@^2.5.8":
+ version "2.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/frontier/-/frontier-2.5.8.tgz#376522eaf2dc915e85fcb8db4ef4323998885e8d"
+ integrity sha512-mskAwtx9NP000V+RdW1KROGrhoxcYtv9o70Rus4lT70HKYDvQqCewRaXgKr/uE0I+M1GUfAIqitG8JF1QHSiSg==
+ dependencies:
+ "@cosmos-kit/frontier-extension" "^2.5.8"
+
+"@cosmos-kit/keplr-extension@^2.7.5":
+ version "2.7.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/keplr-extension/-/keplr-extension-2.7.5.tgz#992916f46fd607bdcb94cdf14f188b71618a38dc"
+ integrity sha512-uZRGXcCm5NmOQGh3bNbliNocBRcIynk0Nbnq/JTHF8ydM/vpoLc92UKEy4iEaoAXvkj/YPuWxgpcgdrhUPjZpQ==
dependencies:
"@chain-registry/keplr" "1.28.0"
"@cosmos-kit/core" "^2.8.9"
"@keplr-wallet/types" "^0.12.58"
-"@cosmos-kit/keplr-mobile@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/keplr-mobile/-/keplr-mobile-2.6.9.tgz#28fcdffc8da11c34a2ece2bbef58e29dc68d0df3"
- integrity sha512-kw0g/TOBKMJMDiIld2wZ7Id47hUA7G3tauijSwbMC+scXnnmOCKZ1x0lRUvW13onV7SiRT21sOm7msl8XwUlgQ==
+"@cosmos-kit/keplr-extension@^2.7.8":
+ version "2.7.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/keplr-extension/-/keplr-extension-2.7.8.tgz#1169aaf84c07e7dc1aca901be034d661757c0f32"
+ integrity sha512-SNmgEjPT294Lc/z5mAss+56Bud35+YxjOTTLhAmY8lrjAeIH6iiYpUbXdpY8J8mbqkdQvVs2TvJUDR/0phKhIw==
+ dependencies:
+ "@chain-registry/keplr" "1.28.0"
+ "@cosmos-kit/core" "^2.8.8"
+ "@keplr-wallet/types" "^0.12.58"
+
+"@cosmos-kit/keplr-mobile@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/keplr-mobile/-/keplr-mobile-2.6.5.tgz#2a8aaff0dab92d950a0df96ba7cb116e4437a101"
+ integrity sha512-BYuHJkJB9NI0dux/mBKoH0ui7guIxl2wivMlk+xC7vB1IW+7B6W7ZKlxqsBxdxNSCO9m5vzTn2YLQrF4A09tvA==
dependencies:
"@chain-registry/keplr" "1.28.0"
"@cosmos-kit/core" "^2.8.9"
"@cosmos-kit/walletconnect" "^2.5.9"
-"@cosmos-kit/keplr@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/keplr/-/keplr-2.6.9.tgz#d861f8ea4471fc72f54c19e287623341e586e8a2"
- integrity sha512-HW+WhELS1dHokSq58NDNCIQaA0xRrg293uKM989XQgDOgD/xsEaRrwwTaj3guOXxVm8BE51PLTFbZesHhflmaQ==
+"@cosmos-kit/keplr-mobile@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/keplr-mobile/-/keplr-mobile-2.6.8.tgz#c9d5d39e7ebf34578285321467c8361691334306"
+ integrity sha512-Kyb/cxYxqlkkdBaFw067FY8yYHuCr2MjtJ2bgQq6sNXHSKRGvUO7aur6Vb/A1uogpmbhMEka0UFA45HHPgAZhw==
+ dependencies:
+ "@chain-registry/keplr" "1.28.0"
+ "@cosmos-kit/core" "^2.8.8"
+ "@cosmos-kit/walletconnect" "^2.5.8"
+
+"@cosmos-kit/keplr@2.6.5", "@cosmos-kit/keplr@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/keplr/-/keplr-2.6.5.tgz#d2e019389b18facad728a32b25649fc03f291a16"
+ integrity sha512-4AfcDEcqQUV3C24yl9tp1haOOPgPOOwsn7s2DEv2KafoMF+TDFUzKYZXcCJ93wcDQpDlfE7b1e2INzmcvPPyTg==
dependencies:
"@cosmos-kit/keplr-extension" "^2.7.9"
"@cosmos-kit/keplr-mobile" "^2.6.9"
-"@cosmos-kit/leap-extension@^2.7.9":
- version "2.7.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/leap-extension/-/leap-extension-2.7.9.tgz#f0d05b9adc89963a31996852d9426e4b47d38492"
- integrity sha512-+F764htkzaHz/MRQOZf/iO72NmquGLU9l+nec+HlInooJ3EZianrP/3H61PVlbjdQ3RrHRFznn4FNapOVnqXCw==
+"@cosmos-kit/keplr@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/keplr/-/keplr-2.6.8.tgz#e5d7a728d5ee7f271258f1f0cdfa8f4ca2b40761"
+ integrity sha512-nJ3hVw7sK5w7vhArcEY+HVj1xmYfj8iUltBNawhGcZhN4x17cMhPOsrLagt1NmYof3+SAw7Ww6NrSX0McUMOyw==
+ dependencies:
+ "@cosmos-kit/keplr-extension" "^2.7.8"
+ "@cosmos-kit/keplr-mobile" "^2.6.8"
+
+"@cosmos-kit/leap-extension@^2.7.5":
+ version "2.7.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/leap-extension/-/leap-extension-2.7.5.tgz#9b6466a191084cea5d0fa8316069bdc7cd1b9e6f"
+ integrity sha512-LLKSap2kcvSjGfQqZQM7neryXHcS2xcgn5HcVyvPStjgVZGB/Q77WTAYIK1RyWxT1a9lve3m6/zU7d4JJvBDQw==
dependencies:
"@chain-registry/keplr" "1.28.0"
"@cosmos-kit/core" "^2.8.9"
-"@cosmos-kit/leap-metamask-cosmos-snap@^0.6.0":
- version "0.6.0"
- resolved "https://registry.npmjs.org/@cosmos-kit/leap-metamask-cosmos-snap/-/leap-metamask-cosmos-snap-0.6.0.tgz#1b3aae4c41bb1f1daf251c30be051c3752bc2203"
- integrity sha512-KO4tHQm8QkHYwnADWGMUkvEEIRJzYHgA09YImhPfSGhqsCsjLzDAIStS12J5iqU1v+Py8uX1n7AGd339amMV5g==
+"@cosmos-kit/leap-extension@^2.7.8":
+ version "2.7.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/leap-extension/-/leap-extension-2.7.8.tgz#4fe98f9662a26500778f7b3873df26ed16b2bd93"
+ integrity sha512-flDsUbJ8na5ye0GY0wFYC0Iv7t9hOXCmLBgdQcb86TaxIZjWMV0sgeU1j7WUZuw83bJFc5wI9S2IXP1m6AkfVQ==
+ dependencies:
+ "@chain-registry/keplr" "1.28.0"
+ "@cosmos-kit/core" "^2.8.8"
+
+"@cosmos-kit/leap-metamask-cosmos-snap@^0.5.5":
+ version "0.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/leap-metamask-cosmos-snap/-/leap-metamask-cosmos-snap-0.5.5.tgz#d4f1d9cc25b6b32ba0cf0a286871246529d14783"
+ integrity sha512-MLJ/9TH0jdI3ToCAXpqD0frt5PO3mehWuf+8beljDA3384G5tfOjgPxWi8XsCTnmFdemFL1Vtbh2nEMQyPsoVQ==
dependencies:
"@chain-registry/keplr" "1.28.0"
"@cosmos-kit/core" "^2.8.9"
"@leapwallet/cosmos-snap-provider" "0.1.25"
"@metamask/providers" "^11.1.1"
-"@cosmos-kit/leap-mobile@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/leap-mobile/-/leap-mobile-2.6.9.tgz#c772988732c9fa92ecbc52b82671e47baa0ab76e"
- integrity sha512-ecyPcfeBSI8PiXbAMG9t9HLTJw7G3C3y6sZpVdJAOp0dOPbLLiHd3782veC0ts3Sps7Z148lSKkpIeRfdfRS4Q==
+"@cosmos-kit/leap-metamask-cosmos-snap@^0.5.8":
+ version "0.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/leap-metamask-cosmos-snap/-/leap-metamask-cosmos-snap-0.5.8.tgz#7207280eb79c931f84cc31ef5c7ab69bc2b9ed9b"
+ integrity sha512-5vy1CfA3ZFlib/qepAeYbgr0rgk4nfFvdJc4lgIwFmOhYVsZvC2+a1rNb1f9V0cuKDsNDLLV/DhUcnrROm1TQQ==
+ dependencies:
+ "@chain-registry/keplr" "1.28.0"
+ "@cosmos-kit/core" "^2.8.8"
+ "@leapwallet/cosmos-snap-provider" "0.1.25"
+ "@metamask/providers" "^11.1.1"
+
+"@cosmos-kit/leap-mobile@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/leap-mobile/-/leap-mobile-2.6.5.tgz#f1a93b6c6a435789f00b2d7740da05b35712b8e3"
+ integrity sha512-Y1bNvgHL6ORbQlR2B9OmwLH6Jdpc3u2X9KQ7FQQwYT1U8GpIXyYSh9Q8aQcM7L3oBCeL3oyCLOAO+48B9WegLg==
dependencies:
"@chain-registry/keplr" "1.28.0"
"@cosmos-kit/core" "^2.8.9"
"@cosmos-kit/walletconnect" "^2.5.9"
-"@cosmos-kit/leap@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/leap/-/leap-2.6.9.tgz#2aad3284699bd77331130b2b62350d0124ef3c0f"
- integrity sha512-jtHzdcQ+Jx7oqQuKl1blu+NpNbjmJxSt0i8La8XMsLK2cYMxmlefelabkK2uV/ToMVpV7DN0zrTvyPWblEWfMw==
+"@cosmos-kit/leap-mobile@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/leap-mobile/-/leap-mobile-2.6.8.tgz#921749155c470280f24cf57049ee3276e321bc5b"
+ integrity sha512-MNapVwhtSQgqFwi08krdY1R/ZhVbzOXc1xisb+zqGDGrsNksrVSQPG/C7soYb3sFa7+veDDpQDL3aa30s01NMg==
+ dependencies:
+ "@chain-registry/keplr" "1.28.0"
+ "@cosmos-kit/core" "^2.8.8"
+ "@cosmos-kit/walletconnect" "^2.5.8"
+
+"@cosmos-kit/leap@2.6.5", "@cosmos-kit/leap@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/leap/-/leap-2.6.5.tgz#aa1792f384528af4f6c69064624a20fc9a5af697"
+ integrity sha512-Rs2zItKaxpxe08jnQYgaMF8vtjSCPckYdMw+6TZ+gZ66TLNnTh3nE1Xr0bHoMZB5ElfEALtilp0PYfbBN3kOAw==
dependencies:
"@cosmos-kit/leap-extension" "^2.7.9"
"@cosmos-kit/leap-metamask-cosmos-snap" "^0.6.0"
"@cosmos-kit/leap-mobile" "^2.6.9"
-"@cosmos-kit/ledger@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/ledger/-/ledger-2.6.9.tgz#e37dfb2ac5941c825477210e091c2a745daf3d61"
- integrity sha512-+zn2U6v2c7vDSz8vTe/4oR4lSO/LH/FkdcrstuFTJm1Mk4tpYmJkJGmUQhYV4ZfjSHXfbfccrCl1jgu4uQgiZQ==
+"@cosmos-kit/leap@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/leap/-/leap-2.6.8.tgz#9836ff7be1038d9513c6db54191f579528bbb092"
+ integrity sha512-5ioVlKa+YqvBVdcEBLCjYLFr8bQgAZlVA74tNIyny+7+69IrZSEbJxPJoOm9i5IMaZuII0AJvThYY0IHR1zN4Q==
+ dependencies:
+ "@cosmos-kit/leap-extension" "^2.7.8"
+ "@cosmos-kit/leap-metamask-cosmos-snap" "^0.5.8"
+ "@cosmos-kit/leap-mobile" "^2.6.8"
+
+"@cosmos-kit/ledger@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/ledger/-/ledger-2.6.5.tgz#05c8563271f8faae4b49caace6f5baa08e319590"
+ integrity sha512-EFUa5QRvrfD+LAWhzpfCv/rTahzF9LzhpNqlEMoTY0badysC48K6RGNMXM66eJTXwhf8hmpJp2IjtWNwuZ9xqw==
dependencies:
"@cosmos-kit/core" "^2.8.9"
"@ledgerhq/hw-app-cosmos" "^6.28.1"
"@ledgerhq/hw-transport-webhid" "^6.27.15"
"@ledgerhq/hw-transport-webusb" "^6.27.15"
-"@cosmos-kit/okxwallet-extension@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/okxwallet-extension/-/okxwallet-extension-2.6.9.tgz#004a83b2b88d26951d74f7eb6747bdca68583d7b"
- integrity sha512-QWPtBSd9l7C5O3P+Djvhq/54KORpvGkk/pixsYmaf2Uyq7jbQUFLdeE9ZDsikWdaYtfvb9F4P+YEA1LfbpeaEg==
+"@cosmos-kit/ledger@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/ledger/-/ledger-2.6.8.tgz#f4131140b42092cba0ea506fbf809106807866af"
+ integrity sha512-6aFkO7iHpc9im6Ouwm7pXDpbBelqtegqhRUbQUTvoJgTPiy9H4ceH1oXYI/nIxwW6dvisjs8W94W6IlwaKYfeg==
+ dependencies:
+ "@cosmos-kit/core" "^2.8.8"
+ "@ledgerhq/hw-app-cosmos" "^6.28.1"
+ "@ledgerhq/hw-transport-webhid" "^6.27.15"
+ "@ledgerhq/hw-transport-webusb" "^6.27.15"
+
+"@cosmos-kit/okxwallet-extension@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/okxwallet-extension/-/okxwallet-extension-2.6.5.tgz#14cbe84ea8fa8bb98d1e618c9521627305733fc4"
+ integrity sha512-gcCsL1Uk3KRqIQdAipRo52QJeDKRaLiBmg6YIpmYsHlneB6jNWlVCdmB2exjKknvtQDbjKacRiCT3sfkJG6xwg==
dependencies:
"@cosmos-kit/core" "^2.8.9"
-"@cosmos-kit/omni-mobile@^2.5.9":
- version "2.5.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/omni-mobile/-/omni-mobile-2.5.9.tgz#c039dd9e1a99d799ad6e92d9094bd7e55bc0f480"
- integrity sha512-SRu6S/PLRfVpO6BFHgPfwuO5om3HhizFYrOvynxZ7zp4yOWsc8c5tQjNcMSpQOFrLx24cJUFiCdd8hfOFthPXw==
+"@cosmos-kit/okxwallet-extension@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/okxwallet-extension/-/okxwallet-extension-2.6.8.tgz#0ba872819d4c1d86b71ad751920930272794fecb"
+ integrity sha512-c6GBMu0Nh88fbLPylG3zOdGs9xp/tC445Wy70fXO2Ijay06ms/gPi+coqARGpwub7dPAqvDMu1nfcaAqT/IOJg==
+ dependencies:
+ "@cosmos-kit/core" "^2.8.8"
+
+"@cosmos-kit/omni-mobile@^2.5.5":
+ version "2.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/omni-mobile/-/omni-mobile-2.5.5.tgz#bb398093a5ac53d3137227e5055941594c86558c"
+ integrity sha512-7PWYYyiayhqjcNwCYM8Uc5Xg+FlCR5k1sdWK6nrJKEmJ2kRIrjnoKjx9a0ucNF+eAKxnYfTLMErFXapU8r4VNw==
dependencies:
"@cosmos-kit/core" "^2.8.9"
"@cosmos-kit/walletconnect" "^2.5.9"
-"@cosmos-kit/omni@^2.5.9":
- version "2.5.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/omni/-/omni-2.5.9.tgz#931c15d3ea3b1d53cf73393d3f842fc4b4520aa3"
- integrity sha512-oJ5ve7Sf7i7g/wpFtisPJfYGpjXweldw0LaYUWPUSttjS+WWhMFWkQGr2lKjyC1A/8Jdmsl8STOJmfyYpD8MMg==
+"@cosmos-kit/omni-mobile@^2.5.8":
+ version "2.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/omni-mobile/-/omni-mobile-2.5.8.tgz#b696f7ffdb1a304c5a46d03c0b13afea5ab9638c"
+ integrity sha512-veUFYwoF6SqPkZGPF02kOU3fLova7S8eIZ2qrBpoliLaMMZdHCaqrhdYZqKiA4sAPHLjcQ3rpb15SDbfJddFmw==
+ dependencies:
+ "@cosmos-kit/core" "^2.8.8"
+ "@cosmos-kit/walletconnect" "^2.5.8"
+
+"@cosmos-kit/omni@^2.5.5":
+ version "2.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/omni/-/omni-2.5.5.tgz#3bec481d59a4a184cd7b21720b1b64ccb9c9c9f9"
+ integrity sha512-f0ZBnc6KR4CIX7WV2K/PJtryMljyy8HjpdgfWBODifC8Cw/VQdHIEzw6JZ5QAe2fhLmRLr1tiwEbhWK8Vm/8Jg==
dependencies:
"@cosmos-kit/omni-mobile" "^2.5.9"
-"@cosmos-kit/react-lite@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/react-lite/-/react-lite-2.6.9.tgz#11d1ad07fc087bad5cac5ee623596bdf70b80681"
- integrity sha512-lDD6JcJstQrrQ5ZpdK2xk2OVma8DNwBe3ng3oNKAQmFTAagRjFS+TwkYF1Onl1kAcNmk0TIYStPzzUm9RkwSNw==
+"@cosmos-kit/omni@^2.5.8":
+ version "2.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/omni/-/omni-2.5.8.tgz#c79739934896cad7c77ede49d0c19ea086d0ab65"
+ integrity sha512-innyul/wgmu1DPyz3Xm2L4C699cRjFN/fAnnLNkPqTyiDc+7q8AGN9np9p6lpUYr9gnXehmu+5eWF9QEon+p8g==
+ dependencies:
+ "@cosmos-kit/omni-mobile" "^2.5.8"
+
+"@cosmos-kit/react-lite@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/react-lite/-/react-lite-2.6.5.tgz#b8a0e37c544635b8578a62dfb02390fe23feca50"
+ integrity sha512-+SuoAgIDwgh0TqmA26i2wUTp4c51dtEHetre74D7D/O5atwCV2ufE9QF3280ehJRnSb8B0BYvdrc5GOGZIN18w==
dependencies:
"@chain-registry/types" "0.17.0"
"@cosmos-kit/core" "^2.8.9"
-"@cosmos-kit/react@2.10.9":
- version "2.10.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/react/-/react-2.10.9.tgz#3ddbc5937a8b77437c184568f8a6da795a414235"
- integrity sha512-1D30apja4hRMGTCz7iifuyMYngu//ClhGGHq3D41jvvrrF3VXYgOQ+5/lPbBEtEpHnCx4RLtsLxf984uIVqfHw==
+"@cosmos-kit/react-lite@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/react-lite/-/react-lite-2.6.8.tgz#9b7860c21f8124aa713e4d1b44bc1b59bb1b49c0"
+ integrity sha512-h2bg4Z/lMXjgzOlBU+Rjl5ywDvOIdICPrKya6QpHPF30hNCYh98iiyZn83HgBGk0QWHwNhJKRJ1cv2jxjeQEIQ==
+ dependencies:
+ "@chain-registry/types" "0.17.0"
+ "@cosmos-kit/core" "^2.8.8"
+
+"@cosmos-kit/react@2.10.5":
+ version "2.10.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/react/-/react-2.10.5.tgz#fd4c00cf0c2bad8cca176a6f880876d13bc5ae96"
+ integrity sha512-xQUAOqu8NxkDKr/7Z/lRI8zGrmDoFQxtP/QELPLb3cJv5BAwfsGXZykm7ulHljuGuuBv0H6aLnxT50Bpyq6Ebw==
dependencies:
"@chain-registry/types" "0.17.0"
"@cosmos-kit/core" "^2.8.9"
"@cosmos-kit/react-lite" "^2.6.9"
"@react-icons/all-files" "^4.1.0"
-"@cosmos-kit/shell-extension@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/shell-extension/-/shell-extension-2.6.9.tgz#a6b3c4611b273fbaf034bba47b9ac196c6bb8f8c"
- integrity sha512-Tl4mnWr0ns8SCNeV1cTqBcQvywl+EiR5YsdlFDnQ04u167Bxe++VXUKKSPBHcwSe2xHslzp9UGK3jGhspaQDWQ==
+"@cosmos-kit/react@2.10.8":
+ version "2.10.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/react/-/react-2.10.8.tgz#bc6850d5852646a28660a6cd7c3f8bcb4d9fadcb"
+ integrity sha512-UESlMSEx9IGXRH007YuczotknoVGNQW4VpOtWCuKPVL+HPoMNeAvM4Jcr2gD3wD70HpU678fmR98QNdpzAtQog==
+ dependencies:
+ "@chain-registry/types" "0.17.0"
+ "@cosmos-kit/core" "^2.8.8"
+ "@cosmos-kit/react-lite" "^2.6.8"
+ "@react-icons/all-files" "^4.1.0"
+
+"@cosmos-kit/shell-extension@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/shell-extension/-/shell-extension-2.6.5.tgz#4b31d54c1c8c895326019c2a731ffecaca6afa18"
+ integrity sha512-OMkqlPveOZeFTZ3LT4l5TK8xvyyhQNHGATNWhLqZidP21uSPEdUUbdufHYatAGKbFVZqM8GH5sz8Sk0bSxneNA==
dependencies:
"@chain-registry/keplr" "1.28.0"
"@cosmos-kit/core" "^2.8.9"
-"@cosmos-kit/shell@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/shell/-/shell-2.6.9.tgz#61cb3390fdac87773caddd5cd5cb0880fde0acd3"
- integrity sha512-YgjYw4m53IsLw9Gusq8IlewcVuR8aF/3+UU9+H1EXVpBiEEDwzEW2+k6Q8XqtSJUAy87BCwjn5cgAxQp73spLw==
+"@cosmos-kit/shell-extension@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/shell-extension/-/shell-extension-2.6.8.tgz#7df9bba0a786555bd7c4e7b548729932617e4b23"
+ integrity sha512-p8vuGdMtcVEYwKGU1HavEBKGm2ox8K/bR5r7nrlSJeumCFXbvaNCGujJlmiU5ETRJsgdSCSRdQmbNWRCpQzglw==
+ dependencies:
+ "@chain-registry/keplr" "1.28.0"
+ "@cosmos-kit/core" "^2.8.8"
+
+"@cosmos-kit/shell@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/shell/-/shell-2.6.5.tgz#60214b8023a694a4360406091eac6c794e96d5f1"
+ integrity sha512-KmgEVv68lq6eEAQ5M/JQzdBa8oZ7K7mdnk/cDa42lKPCiEnRqlT4GTiDzHspfAJMFFmBIpawoXVAzSZO83MxIg==
dependencies:
"@cosmos-kit/shell-extension" "^2.6.9"
-"@cosmos-kit/station-extension@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/station-extension/-/station-extension-2.6.9.tgz#83beddee1cb407f89d7844735478746bc0877f90"
- integrity sha512-OHtOYHGW5/i+l4VpV8zu1Vfduy+aJpH9b8yUjuumcYS1u95zDvBSFNc1ejHcBeOey+fEVObbid6e5WX5B5pmQQ==
+"@cosmos-kit/shell@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/shell/-/shell-2.6.8.tgz#af1755afe55424df8da8bf7c53f006ca2f8d46de"
+ integrity sha512-TKPIKbZ9XfeWXlOCW3muBko5lpvhfDVlyQ+vs+69tkskMWwggxtXdDi3nsLTvsCxag2bWF19sU9Si31BuEcn0Q==
+ dependencies:
+ "@cosmos-kit/shell-extension" "^2.6.8"
+
+"@cosmos-kit/station-extension@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/station-extension/-/station-extension-2.6.5.tgz#2efbf5b6f00a824bf238c178d0a0f8695b0031e3"
+ integrity sha512-ABwOEFZ5bm+vCCnNKjlb2kfFK2idyVXQV4b99/Do58ufsiDQn01PtRie2wxh4V+v38NvPvrBrhCKO93Q4ZLDiA==
dependencies:
"@chain-registry/types" "0.17.0"
"@cosmos-kit/core" "^2.8.9"
@@ -4642,47 +4925,95 @@
"@terra-money/station-connector" "^1.0.5"
"@terra-money/wallet-types" "^3.11.2"
-"@cosmos-kit/station@^2.5.9":
- version "2.5.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/station/-/station-2.5.9.tgz#a71b4bbb497c49dab3782a494da3e9a655a4268f"
- integrity sha512-EIDt586MmT72sCERPOobpUxDQWMoitGIISY9X0fA0NeKTvf4l963ROewx/KHSs/oKRKI5OeM4DShJIF1B7+ZNQ==
+"@cosmos-kit/station-extension@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/station-extension/-/station-extension-2.6.8.tgz#8bab5c42afdd8d078b1b561ace3bd5d03fd52c6d"
+ integrity sha512-7PuqWWAD8U4zuibeLx0X1OwwTvVCpnQKna80OxjZm5TGA3+adOQem98+jv4cDCxV9RwR5k6B3YbqzSpGAqCoNA==
+ dependencies:
+ "@chain-registry/types" "0.17.0"
+ "@cosmos-kit/core" "^2.8.8"
+ "@terra-money/feather.js" "^1.0.8"
+ "@terra-money/station-connector" "^1.0.5"
+ "@terra-money/wallet-types" "^3.11.2"
+
+"@cosmos-kit/station@^2.5.5":
+ version "2.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/station/-/station-2.5.5.tgz#388b14d156ee8b2b0c7162270d789bfbb85c5435"
+ integrity sha512-cEB9b+4Dmnq616twEL9C8RAtEzLKWShOGHARWMuPzIQtUbpXRgAk3YqyyD3uNVtd8yy3qaS4LAhm0AknR6HYYQ==
dependencies:
"@cosmos-kit/station-extension" "^2.6.9"
-"@cosmos-kit/trust-mobile@^2.5.9":
- version "2.5.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/trust-mobile/-/trust-mobile-2.5.9.tgz#b469252096b03d2ede9bb68a7d172fd4ff7478e5"
- integrity sha512-ib9Ojk02hZeKslDfu1S8rbmGu7Hnc+ldcm6hydshmubPAozF8o5Mv6KisTSdktewgkYrZzKVd6xWaB1zxQFEOA==
+"@cosmos-kit/station@^2.5.8":
+ version "2.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/station/-/station-2.5.8.tgz#ada6af087a76c8f205c86820c1225c4390071d9a"
+ integrity sha512-6zfJMB4wu02FQ00IkRijqNN1ttH2A6uZl7y5mUC+Qyh2UHAG866geBZPmQEtt4JNL8lyNVkYfZoq6lwU+tBKrg==
+ dependencies:
+ "@cosmos-kit/station-extension" "^2.6.8"
+
+"@cosmos-kit/trust-mobile@^2.5.5":
+ version "2.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/trust-mobile/-/trust-mobile-2.5.5.tgz#f507bfd867ec12450add5d79a1abc4c4d5291123"
+ integrity sha512-sH6P7nb5apnndVQrarQMy1n00zQ5SG9INGa9o2ddJpYRatSOjxy2EeNrcJ0e19iYK4mb+FFFBQ95cajJ80IrIw==
dependencies:
"@cosmos-kit/core" "^2.8.9"
"@cosmos-kit/walletconnect" "^2.5.9"
-"@cosmos-kit/trust@^2.5.9":
- version "2.5.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/trust/-/trust-2.5.9.tgz#80a9455da3716129e073cf37e609fdf02eff2906"
- integrity sha512-hLH1kgBqrQpQd8EQq1GUj/fiBkSDyedoUrGNZI0OfqXn/G6DI0p+70V/mD/76TrKMZPDRJx73q0KdnraJKfTUg==
+"@cosmos-kit/trust-mobile@^2.5.8":
+ version "2.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/trust-mobile/-/trust-mobile-2.5.8.tgz#7c2e2ac99a2c0791b5297b4ff2e8de8b51d3ce92"
+ integrity sha512-SbVZPme1TmzVaWY03b+surFNnxEyKaGYt4QQYb2GZ9GYJKIMKGPOVWsp2ANVxwM1r40a+PQt6HdTnWuQyY3usA==
+ dependencies:
+ "@cosmos-kit/core" "^2.8.8"
+ "@cosmos-kit/walletconnect" "^2.5.8"
+
+"@cosmos-kit/trust@^2.5.5":
+ version "2.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/trust/-/trust-2.5.5.tgz#7cc4f6ef44a078c8cb5d83473538a2335ae8961b"
+ integrity sha512-Kis9jZ9XFWUn+FwgCfdFLF2SgOO5FaEfIs2broY3a5C/v4oIgFbeHFkLkzjW92xwmbj9g0S0MpRCdsm66zPb/g==
dependencies:
"@cosmos-kit/trust-mobile" "^2.5.9"
-"@cosmos-kit/vectis-extension@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/vectis-extension/-/vectis-extension-2.6.9.tgz#6f0d3e19c38ef158746522ba3a622ec9c3e02636"
- integrity sha512-Q8lzeOt0wEGqLWvEQ+cijkgiF7LZWVu6vsCJ6EKLl5814N92TiWOCLWEVJnaZx2rbG8dkxWQW5HpEOVfpUf4+w==
+"@cosmos-kit/trust@^2.5.8":
+ version "2.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/trust/-/trust-2.5.8.tgz#7dfc3969d6b60691eb6578ac71fe7f4b74a68ed4"
+ integrity sha512-SxzyAxPa6QMxOP9sVcuAO4Wkd9YyS8t3WF7flmkafW3s9N3E1Yf0jMMk44SODHIHmssH3TmOqojt9j+TrDzajA==
+ dependencies:
+ "@cosmos-kit/trust-mobile" "^2.5.8"
+
+"@cosmos-kit/vectis-extension@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/vectis-extension/-/vectis-extension-2.6.5.tgz#a4bb48dbae04be43710722a7583639fc8449c595"
+ integrity sha512-LUAg2Uj2143SqPL8EjC91V2UvPDJkvF2Yu+KIn9ViaC0Wr2d+t4usCY+4G/fnqgnv+QMuCTuA/85eSG7K7+Fwg==
dependencies:
"@chain-registry/keplr" "1.28.0"
"@cosmos-kit/core" "^2.8.9"
-"@cosmos-kit/vectis@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/vectis/-/vectis-2.6.9.tgz#9110658a0e5417b34f9168032c3db960d23c485a"
- integrity sha512-9tVdus0cqudVND5GPUhQgptpHgY1BGNAOsek//WBy3wwmzu6tbZfCwQBGG7hcz1p9peDfUHSLHNvKbvX9/KgJw==
+"@cosmos-kit/vectis-extension@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/vectis-extension/-/vectis-extension-2.6.8.tgz#0d67a2394a23cf5914f814278580bbcdbd383e48"
+ integrity sha512-2/CHDtBSoKGXN3zndTpVZZmwmE8pgTXth4KG/MapZsI5LIamnzLnkZcp5xDbBOFQJ3uLbHELHxA6fBp33ho+WQ==
+ dependencies:
+ "@chain-registry/keplr" "1.28.0"
+ "@cosmos-kit/core" "^2.8.8"
+
+"@cosmos-kit/vectis@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/vectis/-/vectis-2.6.5.tgz#0156104efb7ca8cacf9adf345707188c3a8615f0"
+ integrity sha512-AgmaSUA2bq8m+pdJnPH0f2854D9gskkdqmYkPFiYSSBASDJLcuq4Ul2HYC+YvxeUVL1fQL81LHoxNP8B5TBcSQ==
dependencies:
"@cosmos-kit/vectis-extension" "^2.6.9"
-"@cosmos-kit/walletconnect@^2.5.9":
- version "2.5.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/walletconnect/-/walletconnect-2.5.9.tgz#10c9b6f0077801e39eedf89092deb6310b47d746"
- integrity sha512-wQ3qR75Q5ZpUZMO96FwBVSuLme4z4mZ8OD8fFgs1QM16Kn81+oQ00kXDT/MkokDAVCdz91qS4XHxQEAvxSm0qw==
+"@cosmos-kit/vectis@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/vectis/-/vectis-2.6.8.tgz#a76c4757c6781614896e16d4d85661efbbe55093"
+ integrity sha512-Tfbz8DO6hZ3cfmm8c1Zgyyzgywo5ubXYhdjOn8lGvN6Y7cL4qOxDY941ZDWtJNMb3O+kLIszBGsSrPBO6tArPA==
+ dependencies:
+ "@cosmos-kit/vectis-extension" "^2.6.8"
+
+"@cosmos-kit/walletconnect@^2.5.5":
+ version "2.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/walletconnect/-/walletconnect-2.5.5.tgz#1e8a93c61da7302cfd7746943083be12813e739f"
+ integrity sha512-VpmAT1Yf4YakNXGaBLGtFKGTR8CWrtvblERDEqqlYQxXKJUQERGzsVzJ8t9C137exojWbKEAgvCAvQOzI4vrKw==
dependencies:
"@cosmjs/proto-signing" "0.32.2"
"@cosmos-kit/core" "^2.8.9"
@@ -4690,19 +5021,44 @@
"@walletconnect/utils" "^2.9.0"
events "3.3.0"
-"@cosmos-kit/xdefi-extension@^2.6.9":
- version "2.6.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/xdefi-extension/-/xdefi-extension-2.6.9.tgz#d3a9938a8b907cd5ac86966fad636b9568ce13b6"
- integrity sha512-TenOQOzCiYjQ+LaEkgTnwynRy7T09WK6e7FvsWFcF9HSduJvc4pUuy7Bd/oHfyMiNgeq6ccWxgkFGUh74qkAuw==
+"@cosmos-kit/walletconnect@^2.5.8":
+ version "2.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/walletconnect/-/walletconnect-2.5.8.tgz#d86031bf5bb7997b2df9188fbce740fc54be1067"
+ integrity sha512-k4ookgLGOMy3z/+GCfU6WfOIRFTLB0pu4lwv5fBw10yZXvzeNLWPUNPT8jEe4OF6W6ifcZ+dV7HJt41FiV7zyg==
+ dependencies:
+ "@cosmjs/proto-signing" "0.32.2"
+ "@cosmos-kit/core" "^2.8.8"
+ "@walletconnect/sign-client" "^2.9.0"
+ "@walletconnect/utils" "^2.9.0"
+ events "3.3.0"
+
+"@cosmos-kit/xdefi-extension@^2.6.5":
+ version "2.6.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/xdefi-extension/-/xdefi-extension-2.6.5.tgz#87c7793fee59ed0cb3573df3ad3db576004018e1"
+ integrity sha512-OpQtE2oNH7ExSxbM3/BGZ2C+YAMncu7P2ZtS5TTCJBW7b242y6Dtz5w+oe8pygf4H2wiB/zoGOFmb2ZM69exfg==
dependencies:
"@cosmos-kit/core" "^2.8.9"
-"@cosmos-kit/xdefi@^2.5.9":
- version "2.5.9"
- resolved "https://registry.npmjs.org/@cosmos-kit/xdefi/-/xdefi-2.5.9.tgz#5ec9c5eed555da4e4c7e118f1902f6fcfdc11938"
- integrity sha512-GgSXDaPLBNV9AjIQkmU/NWduYJM15Qqvi2D4B6fw4aWHEsRODw15gnGQMpAvGTBtNU0iYTp7MvvvnjT1wTQxeg==
+"@cosmos-kit/xdefi-extension@^2.6.8":
+ version "2.6.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/xdefi-extension/-/xdefi-extension-2.6.8.tgz#24048bc4e56f37443cc64fce05be31dd8d3a6ac9"
+ integrity sha512-dqxk1vc4kEPVrWwOEv1l1HPVoa3ypoBpACXY+5YVNBVwy83VUQklS5LIr5UPbRD3sijLJGLIXWuSM8xRJgjE9g==
+ dependencies:
+ "@cosmos-kit/core" "^2.8.8"
+
+"@cosmos-kit/xdefi@^2.5.5":
+ version "2.5.5"
+ resolved "https://registry.npmjs.org/@cosmos-kit/xdefi/-/xdefi-2.5.5.tgz#0f67122a83fc61edbc9aa813c59f3fdbb18f76fa"
+ integrity sha512-sNK/In4EIzL7K4OjKJ0w42bnU8E4LIz51Bi3EneMTrXrS8PbMYcN1c2ItkCW+Hk70/EQQ0XHFAZiYpE8EFyGZQ==
dependencies:
- "@cosmos-kit/xdefi-extension" "^2.6.9"
+ "@cosmos-kit/xdefi-extension" "^2.6.5"
+
+"@cosmos-kit/xdefi@^2.5.8":
+ version "2.5.8"
+ resolved "https://registry.npmjs.org/@cosmos-kit/xdefi/-/xdefi-2.5.8.tgz#5d181f60df093207fa1b5b24c44038f590cd95cc"
+ integrity sha512-3VCPT26v12nMLBVq+J4muHXluwY15GgiET8ktkODuudmqx6Oe2CMoZs7HCOl90D4MHFYzCj/tRCQBx3tVigXGQ==
+ dependencies:
+ "@cosmos-kit/xdefi-extension" "^2.6.8"
"@cosmostation/extension-client@0.1.15":
version "0.1.15"
@@ -9646,6 +10002,13 @@
resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
+"@types/lodash.memoize@^4.1.7":
+ version "4.1.9"
+ resolved "https://registry.npmjs.org/@types/lodash.memoize/-/lodash.memoize-4.1.9.tgz#9f8912d39b6e450c0d342a2b74c99d331bf2016b"
+ integrity sha512-glY1nQuoqX4Ft8Uk+KfJudOD7DQbbEDF6k9XpGncaohW3RW4eSWBlx6AA0fZCrh40tZcQNH4jS/Oc59J6Eq+aw==
+ dependencies:
+ "@types/lodash" "*"
+
"@types/lodash.mergewith@4.6.7":
version "4.6.7"
resolved "https://registry.npmmirror.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz"
@@ -10290,6 +10653,11 @@
fast-url-parser "^1.1.3"
tslib "^2.3.1"
+"@wojtekmaj/date-utils@^1.1.3":
+ version "1.5.1"
+ resolved "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.5.1.tgz#c3cd67177ac781cfa5736219d702a55a2aea5f2b"
+ integrity sha512-+i7+JmNiE/3c9FKxzWFi2IjRJ+KzZl1QPu6QNrsgaa2MuBgXvUy4gA1TVzf/JMdIIloB76xSKikTWuyYAIVLww==
+
"@wry/context@^0.7.0":
version "0.7.3"
resolved "https://registry.npmmirror.com/@wry/context/-/context-0.7.3.tgz"
@@ -11935,6 +12303,11 @@ clsx@^2.0.0:
resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb"
integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==
+clsx@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb"
+ integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==
+
cluster-key-slot@^1.1.0:
version "1.1.2"
resolved "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac"
@@ -12332,22 +12705,44 @@ cosmos-kit@2.8.6:
resolved "https://registry.npmjs.org/cosmos-kit/-/cosmos-kit-2.8.6.tgz#4bb336731af08199a1461d94bbe8e2cf7a90acd6"
integrity sha512-7Ksgr3AAhB6ttDVM5yxiWbYybt29QxgNSnMqO6XUO0Unk/LsfedwqfAtCKJJWjJzWEDu3eqkivsKrSqienH24Q==
dependencies:
- "@cosmos-kit/coin98" "^2.6.10"
- "@cosmos-kit/compass" "^2.6.9"
- "@cosmos-kit/cosmostation" "^2.6.10"
- "@cosmos-kit/exodus" "^2.5.9"
- "@cosmos-kit/fin" "^2.6.9"
- "@cosmos-kit/frontier" "^2.5.9"
- "@cosmos-kit/keplr" "^2.6.9"
- "@cosmos-kit/leap" "^2.6.9"
- "@cosmos-kit/ledger" "^2.6.9"
- "@cosmos-kit/okxwallet-extension" "^2.6.9"
- "@cosmos-kit/omni" "^2.5.9"
- "@cosmos-kit/shell" "^2.6.9"
- "@cosmos-kit/station" "^2.5.9"
- "@cosmos-kit/trust" "^2.5.9"
- "@cosmos-kit/vectis" "^2.6.9"
- "@cosmos-kit/xdefi" "^2.5.9"
+ "@cosmos-kit/coin98" "^2.6.5"
+ "@cosmos-kit/compass" "^2.6.5"
+ "@cosmos-kit/cosmostation" "^2.6.5"
+ "@cosmos-kit/exodus" "^2.5.5"
+ "@cosmos-kit/fin" "^2.6.5"
+ "@cosmos-kit/frontier" "^2.5.5"
+ "@cosmos-kit/keplr" "^2.6.5"
+ "@cosmos-kit/leap" "^2.6.5"
+ "@cosmos-kit/ledger" "^2.6.5"
+ "@cosmos-kit/okxwallet-extension" "^2.6.5"
+ "@cosmos-kit/omni" "^2.5.5"
+ "@cosmos-kit/shell" "^2.6.5"
+ "@cosmos-kit/station" "^2.5.5"
+ "@cosmos-kit/trust" "^2.5.5"
+ "@cosmos-kit/vectis" "^2.6.5"
+ "@cosmos-kit/xdefi" "^2.5.5"
+
+cosmos-kit@2.8.4:
+ version "2.8.4"
+ resolved "https://registry.npmjs.org/cosmos-kit/-/cosmos-kit-2.8.4.tgz#24469e570c7a885b8c3841afc568cab1f1e91484"
+ integrity sha512-9Cmxvu0AKOlPInFdTjdLy8IjEb+dk70VaMppHzyjuY+FliAw9O5jMCkhLIlD9KOJSPD3lOk3wlAnSRHnFaQOpA==
+ dependencies:
+ "@cosmos-kit/coin98" "^2.6.8"
+ "@cosmos-kit/compass" "^2.6.8"
+ "@cosmos-kit/cosmostation" "^2.6.8"
+ "@cosmos-kit/exodus" "^2.5.8"
+ "@cosmos-kit/fin" "^2.6.8"
+ "@cosmos-kit/frontier" "^2.5.8"
+ "@cosmos-kit/keplr" "^2.6.8"
+ "@cosmos-kit/leap" "^2.6.8"
+ "@cosmos-kit/ledger" "^2.6.8"
+ "@cosmos-kit/okxwallet-extension" "^2.6.8"
+ "@cosmos-kit/omni" "^2.5.8"
+ "@cosmos-kit/shell" "^2.6.8"
+ "@cosmos-kit/station" "^2.5.8"
+ "@cosmos-kit/trust" "^2.5.8"
+ "@cosmos-kit/vectis" "^2.6.8"
+ "@cosmos-kit/xdefi" "^2.5.8"
create-ecdh@^4.0.0:
version "4.0.4"
@@ -12524,6 +12919,11 @@ dateformat@^3.0.0:
resolved "https://registry.npmmirror.com/dateformat/-/dateformat-3.0.3.tgz"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
+dayjs@1.11.10:
+ version "1.11.10"
+ resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
+ integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
+
dayjs@1.11.8:
version "1.11.8"
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz"
@@ -14406,6 +14806,14 @@ get-tsconfig@^4.2.0:
resolved "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.4.0.tgz"
integrity sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==
+get-user-locale@^2.2.1:
+ version "2.3.1"
+ resolved "https://registry.npmjs.org/get-user-locale/-/get-user-locale-2.3.1.tgz#fc7319429c8a70fac01b3b2a0b08b0c71c1d3fe2"
+ integrity sha512-VEvcsqKYx7zhZYC1CjecrDC5ziPSpl1gSm0qFFJhHSGDrSC+x4+p1KojWC/83QX//j476gFhkVXP/kNUc9q+bQ==
+ dependencies:
+ "@types/lodash.memoize" "^4.1.7"
+ lodash.memoize "^4.1.1"
+
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz"
@@ -17222,7 +17630,7 @@ lodash.isplainobject@^4.0.6:
resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz"
integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
-lodash.memoize@4.x:
+lodash.memoize@4.x, lodash.memoize@^4.1.1:
version "4.1.2"
resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz"
integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
@@ -19980,6 +20388,17 @@ react-aria@^3.29.1:
"@react-aria/visually-hidden" "^3.8.8"
"@react-types/shared" "^3.22.0"
+react-calendar@4.8.0:
+ version "4.8.0"
+ resolved "https://registry.npmjs.org/react-calendar/-/react-calendar-4.8.0.tgz#61edbba6d17e7ef8a8012de9143b5e5ff41104c8"
+ integrity sha512-qFgwo+p58sgv1QYMI1oGNaop90eJVKuHTZ3ZgBfrrpUb+9cAexxsKat0sAszgsizPMVo7vOXedV7Lqa0GQGMvA==
+ dependencies:
+ "@wojtekmaj/date-utils" "^1.1.3"
+ clsx "^2.0.0"
+ get-user-locale "^2.2.1"
+ prop-types "^15.6.0"
+ warning "^4.0.0"
+
react-clientside-effect@^1.2.6:
version "1.2.6"
resolved "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz"
@@ -22760,6 +23179,13 @@ walker@^1.0.8:
dependencies:
makeerror "1.0.12"
+warning@^4.0.0:
+ version "4.0.3"
+ resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
+ integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
+ dependencies:
+ loose-envify "^1.0.0"
+
wasm-ast-types@^0.24.0:
version "0.24.0"
resolved "https://registry.npmjs.org/wasm-ast-types/-/wasm-ast-types-0.24.0.tgz"