From 3989ac2e7ef13a519a12634e80af98891f030ac8 Mon Sep 17 00:00:00 2001 From: brusher_ru Date: Thu, 19 Sep 2024 23:21:31 -0300 Subject: [PATCH 1/5] chore: fix linter error --- src/components/EditAccountModal.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/EditAccountModal.tsx b/src/components/EditAccountModal.tsx index 182cf51..d8cdda0 100644 --- a/src/components/EditAccountModal.tsx +++ b/src/components/EditAccountModal.tsx @@ -347,7 +347,8 @@ function EditAccountModal({ isSubmitted={isSubmitted} /> - Any changes to the parameters below will alter the account address. + Any changes to the parameters below will alter the account + address.
Proceed at your own risk.
From 201480fce6013bb366fce714e1735e66cfab9705 Mon Sep 17 00:00:00 2001 From: brusher_ru Date: Thu, 19 Sep 2024 23:21:47 -0300 Subject: [PATCH 2/5] feat: implement message signing --- src/components/KeyManager.tsx | 145 ++++++++++++++-------- src/components/SignMessageModal.tsx | 178 ++++++++++++++++++++++++++++ src/hooks/useSigning.ts | 31 +++++ src/hooks/useTxMethods.ts | 11 +- src/types/message.ts | 14 +++ src/utils/constants.ts | 2 + 6 files changed, 321 insertions(+), 60 deletions(-) create mode 100644 src/components/SignMessageModal.tsx create mode 100644 src/hooks/useSigning.ts create mode 100644 src/types/message.ts diff --git a/src/components/KeyManager.tsx b/src/components/KeyManager.tsx index d87bd67..ec31c2e 100644 --- a/src/components/KeyManager.tsx +++ b/src/components/KeyManager.tsx @@ -31,6 +31,7 @@ import { IconKey, IconPlus, IconTrash, + IconWritingSign, } from '@tabler/icons-react'; import useConfirmation from '../hooks/useConfirmation'; @@ -64,6 +65,7 @@ import ImportKeyFromLedgerModal from './ImportKeyFromLedgerModal'; import ImportKeyPairModal from './ImportKeyPairModal'; import RenameKeyModal from './RenameKeyModal'; import RevealSecretKeyModal from './RevealSecretKeyModal'; +import SignMessageModal from './SignMessageModal'; type KeyManagerProps = { isOpen: boolean; @@ -117,9 +119,11 @@ function KeyManager({ isOpen, onClose }: KeyManagerProps): JSX.Element { const createAccountModal = useDisclosure(); const importAccountModal = useDisclosure(); const editAccountModal = useDisclosure(); + const signMessageModal = useDisclosure(); const [renameKeyIdx, setRenameKeyIdx] = useState(0); const [editAccountIdx, setEditAccountIdx] = useState(0); + const [signMessageByKeyIndex, setSignMessageByKeyIndex] = useState(0); const closeHandler = () => { onClose(); @@ -241,59 +245,91 @@ function KeyManager({ isOpen, onClose }: KeyManagerProps): JSX.Element { backgroundColor="blackAlpha.300" borderRadius="md" > - {key.type === KeyPairType.Software ? ( - - ) : ( - - - Hardware - - )} - + + {key.type === KeyPairType.Software ? ( + <> + + + + ) : ( + + + Hardware + + )} + + {key.displayName} - onRenameKey(idx)} - icon={} - variant="whiteOutline" - borderWidth={1} - size="xs" - /> - onDeleteKey(idx)} - icon={} - variant="dangerOutline" - borderWidth={1} - size="xs" - /> + + onRenameKey(idx)} + icon={} + variant="whiteOutline" + borderWidth={1} + size="xs" + mr={1} + /> + onDeleteKey(idx)} + icon={} + variant="dangerOutline" + borderWidth={1} + size="xs" + mr={1} + /> + Public Key @@ -483,6 +519,11 @@ function KeyManager({ isOpen, onClose }: KeyManagerProps): JSX.Element { isOpen={editAccountModal.isOpen} onClose={editAccountModal.onClose} /> + diff --git a/src/components/SignMessageModal.tsx b/src/components/SignMessageModal.tsx new file mode 100644 index 0000000..c0187ab --- /dev/null +++ b/src/components/SignMessageModal.tsx @@ -0,0 +1,178 @@ +import fileDownload from 'js-file-download'; +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; + +import { + Button, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Spacer, + Text, + Textarea, +} from '@chakra-ui/react'; + +import useCopy from '../hooks/useCopy'; +import { useSignMessage } from '../hooks/useSigning'; +import usePassword from '../store/usePassword'; +import useWallet from '../store/useWallet'; +import { SignedMessage } from '../types/message'; +import { KeyPairType } from '../types/wallet'; +import { SIGNED_MESSAGE_PREFIX } from '../utils/constants'; +import { toHexString } from '../utils/hexString'; + +type SignMessageModalProps = { + keyIndex: number; + isOpen: boolean; + onClose: () => void; +}; + +function SignMessageModal({ + keyIndex, + isOpen, + onClose, +}: SignMessageModalProps): JSX.Element | null { + const { wallet } = useWallet(); + const signMessage = useSignMessage(); + const { withPassword } = usePassword(); + const { register, reset, handleSubmit } = useForm<{ message: string }>(); + const [signResult, setSignResult] = useState(''); + const { isCopied, onCopy } = useCopy(); + + const keys = wallet?.keychain ?? []; + const key = keys[keyIndex]; + if (!key) return null; + + const close = () => { + setSignResult(''); + reset(); + onClose(); + }; + + const download = () => + fileDownload(signResult, `signed-message.json`, 'plain/text'); + + const submit = handleSubmit(async ({ message }) => { + const result = await withPassword( + async (password) => { + if (key.type === KeyPairType.Hardware) { + // Sign using Ledger device + throw new Error('Hardware wallet is not supported yet'); + } + // Sign using local key + return JSON.stringify( + { + publicKey: key.publicKey, + text: message, + signature: toHexString( + await signMessage( + `${SIGNED_MESSAGE_PREFIX}${message}`, + key.publicKey, + password + ) + ), + } satisfies SignedMessage, + null, + 2 + ); + }, + 'Sign message', + <> + Please enter your password to sign the message using key " + {key.displayName}"{' '} + + ({key.publicKey}) + + + ); + if (result) { + setSignResult(result); + } else { + setSignResult(''); + } + }); + + return ( + + + + + Sign message + {signResult ? ( + <> + + + Here is your message and the signature. You can copy them to the + clipboard and share with another party. + +