Skip to content

Commit

Permalink
Merge pull request #145 from multiversx/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
radumojic authored Dec 6, 2024
2 parents 1fd610b + 2534783 commit d3dc75d
Show file tree
Hide file tree
Showing 21 changed files with 589 additions and 679 deletions.
18 changes: 8 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
"@fortawesome/free-solid-svg-icons": "6.5.1",
"@fortawesome/react-fontawesome": "0.2.0",
"@multiversx/sdk-core": "13.15.0",
"@multiversx/sdk-dapp": "3.0.10",
"@multiversx/sdk-dapp-sc-explorer": "0.0.2-beta.0",
"@react-three/fiber": "8.17.6",
"@multiversx/sdk-dapp": "3.0.23",
"@multiversx/sdk-dapp-sc-explorer": "0.0.2-beta.2",
"@react-three/fiber": "8.17.10",
"@reduxjs/toolkit": "1.9.5",
"anchorme": "2.1.2",
"axios": "1.7.4",
Expand All @@ -39,14 +39,13 @@
"react-helmet-async": "2.0.5",
"react-redux": "8.1.2",
"react-router-dom": "6.23.1",
"react-select": "5.8.0",
"react-select-async-paginate": "0.7.4",
"react-select": "5.8.3",
"react-select-async-paginate": "0.7.6",
"react-simple-maps": "3.0.0",
"react-syntax-highlighter": "15.5.0",
"recharts": "2.7.3",
"redux-persist": "6.0.0",
"three": "0.168.0",
"three-stdlib": "2.32.2",
"three": "0.171.0",
"three-stdlib": "2.34.0",
"ua-parser-js": "1.0.38",
"yup": "0.32.11"
},
Expand Down Expand Up @@ -100,8 +99,7 @@
"@types/react-dom": "18.2.14",
"@types/react-helmet": "6.1.11",
"@types/react-simple-maps": "3.0.4",
"@types/react-syntax-highlighter": "15.5.13",
"@types/three": "0.168.0",
"@types/three": "0.170.0",
"@types/ua-parser-js": "0.7.39",
"@typescript-eslint/eslint-plugin": "5.61.0",
"@typescript-eslint/parser": "5.61.0",
Expand Down
208 changes: 13 additions & 195 deletions src/components/DataDecode/DataDecode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,213 +6,31 @@ import {
useState
} from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import BigNumber from 'bignumber.js';
import { DecodeMethodEnum } from '@multiversx/sdk-dapp/types';
import { decodeForDisplay } from '@multiversx/sdk-dapp/utils/transactions/transactionInfoHelpers/decodeForDisplay';
import { Anchor, Dropdown } from 'react-bootstrap';
import { MAX_DECODE_TX_DATA_LENGTH } from 'appConstants';
import { CopyButton } from 'components';
import { addressIsBech32, bech32, isUtf8 } from 'helpers';
import { faExclamationTriangle } from 'icons/regular';
import { TransactionTokensType } from 'types';
import { WithClassnameType } from 'types';

export enum DecodeMethodEnum {
raw = 'raw',
text = 'text',
decimal = 'decimal',
smart = 'smart'
}

export const decode = (
part: string,
decodeMethod: DecodeMethodEnum,
transactionTokens?: TransactionTokensType
) => {
switch (decodeMethod) {
case DecodeMethodEnum.text:
try {
return Buffer.from(String(part), 'hex').toString('utf8');
} catch {}
return part;
case DecodeMethodEnum.decimal:
return part !== '' ? new BigNumber(part, 16).toString(10) : '';
case DecodeMethodEnum.smart:
try {
const bech32Encoded = bech32.encode(part);
if (addressIsBech32(bech32Encoded)) {
return bech32Encoded;
}
} catch {}
try {
const decoded = Buffer.from(String(part), 'hex').toString('utf8');
if (!isUtf8(decoded)) {
if (transactionTokens) {
const tokens = [
...transactionTokens.esdts,
...transactionTokens.nfts
];
if (tokens.some((token) => decoded.includes(token))) {
return decoded;
}
}
const bn = new BigNumber(part, 16);
return bn.toString(10);
} else {
return decoded;
}
} catch {}
return part;
case DecodeMethodEnum.raw:
default:
return part;
}
};

