From 24d09e92161550c16a7b239a5dcc26e314b00095 Mon Sep 17 00:00:00 2001 From: Sergey Date: Mon, 1 Apr 2024 20:22:14 +0200 Subject: [PATCH 1/5] A-1206908284153320: switch to api v2 (#133) * A-1206908284153320: switch to api v2 * fix tests * adjust error in case when fee is known and total not enough * add extra utxo * revert error messaging * add time data * fix: display delegation_id for withdraw tx * fix: add rounding after multiplication * fix height * fix encoding * add lock input --- src/components/basic/Input/Input.css | 1 + .../composed/Balance/Balance.test.js | 4 +- .../CryptoFiatField/CryptoFiatField.js | 10 +- .../CryptoFiatField/CryptoFiatField.test.js | 6 +- .../composed/CurrentStaking/CurrentStaking.js | 5 + .../SendTransaction/SendTransaction.js | 18 +++- .../containers/Wallet/Delegation.test.js | 2 +- .../UseWalletInfo/useBtcWalletInfo.test.js | 2 +- src/hooks/UseWalletInfo/useMlWalletInfo.js | 20 +++- src/hooks/etc/useEffectOnce.js | 10 ++ src/index.js | 1 - src/pages/Dashboard/Dashboard.js | 7 +- src/pages/SendTransaction/SendTransaction.js | 64 +++++++----- src/pages/Staking/Staking.js | 84 ++++++++-------- src/pages/Wallet/Wallet.js | 15 ++- src/services/API/Mintlayer/Mintlayer.js | 43 +++++++- src/services/Crypto/Mintlayer/Mintlayer.js | 4 +- src/services/Database/IndexedDB/IndexedDB.js | 1 - src/utils/Constants/AppInfo/AppInfo.js | 6 +- src/utils/Helpers/ML/ML.js | 39 ++++---- src/utils/Helpers/ML/MLTransaction.js | 97 ++++++++++++------- src/utils/Helpers/ML/MLTransaction.test.js | 43 ++++---- src/utils/Helpers/Number/Format.js | 8 -- 23 files changed, 311 insertions(+), 179 deletions(-) create mode 100644 src/hooks/etc/useEffectOnce.js diff --git a/src/components/basic/Input/Input.css b/src/components/basic/Input/Input.css index 00f3c350..ea596976 100644 --- a/src/components/basic/Input/Input.css +++ b/src/components/basic/Input/Input.css @@ -16,6 +16,7 @@ .input::placeholder { color: rgb(var(--color-dark-gray)); + opacity: 0.2; font-size: 1.5rem; } diff --git a/src/components/composed/Balance/Balance.test.js b/src/components/composed/Balance/Balance.test.js index 1caaf646..29b8f185 100644 --- a/src/components/composed/Balance/Balance.test.js +++ b/src/components/composed/Balance/Balance.test.js @@ -25,7 +25,7 @@ test('Render account balance with ML', () => { expect(balanceParagraphs[0].textContent).toBe(BALANCE_SAMPLE + ' ML') expect(balanceParagraphs[1].textContent).toBe( - BALANCE_SAMPLE * EXCHANGE_RATE_SAMPLE + ',00 USD', + BALANCE_SAMPLE * EXCHANGE_RATE_SAMPLE + '.00 USD', ) }) @@ -49,7 +49,7 @@ test('Render account balance with BTC', () => { expect(balanceParagraphs[0].textContent).toBe(BALANCE_SAMPLE + ' BTC') expect(balanceParagraphs[1].textContent).toBe( - BALANCE_SAMPLE * EXCHANGE_RATE_SAMPLE + ',00 USD', + BALANCE_SAMPLE * EXCHANGE_RATE_SAMPLE + '.00 USD', ) }) diff --git a/src/components/composed/CryptoFiatField/CryptoFiatField.js b/src/components/composed/CryptoFiatField/CryptoFiatField.js index 4d68faaa..0586ed10 100644 --- a/src/components/composed/CryptoFiatField/CryptoFiatField.js +++ b/src/components/composed/CryptoFiatField/CryptoFiatField.js @@ -39,7 +39,7 @@ const CryptoFiatField = ({ const [value, setValue] = useState(inputValue) const [validity, setValidity] = useState(parentValidity) const amountErrorMessage = 'Amount set is bigger than this wallet balance.' - const amountFormatErrorMessage = 'Amount format is invalid. Use 0,00 instead.' + const amountFormatErrorMessage = 'Amount format is invalid. Use 0.00 instead.' const zeroErrorMessage = 'Amount must be greater than 0.' const isDelegationMode = transactionMode === AppInfo.ML_TRANSACTION_MODES.DELEGATION && @@ -77,7 +77,7 @@ const CryptoFiatField = ({ // Consider the correct format for 0,00 that might also be 0.00 const displayedBottomValue = networkType === AppInfo.NETWORK_TYPES.TESTNET - ? `≈ 0,00 ${fiatName}` + ? `≈ 0.00 ${fiatName}` : formattedBottomValue const calculateFiatValue = (value) => { @@ -141,12 +141,12 @@ const CryptoFiatField = ({ setAmountValidity(true) setValidity('valid') } - setValue(value || 0) - updateValue(value || 0) + setValue(value || '') + updateValue(value || '') const validity = AppInfo.amountRegex.test(value) - if (!validity) { + if (parsedValue > 0 && !validity) { setValidity('invalid') setAmountValidity(false) setErrorMessage(amountFormatErrorMessage) diff --git a/src/components/composed/CryptoFiatField/CryptoFiatField.test.js b/src/components/composed/CryptoFiatField/CryptoFiatField.test.js index c7a855c3..ff540ade 100644 --- a/src/components/composed/CryptoFiatField/CryptoFiatField.test.js +++ b/src/components/composed/CryptoFiatField/CryptoFiatField.test.js @@ -60,7 +60,7 @@ test('Render TextField component', () => { }) expect(input).toHaveValue(maxValueInToken.toString()) - expect(bottomNote).toHaveTextContent('≈ 10054453,50 USD') + expect(bottomNote).toHaveTextContent('≈ 10054453.50 USD') // expect(switchButton).toBeInTheDocument() // expect(arrowIcons).toHaveLength(2) @@ -102,7 +102,7 @@ test('Render TextField component fdf', async () => { const maxValueInCrypto = maxValueInToken - totalFeeCrypto fireEvent.click(actionButton) - expect(cryptoInput).toHaveValue(maxValueInCrypto.toString().replace('.', ',')) + expect(cryptoInput).toHaveValue(maxValueInCrypto.toString()) // fireEvent.click(switchButton) // const fiatInput = screen.getByTestId('input') @@ -156,7 +156,7 @@ test('Render TextField when networkType is testnet', () => { }) expect(input).toHaveValue(maxValueInToken.toString()) - expect(bottomNote).toHaveTextContent('≈ 0,00 USD') + expect(bottomNote).toHaveTextContent('≈ 0.00 USD') // expect(switchButton).toBeInTheDocument() diff --git a/src/components/composed/CurrentStaking/CurrentStaking.js b/src/components/composed/CurrentStaking/CurrentStaking.js index 9a4de933..8b66ce2e 100644 --- a/src/components/composed/CurrentStaking/CurrentStaking.js +++ b/src/components/composed/CurrentStaking/CurrentStaking.js @@ -12,6 +12,7 @@ import { TransactionContext, SettingsContext, AccountContext } from '@Contexts' import './CurrentStaking.css' import Timer from '../../basic/Timer/Timer' +import { useEffectOnce } from '../../../hooks/etc/useEffectOnce' const CurrentStaking = ({ addressList }) => { const { networkType } = useContext(SettingsContext) @@ -39,6 +40,10 @@ const CurrentStaking = ({ addressList }) => { getTransactions, } = useMlWalletInfo(addressList) + useEffectOnce(() => { + getDelegations() + }) + const onNextButtonClick = () => { setDelegationStep(2) } diff --git a/src/components/containers/SendTransaction/SendTransaction.js b/src/components/containers/SendTransaction/SendTransaction.js index 39a6384a..5803b32b 100644 --- a/src/components/containers/SendTransaction/SendTransaction.js +++ b/src/components/containers/SendTransaction/SendTransaction.js @@ -29,6 +29,7 @@ const SendTransaction = ({ confirmTransaction, goBackToWallet, preEnterAddress, + setAdjustedFee, }) => { const { walletType, balanceLoading } = useContext(AccountContext) const { feeLoading, transactionMode, currentDelegationInfo } = @@ -72,7 +73,9 @@ const SendTransaction = ({ } const openConfirmation = async () => { + if (!isFormValid) return setPopupState(true) + setTxErrorMessage('') onSendTransaction && onSendTransaction({ to: addressTo, @@ -106,6 +109,19 @@ const SendTransaction = ({ setPassValidity(false) setPass('') setAllowClosing(true) + } else if (e.message.includes('minimum fee')) { + // need to adjust fee + setAskPassword(false) + setPassPristinity(false) + setPassValidity(false) + setPass('') + setFee(e.message.split('minimum fee ')[1]) // Override fee with minimum fee + setTotalFeeCryptoParent(e.message.split('minimum fee ')[1]) + setAdjustedFee(e.message.split('minimum fee ')[1]) + setTxErrorMessage('Transaction fee adjusted') + console.error(e) + setAllowClosing(true) + setPopupState(true) } else { // handle other errors setAskPassword(false) @@ -336,7 +352,7 @@ const SendTransaction = ({ fiatName={fiatName} totalFeeFiat={totalFeeFiat} totalFeeCrypto={totalFeeCrypto} - // txErrorMessage={txErrorMessage} // TODO move update on confirmation stage + txErrorMessage={txErrorMessage} // TODO move update on confirmation stage fee={fee} onConfirm={handleConfirm} onCancel={handleCancel} diff --git a/src/components/containers/Wallet/Delegation.test.js b/src/components/containers/Wallet/Delegation.test.js index d3cd68b7..e8938985 100644 --- a/src/components/containers/Wallet/Delegation.test.js +++ b/src/components/containers/Wallet/Delegation.test.js @@ -41,7 +41,7 @@ describe('Delegation', () => { ) expect(screen.getByTestId('delegation-date')).toHaveTextContent(date) expect(screen.getByTestId('delegation-amount')).toHaveTextContent( - 'Amount: 0,001', + 'Amount: 0.001', ) }) diff --git a/src/hooks/UseWalletInfo/useBtcWalletInfo.test.js b/src/hooks/UseWalletInfo/useBtcWalletInfo.test.js index 09aad964..57203df8 100644 --- a/src/hooks/UseWalletInfo/useBtcWalletInfo.test.js +++ b/src/hooks/UseWalletInfo/useBtcWalletInfo.test.js @@ -76,7 +76,7 @@ test('UseBtcWalletInfo hook', async () => { transactionsList = result.current.btcTransactionsList }) - expect(balance).toBe('0,02881771') + expect(balance).toBe('0.02881771') // TODO: +1 because of the message transaction. This is a temporary solution expect(transactionsList.length).toBe(rawTransactions.length + 1) }) diff --git a/src/hooks/UseWalletInfo/useMlWalletInfo.js b/src/hooks/UseWalletInfo/useMlWalletInfo.js index 717207fb..399eaf01 100644 --- a/src/hooks/UseWalletInfo/useMlWalletInfo.js +++ b/src/hooks/UseWalletInfo/useMlWalletInfo.js @@ -93,11 +93,22 @@ const useMlWalletInfo = (addresses) => { const delegation_details = await Mintlayer.getDelegationDetails( delegations.map((delegation) => delegation.delegation_id), ) + const blocks_data = await Mintlayer.getBlocksData( + delegation_details.map( + (delegation) => delegation.creation_block_height, + ), + ) const mergedDelegations = delegations.map((delegation, index) => { return { ...delegation, - creation_time: delegation_details[index].creation_time.timestamp, + balance: delegation.balance.atoms, + creation_block_height: + delegation_details[index].creation_block_height, + creation_time: blocks_data.find( + ({ height }) => + height === delegation_details[index].creation_block_height, + ).header.timestamp.timestamp, } }) @@ -119,9 +130,9 @@ const useMlWalletInfo = (addresses) => { if (effectCalled.current) return effectCalled.current = true - getTransactions() - getDelegations() - getBalance() + // getTransactions() + // getDelegations() + // getBalance() }, [getBalance, getTransactions, getDelegations]) return { @@ -134,6 +145,7 @@ const useMlWalletInfo = (addresses) => { mlDelegationsBalance, getDelegations, getTransactions, + getBalance, } } diff --git a/src/hooks/etc/useEffectOnce.js b/src/hooks/etc/useEffectOnce.js new file mode 100644 index 00000000..136db9aa --- /dev/null +++ b/src/hooks/etc/useEffectOnce.js @@ -0,0 +1,10 @@ +import { useEffect, useRef } from 'react' +export function useEffectOnce(effect) { + const called = useRef(false) + useEffect(() => { + if (!called.current) { + called.current = true + effect() + } + }, [effect]) +} diff --git a/src/index.js b/src/index.js index f58cd1af..0e082ad7 100644 --- a/src/index.js +++ b/src/index.js @@ -127,7 +127,6 @@ const App = () => { 'This script should only be loaded in a browser extension.' ) { // not extension env - console.log('not extension env') return } // other error throw further diff --git a/src/pages/Dashboard/Dashboard.js b/src/pages/Dashboard/Dashboard.js index b749e10e..475fcb5e 100644 --- a/src/pages/Dashboard/Dashboard.js +++ b/src/pages/Dashboard/Dashboard.js @@ -18,6 +18,7 @@ import useOneDayAgoHist from 'src/hooks/UseOneDayAgoHist/useOneDayAgoHist' import { useNavigate } from 'react-router-dom' import { BTC } from '@Helpers' import { AppInfo } from '@Constants' +import { useEffectOnce } from 'src/hooks/etc/useEffectOnce' const DashboardPage = () => { const { addresses, accountName, setWalletType, accountID } = @@ -37,7 +38,7 @@ const DashboardPage = () => { const [connectedWalletType, setConnectedWalletType] = useState('') const { btcBalance } = useBtcWalletInfo(currentBtcAddress) - const { mlBalance } = useMlWalletInfo(currentMlAddresses) + const { mlBalance, getBalance } = useMlWalletInfo(currentMlAddresses) const { exchangeRate: btcExchangeRate } = useExchangeRates('btc', 'usd') const { exchangeRate: mlExchangeRate } = useExchangeRates('ml', 'usd') const { yesterdayExchangeRate: btcYesterdayExchangeRate } = @@ -172,6 +173,10 @@ const DashboardPage = () => { getCurrentAccount(accountID).then((account) => setAccount(account)) }, [accountID]) + useEffectOnce(() => { + getBalance() + }, []) + return ( <>
diff --git a/src/pages/SendTransaction/SendTransaction.js b/src/pages/SendTransaction/SendTransaction.js index 07dfdf7b..10f4f259 100644 --- a/src/pages/SendTransaction/SendTransaction.js +++ b/src/pages/SendTransaction/SendTransaction.js @@ -21,6 +21,7 @@ import { ML } from '@Cryptos' import { Mintlayer } from '@APIs' import './SendTransaction.css' +import { useEffectOnce } from '../../hooks/etc/useEffectOnce' const SendTransactionPage = () => { const { addresses, accountID, walletType } = useContext(AccountContext) @@ -36,6 +37,7 @@ const SendTransactionPage = () => { : addresses.mlTestnetAddresses const [totalFeeFiat, setTotalFeeFiat] = useState(0) const [totalFeeCrypto, setTotalFeeCrypto] = useState(0) + const [adjustedFee, setAdjustedFee] = useState(0) const navigate = useNavigate() const tokenName = walletType.name === 'Mintlayer' ? 'ML' : 'BTC' const fiatName = 'USD' @@ -48,7 +50,11 @@ const SendTransactionPage = () => { const { exchangeRate } = useExchangeRates(tokenName, fiatName) const { btcBalance } = useBtcWalletInfo(currentBtcAddress) - const { mlBalance } = useMlWalletInfo(currentMlAddresses) + const { mlBalance, getBalance } = useMlWalletInfo(currentMlAddresses) + + useEffectOnce(() => { + getBalance() + }) const maxValueToken = walletType.name === 'Mintlayer' ? mlBalance : btcBalance @@ -90,26 +96,30 @@ const SendTransactionPage = () => { const calculateMlTotalFee = async (transactionInfo) => { setFeeLoading(true) const address = transactionInfo.to - const amountToSend = MLHelpers.getAmountInAtoms( - transactionInfo.amount, - ).toString() + const amountToSend = MLHelpers.getAmountInAtoms(transactionInfo.amount) const unusedChangeAddress = await ML.getUnusedAddress(changeAddress) const utxos = await Mintlayer.getWalletUtxos(mlAddressList) const parsedUtxos = utxos .map((utxo) => JSON.parse(utxo)) .filter((utxo) => utxo.length > 0) - const fee = await MLTransaction.calculateFee( - parsedUtxos, - address, - unusedChangeAddress, - amountToSend, - networkType, - ) - const feeInCoins = MLHelpers.getAmountInCoins(fee) - setTotalFeeFiat(Format.fiatValue(feeInCoins * exchangeRate)) - setTotalFeeCrypto(feeInCoins) - setFeeLoading(false) - return feeInCoins + try { + const fee = await MLTransaction.calculateFee({ + utxosTotal: parsedUtxos, + address: address, + changeAddress: unusedChangeAddress, + amountToUse: amountToSend, + network: networkType, + }) + const feeInCoins = MLHelpers.getAmountInCoins(Number(fee)) + setTotalFeeFiat(Format.fiatValue(feeInCoins * exchangeRate)) + setTotalFeeCrypto(feeInCoins) + setFeeLoading(false) + return feeInCoins + } catch (e) { + console.error('Error calculating fee:', e) + goBackToWallet() + setFeeLoading(false) + } } const createTransaction = async (transactionInfo) => { @@ -151,7 +161,7 @@ const SendTransactionPage = () => { const confirmMlTransaction = async (password) => { const amountToSend = MLHelpers.getAmountInAtoms( transactionInformation.amount, - ).toString() + ) const { mlPrivKeys } = await Account.unlockAccount(accountID, password) const privKey = networkType === 'mainnet' @@ -173,14 +183,17 @@ const SendTransactionPage = () => { const parsedUtxos = utxos .map((utxo) => JSON.parse(utxo)) .filter((utxo) => utxo.length > 0) - const result = await MLTransaction.sendTransaction( - parsedUtxos, - keysList, - transactionInformation.to, - unusedChageAddress, - amountToSend, - networkType, - ) + const result = await MLTransaction.sendTransaction({ + utxosTotal: parsedUtxos, + keysList: keysList, + address: transactionInformation.to, + changeAddress: unusedChageAddress, + amountToUse: amountToSend, + network: networkType, + ...(adjustedFee && { + adjustedFee: MLHelpers.getAmountInAtoms(adjustedFee), + }), + }) return result } @@ -195,6 +208,7 @@ const SendTransactionPage = () => { totalFeeFiat={totalFeeFiat} totalFeeCrypto={totalFeeCrypto} setTotalFeeCrypto={setTotalFeeCrypto} + setAdjustedFee={setAdjustedFee} transactionData={transactionData} exchangeRate={exchangeRate} maxValueInToken={maxValueToken} diff --git a/src/pages/Staking/Staking.js b/src/pages/Staking/Staking.js index acfbcc8b..868ed725 100644 --- a/src/pages/Staking/Staking.js +++ b/src/pages/Staking/Staking.js @@ -14,6 +14,7 @@ import { ML } from '@Cryptos' import { Mintlayer } from '@APIs' import './Staking.css' +import { useEffectOnce } from '../../hooks/etc/useEffectOnce' const StakingPage = () => { const { state } = useLocation() @@ -50,7 +51,7 @@ const StakingPage = () => { const [transactionInformation, setTransactionInformation] = useState(null) const { exchangeRate } = useExchangeRates(tokenName, fiatName) - const { mlBalance } = useMlWalletInfo(currentMlAddresses) + const { mlBalance, getBalance } = useMlWalletInfo(currentMlAddresses) const delegationBalance = Format.BTCValue( MLHelpers.getAmountInCoins(currentDelegationInfo.balance), ) @@ -63,6 +64,10 @@ const StakingPage = () => { navigate('/wallet') } + useEffectOnce(()=>{ + getBalance() + }) + useEffect(() => { if (state && state.action === 'createDelegate') { setDelegationStep(2) @@ -96,7 +101,7 @@ const StakingPage = () => { const address = transactionInfo.to const amountToSend = MLHelpers.getAmountInAtoms( transactionInfo.amount, - ).toString() + ) const unusedChangeAddress = await ML.getUnusedAddress(changeAddresses) const unusedReceivingAddress = await ML.getUnusedAddress(receivingAddresses) const utxos = await Mintlayer.getWalletUtxos(mlAddressList) @@ -105,15 +110,13 @@ const StakingPage = () => { .filter((utxo) => utxo.length > 0) const fee = transactionMode === AppInfo.ML_TRANSACTION_MODES.STAKING - ? await MLTransaction.calculateFee( - parsedUtxos, - undefined, - unusedChangeAddress, - amountToSend, - networkType, - undefined, - address, - ) + ? await MLTransaction.calculateFee({ + utxosTotal: parsedUtxos, + changeAddress: unusedChangeAddress, + amountToUse: amountToSend, + network: networkType, + delegationId: address, + }) : transactionMode === AppInfo.ML_TRANSACTION_MODES.WITHDRAW ? await MLTransaction.calculateSpenDelegFee( address, @@ -121,15 +124,15 @@ const StakingPage = () => { networkType, currentDelegationInfo, ) - : await MLTransaction.calculateFee( - parsedUtxos, - unusedReceivingAddress, - unusedChangeAddress, - amountToSend, - networkType, - address, - ) - const feeInCoins = MLHelpers.getAmountInCoins(fee) + : await MLTransaction.calculateFee({ + utxosTotal: parsedUtxos, + address: unusedReceivingAddress, + changeAddress: unusedChangeAddress, + amountToUse: BigInt(0), + network: networkType, + poolId: address, + }) + const feeInCoins = MLHelpers.getAmountInCoins(Number(fee)) setTotalFeeFiat(Format.fiatValue(feeInCoins * exchangeRate)) setTotalFeeCrypto(feeInCoins) setFeeLoading(false) @@ -144,7 +147,7 @@ const StakingPage = () => { const confirmMlTransaction = async (password) => { const amountToSend = MLHelpers.getAmountInAtoms( transactionInformation.amount, - ).toString() + ) const { mlPrivKeys } = await Account.unlockAccount(accountID, password) const privKey = networkType === 'mainnet' @@ -170,16 +173,14 @@ const StakingPage = () => { const result = transactionMode === AppInfo.ML_TRANSACTION_MODES.STAKING - ? await MLTransaction.sendTransaction( - parsedUtxos, - keysList, - undefined, - unusedChageAddress, - amountToSend, - networkType, - undefined, - transactionInformation.to, - ) + ? await MLTransaction.sendTransaction({ + utxosTotal: parsedUtxos, + keysList: keysList, + changeAddress: unusedChageAddress, + amountToUse: amountToSend, + network: networkType, + delegationId: transactionInformation.to, + }) : transactionMode === AppInfo.ML_TRANSACTION_MODES.WITHDRAW ? await MLTransaction.spendFromDelegation( keysList, @@ -188,17 +189,16 @@ const StakingPage = () => { networkType, currentDelegationInfo, ) - : await MLTransaction.sendTransaction( - parsedUtxos, - keysList, - unusedReceivingAddress, - unusedChageAddress, - '0', - networkType, - transactionInformation.to, - undefined, - transactionMode, - ) + : await MLTransaction.sendTransaction({ + utxosTotal: parsedUtxos, + keysList: keysList, + address: unusedReceivingAddress, + changeAddress: unusedChageAddress, + amountToUse: BigInt('0'), + network: networkType, + poolId: transactionInformation.to, + transactionMode: transactionMode, + }) return result } diff --git a/src/pages/Wallet/Wallet.js b/src/pages/Wallet/Wallet.js index fed8e860..1096c9e5 100644 --- a/src/pages/Wallet/Wallet.js +++ b/src/pages/Wallet/Wallet.js @@ -12,6 +12,7 @@ import { AppInfo } from '@Constants' import { LocalStorageService } from '@Storage' import './Wallet.css' +import { useEffectOnce } from '../../hooks/etc/useEffectOnce' const WalletPage = () => { const navigate = useNavigate() @@ -29,8 +30,13 @@ const WalletPage = () => { : addresses.mlTestnetAddresses const [openShowAddress, setOpenShowAddress] = useState(false) const { btcTransactionsList, btcBalance } = useBtcWalletInfo(btcAddress) - const { mlTransactionsList, mlBalance, mlBalanceLocked } = - useMlWalletInfo(currentMlAddresses) + const { + mlTransactionsList, + mlBalance, + mlBalanceLocked, + getTransactions, + getBalance, + } = useMlWalletInfo(currentMlAddresses) const { exchangeRate: btcExchangeRate } = useExchangeRates('btc', 'usd') const { exchangeRate: mlExchangeRate } = useExchangeRates('ml', 'usd') @@ -64,6 +70,11 @@ const WalletPage = () => { LocalStorageService.getItem(unconfirmedTransactionString) && walletType.name === 'Mintlayer' + useEffectOnce(() => { + getTransactions() + getBalance() + }) + return (
diff --git a/src/services/API/Mintlayer/Mintlayer.js b/src/services/API/Mintlayer/Mintlayer.js index 5e12834c..162b8bf0 100644 --- a/src/services/API/Mintlayer/Mintlayer.js +++ b/src/services/API/Mintlayer/Mintlayer.js @@ -2,17 +2,19 @@ import { EnvVars } from '@Constants' import { LocalStorageService } from '@Storage' import { AppInfo } from '@Constants' -const prefix = '/api/v1' +const prefix = '/api/v2' const MINTLAYER_ENDPOINTS = { GET_ADDRESS_DATA: '/address/:address', GET_TRANSACTION_DATA: '/transaction/:txid', - GET_ADDRESS_UTXO: '/address/:address/available-utxos', + GET_ADDRESS_UTXO: '/address/:address/spendable-utxos', POST_TRANSACTION: '/transaction', GET_FEES_ESTIMATES: '/feerate', GET_ADDRESS_DELEGATIONS: '/address/:address/delegations', GET_DELEGATION: '/delegation/:delegation', GET_CHAIN_TIP: '/chain/tip', + GET_BLOCK_HASH: '/chain/:height', + GET_BLOCK_DATA: '/block/:hash', } const requestMintlayer = async (url, body = null, request = fetch) => { @@ -28,6 +30,20 @@ const requestMintlayer = async (url, body = null, request = fetch) => { ) } + // handle RPC error + if ( + error.error.includes( + 'Mempool error: Transaction does not pay sufficient fees to be relayed', + ) + ) { + const errorMessage = error.error + .split('Mempool error: ')[1] + .split(')')[0] + .replace('(tx_fee:', '. estimated fee') + .replace('min_relay_fee:', 'minimum fee') + throw new Error(errorMessage) + } + // handle RPC error if (error.error.includes('Mempool error:')) { const errorMessage = error.error @@ -83,10 +99,10 @@ const getAddressBalance = async (address) => { const response = await getAddressData(address) const data = JSON.parse(response) const balance = { - balanceInAtoms: data.coin_balance, + balanceInAtoms: data.coin_balance.atoms, } const balanceLocked = { - balanceInAtoms: data.locked_coin_balance || 0, + balanceInAtoms: data.locked_coin_balance.atoms || 0, } return { balance, balanceLocked } } catch (error) { @@ -192,6 +208,18 @@ const getDelegation = (delegation) => MINTLAYER_ENDPOINTS.GET_DELEGATION.replace(':delegation', delegation), ) +const getBlockDataByHeight = (height) => { + return tryServers( + MINTLAYER_ENDPOINTS.GET_BLOCK_HASH.replace(':height', height), + ) + .then(JSON.parse) + .then((response) => { + return tryServers( + MINTLAYER_ENDPOINTS.GET_BLOCK_DATA.replace(':hash', response), + ) + }) +} + const getWalletDelegations = (addresses) => { const delegationsPromises = addresses.map((address) => getAddressDelegations(address), @@ -208,6 +236,12 @@ const getDelegationDetails = (delegations) => { results.flatMap(JSON.parse), ) } +const getBlocksData = (heights) => { + const heightsPromises = heights.map((height) => getBlockDataByHeight(height)) + return Promise.all(heightsPromises).then((results) => + results.flatMap(JSON.parse), + ) +} const getChainTip = async () => { return tryServers(MINTLAYER_ENDPOINTS.GET_CHAIN_TIP) @@ -237,5 +271,6 @@ export { getChainTip, broadcastTransaction, getFeesEstimates, + getBlocksData, MINTLAYER_ENDPOINTS, } diff --git a/src/services/Crypto/Mintlayer/Mintlayer.js b/src/services/Crypto/Mintlayer/Mintlayer.js index fbac8687..45359211 100644 --- a/src/services/Crypto/Mintlayer/Mintlayer.js +++ b/src/services/Crypto/Mintlayer/Mintlayer.js @@ -14,7 +14,6 @@ import init, { estimate_transaction_size, encode_lock_until_time, encode_output_lock_then_transfer, - encode_lock_until_height, encode_lock_for_block_count, encode_output_create_delegation, encode_output_delegate_staking, @@ -173,6 +172,7 @@ export const getOutputs = async ({ if (type === 'LockThenTransfer' && !lock) { throw new Error('LockThenTransfer requires a lock') } + const amountInstace = Amount.from_atoms(amount) const networkIndex = NETWORKS[networkType] @@ -185,7 +185,7 @@ export const getOutputs = async ({ lockEncoded = encode_lock_until_time(BigInt(lock.UntilTime.timestamp)) } if (lock.ForBlockCount) { - lockEncoded = encode_lock_until_height(BigInt(lock.ForBlockCount)) + lockEncoded = encode_lock_for_block_count(BigInt(lock.ForBlockCount)) } return encode_output_lock_then_transfer( amountInstace, diff --git a/src/services/Database/IndexedDB/IndexedDB.js b/src/services/Database/IndexedDB/IndexedDB.js index 3d861764..6af94f9e 100644 --- a/src/services/Database/IndexedDB/IndexedDB.js +++ b/src/services/Database/IndexedDB/IndexedDB.js @@ -68,7 +68,6 @@ const saveAccounts = async (accounts, onError, DB = IDB) => { for (const account of accounts) { await update(oldAccounts, account) } - console.log('Accounts saved successfully') db.close() } catch (error) { diff --git a/src/utils/Constants/AppInfo/AppInfo.js b/src/utils/Constants/AppInfo/AppInfo.js index 2cdbc046..1601cdef 100644 --- a/src/utils/Constants/AppInfo/AppInfo.js +++ b/src/utils/Constants/AppInfo/AppInfo.js @@ -6,9 +6,9 @@ const appAccounts = async () => { return accounts } -const decimalSeparator = ',' -const thousandsSeparator = '.' -const amountRegex = /^\d+(,\d+)?$/ +const decimalSeparator = '.' +const thousandsSeparator = ' ' +const amountRegex = /^\d+(.\d+)?$/ const minEntropyLength = 192 const DEFAULT_WALLETS_TO_CREATE = ['btc'] const ML_ATOMS_PER_COIN = 100000000000 diff --git a/src/utils/Helpers/ML/ML.js b/src/utils/Helpers/ML/ML.js index 8d19a1ef..c3e0b2b1 100644 --- a/src/utils/Helpers/ML/ML.js +++ b/src/utils/Helpers/ML/ML.js @@ -7,7 +7,7 @@ const getAmountInCoins = (amointInAtoms) => { } const getAmountInAtoms = (amountInCoins) => { - return Math.round(amountInCoins * AppInfo.ML_ATOMS_PER_COIN) + return BigInt(Math.round(amountInCoins * AppInfo.ML_ATOMS_PER_COIN)) } const getParsedTransactions = (transactions, addresses) => { @@ -90,38 +90,38 @@ const getParsedTransactions = (transactions, addresses) => { const totalValue = transaction.outputs.reduce((acc, output) => { if (!addresses.includes(output.destination)) { if (output.type === 'Transfer') { - return acc + output.value.amount + return acc + output.value.amount.decimal } if (output.type === 'LockThenTransfer') { - return acc + Number(output.value.amount) + return acc + Number(output.value.amount.decimal) } if (output.type === 'CreateStakePool') { type = 'CreateStakePool' destAddress = output.pool_id - return acc + Number(output.data.amount) + return acc + Number(output.data.amount.decimal) } if (output.type === 'DelegateStaking') { type = 'DelegateStaking' destAddress = output.delegation_id - return acc + Number(output.amount) + return acc + Number(output.amount.decimal) } if (output.type === 'CreateDelegationId') { type = 'CreateDelegationId' destAddress = output.pool_id sameWalletTransaction = false - return acc + Number(output.amount) + return acc + Number(output.amount.decimal) } } if (addresses.includes(output.destination)) { if (output.type === 'CreateStakePool') { type = 'CreateStakePool' destAddress = output.pool_id - return acc + Number(output.data.amount) + return acc + Number(output.data.amount.decimal) } if (output.type === 'DelegateStaking') { type = 'DelegateStaking' destAddress = output.delegation_id - return acc + Number(output.amount) + return acc + Number(output.amount.decimal) } if (output.type === 'CreateDelegationId') { type = 'CreateDelegationId' @@ -132,7 +132,7 @@ const getParsedTransactions = (transactions, addresses) => { } return acc }, 0) - value = getAmountInCoins(totalValue, AppInfo.ML_ATOMS_PER_COIN) + value = totalValue } if (withInputUTXO && direction === 'in' && transaction.outputs.length > 0) { @@ -140,15 +140,15 @@ const getParsedTransactions = (transactions, addresses) => { const totalValue = transaction.outputs.reduce((acc, output) => { if (addresses.includes(output.destination)) { if (output.type === 'Transfer') { - return acc + output.value.amount + return acc + output.value.amount.decimal } if (output.type === 'LockThenTransfer') { - return acc + Number(output.value.amount) + return acc + Number(output.value.amount.decimal) } } return acc }, 0) - value = getAmountInCoins(totalValue, AppInfo.ML_ATOMS_PER_COIN) + value = totalValue } if ( @@ -163,31 +163,28 @@ const getParsedTransactions = (transactions, addresses) => { const totalValue = transaction.outputs.reduce((acc, output) => { if (addresses.includes(output.destination)) { if (output.type === 'Transfer') { - return acc + output.value.amount + return acc + output.value.amount.decimal } if (output.type === 'LockThenTransfer') { if ( - transaction.inputs[0].input?.Account?.account - ?.DelegationBalance[0] + transaction.inputs[0].input?.account_type === 'DelegationBalance' ) { type = 'Delegate Withdrawal' - destAddress = - transaction.inputs[0].input?.Account?.account - ?.DelegationBalance[0] + destAddress = transaction.inputs[0].input?.delegation_id } - return acc + Number(output.value.amount) + return acc + Number(output.value.amount.decimal) } } return acc }, 0) - value = getAmountInCoins(totalValue, AppInfo.ML_ATOMS_PER_COIN) + value = totalValue } const confirmations = transaction.confirmations const date = transaction.timestamp const txid = transaction.txid - const fee = transaction.fee + const fee = transaction.fee.decimal const isConfirmed = confirmations > 0 return { diff --git a/src/utils/Helpers/ML/MLTransaction.js b/src/utils/Helpers/ML/MLTransaction.js index edede9ea..919bf46a 100644 --- a/src/utils/Helpers/ML/MLTransaction.js +++ b/src/utils/Helpers/ML/MLTransaction.js @@ -6,7 +6,10 @@ import { ML as MLHelpers } from '@Helpers' import { AppInfo } from '@Constants' const getUtxoBalance = (utxo) => { - return utxo.reduce((sum, item) => sum + Number(item.utxo.value.amount), 0) + return utxo.reduce( + (sum, item) => sum + BigInt(item.utxo.value.amount.atoms), + BigInt(0), + ) } const getUtxoAvailable = (utxo) => { @@ -23,7 +26,7 @@ const getUtxoAvailable = (utxo) => { const getUtxoTransaction = (utxo) => { return utxo.map((item) => ({ - transaction: item.outpoint.id.Transaction, + transaction: item.outpoint.source_id, index: item.outpoint.index, })) } @@ -56,19 +59,40 @@ const getTxInput = async (outpointSourceId) => { ) } -const getTransactionUtxos = (utxos, amountToUse, fee = 0) => { - let balance = 0 +/** + * Get utxos to spend + * NOTE: This function require optimization to get UTXOs with the lowest amounts first or 50% lowest and 50% highest, see: https://arxiv.org/pdf/2311.01113.pdf + * At this point there is a risk of not having enough UTXOs to spend because first picked UTXOs is equal to the amount to spend without fee + * In that case backend will return error with proper fee amount wich is parsed and passed as override fee value. + * Need to add some "backup" additional UTXO is AMOUNT is equal of UTXOs amount so that server error less likely to happen but I'm leaving it just to be sure + * @param utxos + * @param amountToUse + * @param fee + * @returns {*[]} + */ +const getTransactionUtxos = (utxos, amountToUse, fee = BigInt(0)) => { + let balance = BigInt(0) const utxosToSpend = [] + let lastIndex = 0 for (let i = 0; i < utxos.length; i++) { + lastIndex = i const utxoBalance = getUtxoBalance(utxos[i]) - if (balance < Number(amountToUse) + fee) { + if (balance < BigInt(amountToUse) + fee) { balance += utxoBalance utxosToSpend.push(utxos[i]) } else { break } } + + if (balance === BigInt(amountToUse)) { + // pick up extra UTXO + if (utxos[lastIndex + 1]) { + utxosToSpend.push(utxos[lastIndex + 1]) + } + } + return utxosToSpend } @@ -130,7 +154,7 @@ const getOptUtxos = async (utxos, network) => { const opt_utxos = await Promise.all( utxos.map((item) => { return ML.getOutputs({ - amount: item.utxo.value.amount, + amount: item.utxo.value.amount.atoms, address: item.utxo.destination, networkType: network, type: item.utxo.type, @@ -188,9 +212,11 @@ const totalUtxosAmount = (utxosToSpend) => { return utxosToSpend .flatMap((utxo) => [...utxo]) .reduce((acc, utxo) => { - const amount = utxo.utxo.value ? Number(utxo.utxo.value.amount) : 0 + const amount = utxo?.utxo?.value?.amount + ? BigInt(utxo.utxo.value.amount.atoms) + : 0 return acc + amount - }, 0) + }, BigInt(0)) } const getUtxoAddress = (utxosToSpend) => { @@ -199,7 +225,7 @@ const getUtxoAddress = (utxosToSpend) => { .map((utxo) => utxo.utxo.destination) } -const calculateFee = async ( +const calculateFee = async ({ utxosTotal, address, changeAddress, @@ -207,11 +233,11 @@ const calculateFee = async ( network, poolId, delegationId, -) => { - const amountToUseFinale = Number(amountToUse) <= 0 ? 1 : amountToUse +}) => { + const amountToUseFinale = amountToUse <= 0 ? BigInt(1) : amountToUse const utxos = getUtxoAvailable(utxosTotal) const totalAmount = !poolId ? totalUtxosAmount(utxos) : 0 - if (totalAmount < Number(amountToUse) && !poolId) { + if (totalAmount < BigInt(amountToUse) && !poolId) { throw new Error('Insufficient funds') } const requireUtxo = getTransactionUtxos(utxos, amountToUseFinale) @@ -229,7 +255,7 @@ const calculateFee = async ( delegationId, ) const changeAmount = ( - totalUtxosAmount(requireUtxo) - Number(amountToUseFinale) + totalUtxosAmount(requireUtxo) - amountToUseFinale ).toString() const txChangeOutput = await getTxOutput(changeAmount, changeAddress, network) const outputs = [...txOutput, ...txChangeOutput] @@ -244,7 +270,7 @@ const calculateFee = async ( const feeEstimates = JSON.parse(feeEstimatesResponse) const fee = Math.ceil((Number(feeEstimates) / 1000) * size) - return fee + return BigInt(fee) } const calculateSpenDelegFee = async (address, amount, network, delegation) => { @@ -277,7 +303,7 @@ const calculateSpenDelegFee = async (address, amount, network, delegation) => { return fee } -const sendTransaction = async ( +const sendTransaction = async ({ utxosTotal, keysList, address, @@ -287,25 +313,29 @@ const sendTransaction = async ( poolId, delegationId, transactionMode, -) => { + adjustedFee, +}) => { const utxos = getUtxoAvailable(utxosTotal) const totalAmount = totalUtxosAmount(utxos) - const fee = await calculateFee( - utxos, - address, - changeAddress, - amountToUse, - network, - poolId, - delegationId, - ) - if (fee > AppInfo.MAX_ML_FEE) { + const fee = + adjustedFee || + (await calculateFee({ + utxosTotal: utxos, + address, + changeAddress, + amountToUse, + network, + poolId, + delegationId, + })) + + if (fee > BigInt(AppInfo.MAX_ML_FEE)) { throw new Error('Fee is too high, please try again later.') } let amount = amountToUse - if (totalAmount < Number(amountToUse) + fee) { + if (totalAmount < amountToUse + fee) { amount = totalAmount - fee } @@ -322,11 +352,8 @@ const sendTransaction = async ( poolId, delegationId, ) - const changeAmount = ( - totalUtxosAmount(requireUtxo) - - Number(amount) - - fee - ).toString() + + const changeAmount = (totalUtxosAmount(requireUtxo) - amount - fee).toString() const txChangeOutput = await getTxOutput(changeAmount, changeAddress, network) const outputs = [...txOutput, ...txChangeOutput] const optUtxos = await getOptUtxos(requireUtxo.flat(), network) @@ -364,11 +391,11 @@ const sendTransaction = async ( direction: 'out', type: 'Unconfirmed', destAddress: address || delegationId, - value: MLHelpers.getAmountInCoins(amount), + value: MLHelpers.getAmountInCoins(Number(amount)), confirmations: 0, date: '', txid: JSON.parse(result).tx_id, - fee: fee, + fee: fee.toString(), isConfirmed: false, mode: transactionMode, poolId: poolId, @@ -448,7 +475,7 @@ const spendFromDelegation = async ( direction: 'out', type: 'Unconfirmed', destAddress: address, - value: MLHelpers.getAmountInCoins(amount), + value: MLHelpers.getAmountInCoins(Number(amount)), confirmations: 0, date: '', txid: JSON.parse(result).tx_id, diff --git a/src/utils/Helpers/ML/MLTransaction.test.js b/src/utils/Helpers/ML/MLTransaction.test.js index 8b1fe23f..5ebd133c 100644 --- a/src/utils/Helpers/ML/MLTransaction.test.js +++ b/src/utils/Helpers/ML/MLTransaction.test.js @@ -8,51 +8,60 @@ import { const UTXSOS_MOCK = [ { outpoint: { - id: { - Transaction: - '0cd5de5319de96f7c967a24a51224f4eab7882e6dbe336ab6837b15e1b68dace', - }, - index: 1, + source_id: + '0cd5de5319de96f7c967a24a51224f4eab7882e6dbe336ab6837b15e1b68dace', + source_type: 'Transaction', + input_type: 'UTXO', + index: 0, }, utxo: { destination: 'tmt1qylgafccyyy26zrtqk8gjvcwzut26taruuyzmcr6', type: 'Transfer', value: { - amount: '200', + amount: { + atoms: '200', + decimals: '0.000000002', + }, type: 'Coin', }, }, }, { outpoint: { - id: { - Transaction: - '0cd5de5319de96f7c967a24a51224f4eab7882e6dbe336ab6837b15e1b68dace', - }, + source_id: + '0cd5de5319de96f7c967a24a51224f4eab7882e6dbe336ab6837b15e1b68dace', + source_type: 'Transaction', + input_type: 'UTXO', index: 1, }, utxo: { destination: 'tmt1qylgafccyyy26zrtqk8gjvcwzut26taruuyzmcr6', type: 'Transfer', value: { - amount: '200', + amount: { + atoms: '200', + decimals: '0.000000002', + }, type: 'Coin', }, }, }, { outpoint: { - id: { - Transaction: - '0cd5de5319de96f7c967a24a51224f4eab7882e6dbe336ab6837b15e1b68dace', - }, - index: 1, + source_id: + '0cd5de5319de96f7c967a24a51224f4eab7882e6dbe336ab6837b15e1b68dace', + source_type: 'Transaction', + input_type: 'UTXO', + index: 2, }, utxo: { destination: 'tmt1qylgafccyyy26zrtqk8gjvcwzut26taruuyzmcr6', type: 'Transfer', value: { - amount: '200', + amount: { + atoms: '200', + decimals: '0.000000002', + }, type: 'Coin', }, }, diff --git a/src/utils/Helpers/Number/Format.js b/src/utils/Helpers/Number/Format.js index 6110378c..945c215a 100644 --- a/src/utils/Helpers/Number/Format.js +++ b/src/utils/Helpers/Number/Format.js @@ -5,14 +5,6 @@ import { getDecimalNumber } from './Number' const getNumber = (value) => typeof value === 'number' ? value : NumbersHelper.floatStringToNumber(value) -//TODO fix the value -// const BTCValue = (value) => -// getNumber(value) -// .toFixed(8) -// .replace(/\.0+$/, '') -// .replace(/(\.0{0,}[1-9]+)(0+)$/, '$1') -// .replace('.', AppInfo.decimalSeparator) - const BTCValue = (value) => { let str = getNumber(value).toString() const decimalIndex = str.indexOf('.') From 166eeddbcd8c63078ec5711b3dccc791fd3beee9 Mon Sep 17 00:00:00 2001 From: Sergey Date: Mon, 1 Apr 2024 20:25:45 +0200 Subject: [PATCH 2/5] fix: fiat display fix (#134) --- src/components/composed/CryptoFiatField/CryptoFiatField.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/composed/CryptoFiatField/CryptoFiatField.js b/src/components/composed/CryptoFiatField/CryptoFiatField.js index 0586ed10..230738e1 100644 --- a/src/components/composed/CryptoFiatField/CryptoFiatField.js +++ b/src/components/composed/CryptoFiatField/CryptoFiatField.js @@ -81,6 +81,9 @@ const CryptoFiatField = ({ : formattedBottomValue const calculateFiatValue = (value) => { + if (!value) { + return Format.fiatValue(0) + } const parsedValue = NumbersHelper.floatStringToNumber(value) return Format.fiatValue(parsedValue * exchangeRate) } From 63993b92ec5c8a0ae6b771b14b3b19381e736f60 Mon Sep 17 00:00:00 2001 From: Alexander <69318224+owlsua@users.noreply.github.com> Date: Tue, 2 Apr 2024 14:23:30 +0200 Subject: [PATCH 3/5] A-1206957896344401 (#132) * feat: update logo on the Home page * feat: update titles * feat: add expand button * style: update styles * test: update unit tests * feat: centers the window and hides the expand button if view is extended * feat: update Tooltip * feat: add Tooltip for the expand button * test: update unit tests * feat: add new entry point if extended * feat: add extended view creating or restoring the wallet * fix: expandHandler --- cypress/e2e/create-backup-db.cy.js | 2 +- packing.sh | 3 + public/background-script.js | 6 +- public/background.js | 6 +- public/index.css | 7 ++ public/index.html | 4 + src/assets/images/icon-expand.svg | 1 + src/assets/images/logo.svg | 6 ++ src/assets/styles/index.css | 2 +- src/components/basic/Button/Button.js | 4 + src/components/basic/Logo/Logo.css | 2 +- src/components/basic/Logo/Logo.js | 2 +- src/components/basic/Tooltip/Tooltip.css | 64 +++++++++++++-- src/components/basic/Tooltip/Tooltip.js | 16 +++- src/components/basic/Tooltip/Tooltip.test.js | 40 +++++++--- .../composed/AddWallet/AddWallet.js | 2 +- .../composed/AddWallet/AddWallet.test.js | 2 +- src/components/composed/Header/Header.css | 27 +++++++ src/components/composed/Header/Header.js | 77 ++++++++++++++++--- src/components/composed/Header/Header.test.js | 2 +- .../composed/HelpTooltip/HelpTooltip.js | 1 + .../composed/HelpTooltip/HelpTooltip.test.js | 14 ++-- .../CreateAccount/CreateAccount.test.js | 10 +-- src/components/containers/Login/Login.css | 2 +- src/components/containers/Login/Login.js | 2 +- src/components/containers/Login/Login.test.js | 6 +- .../RestoreAccount/RestoreAccount.test.js | 10 +-- .../AccountProvider/AccountProvider.js | 2 + src/index.js | 12 ++- src/pages/CreateAccount/CreateAccount.js | 2 +- src/pages/CreateRestore/CreateRestore.css | 2 +- src/pages/CreateRestore/CreateRestore.js | 27 ++++++- src/pages/Home/Home.js | 11 +-- 33 files changed, 299 insertions(+), 77 deletions(-) create mode 100644 public/index.css create mode 100644 src/assets/images/icon-expand.svg create mode 100644 src/assets/images/logo.svg diff --git a/cypress/e2e/create-backup-db.cy.js b/cypress/e2e/create-backup-db.cy.js index 7acccc9d..2301fd99 100644 --- a/cypress/e2e/create-backup-db.cy.js +++ b/cypress/e2e/create-backup-db.cy.js @@ -17,7 +17,7 @@ describe('stuff IndexedDB', () => { return a.then(() => cy.wrap(null).then(() => { cy.restoreWallet(x) - cy.contains('button', 'Create Wallet').click() + cy.contains('button', 'Add Wallet').click() }), ) }, cy.wrap(null)) diff --git a/packing.sh b/packing.sh index fd68a09c..30be7728 100644 --- a/packing.sh +++ b/packing.sh @@ -10,6 +10,9 @@ cd build CSPHEADER="" sed -i "s//$CSPHEADER/g" index.html +# Copying index.html to popup.html to have different entry points for popup and extended view +cp index.html popup.html + # creates zip for Firefox mv manifestFirefox.json manifest.json zip -r ../extFF.zip ./* diff --git a/public/background-script.js b/public/background-script.js index bea30f44..d04c7d41 100644 --- a/public/background-script.js +++ b/public/background-script.js @@ -27,7 +27,7 @@ browser.runtime.onConnect.addListener((port) => { async function handleConnect(request, port) { if (connectWindowId === null) { - await createPopup('index.html', async (win) => { + await createPopup('popup.html', async (win) => { connectWindowId = win.id setTimeout(async () => { const response = await browser.runtime.sendMessage({ @@ -44,7 +44,7 @@ browser.runtime.onConnect.addListener((port) => { async function handleDelegate(request) { if (popupWindowId === null && !isPopupOpening) { isPopupOpening = true - await createPopup('index.html', async (win) => { + await createPopup('popup.html', async (win) => { popupWindowId = win.id isPopupOpening = false setTimeout(async () => { @@ -61,7 +61,7 @@ browser.runtime.onConnect.addListener((port) => { async function handleStake(request) { if (popupWindowId === null) { - await createPopup('index.html', async (win) => { + await createPopup('popup.html', async (win) => { popupWindowId = win.id setTimeout(async () => { await browser.runtime.sendMessage({ diff --git a/public/background.js b/public/background.js index ccaade43..59921997 100644 --- a/public/background.js +++ b/public/background.js @@ -20,7 +20,7 @@ chrome.runtime.onMessageExternal.addListener(function ( popupWindowId = true chrome.windows.create( { - url: chrome.runtime.getURL('index.html'), + url: chrome.runtime.getURL('popup.html'), type: 'popup', width: 800, height: 600, @@ -52,7 +52,7 @@ chrome.runtime.onMessageExternal.addListener(function ( popupWindowId = true chrome.windows.create( { - url: chrome.runtime.getURL('index.html'), + url: chrome.runtime.getURL('popup.html'), type: 'popup', width: 800, height: 600, @@ -80,7 +80,7 @@ chrome.runtime.onMessageExternal.addListener(function ( popupWindowId = true chrome.windows.create( { - url: chrome.runtime.getURL('index.html'), + url: chrome.runtime.getURL('popup.html'), type: 'popup', width: 800, height: 630, diff --git a/public/index.css b/public/index.css new file mode 100644 index 00000000..22c54bab --- /dev/null +++ b/public/index.css @@ -0,0 +1,7 @@ +@media screen and (min-width: 801px) { + html { + display: flex; + justify-content: center; + padding: 30px 0 0 0; + } +} diff --git a/public/index.html b/public/index.html index a6f84f19..152183c3 100644 --- a/public/index.html +++ b/public/index.html @@ -3,6 +3,10 @@ + \ No newline at end of file diff --git a/src/assets/images/logo.svg b/src/assets/images/logo.svg new file mode 100644 index 00000000..d15f310e --- /dev/null +++ b/src/assets/images/logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/styles/index.css b/src/assets/styles/index.css index 296db3ef..863e8b45 100644 --- a/src/assets/styles/index.css +++ b/src/assets/styles/index.css @@ -37,7 +37,7 @@ body { .title-create { font-size: 24px; font-weight: bold; - margin: 8px 0 62px 0; + margin: 4rem 0 4rem 0; } .footnote-wrapper { diff --git a/src/components/basic/Button/Button.js b/src/components/basic/Button/Button.js index 74046556..32c6be40 100644 --- a/src/components/basic/Button/Button.js +++ b/src/components/basic/Button/Button.js @@ -12,6 +12,8 @@ const Button = ({ extraStyleClasses = [], disabled = false, buttonType = 'button', + onMouseEnter, + onMouseLeave, }) => { const classesList = ['btn', ...extraStyleClasses] alternate && classesList.push('alternate') @@ -32,6 +34,8 @@ const Button = ({ data-testid="button" disabled={disabled} type={buttonType} + onMouseEnter={onMouseEnter} + onMouseLeave={onMouseLeave} > {children} diff --git a/src/components/basic/Logo/Logo.css b/src/components/basic/Logo/Logo.css index 994f1cd4..7a954093 100644 --- a/src/components/basic/Logo/Logo.css +++ b/src/components/basic/Logo/Logo.css @@ -8,7 +8,7 @@ } .logoContainer .logo { - width: 64px; + width: 60px; margin: 0; } diff --git a/src/components/basic/Logo/Logo.js b/src/components/basic/Logo/Logo.js index 24a17dc8..0f4d7294 100644 --- a/src/components/basic/Logo/Logo.js +++ b/src/components/basic/Logo/Logo.js @@ -1,6 +1,6 @@ import { useState, useEffect, useContext } from 'react' import { SettingsContext, AccountContext } from '@Contexts' -import LogoIcon from '@Assets/images/logo96.png' +import LogoIcon from '@Assets/images/logo.svg' import { AppInfo } from '@Constants' import './Logo.css' diff --git a/src/components/basic/Tooltip/Tooltip.css b/src/components/basic/Tooltip/Tooltip.css index 7a778b3c..2723a456 100644 --- a/src/components/basic/Tooltip/Tooltip.css +++ b/src/components/basic/Tooltip/Tooltip.css @@ -1,20 +1,70 @@ .tooltip { position: absolute; - top: 0; - left: 34px; display: flex; align-items: center; justify-content: center; - width: 110px; + width: max-content; + max-width: 110px; + max-height: fit-content; padding: 4px 8px; background: rgb(var(--color-green)); border-radius: 5px; color: rgb(var(--color-white)); font-size: 0.8rem; - opacity: 0; + transition: opacity 0.7s ease; } -.tooltip.visible { - opacity: 1; - transition: opacity 0.7s ease; +.top { + bottom: 100%; + left: 50%; + transform: translateX(-50%); +} + +.bottom { + top: 110%; + left: 50%; + transform: translateX(-50%); +} + +.left { + top: 50%; + right: 110%; + transform: translateY(-50%); +} + +.right { + top: 50%; + left: 110%; + transform: translateY(-50%); +} + +.topLeft { + bottom: 100%; + right: 100%; +} + +.topRight { + bottom: 100%; + left: 100%; +} + +.bottomLeft { + top: 110%; + right: 100%; +} + +.bottomRight { + top: 110%; + left: 100%; +} + +.hidden { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + border: 0; + padding: 0; + clip: rect(0 0 0 0); + overflow: hidden; } diff --git a/src/components/basic/Tooltip/Tooltip.js b/src/components/basic/Tooltip/Tooltip.js index 87ceb225..475744a7 100644 --- a/src/components/basic/Tooltip/Tooltip.js +++ b/src/components/basic/Tooltip/Tooltip.js @@ -1,9 +1,21 @@ import './Tooltip.css' -const Tooltip = ({ message, visible }) => { +const Tooltip = ({ message, visible, position }) => { + const orientation = [ + 'top', + 'bottom', + 'left', + 'right', + 'topLeft', + 'topRight', + 'bottomLeft', + 'bottomRight', + ] return ( {message} diff --git a/src/components/basic/Tooltip/Tooltip.test.js b/src/components/basic/Tooltip/Tooltip.test.js index 7911ece7..26703b21 100644 --- a/src/components/basic/Tooltip/Tooltip.test.js +++ b/src/components/basic/Tooltip/Tooltip.test.js @@ -2,31 +2,51 @@ import { render, screen } from '@testing-library/react' import Tooltip from './Tooltip' describe('Tooltip', () => { - it('renders correctly when visible', () => { + it('renders the tooltip message', () => { render( , ) + const tooltipElement = screen.getByText(/Test message/i) + expect(tooltipElement).toBeInTheDocument() + }) + it('applies the correct position class', () => { + render( + , + ) const tooltipElement = screen.getByTestId('tooltip') - expect(tooltipElement).toHaveTextContent('Test tooltip') - expect(tooltipElement).toHaveClass('tooltip') - expect(tooltipElement).toHaveClass('visible') + expect(tooltipElement).toHaveClass('top') }) - it('renders correctly when not visible', () => { + it('applies the hidden class when not visible', () => { render( , ) + const tooltipElement = screen.getByTestId('tooltip') + expect(tooltipElement).toHaveClass('hidden') + }) + it('defaults to the bottom position if an invalid position is provided', () => { + render( + , + ) const tooltipElement = screen.getByTestId('tooltip') - expect(tooltipElement).toHaveTextContent('Test tooltip') - expect(tooltipElement).toHaveClass('tooltip') - expect(tooltipElement).not.toHaveClass('visible') + expect(tooltipElement).toHaveClass('bottom') }) }) diff --git a/src/components/composed/AddWallet/AddWallet.js b/src/components/composed/AddWallet/AddWallet.js index 4fad1789..34260b35 100644 --- a/src/components/composed/AddWallet/AddWallet.js +++ b/src/components/composed/AddWallet/AddWallet.js @@ -28,7 +28,7 @@ const AddWallet = ({ const getMnemonics = () => wordsFields.reduce((acc, word) => `${acc} ${word.value}`, '').trim() - const submitButtonTitle = step === 3 ? 'Create Wallet' : 'Next' + const submitButtonTitle = step === 3 ? 'Add Wallet' : 'Next' const changePassHandle = (value) => { setPass(value) diff --git a/src/components/composed/AddWallet/AddWallet.test.js b/src/components/composed/AddWallet/AddWallet.test.js index 71a8a65e..e7ea089f 100644 --- a/src/components/composed/AddWallet/AddWallet.test.js +++ b/src/components/composed/AddWallet/AddWallet.test.js @@ -102,7 +102,7 @@ describe('AddWallet', () => { const submitButton = screen.getByTestId('button') expect(submitButton).toBeInTheDocument() - expect(submitButton).toHaveTextContent('Create Wallet') + expect(submitButton).toHaveTextContent('Add Wallet') }) test('changes step on submit button click', () => { diff --git a/src/components/composed/Header/Header.css b/src/components/composed/Header/Header.css index d3a63e30..aa188146 100644 --- a/src/components/composed/Header/Header.css +++ b/src/components/composed/Header/Header.css @@ -26,6 +26,16 @@ header { } .settings { + height: 40px; + width: 40px; + padding: 0.2rem; + /* position: absolute; */ + top: 0.5rem; + right: 45px; + outline: none; +} + +.expand { height: 40px; width: 40px; padding: 0.2rem; @@ -39,3 +49,20 @@ header { width: 100%; height: 100%; } + +.expand-wrapped { + position: absolute; + display: flex; + top: 0.5rem; + right: 0; + overflow: visible; +} + +.expand-wrapped-unlocked { + right: 40px; +} + +.tooltipWrapper { + position: relative; + overflow: visible; +} diff --git a/src/components/composed/Header/Header.js b/src/components/composed/Header/Header.js index 162398fb..2a1c7b44 100644 --- a/src/components/composed/Header/Header.js +++ b/src/components/composed/Header/Header.js @@ -1,20 +1,24 @@ +/* eslint-disable no-undef */ import React, { useContext, useEffect, useState } from 'react' import { useNavigate, useLocation } from 'react-router-dom' import { ReactComponent as BackImg } from '@Assets/images/back-button.svg' import { ReactComponent as LogoutImg } from '@Assets/images/logout.svg' +import { ReactComponent as ExpandImg } from '@Assets/images/icon-expand.svg' import { ReactComponent as SettingsImg } from '@Assets/images/settings.svg' -import { Button, Logo } from '@BasicComponents' +import { Button, Logo, Tooltip } from '@BasicComponents' import { AccountContext } from '@Contexts' import './Header.css' const Header = ({ customBackAction, noBackButton = false }) => { const [unlocked, setUnlocked] = useState(false) + const [tooltipVisible, setTooltipVisible] = useState(false) + const tooltipMessage = 'Expand view' const navigate = useNavigate() const location = useLocation() - const { isAccountUnlocked, logout } = useContext(AccountContext) + const { isAccountUnlocked, logout, isExtended } = useContext(AccountContext) const isWalletPage = location.pathname === '/wallet' useEffect(() => { @@ -40,6 +44,20 @@ const Header = ({ customBackAction, noBackButton = false }) => { navigate('/settings') } + const expandHandler = () => { + window.open( + typeof browser !== 'undefined' + ? // eslint-disable-next-line no-undef + browser.runtime.getURL('popup.html') + : chrome.runtime.getURL('popup.html'), + '_blank', + ) + } + + const toggleTooltip = () => { + setTooltipVisible(!tooltipVisible) + } + return (
{!noBackButton && ( @@ -52,15 +70,56 @@ const Header = ({ customBackAction, noBackButton = false }) => { )} + {!unlocked && !isExtended && ( +
+
+ + +
+
+ )} + {unlocked && ( <> - +
+ {!isExtended && ( +
+ + +
+ )} + +
) diff --git a/src/components/composed/HelpTooltip/HelpTooltip.test.js b/src/components/composed/HelpTooltip/HelpTooltip.test.js index cd6a2bfc..692654ad 100644 --- a/src/components/composed/HelpTooltip/HelpTooltip.test.js +++ b/src/components/composed/HelpTooltip/HelpTooltip.test.js @@ -7,6 +7,7 @@ describe('HelpTooltip', () => { , ) @@ -17,15 +18,19 @@ describe('HelpTooltip', () => { fireEvent.mouseEnter(linkElement) let tooltipElement = screen.getByText('Test tooltip') expect(tooltipElement).toBeInTheDocument() - expect(tooltipElement).toHaveClass('visible') fireEvent.mouseLeave(linkElement) tooltipElement = screen.queryByText('Test tooltip') - expect(tooltipElement).not.toHaveClass('visible') + expect(tooltipElement).toHaveClass('hidden') }) it('renders correctly without link and toggles tooltip on mouse enter and leave', () => { - render() + render( + , + ) const divElement = screen.getByTestId('help-tooltip') expect(divElement).toBeInTheDocument() @@ -33,10 +38,9 @@ describe('HelpTooltip', () => { fireEvent.mouseEnter(divElement) let tooltipElement = screen.getByText('Test tooltip') expect(tooltipElement).toBeInTheDocument() - expect(tooltipElement).toHaveClass('visible') fireEvent.mouseLeave(divElement) tooltipElement = screen.queryByText('Test tooltip') - expect(tooltipElement).not.toHaveClass('visible') + expect(tooltipElement).toHaveClass('hidden') }) }) diff --git a/src/components/containers/CreateAccount/CreateAccount.test.js b/src/components/containers/CreateAccount/CreateAccount.test.js index 90b9d04b..a44655e2 100644 --- a/src/components/containers/CreateAccount/CreateAccount.test.js +++ b/src/components/containers/CreateAccount/CreateAccount.test.js @@ -26,7 +26,7 @@ test('Renders set account page with step 1', () => { const buttons = screen.getAllByTestId('button') const inputComponent = screen.getByTestId('input') - expect(buttons).toHaveLength(2) + expect(buttons).toHaveLength(3) expect(setAccountComponent).toBeInTheDocument() expect(setAccountForm).toBeInTheDocument() @@ -63,7 +63,7 @@ test('Renders set account page with step 2', async () => { const buttons = screen.getAllByTestId('button') const inputComponent = screen.getByTestId('input') - expect(buttons).toHaveLength(2) + expect(buttons).toHaveLength(3) expect(setAccountComponent).toBeInTheDocument() expect(setAccountForm).toBeInTheDocument() @@ -144,7 +144,7 @@ test('Renders set account page with step 4', () => { const setAccountForm = screen.getByTestId('set-account-form') const buttons = screen.getAllByTestId('button') - expect(buttons).toHaveLength(2) + expect(buttons).toHaveLength(3) expect(descriptionParagraphs).toHaveLength(2) act(() => { @@ -169,7 +169,7 @@ test('Renders set account page with step 5', () => { const buttons = screen.getAllByTestId('button') const inputs = screen.getAllByTestId('input') - expect(buttons).toHaveLength(2) + expect(buttons).toHaveLength(3) expect(inputs).toHaveLength(WORDSSAMPLE.length) const input = inputs[0] @@ -207,7 +207,7 @@ test('Renders set account page with step 6', () => { const buttons = screen.getAllByTestId('button') const inputs = screen.getAllByTestId('input') - expect(buttons).toHaveLength(2) + expect(buttons).toHaveLength(3) expect(inputs).toHaveLength(WORDSSAMPLE.length) inputs.forEach((input, index) => { diff --git a/src/components/containers/Login/Login.css b/src/components/containers/Login/Login.css index b31a683c..cdf77666 100644 --- a/src/components/containers/Login/Login.css +++ b/src/components/containers/Login/Login.css @@ -6,7 +6,7 @@ flex-direction: column; } .subtitle { - margin-top: 1rem; + margin-top: 4rem; margin-bottom: 0.5rem; font-size: 24px; font-weight: lighter; diff --git a/src/components/containers/Login/Login.js b/src/components/containers/Login/Login.js index 634fcfaa..4ccf1abc 100644 --- a/src/components/containers/Login/Login.js +++ b/src/components/containers/Login/Login.js @@ -25,7 +25,7 @@ const Login = ({ accounts, onSelect, onCreate }) => { /> - + diff --git a/src/components/containers/Login/Login.test.js b/src/components/containers/Login/Login.test.js index 5c206fed..560d539d 100644 --- a/src/components/containers/Login/Login.test.js +++ b/src/components/containers/Login/Login.test.js @@ -15,9 +15,7 @@ test('Renders List Accounts page', () => { expect( screen.getByRole('button', { name: 'Account Name' }), ).toBeInTheDocument() - expect( - screen.getByRole('button', { name: 'Create Wallet' }), - ).toBeInTheDocument() + expect(screen.getByRole('button', { name: 'Add Wallet' })).toBeInTheDocument() }) test('Render Carousel onSelect', () => { @@ -30,6 +28,6 @@ test('Render Carousel onSelect', () => { test('Render button onCreate', () => { render() - fireEvent.click(screen.getByText('Create Wallet')) + fireEvent.click(screen.getByText('Add Wallet')) expect(data.onCreate).toHaveBeenCalled() }) diff --git a/src/components/containers/RestoreAccount/RestoreAccount.test.js b/src/components/containers/RestoreAccount/RestoreAccount.test.js index 4fb29bd1..80f2d556 100644 --- a/src/components/containers/RestoreAccount/RestoreAccount.test.js +++ b/src/components/containers/RestoreAccount/RestoreAccount.test.js @@ -28,7 +28,7 @@ test('Renders restore account page with step 1', () => { const buttons = screen.getAllByTestId('button') const inputComponent = screen.getByTestId('input') - expect(buttons).toHaveLength(2) + expect(buttons).toHaveLength(3) expect(RestoreAccountComponent).toBeInTheDocument() expect(restoreAccountForm).toBeInTheDocument() @@ -69,7 +69,7 @@ test('Renders restore account page with step 2', () => { const buttons = screen.getAllByTestId('button') const inputComponent = screen.getByTestId('input') - expect(buttons).toHaveLength(2) + expect(buttons).toHaveLength(3) expect(RestoreAccountComponent).toBeInTheDocument() expect(restoreAccountForm).toBeInTheDocument() @@ -150,7 +150,7 @@ test('Renders set account page with step 3', () => { const restoreAccountForm = screen.getByTestId('restore-account-form') const buttons = screen.getAllByTestId('button') - expect(buttons).toHaveLength(2) + expect(buttons).toHaveLength(3) expect(descriptionParagraph).toHaveLength(1) act(() => { @@ -189,7 +189,7 @@ test('Renders restore account page with step 4', () => { const buttons = screen.getAllByTestId('button') const inputs = screen.getAllByTestId('input') - expect(buttons).toHaveLength(2) + expect(buttons).toHaveLength(3) expect(inputs).toHaveLength(24) inputs.forEach((input) => expect(input).toHaveAttribute('type', 'text')) @@ -243,7 +243,7 @@ test('Renders set account page with step 6', () => { const buttons = screen.getAllByTestId('button') const legacyRadioButton = screen.getByRole('button', { name: /Legacy/i }) - expect(buttons).toHaveLength(5) + expect(buttons).toHaveLength(6) expect(descriptionParagraph).toHaveLength(1) expect(legacyRadioButton).toBeInTheDocument() diff --git a/src/contexts/AccountProvider/AccountProvider.js b/src/contexts/AccountProvider/AccountProvider.js index 2a34c491..49367bb6 100644 --- a/src/contexts/AccountProvider/AccountProvider.js +++ b/src/contexts/AccountProvider/AccountProvider.js @@ -12,6 +12,7 @@ const AccountProvider = ({ value: propValue, children }) => { const [lines, setLines] = useState([]) const [entropy, setEntropy] = useState([]) const [balanceLoading, setBalanceLoading] = useState(false) + const isExtended = window.location.href.includes('popup.html') const accountRegistryName = 'unlockedAccount' const loginTimeoutInMinutes = 30 @@ -87,6 +88,7 @@ const AccountProvider = ({ value: propValue, children }) => { setWalletType, balanceLoading, setBalanceLoading, + isExtended, } useEffect(() => { diff --git a/src/index.js b/src/index.js index 0e082ad7..6a62d3b4 100644 --- a/src/index.js +++ b/src/index.js @@ -31,6 +31,7 @@ import { TransactionProvider, } from '@Contexts' import { ML } from '@Cryptos' +import { LocalStorageService } from '@Storage' import reportWebVitals from './utils/reportWebVitals' @@ -43,7 +44,8 @@ const App = () => { const [errorPopupOpen, setErrorPopupOpen] = useState(false) const location = useLocation() const navigate = useNavigate() - const { logout, isAccountUnlocked, addresses } = useContext(AccountContext) + const { logout, isAccountUnlocked, addresses, isExtended } = + useContext(AccountContext) const [nextAfterUnlock, setNextAfterUnlock] = useState(null) const isConnectionAvailable = async (accountUnlocked) => { @@ -134,6 +136,14 @@ const App = () => { } }, [addresses, isAccountUnlocked, navigate]) + useEffect(() => { + const extendPath = LocalStorageService.getItem('extendPath') + if (isExtended && extendPath) { + navigate(extendPath) + LocalStorageService.removeItem('extendPath') + } + }, [isExtended, navigate]) + const popupButtonClickHandler = () => { setErrorPopupOpen(false) } diff --git a/src/pages/CreateAccount/CreateAccount.js b/src/pages/CreateAccount/CreateAccount.js index 5d763ade..f41bbc1c 100644 --- a/src/pages/CreateAccount/CreateAccount.js +++ b/src/pages/CreateAccount/CreateAccount.js @@ -69,7 +69,7 @@ const CreateAccountPage = () => { return creatingWallet ? ( <> -
+

diff --git a/src/pages/CreateRestore/CreateRestore.css b/src/pages/CreateRestore/CreateRestore.css index cfb00dfe..c036fc8a 100644 --- a/src/pages/CreateRestore/CreateRestore.css +++ b/src/pages/CreateRestore/CreateRestore.css @@ -1,7 +1,7 @@ .title-create { font-size: 24px; font-weight: bold; - margin: 8px 0 62px 0; + margin: 4rem 0 4rem 0; } .footnote-wrapper { diff --git a/src/pages/CreateRestore/CreateRestore.js b/src/pages/CreateRestore/CreateRestore.js index 4458a59b..850b2e5b 100644 --- a/src/pages/CreateRestore/CreateRestore.js +++ b/src/pages/CreateRestore/CreateRestore.js @@ -1,16 +1,37 @@ -import React from 'react' +import { useContext } from 'react' import { useNavigate } from 'react-router-dom' import { Button } from '@BasicComponents' import { VerticalGroup, CenteredLayout } from '@LayoutComponents' +import { AccountContext } from '@Contexts' import './CreateRestore.css' +import { LocalStorageService } from '@Storage' const CreateRestorePage = () => { + const { isExtended } = useContext(AccountContext) const navigate = useNavigate() - const goToSetAccountPage = () => navigate('/set-account') - const goToRestoreAccountPage = () => navigate('/restore-account') + const expandHandler = (dest) => { + window.open( + typeof browser !== 'undefined' + ? // eslint-disable-next-line no-undef + browser.runtime.getURL('popup.html') + : // eslint-disable-next-line no-undef + chrome.runtime.getURL('popup.html'), + '_blank', + LocalStorageService.setItem('extendPath', dest), + ) + } + + const goToSetAccountPage = () => { + isExtended ? navigate('/set-account') : expandHandler('/set-account') + } + const goToRestoreAccountPage = () => { + isExtended + ? navigate('/restore-account') + : expandHandler('/restore-account') + } return (
diff --git a/src/pages/Home/Home.js b/src/pages/Home/Home.js index a7bcc929..2deb94a3 100644 --- a/src/pages/Home/Home.js +++ b/src/pages/Home/Home.js @@ -5,7 +5,7 @@ import { AccountContext } from '@Contexts' import { AppInfo } from '@Constants' import { Loading } from '@ComposedComponents' import { CreateRestorePage, LoginPage } from '@Pages' -import Logo from '@Assets/images/logo96.png' +import { Header } from '@ComposedComponents' import './Home.css' @@ -50,14 +50,7 @@ const HomePage = () => { return ( !unlocked && ( <> -
- Mojito Logo -

Mojito

-
+
) From bfb8a140628239c1f04668fcdd19bbb2d59dbf9b Mon Sep 17 00:00:00 2001 From: Sergey Date: Tue, 2 Apr 2024 15:40:41 +0200 Subject: [PATCH 4/5] fix tests (#135) --- src/components/composed/Balance/Balance.test.js | 2 +- src/utils/Helpers/ML/ML.test.js | 17 ++++++++++++----- src/utils/Helpers/ML/MLTransaction.test.js | 4 ++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/components/composed/Balance/Balance.test.js b/src/components/composed/Balance/Balance.test.js index 29b8f185..d9d733a2 100644 --- a/src/components/composed/Balance/Balance.test.js +++ b/src/components/composed/Balance/Balance.test.js @@ -73,5 +73,5 @@ test('renders balance with zero value when networkType is testnet', () => { expect(currantBalanceComponent).toBeInTheDocument() expect(balanceParagraphs[0].textContent).toBe(BALANCE_SAMPLE + ' BTC') - expect(balanceParagraphs[1].textContent).toBe('0,00 USD') + expect(balanceParagraphs[1].textContent).toBe('0.00 USD') }) diff --git a/src/utils/Helpers/ML/ML.test.js b/src/utils/Helpers/ML/ML.test.js index a5789c58..47683a96 100644 --- a/src/utils/Helpers/ML/ML.test.js +++ b/src/utils/Helpers/ML/ML.test.js @@ -25,7 +25,9 @@ describe('ML', () => { it('should convert amount in coins to atoms', () => { const coins = 1 const expectedAtoms = AppInfo.ML_ATOMS_PER_COIN - expect(getAmountInAtoms(coins)).toEqual(expectedAtoms) + expect(getAmountInAtoms(coins).toString()).toEqual( + expectedAtoms.toString(), + ) }) }) @@ -38,7 +40,10 @@ describe('ML', () => { utxo: { destination: 'address2', type: 'Transfer', - value: { amount: 100000000 }, + value: { amount: { + atoms: '100000000', + decimal: '0.001' + } }, }, }, ], @@ -47,12 +52,14 @@ describe('ML', () => { destination: 'address1', value: { amount: AppInfo.ML_ATOMS_PER_COIN }, }, - { destination: 'address2', value: { amount: 200000000 } }, + { destination: 'address2', value: { amount: { + atoms: '200000000', decimal: '0.002' + } } }, ], timestamp: 1000, confirmations: 1, txid: 'txid1', - fee: 10000, + fee: { atoms: '10000', decimal: '0.0000001' }, }, ] const addresses = ['address1'] @@ -66,7 +73,7 @@ describe('ML', () => { confirmations: 1, date: 1000, txid: 'txid1', - fee: 10000, + fee: '0.0000001', isConfirmed: true, type: 'Transfer', sameWalletTransaction: false, diff --git a/src/utils/Helpers/ML/MLTransaction.test.js b/src/utils/Helpers/ML/MLTransaction.test.js index 5ebd133c..0a047728 100644 --- a/src/utils/Helpers/ML/MLTransaction.test.js +++ b/src/utils/Helpers/ML/MLTransaction.test.js @@ -71,7 +71,7 @@ const UTXSOS_MOCK = [ describe('getUtxoBalance', () => { it('should return the sum of the utxo values', () => { const result = getUtxoBalance(UTXSOS_MOCK) - expect(result).toBe(600) + expect(result.toString()).toBe('600') }) }) @@ -81,7 +81,7 @@ describe('getUtxoTransaction', () => { result.forEach((item, index) => { expect(item.transaction).toEqual( - UTXSOS_MOCK[index].outpoint.id.Transaction, + UTXSOS_MOCK[index].outpoint.source_id, ) expect(item.index).toEqual(UTXSOS_MOCK[index].outpoint.index) }) From 56d49ebe14b46e0f901f433ed828063d575a5015 Mon Sep 17 00:00:00 2001 From: owlsua Date: Tue, 2 Apr 2024 15:49:50 +0200 Subject: [PATCH 5/5] update version to 1.2.2 --- package-lock.json | 4 ++-- package.json | 2 +- public/manifestDefault.json | 2 +- public/manifestFirefox.json | 2 +- src/pages/CreateRestore/CreateRestore.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb793c0f..d9610d58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "browser-extension", - "version": "1.2.1", + "version": "1.2.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "browser-extension", - "version": "1.2.1", + "version": "1.2.2", "dependencies": { "@mintlayer/entropy-generator": "^1.0.2", "@testing-library/jest-dom": "^6.1.4", diff --git a/package.json b/package.json index 83374f70..414a1f9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "browser-extension", - "version": "1.2.1", + "version": "1.2.2", "private": true, "dependencies": { "@mintlayer/entropy-generator": "^1.0.2", diff --git a/public/manifestDefault.json b/public/manifestDefault.json index bd4e266d..90676133 100644 --- a/public/manifestDefault.json +++ b/public/manifestDefault.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Mojito - A Mintlayer Wallet", - "version": "1.2.1", + "version": "1.2.2", "short_name": "Mojito", "description": "Mojito is a non-custodial decentralized crypto wallet that lets you send and receive BTC and ML from any other address.", "homepage_url": "https://www.mintlayer.org/", diff --git a/public/manifestFirefox.json b/public/manifestFirefox.json index d1810888..adf431d4 100644 --- a/public/manifestFirefox.json +++ b/public/manifestFirefox.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Mojito - A Mintlayer Wallet", - "version": "1.2.1", + "version": "1.2.2", "description": "Mojito is a non-custodial decentralized crypto wallet that lets you send and receive BTC and ML from any other address.", "homepage_url": "https://www.mintlayer.org/", "icons": { diff --git a/src/pages/CreateRestore/CreateRestore.js b/src/pages/CreateRestore/CreateRestore.js index 850b2e5b..ab17d6b8 100644 --- a/src/pages/CreateRestore/CreateRestore.js +++ b/src/pages/CreateRestore/CreateRestore.js @@ -68,7 +68,7 @@ const CreateRestorePage = () => { className="footnote-version" data-testid="footnote-name" > - v1.2.1 + v1.2.2