Page MenuHomePhabricator

D17399.diff
No OneTemporary

D17399.diff

diff --git a/cashtab/package-lock.json b/cashtab/package-lock.json
--- a/cashtab/package-lock.json
+++ b/cashtab/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "cashtab",
- "version": "3.4.5",
+ "version": "3.4.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cashtab",
- "version": "3.4.5",
+ "version": "3.4.6",
"dependencies": {
"@bitgo/utxo-lib": "^11.0.0",
"@zxing/browser": "^0.1.4",
diff --git a/cashtab/package.json b/cashtab/package.json
--- a/cashtab/package.json
+++ b/cashtab/package.json
@@ -1,6 +1,6 @@
{
"name": "cashtab",
- "version": "3.4.5",
+ "version": "3.4.6",
"private": true,
"scripts": {
"start": "node scripts/start.js",
diff --git a/cashtab/src/alias/__tests__/index.test.js b/cashtab/src/alias/__tests__/index.test.js
deleted file mode 100644
--- a/cashtab/src/alias/__tests__/index.test.js
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright (c) 2024 The Bitcoin developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-'use strict';
-import { queryAliasServer } from 'alias';
-import {
- mockAddressApiResponse,
- mockAliasApiResponse,
- mockUnregisteredAliasApiResponse,
-} from 'alias/fixtures/mocks';
-import { when } from 'jest-when';
-import aliasSettings from 'config/alias';
-
-test('queryAliasServer() correctly throws a network error for server downtime or a malformed fetch url', async () => {
- const endPoint = 'address';
- const address = 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj';
- const fetchUrl = `bad-url-to-simulate-server-downtime/${endPoint}/${address}`;
- const expectedError = 'Network request failed';
-
- // mock the fetch call to alias-server's '/address' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({ error: expectedError });
-
- await expect(queryAliasServer(endPoint, address)).rejects.toThrow(
- expectedError,
- );
-});
-
-test('queryAliasServer() correctly returns an array of alias objects for a valid eCash address that has registered aliases', async () => {
- const endPoint = 'address';
- const address = 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj';
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/${endPoint}/${address}`;
-
- // mock the fetch call to alias-server's '/address' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve({ mockAddressApiResponse }),
- });
-
- expect(await queryAliasServer(endPoint, address)).toEqual({
- mockAddressApiResponse,
- });
-});
-
-test('queryAliasServer() correctly returns an array of alias objects for a valid prefix-less eCash address that has registered aliases', async () => {
- const endPoint = 'address';
- const address = 'qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj';
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/${endPoint}/${address}`;
-
- // mock the fetch call to alias-server's '/address' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve({ mockAddressApiResponse }),
- });
-
- expect(await queryAliasServer(endPoint, address)).toEqual({
- mockAddressApiResponse,
- });
-});
-
-test('queryAliasServer() returns an empty array for a valid eCash address with no aliases', async () => {
- const endPoint = 'address';
- const address = 'ecash:qr2nyffmenvrzea3aqhqw0rd3ckk0kkcrgsrugzjph';
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/${endPoint}/${address}`;
-
- // mock the fetch call to alias-server's '/address' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve([]),
- });
-
- expect(await queryAliasServer(endPoint, address)).toEqual([]);
-});
-
-test('queryAliasServer() throws an error for an invalid eCash address', async () => {
- const endPoint = 'address';
- const invalidAddress = 'qpmytrdsaINVALIDDDDDDD7cjctmjasj';
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/${endPoint}/${invalidAddress}`;
- const expectedError = `Error fetching /address/${invalidAddress}: Input must be a valid eCash address`;
-
- // mock the fetch call to alias-server's '/address' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({ error: expectedError });
-
- await expect(queryAliasServer(endPoint, invalidAddress)).rejects.toThrow(
- expectedError,
- );
-});
-
-test('queryAliasServer() returns an alias object for a registered alias', async () => {
- const endPoint = 'alias';
- const alias = 'twelvechar12';
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/${endPoint}/${alias}`;
-
- // mock the fetch call to alias-server's '/alias' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve({ mockAliasApiResponse }),
- });
-
- expect(await queryAliasServer(endPoint, alias)).toEqual({
- mockAliasApiResponse,
- });
-});
-
-test('queryAliasServer() returns an api error for a non-alphanumeric alias', async () => {
- const endPoint = 'alias';
- const alias = '@@@@@@@@@@@@';
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/${endPoint}/${alias}`;
- const expectedError = `Error fetching /alias/${alias}: alias param cannot contain non-alphanumeric characters`;
-
- // mock the fetch call to alias-server's '/alias' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({ error: expectedError });
-
- await expect(queryAliasServer(endPoint, alias)).rejects.toThrow(
- expectedError,
- );
-});
-
-test('queryAliasServer() returns a valid object for an unregistered alias', async () => {
- const endPoint = 'alias';
- const alias = 'foobar';
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/${endPoint}/${alias}`;
-
- // mock the fetch call to alias-server's '/alias' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve({ mockUnregisteredAliasApiResponse }),
- });
-
- expect(await queryAliasServer(endPoint, alias)).toEqual({
- mockUnregisteredAliasApiResponse,
- });
-});
-
-test('queryAliasServer() returns an error for an alias longer than 21 characters', async () => {
- const endPoint = 'alias';
- const alias = 'foobarrrrrrrrrrrrrrrrrrrrrrrrrrr';
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/${endPoint}/${alias}`;
- const expectedError = `Error fetching /alias/${alias}: alias param must be between 1 and 21 characters in length`;
-
- // mock the fetch call to alias-server's '/alias' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({ error: expectedError });
-
- await expect(queryAliasServer(endPoint, alias)).rejects.toThrow(
- expectedError,
- );
-});
diff --git a/cashtab/src/alias/fixtures/mocks.js b/cashtab/src/alias/fixtures/mocks.js
deleted file mode 100644
--- a/cashtab/src/alias/fixtures/mocks.js
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2024 The Bitcoin developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-export const mockAddressApiResponse = [
- {
- alias: '1',
- address: 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj',
- txid: 'ec92610fc41df2387e7febbb358b138a802ac26023f30b2442aa01ca733fff7d',
- blockheight: 792417,
- },
- {
- alias: '333',
- address: 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj',
- txid: '0c77e4f7e0ff4f1028372042cbeb97eaddb64d505efe960b5a1ca4fce65598e2',
- blockheight: 792418,
- },
- {
- alias: '22',
- address: 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj',
- txid: '922bed591461d74c2f2b177b4a28c29a28f5d81ca6cd2859dd31bec086c6a5e2',
- blockheight: 792418,
- },
- {
- alias: '666666',
- address: 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj',
- txid: '9f6a39b8d2af0d686a6282502f7ad51f4b759a896e28b6e9cc3a116e91347d2f',
- blockheight: 792418,
- },
- {
- alias: 'twelvechar12',
- address: 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj',
- txid: '166b21d4631e2a6ec6110061f351c9c3bfb3a8d4e6919684df7e2824b42b0ffe',
- blockheight: 792419,
- },
- {
- alias: 'addressinvalid',
- address: 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj',
- txid: '6792b6d77f3b12b4f4063fdd6ae372a9b8ff242ff073f264a20d4676ec065751',
- blockheight: 792421,
- },
- {
- alias: 'xx',
- address: 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj',
- txid: 'e9f0a9984b4ae354fb8b4dd8193c974074942b0ee6fba14bf85fa1ca14dc5987',
- blockheight: 792598,
- },
-];
-
-export const mockAliasApiResponse = {
- alias: 'twelvechar12',
- address: 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj',
- txid: '166b21d4631e2a6ec6110061f351c9c3bfb3a8d4e6919684df7e2824b42b0ffe',
- blockheight: 792419,
-};
-
-export const mockUnregisteredAliasApiResponse = {
- alias: 'qwerty',
- isRegistered: false,
- registrationFeeSats: 553,
- processedBlockheight: 802969,
-};
diff --git a/cashtab/src/alias/index.ts b/cashtab/src/alias/index.ts
deleted file mode 100644
--- a/cashtab/src/alias/index.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) 2023-2024 The Bitcoin developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-import aliasSettings from 'config/alias';
-
-/**
- * Queries the alias-server for alias related data via Fetch
- *
- * @param endPoint the alias-server endpoint for this query
- * @param aliasParam a param to be passed to the endPoint
- * @returns a JSON response from alias-server via Fetch
- * @throws err server fetch errors from alias-server
- *
- * Example `/address/<address>` response
- * [
- * {alias: 'foo', address: 'ecash:qpmyt....', txid: 'ec927447...', blockheight: '792417'},
- * {alias: 'foo2', address: 'ecash:qpmyt....', txid: 'ec927447...', blockheight: '792417'},
- * ]
- * Example `/alias/<alias>` response for a registered alias:
- * {
- * alias: 'twelvechar12',
- * address:'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj',
- * txid:'166b21d4631e2a6ec6110061f351c9c3bfb3a8d4e6919684df7e2824b42b0ffe',
- * blockheight:792419,
- * }
- * Example `/alias/<alias>` response for an unregistered alias:
- * {
- * alias: 'asdfasdf',
- * isRegistered: false,
- * registrationFeeSats: 551,
- * processedBlockheight: 802965,
- * }
- */
-
-/**
- * alias types
- * Note we will likely implement in a different way
- */
-
-export interface Alias {
- alias: string;
- address: string;
- isRegistered: boolean;
- registrationFeeSats: number;
- processedBlockheight: number;
-}
-export interface AddressAliasStatus {
- registered: Alias[];
- pending: Alias[];
-}
-export interface AliasPrices {
- prices: string[];
-}
-interface AliasErrorResponse {
- error: string;
-}
-export const queryAliasServer = async (
- endPoint: string,
- aliasParam: boolean | string = false,
-): Promise<AliasPrices | Alias[] | Alias | AddressAliasStatus> => {
- let aliasServerResp;
- const fetchUrl = !aliasParam
- ? `${aliasSettings.aliasServerBaseUrl}/${endPoint}`
- : `${aliasSettings.aliasServerBaseUrl}/${endPoint}/${aliasParam}`;
- try {
- aliasServerResp = await fetch(fetchUrl);
- // if alias-server is down, fetch returns undefined
- if (!aliasServerResp) {
- throw new Error('Network request failed');
- }
- // if alias-server returns a valid error message to the query e.g. address not found
- if ('error' in aliasServerResp) {
- throw new Error(
- (aliasServerResp as unknown as AliasErrorResponse).error,
- );
- }
- return await aliasServerResp.json();
- } catch (err) {
- console.error(
- `queryAliasServer(): Error retrieving alias data from alias-server`,
- err,
- );
- console.error(
- `/${endPoint}/ endpoint output: ${JSON.stringify(aliasServerResp)}`,
- );
- throw err;
- }
-};
diff --git a/cashtab/src/components/Alias/Alias.js b/cashtab/src/components/Alias/Alias.js
deleted file mode 100644
--- a/cashtab/src/components/Alias/Alias.js
+++ /dev/null
@@ -1,631 +0,0 @@
-// Copyright (c) 2024 The Bitcoin developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-import React, { useState, useEffect } from 'react';
-import styled from 'styled-components';
-import { WalletContext } from 'wallet/context';
-import { AlertMsg } from 'components/Common/Atoms';
-import PrimaryButton from 'components/Common/Buttons';
-import { getWalletState } from 'utils/cashMethods';
-import { toXec } from 'wallet';
-import { meetsAliasSpec } from 'validation';
-import { queryAliasServer } from 'alias';
-import { decodeCashAddress } from 'ecashaddrjs';
-import CopyToClipboard from 'components/Common/CopyToClipboard';
-import appConfig from 'config/app';
-import aliasSettings from 'config/alias';
-import { explorer } from 'config/explorer';
-import { getAliasTargetOutput, getAliasByteCount } from 'opreturn';
-import { sendXec } from 'transactions';
-import { hasEnoughToken } from 'wallet';
-import { toast } from 'react-toastify';
-import { AliasInput, Input } from 'components/Common/Inputs';
-import Switch from 'components/Common/Switch';
-import Modal from 'components/Common/Modal';
-import Spinner from 'components/Common/Spinner';
-
-const AliasWrapper = styled.div`
- color: ${props => props.theme.primaryText};
- margin: 12px 0;
- box-sizing: border-box;
- *,
- *:before,
- *:after {
- box-sizing: inherit;
- }
-`;
-export const SwitchContainer = styled.div`
- display: flex;
- margin-bottom: 12px;
- justify-content: flex-start;
- align-items: center;
- gap: 12px;
-`;
-const SwitchLabel = styled.div`
- display: flex;
- font-size: 16px;
-`;
-const AltAddressHolder = styled.div`
- display: flex;
- margin-bottom: 12px;
-`;
-const AliasHeader = styled.div`
- color: ${props => props.theme.primaryText};
- font-weight: bold;
- font-size: 20px;
- margin: 12px 0;
- width: 100%;
-`;
-const Panel = styled.div`
- display: flex;
- flex-wrap: wrap;
- padding: 12px;
- width: 100%;
- background-color: ${props => props.theme.secondaryBackground};
- border-radius: 9px;
- margin-bottom: 12px;
- gap: 12px;
-`;
-const AliasTag = styled.div`
- display: flex;
- color: ${props => props.theme.primaryText};
- background-color: #0074c2;
- &:hover {
- background-color: ${props => props.theme.secondaryAccent};
- }
- gap: 12px;
- border-radius: 3px;
- padding: 3px;
- font-size: 12px;
-`;
-// Change mouse cursor to pointer upon hovering over an Alias tag
-export const AliasLabel = styled.div`
- cursor: pointer;
-`;
-
-const AliasRegisteredLink = styled.a`
- color: ${props => props.theme.toastText};
- text-decoration: none;
-`;
-
-export const AliasAvailable = styled.span`
- color: ${props => props.theme.accent};
-`;
-
-export const AliasPending = styled.span`
- color: ${props => props.theme.formError};
-`;
-
-export const NamespaceCtn = styled.div`
- width: 100%;
- margin-top: 50px;
- margin-bottom: 20px;
- overflow-wrap: break-word;
- h2 {
- color: ${props => props.theme.primaryText};
- margin: 0 0 20px;
- }
- h3 {
- color: ${props => props.theme.primaryText};
- margin: 0 0 10px;
- }
- white-space: pre-wrap;
-`;
-
-const Alias = () => {
- const ContextValue = React.useContext(WalletContext);
- const {
- chronik,
- refreshAliases,
- aliases,
- setAliases,
- aliasServerError,
- aliasPrices,
- setAliasPrices,
- cashtabState,
- } = ContextValue;
- const { settings, wallets } = cashtabState;
- const wallet = wallets.length > 0 ? wallets[0] : false;
- const defaultAddress =
- wallet !== false ? wallet.paths.get(1899).address : '';
- const walletState = getWalletState(wallet);
- const { balanceSats, tokens } = walletState;
- const [formData, setFormData] = useState({
- aliasName: '',
- aliasAddress: defaultAddress,
- });
- const [waitingForServer, setWaitingForServer] = useState(false);
- const [registerActiveWallet, setRegisterActiveWallet] = useState(true);
- 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 [aliasDetails, setAliasDetails] = useState(false); // stores the /alias/<alias> endpoint response object
- // Stores a conditional warning to the registration confirmation modal
- const [aliasWarningMsg, setAliasWarningMsg] = useState(false);
- // Show a confirmation modal on alias registrations
- const [isModalVisible, setIsModalVisible] = useState(false);
-
- useEffect(() => {
- setWaitingForServer(false);
- }, [balanceSats]);
-
- useEffect(() => {
- if (registerActiveWallet) {
- // Set address of active wallet to default alias registration address
- handleAliasAddressInput({
- target: {
- name: 'aliasAddress',
- value: defaultAddress,
- },
- });
- } else {
- // Clear the form if the user unchecks
- handleAliasAddressInput({
- target: {
- name: 'aliasAddress',
- value: '',
- },
- });
- }
- }, [registerActiveWallet]);
-
- const handleAliasWalletChange = async () => {
- if (defaultAddress !== '') {
- await refreshAliases(defaultAddress);
- }
-
- // Refresh alias prices if none exist yet
- if (aliasPrices === null) {
- try {
- setAliasPrices(await queryAliasServer('prices'));
- } catch (err) {
- setAliasValidationError(
- `Failed to fetch alias price information from server. Alias registration disabled. Refresh page to try again.`,
- );
- setWaitingForServer(false);
- }
- }
-
- // Track when the user stops typing into the aliasName input field for at least
- // 'aliasSettings.aliasKeyUpTimeoutMs' and make an API call to check the alias status
- const aliasInput = document.getElementsByName('aliasName')[0];
- aliasInput?.addEventListener('keyup', function () {
- setTimeout(async function () {
- // Retrieve alias details
- let aliasDetailsResp;
- if (meetsAliasSpec(aliasInput.value)) {
- try {
- // Note: aliasInput.value is used here as formData is not yet
- // initialized at the point of useEffect execution
- aliasDetailsResp = await queryAliasServer(
- 'alias',
- aliasInput.value,
- );
- if (aliasDetailsResp.address) {
- setAliasValidationError(
- `This alias is already taken`,
- );
- setIsValidAliasInput(false);
- }
- } catch (err) {
- setAliasValidationError(
- `Failed to check alias availability from server. Alias registration disabled. Refresh page to try again.`,
- );
- setWaitingForServer(false);
- }
- }
- }, aliasSettings.aliasKeyUpTimeoutMs);
- });
-
- setWaitingForServer(false);
- };
-
- useEffect(() => {
- // only run this useEffect block if wallet is defined
- if (wallet === false || typeof wallet === 'undefined') {
- return;
- }
- setWaitingForServer(true);
-
- // setWaitingForServer(false) will be called after awaiting expected methods
- handleAliasWalletChange();
- }, [wallet.name]);
-
- const clearInputForms = () => {
- setFormData(p => ({
- ...p,
- aliasName: '',
- }));
- setAliasWarningMsg(false);
- setIsValidAliasInput(false);
- };
-
- const preparePreviewModal = async () => {
- setWaitingForServer(true);
-
- // Retrieve alias details
- let aliasDetailsResp;
- try {
- aliasDetailsResp = await queryAliasServer(
- 'alias',
- formData.aliasName,
- );
- if (
- aliasDetailsResp.pending &&
- aliasDetailsResp.pending.length > 0
- ) {
- const thisWalletThisAliasPendingCount = aliases.pending.filter(
- pendingAliasObj =>
- pendingAliasObj.alias === formData.aliasName,
- ).length;
- const pendingMsgForThisAddress =
- thisWalletThisAliasPendingCount > 0
- ? `, including ${thisWalletThisAliasPendingCount} for this wallet address`
- : '';
- setAliasWarningMsg(
- ` There are currently ${aliasDetailsResp.pending.length} pending registration(s) for this alias${pendingMsgForThisAddress}.`,
- );
- } else {
- setAliasWarningMsg(false);
- }
- } catch (err) {
- const errorMsg =
- 'Error retrieving alias details. Refresh page to try again.';
- console.error(`preparePreviewModal(): ${errorMsg}`, err);
- // Using a pop up notification since this is a modal block
- toast.error(`${errorMsg}`);
- setWaitingForServer(false);
- return;
- }
-
- if (
- aliasDetailsResp &&
- !aliasDetailsResp.address &&
- !aliasDetailsResp.error &&
- aliasDetailsResp.registrationFeeSats
- ) {
- // If alias is unregistered
- setAliasDetails(aliasDetailsResp);
- setIsModalVisible(true);
- } else if (aliasDetailsResp && aliasDetailsResp.address) {
- // If alias is registered
- toast.error(
- `The alias "${formData.aliasName}" is already owned by ${aliasDetailsResp.address}. Please try another alias.`,
- );
- setAliasDetails(false);
- } else {
- setAliasValidationError(
- 'Failed to check alias availability from server. Alias registration disabled. Refresh page to try again.',
- );
- setAliasDetails(false);
- }
- setWaitingForServer(false);
- };
-
- const handleOk = () => {
- setIsModalVisible(false);
- registerAlias();
- };
-
- const handleCancel = () => {
- setIsModalVisible(false);
- };
-
- const registerAlias = async () => {
- setWaitingForServer(true);
-
- // note: input already validated via handleAliasNameInput()
- const aliasInput = formData.aliasName;
- const aliasAddress = formData.aliasAddress;
-
- if (
- !aliasDetails.address &&
- !aliasDetails.error &&
- aliasDetails.registrationFeeSats
- ) {
- console.info(
- 'Registration fee for ' +
- aliasInput +
- ' is ' +
- aliasDetails.registrationFeeSats +
- ' sats.',
- );
- console.info(
- `Alias ${aliasInput} is available. Broadcasting registration transaction.`,
- );
- try {
- const targetOutputs = [
- getAliasTargetOutput(aliasInput, aliasAddress),
- {
- address: aliasSettings.aliasPaymentAddress,
- value: aliasDetails.registrationFeeSats,
- },
- ];
-
- const txObj = await sendXec(
- chronik,
- wallet,
- targetOutputs,
- settings.minFeeSends &&
- (hasEnoughToken(
- tokens,
- appConfig.vipTokens.grumpy.tokenId,
- appConfig.vipTokens.grumpy.vipBalance,
- ) ||
- hasEnoughToken(
- tokens,
- appConfig.vipTokens.cachet.tokenId,
- appConfig.vipTokens.cachet.vipBalance,
- ))
- ? appConfig.minFee
- : appConfig.defaultFee,
- );
- clearInputForms();
- toast.success(
- <AliasRegisteredLink
- href={`${explorer.blockExplorerUrl}/tx/${txObj.response.txid}`}
- >
- {aliasInput}
- </AliasRegisteredLink>,
- );
- // Append the newly broadcasted alias registration to pending list to
- // ensure the alias refresh interval is running in useWallet.js
- setAliases(previousAliases => ({
- ...previousAliases,
- pending: [
- ...previousAliases.pending,
- {
- alias: aliasInput,
- address: defaultAddress,
- },
- ],
- }));
- } catch (err) {
- toast.error(`${err}`);
- }
- setIsValidAliasInput(true);
- } else {
- // error notification on alias being unavailable
- toast.error(
- `Alias "${aliasInput}" has already been taken, please try another alias.`,
- );
- }
- setWaitingForServer(false);
- };
-
- const handleAliasNameInput = e => {
- const { name, value } = e.target;
- const inputMeetsAliasSpec = meetsAliasSpec(value);
- if (inputMeetsAliasSpec === true) {
- setIsValidAliasInput(true);
- setAliasValidationError(false);
- } else {
- // In this case, inputMeetsAliasSpec is a string explaining why not
- setAliasValidationError(inputMeetsAliasSpec);
- setIsValidAliasInput(false);
- }
-
- setFormData(p => ({
- ...p,
- [name]: 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 = decodeCashAddress(value);
- const { hash } = decoded;
- // We only support 20-byte payloads
- isValidAddress = hash.length === 40;
- } catch (err) {
- // Invalid cashaddress
- // Log to console for user support
- console.error(`Invalid address`, err);
- }
-
- if (isValidAddress) {
- setIsValidAliasAddressInput(true);
- setAliasAddressValidationError(false);
- } else {
- setAliasAddressValidationError(
- 'Invalid alias registration address.',
- );
- setIsValidAliasAddressInput(false);
- }
-
- setFormData(p => ({
- ...p,
- [name]: value,
- }));
- };
-
- return (
- <>
- {waitingForServer && <Spinner />}
- {isModalVisible && (
- <Modal
- title={
- aliasWarningMsg
- ? 'Pending registrations detected'
- : 'Confirm alias registration'
- }
- height={aliasWarningMsg ? 350 : 155}
- handleOk={handleOk}
- handleCancel={handleCancel}
- showCancelButton
- >
- {!aliasWarningMsg &&
- aliasDetails &&
- Number.isInteger(aliasDetails.registrationFeeSats) && (
- <AliasAvailable>
- Register &quot;{formData.aliasName}&quot; for{' '}
- {toXec(
- aliasDetails.registrationFeeSats,
- ).toLocaleString()}{' '}
- XEC ?
- </AliasAvailable>
- )}
- {aliasWarningMsg !== false &&
- aliasDetails &&
- Number.isInteger(aliasDetails.registrationFeeSats) && (
- <AlertMsg>
- {` Warning: ${aliasWarningMsg}`}
- <br />
- <br />
- {` Continue the registration anyway for ${toXec(
- aliasDetails.registrationFeeSats,
- ).toLocaleString()} XEC?`}
- </AlertMsg>
- )}
- {!registerActiveWallet &&
- !aliasAddressValidationError &&
- ` Please also note Cashtab will only track alias registrations for ${wallet.name}: ${defaultAddress}.`}
- </Modal>
- )}
- <AliasWrapper>
- <h2>eCash Namespace Alias</h2>
-
- <AliasInput
- name="aliasName"
- value={formData.aliasName}
- placeholder="Enter a desired alias"
- handleInput={handleAliasNameInput}
- error={aliasValidationError}
- />
- {(() => {
- let aliasLength = getAliasByteCount(formData.aliasName);
- if (
- aliasLength > 0 &&
- isValidAliasInput &&
- aliasPrices !== null
- ) {
- // Disable alias registration if the array is not exactly one entry
- if (aliasPrices.prices.length !== 1) {
- setAliasValidationError(
- `Alias registration is temporarily unavailable, please check again later.`,
- );
- setIsValidAliasInput(false);
- return;
- }
- // TODO Once chronik-client has been upgraded for in-node chronik, update
- // this price parsing logic to use the new ws for blockheight comparisons.
- // Intention is to reverse loop through `aliasPrices.prices` and parse for
- // the latest array entry that has a startHeight within the chain's tipHeight.
- let aliasPriceXec = toXec(
- aliasPrices.prices[0].fees[aliasLength],
- ).toLocaleString();
- return (
- <AliasAvailable>
- This {aliasLength} byte alias is available,{' '}
- {aliasPriceXec} XEC to register.
- </AliasAvailable>
- );
- }
- })()}
- <p />
- <SwitchContainer>
- <Switch
- name="register-active-wallet-switch"
- checked={registerActiveWallet}
- handleToggle={() =>
- setRegisterActiveWallet(!registerActiveWallet)
- }
- ></Switch>
- <SwitchLabel>Register active wallet</SwitchLabel>
- </SwitchContainer>
-
- {!registerActiveWallet && (
- <AltAddressHolder>
- <Input
- placeholder="Enter address for this alias"
- value={formData.aliasAddress}
- disabled={registerActiveWallet}
- name="aliasAddress"
- handleInput={handleAliasAddressInput}
- error={aliasAddressValidationError}
- />
- </AltAddressHolder>
- )}
- <PrimaryButton
- disabled={
- !isValidAliasInput ||
- !isValidAliasAddressInput ||
- aliasValidationError !== false ||
- aliasServerError !== false
- }
- onClick={() => preparePreviewModal()}
- >
- Register Alias
- </PrimaryButton>
-
- <AliasHeader>Registered Aliases</AliasHeader>
- <Panel>
- {aliases &&
- aliases.registered &&
- aliases.registered.length > 0
- ? aliases.registered.map((alias, index) => (
- <CopyToClipboard
- data={alias.alias + '.xec'}
- showToast
- key={index}
- >
- <AliasTag key={index}>
- {alias.alias + '.xec'}
- </AliasTag>
- </CopyToClipboard>
- ))
- : !aliasServerError && (
- <AliasHeader>
- {'No registered aliases'}
- </AliasHeader>
- )}
- {aliasServerError && aliasValidationError === false && (
- <AlertMsg>{aliasServerError}</AlertMsg>
- )}
- </Panel>
- <AliasHeader>Pending Aliases</AliasHeader>
- <Panel>
- {aliases && aliases.pending && aliases.pending.length > 0
- ? aliases.pending.map((pendingAlias, index) => (
- <CopyToClipboard
- data={pendingAlias.alias + '.xec'}
- showToast
- key={index}
- >
- <AliasTag key={index}>
- {pendingAlias.alias + '.xec'}
- </AliasTag>
- </CopyToClipboard>
- ))
- : !aliasServerError && (
- <AliasHeader>{'No pending aliases'}</AliasHeader>
- )}
- {aliasServerError && aliasValidationError === false && (
- <AlertMsg>{aliasServerError}</AlertMsg>
- )}
- </Panel>
- </AliasWrapper>
- </>
- );
-};
-
-export default Alias;
diff --git a/cashtab/src/components/Alias/__tests__/Alias.test.js b/cashtab/src/components/Alias/__tests__/Alias.test.js
deleted file mode 100644
--- a/cashtab/src/components/Alias/__tests__/Alias.test.js
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright (c) 2024 The Bitcoin developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-import React from 'react';
-import { walletWithXecAndTokens } from 'components/App/fixtures/mocks';
-import {
- aliasPricesResp,
- aliasAddressTwoRegisteredOnePending,
- aliasAddressOneRegisteredNoPending,
- aliasAddressNoRegisteredOnePending,
-} from 'components/Alias/fixtures/mocks';
-import { render, screen, waitFor } from '@testing-library/react';
-import '@testing-library/jest-dom';
-import 'fake-indexeddb/auto';
-import localforage from 'localforage';
-import { when } from 'jest-when';
-import aliasSettings from 'config/alias';
-import {
- initializeCashtabStateForTests,
- clearLocalForage,
-} from 'components/App/fixtures/helpers';
-import CashtabTestWrapper from 'components/App/fixtures/CashtabTestWrapper';
-import appConfig from 'config/app';
-
-// Activate alias features
-aliasSettings.aliasEnabled = true;
-
-describe('<Alias />', () => {
- beforeEach(() => {
- // Mock the fetch call for Cashtab's price API
- global.fetch = jest.fn();
- const fiatCode = 'usd'; // Use usd until you mock getting settings from localforage
- const cryptoId = appConfig.coingeckoId;
- // Keep this in the code, because different URLs will have different outputs requiring different parsing
- const priceApiUrl = `https://api.coingecko.com/api/v3/simple/price?ids=${cryptoId}&vs_currencies=${fiatCode}&include_last_updated_at=true`;
- const xecPrice = 0.00003;
- const priceResponse = {
- ecash: {
- usd: xecPrice,
- last_updated_at: 1706644626,
- },
- };
- when(fetch)
- .calledWith(priceApiUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(priceResponse),
- });
- });
- afterEach(async () => {
- jest.clearAllMocks();
- await clearLocalForage(localforage);
- });
- it('Registered and Pending Aliases are correctly rendered', async () => {
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
-
- const defaultAddress = walletWithXecAndTokens.paths.get(1899).address;
-
- // Mock the fetch call to alias-server's '/prices' endpoint
- const aliasPricesFetchUrl = `${aliasSettings.aliasServerBaseUrl}/prices`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(aliasPricesFetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(aliasPricesResp),
- });
-
- // Mock the refreshAliases() call to alias-server's '/address' endpoint upon component load
- const addressFetchUrl = `${aliasSettings.aliasServerBaseUrl}/address/${defaultAddress}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(addressFetchUrl)
- .mockResolvedValue({
- json: () =>
- Promise.resolve(aliasAddressTwoRegisteredOnePending),
- });
-
- render(<CashtabTestWrapper chronik={mockedChronik} route="/alias" />);
-
- // Wait for the app to load
- await waitFor(() =>
- expect(
- screen.queryByTitle('Cashtab Loading'),
- ).not.toBeInTheDocument(),
- );
-
- // Validate rendering of registered and pending alias tags
- expect(await screen.findByText('chicken555.xec')).toBeInTheDocument();
- expect(await screen.findByText('chicken666.xec')).toBeInTheDocument();
- expect(await screen.findByText('chicken444.xec')).toBeInTheDocument();
- });
- it('Registered and Pending Aliases are correctly rendered when pending list is empty', async () => {
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
-
- // Mock the fetch call to alias-server's '/prices' endpoint
- const aliasPricesFetchUrl = `${aliasSettings.aliasServerBaseUrl}/prices`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(aliasPricesFetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(aliasPricesResp),
- });
-
- const defaultAddress = walletWithXecAndTokens.paths.get(1899).address;
-
- // Mock the refreshAliases() call to alias-server's '/address' endpoint upon component load
- const addressFetchUrl = `${aliasSettings.aliasServerBaseUrl}/address/${defaultAddress}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(addressFetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(aliasAddressOneRegisteredNoPending),
- });
-
- render(<CashtabTestWrapper chronik={mockedChronik} route="/alias" />);
-
- // Wait for the app to load
- await waitFor(() =>
- expect(
- screen.queryByTitle('Cashtab Loading'),
- ).not.toBeInTheDocument(),
- );
-
- // Tags rendered as expected
- expect(await screen.findByText('chicken555.xec')).toBeInTheDocument();
- expect(
- await screen.findByText('No pending aliases'),
- ).toBeInTheDocument();
- });
- it('Registered and Pending Aliases are correctly rendered when registered list is empty', async () => {
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
-
- // Mock the fetch call to alias-server's '/prices' endpoint
- const aliasPricesFetchUrl = `${aliasSettings.aliasServerBaseUrl}/prices`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(aliasPricesFetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(aliasPricesResp),
- });
-
- const defaultAddress = walletWithXecAndTokens.paths.get(1899).address;
-
- // Mock the refreshAliases() call to alias-server's '/address' endpoint upon component load
- const addressFetchUrl = `${aliasSettings.aliasServerBaseUrl}/address/${defaultAddress}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(addressFetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(aliasAddressNoRegisteredOnePending),
- });
-
- render(<CashtabTestWrapper chronik={mockedChronik} route="/alias" />);
-
- // Wait for the app to load
- await waitFor(() =>
- expect(
- screen.queryByTitle('Cashtab Loading'),
- ).not.toBeInTheDocument(),
- );
-
- // Validate the aliases within the dropdowns
- expect(
- await screen.findByText('No registered aliases'),
- ).toBeInTheDocument();
- expect(await screen.findByText('chicken444.xec')).toBeInTheDocument();
- });
- it('Registered and Pending lists still renders when aliasValidationError is populated and aliasServerError is false', async () => {
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
-
- const defaultAddress = walletWithXecAndTokens.paths.get(1899).address;
-
- // Note: Not mocking the '/prices' API call here in order to populate aliasValidationError
-
- // Mock the refreshAliases() call to alias-server's '/address' endpoint upon component load
- const addressFetchUrl = `${aliasSettings.aliasServerBaseUrl}/address/${defaultAddress}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(addressFetchUrl)
- .mockResolvedValue({
- json: () =>
- Promise.resolve(aliasAddressTwoRegisteredOnePending),
- });
-
- render(<CashtabTestWrapper chronik={mockedChronik} route="/alias" />);
-
- // Wait for the app to load
- await waitFor(() =>
- expect(
- screen.queryByTitle('Cashtab Loading'),
- ).not.toBeInTheDocument(),
- );
-
- // We see registered aliases
- expect(await screen.findByText('chicken555.xec')).toBeInTheDocument();
- expect(await screen.findByText('chicken666.xec')).toBeInTheDocument();
-
- // We see pending alias
- expect(await screen.findByText('chicken444.xec')).toBeInTheDocument();
- });
- it('Registered and Pending lists do not render when aliasValidationError is false and aliasServerError is populated', async () => {
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
-
- // Mock the fetch call to alias-server's '/prices' endpoint
- const aliasPricesFetchUrl = `${aliasSettings.aliasServerBaseUrl}/prices`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(aliasPricesFetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(aliasPricesResp),
- });
-
- // Note: Not mocking the refreshAliases() call to alias-server's '/address' endpoint
- // here in order to simulate aliasServerError
-
- render(<CashtabTestWrapper chronik={mockedChronik} route="/alias" />);
-
- // Wait for the app to load
- await waitFor(() =>
- expect(
- screen.queryByTitle('Cashtab Loading'),
- ).not.toBeInTheDocument(),
- );
-
- // Validate the aliases within the dropdowns
- expect(screen.queryByText('chicken555.xec')).not.toBeInTheDocument();
- expect(screen.queryByText('chicken444.xec')).not.toBeInTheDocument();
- });
-});
diff --git a/cashtab/src/components/Alias/fixtures/mocks.js b/cashtab/src/components/Alias/fixtures/mocks.js
deleted file mode 100644
--- a/cashtab/src/components/Alias/fixtures/mocks.js
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2024 The Bitcoin developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-import { walletWithXecAndTokens } from 'components/App/fixtures/mocks';
-
-const defaultAddress = walletWithXecAndTokens.paths.get(1899).address;
-export const aliasAddressTwoRegisteredOnePending = {
- registered: [
- {
- alias: 'chicken555',
- address: defaultAddress,
- txid: 'ec92610fc41df2387e7febbb358b138a802ac26023f30b2442aa01ca733fff7d',
- blockheight: 792417,
- },
- {
- alias: 'chicken666',
- address: defaultAddress,
- txid: 'ec92610fc41df2387e7febbb358b138a802ac26023f30b2442aa01ca733fff7d',
- blockheight: 792416,
- },
- ],
- pending: [
- {
- alias: 'chicken444',
- address: defaultAddress,
- txid: 'ec92610fc41df2387e7febbb358b138a802ac26023f30b2442aa01ca733fff7d',
- blockheight: 792418,
- },
- ],
-};
-export const aliasAddressOneRegisteredNoPending = {
- registered: [
- {
- alias: 'chicken555',
- address: defaultAddress,
- txid: 'ec92610fc41df2387e7febbb358b138a802ac26023f30b2442aa01ca733fff7d',
- blockheight: 792417,
- },
- ],
- pending: [],
-};
-export const aliasAddressNoRegisteredOnePending = {
- registered: [],
- pending: [
- {
- alias: 'chicken444',
- address: defaultAddress,
- txid: 'ec92610fc41df2387e7febbb358b138a802ac26023f30b2442aa01ca733fff7d',
- blockheight: 792418,
- },
- ],
-};
-export const aliasPricesResp = {
- note: 'alias-server is in beta and these prices are not finalized.',
- prices: [
- {
- startHeight: 785000,
- fees: {
- 1: 558,
- 2: 557,
- 3: 556,
- 4: 555,
- 5: 554,
- 6: 553,
- 7: 552,
- 8: 551,
- 9: 551,
- 10: 551,
- 11: 551,
- 12: 551,
- 13: 551,
- 14: 551,
- 15: 551,
- 16: 551,
- 17: 551,
- 18: 551,
- 19: 551,
- 20: 551,
- 21: 551,
- },
- },
- ],
-};
diff --git a/cashtab/src/components/App/App.js b/cashtab/src/components/App/App.js
--- a/cashtab/src/components/App/App.js
+++ b/cashtab/src/components/App/App.js
@@ -13,7 +13,6 @@
WalletIcon,
ContactsIcon,
ThemedSignAndVerifyMsg,
- ThemedUserProfileIcon,
TokensIcon,
RewardIcon,
NftIcon,
@@ -31,7 +30,6 @@
import BackupWallet from 'components/BackupWallet/BackupWallet';
import Contacts from 'components/Contacts';
import Wallets from 'components/Wallets';
-import Alias from 'components/Alias/Alias';
import Etokens from 'components/Etokens/Etokens';
import Configure from 'components/Configure/Configure';
import SignVerifyMsg from 'components/SignVerifyMsg/SignVerifyMsg';
@@ -50,7 +48,6 @@
import TabCash from 'assets/tabcash.png';
import { hasEnoughToken } from 'wallet';
import ServiceWorkerWrapper from 'components/Common/ServiceWorkerWrapper';
-import aliasSettings from 'config/alias';
import WebApp from 'components/AppModes/WebApp';
import Extension from 'components/AppModes/Extension';
import ExtensionHeader from 'components/Common/ExtensionHeader';
@@ -268,12 +265,6 @@
path="/signverifymsg"
element={<SignVerifyMsg />}
/>
- {aliasSettings.aliasEnabled && (
- <Route
- path="/alias"
- element={<Alias />}
- />
- )}
<Route
path="/configure"
element={<Configure />}
@@ -443,18 +434,6 @@
<p>Sign & Verify</p>
<ThemedSignAndVerifyMsg />
</NavItem>
- {aliasSettings.aliasEnabled && (
- <NavItem
- active={
- location.pathname === '/alias'
- }
- onClick={() => navigate('/alias')}
- >
- {' '}
- <p>Alias</p>
- <ThemedUserProfileIcon />
- </NavItem>
- )}
<NavItem
active={
location.pathname === '/configure'
diff --git a/cashtab/src/components/Common/CustomIcons.js b/cashtab/src/components/Common/CustomIcons.js
--- a/cashtab/src/components/Common/CustomIcons.js
+++ b/cashtab/src/components/Common/CustomIcons.js
@@ -80,10 +80,6 @@
min-width: 24px;
`;
-export const ThemedUserProfileIcon = styled(User)`
- height: 33px;
- width: 30px;
-`;
export const SocialContainer = styled.div`
margin: auto;
display: flex;
@@ -117,12 +113,6 @@
width: 100%;
`;
-export const ThemedAliasOutlined = styled(User)`
- fill: ${props => props.theme.primaryText} !important;
- height: 12px;
- width: 12px;
-`;
-
export const LoadingBlock = styled.div`
width: 100%;
display: flex;
diff --git a/cashtab/src/components/Etokens/Token/index.tsx b/cashtab/src/components/Etokens/Token/index.tsx
--- a/cashtab/src/components/Etokens/Token/index.tsx
+++ b/cashtab/src/components/Etokens/Token/index.tsx
@@ -10,7 +10,7 @@
IconButton,
CopyIconButton,
} from 'components/Common/Buttons';
-import { TxLink, SwitchLabel, Info, Alert } from 'components/Common/Atoms';
+import { SwitchLabel, Info, Alert } from 'components/Common/Atoms';
import Spinner from 'components/Common/Spinner';
import BalanceHeaderToken from 'components/Common/BalanceHeaderToken';
import { Event } from 'components/Common/GoogleAnalytics';
@@ -29,8 +29,6 @@
import TokenIcon from 'components/Etokens/TokenIcon';
import { explorer } from 'config/explorer';
import { token as tokenConfig } from 'config/token';
-import { queryAliasServer, Alias } from 'alias';
-import aliasSettings from 'config/alias';
import { isValidCashAddress } from 'ecashaddrjs';
import appConfig from 'config/app';
import { isMobile, getUserLocale } from 'helpers';
@@ -99,7 +97,6 @@
TokenStatsLabel,
SwitchHolder,
TokenSentLink,
- AliasAddressPreviewLabel,
InfoModalParagraph,
ButtonDisabledMsg,
ButtonDisabledSpan,
@@ -308,9 +305,6 @@
>(false);
const [confirmationOfEtokenToBeBurnt, setConfirmationOfEtokenToBeBurnt] =
useState<string>('');
- const [aliasInputAddress, setAliasInputAddress] = useState<false | string>(
- false,
- );
const [selectedCurrency, setSelectedCurrency] = useState<string>(
appConfig.ticker,
);
@@ -754,7 +748,6 @@
// Clears address and amount fields following a send token notification
const clearInputForms = () => {
setFormData(emptyFormData);
- setAliasInputAddress(false); // clear alias address preview
};
async function sendToken() {
@@ -763,14 +756,7 @@
const { address, amount } = formData;
- let cleanAddress: string;
- // check state on whether this is an alias or ecash address
- if (aliasInputAddress) {
- cleanAddress = aliasInputAddress;
- } else {
- // Get the non-alias param-free address
- cleanAddress = address.split('?')[0];
- }
+ const cleanAddress = address.split('?')[0];
try {
// Get input utxos for slpv1 or ALP send tx
@@ -968,7 +954,6 @@
const handleTokenAddressChange = async (
e: React.ChangeEvent<HTMLInputElement>,
) => {
- setAliasInputAddress(false); // clear alias address preview
const { value, name } = e.target;
// validate for token address
// validate for parameters
@@ -994,38 +979,6 @@
// If address is a valid eToken address, no error
// We support sending to etoken: addresses on SendToken screen
renderedError = false;
- } else if (
- typeof address === 'string' &&
- parsedAddressInput.address.isAlias &&
- parsedAddressInput.address.error === false
- ) {
- // if input is a valid alias (except for server validation check)
-
- // extract alias without the `.xec`
- const aliasName = address.slice(0, address.length - 4);
-
- // retrieve the alias details for `aliasName` from alias-server
- let aliasDetails: Alias;
- try {
- aliasDetails = (await queryAliasServer(
- 'alias',
- aliasName,
- )) as Alias;
- if (!aliasDetails.address) {
- renderedError =
- 'eCash Alias does not exist or yet to receive 1 confirmation';
- } else {
- // Valid address response returned
- setAliasInputAddress(aliasDetails.address);
- }
- } catch (err) {
- console.error(
- `handleTokenAddressChange(): error retrieving alias`,
- err,
- );
- renderedError =
- 'Error resolving alias at indexer, contact admin.';
- }
}
setSendTokenAddressError(renderedError);
@@ -2918,11 +2871,7 @@
<SendTokenFormRow>
<InputRow>
<InputWithScanner
- placeholder={
- aliasSettings.aliasEnabled
- ? `Address or Alias`
- : `Address`
- }
+ placeholder={`Address`}
name="address"
value={
formData.address
@@ -2937,21 +2886,6 @@
openWithScanner
}
/>
- <AliasAddressPreviewLabel>
- <TxLink
- href={`${explorer.blockExplorerUrl}/address/${aliasInputAddress}`}
- target="_blank"
- rel="noreferrer"
- >
- {aliasInputAddress &&
- `${aliasInputAddress.slice(
- 0,
- 10,
- )}...${aliasInputAddress.slice(
- -5,
- )}`}
- </TxLink>
- </AliasAddressPreviewLabel>
</InputRow>
</SendTokenFormRow>
{!isNftChild && (
diff --git a/cashtab/src/components/Etokens/Token/styled.ts b/cashtab/src/components/Etokens/Token/styled.ts
--- a/cashtab/src/components/Etokens/Token/styled.ts
+++ b/cashtab/src/components/Etokens/Token/styled.ts
@@ -113,12 +113,6 @@
color: ${props => props.theme.toastText};
text-decoration: none;
`;
-export const AliasAddressPreviewLabel = styled.div`
- text-align: center;
- color: ${props => props.theme.primaryText};
- padding-left: 1px;
- white-space: nowrap;
-`;
export const ButtonDisabledMsg = styled.div`
font-size: 14px;
diff --git a/cashtab/src/components/Etokens/__tests__/Token.test.js b/cashtab/src/components/Etokens/__tests__/Token.test.js
--- a/cashtab/src/components/Etokens/__tests__/Token.test.js
+++ b/cashtab/src/components/Etokens/__tests__/Token.test.js
@@ -7,7 +7,6 @@
import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event';
import { when } from 'jest-when';
-import aliasSettings from 'config/alias';
import { explorer } from 'config/explorer';
import {
initializeCashtabStateForTests,
@@ -38,8 +37,6 @@
// These could change, which would break tests, which is expected behavior if we haven't
// updated tests properly on changing the app
const SEND_ADDRESS_VALIDATION_ERRORS_TOKEN = [
- `Aliases must end with '.xec'`,
- 'eCash Alias does not exist or yet to receive 1 confirmation',
'Invalid address',
'eToken sends do not support bip21 query strings',
];
@@ -300,74 +297,6 @@
expect(screen.queryByText(amountErr)).not.toBeInTheDocument();
}
});
- it('Accepts a valid alias', async () => {
- render(
- <CashtabTestWrapper
- chronik={mockedChronik}
- ecc={ecc}
- route={`/token/${SEND_TOKEN_TOKENID}`}
- />,
- );
-
- // Wait for element to get token info and load
- expect((await screen.findAllByText(/BEAR/))[0]).toBeInTheDocument();
-
- // Wait for Cashtab to recognize this is an SLP1 fungible token and enable Sale
- expect(await screen.findByTitle('Toggle Sell Token')).toHaveProperty(
- 'checked',
- true,
- );
-
- // Click Send
- await user.click(await screen.findByTitle('Toggle Send'));
-
- const addressInputEl = screen.getByPlaceholderText(/Address/);
-
- const alias = 'twelvechar12';
- const expectedResolvedAddress =
- 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj';
-
- // mock the fetch call to alias-server's '/alias' endpoint
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/alias/${alias}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () =>
- Promise.resolve({
- alias: 'twelvechar12',
- address: expectedResolvedAddress,
- txid: '166b21d4631e2a6ec6110061f351c9c3bfb3a8d4e6919684df7e2824b42b0ffe',
- blockheight: 792419,
- }),
- });
-
- // The user enters a valid alias
- const addressInput = 'twelvechar12.xec';
- await user.type(addressInputEl, addressInput);
-
- // The 'Send To' input field has this address as a value
- expect(addressInputEl).toHaveValue(addressInput);
-
- // No addr validation errors on load
- for (const addrErr of SEND_ADDRESS_VALIDATION_ERRORS_TOKEN) {
- expect(screen.queryByText(addrErr)).not.toBeInTheDocument();
- }
- // No amount validation errors on load
- for (const amountErr of SEND_AMOUNT_VALIDATION_ERRORS_TOKEN) {
- expect(screen.queryByText(amountErr)).not.toBeInTheDocument();
- }
-
- // The alias address preview renders the expected address preview
- expect(
- screen.getByText(
- `${expectedResolvedAddress.slice(
- 0,
- 10,
- )}...${expectedResolvedAddress.slice(-5)}`,
- ),
- ).toBeInTheDocument();
- });
it('Displays a validation error for an invalid address', async () => {
render(
<CashtabTestWrapper
@@ -401,144 +330,6 @@
// We get the expected error
expect(screen.getByText('Invalid address')).toBeInTheDocument();
});
- it('Displays a validation error for an alias without .xec suffix', async () => {
- render(
- <CashtabTestWrapper
- chronik={mockedChronik}
- ecc={ecc}
- route={`/token/${SEND_TOKEN_TOKENID}`}
- />,
- );
-
- // Wait for element to get token info and load
- expect((await screen.findAllByText(/BEAR/))[0]).toBeInTheDocument();
-
- // Wait for Cashtab to recognize this is an SLP1 fungible token and enable Sale
- expect(await screen.findByTitle('Toggle Sell Token')).toHaveProperty(
- 'checked',
- true,
- );
-
- // Click Send
- await user.click(await screen.findByTitle('Toggle Send'));
-
- const addressInputEl = screen.getByPlaceholderText(/Address/);
-
- // The user enters a potentially valid alias without .xec suffix
- const addressInput = 'chicken';
- await user.type(addressInputEl, addressInput);
-
- // The 'Send To' input field has this address as a value
- expect(addressInputEl).toHaveValue(addressInput);
-
- // We get the expected error
- expect(
- screen.getByText(`Aliases must end with '.xec'`),
- ).toBeInTheDocument();
- });
- it('Displays a validation error for valid alias that has not yet been registered', async () => {
- render(
- <CashtabTestWrapper
- chronik={mockedChronik}
- ecc={ecc}
- route={`/token/${SEND_TOKEN_TOKENID}`}
- />,
- );
-
- // Wait for element to get token info and load
- expect((await screen.findAllByText(/BEAR/))[0]).toBeInTheDocument();
-
- // Wait for Cashtab to recognize this is an SLP1 fungible token and enable Sale
- expect(await screen.findByTitle('Toggle Sell Token')).toHaveProperty(
- 'checked',
- true,
- );
-
- // Click Send
- await user.click(await screen.findByTitle('Toggle Send'));
-
- const addressInputEl = screen.getByPlaceholderText(/Address/);
-
- const alias = 'notregistered';
-
- // mock the fetch call to alias-server's '/alias' endpoint
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/alias/${alias}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () =>
- Promise.resolve({
- alias: 'notregistered',
- isRegistered: false,
- pending: [],
- registrationFeeSats: 551,
- processedBlockheight: 827598,
- }),
- });
-
- // The user enters a valid alias
- const addressInput = `${alias}.xec`;
- await user.type(addressInputEl, addressInput);
-
- // The 'Send To' input field has this address as a value
- expect(addressInputEl).toHaveValue(addressInput);
-
- // We get the expected error
- expect(
- screen.getByText(
- `eCash Alias does not exist or yet to receive 1 confirmation`,
- ),
- ).toBeInTheDocument();
- });
- it('Displays expected error if alias server gives a bad response', async () => {
- render(
- <CashtabTestWrapper
- chronik={mockedChronik}
- ecc={ecc}
- route={`/token/${SEND_TOKEN_TOKENID}`}
- />,
- );
-
- // Wait for element to get token info and load
- expect((await screen.findAllByText(/BEAR/))[0]).toBeInTheDocument();
-
- // Wait for Cashtab to recognize this is an SLP1 fungible token and enable Sale
- expect(await screen.findByTitle('Toggle Sell Token')).toHaveProperty(
- 'checked',
- true,
- );
-
- // Click Send
- await user.click(await screen.findByTitle('Toggle Send'));
-
- const addressInputEl = screen.getByPlaceholderText(/Address/);
-
- const alias = 'servererror';
-
- // mock the fetch call to alias-server's '/alias' endpoint
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/alias/${alias}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.reject(new Error('some error')),
- });
-
- // The user enters a valid alias
- const addressInput = `${alias}.xec`;
- await user.type(addressInputEl, addressInput);
-
- // The 'Send To' input field has this address as a value
- expect(addressInputEl).toHaveValue(addressInput);
-
- // We get the expected error
- expect(
- screen.getByText(
- `Error resolving alias at indexer, contact admin.`,
- ),
- ).toBeInTheDocument();
- });
it('Displays a validation error if the user includes any query string', async () => {
render(
<CashtabTestWrapper
diff --git a/cashtab/src/components/Send/SendXec.tsx b/cashtab/src/components/Send/SendXec.tsx
--- a/cashtab/src/components/Send/SendXec.tsx
+++ b/cashtab/src/components/Send/SendXec.tsx
@@ -27,13 +27,7 @@
CashtabParsedAddressInfo,
isValidTokenSendOrBurnAmount,
} from 'validation';
-import {
- ConvertAmount,
- Alert,
- AlertMsg,
- TxLink,
- Info,
-} from 'components/Common/Atoms';
+import { ConvertAmount, Alert, AlertMsg, Info } from 'components/Common/Atoms';
import {
sendXec,
getMultisendTargetOutputs,
@@ -52,10 +46,8 @@
import styled from 'styled-components';
import { opReturn as opreturnConfig } from 'config/opreturn';
import { explorer } from 'config/explorer';
-import { Alias, queryAliasServer } from 'alias';
import { supportedFiatCurrencies } from 'config/CashtabSettings';
import appConfig from 'config/app';
-import aliasSettings from 'config/alias';
import { isMobile, getUserLocale } from 'helpers';
import { hasEnoughToken, fiatToSatoshis } from 'wallet';
import { toast } from 'react-toastify';
@@ -116,13 +108,6 @@
text-decoration: none;
`;
-const AliasAddressPreviewLabel = styled.div`
- text-align: center;
- color: ${props => props.theme.primaryText};
- padding-left: 1px;
- white-space: nowrap;
-`;
-
const AmountPreviewCtn = styled.div`
margin: 12px;
display: flex;
@@ -161,10 +146,6 @@
flex-direction: column;
gap: 12px;
`;
-const InputAndAliasPreviewHolder = styled.div`
- displaly: flex;
- flex-direction: column;
-`;
const InputModesHolder = styled.div<{ open: boolean }>`
min-height: 9rem;
@@ -303,9 +284,6 @@
const [cashtabMsgError, setCashtabMsgError] = useState<string | false>(
false,
);
- const [aliasInputAddress, setAliasInputAddress] = useState<string | false>(
- false,
- );
const [selectedCurrency, setSelectedCurrency] = useState<string>(
appConfig.ticker,
);
@@ -343,7 +321,6 @@
address: {
value: null | string;
error: false | string;
- isAlias: boolean;
};
parsedAdditionalXecOutputs: {
value: [string, string][];
@@ -372,7 +349,6 @@
address: {
value: string;
error: false;
- isAlias: boolean;
};
token_id: {
value: string;
@@ -423,7 +399,6 @@
const userLocale = getUserLocale(navigator);
const clearInputForms = () => {
setFormData(emptyFormData);
- setAliasInputAddress(false); // clear alias address preview
setParsedAddressInput(parseAddressInput('', 0));
// Reset to XEC
// Note, this ensures we never are in fiat send mode for multi-send
@@ -773,14 +748,8 @@
Event('Send.js', 'SendToMany', selectedCurrency);
} else {
// Handle XEC send to one address
- let cleanAddress;
- // check state on whether this is an alias or ecash address
- if (aliasInputAddress) {
- cleanAddress = aliasInputAddress;
- } else {
- // Get the non-alias param-free address
- cleanAddress = formData.address.split('?')[0];
- }
+ const cleanAddress = formData.address.split('?')[0];
+
const satoshisToSend =
selectedCurrency === 'XEC'
? toSatoshis(parseFloat(formData.amount))
@@ -860,7 +829,6 @@
// Clear tokenIdQueryError if we have one
setTokenIdQueryError(false);
}
- setAliasInputAddress(false); // clear alias address preview
const { value, name } = e.target;
const parsedAddressInput = parseAddressInput(
value,
@@ -872,7 +840,6 @@
// For example, a valid amount param should disable user amount input
setParsedAddressInput(parsedAddressInput);
- const address = parsedAddressInput.address.value;
let renderedSendToError = parsedAddressInput.address.error;
if (
typeof parsedAddressInput.queryString !== 'undefined' &&
@@ -880,35 +847,6 @@
) {
// If you have a bad queryString, this should be the rendered error
renderedSendToError = parsedAddressInput.queryString.error;
- } else if (
- parsedAddressInput.address.isAlias &&
- parsedAddressInput.address.error === false &&
- address !== null
- ) {
- // If we have a valid alias input, check the server for full validation
- // extract alias without the `.xec`
- const aliasName = address.slice(0, address.length - 4);
-
- // retrieve the alias details for `aliasName` from alias-server
- let aliasDetails: Alias;
- try {
- aliasDetails = (await queryAliasServer(
- 'alias',
- aliasName,
- )) as Alias;
- if (!aliasDetails.address) {
- renderedSendToError =
- 'eCash Alias does not exist or yet to receive 1 confirmation';
- setAliasInputAddress(false);
- } else {
- // Valid address response returned
- setAliasInputAddress(aliasDetails.address);
- }
- } catch (err) {
- setAliasInputAddress(false);
- renderedSendToError =
- 'Error resolving alias at indexer, contact admin.';
- }
}
// Handle errors in op_return_raw as an address error if no other error is set
@@ -1350,34 +1288,15 @@
<InputModesHolder open={isOneToManyXECSend}>
<SendToOneHolder>
<SendToOneInputForm>
- <InputAndAliasPreviewHolder>
- <InputWithScanner
- placeholder={
- aliasSettings.aliasEnabled
- ? `Address or Alias`
- : `Address`
- }
- name="address"
- value={formData.address}
- disabled={txInfoFromUrl !== false}
- handleInput={handleAddressChange}
- error={sendAddressError}
- loadWithScannerOpen={openWithScanner}
- />
- <AliasAddressPreviewLabel>
- <TxLink
- href={`${explorer.blockExplorerUrl}/address/${aliasInputAddress}`}
- target="_blank"
- rel="noreferrer"
- >
- {aliasInputAddress &&
- `${aliasInputAddress.slice(
- 0,
- 10,
- )}...${aliasInputAddress.slice(-5)}`}
- </TxLink>
- </AliasAddressPreviewLabel>
- </InputAndAliasPreviewHolder>
+ <InputWithScanner
+ placeholder={'Address'}
+ name="address"
+ value={formData.address}
+ disabled={txInfoFromUrl !== false}
+ handleInput={handleAddressChange}
+ error={sendAddressError}
+ loadWithScannerOpen={openWithScanner}
+ />
{isBip21MultipleOutputsSafe(parsedAddressInput) ? (
<Info>
<b>
diff --git a/cashtab/src/components/Send/__tests__/SendXec.test.js b/cashtab/src/components/Send/__tests__/SendXec.test.js
--- a/cashtab/src/components/Send/__tests__/SendXec.test.js
+++ b/cashtab/src/components/Send/__tests__/SendXec.test.js
@@ -12,7 +12,6 @@
SEND_AMOUNT_VALIDATION_ERRORS,
} from 'components/Send/fixtures/mocks';
import { when } from 'jest-when';
-import aliasSettings from 'config/alias';
import { explorer } from 'config/explorer';
import 'fake-indexeddb/auto';
import localforage from 'localforage';
@@ -182,93 +181,6 @@
'cursor: not-allowed',
);
});
- it('Pass valid alias to Send To field', async () => {
- // Mock the app with context at the Send screen
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- render(
- <CashtabTestWrapper
- chronik={mockedChronik}
- ecc={ecc}
- route="/send"
- />,
- );
-
- // Wait for the app to load
- await waitFor(() =>
- expect(
- screen.queryByTitle('Cashtab Loading'),
- ).not.toBeInTheDocument(),
- );
-
- const addressInputEl = screen.getByPlaceholderText('Address');
- const amountInputEl = screen.getByPlaceholderText('Amount');
-
- const alias = 'twelvechar12';
- const expectedResolvedAddress =
- 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj';
-
- // mock the fetch call to alias-server's '/alias' endpoint
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/alias/${alias}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () =>
- Promise.resolve({
- alias: 'twelvechar12',
- address: expectedResolvedAddress,
- txid: '166b21d4631e2a6ec6110061f351c9c3bfb3a8d4e6919684df7e2824b42b0ffe',
- blockheight: 792419,
- }),
- });
-
- // The user enters a valid alias
- const addressInput = 'twelvechar12.xec';
-
- await user.type(addressInputEl, addressInput);
-
- // The 'Send To' input field has this address as a value
- expect(addressInputEl).toHaveValue(addressInput);
-
- // The "Send to Many" switch is not disabled
- expect(screen.getByTitle('Toggle Multisend')).toHaveProperty(
- 'disabled',
- false,
- );
-
- // Amount input is untouched
- expect(amountInputEl).toHaveValue(null);
-
- // The amount input is NOT disabled because there is no BIP21 query string
- expect(amountInputEl).toHaveProperty('disabled', false);
-
- // No addr validation errors on load
- for (const addrErr of SEND_ADDRESS_VALIDATION_ERRORS) {
- expect(screen.queryByText(addrErr)).not.toBeInTheDocument();
- }
- // No amount validation errors on load
- for (const amountErr of SEND_AMOUNT_VALIDATION_ERRORS) {
- expect(screen.queryByText(amountErr)).not.toBeInTheDocument();
- }
-
- // The Send button is disabled because amount is null
- expect(screen.getByRole('button', { name: 'Send' })).toHaveStyle(
- 'cursor: not-allowed',
- );
-
- // The alias address preview renders the expected address preview
- expect(
- screen.getByText(
- `${expectedResolvedAddress.slice(
- 0,
- 10,
- )}...${expectedResolvedAddress.slice(-5)}`,
- ),
- ).toBeInTheDocument();
- });
it('Pass an invalid address to Send To field and get a validation error', async () => {
// Mock the app with context at the Send screen
const mockedChronik = await initializeCashtabStateForTests(
@@ -320,198 +232,6 @@
'cursor: not-allowed',
);
});
- it('Pass a possibly valid alias without .xec suffix to Send To field and get expected error', async () => {
- // Mock the app with context at the Send screen
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- render(
- <CashtabTestWrapper
- chronik={mockedChronik}
- ecc={ecc}
- route="/send"
- />,
- );
-
- // Wait for the app to load
- await waitFor(() =>
- expect(
- screen.queryByTitle('Cashtab Loading'),
- ).not.toBeInTheDocument(),
- );
-
- const addressInputEl = screen.getByPlaceholderText('Address');
- const amountInputEl = screen.getByPlaceholderText('Amount');
-
- // The user enters an alias that could be valid except missing suffix '.xec'
- const addressInput = 'aliasnosuffix';
- await user.type(addressInputEl, addressInput);
-
- // The 'Send To' input field has this address as a value
- expect(addressInputEl).toHaveValue(addressInput);
-
- // The "Send to Many" switch is not disabled
- expect(screen.getByTitle('Toggle Multisend')).toHaveProperty(
- 'disabled',
- false,
- );
-
- // Amount input is untouched
- expect(amountInputEl).toHaveValue(null);
-
- // The amount input is NOT disabled because there is no BIP21 query string
- expect(amountInputEl).toHaveProperty('disabled', false);
-
- // We get expected addr validation error
- expect(
- screen.getByText(`Aliases must end with '.xec'`),
- ).toBeInTheDocument();
-
- // The Send button is disabled
- expect(screen.getByRole('button', { name: 'Send' })).toHaveStyle(
- 'cursor: not-allowed',
- );
- });
- it('Pass a valid alias to Send To field that has not yet been registered and get expected error', async () => {
- // Mock the app with context at the Send screen
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- render(
- <CashtabTestWrapper
- chronik={mockedChronik}
- ecc={ecc}
- route="/send"
- />,
- );
-
- // Wait for the app to load
- await waitFor(() =>
- expect(
- screen.queryByTitle('Cashtab Loading'),
- ).not.toBeInTheDocument(),
- );
-
- const addressInputEl = screen.getByPlaceholderText('Address');
- const amountInputEl = screen.getByPlaceholderText('Amount');
-
- const alias = 'notregistered';
-
- // mock the fetch call to alias-server's '/alias' endpoint
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/alias/${alias}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () =>
- Promise.resolve({
- alias: 'notregistered',
- isRegistered: false,
- pending: [],
- registrationFeeSats: 551,
- processedBlockheight: 827598,
- }),
- });
-
- // The user enters a valid alias
- const addressInput = `${alias}.xec`;
- await user.type(addressInputEl, addressInput);
-
- // The 'Send To' input field has this address as a value
- expect(addressInputEl).toHaveValue(addressInput);
-
- // The "Send to Many" switch is not disabled
- expect(screen.getByTitle('Toggle Multisend')).toHaveProperty(
- 'disabled',
- false,
- );
-
- // Amount input is untouched
- expect(amountInputEl).toHaveValue(null);
-
- // The amount input is NOT disabled because there is no BIP21 query string
- expect(amountInputEl).toHaveProperty('disabled', false);
-
- // We get expected addr validation error
- expect(
- screen.getByText(
- `eCash Alias does not exist or yet to receive 1 confirmation`,
- ),
- ).toBeInTheDocument();
-
- // The Send button is disabled
- expect(screen.getByRole('button', { name: 'Send' })).toHaveStyle(
- 'cursor: not-allowed',
- );
- });
- it('Get expected error msg and send disabled if bad response from alias server', async () => {
- // Mock the app with context at the Send screen
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- render(
- <CashtabTestWrapper
- chronik={mockedChronik}
- ecc={ecc}
- route="/send"
- />,
- );
-
- // Wait for the app to load
- await waitFor(() =>
- expect(
- screen.queryByTitle('Cashtab Loading'),
- ).not.toBeInTheDocument(),
- );
-
- const addressInputEl = screen.getByPlaceholderText('Address');
- const amountInputEl = screen.getByPlaceholderText('Amount');
-
- const alias = 'servererror';
-
- // mock the fetch call to alias-server's '/alias' endpoint
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/alias/${alias}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.reject(new Error('some error')),
- });
-
- // The user enters a valid alias
- const addressInput = `${alias}.xec`;
- await user.type(addressInputEl, addressInput);
-
- // The 'Send To' input field has this address as a value
- expect(addressInputEl).toHaveValue(addressInput);
-
- // The "Send to Many" switch is not disabled
- expect(screen.getByTitle('Toggle Multisend')).toHaveProperty(
- 'disabled',
- false,
- );
-
- // Amount input is untouched
- expect(amountInputEl).toHaveValue(null);
-
- // The amount input is NOT disabled because there is no BIP21 query string
- expect(amountInputEl).toHaveProperty('disabled', false);
-
- // We get expected addr validation error
- expect(
- screen.getByText(
- `Error resolving alias at indexer, contact admin.`,
- ),
- ).toBeInTheDocument();
-
- // The Send button is disabled
- expect(screen.getByRole('button', { name: 'Send' })).toHaveStyle(
- 'cursor: not-allowed',
- );
- });
it('Pass a valid address and bip21 query string with valid amount param to Send To field', async () => {
// Mock the app with context at the Send screen
const mockedChronik = await initializeCashtabStateForTests(
@@ -570,93 +290,6 @@
'cursor: not-allowed',
);
});
- it('Pass a valid alias and bip21 query string with valid amount param to Send To field', async () => {
- // Mock the app with context at the Send screen
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- render(
- <CashtabTestWrapper
- chronik={mockedChronik}
- ecc={ecc}
- route="/send"
- />,
- );
-
- // Wait for the app to load
- await waitFor(() =>
- expect(
- screen.queryByTitle('Cashtab Loading'),
- ).not.toBeInTheDocument(),
- );
-
- const addressInputEl = screen.getByPlaceholderText('Address');
- const amountInputEl = screen.getByPlaceholderText('Amount');
-
- // Prepare alias input with mock success api call
- const alias = 'chicken';
- const expectedResolvedAddress =
- 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj';
-
- // mock the fetch call to alias-server's '/alias' endpoint
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/alias/${alias}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () =>
- Promise.resolve({
- alias: 'chicken',
- address: expectedResolvedAddress,
- txid: '166b21d4631e2a6ec6110061f351c9c3bfb3a8d4e6919684df7e2824b42b0ffe',
- blockheight: 792419,
- }),
- });
-
- // The user enters a valid BIP21 query string with a valid amount param
- const addressInput = `${alias}.xec?amount=500`;
- await user.type(addressInputEl, addressInput);
-
- // The 'Send To' input field has this address as a value
- expect(addressInputEl).toHaveValue(addressInput);
-
- // The "Send to Many" switch is disabled
- expect(screen.getByTitle('Toggle Multisend')).toHaveProperty(
- 'disabled',
- true,
- );
-
- // Amount input is the valid amount param value
- expect(amountInputEl).toHaveValue(500);
-
- // The amount input is disabled because it is set by a bip21 query string
- expect(amountInputEl).toHaveProperty('disabled', true);
-
- // No addr validation errors on load
- for (const addrErr of SEND_ADDRESS_VALIDATION_ERRORS) {
- expect(screen.queryByText(addrErr)).not.toBeInTheDocument();
- }
- // No amount validation errors on load
- for (const amountErr of SEND_AMOUNT_VALIDATION_ERRORS) {
- expect(screen.queryByText(amountErr)).not.toBeInTheDocument();
- }
-
- // The Send button is enabled as we have valid address and amount params
- expect(screen.getByRole('button', { name: 'Send' })).not.toHaveStyle(
- 'cursor: not-allowed',
- );
-
- // The alias address preview renders the expected address preview
- expect(
- screen.getByText(
- `${expectedResolvedAddress.slice(
- 0,
- 10,
- )}...${expectedResolvedAddress.slice(-5)}`,
- ),
- ).toBeInTheDocument();
- });
it('Pass a valid address and bip21 query string with invalid amount param (dust) to Send To field', async () => {
// Mock the app with context at the Send screen
const mockedChronik = await initializeCashtabStateForTests(
@@ -767,93 +400,6 @@
true,
);
});
- it('Pass a valid alias and bip21 query string with invalid amount param (too many decimals) to Send To field', async () => {
- // Mock the app with context at the Send screen
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- render(
- <CashtabTestWrapper
- chronik={mockedChronik}
- ecc={ecc}
- route="/send"
- />,
- );
-
- // Wait for the app to load
- await waitFor(() =>
- expect(
- screen.queryByTitle('Cashtab Loading'),
- ).not.toBeInTheDocument(),
- );
-
- const addressInputEl = screen.getByPlaceholderText('Address');
- const amountInputEl = screen.getByPlaceholderText('Amount');
-
- // Prepare alias input with mock success api call
- const alias = 'chicken';
- const expectedResolvedAddress =
- 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj';
-
- // mock the fetch call to alias-server's '/alias' endpoint
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/alias/${alias}`;
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () =>
- Promise.resolve({
- alias: 'chicken',
- address: expectedResolvedAddress,
- txid: '166b21d4631e2a6ec6110061f351c9c3bfb3a8d4e6919684df7e2824b42b0ffe',
- blockheight: 792419,
- }),
- });
-
- // The user enters a valid BIP21 query string with an invalid amount param
- const amount = 500.123;
- const addressInput = `${alias}.xec?amount=${amount}`;
-
- await user.type(addressInputEl, addressInput);
-
- // The 'Send To' input field has this address as a value
- expect(addressInputEl).toHaveValue(addressInput);
-
- // Amount input is the invalid amount param value
- expect(amountInputEl).toHaveValue(amount);
-
- // The amount input is disabled because it is set by a bip21 query string
- expect(amountInputEl).toHaveProperty('disabled', true);
-
- // We get expected addr validation error
- expect(
- screen.getByText(
- `XEC transactions do not support more than 2 decimal places`,
- ),
- ).toBeInTheDocument();
-
- // The Send button is disabled
- expect(screen.getByRole('button', { name: 'Send' })).toHaveStyle(
- 'cursor: not-allowed',
- );
-
- // The "Send to Many" switch is disabled
- expect(screen.getByTitle('Toggle Multisend')).toHaveProperty(
- 'disabled',
- true,
- );
-
- // The alias address preview renders the expected address preview
- expect(
- screen.getByText(
- `${expectedResolvedAddress.slice(
- 0,
- 10,
- )}...${expectedResolvedAddress.slice(-5)}`,
- ),
- ).toBeInTheDocument();
- });
it('Pass a valid address and an invalid bip21 query string', async () => {
// Mock the app with context at the Send screen
const mockedChronik = await initializeCashtabStateForTests(
diff --git a/cashtab/src/config/alias.js b/cashtab/src/config/alias.js
deleted file mode 100644
--- a/cashtab/src/config/alias.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2023 The Bitcoin developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-const aliasSettings = {
- aliasEnabled: false,
- aliasPaymentAddress: 'ecash:prfhcnyqnl5cgrnmlfmms675w93ld7mvvqd0y8lz07',
- aliasServerBaseUrl: 'https://alias.bytesofman.com',
- aliasMaxLength: 21, // max byte length, refer to the Alias spec at https://reviews.bitcoinabc.org/D12972
- aliasKeyUpTimeoutMs: 1000,
-};
-
-export default aliasSettings;
diff --git a/cashtab/src/opreturn/__tests__/index.test.js b/cashtab/src/opreturn/__tests__/index.test.js
--- a/cashtab/src/opreturn/__tests__/index.test.js
+++ b/cashtab/src/opreturn/__tests__/index.test.js
@@ -5,8 +5,6 @@
import {
getCashtabMsgTargetOutput,
getAirdropTargetOutput,
- getAliasTargetOutput,
- getAliasByteCount,
getCashtabMsgByteCount,
getOpreturnParamTargetOutput,
parseOpReturnRaw,
@@ -62,50 +60,6 @@
});
});
- describe('Alias registration target output building functions', () => {
- const { expectedReturns, expectedErrors } =
- opReturnVectors.aliasRegistrations;
-
- // Successfully created targetOutputs
- expectedReturns.forEach(expectedReturn => {
- const { description, alias, address, returned } = expectedReturn;
- it(`getAliasTargetOutput: ${description}`, () => {
- expect(getAliasTargetOutput(alias, address)).toStrictEqual(
- returned,
- );
- });
- });
- // Error cases
- expectedErrors.forEach(expectedError => {
- const { description, alias, address, errorMsg } = expectedError;
- it(`getAliasTargetOutput throws error for: ${description}`, () => {
- expect(() => getAliasTargetOutput(alias, address)).toThrow(
- errorMsg,
- );
- });
- });
- });
-
- describe('Determines byte count of user input alias registrations', () => {
- const { expectedReturns, expectedErrors } =
- opReturnVectors.aliasByteCounts;
-
- // Successfully calculates alias bytecounts
- expectedReturns.forEach(expectedReturn => {
- const { description, alias, byteCount } = expectedReturn;
- it(`getAliasByteCount: ${description}`, () => {
- expect(getAliasByteCount(alias)).toBe(byteCount);
- });
- });
- // Error cases
- expectedErrors.forEach(expectedError => {
- const { description, alias, errorMsg } = expectedError;
- it(`getAliasByteCount throws error for: ${description}`, () => {
- expect(() => getAliasByteCount(alias)).toThrow(errorMsg);
- });
- });
- });
-
describe('Determines bytecount of user input Cashtab Msg', () => {
const { expectedReturns, expectedErrors } =
opReturnVectors.cashtabMsgByteCounts;
diff --git a/cashtab/src/opreturn/fixtures/vectors.js b/cashtab/src/opreturn/fixtures/vectors.js
--- a/cashtab/src/opreturn/fixtures/vectors.js
+++ b/cashtab/src/opreturn/fixtures/vectors.js
@@ -174,98 +174,6 @@
},
],
},
- aliasRegistrations: {
- expectedReturns: [
- {
- description: 'Valid alias to p2pkh address',
- alias: 'test',
- address: 'ecash:qz2708636snqhsxu8wnlka78h6fdp77ar59jrf5035',
- returned: {
- value: 0,
- script: new Script(
- fromHex(
- '6a042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
- ),
- ),
- },
- },
- {
- description: 'Valid alias to p2sh address',
- alias: 'testtwo',
- address: 'ecash:prfhcnyqnl5cgrnmlfmms675w93ld7mvvqd0y8lz07',
- returned: {
- value: 0,
- script: new Script(
- fromHex(
- '6a042e78656300077465737474776f1508d37c4c809fe9840e7bfa77b86bd47163f6fb6c60',
- ),
- ),
- },
- },
- ],
- expectedErrors: [
- {
- description: 'Invalid alias',
- alias: 'test_WITH_badchars',
- address: 'ecash:qz2708636snqhsxu8wnlka78h6fdp77ar59jrf5035',
- errorMsg:
- 'Invalid alias "test_WITH_badchars": Alias may only contain lowercase characters a-z and 0-9',
- },
- {
- description: 'Invalid address',
- alias: 'test',
- address: 'not an address',
- errorMsg: 'Invalid address "not an address"',
- },
- ],
- },
- aliasByteCounts: {
- expectedReturns: [
- { description: 'Alias with emoji', alias: '🙈', byteCount: 4 },
- {
- description: 'Alias with emoji and text',
- alias: 'monkey🙈',
- byteCount: 10,
- },
- {
- description: 'Alias with special characters',
- alias: 'monkey©®ʕ•́ᴥ•̀ʔっ♡',
- byteCount: 33,
- },
- {
- description: 'Alias with Korean text',
- alias: '소주',
- byteCount: 6,
- },
- {
- description: 'Alias with Arabic text',
- alias: 'محيط',
- byteCount: 8,
- },
- {
- description: 'Alias with Chinese text',
- alias: '冰淇淋',
- byteCount: 9,
- },
- {
- description: 'Alias with mixed foreign alphabets and emoji',
- alias: '🙈©冰소주',
- byteCount: 15,
- },
- {
- description: 'Alphanumeric valid v0 alias',
- alias: 'justanormalalias',
- byteCount: 16,
- },
- ],
- expectedErrors: [
- {
- description: 'non-text input',
- alias: null,
- errorMsg: 'alias input must be a string',
- },
- ],
- },
cashtabMsgByteCounts: {
expectedReturns: [
{
diff --git a/cashtab/src/opreturn/index.ts b/cashtab/src/opreturn/index.ts
--- a/cashtab/src/opreturn/index.ts
+++ b/cashtab/src/opreturn/index.ts
@@ -2,22 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-import { encodeCashAddress, decodeCashAddress } from 'ecashaddrjs';
+import { encodeCashAddress } from 'ecashaddrjs';
import { opReturn } from 'config/opreturn';
-import {
- isValidTokenId,
- meetsAliasSpec,
- getOpReturnRawError,
-} from 'validation';
+import { isValidTokenId, getOpReturnRawError } from 'validation';
import { getStackArray } from 'ecash-script';
-import {
- Script,
- pushBytesOp,
- OP_RETURN,
- OP_0,
- fromHex,
- TxOutput,
-} from 'ecash-lib';
+import { Script, pushBytesOp, OP_RETURN, fromHex, TxOutput } from 'ecash-lib';
import { AddressType } from 'ecashaddrjs/dist/types';
/**
@@ -256,87 +245,6 @@
return { value: 0, script };
};
-/**
- * Generate an OP_RETURN targetOutput for use in broadcasting a v0 alias registration
- *
- * @param alias
- * @param address
- * @throws validation errors on alias or address
- * @returns targetOutput ready for transaction building, see sendXec function at src/transactions
- */
-export const getAliasTargetOutput = (
- alias: string,
- address: string,
-): TxOutput => {
- const aliasMeetsSpec = meetsAliasSpec(alias);
- if (meetsAliasSpec(alias) !== true) {
- throw new Error(`Invalid alias "${alias}": ${aliasMeetsSpec}`);
- }
-
- // Get the type and hash of the address in string format
- let decoded;
- try {
- decoded = decodeCashAddress(address);
- } catch (err) {
- throw new Error(`Invalid address "${address}"`);
- }
- const { type, hash } = decoded;
-
- // Determine address type and corresponding address version byte
- let addressVersionByte;
- // Version bytes per cashaddr spec,https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md
- if (type === 'p2pkh') {
- addressVersionByte = '00'; // one byte 0 in hex
- } else if (type === 'p2sh') {
- addressVersionByte = '08'; // one byte 8 in hex
- } else {
- throw new Error(
- `Unsupported address type ${type}. Only p2pkh and p2sh addresses are supported.`,
- );
- }
-
- const script = Script.fromOps([
- OP_RETURN,
- // LOKAD
- pushBytesOp(
- Buffer.from(opReturn.appPrefixesHex.aliasRegistration, 'hex'),
- ),
- // alias protocol tx version to stack as OP_0 per spec
- OP_0,
- // utf-8 encoded alias
- pushBytesOp(Buffer.from(alias, 'utf8')),
- // spec-encoded registration address as <addressVersionByte> and <addressPayload>
- pushBytesOp(Buffer.from(`${addressVersionByte}${hash}`, 'hex')),
- ]);
-
- // Create output
- return { value: 0, script };
-};
-
-/**
- * Calculates the bytecount of the alias input
- *
- * @param alias alias input from a text input field
- * @returns aliasInputByteSize the byte size of the alias input
- */
-export const getAliasByteCount = (alias: string): number => {
- if (typeof alias !== 'string') {
- // Make sure .trim() is available
- throw new Error('alias input must be a string');
- }
- // Do not validate the specific alias as the user may type in invalid aliases
- // We still want to return a size
- if (alias.trim() === '') {
- return 0;
- }
-
- // Get alias as utf8
- const aliasUtf8Hex = Buffer.from(alias, 'utf8');
-
- // Return bytecount
- return aliasUtf8Hex.length;
-};
-
/**
* Get targetOutput for a bip21-set opreturn param
* Note that this function is creating the OP_RETURN script with raw hex
diff --git a/cashtab/src/utils/__mocks__/mockCachedAliases.js b/cashtab/src/utils/__mocks__/mockCachedAliases.js
deleted file mode 100644
--- a/cashtab/src/utils/__mocks__/mockCachedAliases.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 2024 The Bitcoin developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-export const mockAliasLocalStorage = [
- {
- alias: 'zoo6',
- address: 'ecash:qzvydd4n3lm3xv62cx078nu9rg0e3srmqq0knykfed',
- },
- {
- alias: 'chicken1',
- address: 'ecash:qzvydd4n3lm3xv62cx078nu9rg0e3srmqq0knykfed',
- },
-];
diff --git a/cashtab/src/validation/__tests__/index.test.js b/cashtab/src/validation/__tests__/index.test.js
--- a/cashtab/src/validation/__tests__/index.test.js
+++ b/cashtab/src/validation/__tests__/index.test.js
@@ -16,8 +16,6 @@
migrateLegacyCashtabSettings,
isValidCashtabCache,
validateMnemonic,
- meetsAliasSpec,
- isValidAliasSendInput,
isProbablyNotAScam,
isValidMultiSendUserInput,
shouldSendXecBeDisabled,
@@ -324,28 +322,6 @@
});
});
});
- describe('Returns true if a given input meets alias spec or expected error msg if it does not', () => {
- const { expectedReturns } = vectors.meetsAliasSpecInputCases;
-
- // Successfully created targetOutputs
- expectedReturns.forEach(expectedReturn => {
- const { description, inputStr, response } = expectedReturn;
- it(`meetsAliasSpec: ${description}`, () => {
- expect(meetsAliasSpec(inputStr)).toBe(response);
- });
- });
- });
- describe('Validates user alias input on Send and SendToken screens', () => {
- const { expectedReturns } = vectors.validAliasSendInputCases;
-
- // Successfully created targetOutputs
- expectedReturns.forEach(expectedReturn => {
- const { description, sendToAliasInput, response } = expectedReturn;
- it(`isValidAliasSendInput: ${description}`, () => {
- expect(isValidAliasSendInput(sendToAliasInput)).toBe(response);
- });
- });
- });
describe('Validating Cashtab Contact Lists', () => {
const { expectedReturns } = vectors.isValidContactList;
expectedReturns.forEach(expectedReturn => {
diff --git a/cashtab/src/validation/fixtures/vectors.js b/cashtab/src/validation/fixtures/vectors.js
--- a/cashtab/src/validation/fixtures/vectors.js
+++ b/cashtab/src/validation/fixtures/vectors.js
@@ -397,121 +397,6 @@
},
],
},
- meetsAliasSpecInputCases: {
- expectedReturns: [
- {
- description:
- 'returns true for a valid lowercase alphanumeric input',
- inputStr: 'jasdf3873',
- response: true,
- },
- {
- description:
- 'returns expected error if input contains uppercase char',
- inputStr: 'jasDf3873',
- response:
- 'Alias may only contain lowercase characters a-z and 0-9',
- },
- {
- description:
- 'returns expected error if input contains special char',
- inputStr: 'Glück',
- response:
- 'Alias may only contain lowercase characters a-z and 0-9',
- },
- {
- description: 'returns expected error if input contains emoji',
- inputStr: '😉',
- response:
- 'Alias may only contain lowercase characters a-z and 0-9',
- },
- {
- description:
- 'returns expected error if input contains other special characters',
- inputStr: '( ͡° ͜ʖ ͡°)',
- response:
- 'Alias may only contain lowercase characters a-z and 0-9',
- },
- {
- description:
- 'returns expected error if input is an empty string',
- inputStr: '​',
- response:
- 'Alias may only contain lowercase characters a-z and 0-9',
- },
- {
- description:
- 'returns expected error if input contains an empty space',
- inputStr: '​jasdf3873',
- response:
- 'Alias may only contain lowercase characters a-z and 0-9',
- },
- {
- description: 'returns expected error if input contains symbols',
- inputStr: '​jasdf3873@#',
- response:
- 'Alias may only contain lowercase characters a-z and 0-9',
- },
- {
- description: 'returns expected error if input is not a string',
- inputStr: { testAlias: 'string at key' },
- response: 'Alias input must be a string',
- },
- {
- description:
- 'returns expected error if input contains underscores',
- inputStr: 'test_WITH_badchars',
- response:
- 'Alias may only contain lowercase characters a-z and 0-9',
- },
- {
- description:
- 'returns expected error if exceeds byte restriction',
- inputStr: '0123456789012345678901',
- response: `Invalid bytecount 22. Alias be 1-21 bytes.`,
- },
- {
- description: 'returns true for an alias of max bytecount',
- inputStr: '012345678901234567890',
- response: true,
- },
- ],
- },
- validAliasSendInputCases: {
- expectedReturns: [
- {
- description: 'Valid alias send input',
- sendToAliasInput: 'chicken.xec',
- response: true,
- },
- {
- description: 'Valid alias missing prefix',
- sendToAliasInput: 'chicken',
- response: `Must include '.xec' suffix when sending to an eCash alias`,
- },
- {
- description: 'Valid alias with double suffix',
- sendToAliasInput: 'chicken.xec.xec',
- response: `Must include '.xec' suffix when sending to an eCash alias`,
- },
- {
- description: 'Valid alias with bad suffix',
- sendToAliasInput: 'chicken.xe',
- response: `Must include '.xec' suffix when sending to an eCash alias`,
- },
- {
- description: 'Invalid alias (too long)',
- sendToAliasInput: '0123456789012345678901.xec',
- response: `Invalid bytecount 22. Alias be 1-21 bytes.`,
- },
- {
- description: 'Invalid alias (nonalphanumeric)',
- sendToAliasInput: 'Capitalized@.xec',
- response:
- 'Alias may only contain lowercase characters a-z and 0-9',
- },
- ],
- },
parseAddressInput: {
expectedReturns: [
// address only
@@ -524,7 +409,6 @@
address: {
value: '',
error: 'Invalid address',
- isAlias: false,
},
},
},
@@ -538,7 +422,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
},
},
@@ -551,7 +434,6 @@
address: {
value: 'qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
},
},
@@ -564,8 +446,7 @@
parsedAddressInput: {
address: {
value: 'chicken.xec',
- error: false,
- isAlias: true,
+ error: 'Invalid address',
},
},
},
@@ -577,8 +458,7 @@
parsedAddressInput: {
address: {
value: 'chicken',
- error: `Aliases must end with '.xec'`,
- isAlias: true,
+ error: `Invalid address`,
},
},
},
@@ -594,7 +474,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
amount: { value: '500000', error: false },
queryString: { value: 'amount=500000', error: false },
@@ -611,7 +490,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
amount: {
value: '500001',
@@ -631,7 +509,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
amount: { value: '123.45', error: false },
queryString: { value: 'amount=123.45', error: false },
@@ -647,7 +524,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfg',
error: 'Invalid address',
- isAlias: false,
},
amount: { value: '500000', error: false },
queryString: { value: 'amount=500000', error: false },
@@ -663,7 +539,6 @@
address: {
value: 'etoken:qq9h6d0a5q65fgywv4ry64x04ep906mdkufhx2swv3',
error: `eToken addresses are not supported for ${appConfig.ticker} sends`,
- isAlias: false,
},
amount: { value: '500000', error: false },
queryString: { value: 'amount=500000', error: false },
@@ -680,7 +555,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
amount: {
value: '123.456',
@@ -697,8 +571,7 @@
parsedAddressInput: {
address: {
value: 'chicken.xec',
- error: false,
- isAlias: true,
+ error: 'Invalid address',
},
amount: { value: '125', error: false },
queryString: { value: 'amount=125', error: false },
@@ -712,8 +585,7 @@
parsedAddressInput: {
address: {
value: 'chicken',
- error: `Aliases must end with '.xec'`,
- isAlias: true,
+ error: `Invalid address`,
},
amount: { value: '125', error: false },
queryString: { value: 'amount=125', error: false },
@@ -731,7 +603,6 @@
address: {
value: 'ecash:qr6lws9uwmjkkaau4w956lugs9nlg9hudqs26lyxkv',
error: false,
- isAlias: false,
},
amount: { value: '110', error: false },
parsedAdditionalXecOutputs: {
@@ -760,7 +631,6 @@
address: {
value: 'ecash:qr6lws9uwmjkkaau4w956lugs9nlg9hudqs26lyxkv',
error: false,
- isAlias: false,
},
amount: { value: '110', error: false },
parsedAdditionalXecOutputs: {
@@ -784,7 +654,6 @@
address: {
value: 'ecash:qr6lws9uwmjkkaau4w956lugs9nlg9hudqs26lyxkv',
error: false,
- isAlias: false,
},
amount: { value: '110', error: false },
parsedAdditionalXecOutputs: {
@@ -808,7 +677,6 @@
address: {
value: 'ecash:qr6lws9uwmjkkaau4w956lugs9nlg9hudqs26lyxkv',
error: false,
- isAlias: false,
},
amount: { value: '110', error: false },
parsedAdditionalXecOutputs: {
@@ -832,7 +700,6 @@
address: {
value: 'ecash:qr6lws9uwmjkkaau4w956lugs9nlg9hudqs26lyxkv',
error: false,
- isAlias: false,
},
amount: { value: '110', error: false },
parsedAdditionalXecOutputs: {
@@ -857,7 +724,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
op_return_raw: {
value: '042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
@@ -878,8 +744,7 @@
parsedAddressInput: {
address: {
value: 'chicken.xec',
- error: false,
- isAlias: true,
+ error: 'Invalid address',
},
op_return_raw: {
value: '042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
@@ -901,7 +766,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
op_return_raw: {
value: 'notvalid042e786563000474657374150095e79f51d4260bc0dc3ba7fb77c7be92d0fbdd1d',
@@ -924,7 +788,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
amount: { value: '500', error: false },
op_return_raw: {
@@ -949,7 +812,6 @@
address: {
value: 'ecash:qr6lws9uwmjkkaau4w956lugs9nlg9hudqs26lyxkv',
error: false,
- isAlias: false,
},
amount: { value: '110', error: false },
op_return_raw: {
@@ -981,7 +843,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
queryString: {
value: '*&@^&%@amount=-500000',
@@ -1001,7 +862,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
queryString: {
value: 'token_id=1111111111111111111111111111111111111111111111111111111111111111',
@@ -1020,7 +880,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
queryString: {
value: 'token_decimalized_qty=100.123',
@@ -1039,7 +898,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
queryString: {
value: 'token_id=1111111111111111111111111111111111111111111111111111111111111111&token_decimalized_qty=100.123&amount=100',
@@ -1058,7 +916,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
token_id: {
value: 'gg11111111111111111111111111111111111111111111111111111111111111',
@@ -1082,7 +939,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
token_id: {
value: '1111111111111111111111111111111111111111111111111111111111111111',
@@ -1109,7 +965,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
token_id: {
value: '1111111111111111111111111111111111111111111111111111111111111111',
@@ -1136,7 +991,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
token_id: {
value: '1111111111111111111111111111111111111111111111111111111111111111',
@@ -1164,7 +1018,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
amount: {
value: null,
@@ -1186,7 +1039,6 @@
address: {
value: 'ecash:qq9h6d0a5q65fgywv4ry64x04ep906mdku8f0gxfgx',
error: false,
- isAlias: false,
},
op_return_raw: {
error: 'Duplicated op_return_raw param',
@@ -1272,14 +1124,14 @@
isValid: false,
},
{
- description: 'Valid alias formats are accepted',
+ description: 'Valid alias formats are not accepted',
contactList: [
{
address: 'beta.xec',
name: 'Test',
},
],
- isValid: true,
+ isValid: false,
},
],
},
diff --git a/cashtab/src/validation/index.ts b/cashtab/src/validation/index.ts
--- a/cashtab/src/validation/index.ts
+++ b/cashtab/src/validation/index.ts
@@ -21,8 +21,6 @@
import appConfig from 'config/app';
import { opReturn } from 'config/opreturn';
import { getStackArray } from 'ecash-script';
-import aliasSettings from 'config/alias';
-import { getAliasByteCount } from 'opreturn';
import { CashtabWallet, fiatToSatoshis } from 'wallet';
import CashtabCache, { UNKNOWN_TOKEN_ID } from 'config/CashtabCache';
import { STRINGIFIED_DECIMALIZED_REGEX } from 'wallet';
@@ -53,50 +51,6 @@
}
return false;
};
-/**
- * Does a given string meet the spec of a valid ecash alias
- * See spec a doc/standards/ecash-alias.md
- * Note that an alias is only "valid" if it has been registered
- * So here, we are only testing spec compliance
- * @param inputStr
- * @returns true if isValid, string for reason why if not
- */
-export const meetsAliasSpec = (inputStr: string): true | string => {
- if (typeof inputStr !== 'string') {
- return 'Alias input must be a string';
- }
- if (!/^[a-z0-9]+$/.test(inputStr)) {
- return 'Alias may only contain lowercase characters a-z and 0-9';
- }
- const aliasByteCount = getAliasByteCount(inputStr);
- if (aliasByteCount > aliasSettings.aliasMaxLength) {
- return `Invalid bytecount ${aliasByteCount}. Alias be 1-21 bytes.`;
- }
- return true;
-};
-
-/**
- * Validate user input of an alias for cases that require the .xec suffix
- * Note this only validates the format according to spec and requirements
- * Must validate with indexer for associated ecash address before a tx is broadcast
- * @param sendToAliasInput
- */
-export const isValidAliasSendInput = (
- sendToAliasInput: string,
-): true | string => {
- // To send to an alias, a user must include the '.xec' extension
- // This is to prevent confusion with alias platforms on other crypto networks
- const aliasParts = sendToAliasInput.split('.');
- const aliasName = aliasParts[0];
- const aliasMeetsSpec = meetsAliasSpec(aliasName);
- if (aliasMeetsSpec !== true) {
- return aliasMeetsSpec;
- }
- if (aliasParts.length !== 2 || aliasParts[1] !== 'xec') {
- return `Must include '.xec' suffix when sending to an eCash alias`;
- }
- return true;
-};
export const validateMnemonic = (
mnemonic: string,
@@ -326,8 +280,7 @@
// Address must be a valid XEC address, name must be a string
const { address, name } = contact;
if (
- (isValidCashAddress(address, appConfig.prefix) ||
- isValidAliasSendInput(address)) &&
+ isValidCashAddress(address, appConfig.prefix) &&
typeof name === 'string'
) {
// This contact is valid
@@ -649,7 +602,7 @@
};
export interface CashtabParsedAddressInfo {
- address: { value: null | string; error: false | string; isAlias: boolean };
+ address: { value: null | string; error: false | string };
amount?: { value: null | string; error: false | string };
queryString?: { value: string; error: false | string };
parsedAdditionalXecOutputs?: {
@@ -681,7 +634,7 @@
): CashtabParsedAddressInfo {
// Build return obj
const parsedAddressInput: CashtabParsedAddressInfo = {
- address: { value: null, error: false, isAlias: false },
+ address: { value: null, error: false },
};
// Reject non-string input
@@ -702,23 +655,14 @@
// Validate address
const isValidAddr = isValidCashAddress(cleanAddress, appConfig.prefix);
- // Is this valid address?
if (!isValidAddr) {
- // Check if this is an alias address
- if (isValidAliasSendInput(cleanAddress) !== true) {
- if (meetsAliasSpec(cleanAddress) === true) {
- // If it would be a valid alias except for the missing '.xec', this is a useful validation error
- parsedAddressInput.address.error = `Aliases must end with '.xec'`;
- parsedAddressInput.address.isAlias = true;
- } else if (isValidCashAddress(cleanAddress, 'etoken')) {
- // If it is, though, a valid eToken address
- parsedAddressInput.address.error = `eToken addresses are not supported for ${appConfig.ticker} sends`;
- } else {
- // If your address is not a valid address and not a valid alias format
- parsedAddressInput.address.error = `Invalid address`;
- }
+ // If this is an invalid address
+ if (isValidCashAddress(cleanAddress, 'etoken')) {
+ // If this is a valid etoken address
+ parsedAddressInput.address.error = `eToken addresses are not supported for ${appConfig.ticker} sends`;
} else {
- parsedAddressInput.address.isAlias = true;
+ // If your address is not a valid address and not a valid etoken address
+ parsedAddressInput.address.error = `Invalid address`;
}
}
@@ -849,14 +793,13 @@
const nthAddress = value;
// address validation
// Note: for now, Cashtab only supports valid cash addresses for secondary outputs
- // TODO support aliases
const isValidNthAddress = isValidCashAddress(
nthAddress,
appConfig.prefix,
);
if (!isValidNthAddress) {
//
- // If your address is not a valid address and not a valid alias format
+ // If your address is not a valid address
parsedAddressInput.parsedAdditionalXecOutputs.error = `Invalid address "${nthAddress}"`;
// We do not return a value for parsedAdditionalXecOutputs if there is a validation error
return parsedAddressInput;
diff --git a/cashtab/src/wallet/__tests__/useWallet.test.js b/cashtab/src/wallet/__tests__/useWallet.test.js
--- a/cashtab/src/wallet/__tests__/useWallet.test.js
+++ b/cashtab/src/wallet/__tests__/useWallet.test.js
@@ -18,7 +18,6 @@
clearLocalForage,
initializeCashtabStateForTests,
} from 'components/App/fixtures/helpers';
-import aliasSettings from 'config/alias';
import { cashtabCacheToJSON, storedCashtabCacheToMap } from 'helpers';
import CashtabCache from 'config/CashtabCache';
import {
@@ -196,515 +195,6 @@
),
);
});
- it('processChronikWsMsg() refreshes alias prices when aliasPrices is null', async () => {
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- const { result } = renderHook(() => useWallet(mockedChronik));
- const mockWebsocketMsg = {
- msgType: 'BLK_FINALIZED',
- blockHash:
- '0000000000000000042ceefd8937bb1eb25764c4c5a157701fddbfb552f9803f',
- blockHeight: 838989,
- };
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/prices`;
- const mockAliasServerResponse = {
- note: 'alias-server is in beta and these prices are not finalized.',
- prices: [
- {
- startHeight: 823950,
- fees: {
- 1: 558,
- 2: 557,
- 3: 556,
- 4: 555,
- 5: 554,
- 6: 553,
- 7: 552,
- 8: 551,
- 9: 551,
- 10: 551,
- 11: 551,
- 12: 551,
- 13: 551,
- 14: 551,
- 15: 551,
- 16: 551,
- 17: 551,
- 18: 551,
- 19: 551,
- 20: 551,
- 21: 551,
- },
- },
- ],
- };
-
- // Mock the fetch call to alias-server's '/prices' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(mockAliasServerResponse),
- });
-
- // Wait for the wallet to load
- await waitFor(() =>
- expect(result.current.cashtabState.wallets[0]).toStrictEqual(
- walletWithXecAndTokens,
- ),
- );
-
- await act(async () => {
- await result.current.processChronikWsMsg(
- mockWebsocketMsg,
- result.current.cashtabState,
- result.current.fiatPrice,
- true,
- );
- });
-
- // Verify upon `BlockConnected` events processChronikWsMsg() updates the aliasPrices state var
- expect(result.current.aliasPrices).toStrictEqual(
- mockAliasServerResponse,
- );
- });
-
- it('processChronikWsMsg() refreshes alias prices when aliasPrices exists, server and cashtab prices array length do not match', async () => {
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- const { result } = renderHook(() => useWallet(mockedChronik));
- const mockWebsocketMsg = {
- msgType: 'BLK_FINALIZED',
- blockHash:
- '0000000000000000042ceefd8937bb1eb25764c4c5a157701fddbfb552f9803f',
- blockHeight: 838989,
- };
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/prices`;
- const mockExistingAliasPrices = {
- note: 'alias-server is in beta and these prices are not finalized.',
- prices: [
- {
- startHeight: 823944,
- fees: {
- 1: 558,
- 2: 557,
- 3: 556,
- 4: 555,
- 5: 554,
- 6: 553,
- 7: 552,
- 8: 551,
- 9: 551,
- 10: 9999999999999,
- 11: 551,
- 12: 551,
- 13: 551,
- 14: 551,
- 15: 551,
- 16: 551,
- 17: 551,
- 18: 551,
- 19: 551,
- 20: 551,
- 21: 551,
- },
- },
- ],
- };
- const mockAliasServerResponse = {
- note: 'alias-server is in beta and these prices are not finalized.',
- prices: [
- {
- startHeight: 823944,
- fees: {
- 1: 558,
- 2: 557,
- 3: 556,
- 4: 555,
- 5: 554,
- 6: 553,
- 7: 552,
- 8: 551,
- 9: 551,
- 10: 9999999999999,
- 11: 551,
- 12: 551,
- 13: 551,
- 14: 551,
- 15: 551,
- 16: 551,
- 17: 551,
- 18: 551,
- 19: 551,
- 20: 551,
- 21: 551,
- },
- },
- {
- startHeight: 823950,
- fees: {
- 1: 558,
- 2: 557,
- 3: 556,
- 4: 555,
- 5: 554,
- 6: 553,
- 7: 552,
- 8: 551,
- 9: 551,
- 10: 551,
- 11: 551,
- 12: 551,
- 13: 551,
- 14: 551,
- 15: 551,
- 16: 551,
- 17: 551,
- 18: 551,
- 19: 551,
- 20: 551,
- 21: 551,
- },
- },
- ],
- };
-
- // Mock the existing aliasPrices state value
- result.current.setAliasPrices(mockExistingAliasPrices);
-
- // Mock the fetch call to alias-server's '/prices' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(mockAliasServerResponse),
- });
-
- // Wait for the wallet to load
- await waitFor(() =>
- expect(result.current.cashtabState.wallets[0]).toStrictEqual(
- walletWithXecAndTokens,
- ),
- );
-
- await act(async () => {
- await result.current.processChronikWsMsg(
- mockWebsocketMsg,
- result.current.cashtabState,
- result.current.fiatPrice,
- true,
- );
- });
-
- // Verify upon `BlockConnected` events processChronikWsMsg() updates the aliasPrices state var
- expect(result.current.aliasPrices).toEqual(mockAliasServerResponse);
- });
-
- it('processChronikWsMsg() does not refresh alias prices when aliasPrices exists, server and cashtab array length do match', async () => {
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- const { result } = renderHook(() => useWallet(mockedChronik));
- const mockWebsocketMsg = {
- msgType: 'BLK_FINALIZED',
- blockHash:
- '0000000000000000042ceefd8937bb1eb25764c4c5a157701fddbfb552f9803f',
- blockHeight: 838989,
- };
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/prices`;
- const mockExistingAliasPrices = {
- note: 'alias-server is in beta and these prices are not finalized.',
- prices: [
- {
- // Technically, there should never be a scenario where the prices array length matches between
- // server and cashtab and the height does not. But for the purposes of this unit test we need
- // to validate the existing aliasPrices state var was not updated, hence this startHeight differentiation.
- startHeight: 111111,
- fees: {
- 1: 558,
- 2: 557,
- 3: 556,
- 4: 555,
- 5: 554,
- 6: 553,
- 7: 552,
- 8: 551,
- 9: 551,
- 10: 9999999999999,
- 11: 551,
- 12: 551,
- 13: 551,
- 14: 551,
- 15: 551,
- 16: 551,
- 17: 551,
- 18: 551,
- 19: 551,
- 20: 551,
- 21: 551,
- },
- },
- {
- startHeight: 823950,
- fees: {
- 1: 558,
- 2: 557,
- 3: 556,
- 4: 555,
- 5: 554,
- 6: 553,
- 7: 552,
- 8: 551,
- 9: 551,
- 10: 551,
- 11: 551,
- 12: 551,
- 13: 551,
- 14: 551,
- 15: 551,
- 16: 551,
- 17: 551,
- 18: 551,
- 19: 551,
- 20: 551,
- 21: 551,
- },
- },
- ],
- };
- const mockAliasServerResponse = {
- note: 'alias-server is in beta and these prices are not finalized.',
- prices: [
- {
- startHeight: 823944,
- fees: {
- 1: 558,
- 2: 557,
- 3: 556,
- 4: 555,
- 5: 554,
- 6: 553,
- 7: 552,
- 8: 551,
- 9: 551,
- 10: 9999999999999,
- 11: 551,
- 12: 551,
- 13: 551,
- 14: 551,
- 15: 551,
- 16: 551,
- 17: 551,
- 18: 551,
- 19: 551,
- 20: 551,
- 21: 551,
- },
- },
- {
- startHeight: 823950,
- fees: {
- 1: 558,
- 2: 557,
- 3: 556,
- 4: 555,
- 5: 554,
- 6: 553,
- 7: 552,
- 8: 551,
- 9: 551,
- 10: 551,
- 11: 551,
- 12: 551,
- 13: 551,
- 14: 551,
- 15: 551,
- 16: 551,
- 17: 551,
- 18: 551,
- 19: 551,
- 20: 551,
- 21: 551,
- },
- },
- ],
- };
-
- // Wait for the wallet to load
- await waitFor(() =>
- expect(result.current.cashtabState.wallets[0]).toStrictEqual(
- walletWithXecAndTokens,
- ),
- );
-
- // Mock the existing aliasPrices state value
- await act(async () => {
- result.current.setAliasPrices(mockExistingAliasPrices);
- });
-
- // Mock the fetch call to alias-server's '/prices' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(mockAliasServerResponse),
- });
-
- await act(async () => {
- await result.current.processChronikWsMsg(
- mockWebsocketMsg,
- result.current.cashtabState,
- result.current.fiatPrice,
- true,
- );
- });
-
- // Verify upon `BlockConnected` events processChronikWsMsg() does not update the aliasPrices state var
- expect(result.current.aliasPrices).toStrictEqual(
- mockExistingAliasPrices,
- );
- });
-
- it('Verify a processChronikWsMsg() new block event updates the `aliasServerError` state var upon a /prices/ endpoint error', async () => {
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- const { result } = renderHook(() => useWallet(mockedChronik));
- const mockWebsocketMsg = {
- msgType: 'BLK_FINALIZED',
- blockHash:
- '0000000000000000042ceefd8937bb1eb25764c4c5a157701fddbfb552f9803f',
- blockHeight: 838989,
- };
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/prices`;
- const expectedError = 'Invalid response from alias prices endpoint';
-
- // Mock the fetch call to alias-server's '/prices' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () =>
- Promise.resolve({ error: 'not a valid prices response' }),
- });
-
- // Wait for the wallet to load
- await waitFor(() =>
- expect(result.current.cashtabState.wallets[0]).toStrictEqual(
- walletWithXecAndTokens,
- ),
- );
-
- await act(async () => {
- await result.current.processChronikWsMsg(
- mockWebsocketMsg,
- result.current.cashtabState,
- result.current.fiatPrice,
- true,
- );
- });
-
- // Verify the `aliasServerError` state var in useWallet is updated
- expect(result.current.aliasServerError).toStrictEqual(
- `Error: ${expectedError}`,
- );
- });
-
- it('Verify refreshAliases() updates the `aliases` state variable on a successful /address/ endpoint response', async () => {
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- const { result } = renderHook(() => useWallet(mockedChronik));
- const address = 'ecash:qzth8qvakhr6y8zcefdrvx30zrdmt2z2gvp7zc5vj8';
- const endPoint = 'address';
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/${endPoint}/${address}`;
- const mockAliasServerResponse = {
- registered: [
- {
- alias: 'john',
- address: 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj',
- txid: 'ec92610fc41df2387e7febbb358b138a802ac26023f30b2442aa01ca733fff7d',
- blockheight: 792417,
- },
- {
- alias: 'jane',
- address: 'ecash:qpmytrdsakt0axrrlswvaj069nat3p9s7cjctmjasj',
- txid: '0c77e4f7e0ff4f1028372042cbeb97eaddb64d505efe960b5a1ca4fce65598e2',
- blockheight: 792418,
- },
- ],
- pending: [],
- };
-
- // Mock the fetch call to alias-server's '/address' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(mockAliasServerResponse),
- });
-
- // Wait for the wallet to load
- await waitFor(() =>
- expect(result.current.cashtabState.wallets[0]).toStrictEqual(
- walletWithXecAndTokens,
- ),
- );
-
- // Execute the refreshAliases function with the mocked alias-server call
- await act(async () => {
- await result.current.refreshAliases(address);
- });
-
- // Verify the `aliases` state var in useWallet is updated
- expect(result.current.aliases).toStrictEqual(mockAliasServerResponse);
- });
-
- it('Verify refreshAliases() updates the `aliasServerError` state variable upon an /address/ endpoint error', async () => {
- const mockedChronik = await initializeCashtabStateForTests(
- walletWithXecAndTokens,
- localforage,
- );
- const { result } = renderHook(() => useWallet(mockedChronik));
- const address = 'ecash:qzth8qvakhr6y8zcefdrvx30zrdmt2z2gvp7zc5vj8';
- const endPoint = 'address';
- const fetchUrl = `${aliasSettings.aliasServerBaseUrl}/${endPoint}/${address}`;
- const expectedError = {
- error: 'Error: Unable to retrieve aliases',
- };
-
- // Mock the fetch call to alias-server's '/address' endpoint
- global.fetch = jest.fn();
- when(fetch)
- .calledWith(fetchUrl)
- .mockResolvedValue({
- json: () => Promise.resolve(expectedError),
- });
-
- // Wait for the wallet to load
- await waitFor(() =>
- expect(result.current.cashtabState.wallets[0]).toStrictEqual(
- walletWithXecAndTokens,
- ),
- );
-
- // Execute the refreshAliases function with the mocked alias-server call
- await act(async () => {
- await result.current.refreshAliases(address);
- });
-
- // Verify the `aliasServerError` state var in useWallet is updated
- expect(result.current.aliasServerError).toStrictEqual(
- expectedError.error,
- );
- });
it('An incoming tx message from the websocket causes the wallet to update', async () => {
const mockedChronik = await initializeCashtabStateForTests(
walletWithXecAndTokens,
diff --git a/cashtab/src/wallet/context.tsx b/cashtab/src/wallet/context.tsx
--- a/cashtab/src/wallet/context.tsx
+++ b/cashtab/src/wallet/context.tsx
@@ -21,13 +21,6 @@
'cashtabLoaded' in context &&
'loading' in context &&
'apiError' in context &&
- 'refreshAliases' in context &&
- 'aliases' in context &&
- 'setAliases' in context &&
- 'aliasServerError' in context &&
- 'setAliasServerError' in context &&
- 'aliasPrices' in context &&
- 'setAliasPrices' in context &&
'updateCashtabState' in context &&
'processChronikWsMsg' in context &&
'cashtabState' in context
@@ -43,13 +36,6 @@
cashtabLoaded: undefined;
loading: undefined;
apiError: undefined;
- refreshAliases: undefined;
- aliases: undefined;
- setAliases: undefined;
- aliasServerError: undefined;
- setAliasServerError: undefined;
- aliasPrices: undefined;
- setAliasPrices: undefined;
updateCashtabState: undefined;
processChronikWsMsg: undefined;
cashtabState: undefined;
@@ -64,13 +50,6 @@
cashtabLoaded: undefined,
loading: undefined,
apiError: undefined,
- refreshAliases: undefined,
- aliases: undefined,
- setAliases: undefined,
- aliasServerError: undefined,
- setAliasServerError: undefined,
- aliasPrices: undefined,
- setAliasPrices: undefined,
updateCashtabState: undefined,
processChronikWsMsg: undefined,
cashtabState: undefined,
diff --git a/cashtab/src/wallet/useWallet.ts b/cashtab/src/wallet/useWallet.ts
--- a/cashtab/src/wallet/useWallet.ts
+++ b/cashtab/src/wallet/useWallet.ts
@@ -19,9 +19,7 @@
getTokenBalances,
getTokenGenesisInfo,
} from 'chronik';
-import { queryAliasServer, AliasPrices, AddressAliasStatus } from 'alias';
import appConfig from 'config/app';
-import aliasSettings from 'config/alias';
import { CashReceivedNotificationIcon } from 'components/Common/CustomIcons';
import CashtabSettings, {
supportedFiatCurrencies,
@@ -43,7 +41,6 @@
hasUnfinalizedTxsInHistory,
CashtabWallet,
LegacyCashtabWallet,
- CashtabPathInfo,
} from 'wallet';
import { toast } from 'react-toastify';
import CashtabState, { CashtabContact } from 'config/CashtabState';
@@ -70,13 +67,6 @@
cashtabLoaded: boolean;
loading: boolean;
apiError: boolean;
- refreshAliases: (address: string) => Promise<void>;
- aliases: AddressAliasStatus;
- setAliases: React.Dispatch<React.SetStateAction<AddressAliasStatus>>;
- aliasServerError: false | string;
- setAliasServerError: React.Dispatch<React.SetStateAction<false | string>>;
- aliasPrices: null | AliasPrices;
- setAliasPrices: React.Dispatch<React.SetStateAction<null | AliasPrices>>;
updateCashtabState: (
key: string,
value:
@@ -92,7 +82,6 @@
msg: WsMsgClient,
cashtabState: CashtabState,
fiatPrice: null | number,
- aliasesEnabled: boolean,
) => Promise<boolean>;
cashtabState: CashtabState;
}
@@ -105,16 +94,6 @@
const [checkFiatInterval, setCheckFiatInterval] =
useState<null | NodeJS.Timeout>(null);
const [loading, setLoading] = useState<boolean>(true);
- const [aliases, setAliases] = useState<AddressAliasStatus>({
- registered: [],
- pending: [],
- });
- const [aliasPrices, setAliasPrices] = useState<null | AliasPrices>(null);
- const [aliasServerError, setAliasServerError] = useState<false | string>(
- false,
- );
- const [aliasIntervalId, setAliasIntervalId] =
- useState<null | NodeJS.Timeout>(null);
const [chaintipBlockheight, setChaintipBlockheight] = useState(0);
const [cashtabState, setCashtabState] = useState<CashtabState>(
new CashtabState(),
@@ -651,12 +630,7 @@
// Set or update the onMessage handler
// We can only set this when wallet is defined, so we do not set it in loadCashtabState
ws.onMessage = msg => {
- processChronikWsMsg(
- msg,
- cashtabState,
- fiatPrice,
- aliasSettings.aliasEnabled,
- );
+ processChronikWsMsg(msg, cashtabState, fiatPrice);
};
// Check if current subscriptions match current wallet
@@ -710,7 +684,6 @@
msg: WsMsgClient,
cashtabState: CashtabState,
fiatPrice: null | number,
- aliasesEnabled: boolean,
) => {
if (!('msgType' in msg)) {
// No processing chronik error msgs
@@ -729,38 +702,12 @@
return;
}
- // when new blocks are found, refresh alias prices
if (msgType === 'BLK_FINALIZED') {
// Handle avalanche finalized block
const { blockHeight } = msg;
// Set chaintip height
setChaintipBlockheight(blockHeight);
- if (aliasesEnabled) {
- try {
- const aliasPricesResp = await queryAliasServer('prices');
- if (!aliasPricesResp || !('prices' in aliasPricesResp)) {
- throw new Error(
- 'Invalid response from alias prices endpoint',
- );
- }
-
- // Only refresh alias prices if new tiers have been published.
- // The 'prices' API tracks historical pricing via an array of 'prices[].fees'.
- // Therefore a pricing update can be identified as a length change to the 'prices' object.
- if (
- aliasPrices &&
- aliasPrices.prices.length ===
- aliasPricesResp.prices.length
- ) {
- return;
- }
- setAliasPrices(aliasPricesResp);
- } catch (err) {
- setAliasServerError(`${err}`);
- }
- }
-
// If you have unfinalized txs in tx history,
// Update cashtab state on avalanche finalized block
// This will update tx history and finalize any txs that are now finalized
@@ -959,44 +906,6 @@
return setFiatPrice(null);
};
- /**
- * Retrieve registered and pending aliases for this active wallet from alias-server
- * and stores them in the aliases state var for other components to access
- * @param {string} thisAddress the address to be queried for attached aliases
- */
- const refreshAliases = async (thisAddress: string) => {
- try {
- const aliasesForThisAddress = await queryAliasServer(
- 'address',
- thisAddress,
- );
- setAliases({
- registered: (
- aliasesForThisAddress as AddressAliasStatus
- ).registered.sort((a, b) => a.alias.localeCompare(b.alias)),
- pending: (
- aliasesForThisAddress as AddressAliasStatus
- ).pending.sort((a, b) => a.alias.localeCompare(b.alias)),
- });
- setAliasServerError(false);
- // Clear interval if there are no pending aliases
- if (
- (aliasesForThisAddress as AddressAliasStatus).pending.length ===
- 0 &&
- aliasIntervalId
- ) {
- console.info(
- `refreshAliases(): No pending aliases, clearing interval ${aliasIntervalId}`,
- );
- clearInterval(aliasIntervalId);
- }
- } catch (err) {
- const errorMsg = 'Error: Unable to retrieve aliases';
- console.error(`refreshAliases(): ${errorMsg}`, err);
- setAliasServerError(errorMsg);
- }
- };
-
const cashtabBootup = async () => {
await loadCashtabState();
};
@@ -1119,49 +1028,6 @@
updateWebsocket(cashtabState, fiatPrice);
}, [cashtabState, fiatPrice, ws, cashtabLoaded]);
- /**
- * Set an interval to monitor pending alias txs
- * @param {string} address
- * @returns callback function to cleanup interval
- */
- const refreshAliasesOnStartup = async (address: string) => {
- // Initial refresh to ensure `aliases` state var is up to date
- await refreshAliases(address);
- const aliasRefreshInterval = 30000;
- const intervalId = setInterval(async function () {
- if (aliases?.pending?.length > 0) {
- await refreshAliases(address);
- }
- }, aliasRefreshInterval);
- setAliasIntervalId(intervalId);
- // Clear the interval when useWallet unmounts
- return () => clearInterval(intervalId);
- };
-
- useEffect(() => {
- if (
- aliasSettings.aliasEnabled &&
- aliases?.pending?.length > 0 &&
- aliasIntervalId === null &&
- typeof cashtabState.wallets !== 'undefined' &&
- cashtabState.wallets.length > 0
- ) {
- // If
- // 1) aliases are enabled in Cashtab
- // 2) we have pending aliases
- // 3) No interval is set to watch these pending aliases
- // 4) We have an active wallet
- // Set an interval to watch these pending aliases
- refreshAliasesOnStartup(
- (cashtabState.wallets[0].paths.get(1899) as CashtabPathInfo)
- .address,
- );
- } else if (aliases?.pending?.length === 0 && aliasIntervalId !== null) {
- // If we have no pending aliases but we still have an interval to check them, clearInterval
- clearInterval(aliasIntervalId);
- }
- }, [cashtabState.wallets[0]?.name, aliases]);
-
return {
chronik,
agora,
@@ -1171,13 +1037,6 @@
cashtabLoaded,
loading,
apiError,
- refreshAliases,
- aliases,
- setAliases,
- aliasServerError,
- setAliasServerError,
- aliasPrices,
- setAliasPrices,
updateCashtabState,
processChronikWsMsg,
cashtabState,

File Metadata

Mime Type
text/plain
Expires
Tue, May 20, 22:57 (5 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5866114
Default Alt Text
D17399.diff (159 KB)

Event Timeline