const treatSmartDecodingCases = ({
parts,
decodedParts,
identifier
}: {
parts: string[];
decodedParts: string[];
identifier?: string;
}) => {
const updatedParts = [...decodedParts];

if (parts[0] === 'ESDTNFTTransfer' && parts[2]) {
updatedParts[2] = decode(parts[2], DecodeMethodEnum.decimal);
}
if (identifier === 'ESDTNFTTransfer' && parts[1]) {
const base64Buffer = Buffer.from(String(parts[1]), 'base64');
updatedParts[1] = decode(
base64Buffer.toString('hex'),
DecodeMethodEnum.decimal
);
}

return updatedParts;
};

export const decodeForDisplay = ({
input,
decodeMethod,
identifier
}: {
input: string;
decodeMethod: DecodeMethodEnum;
export interface DataDecodeUIType extends WithClassnameType {
value: string;
className?: string;
initialDecodeMethod?: DecodeMethodEnum;
setDecodeMethod?: Dispatch<SetStateAction<DecodeMethodEnum>>;
identifier?: string;
}) => {
const display: {
displayValue: string;
validationWarnings: string[];
} = {
displayValue: '',
validationWarnings: []
};

const getDecodedParts = (parts: string[]) => {
const initialDecodedParts = parts.map((part, index) => {
if (
parts.length >= 2 &&
((index === 0 && part.length < 64) || (index === 1 && !parts[0]))
) {
const encodedDisplayValue = /[^a-z0-9]/gi.test(part);
if (encodedDisplayValue) {
return decode(part, decodeMethod);
} else {
return part;
}
} else {
const hexValidationWarnings = getHexValidationWarnings(part);
if (hexValidationWarnings.length) {
display.validationWarnings = Array.from(
new Set([...display.validationWarnings, ...hexValidationWarnings])
);
}

return decode(part, decodeMethod);
}
});

const decodedParts =
decodeMethod === 'smart'
? treatSmartDecodingCases({
parts,
decodedParts: initialDecodedParts,
identifier
})
: initialDecodedParts;

return decodedParts;
};

if (input.includes('@') || input.includes('\n')) {
if (input.includes('@')) {
const parts = input.split('@');
const decodedParts = getDecodedParts(parts);
display.displayValue = decodedParts.join('@');
}
if (input.includes('\n')) {
const parts = input.split('\n');
const initialDecodedParts = parts.map((part) => {
const base64Buffer = Buffer.from(String(part), 'base64');
if (decodeMethod === DecodeMethodEnum.raw) {
return part;
} else {
return decode(base64Buffer.toString('hex'), decodeMethod);
}
});

const decodedParts =
decodeMethod === 'smart'
? treatSmartDecodingCases({
parts,
decodedParts: initialDecodedParts,
identifier
})
: initialDecodedParts;

display.displayValue = decodedParts.join('\n');
}
} else {
display.displayValue = decode(input, decodeMethod);
}

return display;
};

const isHexValidCharacters = (str: string) => {
return str.toLowerCase().match(/[0-9a-f]/g);
};
const isHexValidLength = (str: string) => {
return str.length % 2 === 0;
};

const getHexValidationWarnings = (str: string) => {
const warnings = [];
if (str && !isHexValidCharacters(str)) {
warnings.push(`Invalid Hex characters on argument @${str}`);
}
if (str && !isHexValidLength(str)) {
warnings.push(`Odd number of Hex characters on argument @${str}`);
}

return warnings;
};
anchoredContent?: ReactNode;
}

