|
|
@ -29,6 +29,29 @@ const extractError = (err: any) => { |
|
|
|
return errorMessage || err; |
|
|
|
return errorMessage || err; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const getTxInputSignature = async (trx: RPCTransactionHarmony) => { |
|
|
|
|
|
|
|
let signature |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
const signatures = await getByteCodeSignatureByHash([trx.input.slice(0, 10)]) |
|
|
|
|
|
|
|
if(signatures && signatures.length > 0) { |
|
|
|
|
|
|
|
signature = signatures[0] |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
console.error('Cannot get tx input signature: ', (e as Error).message) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return signature |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const getTransactionErrorMessage = (txHash: string, tx: RPCTransactionHarmony) => { |
|
|
|
|
|
|
|
if(txHash.length !== 66) { |
|
|
|
|
|
|
|
return 'Invalid Txn hash' |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if(!tx || !tx.hash) { |
|
|
|
|
|
|
|
return 'Unable to locate this TxnHash' |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return '' |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export const TransactionPage = () => { |
|
|
|
export const TransactionPage = () => { |
|
|
|
const history = useHistory(); |
|
|
|
const history = useHistory(); |
|
|
|
const { id } = useParams<{id: string}>(); |
|
|
|
const { id } = useParams<{id: string}>(); |
|
|
@ -40,27 +63,13 @@ export const TransactionPage = () => { |
|
|
|
const [txReceipt, setTxReceipt] = useState<TxReceipt>(); |
|
|
|
const [txReceipt, setTxReceipt] = useState<TxReceipt>(); |
|
|
|
const [trxs, setTrxs] = useState<InternalTransaction[]>([]); |
|
|
|
const [trxs, setTrxs] = useState<InternalTransaction[]>([]); |
|
|
|
const [logs, setLogs] = useState<any[]>([]); |
|
|
|
const [logs, setLogs] = useState<any[]>([]); |
|
|
|
const [isLoading, setIsLoading] = useState<boolean>(false); |
|
|
|
const [isLoading, setIsLoading] = useState(false); |
|
|
|
const [txrsLoading, setTxrsLoading] = useState<boolean>(true); |
|
|
|
|
|
|
|
const [activeIndex, setActiveIndex] = useState(+activeTab); |
|
|
|
const [activeIndex, setActiveIndex] = useState(+activeTab); |
|
|
|
const [inputSignature, setInputSignature] = useState<IHexSignature>() |
|
|
|
const [inputSignature, setInputSignature] = useState<IHexSignature>() |
|
|
|
|
|
|
|
|
|
|
|
const { availableShards } = config |
|
|
|
const { availableShards } = config |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
useEffect(() => { |
|
|
|
const getTxInputSignature = async (trx: RPCTransactionHarmony) => { |
|
|
|
|
|
|
|
let signature |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
const signatures = await getByteCodeSignatureByHash([trx.input.slice(0, 10)]) |
|
|
|
|
|
|
|
if(signatures && signatures.length > 0) { |
|
|
|
|
|
|
|
signature = signatures[0] |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
console.error('Cannot get tx input signature: ', (e as Error).message) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return signature |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const getTx = async () => { |
|
|
|
const getTx = async () => { |
|
|
|
let trx; |
|
|
|
let trx; |
|
|
|
let trxInputSignature; |
|
|
|
let trxInputSignature; |
|
|
@ -98,19 +107,16 @@ export const TransactionPage = () => { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
setTx((trx || {}) as RPCTransactionHarmony); |
|
|
|
const txData = trx || {} |
|
|
|
|
|
|
|
setTx(txData as RPCTransactionHarmony); |
|
|
|
setInputSignature(trxInputSignature) |
|
|
|
setInputSignature(trxInputSignature) |
|
|
|
|
|
|
|
return txData |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
getTx(); |
|
|
|
|
|
|
|
}, [id]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
|
|
const getInternalTxs = async () => { |
|
|
|
const getInternalTxs = async () => { |
|
|
|
if (tx.hash && tx.shardID === 0) { |
|
|
|
if (tx.hash && tx.shardID === 0) { |
|
|
|
try { |
|
|
|
//@ts-ignore
|
|
|
|
//@ts-ignore
|
|
|
|
const txs = await getInternalTransactionsByField([ |
|
|
|
const txs = await getInternalTransactionsByField([ |
|
|
|
|
|
|
|
0, |
|
|
|
0, |
|
|
|
"transaction_hash", |
|
|
|
"transaction_hash", |
|
|
|
tx.hash, |
|
|
|
tx.hash, |
|
|
@ -118,76 +124,70 @@ export const TransactionPage = () => { |
|
|
|
// need to track fallback
|
|
|
|
// need to track fallback
|
|
|
|
tx.blockNumber |
|
|
|
tx.blockNumber |
|
|
|
); |
|
|
|
); |
|
|
|
const methodSignatures = await Promise.all( |
|
|
|
const methodSignatures = await Promise.all( |
|
|
|
txs.map((tx) => { |
|
|
|
txs.map((tx) => { |
|
|
|
return tx.input && tx.input.length > 10 |
|
|
|
return tx.input && tx.input.length > 10 |
|
|
|
? getByteCodeSignatureByHash([tx.input.slice(0, 10)]) |
|
|
|
? getByteCodeSignatureByHash([tx.input.slice(0, 10)]) |
|
|
|
: Promise.resolve([]); |
|
|
|
: Promise.resolve([]); |
|
|
|
}) |
|
|
|
}) |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
const txsWithSignatures = txs.map((l, i) => ({ |
|
|
|
const txsWithSignatures = txs.map((l, i) => ({ |
|
|
|
...l, |
|
|
|
...l, |
|
|
|
signatures: methodSignatures[i], |
|
|
|
signatures: methodSignatures[i], |
|
|
|
})); |
|
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
setTrxs(txsWithSignatures as InternalTransaction[]); |
|
|
|
setTrxs(txsWithSignatures as InternalTransaction[]); |
|
|
|
setTxrsLoading(false); |
|
|
|
|
|
|
|
} catch (err) { |
|
|
|
|
|
|
|
console.log(err); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
setTrxs([]); |
|
|
|
setTrxs([]); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
getInternalTxs(); |
|
|
|
|
|
|
|
}, [tx.hash]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
|
|
const getLogs = async () => { |
|
|
|
const getLogs = async () => { |
|
|
|
//
|
|
|
|
|
|
|
|
const contractShardID = process.env.REACT_APP_CONTRACT_SHARD ? (process.env.REACT_APP_CONTRACT_SHARD || 0) : 0 |
|
|
|
const contractShardID = process.env.REACT_APP_CONTRACT_SHARD ? (process.env.REACT_APP_CONTRACT_SHARD || 0) : 0 |
|
|
|
if (tx.hash && [0, contractShardID].includes(tx.shardID)) { |
|
|
|
if (tx.hash && [0, contractShardID].includes(tx.shardID)) { |
|
|
|
|
|
|
|
//@ts-ignore
|
|
|
|
|
|
|
|
const logs: any[] = await getTransactionLogsByField([ |
|
|
|
|
|
|
|
0, |
|
|
|
|
|
|
|
"transaction_hash", |
|
|
|
|
|
|
|
tx.hash, |
|
|
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
// const SevenDaysBlock = 60 * 60 * 24 * 7 / 2
|
|
|
|
const logsSignatures = await Promise.all( |
|
|
|
// const txDate = new Date(tx.timestamp).getTime()
|
|
|
|
logs.map((l) => getByteCodeSignatureByHash([l.topics[0]])) |
|
|
|
// const now = Date.now()
|
|
|
|
); |
|
|
|
//
|
|
|
|
|
|
|
|
// if (now - txDate > SevenDaysBlock) {
|
|
|
|
|
|
|
|
// console.log('no logs served')
|
|
|
|
|
|
|
|
// return
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
//@ts-ignore
|
|
|
|
|
|
|
|
const logs: any[] = await getTransactionLogsByField([ |
|
|
|
|
|
|
|
0, |
|
|
|
|
|
|
|
"transaction_hash", |
|
|
|
|
|
|
|
tx.hash, |
|
|
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const logsSignatures = await Promise.all( |
|
|
|
|
|
|
|
logs.map((l) => getByteCodeSignatureByHash([l.topics[0]])) |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const logsWithSignatures = logs.map((l, i) => ({ |
|
|
|
const logsWithSignatures = logs.map((l, i) => ({ |
|
|
|
...l, |
|
|
|
...l, |
|
|
|
signatures: logsSignatures[i], |
|
|
|
signatures: logsSignatures[i], |
|
|
|
})); |
|
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
setLogs(logsWithSignatures as any); |
|
|
|
setLogs(logsWithSignatures as any); |
|
|
|
setIsLoading(false); |
|
|
|
|
|
|
|
} catch (err) { |
|
|
|
|
|
|
|
console.log(err); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
setLogs([]); |
|
|
|
setLogs([]); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
getLogs(); |
|
|
|
const loadTxData = async () => { |
|
|
|
}, [tx]); |
|
|
|
let tx = {} |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
setIsLoading(true) |
|
|
|
|
|
|
|
tx = await getTx() |
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
console.log('Cannot load tx data:', e) |
|
|
|
|
|
|
|
} finally { |
|
|
|
|
|
|
|
setIsLoading(false) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if(tx) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
await Promise.allSettled([getLogs(), getInternalTxs()]) |
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
console.log('Cannot load transaction logs:', e) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
loadTxData() |
|
|
|
|
|
|
|
}, [tx.hash]); |
|
|
|
|
|
|
|
|
|
|
|
if (isLoading) { |
|
|
|
if (isLoading) { |
|
|
|
return ( |
|
|
|
return ( |
|
|
@ -197,15 +197,14 @@ export const TransactionPage = () => { |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const internalErrorMsg = txrsLoading |
|
|
|
const txErrorMsg = isLoading ? '' : getTransactionErrorMessage(id, tx) |
|
|
|
? undefined |
|
|
|
|
|
|
|
: trxs.length |
|
|
|
const internalErrorMsg = trxs |
|
|
|
? trxs |
|
|
|
.map((t) => t.error) |
|
|
|
.map((t) => t.error) |
|
|
|
.filter((_) => _) |
|
|
|
.filter((_) => _) |
|
|
|
.map(extractError) |
|
|
|
.map(extractError) |
|
|
|
.join(",") |
|
|
|
.join(",") |
|
|
|
|
|
|
|
: "" |
|
|
|
|
|
|
|
const txReceiptErrorMsg = txReceipt && txReceipt.status === 0 |
|
|
|
const txReceiptErrorMsg = txReceipt && txReceipt.status === 0 |
|
|
|
? 'Failed (from receipt)' |
|
|
|
? 'Failed (from receipt)' |
|
|
|
: '' |
|
|
|
: '' |
|
|
@ -235,7 +234,8 @@ export const TransactionPage = () => { |
|
|
|
logs={logs} |
|
|
|
logs={logs} |
|
|
|
internalTxs={trxs} |
|
|
|
internalTxs={trxs} |
|
|
|
inputSignature={inputSignature} |
|
|
|
inputSignature={inputSignature} |
|
|
|
errorMsg={internalErrorMsg || txReceiptErrorMsg} |
|
|
|
errorMsg={txErrorMsg || internalErrorMsg || txReceiptErrorMsg} |
|
|
|
|
|
|
|
hideShowMore={isLoading || Boolean(txErrorMsg)} |
|
|
|
/> |
|
|
|
/> |
|
|
|
</Tab> |
|
|
|
</Tab> |
|
|
|
{trxs.length ? ( |
|
|
|
{trxs.length ? ( |
|
|
|