diff --git a/.gitignore b/.gitignore index 5e489ce..c7fbfeb 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,7 @@ yarn-debug.log* yarn-error.log* # Local Netlify folder -.netlify \ No newline at end of file +.netlify + +# IDE folder +.idea diff --git a/components/dataViews/TransactionInfo.tsx b/components/dataViews/TransactionInfo.tsx index 2abb112..ac01b44 100644 --- a/components/dataViews/TransactionInfo.tsx +++ b/components/dataViews/TransactionInfo.tsx @@ -5,7 +5,7 @@ import { DbTransaction } from "../../types"; import { useAppContext } from "../../context/AppContext"; import HashView from "./HashView"; import StackableContainer from "../layout/StackableContainer"; -import { printableCoins } from "../../lib/displayHelpers"; +import { printableCoins, printableCoin } from "../../lib/displayHelpers"; interface Props { tx: DbTransaction; @@ -16,38 +16,75 @@ const TransactionInfo = (props: Props) => { return ( - {props.tx.msgs && ( - - Amount: - {printableCoins(props.tx.msgs[0].value.amount, state.chain)} - - )} - {props.tx.msgs && ( - - To: - - - - - )} - {props.tx.fee && ( - <> + <> + {(props.tx.msgs || []).map((msg) => + msg.typeUrl === "/cosmos.bank.v1beta1.MsgSend" ? ( + <> + + Amount: + {printableCoins(msg.value.amount, state.chain)} + + + To: + + + + + > + ) : msg.typeUrl === "/cosmos.staking.v1beta1.MsgDelegate" || + msg.typeUrl === "/cosmos.staking.v1beta1.MsgUnDelegate" ? ( + <> + + Amount: + {printableCoin(props.tx.msgs[0].value.amount, state.chain)} + + + Validator Address: + + + + + > + ) : msg.typeUrl === "/cosmos.staking.v1beta1.MsgBeginRedelegate" ? ( + <> + + Amount: + {printableCoin(props.tx.msgs[0].value.amount, state.chain)} + + + Source Validator Address: + + + + + + Destination Validator Address: + + + + + > + ) : null, + )} + {props.tx.fee && ( + <> + + Gas: + {props.tx.fee.gas} + + + Fee: + {printableCoins(props.tx.fee.amount as Coin[], state.chain)} + + > + )} + {props.tx.memo && ( - Gas: - {props.tx.fee.gas} + Memo: + {props.tx.memo} - - Fee: - {printableCoins(props.tx.fee.amount as Coin[], state.chain)} - - > - )} - {props.tx.memo && ( - - Memo: - {props.tx.memo} - - )} + )} + > + + ); +}; + +export default withRouter(DelegationForm); diff --git a/lib/displayHelpers.ts b/lib/displayHelpers.ts index c54a587..872fb17 100644 --- a/lib/displayHelpers.ts +++ b/lib/displayHelpers.ts @@ -126,6 +126,32 @@ const checkAddress = (input: string, chainAddressPrefix: string) => { return null; }; +/** + * Returns an error message for invalid addresses. + * + * Returns null of there is no error. + */ +const checkValidatorAddress = (input: string, chainAddressPrefix: string): string | null => { + if (!input) return "Empty"; + let prefix; + try { + ({ prefix } = fromBech32(input)); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + return error.toString(); + } + + if (prefix !== chainAddressPrefix) { + return `Expected address prefix '${chainAddressPrefix}' but got '${prefix}'`; + } + + if (input.length !== 52) { + return "Invalid address length in validator address. Must be 52 bytes."; + } + + return null; +}; + /** * Returns a link to a transaction in an explorer if an explorer is configured * for transactions. Returns null otherwise. @@ -145,4 +171,5 @@ export { examplePubkey, checkAddress, explorerLinkTx, + checkValidatorAddress, }; diff --git a/pages/multi/[address]/index.tsx b/pages/multi/[address]/index.tsx index 16db4c5..ffdf9b0 100644 --- a/pages/multi/[address]/index.tsx +++ b/pages/multi/[address]/index.tsx @@ -14,6 +14,7 @@ import MultisigMembers from "../../../components/dataViews/MultisigMembers"; import Page from "../../../components/layout/Page"; import StackableContainer from "../../../components/layout/StackableContainer"; import TransactionForm from "../../../components/forms/TransactionForm"; +import DelegationForm from "../../../components/forms/DelegationForm"; function participantPubkeysFromMultisig(multisigPubkey: Pubkey) { return multisigPubkey.value.pubkeys; @@ -27,7 +28,8 @@ function participantAddressesFromMultisig(multisigPubkey: Pubkey, addressPrefix: const multipage = () => { const { state } = useAppContext(); - const [showTxForm, setShowTxForm] = useState(false); + const [showSendTxForm, setShowSendTxForm] = useState(false); + const [showDelegateTxForm, setShowDelegateTxForm] = useState(false); const [holdings, setHoldings] = useState(null); const [multisigAddress, setMultisigAddress] = useState(""); const [accountOnChain, setAccountOnChain] = useState(null); @@ -97,15 +99,25 @@ const multipage = () => { )} - {showTxForm ? ( + {showSendTxForm && ( { - setShowTxForm(false); + setShowSendTxForm(false); }} /> - ) : ( + )} + {showDelegateTxForm && ( + { + setShowDelegateTxForm(false); + }} + /> + )} + {!showSendTxForm && !showDelegateTxForm && ( @@ -120,7 +132,13 @@ const multipage = () => { { - setShowTxForm(true); + setShowSendTxForm(true); + }} + /> + { + setShowDelegateTxForm(true); }} /> @@ -133,10 +151,12 @@ const multipage = () => { display: flex; justify-content: space-between; margin-top: 50px; + flex-direction: column; } .col-1 { flex: 1; - padding-right: 50px; + padding-right: 0; + margin-bottom: 50px; } .col-2 { flex: 1; @@ -147,6 +167,7 @@ const multipage = () => { } p { margin-top: 15px; + max-width: 100%; } .multisig-error p { max-width: 550px;