diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 01c0f37..9b7e06c 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -78,6 +78,7 @@ "StateRootHash": "State Root Hash", "Status": "Status", "Sender": "Sender", + "FailureFunctionName": "Executtion Failure Function", "FunctionModuleAddress": "Contract Address", "FunctionModuleName": "Module Name", "FunctionName": "Function Name", @@ -176,4 +177,4 @@ "holderList": "Token Holders List", "transactionList": "Token Transactions List" } -} \ No newline at end of file +} diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index cae942b..c02bf95 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -78,6 +78,7 @@ "StateRootHash": "根哈希", "Status": "状态", "Sender": "发送方", + "FailureFunctionName": "执行失败函数名", "FunctionModuleAddress": "合约地址", "FunctionModuleName": "模块名", "FunctionName": "函数名", @@ -176,4 +177,4 @@ "holderList": "代币持有者列表", "transactionList": "代币交易列表" } -} \ No newline at end of file +} diff --git a/src/modules/Transactions/components/Detail/adapter.ts b/src/modules/Transactions/components/Detail/adapter.ts index 9afcede..ff7c1f0 100644 --- a/src/modules/Transactions/components/Detail/adapter.ts +++ b/src/modules/Transactions/components/Detail/adapter.ts @@ -20,4 +20,5 @@ const selector = createSelector( export default connect(selector, { getTransaction: actions.getTransaction, -})(Index as any) as any; \ No newline at end of file + getModuleFunctionIndex: actions.getModuleFunctionIndex, +})(Index as any) as any; diff --git a/src/modules/Transactions/components/Detail/index.tsx b/src/modules/Transactions/components/Detail/index.tsx index 6494249..f6a0995 100644 --- a/src/modules/Transactions/components/Detail/index.tsx +++ b/src/modules/Transactions/components/Detail/index.tsx @@ -14,7 +14,7 @@ import { } from '@starcoin/starcoin'; import { arrayify } from '@ethersproject/bytes'; import get from 'lodash/get'; -import {toObject } from '@/utils/helper'; +import { toObject } from '@/utils/helper'; import useSWR from 'swr'; import FileSaver from 'file-saver'; import { GetApp } from '@mui/icons-material'; @@ -29,7 +29,7 @@ import Loading from '@/common/Loading'; import EventViewTable from '@/common/View/EventViewTable'; import PageView from '@/common/View/PageView'; import CommonLink from '@/common/Link'; -import { withRouter,RoutedProps } from '@/utils/withRouter'; +import { withRouter, RoutedProps } from '@/utils/withRouter'; @@ -43,9 +43,9 @@ function formatArgsWithTypeTag( switch (typeTag) { case 'Signer': case 'Address': { - let decodeAddress:string='0x'; - for(let i=0;i<16;i++){ - decodeAddress+=deserializer.deserializeU8().toString(16); + let decodeAddress: string = '0x'; + for (let i = 0; i < 16; i++) { + decodeAddress += deserializer.deserializeU8().toString(16); } return decodeAddress; } @@ -74,15 +74,13 @@ function formatArgsWithTypeTag( // return hexlify(deserializer.deserializeBytes()); } if ('Struct' in typeTag) { - return `${typeTag.Struct.address}::${typeTag.Struct.module}::${ - typeTag.Struct.name - }${ - typeTag.Struct.type_params + return `${typeTag.Struct.address}::${typeTag.Struct.module}::${typeTag.Struct.name + }${typeTag.Struct.type_params ? `<${typeTag.Struct.type_params .map((param) => formatArgsWithTypeTag(deserializer, param)) .join(', ')}>` : '' - }`; + }`; } return undefined; } catch { @@ -105,10 +103,10 @@ export function useResolveFunction(functionId?: string, network?: string) { } const DecodedPayloadContent = ({ - network, - txnPayload, - alt, - }: { + network, + txnPayload, + alt, +}: { network: string; alt: string; txnPayload: any; @@ -135,11 +133,10 @@ const DecodedPayloadContent = ({ const decodedArgs = args ? args.map((arg: string, index: number) => { const type_tag = resolvedFunction?.args[index + 1]?.type_tag; return resolvedFunction?.args[index + 1] - ? `${types.formatTypeTag(resolvedFunction.args[index + 1]?.type_tag)}: ${ - type_tag !== 'Address' ? formatArgsWithTypeTag( - new bcs.BcsDeserializer(arrayify(arg)), - resolvedFunction.args[index + 1]?.type_tag, - ) : arg + ? `${types.formatTypeTag(resolvedFunction.args[index + 1]?.type_tag)}: ${type_tag !== 'Address' ? formatArgsWithTypeTag( + new bcs.BcsDeserializer(arrayify(arg)), + resolvedFunction.args[index + 1]?.type_tag, + ) : arg }` : arg; }) : {}; @@ -176,7 +173,7 @@ const useStyles = (theme: any) => rawData: { wordBreak: 'break-all', overflow: 'auto', - fontSize:theme.spacing(1) + fontSize: theme.spacing(1) }, accordion: { @@ -189,8 +186,8 @@ const useStyles = (theme: any) => csvExportIcon: { verticalAlign: 'middle', }, - card:{ - marginTop:theme.spacing(2), + card: { + marginTop: theme.spacing(2), display: 'flex', backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[800] : undefined, color: theme.palette.getContrastText(theme.palette.background.paper), @@ -200,15 +197,17 @@ const useStyles = (theme: any) => interface IndexState { resolvedFunction: any; - tabSelect:number, + tabSelect: number, } -interface IndexProps extends RoutedProps { +interface IndexProps extends RoutedProps { classes: any; t: any; match: any; transaction: any; + failureFunction: any; getTransaction: (data: any, callback?: any) => any; + getModuleFunctionIndex: (data: any, callback?: any) => any; } class Index extends PureComponent { @@ -216,6 +215,7 @@ class Index extends PureComponent { static defaultProps = { match: {}, transaction: null, + failureFunction: null, getTransaction: () => { }, }; @@ -223,8 +223,8 @@ class Index extends PureComponent { constructor(props: IndexProps) { super(props); this.state = { - resolvedFunction:undefined, - tabSelect:0, + resolvedFunction: undefined, + tabSelect: 0, }; } @@ -234,6 +234,22 @@ class Index extends PureComponent { this.props.getTransaction({ hash }); } + componentDidUpdate(prevProps: IndexProps) { + if (prevProps.failureFunction == null && this.props.transaction.status !== "Executed") { + try { + const status = JSON.parse(this.props.transaction.status); + if (status.ExecutionFailure !== undefined) { + const st = status.ExecutionFailure; + const moduleId = `${st.location.Module.address}::${st.location.Module.name}`; + const index = st.function; + this.props.getModuleFunctionIndex({ moduleId, index }); + } + } catch (e) { + console.log(e); + } + } + } + generateExtraTabs() { const { transaction, classes, t, params } = this.props; const network = params.network; @@ -297,42 +313,42 @@ class Index extends PureComponent { {t('transaction.NoRawData')} ); const handleChange = (event: React.SyntheticEvent, newValue: number) => { - this.setState({tabSelect:newValue}); + this.setState({ tabSelect: newValue }); }; return ( - - - - - - - - - - {isInitialLoad ? : eventsContent} - - -
- {isInitialLoad ? : rawContent} -
-
- -
- {isInitialLoad ? ( - - ) : ( - - )} -
-
- + + + + + + + -
); + + {isInitialLoad ? : eventsContent} + + +
+ {isInitialLoad ? : rawContent} +
+
+ +
+ {isInitialLoad ? ( + + ) : ( + + )} +
+
+ + + ); } render() { @@ -442,7 +458,7 @@ class Index extends PureComponent { this.setState({ resolvedFunction: data }); }; const resolvedFunction = this.state?.resolvedFunction; - if (!resolvedFunction){ + if (!resolvedFunction) { getResolvedFunction(); } @@ -451,10 +467,10 @@ class Index extends PureComponent { const type_tag = resolvedFunction?.args[index + 1]?.type_tag; return resolvedFunction?.args[index + 1] ? [types.formatTypeTag(type_tag), - type_tag !== 'Address' ? formatArgsWithTypeTag( - new bcs.BcsDeserializer(arrayify(arg)), - resolvedFunction.args[index + 1].type_tag, - ) : arg, + type_tag !== 'Address' ? formatArgsWithTypeTag( + new bcs.BcsDeserializer(arrayify(arg)), + resolvedFunction.args[index + 1].type_tag, + ) : arg, ] : arg; }) : {}; @@ -491,13 +507,19 @@ class Index extends PureComponent { // [t('common.Time'), new Date(parseInt(blockTime, 10)).toLocaleString()], [t('transaction.StateRootHash'), source.state_root_hash], [t('transaction.Status'), source.status], - [t('common.GasUsed'), source.gas_used], - [ - t('transaction.Sender'), - , - ], + ]; + if (this.props.failureFunction !== null) { + columns.push([t('transaction.FailureFunctionName'), this.props.failureFunction.name]); + } + + columns.push([t('common.GasUsed'), source.gas_used]); + columns.push([ + t('transaction.Sender'), + , + ]) + if (moduleAddress) { columns.push([t('transaction.FunctionModuleAddress'), moduleAddress]); } @@ -528,9 +550,9 @@ class Index extends PureComponent { const savData = [ [t('common.Hash'), source.transaction_hash], [t('transaction.Type'), type], - [t('common.Time'),`${new Date(parseInt(source.timestamp, 10)).toLocaleString()} ${new Date().toTimeString().slice(9)}`], - [t('transaction.BlockHash'),source.block_hash], - [t('transaction.BlockHeight'),source.block_number], + [t('common.Time'), `${new Date(parseInt(source.timestamp, 10)).toLocaleString()} ${new Date().toTimeString().slice(9)}`], + [t('transaction.BlockHash'), source.block_hash], + [t('transaction.BlockHeight'), source.block_number], [t('transaction.StateRootHash'), source.state_root_hash], [t('transaction.Status'), source.status], [t('common.GasUsed'), source.gas_used], @@ -549,13 +571,13 @@ class Index extends PureComponent { if (txn_type_args) { savData.push([t('transaction.TxnTypeArgs'), JSON.stringify(txn_type_args[0] || [])]); } - + for (let i = 0; i < decodedArgs.length; i++) { if (decodedArgs[i][0] === 'address') { const address = decodedArgs[i][1]; - savData.push([`${t('transaction.arg')} ${i+1}`,address]); + savData.push([`${t('transaction.arg')} ${i + 1}`, address]); } else { - savData.push([`${t('transaction.arg')} ${i+1}`, decodedArgs[i][1]]); + savData.push([`${t('transaction.arg')} ${i + 1}`, decodedArgs[i][1]]); } } @@ -568,7 +590,7 @@ class Index extends PureComponent { csvRow += `"${element[1]}",`; } csvData = `${csvTitle}\r\n${csvRow}`; - const blob = new Blob([csvData], {type: "text/plain;charset=utf-8"}); + const blob = new Blob([csvData], { type: "text/plain;charset=utf-8" }); FileSaver.saveAs(blob, `${source.transaction_hash}.csv`); } @@ -576,13 +598,13 @@ class Index extends PureComponent { columns.push([ ``,
- [ + [ ]
]); - + return ( @@ -599,4 +621,4 @@ class Index extends PureComponent { } } -export default withStyles(useStyles)(withTranslation()(withRouter(Index)) ); +export default withStyles(useStyles)(withTranslation()(withRouter(Index))); diff --git a/src/modules/Transactions/store/actions.ts b/src/modules/Transactions/store/actions.ts index 83d08ab..f941db2 100644 --- a/src/modules/Transactions/store/actions.ts +++ b/src/modules/Transactions/store/actions.ts @@ -131,4 +131,18 @@ export function getPendingTransactionListInDelay(payload: any) { type: types.GET_PENDING_TRANSACTION_LIST_IN_DELAY, payload, }; -} \ No newline at end of file +} + +export function getModuleFunctionIndex(payload: any) { + return { + type: types.GET_MODULE_FUNCTION_INDEX, + payload, + } +} + +export function setModuleFunctionIndex(payload: any) { + return { + type: types.SET_MODULE_FUNCTION_INDEX, + payload, + } +} diff --git a/src/modules/Transactions/store/constants.ts b/src/modules/Transactions/store/constants.ts index b4a34e8..a4a2b3f 100644 --- a/src/modules/Transactions/store/constants.ts +++ b/src/modules/Transactions/store/constants.ts @@ -36,3 +36,6 @@ export const GET_BLOCK_TRANSACTIONS_BY_HEIGHT = `${SCOPENAME}/GET_BLOCK_TRANSACT export const SET_BLOCK_TRANSACTIONS = `${SCOPENAME}/SET_BLOCK_TRANSACTIONS`; +// Resolve module function index +export const GET_MODULE_FUNCTION_INDEX = `${SCOPENAME}/GET_MODULE_FUNCTION_INDEX`; +export const SET_MODULE_FUNCTION_INDEX = `${SCOPENAME}/SET_MODULE_FUNCTION_INDEX`; diff --git a/src/modules/Transactions/store/reducers.ts b/src/modules/Transactions/store/reducers.ts index 4efb0cb..554c3fc 100644 --- a/src/modules/Transactions/store/reducers.ts +++ b/src/modules/Transactions/store/reducers.ts @@ -51,7 +51,10 @@ export default function reducers(state: any = initState, action: any) { case types.SET_BLOCK_TRANSACTIONS: { return { ...state, blockTransactions: action.payload }; } + case types.SET_MODULE_FUNCTION_INDEX: { + return { ...state, failureFunction: action.payload }; + } default: return state; } -} \ No newline at end of file +} diff --git a/src/modules/Transactions/store/sagas.ts b/src/modules/Transactions/store/sagas.ts index ed0d7bc..e4db1d7 100644 --- a/src/modules/Transactions/store/sagas.ts +++ b/src/modules/Transactions/store/sagas.ts @@ -1,5 +1,6 @@ import { call, put, takeLatest, fork, delay } from 'redux-saga/effects'; import withLoading from '@/sagaMiddleware/index'; +import * as sdk from '@/utils/sdk'; import * as api from './apis'; import * as actions from './actions'; import * as types from './constants'; @@ -170,6 +171,24 @@ function* watchGetTransactionListInDelay() { yield takeLatest(types.GET_TRANSACTION_LIST_IN_DELAY, getTransactionListInDelay); } + + +export function* getModuleFunctionIndex(action: ReturnType) { + try { + // @ts-ignore + const res = yield call(sdk.getResolveModuleFunctionIndex, action.payload.moduleId, action.payload.index); + yield put(actions.setModuleFunctionIndex(res)); + } catch ({ message }) { + if (message) { + console.log(message); + } + } +} + +function* watchGetModuleFunctionIndex() { + yield takeLatest(types.GET_MODULE_FUNCTION_INDEX, getModuleFunctionIndex); +} + const sagas = [ watchGetTransaction, watchGetPendingTransaction, @@ -180,6 +199,7 @@ const sagas = [ watchGetAddressTransactions, watchGetBlockTransactions, watchGetBlockTransactionsByHeight, + watchGetModuleFunctionIndex, ]; -export default sagas; \ No newline at end of file +export default sagas; diff --git a/src/utils/sdk.ts b/src/utils/sdk.ts index da84c1d..ef3a16f 100644 --- a/src/utils/sdk.ts +++ b/src/utils/sdk.ts @@ -205,6 +205,17 @@ export async function getResolveStruct(struct_tag: string) { } } +export async function getResolveModuleFunctionIndex(module_id: string, function_index: number) { + try { + const provider = providerMap[getNetwork()]; + const result = await provider.send('contract.resolve_module_function_index', [module_id, function_index]); + return result; + } catch (error: any) { + console.info(error); + return false; + } +} + export async function getResolveFunction(function_id: string) { try { const provider = providerMap[getNetwork()]; @@ -226,4 +237,4 @@ export async function getAddressCode(address: string) { return Object.keys(codes).map((value, index) => { return { 'name': value, code: all[index] }; }); -} \ No newline at end of file +}