Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query network costs #118

Merged
merged 4 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions packages/site/src/common/config/config.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,18 @@
"mockedAddress": "rXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"fiatDecimals": 2,
"xrplNetwork": {
"baseReserveCostInXrp": "10",
"ownerReserveCostInXrpPerItem": "2"
"mainnet": {
"baseReserveCostInXrp": "1",
"ownerReserveCostInXrpPerItem": "0.2"
},
"testnet": {
"baseReserveCostInXrp": "10",
"ownerReserveCostInXrpPerItem": "2"
},
"devnet": {
"baseReserveCostInXrp": "10",
"ownerReserveCostInXrpPerItem": "2"
}
},
"featureFlags": {
"enablePlayground": false
Expand Down
2 changes: 1 addition & 1 deletion packages/site/src/common/config/config.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export type Config = {
};
mockedAddress: string;
fiatDecimals: number;
xrplNetwork: NetworkReserve;
xrplNetwork: Record<'mainnet' | 'testnet' | 'devnet', NetworkReserve>;
featureFlags: {
enablePlayground: boolean;
};
Expand Down
14 changes: 14 additions & 0 deletions packages/site/src/data-access/repository/xrpl/XrplService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,20 @@ export class XrplService {
});
}

public async getNetworkReserve(): Promise<{ baseReserve?: number; ownerReserve?: number }> {
try {
const client = await this.getClient();
const serverInfo = await client.request({ command: 'server_info' });

return {
baseReserve: serverInfo.result.info.validated_ledger?.reserve_base_xrp,
ownerReserve: serverInfo.result.info.validated_ledger?.reserve_inc_xrp,
};
} catch (e) {
return {};
}
}

