Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 63 additions & 28 deletions client/src/pages/Wallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,35 @@ 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()
const wallet = useSelector<RootState, WalletState>(state => state.wallet || {})
const address = Object.keys(wallet).find(e => apis.web3.isValidAddress(e))
const balance = useSelector<RootState, string>(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)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -159,21 +184,31 @@ const Wallet = (): React.JSX.Element => {
<Row style={{ position: 'relative' }}>

<Label>To</Label>
{!isAddressInput &&
{destinationType === "phone" ? (
<PhoneInput
margin='16px'
inputComponent={Input}
defaultCountry='US'
placeholder='phone number of recipient'
value={phone} onChange={e => { setPhone(e ?? '') }}
/>}
{isAddressInput &&
value={destination}
onChange={e => setDestination(e ?? "")}
/>
) : (
<Input
onChange={({ target: { value } }) => { setTo(value) }}
placeholder='0x1234abcde...'
$width='100%' value={to} $margin='16px' style={{ fontSize: 10, flex: 1 }}
/>}
<FloatingSwitch href='#' onClick={() => { setIsAddressInput(!isAddressInput) }}>use {isAddressInput ? 'phone number' : 'crypto address'}</FloatingSwitch>
onChange={e => setDestination(e.target.value)}
placeholder={destinationType === "address" ? '0x1234abcde...' : "telegramid"}
$width='100%'
value={destination}
$margin='16px'
style={{ fontSize: 10, flex: 1 }}
/>
)}
<FloatingSwitch
href="#"
onClick={() => setDestinationType(switchDestinationType)}
>
switch to {DestinationType[switchDestinationType(destinationType)]}
</FloatingSwitch>
</Row>
<Row>
<Label>Amount</Label>
Expand Down
9 changes: 7 additions & 2 deletions server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down