Skip to content

Commit

Permalink
[gateway] return TransactionEffects for GetTransactions (MystenLabs#1684
Browse files Browse the repository at this point in the history
)

Co-authored-by: Chris Li <[email protected]>
  • Loading branch information
patrickkuo and 666lcz authored May 1, 2022
1 parent 8a9fa49 commit 319c095
Show file tree
Hide file tree
Showing 18 changed files with 810 additions and 301 deletions.
94 changes: 55 additions & 39 deletions explorer/client/src/components/transaction-card/RecentTxCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,74 @@
import cl from 'classnames';
import { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import {
getSingleTransactionKind,
getTransactionKind,
getTransferTransaction,
} from 'sui.js';

import Longtext from '../../components/longtext/Longtext';
import Search from '../../components/search/Search';
import theme from '../../styles/theme.module.css';
import { DefaultRpcClient as rpc } from '../../utils/api/DefaultRpcClient';
import ErrorResult from '../error-result/ErrorResult';

import type { CertifiedTransaction, GetTxnDigestsResponse } from 'sui.js';

import styles from './RecentTxCard.module.css';

const initState = {
loadState: 'pending',
lastestTx: [],
latestTx: [],
};

const getRecentTransactions = async (txNum: number) => {
try {
// Get the lastest transactions
// Get the latest transactions
// TODO add batch transaction kind
// TODO sui.js to get the lastest transactions meta data
// TODO sui.js to get the latest transactions meta data
const transactions = await rpc
.getRecentTransactions(txNum)
.then((res: any) => res);
.then((res: GetTxnDigestsResponse) => res);
const txLatest = await Promise.all(
transactions.map(async (tx: any) => {
//
const txData = await rpc
transactions.map(async (tx) => {
return await rpc
.getTransaction(tx[1])
.then((res: any) => res)
.catch((err: any) => false);

// For tx with errors or not found
// return false and skip the transaction
if (!txData) {
return false;
}
const txKind = Object.keys(
txData.transaction.data.kind.Single
)[0];

return {
block: tx[0],
txId: tx[1],
// success: txData ? true : false,
kind: txKind,
From: txData.transaction.data.sender,
...(txKind === 'Transfer'
? {
To: txData.transaction.data.kind.Single.Transfer
.recipient,
}
: {}),
};
.then((res: CertifiedTransaction) => {
const singleTransaction = getSingleTransactionKind(
res.data
);
if (!singleTransaction) {
throw new Error(
`Transaction kind not supported yet ${res.data.kind}`
);
}
const txKind = getTransactionKind(res.data);
const recipient = getTransferTransaction(
res.data
)?.recipient;

return {
block: tx[0],
txId: tx[1],
// success: txData ? true : false,
kind: txKind,
From: res.data.sender,
...(recipient
? {
To: recipient,
}
: {}),
};
})
.catch((err) => {
console.error(
'Failed to get transaction details for txn digest',
tx[1],
err
);
return false;
});
})
);
// Remove failed transactions and sort by block number
Expand Down Expand Up @@ -84,7 +100,7 @@ function truncate(fullStr: string, strLen: number, separator: string) {
);
}

function LastestTxCard() {
function LatestTxCard() {
const [isLoaded, setIsLoaded] = useState(false);
const [results, setResults] = useState(initState);
useEffect(() => {
Expand All @@ -96,7 +112,7 @@ function LastestTxCard() {
}
setResults({
loadState: 'loaded',
lastestTx: resp,
latestTx: resp,
});
})
.catch((err) => {
Expand All @@ -120,8 +136,8 @@ function LastestTxCard() {
if (!isLoaded && results.loadState === 'fail') {
return (
<ErrorResult
id="lastestTx"
errorMsg="There was an issue getting the lastest transaction"
id="latestTx"
errorMsg="There was an issue getting the latest transaction"
/>
);
}
Expand All @@ -144,9 +160,9 @@ function LastestTxCard() {
<div className={styles.txcardgridlarge}>TxId</div>
<div className={styles.txtype}>Tx Type</div>
<div className={styles.txgas}>Gas</div>
<div className={styles.txadd}>Sender & Reciever</div>
<div className={styles.txadd}>Sender & Receiver</div>
</div>
{results.lastestTx.map((tx: any, index: number) => (
{results.latestTx.map((tx: any, index: number) => (
<div
key={index}
className={cl(styles.txcardgrid, styles.txcard)}
Expand Down Expand Up @@ -193,4 +209,4 @@ function LastestTxCard() {
);
}

export default LastestTxCard;
export default LatestTxCard;
176 changes: 101 additions & 75 deletions explorer/client/src/components/transaction-card/TransactionCard.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { Buffer } from 'buffer';
import cl from 'classnames';
import {
getMoveCallTransaction,
getPublishTransaction,
getTransactionKind,
getTransferTransaction,
} from 'sui.js';

import Longtext from '../../components/longtext/Longtext';

import type {
CertifiedTransaction,
TransactionData,
TransactionKindName,
} from 'sui.js';

import styles from './TransactionCard.module.css';

// Generate an Arr of Obj with Label and Value
// TODO rewrite to use sue.js, verify tx types and dynamically generate list
function formatTxResponse(data: any) {
const tx = data.transaction;

function formatTxResponse(tx: CertifiedTransaction, txId: string) {
// Todo add batch kind
const txKind = tx.data.kind;
const txKindName = Object.keys(txKind.Single)[0];
const txKindName = getTransactionKind(tx.data);
return [
{
label: 'Transaction ID',
value: data.txId,
value: txId,
className: 'columnheader',
},
{
Expand All @@ -32,71 +42,7 @@ function formatTxResponse(data: any) {
value: txKindName,
},
// txKind Transfer or Call
...(txKindName === 'Transfer'
? [
{
label: 'Object',
value: txKind.Single.Transfer.object_ref[0],
link: true,
category: 'objects',
},
{
label: 'Sender',
value: tx.data.sender,
link: true,
category: 'addresses',
className: 'Receiver',
},
{
label: 'To',
value: txKind.Single.Transfer.recipient,
category: 'addresses',
link: true,
},
]
: txKindName === 'Call'
? [
{
label: 'From',
value: tx.data.sender,
link: true,
category: 'addresses',
},
{
label: 'Package',
value: txKind.Single.Call.package,
list: true,
},
{
label: 'Module',
value: txKind.Single.Call.module,
},
{
label: 'Function',
value: txKind.Single.Call.function,
},
{
label: 'Arguments',
// convert pure type
value: txKind.Single.Call.arguments
.filter((itm: any) => itm['Pure'])
.map((data: any) =>
Buffer.from(data['Pure']).toString('base64')
),
// list: true,
},
]
: txKindName === 'Publish'
? [
{
label: 'Modules',
value: txKind.Single.Publish.modules,
list: true,
// sublist: true,
},
]
: []),

...(formatByTransactionKind(txKindName, tx.data) ?? []),
{
label: 'Transactions Signature',
value: tx.tx_signature,
Expand All @@ -114,20 +60,100 @@ function formatTxResponse(data: any) {
},
{
label: 'Validator Signatures',
value: data.signatures,
value: tx.auth_sign_info.signatures,
list: true,
sublist: true,
},
];
}

function TransactionCard({ txdata }: any) {
function formatByTransactionKind(
kind: TransactionKindName | undefined,
data: TransactionData
) {
switch (kind) {
case 'Transfer':
const transfer = getTransferTransaction(data)!;
return [
{
label: 'Object',
value: transfer.object_ref[0],
link: true,
category: 'objects',
},
{
label: 'Sender',
value: data.sender,
link: true,
category: 'addresses',
className: 'Receiver',
},
{
label: 'To',
value: transfer.recipient,
category: 'addresses',
link: true,
},
];
case 'Call':
const moveCall = getMoveCallTransaction(data)!;
return [
{
label: 'From',
value: data.sender,
link: true,
category: 'addresses',
},
{
label: 'Package',
value: moveCall.package,
list: true,
},
{
label: 'Module',
value: moveCall.module,
},
{
label: 'Function',
value: moveCall.function,
},
{
label: 'Arguments',
// convert pure type
value: moveCall.arguments
.filter((itm: any) => itm['Pure'])
.map((data: any) =>
Buffer.from(data['Pure']).toString('base64')
),
// list: true,
},
];
case 'Publish':
const publish = getPublishTransaction(data)!;
return [
{
label: 'Modules',
value: publish.modules,
list: true,
// sublist: true,
},
];
default:
return [];
}
}

type Props = {
txdata: CertifiedTransaction & { loadState: string; txId: string };
};

function TransactionCard({ txdata }: Props) {
return (
<>
{txdata?.transaction && (
{txdata && (
<div className={styles.transactioncard}>
<div className={styles.txcard}>
{formatTxResponse(txdata).map(
{formatTxResponse(txdata, txdata.txId).map(
(itm: any, index: number) => (
<div
key={index}
Expand Down
Loading

0 comments on commit 319c095

Please sign in to comment.