Skip to content

Commit

Permalink
refactor: replace old faucet approach with use-faucet (#1484)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsimao authored Jul 31, 2023
1 parent 5336dc7 commit 12b9271
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 92 deletions.
27 changes: 1 addition & 26 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import './i18n';

import { FaucetClient } from '@interlay/interbtc-api';
import { Keyring } from '@polkadot/keyring';
import * as React from 'react';
import { useErrorHandler, withErrorBoundary } from 'react-error-boundary';
import { useQuery } from 'react-query';
import { useDispatch, useSelector } from 'react-redux';
import { Redirect, Route, Switch } from 'react-router-dom';

import { isFaucetLoaded, isVaultClientLoaded } from '@/common/actions/general.actions';
import { isVaultClientLoaded } from '@/common/actions/general.actions';
import { StoreType } from '@/common/types/util.types';
import ErrorFallback from '@/legacy-components/ErrorFallback';
import FullLoadingSpinner from '@/legacy-components/FullLoadingSpinner';
Expand Down Expand Up @@ -49,30 +48,6 @@ const App = (): JSX.Element => {
const isStrategiesEnabled = useFeatureFlag(FeatureFlags.STRATEGIES);
const isOnboardingEnabled = useFeatureFlag(FeatureFlags.ONBOARDING);

// Loads the connection to the faucet - only for testnet purposes
const loadFaucet = React.useCallback(async (): Promise<void> => {
try {
window.faucet = new FaucetClient(window.bridge.api, constants.FAUCET_URL);
dispatch(isFaucetLoaded(true));
} catch (error) {
console.log('[loadFaucet] error.message => ', error.message);
}
}, [dispatch]);

// Loads the faucet
React.useEffect(() => {
if (!bridgeLoaded) return;
// if (process.env.REACT_APP_BITCOIN_NETWORK === BitcoinNetwork.Mainnet) return;

(async () => {
try {
await loadFaucet();
} catch (error) {
console.log('[App React.useEffect 8] error.message => ', error.message);
}
})();
}, [bridgeLoaded, loadFaucet]);

// Detects if the connected account is a vault operator
const { error: vaultsError } = useQuery<GraphqlReturn<any>, Error>(
[GRAPHQL_FETCHER, vaultsByAccountIdQuery(selectedAccount?.address ?? '')],
Expand Down
7 changes: 0 additions & 7 deletions src/common/actions/general.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import {
ADD_NOTIFICATION,
AddNotification,
IS_BRIDGE_LOADED,
IS_FAUCET_LOADED,
IS_VAULT_CLIENT_LOADED,
IsBridgeLoaded,
IsFaucetLoaded,
IsVaultClientLoaded,
SHOW_ACCOUNT_MODAL,
SHOW_BUY_MODAL,
Expand All @@ -23,11 +21,6 @@ export const isBridgeLoaded = (isLoaded = false): IsBridgeLoaded => ({
isLoaded
});

export const isFaucetLoaded = (isLoaded = false): IsFaucetLoaded => ({
type: IS_FAUCET_LOADED,
isLoaded
});

export const isVaultClientLoaded = (isLoaded = false): IsVaultClientLoaded => ({
type: IS_VAULT_CLIENT_LOADED,
isLoaded
Expand Down
6 changes: 0 additions & 6 deletions src/common/types/actions.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Notification, StoreType, TransactionModalData } from './util.types';

// GENERAL ACTIONS
export const IS_BRIDGE_LOADED = 'IS_BRIDGE_LOADED';
export const IS_FAUCET_LOADED = 'IS_FAUCET_LOADED';
export const IS_VAULT_CLIENT_LOADED = 'IS_VAULT_CLIENT_LOADED';
export const INIT_STATE = 'INIT_STATE';
export const UPDATE_BALANCE_POLKA_BTC = 'UPDATE_BALANCE_POLKA_BTC';
Expand All @@ -24,11 +23,6 @@ export interface IsBridgeLoaded {
isLoaded: boolean;
}

export interface IsFaucetLoaded {
type: typeof IS_FAUCET_LOADED;
isLoaded: boolean;
}

export interface IsVaultClientLoaded {
type: typeof IS_VAULT_CLIENT_LOADED;
isLoaded: boolean;
Expand Down
60 changes: 9 additions & 51 deletions src/legacy-components/Topbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@ import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline';
import { Keyring } from '@polkadot/api';
import { InjectedAccountWithMeta } from '@polkadot/extension-inject/types';
import clsx from 'clsx';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';

import { showAccountModalAction, showSignTermsModalAction } from '@/common/actions/general.actions';
import { StoreType } from '@/common/types/util.types';
import { FundWallet, NotificationsPopover } from '@/components';
import { AuthModal, SignTermsModal } from '@/components/AuthModal';
import { ACCOUNT_ID_TYPE_NAME } from '@/config/general';
import { GOVERNANCE_TOKEN } from '@/config/relay-chains';
import { SS58_FORMAT } from '@/constants';
import { useGetBalances } from '@/hooks/api/tokens/use-get-balances';
import { useSignMessage } from '@/hooks/use-sign-message';
import InterlayCaliforniaOutlinedButton from '@/legacy-components/buttons/InterlayCaliforniaOutlinedButton';
import InterlayDefaultContainedButton from '@/legacy-components/buttons/InterlayDefaultContainedButton';
Expand All @@ -23,57 +19,25 @@ import InterlayLink from '@/legacy-components/UI/InterlayLink';
import { KeyringPair, useSubstrate, useSubstrateSecureState } from '@/lib/substrate';
import { BitcoinNetwork } from '@/types/bitcoin';
import { POLKADOT } from '@/utils/constants/relay-chain-names';
import { NotificationToastType, useNotifications } from '@/utils/context/Notifications';
import { useNotifications } from '@/utils/context/Notifications';
import { useFaucet } from '@/utils/hooks/use-faucet';

import ManualIssueExecutionActionsBadge from './ManualIssueExecutionActionsBadge';

const SMALL_SIZE_BUTTON_CLASSES = clsx('leading-7', '!px-3');

const Topbar = (): JSX.Element => {
const { bridgeLoaded, showAccountModal, isSignTermsModalOpen } = useSelector((state: StoreType) => state.general);
const { showAccountModal, isSignTermsModalOpen } = useSelector((state: StoreType) => state.general);
const dispatch = useDispatch();
const { t } = useTranslation();
const { getAvailableBalance } = useGetBalances();
const { setSelectedAccount, removeSelectedAccount } = useSubstrate();
const { selectProps } = useSignMessage();
const notifications = useNotifications();

const governanceTokenBalanceIsZero = getAvailableBalance(GOVERNANCE_TOKEN.ticker)?.isZero();

const handleRequestFromFaucet = async (): Promise<void> => {
if (!selectedAccount) return;

try {
const receiverId = window.bridge.api.createType(ACCOUNT_ID_TYPE_NAME, selectedAccount.address);
await window.faucet.fundAccount(receiverId, GOVERNANCE_TOKEN);

notifications.show('faucet', {
type: NotificationToastType.STANDARD,
props: { variant: 'success', title: t('notifications.funding_account_successful') }
});
} catch (error) {
notifications.show('faucet', {
type: NotificationToastType.STANDARD,
props: { variant: 'error', title: t('notifications.funding_account_failed') }
});
}
};

const [isRequestPending, setIsRequestPending] = React.useState(false);
const { buttonProps, isAvailable } = useFaucet();

const { extensions, selectedAccount } = useSubstrateSecureState();

const handleFundsRequest = async () => {
if (!bridgeLoaded) return;
setIsRequestPending(true);
try {
await handleRequestFromFaucet();
} catch (error) {
console.log('[requestFunds] error.message => ', error.message);
}
setIsRequestPending(false);
};

const handleAccountModalOpen = () => dispatch(showAccountModalAction(true));

const handleAccountModalClose = () => dispatch(showAccountModalAction(false));
Expand Down Expand Up @@ -107,19 +71,13 @@ const Topbar = (): JSX.Element => {
<header className={clsx('p-4', 'flex', 'items-center', 'justify-end', 'space-x-2')}>
<ManualIssueExecutionActionsBadge />
<FundWallet />
{isAvailable && (
<InterlayDenimOrKintsugiMidnightOutlinedButton className={SMALL_SIZE_BUTTON_CLASSES} {...buttonProps}>
{t('request_funds')}
</InterlayDenimOrKintsugiMidnightOutlinedButton>
)}
{selectedAccount !== undefined && (
<>
{process.env.REACT_APP_FAUCET_URL && governanceTokenBalanceIsZero && (
<>
<InterlayDenimOrKintsugiMidnightOutlinedButton
className={SMALL_SIZE_BUTTON_CLASSES}
pending={isRequestPending}
onClick={handleFundsRequest}
>
{t('request_funds')}
</InterlayDenimOrKintsugiMidnightOutlinedButton>
</>
)}
{process.env.REACT_APP_BITCOIN_NETWORK !== BitcoinNetwork.Mainnet && (
<>
<InterlayLink
Expand Down
3 changes: 1 addition & 2 deletions src/store.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { FaucetClient, InterBtcApi } from '@interlay/interbtc-api';
import { InterBtcApi } from '@interlay/interbtc-api';
import { createStore } from 'redux';

import { rootReducer } from './common/reducers/index';

declare global {
interface Window {
bridge: InterBtcApi;
faucet: FaucetClient;
isFetchingActive: boolean;
}
}
Expand Down
79 changes: 79 additions & 0 deletions src/utils/hooks/use-faucet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { FaucetClient } from '@interlay/interbtc-api';
import { useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useMutation } from 'react-query';

import { ACCOUNT_ID_TYPE_NAME } from '@/config/general';
import { GOVERNANCE_TOKEN } from '@/config/relay-chains';
import { BITCOIN_NETWORK, FAUCET_URL } from '@/constants';
import { useGetBalances } from '@/hooks/api/tokens/use-get-balances';
import { useWallet } from '@/hooks/use-wallet';
import { BitcoinNetwork } from '@/types/bitcoin';

import { NotificationToastType, useNotifications } from '../context/Notifications';

type UseFaucetResult = {
isAvailable: boolean;
buttonProps: {
pending: boolean;
onClick: () => void;
};
};

const useFaucet = (): UseFaucetResult => {
const { t } = useTranslation();

const wallet = useWallet();
const notifications = useNotifications();
const { getAvailableBalance } = useGetBalances();

const faucetRef = useRef<FaucetClient>();

const { mutate, isLoading } = useMutation<void, Error, string, unknown>({
mutationFn: async (account: string) => {
const faucet = faucetRef.current || new FaucetClient(window.bridge.api, FAUCET_URL);

faucetRef.current = faucet;

const receiverId = window.bridge.api.createType(ACCOUNT_ID_TYPE_NAME, account);
await faucet.fundAccount(receiverId, GOVERNANCE_TOKEN);
},
onSuccess: () =>
notifications.show('faucet', {
type: NotificationToastType.STANDARD,
props: { variant: 'success', title: t('notifications.funding_account_successful') }
}),
onError: () =>
notifications.show('faucet', {
type: NotificationToastType.STANDARD,
props: { variant: 'error', title: t('notifications.funding_account_failed') }
})
});

const isAvailable = useMemo(() => {
if (isLoading) return true;

const isTestnet = BITCOIN_NETWORK === BitcoinNetwork.Testnet;

const hasGovernanceBalance = getAvailableBalance(GOVERNANCE_TOKEN.ticker)?.isZero();

return !!wallet.account && isTestnet && !!FAUCET_URL && !!hasGovernanceBalance;
}, [getAvailableBalance, isLoading, wallet.account]);

const handleFundAccount = () => {
if (!wallet.account || !isAvailable) return;

mutate(wallet.account.toString());
};

return {
isAvailable,
buttonProps: {
pending: isLoading,
onClick: handleFundAccount
}
};
};

export { useFaucet };
export type { UseFaucetResult };

2 comments on commit 12b9271

@vercel
Copy link

@vercel vercel bot commented on 12b9271 Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 12b9271 Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.