diff --git a/client/src/pages/Wallet.tsx b/client/src/pages/Wallet.tsx index cebc58d..2d51390 100644 --- a/client/src/pages/Wallet.tsx +++ b/client/src/pages/Wallet.tsx @@ -18,6 +18,24 @@ import { type RootState } from '../state/rootReducer' import { type WalletState } from '../state/modules/wallet/reducers' import { Navigate } from 'react-router' +const switchDestinationType = (prev: string) => { + switch (prev) { + case "address": + return "phone" + case "phone": + return "tgid" + case "tgid": + default: + return "address" + } +} + +enum DestinationType { + "address" = "crypto address", + "phone" = "phone number", + "tgid" = "telegram id" +} + const Wallet = (): React.JSX.Element => { // const history = useHistory() const dispatch = useDispatch() @@ -25,11 +43,10 @@ const Wallet = (): React.JSX.Element => { const address = Object.keys(wallet).find(e => apis.web3.isValidAddress(e)) const balance = useSelector(state => state.balance[address ?? '']?.balance || '0') - const [to, setTo] = useState('') const [amount, setAmount] = useState('') const [sendModalVisible, setSendModalVisible] = useState(false) - const [phone, setPhone] = useState('') - const [isAddressInput, setIsAddressInput] = useState(false) + const [destinationType, setDestinationType] = useState<"address" | "phone" | "tgid">("address") + const [destination, setDestination] = useState("") const [isSending, setIsSending] = useState(false) const [showFullAddress, setShowFullAddress] = useState(false) @@ -82,26 +99,34 @@ const Wallet = (): React.JSX.Element => { toast.error('Amount exceeds balance') return } - let dest = to - if (isAddressInput) { - if (!(dest?.startsWith('0x')) || !apis.web3.isValidAddress(dest)) { - toast.error('Invalid address') + + let dest = destination + + switch (destinationType) { + case "address": + if (!(dest?.startsWith('0x')) || !apis.web3.isValidAddress(dest)) { + toast.error('Invalid address') + } return - } - } else { - try { - const signature = apis.web3.signWithNonce(phone, pk) - dest = await apis.server.lookup({ destPhone: phone, address, signature }) - if (!apis.web3.isValidAddress(dest)) { - toast.error(`Cannot find recipient with phone number ${phone}`) + + case "tgid": + dest = "tg:" + dest + + case "phone": + try { + const signature = apis.web3.signWithNonce(destination, pk) + dest = await apis.server.lookup({ destPhone: dest, address, signature }) + if (!apis.web3.isValidAddress(dest)) { + toast.error(`Cannot find recipient with ${DestinationType[destinationType]} ${destination}`) + return + } + } catch (ex) { + console.error(ex) + toast.error(`Error in looking up recipient: ${processError(ex)}`) return } - } catch (ex) { - console.error(ex) - toast.error(`Error in looking up recipient: ${processError(ex)}`) - return - } } + toast.info('Submitting transaction...') try { apis.web3.changeAccount(pk) @@ -159,21 +184,31 @@ const Wallet = (): React.JSX.Element => { - {!isAddressInput && + {destinationType === "phone" ? ( { setPhone(e ?? '') }} - />} - {isAddressInput && + value={destination} + onChange={e => setDestination(e ?? "")} + /> + ) : ( { setTo(value) }} - placeholder='0x1234abcde...' - $width='100%' value={to} $margin='16px' style={{ fontSize: 10, flex: 1 }} - />} - { setIsAddressInput(!isAddressInput) }}>use {isAddressInput ? 'phone number' : 'crypto address'} + onChange={e => setDestination(e.target.value)} + placeholder={destinationType === "address" ? '0x1234abcde...' : "telegramid"} + $width='100%' + value={destination} + $margin='16px' + style={{ fontSize: 10, flex: 1 }} + /> + )} + setDestinationType(switchDestinationType)} + > + switch to {DestinationType[switchDestinationType(destinationType)]} + diff --git a/server/routes/index.ts b/server/routes/index.ts index 80a0a5a..82e712c 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -169,16 +169,21 @@ router.post('/restore-verify', partialReqCheck, async (req, res) => { // allows an existing user to lookup another user's address by their phone number, iff the phone number exists and the target user does not choose to hide its phone-address mapping (under `hide` parameter in settings) router.post('/lookup', async (req, res) => { const { address, signature, destPhone } = req.body + if (!address || !signature || !destPhone) { return res.status(StatusCodes.BAD_REQUEST).json({ error: 'require address, signature, destPhone' }) } + if (!utils.isValidAddress(address)) { return res.status(StatusCodes.BAD_REQUEST).json({ error: 'invalid address' }) } - const { isValid, userHandle } = parseUserHandle(destPhone) + + const { isValid, userHandle, userType } = parseUserHandle(destPhone) + if (!isValid) { - return res.status(StatusCodes.BAD_REQUEST).json({ error: 'bad phone number' }) + return res.status(StatusCodes.BAD_REQUEST).json({ error: userType === UserType.Phone ? 'bad phone number' : 'bad telegram id'}) } + const message = `${userHandle} ${Math.floor(Date.now() / (config.defaultSignatureValidDuration)) * config.defaultSignatureValidDuration}` // console.log(message, signature) const expectedAddress = utils.recover(message, signature)