diff --git a/cashtab/src/components/Alias/Alias.js b/cashtab/src/components/Alias/Alias.js
index e5f136b76..1c1a5014e 100644
--- a/cashtab/src/components/Alias/Alias.js
+++ b/cashtab/src/components/Alias/Alias.js
@@ -1,531 +1,519 @@
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { WalletContext } from 'utils/context';
import PropTypes from 'prop-types';
import WalletLabel from 'components/Common/WalletLabel.js';
import {
ZeroBalanceHeader,
SidePaddingCtn,
WalletInfoCtn,
} from 'components/Common/Atoms';
import {
AntdFormWrapper,
AliasInput,
AliasAddressInput,
CashtabCheckbox,
} from 'components/Common/EnhancedInputs';
import { Form, Modal } from 'antd';
import { SmartButton } from 'components/Common/PrimaryButton';
import BalanceHeader from 'components/Common/BalanceHeader';
import BalanceHeaderFiat from 'components/Common/BalanceHeaderFiat';
import { Row, Col } from 'antd';
import { UserOutlined } from '@ant-design/icons';
import {
getWalletState,
fromSatoshisToXec,
convertToEcashPrefix,
} from 'utils/cashMethods';
import { isAliasAvailable, isAddressRegistered } from 'utils/chronik';
import { currency } from 'components/Common/Ticker.js';
import { registerNewAlias } from 'utils/transactions';
import {
errorNotification,
registerAliasNotification,
} from 'components/Common/Notifications';
import { isAliasFormat, isValidAliasString } from 'utils/validation';
import { getAliasByteSize, getAliasRegistrationFee } from 'utils/aliasUtils';
import cashaddr from 'ecashaddrjs';
export const CheckboxContainer = styled.div`
text-align: left;
margin-bottom: 12px;
`;
export const NamespaceCtn = styled.div`
width: 100%;
margin-top: 50px;
margin-bottom: 20px;
overflow-wrap: break-word;
h2 {
color: ${props => props.theme.contrast};
margin: 0 0 20px;
}
h3 {
color: ${props => props.theme.contrast};
margin: 0 0 10px;
}
white-space: pre-wrap;
`;
const StyledSpacer = styled.div`
height: 1px;
width: 100%;
background-color: ${props => props.theme.lightWhite};
margin: 60px 0 50px;
`;
const Alias = ({ passLoadingStatus }) => {
const ContextValue = React.useContext(WalletContext);
const {
wallet,
fiatPrice,
cashtabSettings,
chronik,
changeCashtabSettings,
cashtabCache,
- isAliasServerOnline,
} = ContextValue;
const walletState = getWalletState(wallet);
const { balances } = walletState;
const [formData, setFormData] = useState({
aliasName: '',
aliasAddress: '',
});
const [useThisAddressChecked, setUseThisAddressChecked] = useState(false);
const [isValidAliasInput, setIsValidAliasInput] = useState(false); // tracks whether to activate the registration button
const [isValidAliasAddressInput, setIsValidAliasAddressInput] =
useState(false); // tracks whether to activate the registration button
const [aliasValidationError, setAliasValidationError] = useState(false);
const [aliasAddressValidationError, setAliasAddressValidationError] =
useState(false);
const [activeWalletAliases, setActiveWalletAliases] = useState([]); // stores the list of aliases registered to this active wallet
const [aliasLength, setAliasLength] = useState(false); // real time tracking of alias char length
const [aliasFee, setAliasFee] = useState(false); // real time tracking of alias registration fee
// Show a confirmation modal on alias registrations
const [isModalVisible, setIsModalVisible] = useState(false);
useEffect(() => {
passLoadingStatus(false);
}, [balances.totalBalance]);
useEffect(async () => {
// only run this useEffect block if wallet or cashtabCache is defined
if (
!wallet ||
typeof wallet === 'undefined' ||
!cashtabCache ||
typeof cashtabCache === 'undefined'
) {
return;
}
passLoadingStatus(true);
// Default to registering the user's active wallet
// Must be called in this useEffect to ensure that wallet is loaded
// Call with this function to ensure that checkbox state and checkbox are updated
handleDefaultAddressCheckboxChange({ target: { checked: true } });
// check whether the address is attached to an onchain alias on page load
const walletHasAlias = isAddressRegistered(
wallet,
cashtabCache.aliasCache,
);
// retrieve aliases for this active wallet from cache for rendering on the frontend
if (
walletHasAlias &&
cashtabCache.aliasCache &&
cashtabCache.aliasCache.cachedAliasCount > 0
) {
const thisAddress = convertToEcashPrefix(
wallet.Path1899.cashAddress,
);
// filter for aliases that matches this wallet's address
const registeredAliasesToWallet =
cashtabCache.aliasCache.aliases.filter(
alias => alias.address === thisAddress,
);
setActiveWalletAliases([...new Set(registeredAliasesToWallet)]); // new Set() removes duplicate entries
}
passLoadingStatus(false);
}, [wallet.name, cashtabCache.aliasCache.aliases]);
const handleOk = () => {
setIsModalVisible(false);
registerAlias();
};
const handleCancel = () => {
setIsModalVisible(false);
};
const registerAlias = async () => {
passLoadingStatus(true);
- if (!isAliasServerOnline) {
- // error notification on alias-server being unavailable
- errorNotification(
- null,
- 'Unable to connect to alias server, please try again later',
- 'Alias-server status check',
- );
- passLoadingStatus(false);
- return;
- }
-
// note: input already validated via handleAliasNameInput()
const aliasInput = formData.aliasName;
const aliasAddress = formData.aliasAddress;
// check if the user is trying to essentially register chicken.xec.xec
const doubleExtensionInput = isAliasFormat(aliasInput);
if (doubleExtensionInput) {
errorNotification(
null,
'Please input an alias without the ".xec"',
'Alias extension check',
);
passLoadingStatus(false);
return;
}
const aliasAvailable = await isAliasAvailable(
aliasInput,
cashtabCache.aliasCache,
);
if (aliasAvailable) {
// calculate registration fee based on chars
const registrationFee = getAliasRegistrationFee(aliasInput);
console.log(
'Registration fee for ' +
aliasInput +
' is ' +
registrationFee +
' sats.',
);
console.log(
`Alias ${aliasInput} is available. Broadcasting registration transaction.`,
);
try {
const result = await registerNewAlias(
chronik,
wallet,
currency.defaultFee,
aliasInput,
aliasAddress,
registrationFee,
);
registerAliasNotification(result.explorerLink, aliasInput);
} catch (err) {
handleAliasRegistrationError(err);
}
setIsValidAliasInput(true);
} else {
// error notification on alias being unavailable
errorNotification(
null,
'This alias [' +
aliasInput +
'] has already been taken, please try another alias',
'Alias availability check',
);
}
passLoadingStatus(false);
};
const handleAliasNameInput = e => {
const { name, value } = e.target;
const validAliasInput = isValidAliasString(value);
const aliasInputByteSize = getAliasByteSize(value);
if (
value &&
value.trim() !== '' &&
aliasInputByteSize <= currency.aliasSettings.aliasMaxLength &&
validAliasInput
) {
setIsValidAliasInput(true);
const registrationFee = getAliasRegistrationFee(value);
setAliasFee(registrationFee);
setAliasLength(aliasInputByteSize);
setAliasValidationError(false);
} else {
setAliasValidationError(
'Please enter an alias (lowercase a-z, 0-9) between 1 and 21 bytes',
);
setIsValidAliasInput(false);
setAliasFee(false);
setAliasLength(false);
}
setFormData(p => ({
...p,
[name]: value,
}));
};
const handleDefaultAddressCheckboxChange = e => {
/* handleDefaultAddressCheckboxChange
*
* Function to handle user action of checking or unchecking the
* checkbox on this page labeled 'Register active wallet address'
*
* May be called programmatically by mocking the usual js event
* of a user checking the box
*
* If the box is checked, set formData for aliasAddress to the active wallet's address
* If the box is unchecked, clear formData for aliasAddress
*/
const checked = e.target.checked;
setUseThisAddressChecked(checked);
if (checked) {
// Set address of active wallet to default alias registration address
handleAliasAddressInput({
target: {
name: 'aliasAddress',
value: wallet.Path1899.cashAddress,
},
});
} else {
// Clear the form if the user unchecks
handleAliasAddressInput({
target: {
name: 'aliasAddress',
value: '',
},
});
}
};
const handleAliasAddressInput = e => {
/* handleAliasAddressInput
*
* Function called to handle any changes to the aliasAddress input form
*
* May be called programmatically by mocking the usual js event
* of a user updating the addressName input field
*/
let { name, value } = e.target;
// remove any whitespaces
value = value.trim();
// Validate
let decoded;
let isValidAddress = false;
try {
decoded = cashaddr.decode(value, true);
const { hash } = decoded;
// We only support 20-byte payloads
isValidAddress = hash.length === 40;
} catch (err) {
// Invalid cashaddress
// Log to console for user support
console.log(`Invalid address`, err);
}
if (isValidAddress) {
setIsValidAliasAddressInput(true);
setAliasAddressValidationError(false);
} else {
setAliasAddressValidationError(
'Invalid alias registration address.',
);
setIsValidAliasAddressInput(false);
}
setFormData(p => ({
...p,
[name]: value,
}));
};
function handleAliasRegistrationError(errorObj) {
// Set loading to false here as well, as balance may not change depending on where error occured in try loop
passLoadingStatus(false);
let message;
if (
errorObj.error &&
errorObj.error.includes(
'too-long-mempool-chain, too many unconfirmed ancestors [limit: 50] (code 64)',
)
) {
message = `The address you are trying to register has too many unconfirmed ancestors (limit 50). Registration will be possible after a block confirmation. Try again in about 10 minutes.`;
} else {
message =
errorObj.message || errorObj.error || JSON.stringify(errorObj);
}
errorNotification(errorObj, message, 'Registering Alias');
}
return (
<>
{`Are you sure you want to register the alias '${
formData.aliasName
}' for ${fromSatoshisToXec(aliasFee)} XECs?`}
{!balances.totalBalance ? (
You currently have 0 {currency.ticker}
Deposit some funds to use this feature
) : (
<>
{fiatPrice !== null && (
)}
>
)}
eCash Namespace Alias
handleAliasNameInput(e),
required: true,
}}
/>
Register active wallet address
{!useThisAddressChecked && (
handleAliasAddressInput(
e,
),
required: true,
}}
/>
)}
{aliasLength &&
aliasFee &&
`Registration fee for this ${aliasLength} byte Alias is ${fromSatoshisToXec(
aliasFee,
)} XEC`}
setIsModalVisible(true)
}
>
Register Alias
Registered aliases
{activeWalletAliases &&
activeWalletAliases.length > 0
? activeWalletAliases
.map(
alias => alias.alias + '.xec',
)
.join('\n')
: 'N/A'}
>
);
};
/*
passLoadingStatus must receive a default prop that is a function
in order to pass the rendering unit test in Alias.test.js
status => {console.log(status)} is an arbitrary stub function
*/
Alias.defaultProps = {
passLoadingStatus: status => {
console.log(status);
},
};
Alias.propTypes = {
passLoadingStatus: PropTypes.func,
};
export default Alias;