diff --git a/infra/rooch-portal/package.json b/infra/rooch-portal/package.json index 8d6f8455a9..16dd41e01d 100644 --- a/infra/rooch-portal/package.json +++ b/infra/rooch-portal/package.json @@ -34,6 +34,7 @@ "@roochnetwork/rooch-sdk-kit": "0.1.8", "@tanstack/react-query": "^5.0.0", "@tanstack/react-table": "^8.11.8", + "bitcoinjs-lib": "^6.1.6", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "cmdk": "^0.2.1", diff --git a/infra/rooch-portal/src/components/profile-info.tsx b/infra/rooch-portal/src/components/profile-info.tsx index a778d0207a..d320ea6a8a 100644 --- a/infra/rooch-portal/src/components/profile-info.tsx +++ b/infra/rooch-portal/src/components/profile-info.tsx @@ -16,7 +16,7 @@ export const ProfileInfo = () => {
{account ? ( - + ) : ( )} diff --git a/infra/rooch-portal/src/pages/assets/assets-details/tabs/coin/assets-coin.tsx b/infra/rooch-portal/src/pages/assets/assets-details/tabs/coin/assets-coin.tsx index 6117944d74..58fcb15a0d 100644 --- a/infra/rooch-portal/src/pages/assets/assets-details/tabs/coin/assets-coin.tsx +++ b/infra/rooch-portal/src/pages/assets/assets-details/tabs/coin/assets-coin.tsx @@ -26,6 +26,7 @@ import CustomPagination from '@/components/custom-pagination.tsx' import { formatCoin } from '@/utils/format.ts' import { useToast } from '@/components/ui/use-toast' import { ToastAction } from '@/components/ui/toast' +import { isValidBitcoinAddress } from '@/utils/addressValidation' // Import the validation function export const AssetsCoin = () => { const account = useCurrentAccount() @@ -150,18 +151,42 @@ export const AssetsCoin = () => { } const handleTransferCoin = async () => { + try { + if (!sessionKey || (await sessionKey.isExpired())) { + toast({ + title: 'Session Key Expired', + description: 'The session key was expired, please authorize the latest one.', + action: Close, + }) + return + } + } catch (error) { + console.error('Failed to check session key expiration', error) + toast({ + title: 'Error', + description: 'An error occurred while checking the session key expiration.', + action: Close, + }) + return + } + if (recipient === '' || amount === '0' || !selectedCoin || error) { setError('Please enter a valid recipient and amount.') return } + if (!isValidBitcoinAddress(recipient)) { + setError('Invalid recipient address.') + return + } + const amountNumber = Math.floor(Number(amount) * 10 ** selectedCoin.decimals) setTransferLoading(true) try { await transferCoin({ - account: sessionKey!, + account: sessionKey, recipient: recipient, amount: amountNumber, coinType: selectedCoin.coin_type, diff --git a/infra/rooch-portal/src/pages/assets/profile-card/profile-card.tsx b/infra/rooch-portal/src/pages/assets/profile-card/profile-card.tsx index 590ac50faf..532ab4a5c8 100644 --- a/infra/rooch-portal/src/pages/assets/profile-card/profile-card.tsx +++ b/infra/rooch-portal/src/pages/assets/profile-card/profile-card.tsx @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import toast from 'react-hot-toast' // import { RotateCcw } from 'lucide-react' - import { Button } from '@/components/ui/button' import { Avatar } from '@/components/ui/avatar' import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card' @@ -10,7 +9,6 @@ import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/componen import { formatAddress } from '@/utils/format' import { useCurrentAccount } from '@roochnetwork/rooch-sdk-kit' // import { useNavigate } from 'react-router-dom' - import Jazzicon, { jsNumberForAddress } from 'react-jazzicon' export const ProfileCard = () => { @@ -82,7 +80,7 @@ export const ProfileCard = () => {
{account ? ( - + ) : ( )} diff --git a/infra/rooch-portal/src/pages/settings/components/manage-sessions.tsx b/infra/rooch-portal/src/pages/settings/components/manage-sessions.tsx index 592b81f511..cdea6b9941 100644 --- a/infra/rooch-portal/src/pages/settings/components/manage-sessions.tsx +++ b/infra/rooch-portal/src/pages/settings/components/manage-sessions.tsx @@ -57,6 +57,8 @@ export const ManageSessions: React.FC = () => { authKey: authKey, }) await refetch() + } catch (error) { + console.error(error) } finally { setLoading(null) } diff --git a/infra/rooch-portal/src/utils/addressValidation.ts b/infra/rooch-portal/src/utils/addressValidation.ts new file mode 100644 index 0000000000..408e2fcef7 --- /dev/null +++ b/infra/rooch-portal/src/utils/addressValidation.ts @@ -0,0 +1,19 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 +import * as bitcoin from 'bitcoinjs-lib' + +export const isValidBitcoinAddress = (address: string): boolean => { + try { + // Validate mainnet and testnet Bech32 addresses + bitcoin.address.fromBech32(address) + return true + } catch (e) { + try { + // Validate mainnet and testnet base58 addresses + bitcoin.address.fromBech32(address) + return true + } catch (e) { + return false + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 551eaf9d0e..0f724d8238 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -424,6 +424,9 @@ importers: '@tanstack/react-table': specifier: ^8.11.8 version: 8.17.3(react-dom@18.2.0)(react@18.2.0) + bitcoinjs-lib: + specifier: ^6.1.6 + version: 6.1.6 class-variance-authority: specifier: ^0.7.0 version: 0.7.0 @@ -8551,6 +8554,10 @@ packages: dev: false optional: true + /base-x@4.0.0: + resolution: {integrity: sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==} + dev: false + /base58-js@1.0.5: resolution: {integrity: sha512-LkkAPP8Zu+c0SVNRTRVDyMfKVORThX+rCViget00xdgLRrKkClCTz1T7cIrpr69ShwV5XJuuoZvMvJ43yURwkA==} engines: {node: '>= 8'} @@ -8573,6 +8580,23 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + /bip174@2.1.1: + resolution: {integrity: sha512-mdFV5+/v0XyNYXjBS6CQPLo9ekCx4gtKZFnJm5PMto7Fs9hTTDpkkzOB7/FtluRI6JbUUAu+snTYfJRgHLZbZQ==} + engines: {node: '>=8.0.0'} + dev: false + + /bitcoinjs-lib@6.1.6: + resolution: {integrity: sha512-Fk8+Vc+e2rMoDU5gXkW9tD+313rhkm5h6N9HfZxXvYU9LedttVvmXKTgd9k5rsQJjkSfsv6XRM8uhJv94SrvcA==} + engines: {node: '>=8.0.0'} + dependencies: + '@noble/hashes': 1.4.0 + bech32: 2.0.0 + bip174: 2.1.1 + bs58check: 3.0.1 + typeforce: 1.18.0 + varuint-bitcoin: 1.1.2 + dev: false + /bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: @@ -8627,6 +8651,19 @@ packages: node-releases: 2.0.14 update-browserslist-db: 1.0.16(browserslist@4.23.0) + /bs58@5.0.0: + resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} + dependencies: + base-x: 4.0.0 + dev: false + + /bs58check@3.0.1: + resolution: {integrity: sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==} + dependencies: + '@noble/hashes': 1.4.0 + bs58: 5.0.0 + dev: false + /bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: @@ -18213,6 +18250,10 @@ packages: typescript: 5.4.5 dev: true + /typeforce@1.18.0: + resolution: {integrity: sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==} + dev: false + /typescript@4.8.4: resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} engines: {node: '>=4.2.0'} @@ -18540,6 +18581,12 @@ packages: sade: 1.8.1 dev: false + /varuint-bitcoin@1.1.2: + resolution: {integrity: sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'}