From 1db9be9e62235161e6f58578d3a7d46eb1ec3426 Mon Sep 17 00:00:00 2001 From: thibautbremand Date: Wed, 1 Nov 2023 15:42:42 +0100 Subject: [PATCH 1/5] Fix LP Tokens display --- .../atoms/Tokens/LPToken/LPToken.tsx | 24 +++++++++ .../components/atoms/Tokens/LPToken/index.ts | 1 + .../src/components/atoms/Tokens/index.ts | 1 + .../molecules/TokenDisplay/TokenDisplay.tsx | 2 + .../RenderTokenIcon/RenderTokenIcon.tsx | 14 ++++- .../TokenDisplay/TokenInfo/TokenInfo.tsx | 51 ++++++++++++++++--- .../organisms/TokenListing/TokenListing.tsx | 5 +- .../src/utils/convertHexCurrencyString.ts | 6 +++ packages/extension/src/utils/format.ts | 4 +- .../extension/src/utils/trustlines.test.ts | 27 ++++++++++ packages/extension/src/utils/trustlines.ts | 9 ++++ 11 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 packages/extension/src/components/atoms/Tokens/LPToken/LPToken.tsx create mode 100644 packages/extension/src/components/atoms/Tokens/LPToken/index.ts create mode 100644 packages/extension/src/utils/trustlines.test.ts create mode 100644 packages/extension/src/utils/trustlines.ts diff --git a/packages/extension/src/components/atoms/Tokens/LPToken/LPToken.tsx b/packages/extension/src/components/atoms/Tokens/LPToken/LPToken.tsx new file mode 100644 index 000000000..daf042f9c --- /dev/null +++ b/packages/extension/src/components/atoms/Tokens/LPToken/LPToken.tsx @@ -0,0 +1,24 @@ +import { FC, SVGProps } from 'react'; + +export const LPToken: FC> = (props) => ( + + + + + + + +); diff --git a/packages/extension/src/components/atoms/Tokens/LPToken/index.ts b/packages/extension/src/components/atoms/Tokens/LPToken/index.ts new file mode 100644 index 000000000..265c55b1b --- /dev/null +++ b/packages/extension/src/components/atoms/Tokens/LPToken/index.ts @@ -0,0 +1 @@ +export * from './LPToken'; diff --git a/packages/extension/src/components/atoms/Tokens/index.ts b/packages/extension/src/components/atoms/Tokens/index.ts index d46364979..ab55385fc 100644 --- a/packages/extension/src/components/atoms/Tokens/index.ts +++ b/packages/extension/src/components/atoms/Tokens/index.ts @@ -1,2 +1,3 @@ export * from './GemWallet'; +export * from './LPToken'; export * from './Xrp'; diff --git a/packages/extension/src/components/molecules/TokenDisplay/TokenDisplay.tsx b/packages/extension/src/components/molecules/TokenDisplay/TokenDisplay.tsx index 8dfe35408..f522f7433 100644 --- a/packages/extension/src/components/molecules/TokenDisplay/TokenDisplay.tsx +++ b/packages/extension/src/components/molecules/TokenDisplay/TokenDisplay.tsx @@ -104,6 +104,7 @@ export const TokenDisplay: FC = ({ tokenWarningMessage={tokenWarningMessage} balance={balance} issuerName={tokenData?.issuerName} + issuerAddress={issuer} /> ) : ( = ({ token={token} tokenWarningMessage={tokenWarningMessage} balance={balance} + issuerAddress={issuer} /> )} {onExplainClick ? ( diff --git a/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/RenderTokenIcon/RenderTokenIcon.tsx b/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/RenderTokenIcon/RenderTokenIcon.tsx index 1c7fe0c71..35bfbe68f 100644 --- a/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/RenderTokenIcon/RenderTokenIcon.tsx +++ b/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/RenderTokenIcon/RenderTokenIcon.tsx @@ -2,19 +2,29 @@ import { FC } from 'react'; import { Avatar } from '@mui/material'; -import { GemWallet, Xrp } from '../../../../atoms'; +import { GemWallet, LPToken, Xrp } from '../../../../atoms'; interface RenderTokenIconProps { isXRPToken?: boolean; tokenIconUrl: string; token: string; + isLPToken?: boolean; } -export const RenderTokenIcon: FC = ({ isXRPToken, tokenIconUrl, token }) => { +export const RenderTokenIcon: FC = ({ + isXRPToken, + tokenIconUrl, + token, + isLPToken +}) => { if (isXRPToken) { return ; } + if (isLPToken) { + return ; + } + if (tokenIconUrl) { return ; } diff --git a/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/TokenInfo.tsx b/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/TokenInfo.tsx index 8441829db..e4dbd1918 100644 --- a/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/TokenInfo.tsx +++ b/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/TokenInfo.tsx @@ -1,9 +1,10 @@ -import { Ref, forwardRef } from 'react'; +import { Ref, forwardRef, useMemo } from 'react'; -import { Typography } from '@mui/material'; +import { Tooltip, Typography } from '@mui/material'; import { SECONDARY_GRAY } from '../../../../constants'; import { formatToken } from '../../../../utils'; +import { LP_TOKEN } from '../../../../utils/trustlines'; import { RenderTokenIcon } from './RenderTokenIcon'; export interface TokenInfoProps { @@ -13,6 +14,7 @@ export interface TokenInfoProps { tokenWarningMessage: string | undefined; balance: number; issuerName?: string; + issuerAddress?: string; } const MAX_TOKEN_LENGTH = 5; @@ -25,17 +27,33 @@ export const TokenInfo = forwardRef((props: TokenInfoProps, ref: Ref MAX_TOKEN_LENGTH ? `${token.slice(0, MAX_TOKEN_LENGTH)}...` : token; + const isLPToken = useMemo(() => { + return token === LP_TOKEN; + }, [token]); + + const displayToken = useMemo(() => { + return token.length > MAX_TOKEN_LENGTH && !isLPToken + ? `${token.slice(0, MAX_TOKEN_LENGTH)}...` + : token; + }, [token, isLPToken]); + + const formattedIssuerAddress = useMemo(() => { + return issuerAddress && issuerAddress.length > 20 + ? `${issuerAddress.slice(0, 20)}...` + : issuerAddress; + }, [issuerAddress]); return (
- +
- + {displayToken} {issuerName ? ( ) : null} + {isLPToken ? ( + + + {formattedIssuerAddress} + + + ) : null} {formatToken(balance, token)} diff --git a/packages/extension/src/components/organisms/TokenListing/TokenListing.tsx b/packages/extension/src/components/organisms/TokenListing/TokenListing.tsx index bdda53ecc..43d6d2664 100644 --- a/packages/extension/src/components/organisms/TokenListing/TokenListing.tsx +++ b/packages/extension/src/components/organisms/TokenListing/TokenListing.tsx @@ -17,6 +17,7 @@ import { } from '../../../constants'; import { useLedger, useNetwork, useServer } from '../../../contexts'; import { convertHexCurrencyString, generateKey, saveInChromeSessionStorage } from '../../../utils'; +import { isLPToken } from '../../../utils/trustlines'; import { TokenLoader } from '../../atoms'; import { InformationMessage } from '../../molecules/InformationMessage'; import { TokenDisplay } from '../../molecules/TokenDisplay'; @@ -203,8 +204,10 @@ export const TokenListing: FC = ({ address }) => { onExplainClick={handleOpen} /> {trustLineBalances.map((trustedLine) => { + const isAMMLPToken = isLPToken(trustedLine.currency); const currencyToDisplay = convertHexCurrencyString(trustedLine.currency); - const canBeEdited = trustedLine.trustlineDetails || trustedLine.value !== '0'; + const canBeEdited = + (trustedLine.trustlineDetails || trustedLine.value !== '0') && !isAMMLPToken; const limit = trustedLine.trustlineDetails?.limit || 0; const noRipple = trustedLine.trustlineDetails?.noRipple || false; const flags = noRipple ? TrustSetFlagsBitmask.tfSetNoRipple : undefined; diff --git a/packages/extension/src/utils/convertHexCurrencyString.ts b/packages/extension/src/utils/convertHexCurrencyString.ts index 9ec20be9a..cc2d344c1 100644 --- a/packages/extension/src/utils/convertHexCurrencyString.ts +++ b/packages/extension/src/utils/convertHexCurrencyString.ts @@ -1,11 +1,17 @@ import { Amount, IssuedCurrencyAmount } from 'xrpl/dist/npm/models/common'; import { currencyToHex } from './hexConverter'; +import { isLPToken, LP_TOKEN } from './trustlines'; export const convertHexCurrencyString = (hexCurrency: string): string => { if (hexCurrency.length !== 40) { return hexCurrency; } + + if (isLPToken(hexCurrency)) { + return LP_TOKEN; + } + // Trim trailing zeros in the hex string hexCurrency = hexCurrency.toLowerCase().replace(/0+$/, ''); diff --git a/packages/extension/src/utils/format.ts b/packages/extension/src/utils/format.ts index dea919a03..3e21452cc 100644 --- a/packages/extension/src/utils/format.ts +++ b/packages/extension/src/utils/format.ts @@ -22,6 +22,7 @@ import { } from '@gemwallet/constants'; import { convertHexCurrencyString } from './convertHexCurrencyString'; +import { LP_TOKEN } from './trustlines'; const formatValue = (value: number) => { let decimalLength = value.toString().split('.')[1]?.length || 0; @@ -87,7 +88,8 @@ export const formatToken = (value: number, currency: string = 'XRP', isDrops = f value = Number(dropsToXrp(value)); } - return `${formatValue(value)} ${currency.toUpperCase()}`; + const displayedCurrency = currency === LP_TOKEN ? '' : currency.toUpperCase(); + return `${formatValue(value)} ${displayedCurrency}`; }; // NFTokenCreateOffer diff --git a/packages/extension/src/utils/trustlines.test.ts b/packages/extension/src/utils/trustlines.test.ts new file mode 100644 index 000000000..bf516fbc9 --- /dev/null +++ b/packages/extension/src/utils/trustlines.test.ts @@ -0,0 +1,27 @@ +import { isLPToken } from './trustlines'; + +describe('isLPToken', () => { + it('returns true for hex strings starting with 0x03', () => { + expect(isLPToken('03abcdef')).toBeTruthy(); + }); + + it('returns false for hex strings not starting with 0x03', () => { + expect(isLPToken('04abcdef')).toBeFalsy(); + expect(isLPToken('00abcdef')).toBeFalsy(); + expect(isLPToken('ffabcdef')).toBeFalsy(); + }); + + it('returns false for non-hex strings or strings with less than 2 characters', () => { + expect(isLPToken('g3abcdef')).toBeFalsy(); + expect(isLPToken('z')).toBeFalsy(); + expect(isLPToken('')).toBeFalsy(); + expect(isLPToken('1')).toBeFalsy(); + }); + + // Additional test for case insensitivity, if applicable + it('correctly handles case-insensitive hex values', () => { + expect(isLPToken('03ABCDEF')).toBeTruthy(); + expect(isLPToken('03abcdef')).toBeTruthy(); + expect(isLPToken('03AbCdEf')).toBeTruthy(); + }); +}); diff --git a/packages/extension/src/utils/trustlines.ts b/packages/extension/src/utils/trustlines.ts new file mode 100644 index 000000000..d7c220c1f --- /dev/null +++ b/packages/extension/src/utils/trustlines.ts @@ -0,0 +1,9 @@ +/* + * AMM Related + */ +export const LP_TOKEN = 'LP Token'; + +// Source: https://xrpl.org/automated-market-makers.html#lp-token-currency-codes +export const isLPToken = (hexCurrency: string): boolean => { + return parseInt(hexCurrency.substring(0, 2), 16) === 0x03; +}; From 29ea69161d72f06c864016d8663aba0cf995ce85 Mon Sep 17 00:00:00 2001 From: thibautbremand Date: Wed, 1 Nov 2023 18:36:25 +0100 Subject: [PATCH 2/5] Fix AMM related fields display in Transaction views --- .../XRPLTransaction/XRPLTransaction.tsx | 73 +++++++++++++++++-- .../TransactionDetails/TransactionDetails.tsx | 3 + 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/packages/extension/src/components/molecules/XRPLTransaction/XRPLTransaction.tsx b/packages/extension/src/components/molecules/XRPLTransaction/XRPLTransaction.tsx index 17e3f653b..26d07ce7e 100644 --- a/packages/extension/src/components/molecules/XRPLTransaction/XRPLTransaction.tsx +++ b/packages/extension/src/components/molecules/XRPLTransaction/XRPLTransaction.tsx @@ -1,24 +1,31 @@ import { FC } from 'react'; import { TypographyProps } from '@mui/material'; -import { convertHexToString, Transaction } from 'xrpl'; +import { convertHexToString, Currency, Transaction } from 'xrpl'; import { Amount, Memo, Signer } from 'xrpl/dist/npm/models/common'; import { GlobalFlags } from 'xrpl/dist/npm/models/transactions/common'; import { useWallet } from '../../../contexts'; -import { formatFlags, formatTransferFee, parseAmountObject } from '../../../utils'; +import { + convertHexCurrencyString, + formatFlags, + formatTransferFee, + parseAmountObject +} from '../../../utils'; import { KeyValueDisplay } from '../../atoms'; type XRPLTxProps = { tx: Transaction; displayTransactionType?: boolean; useLegacy?: boolean; + hasMultipleAmounts?: boolean; }; export const XRPLTransaction: FC = ({ tx, displayTransactionType = true, - useLegacy = true + useLegacy = true, + hasMultipleAmounts = false }) => { const { selectedWallet, wallets } = useWallet(); const keyMap: Record JSX.Element | null> = { @@ -32,7 +39,15 @@ export const XRPLTransaction: FC = ({ renderAmount({ title: 'Amount', value, - useLegacy + useLegacy, + hasMultipleAmounts + }), + Amount2: (value: Amount) => + renderAmount({ + title: 'Amount 2', + value, + useLegacy, + hasMultipleAmounts }), Account: (value: string) => wallets[selectedWallet].publicAddress === value @@ -40,6 +55,12 @@ export const XRPLTransaction: FC = ({ : renderSimpleText({ title: 'Account', value, hasTooltip: true, useLegacy }), NFTokenID: (value: string) => renderSimpleText({ title: 'NFT', value, hasTooltip: true, useLegacy }), + DeliverMin: (value: Amount) => + renderAmount({ + title: 'Deliver Min', + value, + useLegacy + }), Destination: (value: string) => renderSimpleText({ title: 'Destination', value, hasTooltip: true, useLegacy }), DestinationTag: (value?: number) => @@ -90,7 +111,9 @@ export const XRPLTransaction: FC = ({ OfferSequence: (value?: number) => renderSimpleText({ title: 'Offer Sequence', value, useLegacy }), EmailHash: (value?: string) => renderSimpleText({ title: 'Email Hash', value, useLegacy }), - NFTokenTaxon: (value?: string) => renderSimpleText({ title: 'Taxon', value, useLegacy }) + NFTokenTaxon: (value?: string) => renderSimpleText({ title: 'Taxon', value, useLegacy }), + Asset: (value: Currency) => renderCurrency({ title: 'Asset', value }), + Asset2: (value: Currency) => renderCurrency({ title: 'Asset 2', value }) }; const renderSimpleText = (params: { @@ -115,14 +138,51 @@ export const XRPLTransaction: FC = ({ ); }; + const renderCurrency = (params: { title: string; value: Currency }) => { + const { title, value } = params; + const currency = + value.currency.length === 40 ? convertHexCurrencyString(value.currency) : value.currency; + const formatted = value.issuer ? `${currency}\n${value.issuer}` : currency; + const hasTooltip = !!value.issuer; + return ( + <> + + + ); + }; + const renderAmount = (params: { title: string; value: Amount; valueTypographyProps?: TypographyProps; useLegacy: boolean; + hasMultipleAmounts?: boolean; }) => { const { title, value, valueTypographyProps, useLegacy } = params; const res = parseAmountObject(value); + if (hasMultipleAmounts) { + const formattedValue = res.issuer + ? `${res.amount} ${res.currency}\n${res.issuer}` + : `${res.amount} ${res.currency}`; + + const hasTooltip = !!res.issuer; + + return ( + + ); + } + return ( <> = ({ const order = [ 'TransactionType', 'Amount', + 'Asset', + 'Amount2', + 'Asset2', 'LimitAmount', 'NFTokenID', 'NFTokenOffers', diff --git a/packages/extension/src/components/organisms/TransactionDetails/TransactionDetails.tsx b/packages/extension/src/components/organisms/TransactionDetails/TransactionDetails.tsx index cc95e180f..20d607406 100644 --- a/packages/extension/src/components/organisms/TransactionDetails/TransactionDetails.tsx +++ b/packages/extension/src/components/organisms/TransactionDetails/TransactionDetails.tsx @@ -29,6 +29,8 @@ export const TransactionDetails: FC = ({ return ; } + const hasMultipleAmounts = 'Amount' in txParam && 'Amount2' in txParam; + return ( <> = ({ tx={txParam} useLegacy={false} displayTransactionType={displayTransactionType} + hasMultipleAmounts={hasMultipleAmounts} /> } dataName="Transaction details" From 885f4864b31f7e01ca7b075b3dd38f4092f78652 Mon Sep 17 00:00:00 2001 From: thibautbremand Date: Wed, 1 Nov 2023 19:21:57 +0100 Subject: [PATCH 3/5] Format AMM Flags in Transaction views --- packages/constants/src/xrpl/basic.types.ts | 8 +- packages/extension/src/utils/format.test.ts | 86 ++++++++++ packages/extension/src/utils/format.ts | 168 +++++++++++++++++++- 3 files changed, 258 insertions(+), 4 deletions(-) diff --git a/packages/constants/src/xrpl/basic.types.ts b/packages/constants/src/xrpl/basic.types.ts index 946781a29..83465d0f4 100644 --- a/packages/constants/src/xrpl/basic.types.ts +++ b/packages/constants/src/xrpl/basic.types.ts @@ -4,7 +4,9 @@ import { TrustSetFlagsInterface, NFTokenCreateOfferFlagsInterface, AccountSetFlagsInterface, - OfferCreateFlagsInterface + OfferCreateFlagsInterface, + AMMDepositFlagsInterface, + AMMWithdrawFlagsInterface } from 'xrpl'; export interface Memo { @@ -34,3 +36,7 @@ export type CreateNFTOfferFlags = NFTokenCreateOfferFlagsInterface | number; export type SetAccountFlags = AccountSetFlagsInterface | number; export type CreateOfferFlags = OfferCreateFlagsInterface | number; + +export type DepositAMMFlags = AMMDepositFlagsInterface | number; + +export type WithdrawAMMFlags = AMMWithdrawFlagsInterface | number; diff --git a/packages/extension/src/utils/format.test.ts b/packages/extension/src/utils/format.test.ts index a1daa3ad3..acbc50fe0 100644 --- a/packages/extension/src/utils/format.test.ts +++ b/packages/extension/src/utils/format.test.ts @@ -299,6 +299,92 @@ describe('formatFlags', () => { }; expect(formatFlags(flags, 'TrustSet')).toBe('None'); }); + + // AMMDeposit + it('should format AMMDeposit flags correctly when given as a number', () => { + const flags = 589824; + const expectedResult = 'LP Token\nSingle Asset'; + expect(formatFlags(flags, 'AMMDeposit')).toBe(expectedResult); + }); + + it('should format AMMDeposit flags correctly when given as an object', () => { + const flags = { + tfLPToken: true, + tfSingleAsset: true, + tfTwoAsset: false, + tfOneAssetLPToken: false, + tfLimitLPToken: false + }; + const expectedResult = 'LP Token\nSingle Asset'; + expect(formatFlags(flags, 'AMMDeposit')).toBe(expectedResult); + }); + + it('should not show false flags for AMMDeposit', () => { + const flags = { + tfLPToken: false, + tfSingleAsset: false, + tfTwoAsset: false, + tfOneAssetLPToken: true, + tfLimitLPToken: false + }; + expect(formatFlags(flags, 'AMMDeposit')).toBe('One Asset LP Token'); + }); + + it('should format AMMDeposit flags to "None" when no flags are set as a number', () => { + const flags = 0; // no flags are set + expect(formatFlags(flags, 'AMMDeposit')).toBe('None'); + }); + + it('should format AMMDeposit flags to "None" when no flags are set as an object', () => { + const flags = { + tfLPToken: false, + tfSingleAsset: false, + tfTwoAsset: false, + tfOneAssetLPToken: false, + tfLimitLPToken: false + }; + expect(formatFlags(flags, 'AMMDeposit')).toBe('None'); + }); + + //AMMWithdraw + it('should format AMMWithdraw flags correctly when given as a number', () => { + const flags = 589824; + const expectedResult = 'LP Token\nSingle Asset'; + expect(formatFlags(flags, 'AMMWithdraw')).toBe(expectedResult); + }); + + it('should format AMMWithdraw flags correctly when given as an object', () => { + const flags = { + tfLPToken: true, + tfSingleAsset: true + // other flags as false + }; + const expectedResult = 'LP Token\nSingle Asset'; + expect(formatFlags(flags, 'AMMWithdraw')).toBe(expectedResult); + }); + + it('should not show false flags for AMMWithdraw', () => { + const flags = { + tfLPToken: false, + tfSingleAsset: true + // other flags as false + }; + expect(formatFlags(flags, 'AMMWithdraw')).toBe('Single Asset'); + }); + + it('should format AMMWithdraw flags to "None" when no flags are set as a number', () => { + const flags = 0; // no flags are set + expect(formatFlags(flags, 'AMMWithdraw')).toBe('None'); + }); + + it('should format AMMWithdraw flags to "None" when no flags are set as an object', () => { + const flags = { + tfLPToken: false, + tfSingleAsset: false + // other flags as false + }; + expect(formatFlags(flags, 'AMMWithdraw')).toBe('None'); + }); }); describe('formatFlagsToNumber', () => { diff --git a/packages/extension/src/utils/format.ts b/packages/extension/src/utils/format.ts index 3e21452cc..b78c7046a 100644 --- a/packages/extension/src/utils/format.ts +++ b/packages/extension/src/utils/format.ts @@ -7,7 +7,9 @@ import { Transaction, TrustSetFlags as TrustSetFlagsBitmask, PaymentFlags as PaymentFlagsBitmask, - AccountSetTfFlags as AccountSetTfFlagsBitmask + AccountSetTfFlags as AccountSetTfFlagsBitmask, + AMMDepositFlags as AMMDepositFlagsBitmask, + AMMWithdrawFlags as AMMWithdrawFlagsBitmask } from 'xrpl'; import { Amount, IssuedCurrencyAmount } from 'xrpl/dist/npm/models/common'; import { GlobalFlags } from 'xrpl/dist/npm/models/transactions/common'; @@ -15,10 +17,12 @@ import { GlobalFlags } from 'xrpl/dist/npm/models/transactions/common'; import { CreateNFTOfferFlags, CreateOfferFlags, + DepositAMMFlags, MintNFTFlags, PaymentFlags, SetAccountFlags, - TrustSetFlags + TrustSetFlags, + WithdrawAMMFlags } from '@gemwallet/constants'; import { convertHexCurrencyString } from './convertHexCurrencyString'; @@ -123,6 +127,15 @@ const LABEL_SET_NO_RIPPLE = 'Set No Ripple'; const LABEL_CLEAR_NO_RIPPLE = 'Clear No Ripple'; const LABEL_SET_FREEZE = 'Set Freeze'; const LABEL_CLEAR_FREEZE = 'Clear Freeze'; +// AMMDeposit +const LABEL_LPTOKEN = 'LP Token'; +const LABEL_SINGLE_ASSET = 'Single Asset'; +const LABEL_TWO_ASSET = 'Two Assets'; +const LABEL_ONE_ASSET_LPTOKEN = 'One Asset LP Token'; +const LABEL_LIMIT_LPTOKEN = 'Limit LP Token'; +// AMMWithdraw +const LABEL_WITHDRAW_ALL = 'Withdraw All'; +const LABEL_ONE_ASSET_WITHDRAW_ALL = 'One Asset Withdraw All'; export const formatFlags = ( flags: | PaymentFlags @@ -131,8 +144,19 @@ export const formatFlags = ( | CreateNFTOfferFlags | SetAccountFlags | CreateOfferFlags + | DepositAMMFlags + | WithdrawAMMFlags | GlobalFlags, - flagsType?: 'NFTokenCreateOffer' | 'NFTokenMint' | string + flagsType?: + | 'NFTokenCreateOffer' + | 'NFTokenMint' + | 'OfferCreate' + | 'Payment' + | 'AccountSet' + | 'TrustSet' + | 'AMMDeposit' + | 'AMMWithdraw' + | string ) => { if (flagsType === 'NFTokenCreateOffer') { if (typeof flags === 'number') { @@ -415,6 +439,144 @@ export const formatFlags = ( } } + if (flagsType === 'AMMDeposit') { + if (typeof flags === 'number') { + let flagDescriptions: string[] = []; + + if (flags & AMMDepositFlagsBitmask.tfLPToken) { + flagDescriptions.push(LABEL_LPTOKEN); + } + if (flags & AMMDepositFlagsBitmask.tfSingleAsset) { + flagDescriptions.push(LABEL_SINGLE_ASSET); + } + if (flags & AMMDepositFlagsBitmask.tfTwoAsset) { + flagDescriptions.push(LABEL_TWO_ASSET); + } + if (flags & AMMDepositFlagsBitmask.tfOneAssetLPToken) { + flagDescriptions.push(LABEL_ONE_ASSET_LPTOKEN); + } + if (flags & AMMDepositFlagsBitmask.tfLimitLPToken) { + flagDescriptions.push(LABEL_LIMIT_LPTOKEN); + } + + return flagDescriptions.length > 0 ? flagDescriptions.join('\n') : 'None'; + } + + // If flags is an object + if (typeof flags === 'object') { + const formattedFlags = Object.entries(flags) + .map(([key, value]) => { + if (key === 'tfLPToken' && value) { + return `${LABEL_LPTOKEN}`; + } + if (key === 'tfSingleAsset' && value) { + return `${LABEL_SINGLE_ASSET}`; + } + if (key === 'tfTwoAsset' && value) { + return `${LABEL_TWO_ASSET}`; + } + if (key === 'tfOneAssetLPToken' && value) { + return `${LABEL_ONE_ASSET_LPTOKEN}`; + } + if (key === 'tfLimitLPToken' && value) { + return `${LABEL_LIMIT_LPTOKEN}`; + } + + if ( + [ + 'tfLPToken', + 'tfSingleAsset', + 'tfTwoAsset', + 'tfOneAssetLPToken', + 'tfLimitLPToken' + ].includes(key) + ) { + return null; + } + return `${key}: ${value}`; + }) + .filter((flag) => flag !== null); + + return formattedFlags.length > 0 ? formattedFlags.join('\n') : 'None'; + } + } + + if (flagsType === 'AMMWithdraw') { + if (typeof flags === 'number') { + let flagDescriptions: string[] = []; + + if (flags & AMMWithdrawFlagsBitmask.tfLPToken) { + flagDescriptions.push(LABEL_LPTOKEN); + } + if (flags & AMMWithdrawFlagsBitmask.tfWithdrawAll) { + flagDescriptions.push(LABEL_WITHDRAW_ALL); + } + if (flags & AMMWithdrawFlagsBitmask.tfOneAssetWithdrawAll) { + flagDescriptions.push(LABEL_ONE_ASSET_WITHDRAW_ALL); + } + if (flags & AMMWithdrawFlagsBitmask.tfSingleAsset) { + flagDescriptions.push(LABEL_SINGLE_ASSET); + } + if (flags & AMMWithdrawFlagsBitmask.tfTwoAsset) { + flagDescriptions.push(LABEL_TWO_ASSET); + } + if (flags & AMMWithdrawFlagsBitmask.tfOneAssetLPToken) { + flagDescriptions.push(LABEL_ONE_ASSET_LPTOKEN); + } + if (flags & AMMWithdrawFlagsBitmask.tfLimitLPToken) { + flagDescriptions.push(LABEL_LIMIT_LPTOKEN); + } + + return flagDescriptions.length > 0 ? flagDescriptions.join('\n') : 'None'; + } + + // If flags is an object + if (typeof flags === 'object') { + const formattedFlags = Object.entries(flags) + .map(([key, value]) => { + if (key === 'tfLPToken' && value) { + return `${LABEL_LPTOKEN}`; + } + if (key === 'tfWithdrawAll' && value) { + return `${LABEL_WITHDRAW_ALL}`; + } + if (key === 'tfOneAssetWithdrawAll' && value) { + return `${LABEL_ONE_ASSET_WITHDRAW_ALL}`; + } + if (key === 'tfSingleAsset' && value) { + return `${LABEL_SINGLE_ASSET}`; + } + if (key === 'tfTwoAsset' && value) { + return `${LABEL_TWO_ASSET}`; + } + if (key === 'tfOneAssetLPToken' && value) { + return `${LABEL_ONE_ASSET_LPTOKEN}`; + } + if (key === 'tfLimitLPToken' && value) { + return `${LABEL_LIMIT_LPTOKEN}`; + } + + if ( + [ + 'tfLPToken', + 'tfWithdrawAll', + 'tfOneAssetWithdrawAll', + 'tfSingleAsset', + 'tfTwoAsset', + 'tfOneAssetLPToken', + 'tfLimitLPToken' + ].includes(key) + ) { + return null; + } + return `${key}: ${value}`; + }) + .filter((flag) => flag !== null); + + return formattedFlags.length > 0 ? formattedFlags.join('\n') : 'None'; + } + } + // Fallback if (typeof flags === 'object') { const formattedFlags = Object.entries(flags) From 3149b9683a69b62f12c6e5a1f179756d0d2bcae3 Mon Sep 17 00:00:00 2001 From: thibautbremand Date: Thu, 2 Nov 2023 19:51:31 +0100 Subject: [PATCH 4/5] Format missing transaction fields --- .../molecules/XRPLTransaction/XRPLTransaction.tsx | 10 +++++++++- .../TransactionDetails/TransactionDetails.tsx | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/extension/src/components/molecules/XRPLTransaction/XRPLTransaction.tsx b/packages/extension/src/components/molecules/XRPLTransaction/XRPLTransaction.tsx index 26d07ce7e..5194aaa95 100644 --- a/packages/extension/src/components/molecules/XRPLTransaction/XRPLTransaction.tsx +++ b/packages/extension/src/components/molecules/XRPLTransaction/XRPLTransaction.tsx @@ -113,7 +113,13 @@ export const XRPLTransaction: FC = ({ EmailHash: (value?: string) => renderSimpleText({ title: 'Email Hash', value, useLegacy }), NFTokenTaxon: (value?: string) => renderSimpleText({ title: 'Taxon', value, useLegacy }), Asset: (value: Currency) => renderCurrency({ title: 'Asset', value }), - Asset2: (value: Currency) => renderCurrency({ title: 'Asset 2', value }) + Asset2: (value: Currency) => renderCurrency({ title: 'Asset 2', value }), + SendMax: (value: Amount) => + renderAmount({ + title: 'Send Max', + value, + useLegacy + }) }; const renderSimpleText = (params: { @@ -271,6 +277,8 @@ export const XRPLTransaction: FC = ({ 'TakerPays', 'Destination', 'DestinationTag', + 'DeliverMin', + 'SendMax', 'NFTokenSellOffer', 'NFTokenBuyOffer', 'NFTokenBrokerFee', diff --git a/packages/extension/src/components/organisms/TransactionDetails/TransactionDetails.tsx b/packages/extension/src/components/organisms/TransactionDetails/TransactionDetails.tsx index 20d607406..066ce701c 100644 --- a/packages/extension/src/components/organisms/TransactionDetails/TransactionDetails.tsx +++ b/packages/extension/src/components/organisms/TransactionDetails/TransactionDetails.tsx @@ -29,7 +29,9 @@ export const TransactionDetails: FC = ({ return ; } - const hasMultipleAmounts = 'Amount' in txParam && 'Amount2' in txParam; + const hasMultipleAmounts = + ('Amount' in txParam && 'Amount2' in txParam) || + ('DeliverMin' in txParam && 'SendMax' in txParam); return ( <> From 259f3450d3d7ef40be6bc293108c8cb954fb8b57 Mon Sep 17 00:00:00 2001 From: thibautbremand Date: Thu, 2 Nov 2023 19:56:33 +0100 Subject: [PATCH 5/5] Fix post review --- .../TokenDisplay/TokenInfo/TokenInfo.tsx | 15 +++++++++------ .../XRPLTransaction/XRPLTransaction.tsx | 17 +++++++---------- .../src/utils/convertHexCurrencyString.ts | 4 ++-- packages/extension/src/utils/format.ts | 4 ++-- packages/extension/src/utils/trustlines.ts | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/TokenInfo.tsx b/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/TokenInfo.tsx index e4dbd1918..8e7d3192d 100644 --- a/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/TokenInfo.tsx +++ b/packages/extension/src/components/molecules/TokenDisplay/TokenInfo/TokenInfo.tsx @@ -4,7 +4,7 @@ import { Tooltip, Typography } from '@mui/material'; import { SECONDARY_GRAY } from '../../../../constants'; import { formatToken } from '../../../../utils'; -import { LP_TOKEN } from '../../../../utils/trustlines'; +import { LP_TOKEN_NAME } from '../../../../utils/trustlines'; import { RenderTokenIcon } from './RenderTokenIcon'; export interface TokenInfoProps { @@ -18,6 +18,7 @@ export interface TokenInfoProps { } const MAX_TOKEN_LENGTH = 5; +const MAX_ISSUER_LENGTH = 20; export const TokenInfo = forwardRef((props: TokenInfoProps, ref: Ref) => { const { @@ -32,7 +33,7 @@ export const TokenInfo = forwardRef((props: TokenInfoProps, ref: Ref { - return token === LP_TOKEN; + return token === LP_TOKEN_NAME; }, [token]); const displayToken = useMemo(() => { @@ -42,8 +43,8 @@ export const TokenInfo = forwardRef((props: TokenInfoProps, ref: Ref { - return issuerAddress && issuerAddress.length > 20 - ? `${issuerAddress.slice(0, 20)}...` + return issuerAddress && issuerAddress.length > MAX_ISSUER_LENGTH + ? `${issuerAddress.slice(0, MAX_ISSUER_LENGTH)}...` : issuerAddress; }, [issuerAddress]); @@ -74,13 +75,15 @@ export const TokenInfo = forwardRef((props: TokenInfoProps, ref: Ref = ({ const renderCurrency = (params: { title: string; value: Currency }) => { const { title, value } = params; - const currency = - value.currency.length === 40 ? convertHexCurrencyString(value.currency) : value.currency; + const currency = convertHexCurrencyString(value.currency); const formatted = value.issuer ? `${currency}\n${value.issuer}` : currency; const hasTooltip = !!value.issuer; return ( - <> - - + ); }; diff --git a/packages/extension/src/utils/convertHexCurrencyString.ts b/packages/extension/src/utils/convertHexCurrencyString.ts index cc2d344c1..5e92b4584 100644 --- a/packages/extension/src/utils/convertHexCurrencyString.ts +++ b/packages/extension/src/utils/convertHexCurrencyString.ts @@ -1,7 +1,7 @@ import { Amount, IssuedCurrencyAmount } from 'xrpl/dist/npm/models/common'; import { currencyToHex } from './hexConverter'; -import { isLPToken, LP_TOKEN } from './trustlines'; +import { isLPToken, LP_TOKEN_NAME } from './trustlines'; export const convertHexCurrencyString = (hexCurrency: string): string => { if (hexCurrency.length !== 40) { @@ -9,7 +9,7 @@ export const convertHexCurrencyString = (hexCurrency: string): string => { } if (isLPToken(hexCurrency)) { - return LP_TOKEN; + return LP_TOKEN_NAME; } // Trim trailing zeros in the hex string diff --git a/packages/extension/src/utils/format.ts b/packages/extension/src/utils/format.ts index b78c7046a..c82bf7f8b 100644 --- a/packages/extension/src/utils/format.ts +++ b/packages/extension/src/utils/format.ts @@ -26,7 +26,7 @@ import { } from '@gemwallet/constants'; import { convertHexCurrencyString } from './convertHexCurrencyString'; -import { LP_TOKEN } from './trustlines'; +import { LP_TOKEN_NAME } from './trustlines'; const formatValue = (value: number) => { let decimalLength = value.toString().split('.')[1]?.length || 0; @@ -92,7 +92,7 @@ export const formatToken = (value: number, currency: string = 'XRP', isDrops = f value = Number(dropsToXrp(value)); } - const displayedCurrency = currency === LP_TOKEN ? '' : currency.toUpperCase(); + const displayedCurrency = currency === LP_TOKEN_NAME ? '' : currency.toUpperCase(); return `${formatValue(value)} ${displayedCurrency}`; }; diff --git a/packages/extension/src/utils/trustlines.ts b/packages/extension/src/utils/trustlines.ts index d7c220c1f..38c96ef5d 100644 --- a/packages/extension/src/utils/trustlines.ts +++ b/packages/extension/src/utils/trustlines.ts @@ -1,7 +1,7 @@ /* * AMM Related */ -export const LP_TOKEN = 'LP Token'; +export const LP_TOKEN_NAME = 'LP Token'; // Source: https://xrpl.org/automated-market-makers.html#lp-token-currency-codes export const isLPToken = (hexCurrency: string): boolean => {