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'}