Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14864880
D17399.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
159 KB
Subscribers
None
D17399.diff
View Options
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 "{formData.aliasName}" 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
Details
Attached
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)
Attached To
D17399: [Cashtab] Remove legacy aliases implementation
Event Timeline
Log In to Comment