[ENG-3496] fix: utxo 500 issue add a fallback option to our api httpsbtc (#738)

* chore: fix for core local dev

* feat: use btc esplora api with a fallback url

* feat: add fallbackBtcApiUrl to store and refactor network state

* fix: types and comments

* refactor: clean up local react state in changeNetwork

* fix: clean up UI in change network node inputs

* chore: bump core version

* chore: bump core version

* fix: type error from core version

* fix: make fallback btc url not a required field, and update styles

* fix: remove unused remote btcApiUrl config functionality

* chore: not really a factory
This commit is contained in:
Tim Man
2024-01-11 16:55:28 +08:00
committed by GitHub
parent e38eb594bf
commit c5b32b564a
15 changed files with 246 additions and 283 deletions

77
package-lock.json generated
View File

@@ -28,6 +28,7 @@
"axios": "^1.1.3",
"bignumber.js": "^9.1.0",
"bip39": "^3.0.3",
"buffer": "6.0.3",
"c32check": "^2.0.0",
"classnames": "^2.3.2",
"crypto-browserify": "^3.12.0",
@@ -3624,29 +3625,6 @@
"node": ">=8"
}
},
"node_modules/@zondax/ledger-stacks/node_modules/c32check/node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -5418,29 +5396,6 @@
"buffer": "^5.6.0"
}
},
"node_modules/cross-sha256/node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -15829,7 +15784,7 @@
"@stacks/profile": "^6.9.0",
"@stacks/storage": "^6.9.0",
"@stacks/transactions": "^6.9.0",
"buffer": "^6.0.3",
"buffer": "6.0.3",
"c32check": "^2.0.0",
"jsontokens": "^4.0.1",
"triplesec": "^4.0.3",
@@ -17017,7 +16972,7 @@
"requires": {
"@types/bn.js": "^5.1.0",
"@types/node": "^18.0.4",
"buffer": "^6.0.3"
"buffer": "6.0.3"
}
},
"@stacks/network": {
@@ -17061,19 +17016,8 @@
"integrity": "sha512-ADADE/PjAbJRlwpG3ShaOMbBUlJJZO7xaYSRD5Tub6PixQlgR4s36y9cvMf/YRGpkqX+QOxIdMw216iC320q9A==",
"requires": {
"base-x": "^3.0.8",
"buffer": "^5.6.0",
"buffer": "6.0.3",
"cross-sha256": "^1.2.0"
},
"dependencies": {
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
}
}
}
}
@@ -18453,18 +18397,7 @@
"resolved": "https://registry.npmjs.org/cross-sha256/-/cross-sha256-1.2.0.tgz",
"integrity": "sha512-KViLNMDZKV7jwFqjFx+rNhG26amnFYYQ0S+VaFlVvpk8tM+2XbFia/don/SjGHg9WQxnFVi6z64CGPuF3T+nNw==",
"requires": {
"buffer": "^5.6.0"
},
"dependencies": {
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
}
"buffer": "6.0.3"
}
},
"cross-spawn": {

View File

@@ -27,6 +27,7 @@
"axios": "^1.1.3",
"bignumber.js": "^9.1.0",
"bip39": "^3.0.3",
"buffer": "6.0.3",
"c32check": "^2.0.0",
"classnames": "^2.3.2",
"crypto-browserify": "^3.12.0",
@@ -77,8 +78,7 @@
"style": "prettier --write \"src/**/*.{ts,tsx}\"",
"prepare": "husky install"
},
"resolutions": {
"styled-components": "^5",
"overrides": {
"buffer": "6.0.3"
},
"lint-staged": {

View File

@@ -1,21 +1,14 @@
import useWalletSelector from '@hooks/useWalletSelector';
import { getAppConfig } from '@secretkeylabs/xverse-core';
import { ChangeNetworkAction } from '@stores/wallet/actions/actionCreators';
import { useQuery } from '@tanstack/react-query';
import { useDispatch } from 'react-redux';
const useAppConfig = () => {
const { network, networkAddress, btcApiUrl } = useWalletSelector();
const dispatch = useDispatch();
const { network } = useWalletSelector();
return useQuery({
queryKey: ['app-config', network.type, btcApiUrl],
queryKey: ['app-config', network.type],
queryFn: async () => {
const response = await getAppConfig(network.type);
if (response.data.btcApiURL && network.type === 'Mainnet' && !btcApiUrl) {
const updatedNetwork = { ...network, btcApiUrl: response.data.btcApiURL };
dispatch(ChangeNetworkAction(updatedNetwork, networkAddress, ''));
}
return response;
},
});

View File

@@ -3,17 +3,18 @@ import { useMemo } from 'react';
import useWalletSelector from './useWalletSelector';
const useBtcClient = () => {
const { network, btcApiUrl } = useWalletSelector();
const { type, btcApiUrl: remoteBtcApiURL } = network;
const url = btcApiUrl || remoteBtcApiURL;
const { network } = useWalletSelector();
const { type, btcApiUrl, fallbackBtcApiUrl } = network;
const url = btcApiUrl;
const esploraInstance = useMemo(
() =>
new BitcoinEsploraApiProvider({
url,
fallbackUrl: fallbackBtcApiUrl,
network: type,
}),
[url, type],
[url, fallbackBtcApiUrl, type],
);
return esploraInstance;

View File

@@ -3,14 +3,14 @@ import { useMemo } from 'react';
import useWalletSelector from './useWalletSelector';
const useNetworkSelector = () => {
const { network, networkAddress } = useWalletSelector();
const { network } = useWalletSelector();
const selectedNetwork = useMemo(
() =>
network.type === 'Mainnet'
? new StacksMainnet({ url: networkAddress })
: new StacksTestnet({ url: networkAddress }),
[network.type, networkAddress],
? new StacksMainnet({ url: network.address })
: new StacksTestnet({ url: network.address }),
[network.type, network.address],
);
return selectedNetwork;
};

View File

@@ -11,7 +11,9 @@ import {
newWallet,
restoreWalletWithAccounts,
SettingsNetwork,
StacksMainnet,
StacksNetwork,
StacksTestnet,
walletFromSeedPhrase,
} from '@secretkeylabs/xverse-core';
import {
@@ -281,14 +283,9 @@ const useWalletReducer = () => {
dispatch(fetchAccountAction(account, accountsList));
};
const changeNetwork = async (
changedNetwork: SettingsNetwork,
networkObject: StacksNetwork,
networkAddress: string,
btcApiUrl: string,
) => {
const changeNetwork = async (changedNetwork: SettingsNetwork) => {
const seedPhrase = await seedVault.getSeed();
dispatch(ChangeNetworkAction(changedNetwork, networkAddress, btcApiUrl));
dispatch(ChangeNetworkAction(changedNetwork));
const wallet = await walletFromSeedPhrase({
mnemonic: seedPhrase,
index: 0n,
@@ -305,6 +302,10 @@ const useWalletReducer = () => {
stxPublicKey: wallet.stxPublicKey,
};
dispatch(setWalletAction(wallet));
const networkObject =
changedNetwork.type === 'Mainnet'
? new StacksMainnet({ url: changedNetwork.address })
: new StacksTestnet({ url: changedNetwork.address });
try {
await loadActiveAccounts(wallet.seedPhrase, changedNetwork, networkObject, [account]);
} catch (err) {

View File

@@ -1,19 +1,24 @@
import Cross from '@assets/img/settings/x.svg';
import ActionButton from '@components/button';
import BottomBar from '@components/tabBar';
import TopRow from '@components/topRow';
import useWalletReducer from '@hooks/useWalletReducer';
import useWalletSelector from '@hooks/useWalletSelector';
import { SettingsNetwork, StacksMainnet, StacksTestnet } from '@secretkeylabs/xverse-core';
import { initialNetworksList } from '@utils/constants';
import {
SettingsNetwork,
defaultMainnet,
defaultTestnet,
initialNetworksList,
} from '@secretkeylabs/xverse-core';
import { isValidBtcApi, isValidStacksApi } from '@utils/helper';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import styled from 'styled-components';
import NetworkRow from './networkRow';
import NodeInput from './nodeInput';
const Container = styled.div`
${(props) => props.theme.typography.body_medium_m}
display: flex;
flex-direction: column;
flex: 1;
@@ -26,180 +31,155 @@ const Container = styled.div`
}
`;
const NodeInputHeader = styled.div((props) => ({
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingLeft: props.theme.spacing(1),
paddingRight: props.theme.spacing(1),
}));
const ButtonContainer = styled.div`
margin: ${(props) => props.theme.space.m};
`;
const NodeText = styled.h1((props) => ({
...props.theme.body_medium_m,
marginTop: props.theme.spacing(6),
}));
const NodeInputsContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${(props) => props.theme.space.s};
margin-top: ${(props) => props.theme.space.s};
`;
const NodeResetButton = styled.button((props) => ({
background: 'none',
color: props.theme.colors.action.classicLight,
}));
type NodeInputKey = keyof Pick<SettingsNetwork, 'address' | 'btcApiUrl' | 'fallbackBtcApiUrl'>;
const nodeInputs: { key: NodeInputKey; labelKey: string }[] = [
{ key: 'address', labelKey: 'STACKS_URL' },
{ key: 'btcApiUrl', labelKey: 'BTC_URL' },
{ key: 'fallbackBtcApiUrl', labelKey: 'FALLBACK_BTC_URL' },
];
const InputContainer = styled.div((props) => ({
display: 'flex',
alignItems: 'center',
width: '100%',
border: `1px solid ${props.theme.colors.elevation3}`,
backgroundColor: props.theme.colors.elevation_n1,
borderRadius: props.theme.radius(1),
paddingLeft: props.theme.spacing(4),
paddingRight: props.theme.spacing(4),
marginTop: props.theme.spacing(4),
marginBottom: props.theme.spacing(3),
}));
const ButtonContainer = styled.div((props) => ({
marginLeft: props.theme.spacing(8),
marginRight: props.theme.spacing(8),
marginBottom: props.theme.spacing(16),
}));
const ErrorMessage = styled.h2((props) => ({
...props.theme.body_medium_m,
textAlign: 'left',
color: props.theme.colors.feedback.error,
}));
const Input = styled.input((props) => ({
...props.theme.body_medium_m,
height: 44,
display: 'flex',
flex: 1,
backgroundColor: props.theme.colors.elevation_n1,
color: props.theme.colors.white_0,
border: 'none',
}));
const Button = styled.button({
background: 'none',
});
type NodeInputErrors = Record<NodeInputKey, string>;
const initialNodeErrors: NodeInputErrors = {
address: '',
btcApiUrl: '',
fallbackBtcApiUrl: '',
};
function ChangeNetworkScreen() {
const { t } = useTranslation('translation', { keyPrefix: 'SETTING_SCREEN' });
const { network, btcApiUrl, networkAddress } = useWalletSelector();
const [changedNetwork, setChangedNetwork] = useState<SettingsNetwork>(network);
const [stacksUrlError, setStacksUrlError] = useState<string>('');
const [btcURLError, setBtcURLError] = useState('');
const [btcUrl, setBtcUrl] = useState(btcApiUrl || network.btcApiUrl);
const [stacksUrl, setStacksUrl] = useState<string>(networkAddress || network.address);
const [isChangingNetwork, setIsChangingNetwork] = useState<boolean>(false);
const navigate = useNavigate();
const { changeNetwork } = useWalletReducer();
const { network, savedNetworks } = useWalletSelector();
const [isChangingNetwork, setIsChangingNetwork] = useState(false);
const [formErrors, setFormErrors] = useState<NodeInputErrors>(initialNodeErrors);
const [formInputs, setFormInputs] = useState<SettingsNetwork>(network);
const handleBackButtonClick = () => {
navigate('/settings');
};
const onNetworkSelected = (networkSelected: SettingsNetwork) => {
setStacksUrl(networkSelected.address);
setChangedNetwork(networkSelected);
setBtcUrl(networkSelected.btcApiUrl);
setStacksUrlError('');
setBtcURLError('');
setFormInputs(networkSelected);
setFormErrors(initialNodeErrors);
};
const onChangeStacksUrl = (event: React.FormEvent<HTMLInputElement>) => {
setStacksUrlError('');
setStacksUrl(event.currentTarget.value);
// TODO should validate required fields on change
const onChangeCreator = (key: NodeInputKey) => (event: React.ChangeEvent<HTMLInputElement>) => {
setFormErrors((prevErrors) => ({
...prevErrors,
[key]: '',
}));
setFormInputs((prevInputs) => ({
...prevInputs,
[key]: event.target.value,
}));
};
const onChangeBtcApiUrl = (event: React.FormEvent<HTMLInputElement>) => {
setBtcURLError('');
setBtcUrl(event.currentTarget.value);
const onClearCreator = (key: NodeInputKey) => () => {
setFormErrors((prevErrors) => ({
...prevErrors,
[key]: '',
}));
setFormInputs((prevInputs) => ({
...prevInputs,
[key]: '',
}));
};
const onClearStacksUrl = () => {
setStacksUrl('');
};
const onClearBtcUrl = () => {
setBtcUrl('');
};
const onResetBtcUrl = async () => {
setBtcUrl(changedNetwork.btcApiUrl);
setBtcURLError('');
};
const onResetStacks = async () => {
setStacksUrl(changedNetwork.address);
setStacksUrlError('');
const onResetCreator = (key: NodeInputKey) => () => {
setFormErrors((prevErrors) => ({
...prevErrors,
[key]: '',
}));
setFormInputs((prevInputs) => ({
...prevInputs,
[key]: initialNetworksList.find((n) => n.type === formInputs.type)?.[key],
}));
};
const onSubmit = async () => {
setIsChangingNetwork(true);
const [isValidStacksUrl, isValidBtcApiUrl] = await Promise.all([
isValidStacksApi(stacksUrl, changedNetwork.type),
isValidBtcApi(btcUrl, changedNetwork.type),
if (!formInputs.address) {
setFormErrors((prevErrors) => ({
...prevErrors,
address: t('REQUIRED'),
}));
setIsChangingNetwork(false);
return;
}
if (!formInputs.btcApiUrl) {
setFormErrors((prevErrors) => ({
...prevErrors,
btcApiUrl: t('REQUIRED'),
}));
setIsChangingNetwork(false);
return;
}
const [isValidStacksUrl, isValidBtcApiUrl, isValidFallbackBtcApiUrl] = await Promise.all([
isValidStacksApi(formInputs.address, formInputs.type),
isValidBtcApi(formInputs.btcApiUrl, formInputs.type),
!formInputs.fallbackBtcApiUrl || isValidBtcApi(formInputs.fallbackBtcApiUrl, formInputs.type),
]);
if (isValidStacksUrl && isValidBtcApiUrl) {
const networkObject =
changedNetwork.type === 'Mainnet'
? new StacksMainnet({ url: stacksUrl })
: new StacksTestnet({ url: stacksUrl });
await changeNetwork(changedNetwork, networkObject, stacksUrl, btcUrl);
if (isValidStacksUrl && isValidBtcApiUrl && isValidFallbackBtcApiUrl) {
await changeNetwork(formInputs);
navigate('/settings');
} else {
if (!isValidStacksUrl) {
setStacksUrlError(t('INVALID_URL'));
}
if (!isValidBtcApiUrl) {
setBtcURLError(t('INVALID_URL'));
}
setFormErrors({
address: !isValidStacksUrl ? t('INVALID_URL') : '',
btcApiUrl: !isValidBtcApiUrl ? t('INVALID_URL') : '',
fallbackBtcApiUrl: !isValidFallbackBtcApiUrl ? t('INVALID_URL') : '',
});
setIsChangingNetwork(false);
}
};
const savedMainnet = savedNetworks.find((n) => n.type === 'Mainnet');
const savedTestnet = savedNetworks.find((n) => n.type === 'Testnet');
return (
<>
<TopRow title={t('NETWORK')} onClick={handleBackButtonClick} />
<Container>
<NetworkRow
network={initialNetworksList[0]}
isSelected={changedNetwork.type === 'Mainnet'}
network={savedMainnet || defaultMainnet}
isSelected={formInputs.type === 'Mainnet'}
onNetworkSelected={onNetworkSelected}
showDivider
/>
<NetworkRow
network={initialNetworksList[1]}
isSelected={changedNetwork.type === 'Testnet'}
network={savedTestnet || defaultTestnet}
isSelected={formInputs.type === 'Testnet'}
onNetworkSelected={onNetworkSelected}
showDivider={false}
/>
<NodeInputHeader>
<NodeText>{t('NODE')}</NodeText>
<NodeResetButton onClick={onResetStacks}>Reset URL</NodeResetButton>
</NodeInputHeader>
<InputContainer>
<Input onChange={onChangeStacksUrl} value={stacksUrl} />
<Button onClick={onClearStacksUrl}>
<img width={22} height={22} src={Cross} alt="cross" />
</Button>
</InputContainer>
<ErrorMessage>{stacksUrlError}</ErrorMessage>
<NodeInputHeader>
<NodeText>BTC API URL</NodeText>
<NodeResetButton onClick={onResetBtcUrl}>Reset URL</NodeResetButton>
</NodeInputHeader>
<InputContainer>
<Input onChange={onChangeBtcApiUrl} value={btcUrl} />
<Button onClick={onClearBtcUrl}>
<img width={22} height={22} src={Cross} alt="cross" />
</Button>
</InputContainer>
<ErrorMessage>{btcURLError}</ErrorMessage>
<NodeInputsContainer>
{nodeInputs.map(({ key, labelKey }) => (
<NodeInput
key={key}
label={t(labelKey)}
onChange={onChangeCreator(key)}
value={formInputs[key]}
onClear={onClearCreator(key)}
onReset={onResetCreator(key)}
error={formErrors[key]}
/>
))}
</NodeInputsContainer>
</Container>
<ButtonContainer>
<ActionButton
@@ -209,7 +189,6 @@ function ChangeNetworkScreen() {
disabled={isChangingNetwork}
/>
</ButtonContainer>
<BottomBar tab="settings" />
</>
);

View File

@@ -20,7 +20,7 @@ const Button = styled.button<ButtonProps>((props) => ({
}));
const Text = styled.h1<TitleProps>((props) => ({
...props.theme.body_medium_m,
...props.theme.typography.body_medium_m,
color: props.color,
flex: 1,
textAlign: 'left',

View File

@@ -0,0 +1,90 @@
import { XCircle } from '@phosphor-icons/react';
import InputFeedback from '@ui-library/inputFeedback';
import { useTranslation } from 'react-i18next';
import styled, { useTheme } from 'styled-components';
const NodeInputHeader = styled.div`
display: flex;
flex-direction: row;
align-items: flex-end;
justify-content: space-between;
padding-left: ${(props) => props.theme.spacing(1)};
padding-right: ${(props) => props.theme.spacing(1)};
`;
const NodeText = styled.label`
${(props) => props.theme.typography.body_medium_m}
color: ${(props) => props.theme.colors.white_200};
`;
const NodeResetButton = styled.button`
${(props) => props.theme.typography.body_medium_m}
background: none;
color: ${(props) => props.theme.colors.white_200};
`;
// TODO create and use a ui-library input with proper input box styling
const InputContainer = styled.div`
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
border: 1px solid ${(props) => props.theme.colors.elevation3};
background-color: ${(props) => props.theme.colors.elevation_n1};
border-radius: ${(props) => props.theme.radius(1)}px;
padding-left: ${(props) => props.theme.space.m};
padding-right: ${(props) => props.theme.space.m};
margin-top: ${(props) => props.theme.space.s};
margin-bottom: ${(props) => props.theme.space.s};
`;
const Input = styled.input`
${(props) => props.theme.typography.body_medium_m}
height: 44px;
display: flex;
flex: 1;
background-color: ${(props) => props.theme.colors.elevation_n1};
color: ${(props) => props.theme.colors.white_200};
border: none;
`;
const Button = styled.button`
background: none;
`;
function NodeInput({
label,
onChange,
value,
onClear,
onReset,
error,
}: {
label: string;
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
value: string;
onClear: () => void;
onReset: () => void;
error: string;
}) {
const { t } = useTranslation('translation', { keyPrefix: 'SETTING_SCREEN' });
const theme = useTheme();
return (
<div>
<NodeInputHeader>
<NodeText>{label}</NodeText>
<NodeResetButton onClick={onReset}>{t('RESET_TO_DEFAULT')}</NodeResetButton>
</NodeInputHeader>
<InputContainer>
<Input onChange={onChange} value={value} />
<Button onClick={onClear}>
<XCircle size={18} weight="fill" color={theme.colors.white_200} />
</Button>
</InputContainer>
<InputFeedback variant="danger" message={error} />
</div>
);
}
export default NodeInput;

View File

@@ -176,16 +176,10 @@ export function ChangeFiatCurrencyAction(
};
}
export function ChangeNetworkAction(
network: SettingsNetwork,
networkAddress: string | undefined,
btcApiUrl: string,
): actions.ChangeNetwork {
export function ChangeNetworkAction(network: SettingsNetwork): actions.ChangeNetwork {
return {
type: actions.ChangeNetworkKey,
network,
networkAddress,
btcApiUrl,
};
}

View File

@@ -21,30 +21,21 @@ export const SetFeeMultiplierKey = 'SetFeeMultiplierKey';
export const ChangeFiatCurrencyKey = 'ChangeFiatCurrency';
export const ChangeNetworkKey = 'ChangeNetwork';
export const GetActiveAccountsKey = 'GetActiveAccounts';
export const FetchStxWalletDataRequestKey = 'FetchStxWalletDataRequest';
export const SetStxWalletDataKey = 'SetStxWalletDataKey';
export const SetBtcWalletDataKey = 'SetBtcWalletData';
export const SetCoinRatesKey = 'SetCoinRatesKey';
export const SetCoinDataKey = 'SetCoinDataKey';
export const ChangeHasActivatedOrdinalsKey = 'ChangeHasActivatedOrdinalsKey';
export const RareSatsNoticeDismissedKey = 'RareSatsNoticeDismissedKey';
export const ChangeHasActivatedRareSatsKey = 'ChangeHasActivatedRareSatsKey';
export const ChangeHasActivatedRBFKey = 'ChangeHasActivatedRBFKey';
export const ChangeShowBtcReceiveAlertKey = 'ChangeShowBtcReceiveAlertKey';
export const ChangeShowOrdinalReceiveAlertKey = 'ChangeShowOrdinalReceiveAlertKey';
export const ChangeShowDataCollectionAlertKey = 'ChangeShowDataCollectionAlertKey';
export const UpdateLedgerAccountsKey = 'UpdateLedgerAccountsKey';
export const SetBrcCoinsListKey = 'SetBrcCoinsList';
export const SetWalletLockPeriodKey = 'SetWalletLockPeriod';
export const SetWalletUnlockedKey = 'SetWalletUnlocked';
export enum WalletSessionPeriods {
@@ -64,7 +55,8 @@ export interface WalletState {
accountsList: Account[];
ledgerAccountsList: Account[];
selectedAccount: Account | null;
network: SettingsNetwork;
network: SettingsNetwork; // currently selected network urls and type
savedNetworks: SettingsNetwork[]; // previously set network urls for type
encryptedSeed: string;
fiatCurrency: SupportedCurrency;
btcFiatRate: string;
@@ -78,7 +70,6 @@ export interface WalletState {
coins: Coin[];
brcCoinsList: FungibleToken[] | null;
feeMultipliers: AppInfo | null;
networkAddress: string | undefined;
hasActivatedOrdinalsKey: boolean | undefined;
hasActivatedRareSatsKey: boolean | undefined;
hasActivatedRBFKey: boolean | undefined;
@@ -88,7 +79,6 @@ export interface WalletState {
showDataCollectionAlert: boolean | null;
accountType: AccountType | undefined;
accountName: string | undefined;
btcApiUrl: string;
walletLockPeriod: WalletSessionPeriods;
isUnlocked: boolean;
}
@@ -178,8 +168,6 @@ export interface ChangeFiatCurrency {
export interface ChangeNetwork {
type: typeof ChangeNetworkKey;
network: SettingsNetwork;
networkAddress: string | undefined;
btcApiUrl: string;
}
export interface GetActiveAccounts {

View File

@@ -1,10 +1,10 @@
import { initialNetworksList } from '@utils/constants';
import { defaultMainnet, initialNetworksList } from '@secretkeylabs/xverse-core';
import {
AddAccountKey,
ChangeFiatCurrencyKey,
ChangeHasActivatedOrdinalsKey,
ChangeHasActivatedRareSatsKey,
ChangeHasActivatedRBFKey,
ChangeHasActivatedRareSatsKey,
ChangeNetworkKey,
ChangeShowBtcReceiveAlertKey,
ChangeShowDataCollectionAlertKey,
@@ -70,7 +70,8 @@ const initialWalletState: WalletState = {
stxPublicKey: '',
btcPublicKey: '',
ordinalsPublicKey: '',
network: initialNetworksList[0],
network: { ...defaultMainnet },
savedNetworks: initialNetworksList,
accountsList: [],
ledgerAccountsList: [],
selectedAccount: null,
@@ -87,8 +88,6 @@ const initialWalletState: WalletState = {
coins: [],
brcCoinsList: [],
feeMultipliers: null,
networkAddress: undefined,
btcApiUrl: '',
hasActivatedOrdinalsKey: undefined,
hasActivatedRareSatsKey: undefined,
hasActivatedRBFKey: true,
@@ -204,8 +203,10 @@ const walletReducer = (
return {
...state,
network: action.network,
networkAddress: action.networkAddress,
btcApiUrl: action.btcApiUrl,
savedNetworks: [
...state.savedNetworks.filter((n) => n.type !== action.network.type),
action.network,
],
};
case GetActiveAccountsKey:
return {

View File

@@ -1,11 +1,5 @@
/* eslint-disable prefer-destructuring */
import type { NetworkType, SettingsNetwork } from '@secretkeylabs/xverse-core';
import {
BTC_BASE_URI_MAINNET,
BTC_BASE_URI_TESTNET,
HIRO_MAINNET_DEFAULT,
HIRO_TESTNET_DEFAULT,
} from '@secretkeylabs/xverse-core';
import type { NetworkType } from '@secretkeylabs/xverse-core';
export const GAMMA_URL = 'https://gamma.io/';
export const TERMS_LINK = 'https://xverse.app/terms';
@@ -47,21 +41,6 @@ export const BITCOIN_DUST_AMOUNT_SATS = 1500;
export const PAGINATION_LIMIT = 50;
export const REFETCH_UNSPENT_UTXO_TIME = 2 * 60 * 60 * 1000;
export const initialNetworksList: SettingsNetwork[] = [
{
type: 'Mainnet',
address: HIRO_MAINNET_DEFAULT,
btcApiUrl: BTC_BASE_URI_MAINNET,
fallbackBtcApiUrl: '',
},
{
type: 'Testnet',
address: HIRO_TESTNET_DEFAULT,
btcApiUrl: BTC_BASE_URI_TESTNET,
fallbackBtcApiUrl: '',
},
];
/**
* contract id of send_many transaction type
*/

View File

@@ -711,6 +711,11 @@
"DESCRIPTION": "Help improve the app experience, by allowing Xverse to collect anonymized usage data. This data cannot be used to identify your wallet individually.",
"AUTHORIZE_DATA_COLLECTION": "Authorize data collection"
},
"BTC_URL": "BTC URL",
"STACKS_URL": "Stacks URL",
"FALLBACK_BTC_URL": "Fallback BTC URL",
"RESET_TO_DEFAULT": "Reset to default",
"REQUIRED": "Required",
"NETWORK": "Network",
"SECURITY": "Security",
"UPDATE_PASSWORD": "Update Password",

View File

@@ -16,7 +16,6 @@
"isolatedModules": true,
"noEmit": false,
"jsx": "react-jsx",
"rootDir": "src",
"outDir": "build/js",
"baseUrl": "./src",
"paths": {