Please write down your wallet 12-word seed and
import it at the new domain.
At the end of the month, cashtabapp.com will
auto-fwd to cashtab.com after one minute.
),
});
}
}, []);
return (
{/*Begin component not included in extension as desktop only*/}
{hasTab && (
)}
{/*End component not included in extension as desktop only*/}
{/*Begin component not included in extension as replaced by open in tab link*/}
{/*Begin component not included in extension as replaced by open in tab link*/}
(
)}
/>
{wallet ? (
) : null}
);
};
export default App;
diff --git a/web/cashtab/src/components/Tokens/CreateTokenForm.js b/web/cashtab/src/components/Tokens/CreateTokenForm.js
index 36bf36a5c..15ffb1e79 100644
--- a/web/cashtab/src/components/Tokens/CreateTokenForm.js
+++ b/web/cashtab/src/components/Tokens/CreateTokenForm.js
@@ -1,385 +1,385 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { AntdFormWrapper } from '@components/Common/EnhancedInputs';
import { TokenCollapse } from '@components/Common/StyledCollapse';
import { currency } from '@components/Common/Ticker.js';
import { WalletContext } from '@utils/context';
import {
isValidTokenName,
isValidTokenTicker,
isValidTokenDecimals,
isValidTokenInitialQty,
isValidTokenDocumentUrl,
} from '@utils/validation';
import { PlusSquareOutlined } from '@ant-design/icons';
import { SmartButton } from '@components/Common/PrimaryButton';
import { Collapse, Form, Input, Modal, notification } from 'antd';
const { Panel } = Collapse;
import Paragraph from 'antd/lib/typography/Paragraph';
import { TokenParamLabel } from '@components/Common/Atoms';
import { TokenReceivedNotificationIcon } from '@components/Common/CustomIcons';
const CreateTokenForm = ({
BCH,
getRestUrl,
createToken,
disabled,
passLoadingStatus,
}) => {
const { wallet } = React.useContext(WalletContext);
// New Token Name
const [newTokenName, setNewTokenName] = useState('');
const [newTokenNameIsValid, setNewTokenNameIsValid] = useState(null);
const handleNewTokenNameInput = e => {
const { value } = e.target;
// validation
setNewTokenNameIsValid(isValidTokenName(value));
setNewTokenName(value);
};
// New Token Ticker
const [newTokenTicker, setNewTokenTicker] = useState('');
const [newTokenTickerIsValid, setNewTokenTickerIsValid] = useState(null);
const handleNewTokenTickerInput = e => {
const { value } = e.target;
// validation
setNewTokenTickerIsValid(isValidTokenTicker(value));
setNewTokenTicker(value);
};
// New Token Decimals
const [newTokenDecimals, setNewTokenDecimals] = useState(0);
const [newTokenDecimalsIsValid, setNewTokenDecimalsIsValid] =
useState(true);
const handleNewTokenDecimalsInput = e => {
const { value } = e.target;
// validation
setNewTokenDecimalsIsValid(isValidTokenDecimals(value));
// Also validate the supply here if it has not yet been set
if (newTokenInitialQtyIsValid !== null) {
setNewTokenInitialQtyIsValid(
isValidTokenInitialQty(value, newTokenDecimals),
);
}
setNewTokenDecimals(value);
};
// New Token Initial Quantity
const [newTokenInitialQty, setNewTokenInitialQty] = useState('');
const [newTokenInitialQtyIsValid, setNewTokenInitialQtyIsValid] =
useState(null);
const handleNewTokenInitialQtyInput = e => {
const { value } = e.target;
// validation
setNewTokenInitialQtyIsValid(
isValidTokenInitialQty(value, newTokenDecimals),
);
setNewTokenInitialQty(value);
};
// New Token document URL
const [newTokenDocumentUrl, setNewTokenDocumentUrl] = useState('');
// Start with this as true, field is not required
const [newTokenDocumentUrlIsValid, setNewTokenDocumentUrlIsValid] =
useState(true);
const handleNewTokenDocumentUrlInput = e => {
const { value } = e.target;
// validation
setNewTokenDocumentUrlIsValid(isValidTokenDocumentUrl(value));
setNewTokenDocumentUrl(value);
};
// New Token fixed supply
// Only allow creation of fixed supply tokens until Minting support is added
// New Token document hash
// Do not include this; questionable value to casual users and requires significant complication
// Only enable CreateToken button if all form entries are valid
let tokenGenesisDataIsValid =
newTokenNameIsValid &&
newTokenTickerIsValid &&
newTokenDecimalsIsValid &&
newTokenInitialQtyIsValid &&
newTokenDocumentUrlIsValid;
// Modal settings
const [showConfirmCreateToken, setShowConfirmCreateToken] = useState(false);
const createPreviewedToken = async () => {
passLoadingStatus(true);
// If data is for some reason not valid here, bail out
if (!tokenGenesisDataIsValid) {
return;
}
// data must be valid and user reviewed to get here
const configObj = {
name: newTokenName,
ticker: newTokenTicker,
documentUrl:
newTokenDocumentUrl === ''
? 'https://cashtabapp.com/'
: newTokenDocumentUrl,
decimals: newTokenDecimals,
initialQty: newTokenInitialQty,
documentHash: '',
};
// create token with data in state fields
try {
const link = await createToken(
BCH,
wallet,
currency.defaultFee,
configObj,
);
notification.success({
message: 'Success',
description: (
Token created! Click to view in block explorer.
),
icon: ,
style: { width: '100%' },
});
} catch (e) {
// Set loading to false here as well, as balance may not change depending on where error occured in try loop
passLoadingStatus(false);
let message;
if (!e.error && !e.message) {
message = `Transaction failed: no response from ${getRestUrl()}.`;
} else if (
/Could not communicate with full node or other external service/.test(
e.error,
)
) {
message = 'Could not communicate with API. Please try again.';
} else if (
e.error &&
e.error.includes(
'too-long-mempool-chain, too many unconfirmed ancestors [limit: 50] (code 64)',
)
) {
message = `The ${currency.ticker} you are trying to send has too many unconfirmed ancestors to send (limit 50). Sending will be possible after a block confirmation. Try again in about 10 minutes.`;
} else {
message = e.message || e.error || JSON.stringify(e);
}
notification.error({
message: 'Error',
description: message,
duration: 5,
});
console.error(e);
}
// Hide the modal
setShowConfirmCreateToken(false);
// Stop spinner
passLoadingStatus(false);
};
return (
<>
setShowConfirmCreateToken(false)}
>
Name: {newTokenName}
Ticker: {newTokenTicker}
Decimals: {newTokenDecimals}
Supply: {newTokenInitialQty}
Document URL:{' '}
{newTokenDocumentUrl === ''
? 'https://cashtabapp.com/'
: newTokenDocumentUrl}
<>
-
+
handleNewTokenNameInput(e)
}
/>
handleNewTokenTickerInput(e)
}
/>
handleNewTokenDecimalsInput(e)
}
/>
handleNewTokenInitialQtyInput(e)
}
/>
handleNewTokenDocumentUrlInput(e)
}
/>
setShowConfirmCreateToken(true)}
disabled={!tokenGenesisDataIsValid}
>
- Create Token
+ Create eToken
>
>
);
};
/*
passLoadingStatus must receive a default prop that is a function
in order to pass the rendering unit test in CreateTokenForm.test.js
status => {console.log(status)} is an arbitrary stub function
*/
CreateTokenForm.defaultProps = {
passLoadingStatus: status => {
console.log(status);
},
};
CreateTokenForm.propTypes = {
BCH: PropTypes.object,
getRestUrl: PropTypes.func,
createToken: PropTypes.func,
disabled: PropTypes.bool,
passLoadingStatus: PropTypes.func,
};
export default CreateTokenForm;
diff --git a/web/cashtab/src/components/Tokens/__tests__/__snapshots__/CreateTokenForm.test.js.snap b/web/cashtab/src/components/Tokens/__tests__/__snapshots__/CreateTokenForm.test.js.snap
index 55d691d4a..e1c0b2ca6 100644
--- a/web/cashtab/src/components/Tokens/__tests__/__snapshots__/CreateTokenForm.test.js.snap
+++ b/web/cashtab/src/components/Tokens/__tests__/__snapshots__/CreateTokenForm.test.js.snap
@@ -1,47 +1,47 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP @generated
exports[`Wallet with BCH balances and tokens and state field 1`] = `