Compare commits
6 Commits
main
...
exchange-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
466cd85502 | ||
|
|
4ced747e8d | ||
|
|
8e1f497f43 | ||
|
|
a4750921d6 | ||
|
|
51d056d346 | ||
|
|
103d1718ca |
8
public/configs/exchanges.json
Normal file
8
public/configs/exchanges.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "coinbase",
|
||||||
|
"label": "Coinbase",
|
||||||
|
"icon": "/exchanges/coinbase.png",
|
||||||
|
"depositType": "noble"
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
public/exchanges/coinbase.png
Normal file
BIN
public/exchanges/coinbase.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
@ -11,6 +11,7 @@ type ElementProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type StyleProps = {
|
type StyleProps = {
|
||||||
|
className?: string;
|
||||||
hasLogo?: boolean;
|
hasLogo?: boolean;
|
||||||
size?: number;
|
size?: number;
|
||||||
};
|
};
|
||||||
@ -18,7 +19,7 @@ type StyleProps = {
|
|||||||
const DARK_LOGO_MARK_URL = '/logos/logo-mark-dark.svg';
|
const DARK_LOGO_MARK_URL = '/logos/logo-mark-dark.svg';
|
||||||
const LIGHT_LOGO_MARK_URL = '/logos/logo-mark-light.svg';
|
const LIGHT_LOGO_MARK_URL = '/logos/logo-mark-light.svg';
|
||||||
|
|
||||||
export const QrCode = ({ value, hasLogo, size = 300 }: ElementProps & StyleProps) => {
|
export const QrCode = ({ className, value, hasLogo, size = 300 }: ElementProps & StyleProps) => {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const appTheme: AppTheme = useSelector(getAppTheme);
|
const appTheme: AppTheme = useSelector(getAppTheme);
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ export const QrCode = ({ value, hasLogo, size = 300 }: ElementProps & StyleProps
|
|||||||
}
|
}
|
||||||
}, [appTheme, hasLogo]);
|
}, [appTheme, hasLogo]);
|
||||||
|
|
||||||
return <Styled.QrCode ref={ref} />;
|
return <Styled.QrCode className={className} ref={ref} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Styled: Record<string, AnyStyledComponent> = {};
|
const Styled: Record<string, AnyStyledComponent> = {};
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { Button, type ButtonStateConfig, type ButtonProps } from '@/components/B
|
|||||||
|
|
||||||
type ElementProps = {
|
type ElementProps = {
|
||||||
timeoutInSeconds: number;
|
timeoutInSeconds: number;
|
||||||
|
slotFinal?: ReactNode;
|
||||||
} & ButtonProps;
|
} & ButtonProps;
|
||||||
|
|
||||||
export type TimeoutButtonProps = ElementProps;
|
export type TimeoutButtonProps = ElementProps;
|
||||||
@ -15,6 +16,7 @@ export type TimeoutButtonProps = ElementProps;
|
|||||||
export const TimeoutButton = ({
|
export const TimeoutButton = ({
|
||||||
children,
|
children,
|
||||||
timeoutInSeconds,
|
timeoutInSeconds,
|
||||||
|
slotFinal,
|
||||||
...otherProps
|
...otherProps
|
||||||
}: TimeoutButtonProps) => {
|
}: TimeoutButtonProps) => {
|
||||||
const [timeoutDeadline] = useState(Date.now() + timeoutInSeconds * 1000);
|
const [timeoutDeadline] = useState(Date.now() + timeoutInSeconds * 1000);
|
||||||
@ -23,6 +25,8 @@ export const TimeoutButton = ({
|
|||||||
|
|
||||||
const secondsLeft = Math.max(0, (timeoutDeadline - now) / 1000);
|
const secondsLeft = Math.max(0, (timeoutDeadline - now) / 1000);
|
||||||
|
|
||||||
|
if (slotFinal && secondsLeft <= 0) return slotFinal;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
|
|||||||
@ -161,6 +161,7 @@ const useAccountsContext = () => {
|
|||||||
|
|
||||||
// dYdX wallet / onboarding state
|
// dYdX wallet / onboarding state
|
||||||
const [localDydxWallet, setLocalDydxWallet] = useState<LocalWallet>();
|
const [localDydxWallet, setLocalDydxWallet] = useState<LocalWallet>();
|
||||||
|
const [localNobleWallet, setLocalNobleWallet] = useState<LocalWallet>();
|
||||||
const [hdKey, setHdKey] = useState<PrivateInformation>();
|
const [hdKey, setHdKey] = useState<PrivateInformation>();
|
||||||
|
|
||||||
const dydxAccounts = useMemo(() => localDydxWallet?.accounts, [localDydxWallet]);
|
const dydxAccounts = useMemo(() => localDydxWallet?.accounts, [localDydxWallet]);
|
||||||
@ -170,6 +171,11 @@ const useAccountsContext = () => {
|
|||||||
[localDydxWallet]
|
[localDydxWallet]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const nobleAddress = useMemo(
|
||||||
|
() => localNobleWallet?.address,
|
||||||
|
[localNobleWallet]
|
||||||
|
);
|
||||||
|
|
||||||
const setWalletFromEvmSignature = async (signature: string) => {
|
const setWalletFromEvmSignature = async (signature: string) => {
|
||||||
const { wallet, mnemonic, privateKey, publicKey } = await getWalletFromEvmSignature({
|
const { wallet, mnemonic, privateKey, publicKey } = await getWalletFromEvmSignature({
|
||||||
signature,
|
signature,
|
||||||
@ -251,6 +257,7 @@ const useAccountsContext = () => {
|
|||||||
if (hdKey?.mnemonic) {
|
if (hdKey?.mnemonic) {
|
||||||
const nobleWallet = await LocalWallet.fromMnemonic(hdKey.mnemonic, NOBLE_BECH32_PREFIX);
|
const nobleWallet = await LocalWallet.fromMnemonic(hdKey.mnemonic, NOBLE_BECH32_PREFIX);
|
||||||
abacusStateManager.setNobleWallet(nobleWallet);
|
abacusStateManager.setNobleWallet(nobleWallet);
|
||||||
|
setLocalNobleWallet(nobleWallet);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
setNobleWallet();
|
setNobleWallet();
|
||||||
@ -349,6 +356,7 @@ const useAccountsContext = () => {
|
|||||||
localDydxWallet,
|
localDydxWallet,
|
||||||
dydxAccounts,
|
dydxAccounts,
|
||||||
dydxAddress,
|
dydxAddress,
|
||||||
|
nobleAddress,
|
||||||
|
|
||||||
// Onboarding state
|
// Onboarding state
|
||||||
saveHasAcknowledgedTerms,
|
saveHasAcknowledgedTerms,
|
||||||
|
|||||||
@ -26,6 +26,10 @@ class TestFlags {
|
|||||||
get addressOverride():string {
|
get addressOverride():string {
|
||||||
return this.queryParams.address;
|
return this.queryParams.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get showCEXDepositOption() {
|
||||||
|
return !!this.queryParams.cexdeposit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const testFlags = new TestFlags();
|
export const testFlags = new TestFlags();
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import type { EvmAddress } from '@/constants/wallets';
|
|||||||
import { useAccounts, useDebounce, useStringGetter, useSelectedNetwork } from '@/hooks';
|
import { useAccounts, useDebounce, useStringGetter, useSelectedNetwork } from '@/hooks';
|
||||||
import { useAccountBalance, CHAIN_DEFAULT_TOKEN_ADDRESS } from '@/hooks/useAccountBalance';
|
import { useAccountBalance, CHAIN_DEFAULT_TOKEN_ADDRESS } from '@/hooks/useAccountBalance';
|
||||||
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
|
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
|
||||||
import { useWalletConnection } from '@/hooks/useWalletConnection';
|
|
||||||
|
|
||||||
import { layoutMixins } from '@/styles/layoutMixins';
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
import { formMixins } from '@/styles/formMixins';
|
import { formMixins } from '@/styles/formMixins';
|
||||||
@ -41,10 +40,11 @@ import { getNobleChainId, NATIVE_TOKEN_ADDRESS } from '@/lib/squid';
|
|||||||
import { log } from '@/lib/telemetry';
|
import { log } from '@/lib/telemetry';
|
||||||
import { parseWalletError } from '@/lib/wallet';
|
import { parseWalletError } from '@/lib/wallet';
|
||||||
|
|
||||||
import { ChainSelectMenu } from './ChainSelectMenu';
|
import { SourceSelectMenu } from './SourceSelectMenu';
|
||||||
import { TokenSelectMenu } from './TokenSelectMenu';
|
import { TokenSelectMenu } from './TokenSelectMenu';
|
||||||
|
|
||||||
import { DepositButtonAndReceipt } from './DepositForm/DepositButtonAndReceipt';
|
import { DepositButtonAndReceipt } from './DepositForm/DepositButtonAndReceipt';
|
||||||
|
import { NobleDeposit } from '../NobleDeposit';
|
||||||
|
|
||||||
type DepositFormProps = {
|
type DepositFormProps = {
|
||||||
onDeposit?: () => void;
|
onDeposit?: () => void;
|
||||||
@ -57,13 +57,14 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const { selectedNetwork } = useSelectedNetwork();
|
const { selectedNetwork } = useSelectedNetwork();
|
||||||
|
|
||||||
const { evmAddress, signerWagmi, publicClientWagmi } = useAccounts();
|
const { evmAddress, signerWagmi, publicClientWagmi, nobleAddress } = useAccounts();
|
||||||
|
|
||||||
const { addTransferNotification } = useLocalNotifications();
|
const { addTransferNotification } = useLocalNotifications();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
requestPayload,
|
requestPayload,
|
||||||
token,
|
token,
|
||||||
|
exchange,
|
||||||
chain: chainIdStr,
|
chain: chainIdStr,
|
||||||
resources,
|
resources,
|
||||||
summary,
|
summary,
|
||||||
@ -129,14 +130,21 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
if (error) onError?.();
|
if (error) onError?.();
|
||||||
}, [error]);
|
}, [error]);
|
||||||
|
|
||||||
const onSelectChain = useCallback((chain: string) => {
|
const onSelectChain = useCallback((name: string, type: 'chain' | 'exchange') => {
|
||||||
if (chain) {
|
if (name) {
|
||||||
abacusStateManager.clearTransferInputValues();
|
abacusStateManager.clearTransferInputValues();
|
||||||
abacusStateManager.setTransferValue({
|
|
||||||
field: TransferInputField.chain,
|
|
||||||
value: chain,
|
|
||||||
});
|
|
||||||
setFromAmount('');
|
setFromAmount('');
|
||||||
|
if (type === 'chain') {
|
||||||
|
abacusStateManager.setTransferValue({
|
||||||
|
field: TransferInputField.chain,
|
||||||
|
value: name,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
abacusStateManager.setTransferValue({
|
||||||
|
field: TransferInputField.exchange,
|
||||||
|
value: name,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -369,39 +377,49 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Styled.Form onSubmit={onSubmit}>
|
<Styled.Form onSubmit={onSubmit}>
|
||||||
<ChainSelectMenu selectedChain={chainIdStr || undefined} onSelectChain={onSelectChain} />
|
<SourceSelectMenu
|
||||||
<TokenSelectMenu selectedToken={sourceToken || undefined} onSelectToken={onSelectToken} />
|
selectedChain={chainIdStr || undefined}
|
||||||
<Styled.WithDetailsReceipt side="bottom" detailItems={amountInputReceipt}>
|
selectedExchange={exchange || undefined}
|
||||||
<FormInput
|
onSelect={onSelectChain}
|
||||||
type={InputType.Number}
|
/>
|
||||||
onChange={onChangeAmount}
|
{exchange && nobleAddress ? (
|
||||||
label={stringGetter({ key: STRING_KEYS.AMOUNT })}
|
<NobleDeposit />
|
||||||
value={fromAmount}
|
) : (
|
||||||
slotRight={
|
<>
|
||||||
<Styled.FormInputButton size={ButtonSize.XSmall} onClick={onClickMax}>
|
<TokenSelectMenu selectedToken={sourceToken || undefined} onSelectToken={onSelectToken} />
|
||||||
{stringGetter({ key: STRING_KEYS.MAX })}
|
<Styled.WithDetailsReceipt side="bottom" detailItems={amountInputReceipt}>
|
||||||
</Styled.FormInputButton>
|
<FormInput
|
||||||
}
|
type={InputType.Number}
|
||||||
/>
|
onChange={onChangeAmount}
|
||||||
</Styled.WithDetailsReceipt>
|
label={stringGetter({ key: STRING_KEYS.AMOUNT })}
|
||||||
{errorMessage && <AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>}
|
value={fromAmount}
|
||||||
{requireUserActionInWallet && (
|
slotRight={
|
||||||
<AlertMessage type={AlertType.Warning}>
|
<Styled.FormInputButton size={ButtonSize.XSmall} onClick={onClickMax}>
|
||||||
{stringGetter({ key: STRING_KEYS.CHECK_WALLET_FOR_REQUEST })}
|
{stringGetter({ key: STRING_KEYS.MAX })}
|
||||||
</AlertMessage>
|
</Styled.FormInputButton>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Styled.WithDetailsReceipt>
|
||||||
|
{errorMessage && <AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>}
|
||||||
|
{requireUserActionInWallet && (
|
||||||
|
<AlertMessage type={AlertType.Warning}>
|
||||||
|
{stringGetter({ key: STRING_KEYS.CHECK_WALLET_FOR_REQUEST })}
|
||||||
|
</AlertMessage>
|
||||||
|
)}
|
||||||
|
<Styled.Footer>
|
||||||
|
<DepositButtonAndReceipt
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
isLoading={isLoading}
|
||||||
|
chainId={chainId || undefined}
|
||||||
|
setSlippage={onSetSlippage}
|
||||||
|
slippage={slippage}
|
||||||
|
sourceToken={sourceToken || undefined}
|
||||||
|
setRequireUserActionInWallet={setRequireUserActionInWallet}
|
||||||
|
setError={setError}
|
||||||
|
/>
|
||||||
|
</Styled.Footer>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<Styled.Footer>
|
|
||||||
<DepositButtonAndReceipt
|
|
||||||
isDisabled={isDisabled}
|
|
||||||
isLoading={isLoading}
|
|
||||||
chainId={chainId || undefined}
|
|
||||||
setSlippage={onSetSlippage}
|
|
||||||
slippage={slippage}
|
|
||||||
sourceToken={sourceToken || undefined}
|
|
||||||
setRequireUserActionInWallet={setRequireUserActionInWallet}
|
|
||||||
setError={setError}
|
|
||||||
/>
|
|
||||||
</Styled.Footer>
|
|
||||||
</Styled.Form>
|
</Styled.Form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { shallowEqual, useSelector } from 'react-redux';
|
|||||||
|
|
||||||
import { TransferType } from '@/constants/abacus';
|
import { TransferType } from '@/constants/abacus';
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
|
|
||||||
import { useStringGetter } from '@/hooks';
|
import { useStringGetter } from '@/hooks';
|
||||||
|
|
||||||
import { SearchSelectMenu } from '@/components/SearchSelectMenu';
|
import { SearchSelectMenu } from '@/components/SearchSelectMenu';
|
||||||
@ -12,45 +13,78 @@ import { popoverMixins } from '@/styles/popoverMixins';
|
|||||||
|
|
||||||
import { getTransferInputs } from '@/state/inputsSelectors';
|
import { getTransferInputs } from '@/state/inputsSelectors';
|
||||||
|
|
||||||
|
import { isTruthy } from '@/lib/isTruthy';
|
||||||
|
import { testFlags } from '@/lib/testFlags';
|
||||||
|
|
||||||
type ElementProps = {
|
type ElementProps = {
|
||||||
label?: string;
|
label?: string;
|
||||||
|
selectedExchange?: string;
|
||||||
selectedChain?: string;
|
selectedChain?: string;
|
||||||
onSelectChain: (chain: string) => void;
|
onSelect: (name: string, type: 'chain' | 'exchange') => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ChainSelectMenu = ({ label, selectedChain, onSelectChain }: ElementProps) => {
|
export const SourceSelectMenu = ({
|
||||||
|
label,
|
||||||
|
selectedExchange,
|
||||||
|
selectedChain,
|
||||||
|
onSelect,
|
||||||
|
}: ElementProps) => {
|
||||||
const stringGetter = useStringGetter();
|
const stringGetter = useStringGetter();
|
||||||
const { type, depositOptions, withdrawalOptions, resources } =
|
const { type, depositOptions, withdrawalOptions, resources } =
|
||||||
useSelector(getTransferInputs, shallowEqual) || {};
|
useSelector(getTransferInputs, shallowEqual) || {};
|
||||||
const chains =
|
const chains =
|
||||||
(type === TransferType.deposit ? depositOptions : withdrawalOptions)?.chains?.toArray() || [];
|
(type === TransferType.deposit ? depositOptions : withdrawalOptions)?.chains?.toArray() || [];
|
||||||
|
|
||||||
|
const exchanges =
|
||||||
|
(type === TransferType.deposit ? depositOptions : withdrawalOptions)?.exchanges?.toArray() ||
|
||||||
|
[];
|
||||||
|
|
||||||
const chainItems = Object.values(chains).map((chain) => ({
|
const chainItems = Object.values(chains).map((chain) => ({
|
||||||
value: chain.type,
|
value: chain.type,
|
||||||
label: chain.stringKey,
|
label: chain.stringKey,
|
||||||
onSelect: () => {
|
onSelect: () => {
|
||||||
onSelectChain(chain.type);
|
onSelect(chain.type, 'chain');
|
||||||
},
|
},
|
||||||
slotBefore: <Styled.Img src={chain.iconUrl} alt="" />,
|
slotBefore: <Styled.Img src={chain.iconUrl} alt="" />,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const selectedOption = chains.find((item) => item.type === selectedChain);
|
const exchangeItems = Object.values(exchanges).map((exchange) => ({
|
||||||
|
value: exchange.type,
|
||||||
|
label: exchange.string,
|
||||||
|
onSelect: () => {
|
||||||
|
onSelect(exchange.type, 'exchange');
|
||||||
|
},
|
||||||
|
slotBefore: <Styled.Img src={exchange.iconUrl} alt="" />,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const selectedChainOption = chains.find((item) => item.type === selectedChain);
|
||||||
|
const selectedExchangeOption = exchanges.find((item) => item.type === selectedExchange);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SearchSelectMenu
|
<SearchSelectMenu
|
||||||
items={[
|
items={[
|
||||||
{
|
exchangeItems.length > 0 && testFlags.showCEXDepositOption && {
|
||||||
|
group: 'exchanges',
|
||||||
|
groupLabel: stringGetter({ key: STRING_KEYS.EXCHANGES }),
|
||||||
|
items: exchangeItems,
|
||||||
|
},
|
||||||
|
chainItems.length > 0 && {
|
||||||
group: 'chains',
|
group: 'chains',
|
||||||
groupLabel: stringGetter({ key: STRING_KEYS.CHAINS }),
|
groupLabel: stringGetter({ key: STRING_KEYS.CHAINS }),
|
||||||
items: chainItems,
|
items: chainItems,
|
||||||
},
|
},
|
||||||
]}
|
].filter(isTruthy)}
|
||||||
label={label || (type === TransferType.deposit ? 'Source' : 'Destination')}
|
label={label || (type === TransferType.deposit ? 'Source' : 'Destination')}
|
||||||
>
|
>
|
||||||
<Styled.ChainRow>
|
<Styled.ChainRow>
|
||||||
{selectedChain ? (
|
{selectedChainOption ? (
|
||||||
<>
|
<>
|
||||||
<Styled.Img src={selectedOption?.iconUrl} alt="" /> {selectedOption?.stringKey}
|
<Styled.Img src={selectedChainOption.iconUrl} alt="" /> {selectedChainOption.stringKey}
|
||||||
|
</>
|
||||||
|
) : selectedExchangeOption ? (
|
||||||
|
<>
|
||||||
|
<Styled.Img src={selectedExchangeOption.iconUrl} alt="" />{' '}
|
||||||
|
{selectedExchangeOption.string}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
stringGetter({ key: STRING_KEYS.SELECT_CHAIN })
|
stringGetter({ key: STRING_KEYS.SELECT_CHAIN })
|
||||||
@ -38,7 +38,7 @@ import { Tag } from '@/components/Tag';
|
|||||||
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
|
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
|
||||||
import { Icon, IconName } from '@/components/Icon';
|
import { Icon, IconName } from '@/components/Icon';
|
||||||
|
|
||||||
import { ChainSelectMenu } from '@/views/forms/AccountManagementForms/ChainSelectMenu';
|
import { SourceSelectMenu } from '@/views/forms/AccountManagementForms/SourceSelectMenu';
|
||||||
|
|
||||||
import { getSubaccount } from '@/state/accountSelectors';
|
import { getSubaccount } from '@/state/accountSelectors';
|
||||||
import { getTransferInputs } from '@/state/inputsSelectors';
|
import { getTransferInputs } from '@/state/inputsSelectors';
|
||||||
@ -374,10 +374,10 @@ export const WithdrawForm = () => {
|
|||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ChainSelectMenu
|
<SourceSelectMenu
|
||||||
label={stringGetter({ key: STRING_KEYS.NETWORK })}
|
label={stringGetter({ key: STRING_KEYS.NETWORK })}
|
||||||
selectedChain={chainIdStr || undefined}
|
selectedChain={chainIdStr || undefined}
|
||||||
onSelectChain={onSelectChain}
|
onSelect={onSelectChain}
|
||||||
/>
|
/>
|
||||||
</Styled.DestinationRow>
|
</Styled.DestinationRow>
|
||||||
<TokenSelectMenu selectedToken={toToken || undefined} onSelectToken={onSelectToken} />
|
<TokenSelectMenu selectedToken={toToken || undefined} onSelectToken={onSelectToken} />
|
||||||
|
|||||||
131
src/views/forms/NobleDeposit.tsx
Normal file
131
src/views/forms/NobleDeposit.tsx
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||||
|
|
||||||
|
import { OpacityToken } from '@/constants/styles/base';
|
||||||
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
|
|
||||||
|
import { useAccounts, useStringGetter } from '@/hooks';
|
||||||
|
|
||||||
|
import { CopyButton } from '@/components/CopyButton';
|
||||||
|
import { QrCode } from '@/components/QrCode';
|
||||||
|
import { Checkbox } from '@/components/Checkbox';
|
||||||
|
import { Icon, IconName } from '@/components/Icon';
|
||||||
|
import { TimeoutButton } from '@/components/TimeoutButton';
|
||||||
|
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
|
||||||
|
import { WithReceipt } from '@/components/WithReceipt';
|
||||||
|
|
||||||
|
import { generateFadedColorVariant } from '@/lib/styles';
|
||||||
|
|
||||||
|
export const NobleDeposit = () => {
|
||||||
|
const [hasAcknowledged, setHasAcknowledged] = useState(false);
|
||||||
|
const stringGetter = useStringGetter();
|
||||||
|
const { nobleAddress } = useAccounts();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<WithDetailsReceipt
|
||||||
|
side="bottom"
|
||||||
|
detailItems={[
|
||||||
|
{
|
||||||
|
key: 'nobleAddress',
|
||||||
|
label: stringGetter({ key: STRING_KEYS.NOBLE_ADDRESS }),
|
||||||
|
value: nobleAddress,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Styled.QrCodeContainer>
|
||||||
|
<Styled.QrCode size={432} value={nobleAddress || ''} />
|
||||||
|
</Styled.QrCodeContainer>
|
||||||
|
</WithDetailsReceipt>
|
||||||
|
|
||||||
|
<Styled.WaitingSpan>
|
||||||
|
<Styled.CautionIconContainer>
|
||||||
|
<Icon iconName={IconName.CautionCircleStroked} />
|
||||||
|
</Styled.CautionIconContainer>
|
||||||
|
|
||||||
|
<p>{stringGetter({ key: STRING_KEYS.NOBLE_WARNING })}</p>
|
||||||
|
</Styled.WaitingSpan>
|
||||||
|
|
||||||
|
<Styled.WithReceipt
|
||||||
|
slotReceipt={
|
||||||
|
<Styled.CheckboxContainer>
|
||||||
|
<Checkbox
|
||||||
|
checked={hasAcknowledged}
|
||||||
|
onCheckedChange={setHasAcknowledged}
|
||||||
|
id="acknowledge-secret-phase-risk"
|
||||||
|
label={stringGetter({
|
||||||
|
key: STRING_KEYS.NOBLE_ACKNOWLEDGEMENT,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Styled.CheckboxContainer>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TimeoutButton
|
||||||
|
timeoutInSeconds={8}
|
||||||
|
slotFinal={<CopyButton state={{ isDisabled: !hasAcknowledged }} value={nobleAddress} />}
|
||||||
|
/>
|
||||||
|
</Styled.WithReceipt>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Styled: Record<string, AnyStyledComponent> = {};
|
||||||
|
|
||||||
|
Styled.WaitingSpan = styled.span`
|
||||||
|
${layoutMixins.row}
|
||||||
|
gap: 1rem;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.WithReceipt = styled(WithReceipt)`
|
||||||
|
--withReceipt-backgroundColor: var(--color-layer-2);
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.QrCodeContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
padding: 0.5rem;
|
||||||
|
|
||||||
|
background-color: var(--color-layer-2);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.QrCode = styled(QrCode)`
|
||||||
|
max-height: 20rem;
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
max-height: 20rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.CheckboxContainer = styled.div`
|
||||||
|
padding: 1rem;
|
||||||
|
color: var(--color-text-0);
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.CautionIconContainer = styled.div`
|
||||||
|
${layoutMixins.stack}
|
||||||
|
min-width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--color-warning);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 1.125em;
|
||||||
|
height: 1.125em;
|
||||||
|
justify-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
background-color: ${({ theme }) =>
|
||||||
|
generateFadedColorVariant(theme.warning, OpacityToken.Opacity16)};
|
||||||
|
}
|
||||||
|
`;
|
||||||
Loading…
Reference in New Issue
Block a user