Skip to content
Open
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
17 changes: 14 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"@types/jest": "^29.5.0",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"antd": "^4.24.15",
"axios": "^1.7.9",
"axios": "^1.12.0",
"bignumber.js": "^9.1.2",
"currency-symbol-map": "^5.1.0",
"currency.js": "^2.0.4",
Expand Down Expand Up @@ -113,7 +113,7 @@
"@babel/plugin-transform-modules-commonjs": "^7.25.9",
"@babel/plugin-transform-runtime": "^7.25.9",
"@babel/preset-env": "^7.26.0",
"@playwright/test": "^1.49.1",
"@playwright/test": "^1.55.1",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
Expand Down Expand Up @@ -172,5 +172,16 @@
"url": "https://github.com/syscoin/pali_wallet/issues"
},
"homepage": "https://github.com/syscoin/pali_wallet#readme",
"author": "Syscoin"
"author": "Syscoin",
"resolutions": {
"node-fetch": "^2.7.0",
"axios": "^1.12.0",
"node-forge": "^1.3.2",
"valibot": "^1.2.0",
"glob": "^10.5.0",
"cookie": "^0.7.0",
"tmp": "^0.2.4",
"jws": "^4.0.1",
"js-yaml": "^4.1.1"
}
}
4 changes: 2 additions & 2 deletions source/assets/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,9 @@
"addressType": "Adresstyp"
},
"components": {
"newPassword": "Neues Passwort (min. 8 Zeichen)",
"newPassword": "Neues Passwort (min. 12 Zeichen)",
"confirmPassword": "Passwort bestätigen",
"atLeast": "Mindestens 8 Zeichen, 1 Kleinbuchstabe und 1 Ziffer.",
"atLeast": "Mindestens 12 Zeichen, 1 Großbuchstabe, 1 Kleinbuchstabe, 1 Ziffer und 1 Sonderzeichen (!@#$%^&*...).",
"doNotForget": "Vergessen Sie nicht, Ihr Passwort zu speichern. Sie benötigen dieses Passwort, um Ihre Wallet zu entsperren.",
"feeRateLabel": "Gebührenrate ({{currency}}/Byte)",
"gasPriceLabel": "Gaspreis (gwei)",
Expand Down
4 changes: 2 additions & 2 deletions source/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
"generate": "Generate"
},
"components": {
"newPassword": "New password (min 8 chars)",
"newPassword": "New password (min 12 chars)",
"confirmPassword": "Confirm password",
"atLeast": "At least 8 characters, 1 lower-case and 1 numeral.",
"atLeast": "At least 12 characters, 1 uppercase, 1 lowercase, 1 number, and 1 special character (!@#$%^&*...).",
"doNotForget": "Do not forget to save your password. You will need this password to unlock your wallet.",
"feeRateLabel": "Fee Rate ({{currency}}/byte)",
"gasPriceLabel": "Gas Price (gwei)",
Expand Down
4 changes: 2 additions & 2 deletions source/assets/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
"generate": "Generar"
},
"components": {
"newPassword": "Nueva contraseña (mín. 8 caracteres)",
"newPassword": "Nueva contraseña (mín. 12 caracteres)",
"confirmPassword": "Confirmar contraseña",
"atLeast": "Al menos 8 caracteres, 1 en minúscula y 1 numérico.",
"atLeast": "Al menos 12 caracteres, 1 mayúscula, 1 minúscula, 1 número y 1 carácter especial (!@#$%^&*...).",
"doNotForget": "No olvides guardar tu contraseña. Necesitarás esta contraseña para desbloquear tu billetera.",
"feeRateLabel": "Tarifa ({{currency}}/byte)",
"gasPriceLabel": "Precio del Gas (gwei)",
Expand Down
4 changes: 2 additions & 2 deletions source/assets/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,9 @@
"addressType": "Type d'adresse"
},
"components": {
"newPassword": "Nouveau mot de passe (8 caractères min.)",
"newPassword": "Nouveau mot de passe (12 caractères min.)",
"confirmPassword": "Confirmer le mot de passe",
"atLeast": "Au moins 8 caractères, 1 en minuscule et 1 chiffre.",
"atLeast": "Au moins 12 caractères, 1 majuscule, 1 minuscule, 1 chiffre et 1 caractère spécial (!@#$%^&*...).",
"doNotForget": "N'oubliez pas de sauvegarder votre mot de passe. Vous aurez besoin de ce mot de passe pour déverrouiller votre portefeuille.",
"feeRateLabel": "Tarif de frais ({{currency}}/byte)",
"gasPriceLabel": "Prix du gaz (gwei)",
Expand Down
4 changes: 2 additions & 2 deletions source/assets/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,9 @@
"addressType": "アドレスタイプ"
},
"components": {
"newPassword": "新しいパスワード(最低8文字)",
"newPassword": "新しいパスワード(最低12文字)",
"confirmPassword": "パスワードを確認",
"atLeast": "最低8文字、小文字1文字、数字1文字。",
"atLeast": "最低12文字、大文字1文字、小文字1文字、数字1文字、特殊文字1文字(!@#$%^&*...)。",
"doNotForget": "パスワードを保存することを忘れないでください。ウォレットのロックを解除するには、このパスワードが必要です。",
"feeRateLabel": "手数料レート ({{currency}}/byte)",
"gasPriceLabel": "ガス価格 (gwei)",
Expand Down
4 changes: 2 additions & 2 deletions source/assets/locales/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,9 @@
"addressType": "주소 유형"
},
"components": {
"newPassword": "새 비밀번호 (최소 8자)",
"newPassword": "새 비밀번호 (최소 12자)",
"confirmPassword": "비밀번호 확인",
"atLeast": "최소 8자, 소문자 1개, 숫자 1개.",
"atLeast": "최소 12자, 대문자 1개, 소문자 1개, 숫자 1개, 특수문자 1개 (!@#$%^&*...).",
"doNotForget": "비밀번호를 저장하는 것을 잊지 마세요. 지갑을 잠금 해제하려면 이 비밀번호가 필요합니다.",
"feeRateLabel": "수수료율 ({{currency}}/바이트)",
"gasPriceLabel": "가스 가격 (gwei)",
Expand Down
4 changes: 2 additions & 2 deletions source/assets/locales/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
"generate": "Gerar"
},
"components": {
"newPassword": "Nova senha (mín. 8 caracteres)",
"newPassword": "Nova senha (mín. 12 caracteres)",
"confirmPassword": "Confirmar senha",
"atLeast": "Pelo menos 8 caracteres, 1 minúscula e 1 numeral.",
"atLeast": "Pelo menos 12 caracteres, 1 maiúscula, 1 minúscula, 1 número e 1 caractere especial (!@#$%^&*...).",
"doNotForget": "Não esqueça de salvar sua senha. Você precisará desta senha para desbloquear sua carteira.",
"feeRateLabel": "Taxa ({{currency}}/byte)",
"gasPriceLabel": "Preço do gás (gwei)",
Expand Down
4 changes: 2 additions & 2 deletions source/assets/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,9 @@
"addressType": "Тип адреса"
},
"components": {
"newPassword": "Новый пароль (мин. 8 символов)",
"newPassword": "Новый пароль (мин. 12 символов)",
"confirmPassword": "Подтвердите пароль",
"atLeast": "Минимум 8 символов, 1 строчная буква и 1 цифра.",
"atLeast": "Минимум 12 символов, 1 заглавная буква, 1 строчная буква, 1 цифра и 1 специальный символ (!@#$%^&*...).",
"doNotForget": "Не забудьте сохранить ваш пароль. Он понадобится для разблокировки кошелька.",
"feeRateLabel": "Размер комиссии ({{currency}}/байт)",
"gasPriceLabel": "Цена газа (gwei)",
Expand Down
4 changes: 2 additions & 2 deletions source/assets/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,9 @@
"addressType": "地址类型"
},
"components": {
"newPassword": "新密码(至少8个字符)",
"newPassword": "新密码(至少12个字符)",
"confirmPassword": "确认密码",
"atLeast": "至少8个字符,1个小写字母和1个数字。",
"atLeast": "至少12个字符,1个大写字母,1个小写字母,1个数字和1个特殊字符(!@#$%^&*...)。",
"doNotForget": "不要忘记保存您的密码。您需要此密码来解锁您的钱包。",
"feeRateLabel": "费率 ({{currency}}/字节)",
"gasPriceLabel": "燃气价格 (gwei)",
Expand Down
82 changes: 10 additions & 72 deletions source/components/Input/SeedPhraseDisplay.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
import { isEmpty } from 'lodash';
import React, { useState, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { BiCopy } from 'react-icons/bi';
import React, { useState } from 'react';

interface ISeedPhraseDisplayProps {
/**
* Additional CSS classes
*/
className?: string;

/**
* Whether the phrase was successfully copied (for UI feedback)
*/
copied?: boolean;

/**
* Whether to use the TextArea style (ForgetWallet) or word-by-word style (Phrase)
*/
Expand All @@ -24,11 +17,6 @@ interface ISeedPhraseDisplayProps {
*/
isEnabled?: boolean;

/**
* Callback when copy is clicked
*/
onCopy: (phrase: string) => void;

/**
* Placeholder text when seed phrase is not available
*/
Expand All @@ -49,34 +37,24 @@ export const SeedPhraseDisplay: React.FC<ISeedPhraseDisplayProps> = ({
seedPhrase,
isEnabled = true,
showEyeToggle = true,
onCopy,
copied = false,
placeholder,
className = '',
displayMode = 'words',
}) => {
const { t } = useTranslation();
const [visible, setVisible] = useState<boolean>(false);

const mockedPhrase =
'**** ******* ****** ****** ****** ******** *** ***** ****** ***** *****';
const displayPhrase = isEmpty(seedPhrase)
? placeholder || mockedPhrase
: seedPhrase;
const canCopy = isEnabled && seedPhrase && !isEmpty(seedPhrase);

const handleCopy = useCallback(() => {
if (canCopy && seedPhrase) {
onCopy(seedPhrase);
}
}, [canCopy, seedPhrase, onCopy]);

const toggleVisible = useCallback(() => {
const toggleVisible = () => {
setVisible((prev) => !prev);
}, []);
};

if (displayMode === 'textarea') {
// ForgetWallet style - TextArea with copy icon
// ForgetWallet style - TextArea (view-only, no copy to prevent clipboard attacks)
return (
<div className={`w-full md:max-w-md ${className}`}>
<div className="relative">
Expand All @@ -87,13 +65,13 @@ export const SeedPhraseDisplay: React.FC<ISeedPhraseDisplayProps> = ({
: 'opacity-100 bg-fields-input-primary'
} ${
showEyeToggle && !visible ? 'filter blur-sm' : ''
} p-2 pl-4 pr-12 w-full h-[90px] text-brand-graylight text-sm border border-border-default focus:border-fields-input-borderfocus rounded-[10px] outline-none resize-none`}
placeholder={!isEnabled ? t('settings.enterYourPassword') : ''}
} p-2 pl-4 pr-12 w-full h-[90px] text-brand-graylight text-sm border border-border-default focus:border-fields-input-borderfocus rounded-[10px] outline-none resize-none select-none`}
placeholder={!isEnabled ? 'Enter your password' : ''}
value={displayPhrase}
readOnly={true}
/>

{/* Controls container - eye toggle and copy button */}
{/* Controls container - eye toggle only (copy removed for security) */}
<div className="absolute top-2 right-2 flex items-center">
{/* Eye toggle - only show when showEyeToggle is true and seed is available */}
{showEyeToggle && seedPhrase && (
Expand All @@ -118,39 +96,19 @@ export const SeedPhraseDisplay: React.FC<ISeedPhraseDisplayProps> = ({
)}
</button>
)}

{/* Copy button - only show when enabled and seed is available */}
{canCopy && (
<button
type="button"
onClick={handleCopy}
className="p-1 rounded hover:bg-gray-700 transition-colors duration-200"
title={t('buttons.copy')}
>
{copied ? (
<img
className="w-[16px] max-w-none"
src="/assets/all_assets/successIcon.svg"
alt="Copied"
/>
) : (
<BiCopy className="w-4 h-4 text-brand-graylight hover:text-white" />
)}
</button>
)}
</div>
</div>
</div>
);
}

// Phrase style - Word-by-word with eye toggle and copy button inside the box
// Phrase style - Word-by-word (view-only, no copy to prevent clipboard attacks)
return (
<div className={`flex flex-col ${className}`}>
{/* Seed phrase display box */}
<div className="relative flex gap-3 items-start p-4 w-full max-w-[22rem] border border-border-default rounded-[10px] bg-brand-blue800">
<div
className={`flex flex-wrap flex-row gap-2 bg-brand-blue800 flex-1 ${
className={`flex flex-wrap flex-row gap-2 bg-brand-blue800 flex-1 select-none ${
visible || !showEyeToggle ? '' : 'filter blur-sm'
}`}
>
Expand All @@ -161,7 +119,7 @@ export const SeedPhraseDisplay: React.FC<ISeedPhraseDisplayProps> = ({
))}
</div>

{/* Controls container - eye toggle and copy button */}
{/* Controls container - eye toggle only (copy removed for security) */}
<div className="flex items-center bg-brand-blue800">
{/* Eye toggle - only show when enabled and showEyeToggle is true */}
{showEyeToggle && seedPhrase && (
Expand All @@ -186,26 +144,6 @@ export const SeedPhraseDisplay: React.FC<ISeedPhraseDisplayProps> = ({
)}
</button>
)}

{/* Copy button inside the box */}
{canCopy && (
<button
type="button"
onClick={handleCopy}
className="p-1 rounded hover:bg-gray-700 transition-colors duration-200"
title={t('buttons.copy')}
>
{copied ? (
<img
className="w-[18px] h-[18px] max-w-none"
src="/assets/all_assets/successIcon.svg"
alt="Copied"
/>
) : (
<BiCopy className="w-[18px] h-[18px] text-brand-graylight hover:text-white" />
)}
</button>
)}
</div>
</div>
</div>
Expand Down
4 changes: 3 additions & 1 deletion source/components/PasswordForm/PasswordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ export const PasswordForm: React.FC<IPasswordForm> = ({ onSubmit }) => {
message: '',
},
{
pattern: /^(?=.*[a-z])(?=.*[0-9])(?=.{8,})/,
// Strong password requirements: 12+ chars, uppercase, lowercase, number, special char
pattern:
/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]).{12,}$/,
message: '',
},
]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import { useTransactionsListConfig, useUtils } from 'hooks/index';
import { useController } from 'hooks/useController';
import type { IEvmTransactionResponse } from 'scripts/Background/controllers/transactions/types';
import { RootState } from 'state/store';
import { selectActiveAccount } from 'state/vault/selectors';
import {
selectActiveAccount,
selectValidEnsCache,
} from 'state/vault/selectors';
import { IDecodedTx } from 'types/transactions';
import { formatMethodName } from 'utils/commonMethodSignatures';
import { camelCaseToText } from 'utils/index';
Expand All @@ -37,9 +40,8 @@ export const EvmTransactionDetailsEnhanced = ({
tx: IEvmTransactionResponse;
}) => {
const { controllerEmitter } = useController();
const ensCache = useSelector(
(state: RootState) => state.vaultGlobal.ensCache
);
// Use valid (non-expired) ENS cache for security
const ensCache = useSelector(selectValidEnsCache);
const {
activeNetwork: { chainId, currency, apiUrl },
} = useSelector((state: RootState) => state.vault);
Expand Down
14 changes: 0 additions & 14 deletions source/pages/SeedConfirm/CreatePhrase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export const CreatePhrase = ({ password }: { password: string }) => {
const [wordCount, setWordCount] = useState<number>(12);
const { t } = useTranslation();

const [copied, setCopied] = useState<boolean>(false);
const [isTermsConfirmed, setIsTermsConfirmed] = useState<boolean>(false);

const navigate = useNavigate();
Expand All @@ -25,17 +24,6 @@ export const CreatePhrase = ({ password }: { password: string }) => {
);
}, [controllerEmitter, wordCount]);

const handleCopyToClipboard = useCallback(async (seedPhrase: string) => {
try {
await navigator.clipboard.writeText(seedPhrase);
setCopied(true);
// Reset copied state after a delay
setTimeout(() => setCopied(false), 2000);
} catch (error) {
console.error('Failed to copy seed:', error);
}
}, []);

const handleNext = useCallback(() => {
if (isTermsConfirmed) {
navigate('/phrase', {
Expand Down Expand Up @@ -73,8 +61,6 @@ export const CreatePhrase = ({ password }: { password: string }) => {
seedPhrase={seed}
isEnabled={true}
showEyeToggle={true}
onCopy={handleCopyToClipboard}
copied={copied}
displayMode="words"
className="w-[17.5rem] max-w-[17.5rem]"
/>
Expand Down
Loading
Loading