Skip to content

Commit

Permalink
feat(app): show NEP-245 token transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
yudhomax committed Nov 12, 2024
1 parent afc435a commit 354d43a
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 15 deletions.
25 changes: 22 additions & 3 deletions apps/app/src/components/Transactions/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
import {
FtsInfo,
InventoryInfo,
MtEventLogData,
NftsInfo,
RPCTransactionInfo,
TransactionInfo,
Expand All @@ -40,11 +41,17 @@ import useTranslation from 'next-translate/useTranslation';
import TxnStatus from '../common/Status';
import FaRight from '../Icons/FaRight';
import { Tooltip } from '@reach/tooltip';
import { txnActions, txnErrorMessage, txnLogs } from '@/utils/near';
import {
parseEventLogs,
txnActions,
txnErrorMessage,
txnLogs,
} from '@/utils/near';
import EventLogs from './Action';
import Actions from './Actions';
import TokenImage, { NFTImage } from '../common/TokenImage';
import { isEmpty } from 'lodash';
import NEPTokenTransactions from '../common/NEPTokenTransactions';

interface Props {
loading: boolean;
Expand Down Expand Up @@ -126,6 +133,18 @@ const Details = (props: Props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [rpcTxn]);

const parsedEvents = useMemo(() => {
return (
logs?.filter((log: TransactionLog) => {
const parsedLog: MtEventLogData = parseEventLogs(log);
return parsedLog?.standard === 'nep245';
}) ?? []
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [logs]);

const showRow = parsedEvents && parsedEvents.length > 0;

useEffect(() => {
// Hide txn actions row
if (typeof document !== 'undefined') {
Expand Down Expand Up @@ -379,7 +398,7 @@ const Details = (props: Props) => {
)}
</div>
</div>
{(fts?.length > 0 || nfts?.length > 0) && (
{(fts?.length > 0 || nfts?.length > 0 || showRow) && (
<div className="flex items-start flex-wrap p-4">
<div className="flex items-center w-full md:w-1/4 mb-2 md:mb-0 leading-7">
<Tooltip
Expand Down Expand Up @@ -593,7 +612,6 @@ const Details = (props: Props) => {
]
</span>
</div>

<Link
href={`/nft-token/${nft?.nft_meta?.contract}`}
className="text-green flex items-center hover:no-underline dark:text-green-250"
Expand Down Expand Up @@ -634,6 +652,7 @@ const Details = (props: Props) => {
</div>
</div>
))}
<NEPTokenTransactions events={parsedEvents} />
</div>
</PerfectScrollbar>
</div>
Expand Down
116 changes: 116 additions & 0 deletions apps/app/src/components/common/NEPTokenTransactions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React from 'react';
import Link from 'next/link';
import { parseEventLogs } from '@/utils/near';
import FaRight from '../Icons/FaRight';
import { shortenAddress } from '@/utils/libs';
import TokenInfo from './TokenInfo';
import { MtEventLogData, TransactionLog } from '@/utils/types';
interface ParsedEventListProps {
events: TransactionLog[];
}

const NEPTokenTransactions: React.FC<ParsedEventListProps> = ({
events,
}: ParsedEventListProps) => {
return (
<>
{events &&
events?.map((event: any, index: number) => {
const parsedEvent: MtEventLogData = parseEventLogs(event);
if (
parsedEvent?.standard === 'nep245' &&
Array.isArray(parsedEvent.data)
) {
return parsedEvent?.data?.map((data, j) => (
<div className="flex" key={`${index}-${j}`}>
<div className="flex items-center flex-wrap break-all leading-7">
<FaRight className="inline-flex text-gray-400 text-xs" />
{['mt_mint', 'mt_burn'].includes(parsedEvent.event) ? (
<>
<div className="font-semibold text-gray px-1">
From{' '}
{'old_owner_id' in data && data?.old_owner_id ? (
<Link
href={`/address/${data.old_owner_id}`}
className="text-green-500 dark:text-green-250 font-normal pl-1 hover:no-underline"
>
{shortenAddress(data.old_owner_id)}
</Link>
) : (
<span className="font-normal pl-1">system</span>
)}
</div>
<div className="font-semibold text-gray px-1">
To{' '}
{'owner_id' in data && data?.owner_id ? (
<Link
href={`/address/${data.owner_id}`}
className="text-green-500 dark:text-green-250 font-normal pl-1"
>
{shortenAddress(data.owner_id)}
</Link>
) : (
<span className="font-normal pl-1">system</span>
)}
</div>
</>
) : (
<>
<div className="font-semibold text-gray px-1">
From{' '}
{'old_owner_id' in data && data?.old_owner_id ? (
<Link
href={`/address/${data.old_owner_id}`}
className="text-green-500 dark:text-green-250 font-normal pl-1 hover:no-underline"
>
{shortenAddress(data.old_owner_id)}
</Link>
) : (
<span className="font-normal pl-1">system</span>
)}
</div>
<div className="font-semibold text-gray px-1">
To{' '}
{'new_owner_id' in data && data?.new_owner_id ? (
<Link
href={`/address/${data.new_owner_id}`}
className="text-green-500 dark:text-green-250 font-normal pl-1"
>
{shortenAddress(data.new_owner_id)}
</Link>
) : (
<span className="font-normal pl-1">system</span>
)}
</div>
</>
)}
<div className="flex items-center font-semibold text-gray px-1">
For{' '}
<span className="flex items-center pl-1 font-normal">
<TokenInfo
contract={
Array.isArray(data?.token_ids) &&
data.token_ids.length > 0
? data.token_ids?.[0]?.split(':')[1]
: ''
}
amount={
Array.isArray(data?.amounts) &&
data.amounts.length > 0
? data.amounts[0]
: '0'
}
/>
</span>
</div>
</div>
</div>
));
}
return null;
})}
</>
);
};

export default NEPTokenTransactions;
33 changes: 24 additions & 9 deletions apps/app/src/components/common/TokenInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,35 @@ const TokenInfo = (props: TokenInfoProps) => {
const { contract, amount, decimals } = props;
const [meta, setMeta] = useState<MetaInfo>({} as MetaInfo);
const { ftMetadata } = useRpc();
const [loading, setLoading] = useState(true);

useEffect(() => {
ftMetadata(contract).then(setMeta).catch(console.error);
setLoading(true);
ftMetadata(contract)
.then((data) => {
setMeta(data);
setLoading(false);
})
.catch((error) => {
console.error(error);
setLoading(false);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [contract]);
const Loader = (props: { className?: string; wrapperClassName?: string }) => {
return (
<div
className={`bg-gray-200 dark:bg-black-200 h-5 rounded shadow-sm animate-pulse ${props.className} ${props?.wrapperClassName}`}
></div>
);
};

return !meta?.name ? (
const Loader = ({
className = '',
wrapperClassName = '',
}: {
className?: string;
wrapperClassName?: string;
}) => (
<div
className={`bg-gray-200 dark:bg-black-200 h-5 w-full max-w-xs rounded shadow-sm animate-pulse ${className} ${wrapperClassName}`}
></div>
);

return loading ? (
<Loader wrapperClassName="flex w-full max-w-xs" />
) : (
<>
Expand Down
6 changes: 3 additions & 3 deletions apps/app/src/utils/libs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ export function yoctoToNear(yocto: string, format: boolean) {
}

export function truncateString(str: string, maxLength: number, suffix: string) {
if (str.length <= maxLength) {
if (str && str?.length <= maxLength) {
return str;
}
return str.substring(0, maxLength) + suffix;
return str?.substring(0, maxLength) + suffix;
}

export function currency(number: string) {
Expand Down Expand Up @@ -498,7 +498,7 @@ export function tokenAmount(
if (amount === undefined || amount === null) return 'N/A';

const decimalNumber = Number(decimal);
if (isNaN(decimalNumber)) throw new Error('Invalid decimal value');
if (isNaN(decimalNumber)) return '0';

const near = Big(amount).div(Big(10).pow(decimalNumber));

Expand Down
12 changes: 12 additions & 0 deletions apps/app/src/utils/near.ts
Original file line number Diff line number Diff line change
Expand Up @@ -921,3 +921,15 @@ export const txnFee = (
.map((receipt) => receipt.outcome.tokens_burnt)
.reduce((acc, fee) => Big(acc).add(fee).toString(), txnTokensBurnt);
};

export function parseEventLogs(event: TransactionLog): any | {} {
let parsedEvent: any | {} = {};

try {
parsedEvent = JSON.parse(event?.logs?.replace('EVENT_JSON:', ''));
} catch (error) {
console.error('Failed to parse event logs:', error);
}

return parsedEvent;
}
31 changes: 31 additions & 0 deletions apps/app/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1708,3 +1708,34 @@ export type MultiChainTxnInfo = {
transaction_hash: string;
status: boolean;
};

export type MtTransferLog = {
authorized_id?: string;
old_owner_id: string;
new_owner_id: string;
token_ids: string[];
amounts: string[];
memo?: string;
};

export type MtBurnLog = {
owner_id: string;
authorized_id?: string;
token_ids: string[];
amounts: string[];
memo?: string;
};

export type MtMintLog = {
owner_id: string;
token_ids: string[];
amounts: string[];
memo?: string;
};

export type MtEventLogData = {
standard: string;
version: string;
event: string;
data: MtMintLog[] | MtBurnLog[] | MtTransferLog[];
};

0 comments on commit 354d43a

Please sign in to comment.