public async getAccountInfo(account: string): Promise<any & { signer_lists?: any[] }> {
try {
const client = await this.getClient();
Expand Down
18 changes: 16 additions & 2 deletions packages/site/src/domain/network/controller/NetworkController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import type { MetaMaskRepository } from '../../../data-access/repository/metamas
import { XrplService } from '../../../data-access/repository/xrpl/XrplService';

export default class NetworkController {
private baseReserveCostInXrp: string;

private ownerReserveCostInXrpPerItem: string;

constructor(private readonly metamaskRepository: MetaMaskRepository, private readonly xrplService: XrplService) {}

async load(): Promise<void> {
Expand All @@ -16,22 +20,32 @@ export default class NetworkController {
switch (chainId) {
case NetworkChainId.DEVNET:
node = config.nodeUrls.devnet;
this.baseReserveCostInXrp = config.xrplNetwork.devnet.baseReserveCostInXrp;
this.ownerReserveCostInXrpPerItem = config.xrplNetwork.devnet.ownerReserveCostInXrpPerItem;
break;
case NetworkChainId.TESTNET:
node = config.nodeUrls.testnet;
this.baseReserveCostInXrp = config.xrplNetwork.testnet.baseReserveCostInXrp;
this.ownerReserveCostInXrpPerItem = config.xrplNetwork.testnet.ownerReserveCostInXrpPerItem;
break;
default:
node = config.nodeUrls.mainnet;
this.baseReserveCostInXrp = config.xrplNetwork.mainnet.baseReserveCostInXrp;
this.ownerReserveCostInXrpPerItem = config.xrplNetwork.mainnet.ownerReserveCostInXrpPerItem;
break;
}

await withRetries(async () => this.xrplService.load(node), config.retry.times, config.retry.delay);

const { baseReserve, ownerReserve } = await this.xrplService.getNetworkReserve();
this.baseReserveCostInXrp = String(baseReserve || this.baseReserveCostInXrp);
this.ownerReserveCostInXrpPerItem = String(ownerReserve || this.ownerReserveCostInXrpPerItem);
}

getNetworkReserve(): NetworkReserve {
return {
baseReserveCostInXrp: config.xrplNetwork.baseReserveCostInXrp,
ownerReserveCostInXrpPerItem: config.xrplNetwork.ownerReserveCostInXrpPerItem,
baseReserveCostInXrp: this.baseReserveCostInXrp,
ownerReserveCostInXrpPerItem: this.ownerReserveCostInXrpPerItem,
} as const;
}

Expand Down
10 changes: 5 additions & 5 deletions packages/site/src/ui/locale/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,22 @@
"expendable": "Expendable",
"reserve": "Reserve",
"balanceInfoExplanationTitle": "Why a non-expendable reserve?",
"balanceInfoExplanation": "XRPL accounts require a minimum reserve of 10 XRP to prevent spam and cover the computational costs of maintaining the network, ensuring account integrity and discouraging frivolous account creation. For each OwnerCount, an additional 2 XRP is required.",
"balanceInfoExplanation": "XRPL accounts require a minimum reserve of {{baseReserveCostInXrp}} XRP to prevent spam and cover the computational costs of maintaining the network, ensuring account integrity and discouraging frivolous account creation. For each OwnerCount, an additional {{ownerReserveCostInXrpPerItem}} XRP is required.",
"balanceInfoExplanation2": "The reserve can only be recovered by deleting the account from the XRPL.",
"knowMore": "Know more",
"reviewTxTitle": "Review your transaction",
"reviewTxSubtitle": "Before confirming your transaction, please double-check all the information listed. Once the transaction is signed and broadcasted, it cannot be undone. Ensuring accuracy now can prevent potential issues later.",
"visiteSite": "Visit {{name}}",
"transaction": "Transaction",
"accountNotActiveTitle": "One more step... activate your account.",
"accountNotActiveText": "We need you to activate your account to start using the wallet. Deposit at least 10 XRP in your wallet and you will have the wallet full operative.",
"accountNotActiveText": "We need you to activate your account to start using the wallet. Deposit at least {{baseReserveCostInXrp}} XRP in your wallet and you will have the wallet full operative.",
"accountNotActiveCTA": "Active now",
"activateAccountAlertTitle": "Why is activation necessary?",
"activateAccountAlertText": "In the XRP Ledger (XRPL), a minimum of 10 XRP must be sent to a new account to activate it. This requirement is primarily for security and efficiency reasons.",
"activateAccountAlertText": "In the XRP Ledger (XRPL), a minimum of {{baseReserveCostInXrp}} XRP must be sent to a new account to activate it. This requirement is primarily for security and efficiency reasons.",
"activateAccountText": "You can activate it right now by sending at least ",
"activateAccountText2": "to your address, which you can obtain by copying it below or by scanning the QR code. ",
"inviteToBuyText": "Also, you can buy 10 XRP right now",
"buyXRPCTAButton": "Buy now 10 XRP with credit card",
"inviteToBuyText": "Also, you can buy {{baseReserveCostInXrp}} XRP right now",
"buyXRPCTAButton": "Buy now {{baseReserveCostInXrp}} XRP with credit card",
"activateAccoutModalTitle": "Activate account",
"inviteToGoToFaucetText": "Also, you can get XRP for free from the faucet",
"goToFaucetCTAButton": "Go to faucet",
Expand Down
25 changes: 25 additions & 0 deletions packages/site/src/ui/network/hooks/useNetworkReserve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useQuery } from '@tanstack/react-query';
import { NetworkReserve } from 'common/models';
import ControllerFactory from 'ui/adapter/ControllerFactory';
import useSnapState from 'ui/adapter/state/useSnapState';
import useGetActiveNetwork from 'ui/network/query/useGetActiveNetwork';
import { Queries } from 'ui/query/queries';
import type { QueryOptions, QueryResult } from 'ui/query/react-query-overrides';

export default function useNetworkReserve<T = NetworkReserve>({
enabled = true,
...options
}: Omit<
QueryOptions<NetworkReserve, unknown, T, (Queries | number | undefined | string | undefined)[]>,
'refetchInterval' | ''
> = {}): QueryResult<T> {
const { data: network } = useGetActiveNetwork();
const { isSnapInstalled } = useSnapState();

return useQuery({
enabled: enabled && isSnapInstalled && Boolean(network),
queryKey: [Queries.GET_NETWORK_RESERVE, network?.chainId],
queryFn: async () => ControllerFactory.networkController.getNetworkReserve(),
...options,
});
}
1 change: 1 addition & 0 deletions packages/site/src/ui/query/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export enum Queries {
GET_XRP_PRICE = 'get-xrp-price',
GET_TOKEN_INFO = 'get-token-info',
GET_PROMO_CODE = 'get-promo-code',
GET_NETWORK_RESERVE = 'get-network-reserve',
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Button from 'ui/common/components/input/Button/Button';
import IconCard from 'ui/common/components/surface/IconCard/IconCard';
import { LockIcon } from 'ui/common/icons';
import { useTranslate } from 'ui/locale';
import useNetworkReserve from 'ui/network/hooks/useNetworkReserve';

import ActivateAccountModal from '../ActivateAccountModal/ActivateAccountModal';
import { AccountNotActiveRoot } from './AccountNotActive.styles';
Expand All @@ -17,6 +18,7 @@ export interface AccountNotActiveProps {
function AccountNotActive({ className, ...rest }: AccountNotActiveProps) {
const translate = useTranslate();
const [modalOpened, setModalOpened] = useState(false);
const { data: { baseReserveCostInXrp } = {} } = useNetworkReserve();

return (
<>
Expand All @@ -29,7 +31,7 @@ function AccountNotActive({ className, ...rest }: AccountNotActiveProps) {
{translate('accountNotActiveTitle')}
</Typography>
<Typography variant="body1" light textAlign="center">
{translate('accountNotActiveText')}
{translate('accountNotActiveText', { baseReserveCostInXrp })}
</Typography>
</Col>
<Button onClick={() => setModalOpened(true)} variant="primary">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { NetworkChainId } from 'common/models';
import Button from 'ui/common/components/input/Button/Button';
import OnRampModal from 'ui/fiat-orders/transak/containers/OnRampModal';
import { useTranslate } from 'ui/locale';
import useNetworkReserve from 'ui/network/hooks/useNetworkReserve';
import useGetActiveNetwork from 'ui/network/query/useGetActiveNetwork';
import useFundWallet from 'ui/wallet/query/useFundWallet';

Expand All @@ -15,7 +16,7 @@ export interface ActivateAccountCTAProps {
function ActivateAccountCTA(): JSX.Element {
const { data: network } = useGetActiveNetwork();
const translate = useTranslate();

const { data: { baseReserveCostInXrp } = {} } = useNetworkReserve();
const { showModal } = useModal();
const { mutate, isPending } = useFundWallet();

Expand All @@ -24,10 +25,10 @@ function ActivateAccountCTA(): JSX.Element {
return (
<>
<Typography variant="body1" light>
{translate('inviteToBuyText')}
{translate('inviteToBuyText', { baseReserveCostInXrp })}
</Typography>
<Button variant="primary" onClick={() => showModal(OnRampModal)}>
{translate('buyXRPCTAButton')}
{translate('buyXRPCTAButton', { baseReserveCostInXrp })}
</Button>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Col, Typography, useConfig } from '@peersyst/react-components';
import { Col, Typography } from '@peersyst/react-components';
import clsx from 'clsx';
import { useTheme } from 'styled-components';
import QrCode from 'ui/common/components/display/QrCode/QrCode';
Expand All @@ -7,6 +7,7 @@ import Modal from 'ui/common/components/feedback/Modal/Modal';
import { ModalProps } from 'ui/common/components/feedback/Modal/Modal.types';
import Card from 'ui/common/components/surface/Card/Card';
import { useTranslate } from 'ui/locale';
import useNetworkReserve from 'ui/network/hooks/useNetworkReserve';
import AccountChip from 'ui/wallet/components/display/AccountChip';
import useGetAddress from 'ui/wallet/hooks/useGetAddress';

Expand All @@ -17,8 +18,8 @@ export interface ActivateAccountModalProps extends Omit<ModalProps, 'title'> {}
function ActivateAccountModal({ className, children, ...rest }: ActivateAccountModalProps) {
const { spacing } = useTheme();
const translate = useTranslate();
const { baseReserveCostInXrp } = useConfig('xrplNetwork');
const address = useGetAddress() || '';
const { data: { baseReserveCostInXrp } = {} } = useNetworkReserve();

return (
<Modal title={translate('activateAccoutModalTitle')} className={clsx('ActivateAccountModal', className)} {...rest}>
Expand All @@ -29,7 +30,7 @@ function ActivateAccountModal({ className, children, ...rest }: ActivateAccountM
<Col gap={spacing[2]}>
<Typography variant="body1">{translate('activateAccountAlertTitle')}</Typography>
<Typography variant="body1" light>
{translate('activateAccountAlertText')}
{translate('activateAccountAlertText', { baseReserveCostInXrp })}
</Typography>
</Col>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import InfoDisplay from 'ui/common/components/display/InfoDisplay/InfoDisplay';
import AlertCallout from 'ui/common/components/feedback/AlertCallout/AlertCallout';
import type { ModalProps } from 'ui/common/components/feedback/Modal/Modal.types';
import ExternalLink from 'ui/common/components/navigation/ExternalLink/ExternalLink';
import useNetworkReserve from 'ui/network/hooks/useNetworkReserve';
import useGetBalanceInfo from 'ui/wallet/query/useGetBalanceInfo';

import Modal from '../../../common/components/feedback/Modal/Modal';
Expand All @@ -14,6 +15,7 @@ function BalanceDetailsModal({ ...rest }: ModalProps) {
const reserveInfoLink = useConfig('reserveInfoLink');
const translate = useTranslate();
const { data: { expendable, total, reserve } = {}, isLoading } = useGetBalanceInfo();
const { data: { baseReserveCostInXrp, ownerReserveCostInXrpPerItem } = {} } = useNetworkReserve();

return (
<Modal title={translate('aboutYourBalance')} {...rest}>
Expand Down Expand Up @@ -46,7 +48,7 @@ function BalanceDetailsModal({ ...rest }: ModalProps) {
title={translate('reserve')}
content={
<Balance
balance={reserve?.formatAmount() ?? '0'}
balance={total?.amount !== '0' ? reserve?.formatAmount() ?? '0' : '0'}
variant="body1"
currency={total?.currency}
loading={isLoading}
Expand All @@ -61,7 +63,8 @@ function BalanceDetailsModal({ ...rest }: ModalProps) {
<Typography variant="body1">{translate('balanceInfoExplanationTitle')}</Typography>
<Col>
<Typography variant="body1" light>
{translate('balanceInfoExplanation')} <ExternalLink to={reserveInfoLink}>{`${translate('knowMore')}.`}</ExternalLink>
{translate('balanceInfoExplanation', { baseReserveCostInXrp, ownerReserveCostInXrpPerItem })}{' '}
<ExternalLink to={reserveInfoLink}>{`${translate('knowMore')}.`}</ExternalLink>
</Typography>
<Row>
<Typography variant="body1" light>
Expand Down
Loading