diff --git a/frontend/src/components/FeegranterInfo.jsx b/frontend/src/components/FeegranterInfo.jsx new file mode 100644 index 000000000..617418f7e --- /dev/null +++ b/frontend/src/components/FeegranterInfo.jsx @@ -0,0 +1,63 @@ +import CloseOutlined from "@mui/icons-material/CloseOutlined"; +import { Alert, Button, Typography } from "@mui/material"; +import React, { useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { getICNSName } from "../features/common/commonSlice"; + +export default function FeegranterInfo(props) { + const dispatch = useDispatch(); + const icnsNames = useSelector((state) => state.common.icnsNames); + + const [showAddress, setShowAddress] = useState(false); + + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + + const address = props.feegrant?.granter; + const name = fetchName(address); + + const toggleAddress = () => { + setShowAddress((showAddress) => !showAddress); + }; + + return ( + } + onClick={() => props.onRemove()} + > + Remove Feegrant + + } + > + + Transaction fees will be deducted from{" "} + + {showAddress ? address : name || address} + {" "} + account + + + ); +} diff --git a/frontend/src/components/FeegranterInfo.tsx b/frontend/src/components/FeegranterInfo.tsx deleted file mode 100644 index 8a5f8f25e..000000000 --- a/frontend/src/components/FeegranterInfo.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import CloseOutlined from "@mui/icons-material/CloseOutlined"; -import { Alert, Button, Typography } from "@mui/material"; -import { Grant } from "cosmjs-types/cosmos/feegrant/v1beta1/feegrant"; -import React from "react"; - -interface FeegranterInfoProps { - feegrant: Grant | undefined; - onRemove: () => {}; -} - -export default function FeegranterInfo(props: FeegranterInfoProps) { - return ( - } - onClick={() => props.onRemove()} - > - Remove Feegrant - - } - > - - Transaction fees will be deducted from{" "} - {props.feegrant?.granter} account - - - ); -} diff --git a/frontend/src/components/PeriodicFeeGrant.jsx b/frontend/src/components/PeriodicFeeGrant.jsx index 0ea3f6355..6998e19f4 100644 --- a/frontend/src/components/PeriodicFeeGrant.jsx +++ b/frontend/src/components/PeriodicFeeGrant.jsx @@ -11,14 +11,28 @@ import { FormControl } from "@mui/material"; import InputLabel from "@mui/material/InputLabel"; import MenuItem from "@mui/material/MenuItem"; import Select from "@mui/material/Select"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; +import { getICNSName } from "../features/common/commonSlice"; export function PeriodicFeegrant(props) { const { loading, onGrant, currency, granters, granter, setGranter } = props; const { handleSubmit, control, getValues } = useFormContext(); + const dispatch = useDispatch(); const isAuthzMode = useSelector((state) => state.common.authzMode); + const icnsNames = useSelector((state) => state.common.icnsNames); + + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; return ( <> @@ -46,7 +60,7 @@ export function PeriodicFeegrant(props) { > {granters.map((granter, index) => ( - {granter} + {fetchName(granter) || granter} ))} diff --git a/frontend/src/components/Send.js b/frontend/src/components/Send.js index 8fc749bd7..564fc961e 100644 --- a/frontend/src/components/Send.js +++ b/frontend/src/components/Send.js @@ -8,10 +8,12 @@ import Box from "@mui/material/Box"; import InputAdornment from "@mui/material/InputAdornment"; import CircularProgress from "@mui/material/CircularProgress"; import PropTypes from "prop-types"; +import { useDispatch, useSelector } from "react-redux"; import { FormControl } from "@mui/material"; import InputLabel from "@mui/material/InputLabel"; import MenuItem from "@mui/material/MenuItem"; import Select from "@mui/material/Select"; +import { getICNSName } from "../features/common/commonSlice"; Send.propTypes = { onSend: PropTypes.func.isRequired, @@ -34,10 +36,12 @@ export default function Send(props) { isAuthzMode, grantsToMe, setGranter, - granter + granter, } = props; + const dispatch = useDispatch(); const currency = chainInfo?.config?.currencies[0]; + const icnsNames = useSelector((state) => state.common.icnsNames); const { handleSubmit, control, setValue } = useForm({ defaultValues: { @@ -55,6 +59,17 @@ export default function Send(props) { }); }; + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + return ( - {isAuthzMode && grantsToMe?.length > 0 ? ( + {isAuthzMode && grantsToMe?.length > 0 ? ( {grantsToMe.map((granter, index) => ( - {granter} + {fetchName(granter) || granter} ))} diff --git a/frontend/src/components/Vote.js b/frontend/src/components/Vote.js index bf73efb43..b0ab06d3e 100644 --- a/frontend/src/components/Vote.js +++ b/frontend/src/components/Vote.js @@ -14,7 +14,7 @@ import MenuItem from "@mui/material/MenuItem"; import Select from "@mui/material/Select"; import PropTypes from "prop-types"; import { useDispatch } from "react-redux"; -import { setError } from "../features/common/commonSlice"; +import { getICNSName, setError } from "../features/common/commonSlice"; import CloseIcon from "@mui/icons-material/Close"; VoteDialog.propTypes = { @@ -37,11 +37,12 @@ export default function VoteDialog(props) { const [option, setOption] = React.useState(""); const dispatch = useDispatch(); const [granter, setGranter] = React.useState( - props.granters.length > 0 ? props.granters[0] : "" + props.granters.length ? props.granters[0] : "" ); const [justification, setJustification] = React.useState(""); const govTx = useSelector((state) => state.gov.tx); const authzExecTx = useSelector((state) => state.authz.execTx); + const icnsNames = useSelector((state) => state.common.icnsNames); const handleClose = () => { setOption(""); @@ -69,6 +70,17 @@ export default function VoteDialog(props) { setOption(e.target.value); }; + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + return ( {props.granters.map((granter, index) => ( - {granter} + {fetchName(granter) || granter} ))} diff --git a/frontend/src/components/common/NameAddress.jsx b/frontend/src/components/common/NameAddress.jsx new file mode 100644 index 000000000..f1982510f --- /dev/null +++ b/frontend/src/components/common/NameAddress.jsx @@ -0,0 +1,16 @@ +import React, { useState } from "react"; +import { shortenAddress } from "../../utils/util"; + +const NameAddress = ({ address, name }) => { + const [show, setShow] = useState(false); + const toggleAddress = () => { + setShow(!show); + }; + return ( + + {show ? shortenAddress(address, 24) : name || shortenAddress(address, 24)} + + ); +}; + +export default NameAddress; \ No newline at end of file diff --git a/frontend/src/components/feegrant/BasicFeeGrant.jsx b/frontend/src/components/feegrant/BasicFeeGrant.jsx index bf061027d..31c561a25 100644 --- a/frontend/src/components/feegrant/BasicFeeGrant.jsx +++ b/frontend/src/components/feegrant/BasicFeeGrant.jsx @@ -3,15 +3,18 @@ import { FormControl, InputAdornment, TextField } from "@mui/material"; import { DateTimePicker, LocalizationProvider } from "@mui/x-date-pickers"; import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns"; import { Controller, useFormContext } from "react-hook-form"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { useParams } from "react-router-dom"; import InputLabel from "@mui/material/InputLabel"; import MenuItem from "@mui/material/MenuItem"; import Select from "@mui/material/Select"; +import { getICNSName } from "../../features/common/commonSlice"; function BasicFeeGrant(props) { const { granters, granter, setGranter } = props; const params = useParams(); + const dispatch = useDispatch(); + const selectedNetwork = useSelector( (state) => state.common.selectedNetwork.chainName ); @@ -21,6 +24,7 @@ function BasicFeeGrant(props) { const networks = useSelector((state) => state.wallet.networks); const nameToChainIDs = useSelector((state) => state.wallet.nameToChainIDs); + const icnsNames = useSelector((state) => state.common.icnsNames); const currency = networks[nameToChainIDs[currentNetwork]]?.network.config.currencies; @@ -28,6 +32,17 @@ function BasicFeeGrant(props) { const { control } = useFormContext(); + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + return ( <> {isAuthzMode && granters?.length > 0 ? ( @@ -54,7 +69,7 @@ function BasicFeeGrant(props) { > {granters.map((granter, index) => ( - {granter} + {fetchName(granter) || granter} ))} diff --git a/frontend/src/components/group/MembersTable.js b/frontend/src/components/group/MembersTable.js index 502fad56e..393780cd4 100644 --- a/frontend/src/components/group/MembersTable.js +++ b/frontend/src/components/group/MembersTable.js @@ -6,12 +6,13 @@ import TableHead from "@mui/material/TableHead"; import Paper from "@mui/material/Paper"; import TablePagination from "@mui/material/TablePagination"; import PropTypes from "prop-types"; -import { shortenAddress } from "../../utils/util"; import { StyledTableCell, StyledTableRow } from "./../CustomTable"; import ContentCopyOutlined from "@mui/icons-material/ContentCopyOutlined"; import { Chip } from "@mui/material"; import { copyToClipboard } from "../../utils/clipboard"; -import { useDispatch } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; +import NameAddress from "../common/NameAddress"; +import { getICNSName } from "../../features/common/commonSlice"; const MembersTable = (props) => { const { members } = props; @@ -22,6 +23,8 @@ const MembersTable = (props) => { const dispatch = useDispatch(); + const icnsNames = useSelector((state) => state.common.icnsNames); + const handleChangePage = (event, newPage) => { setCurrentPage(newPage); }; @@ -30,6 +33,17 @@ const MembersTable = (props) => { setPerPage(event.target.value); }; + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + return ( @@ -38,7 +52,6 @@ const MembersTable = (props) => { Address Weight Name - {/* Action */} @@ -47,15 +60,14 @@ const MembersTable = (props) => { .map((row, index) => ( - {/* {shortenAddress(row?.member?.address, 26) || "-"} */} } - onDelete={() => { - copyToClipboard(row?.member?.address, dispatch); - }} - /> + label={} + size="small" + deleteIcon={} + onDelete={() => { + copyToClipboard(row?.member?.address, dispatch); + }} + /> {row?.member?.weight || "-"} @@ -63,22 +75,6 @@ const MembersTable = (props) => { {row?.member?.metadata || "-"} - {/* - - { - // handleDeleteMember({ - // address: row?.member?.address, - // weight: "0", - // metadata: row?.member?.metadata, - // }); - }} - color="error" - > - - - - */} ))} diff --git a/frontend/src/features/common/commonService.ts b/frontend/src/features/common/commonService.ts index 3d51b65d5..3e4e70076 100644 --- a/frontend/src/features/common/commonService.ts +++ b/frontend/src/features/common/commonService.ts @@ -1,3 +1,4 @@ +import { toBase64 } from "@cosmjs/encoding"; import Axios, { AxiosResponse } from "axios"; const BASE_URL = process.env.REACT_APP_API_URI; @@ -12,9 +13,17 @@ const fetchAllTokensPriceInfo = (): Promise => { return Axios.get(uri); }; +const fetchICNSName = (data?: any) : Promise => { + const query = btoa(JSON.stringify({"icns_names": {"address": data.address}})) + const baseURL = 'https://lcd.osmosis.zone/cosmwasm/wasm/v1/contract/osmo1xk0s8xgktn9x5vwcgtjdxqzadg88fgn33p8u9cnpdxwemvxscvast52cdd/smart'; + const uri = `${baseURL}/${query}`; + return Axios.get(uri); +}; + const result = { tokenInfo: fetchPriceInfo, allTokensInfo : fetchAllTokensPriceInfo, + fetchICNSName: fetchICNSName, }; export default result; diff --git a/frontend/src/features/common/commonSlice.js b/frontend/src/features/common/commonSlice.js index b16994ed9..a6bcde265 100644 --- a/frontend/src/features/common/commonSlice.js +++ b/frontend/src/features/common/commonSlice.js @@ -25,6 +25,7 @@ const initialState = { selectedNetwork: { chainName: "", }, + icnsNames: {}, selectedNetworkLocal: { chainName: "cosmoshub", }, @@ -59,6 +60,25 @@ export const getAllTokensPrice = createAsyncThunk( } ); +export const getICNSName = createAsyncThunk( + "common/getICNSName", + async (data, { rejectWithValue }) => { + try { + const response = await commonService.fetchICNSName({ + address: data.address, + }); + return { + data: response.data, + address: data.address, + }; + } catch (error) { + return rejectWithValue( + error?.response?.data?.message || error?.message || SOMETHING_WRONG + ); + } + } +); + export const commonSlice = createSlice({ name: "common", initialState, @@ -151,6 +171,21 @@ export const commonSlice = createSlice({ state.allTokensInfoState.error = action.payload; state.allTokensInfoState.info = {}; }); + + builder + .addCase(getICNSName.pending, (state) => {}) + .addCase(getICNSName.fulfilled, (state, action) => { + const address = action.payload?.address || ""; + if (address?.length) { + let result = { + name: action.payload?.data?.data?.primary_name, + status: "idle", + errMsg: "", + }; + state.icnsNames[address] = result; + } + }) + .addCase(getICNSName.rejected, (state) => {}); }, }); diff --git a/frontend/src/pages/authz/Authz.js b/frontend/src/pages/authz/Authz.js index fee12a492..f51959677 100644 --- a/frontend/src/pages/authz/Authz.js +++ b/frontend/src/pages/authz/Authz.js @@ -31,6 +31,7 @@ import { setError, removeFeegrant as removeFeegrantState, setFeegrant as setFeegrantState, + getICNSName, } from "../../features/common/commonSlice"; import { getMsgNameFromAuthz, @@ -43,7 +44,9 @@ import { getFeegrant, removeFeegrant as removeFeegrantLocalState, } from "../../utils/localStorage"; -import { CopyToClipboard } from "../../components/CopyToClipboard"; +import ContentCopyOutlined from "@mui/icons-material/ContentCopyOutlined"; +import { copyToClipboard } from "../../utils/clipboard"; +import NameAddress from "../../components/common/NameAddress"; export default function Authz() { const dispatch = useDispatch(); @@ -71,6 +74,7 @@ export default function Authz() { const grantsByMe = useSelector((state) => state.authz.grantsByMe?.[chainID]); const grantsToMe = useSelector((state) => state.authz.grantsToMe?.[chainID]); const txAuthzRes = useSelector((state) => state.authz.txAuthzRes); + const icnsNames = useSelector((state) => state.common.icnsNames); const handleInfoClose = (value) => { setInfoOpen(false); @@ -227,6 +231,17 @@ export default function Authz() { }); }; + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + return ( <> {selected?.authorization ? ( @@ -365,13 +380,19 @@ export default function Authz() { }} > - - {shortenAddress(row.grantee, 21)} - - + + } + size="small" + deleteIcon={} + onDelete={() => { + copyToClipboard(row.grantee, dispatch); + }} + /> - - {shortenAddress(row.granter, 21)} - - + + } + size="small" + deleteIcon={} + onDelete={() => { + copyToClipboard(row.granter, dispatch); + }} + /> { ); const feegrant = useSelector((state) => state.common.feegrant?.[chainName] || {}); const authzTx = useSelector((state) => state.authz.tx); + const icnsNames = useSelector((state) => state.common.icnsNames); + const currency = chainInfo?.config?.currencies[0]; const [tab, setTab] = useState(0); const [selected, setSelected] = React.useState({}); @@ -80,6 +86,17 @@ export const ChainAuthz = (props) => { ); }; + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + return chainGrantsByMe?.grants?.length || chainGrantsToMe?.grants?.length ? ( @@ -183,14 +200,32 @@ export const ChainAuthz = (props) => { }} > - - {shortenAddress(row.grantee, 21)} - - + + } + size="small" + deleteIcon={ + + } + onDelete={() => { + copyToClipboard(row.grantee, dispatch); + }} + /> + {/* + + */} { }} > - - {shortenAddress(row.granter, 21)} - - + + } + size="small" + deleteIcon={ + + } + onDelete={() => { + copyToClipboard(row.granter, dispatch); + }} + /> { (state) => state.common.feegrant?.[chainName] || {} ); const txStatus = useSelector((state) => state.feegrant.tx); + const icnsNames = useSelector((state) => state.common.icnsNames); + const currency = chainInfo?.config?.currencies[0]; const [tab, setTab] = useState(0); const [selected, setSelected] = React.useState({}); @@ -74,6 +80,17 @@ export const ChainGrants = (props) => { ); }; + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + return chainGrantsByMe?.length || chainGrantsToMe?.length ? ( @@ -177,13 +194,24 @@ export const ChainGrants = (props) => { }} > - - {shortenAddress(row.grantee, 21)} - - + + } + size="small" + deleteIcon={ + + } + onDelete={() => { + copyToClipboard(row.grantee, dispatch); + }} + /> {renderExpiration(row)} @@ -255,13 +283,24 @@ export const ChainGrants = (props) => { }} > - - {shortenAddress(row.granter, 21)} - - + + } + size="small" + deleteIcon={ + + } + onDelete={() => { + copyToClipboard(row.granter, dispatch); + }} + /> {renderExpiration(row)} diff --git a/frontend/src/pages/feegrant/Feegrant.js b/frontend/src/pages/feegrant/Feegrant.js index ed2edee8c..54d87017d 100644 --- a/frontend/src/pages/feegrant/Feegrant.js +++ b/frontend/src/pages/feegrant/Feegrant.js @@ -26,6 +26,7 @@ import { setFeegrant as setFeegrantState, resetFeegrant, removeFeegrant as removeFeegrantState, + getICNSName, } from "./../../features/common/commonSlice"; import Chip from "@mui/material/Chip"; import { getTypeURLName, shortenAddress } from "./../../utils/util"; @@ -48,7 +49,9 @@ import { } from "../../utils/localStorage"; import SelectNetwork from "../../components/common/SelectNetwork"; import FeegranterInfo from "../../components/FeegranterInfo"; -import { CopyToClipboard } from "../../components/CopyToClipboard"; +import ContentCopyOutlined from "@mui/icons-material/ContentCopyOutlined"; +import { copyToClipboard } from "../../utils/clipboard"; +import NameAddress from "../../components/common/NameAddress"; export const renderExpiration = (row) => { const PERIODIC_ALLOWANCE = "/cosmos.feegrant.v1beta1.PeriodicAllowance"; @@ -173,6 +176,7 @@ export default function Feegrant() { const currency = chainInfo?.config?.currencies[0]; const [infoOpen, setInfoOpen] = React.useState(false); const isNanoLedger = useSelector((state) => state.wallet.isNanoLedger); + const icnsNames = useSelector((state) => state.common.icnsNames); const [selected, setSelected] = React.useState({}); const [isNoAuthzs, setNoAuthzs] = useState(false); @@ -384,6 +388,17 @@ export default function Feegrant() { } }, [authzExecTx]); + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + return ( <> {feegrant?.granter?.length > 0 ? ( @@ -489,13 +504,26 @@ export default function Feegrant() { }} > - - {shortenAddress(row.grantee, 21)} - - + + } + size="small" + deleteIcon={} + onDelete={() => { + copyToClipboard(row.grantee, dispatch); + }} + /> + + + {renderExpiration(row)} @@ -571,13 +599,26 @@ export default function Feegrant() { }} > - - {shortenAddress(row.granter, 21)} - - + + } + size="small" + deleteIcon={} + onDelete={() => { + copyToClipboard(row.granter, dispatch); + }} + /> + + + {renderExpiration(row)} diff --git a/frontend/src/pages/group/Proposal.jsx b/frontend/src/pages/group/Proposal.jsx index 1b4750a05..41ed8371b 100644 --- a/frontend/src/pages/group/Proposal.jsx +++ b/frontend/src/pages/group/Proposal.jsx @@ -28,6 +28,8 @@ import { parseProposalStatus } from "../../components/group/ProposalCard"; import ContentCopyOutlined from "@mui/icons-material/ContentCopyOutlined"; import { copyToClipboard } from "../../utils/clipboard"; import VotingDetails from "./VotingDetails"; +import { getICNSName } from "../../features/common/commonSlice"; +import NameAddress from "../../components/common/NameAddress"; const ProposalInfo = ({ id, wallet, address, chainID, chainInfo }) => { const [voteOpen, setVoteOpen] = useState(false); @@ -37,6 +39,7 @@ const ProposalInfo = ({ id, wallet, address, chainID, chainInfo }) => { (state) => state.group?.groupProposal?.[chainID] ); const voteRes = useSelector((state) => state.group?.voteRes); + const icnsNames = useSelector((state) => state.common.icnsNames); const proposal = proposalInfo?.data?.proposal; const [total, setTotal] = useState(0); @@ -106,6 +109,17 @@ const ProposalInfo = ({ id, wallet, address, chainID, chainInfo }) => { ); }; + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + return ( <> @@ -361,7 +375,12 @@ const ProposalInfo = ({ id, wallet, address, chainID, chainInfo }) => { > {proposal?.proposers?.map((p, index) => ( + } size="small" deleteIcon={} onDelete={() => { @@ -501,8 +520,8 @@ function Proposal() { pagination: { limit: 100, key: "" }, chainID: chainID, }) - ) - }) + ); + }); return ( diff --git a/frontend/src/pages/slashing/UnjailPage.jsx b/frontend/src/pages/slashing/UnjailPage.jsx index 38dfa12a2..25d298235 100644 --- a/frontend/src/pages/slashing/UnjailPage.jsx +++ b/frontend/src/pages/slashing/UnjailPage.jsx @@ -18,6 +18,7 @@ import { resetTxHash, removeFeegrant as removeFeegrantState, setFeegrant as setFeegrantState, + getICNSName, } from "../../features/common/commonSlice"; import { txUnjail } from "../../features/slashing/slashingSlice"; import TextField from "@mui/material/TextField"; @@ -67,6 +68,7 @@ export default function Unjail() { (state) => state.common.feegrant?.[selectedNetwork] || {} ); const grantsToMe = useSelector((state) => state.authz.grantsToMe?.[chainID]); + const icnsNames = useSelector((state) => state.common.icnsNames); const [isNoAuthzs, setNoAuthzs] = useState(false); const [authzGrants, setAuthzGrants] = useState(); @@ -153,14 +155,23 @@ export default function Unjail() { useEffect(() => { const currentChainGrants = getFeegrant()?.[selectedNetwork]; - dispatch( - setFeegrantState({ - grants: currentChainGrants, - chainName: selectedNetwork.toLowerCase(), - }) - ); + dispatch(setFeegrantState({ + grants: currentChainGrants, + chainName: selectedNetwork.toLowerCase() + })); }, [selectedNetwork]); + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + return ( {authzGrants.map((granter, index) => ( - {granter} + {fetchName(granter) || granter} ))} diff --git a/frontend/src/pages/staking/overview/StakingGranter.js b/frontend/src/pages/staking/overview/StakingGranter.js index 4ca9d066d..a04668d1b 100644 --- a/frontend/src/pages/staking/overview/StakingGranter.js +++ b/frontend/src/pages/staking/overview/StakingGranter.js @@ -4,6 +4,7 @@ import { Button, Grid, CircularProgress, + Chip, Tooltip, } from "@mui/material"; import AuthzDelegations from "./AuthzDelegations"; @@ -18,10 +19,13 @@ import { getBalances } from "../../../features/bank/bankSlice"; import { parseBalance } from "../../../utils/denom"; import { DialogDelegate } from "../../../components/DialogDelegate"; import { authzExecHelper } from "../../../features/authz/authzSlice"; -import { setError } from "../../../features/common/commonSlice"; +import { getICNSName, setError } from "../../../features/common/commonSlice"; import { DialogUndelegate } from "../../../components/DialogUndelegate"; import { DialogRedelegate } from "../../../components/DialogRedelegate"; import PropTypes from "prop-types"; +import NameAddress from "../../../components/common/NameAddress"; +import ContentCopyOutlined from "@mui/icons-material/ContentCopyOutlined"; +import { copyToClipboard } from "../../../utils/clipboard"; export default function StakingGranter(props) { const { @@ -57,6 +61,7 @@ export default function StakingGranter(props) { const txStatus = useSelector((state) => state.staking.chains[chainID].tx); const authzExecTx = useSelector((state) => state.authz.execTx); const balances = useSelector((state) => state.bank.balances); + const icnsNames = useSelector((state) => state.common.icnsNames); const nameToChainIDs = useSelector((state) => state.wallet.nameToChainIDs); let chainName; @@ -295,6 +300,17 @@ export default function StakingGranter(props) { } }, [authzExecTx]); + const fetchName = (address) => { + if (!icnsNames?.[address]) { + dispatch( + getICNSName({ + address: address, + }) + ); + } + return icnsNames?.[address]?.name; + }; + return ( <> - Granter: {granter} + Granter:{" "} + + } + size="small" + deleteIcon={} + onDelete={() => { + copyToClipboard(granter, dispatch); + }} + />