export const DataDecode = ({
value,
className,
initialDecodeMethod,
setDecodeMethod,
identifier,
children
}: {
value: string;
className?: string;
initialDecodeMethod?: DecodeMethodEnum;
setDecodeMethod?: Dispatch<SetStateAction<DecodeMethodEnum>>;
identifier?: string;
children?: ReactNode;
}) => {
anchoredContent
}: DataDecodeUIType) => {
const defaultDecodeMethod =
initialDecodeMethod &&
Object.values<string>(DecodeMethodEnum).includes(initialDecodeMethod)
Expand Down Expand Up @@ -261,7 +79,7 @@ export const DataDecode = ({
}`}
>
<div className={`form-control textarea ${className ? className : ''}`}>
{children ? children : displayValue}
{anchoredContent ? anchoredContent : displayValue}
</div>
{value && value !== 'N/A' && (
<div className='d-flex button-holder'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ export const TransactionInPoolTypeFilter = ({
hideFilters?: boolean;
}) => {
const [searchParams, setSearchParams] = useSearchParams();
const { type, page, size, ...rest } = Object.fromEntries(searchParams);
const {
type = TransactionInPoolTypeEnum.Transaction,
page,
size,
...rest
} = Object.fromEntries(searchParams);

const typeLink = (type: TransactionInPoolTypeEnum) => {
const nextUrlParams = {
Expand Down Expand Up @@ -53,16 +58,13 @@ export const TransactionInPoolTypeFilter = ({
/>
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item as={Anchor} className='text-neutral-400' eventKey=''>
All
</Dropdown.Item>
{Object.keys(TransactionInPoolTypeEnum).map(
(transactionType, key) => {
return (
<Dropdown.Item
as={Anchor}
eventKey={transactionType}
className={`dropdown-item text-cyan-400 ${
className={`dropdown-item ${
type === transactionType ? 'active' : ''
}`}
key={key}
Expand Down
21 changes: 15 additions & 6 deletions src/components/TransactionsInPoolTable/TransactionsInPoolTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@ import {
PageState,
PulsatingLed
} from 'components';
import { formatBigNumber } from 'helpers';
import { formatBigNumber, getStringPlural } from 'helpers';
import { useGetTransactionInPoolFilters } from 'hooks';
import { faCode, faExchangeAlt } from 'icons/regular';
import { TransactionInPoolType, TransactionFiltersEnum } from 'types';
import {
UITransactionInPoolType,
TransactionFiltersEnum,
TransactionInPoolTypeEnum
} from 'types';

import { TransactionsInPoolHeader, TransactionInPoolRow } from './components';

export interface TransactionsInPoolTableUIType {
transactionsInPool: TransactionInPoolType[];
transactionsInPool: UITransactionInPoolType[];
totalTransactionsInPool: number | typeof ELLIPSIS;
title?: React.ReactNode;
dataChanged?: boolean;
Expand All @@ -34,8 +38,11 @@ export const TransactionsInPoolTable = ({
totalTransactionsInPool,
title = (
<h5 data-testid='title' className='table-title d-flex align-items-center'>
{formatBigNumber({ value: totalTransactionsInPool })} Transactions In Pool{' '}
<PulsatingLed className='ms-2 mt-1' />
{formatBigNumber({ value: totalTransactionsInPool })}{' '}
{getStringPlural(totalTransactionsInPool, {
string: 'Transaction'
})}{' '}
In Pool <PulsatingLed className='ms-2 mt-1' />
</h5>
),
dataChanged = false,
Expand Down Expand Up @@ -97,7 +104,9 @@ export const TransactionsInPoolTable = ({
<PageState
icon={faCode}
title={`No ${
type ? `${type} ` : ''
type && type !== TransactionInPoolTypeEnum.All
? `${type} `
: ''
}Transactions in Pool`}
className='py-spacer my-auto'
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
FromColumnFilters,
TransactionInPoolTypeFilter,
ToColumnFilters
ToColumnFilters,
ShardColumnFilters
} from 'components';
import { useIsSovereign } from 'hooks';
import { TransactionFiltersEnum, WithClassnameType } from 'types';

export interface TransactionsInPoolHeaderUIType extends WithClassnameType {
Expand All @@ -12,10 +14,20 @@ export interface TransactionsInPoolHeaderUIType extends WithClassnameType {
export const TransactionsInPoolHeader = ({
inactiveFilters
}: TransactionsInPoolHeaderUIType) => {
const isSovereign = useIsSovereign();
return (
<thead>
<tr>
<th scope='col'>Txn Hash</th>
<th scope='col'>
{isSovereign ? (
<>Chain</>
) : (
<>
Shard <ShardColumnFilters inactiveFilters={inactiveFilters} />
</>
)}
</th>
<th scope='col'>
From <FromColumnFilters inactiveFilters={inactiveFilters} />
</th>
Expand Down
Loading

0 comments on commit d3dc75d

Please sign in to comment.