diff --git a/alm/src/components/ExecutionConfirmation.tsx b/alm/src/components/ExecutionConfirmation.tsx
index 1541e7bf..0ee1ff88 100644
--- a/alm/src/components/ExecutionConfirmation.tsx
+++ b/alm/src/components/ExecutionConfirmation.tsx
@@ -36,10 +36,12 @@ export const ExecutionConfirmation = ({
const availableManualExecution =
!isHome &&
(executionData.status === VALIDATOR_CONFIRMATION_STATUS.WAITING ||
+ executionData.status === VALIDATOR_CONFIRMATION_STATUS.FAILED ||
(executionData.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED &&
executionEventsFetched &&
!!executionData.validator))
const requiredManualExecution = availableManualExecution && ALM_HOME_TO_FOREIGN_MANUAL_EXECUTION
+ const showAgeColumn = !requiredManualExecution || executionData.status === VALIDATOR_CONFIRMATION_STATUS.FAILED
const windowWidth = useWindowWidth()
const txExplorerLink = getExplorerTxUrl(executionData.txHash, isHome)
@@ -71,7 +73,7 @@ export const ExecutionConfirmation = ({
{requiredManualExecution ? 'Execution info' : 'Executed by'} |
Status |
- {!requiredManualExecution && Age | }
+ {showAgeColumn && Age | }
{availableManualExecution && Actions | }
@@ -87,7 +89,7 @@ export const ExecutionConfirmation = ({
)}
{getExecutionStatusElement(executionData.status)}
- {!requiredManualExecution && (
+ {showAgeColumn && (
{executionData.timestamp > 0 ? (
diff --git a/alm/src/components/ManualExecutionButton.tsx b/alm/src/components/ManualExecutionButton.tsx
index e39c6150..5a7d1f90 100644
--- a/alm/src/components/ManualExecutionButton.tsx
+++ b/alm/src/components/ManualExecutionButton.tsx
@@ -2,9 +2,17 @@ import React, { useState, useEffect } from 'react'
import styled from 'styled-components'
import { InjectedConnector } from '@web3-react/injected-connector'
import { useWeb3React } from '@web3-react/core'
-import { INCORRECT_CHAIN_ERROR, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
+import {
+ DOUBLE_EXECUTION_ATTEMPT_ERROR,
+ EXECUTION_FAILED_ERROR,
+ EXECUTION_OUT_OF_GAS_ERROR,
+ INCORRECT_CHAIN_ERROR,
+ VALIDATOR_CONFIRMATION_STATUS
+} from '../config/constants'
import { useStateProvider } from '../state/StateProvider'
import { signatureToVRS, packSignatures } from '../utils/signatures'
+import { getSuccessExecutionData } from '../utils/getFinalizationEvent'
+import { TransactionReceipt } from 'web3-eth'
const StyledButton = styled.button`
color: var(--button-color);
@@ -61,7 +69,9 @@ export const ManualExecutionButton = ({
if (!library || !foreign.bridgeContract || !signatureCollected || !signatureCollected.length) return
const signatures = packSignatures(signatureCollected.map(signatureToVRS))
- const data = foreign.bridgeContract.methods.executeSignatures(messageData, signatures).encodeABI()
+ const messageId = messageData.slice(0, 66)
+ const bridge = foreign.bridgeContract
+ const data = bridge.methods.executeSignatures(messageData, signatures).encodeABI()
setManualExecution(false)
library.eth
@@ -80,7 +90,27 @@ export const ManualExecutionButton = ({
})
setPendingExecution(true)
})
- .on('error', (e: Error) => setError(e.message))
+ .on('error', async (e: Error, receipt: TransactionReceipt) => {
+ if (e.message.includes('Transaction has been reverted by the EVM')) {
+ const successExecutionData = await getSuccessExecutionData(bridge, 'RelayedMessage', library, messageId)
+ if (successExecutionData) {
+ setExecutionData(successExecutionData)
+ setError(DOUBLE_EXECUTION_ATTEMPT_ERROR)
+ } else {
+ const { gas } = await library.eth.getTransaction(receipt.transactionHash)
+ setExecutionData({
+ status: VALIDATOR_CONFIRMATION_STATUS.FAILED,
+ validator: account,
+ txHash: receipt.transactionHash,
+ timestamp: Math.floor(new Date().getTime() / 1000.0),
+ executionResult: false
+ })
+ setError(gas === receipt.gasUsed ? EXECUTION_OUT_OF_GAS_ERROR : EXECUTION_FAILED_ERROR)
+ }
+ } else {
+ setError(e.message)
+ }
+ })
},
[
manualExecution,
diff --git a/alm/src/components/commons/ErrorAlert.tsx b/alm/src/components/commons/ErrorAlert.tsx
index af3e3f59..236353b0 100644
--- a/alm/src/components/commons/ErrorAlert.tsx
+++ b/alm/src/components/commons/ErrorAlert.tsx
@@ -2,6 +2,7 @@ import React from 'react'
import styled from 'styled-components'
import { InfoIcon } from './InfoIcon'
import { CloseIcon } from './CloseIcon'
+import { ExplorerTxLink } from './ExplorerTxLink'
const StyledErrorAlert = styled.div`
border: 1px solid var(--failed-color);
@@ -15,17 +16,33 @@ const CloseIconContainer = styled.div`
`
const TextContainer = styled.div`
+ white-space: pre-wrap;
flex-direction: column;
`
-export const ErrorAlert = ({ onClick, error }: { onClick: () => void; error: string }) => (
-
-
-
- {error}
-
-
-
-
-
-)
+export const ErrorAlert = ({ onClick, error }: { onClick: () => void; error: string }) => {
+ const errorArray = error.split('%link')
+ const text = errorArray[0]
+ let link
+ if (errorArray.length > 1) {
+ link = (
+
+ {errorArray[1]}
+
+ )
+ }
+ return (
+
+
+
+
+ {text}
+ {link}
+
+
+
+
+
+
+ )
+}
diff --git a/alm/src/config/constants.ts b/alm/src/config/constants.ts
index 5685440d..070ad9e8 100644
--- a/alm/src/config/constants.ts
+++ b/alm/src/config/constants.ts
@@ -66,3 +66,12 @@ export const VALIDATOR_CONFIRMATION_STATUS = {
export const SEARCHING_TX = 'Searching Transaction...'
export const INCORRECT_CHAIN_ERROR = `Incorrect chain chosen. Switch to ${FOREIGN_NETWORK_NAME} in the wallet.`
+
+export const DOUBLE_EXECUTION_ATTEMPT_ERROR = `Your execution transaction has been reverted.
+However, the execution completed successfully in the transaction sent by a different party.`
+
+export const EXECUTION_FAILED_ERROR = `Your execution transaction has been reverted.
+Please, contact the support by messaging on %linkhttps://forum.poa.network/c/support`
+
+export const EXECUTION_OUT_OF_GAS_ERROR = `Your execution transaction has been reverted due to Out-of-Gas error.
+Please, resend the transaction and provide more gas to it.`
diff --git a/alm/src/utils/getFinalizationEvent.ts b/alm/src/utils/getFinalizationEvent.ts
index 8f383a08..f3ff25c5 100644
--- a/alm/src/utils/getFinalizationEvent.ts
+++ b/alm/src/utils/getFinalizationEvent.ts
@@ -11,6 +11,37 @@ import {
import { getBlock, MessageObject } from './web3'
import validatorsCache from '../services/ValidatorsCache'
+export const getSuccessExecutionData = async (contract: Contract, eventName: string, web3: Web3, messageId: string) => {
+ // Since it filters by the message id, only one event will be fetched
+ // so there is no need to limit the range of the block to reduce the network traffic
+ const events: EventData[] = await contract.getPastEvents(eventName, {
+ fromBlock: 0,
+ toBlock: 'latest',
+ filter: {
+ messageId
+ }
+ })
+ if (events.length > 0) {
+ const event = events[0]
+ const [txReceipt, block] = await Promise.all([
+ web3.eth.getTransactionReceipt(event.transactionHash),
+ getBlock(web3, event.blockNumber)
+ ])
+
+ const blockTimestamp = typeof block.timestamp === 'string' ? parseInt(block.timestamp) : block.timestamp
+ const validatorAddress = web3.utils.toChecksumAddress(txReceipt.from)
+
+ return {
+ status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
+ validator: validatorAddress,
+ txHash: event.transactionHash,
+ timestamp: blockTimestamp,
+ executionResult: event.returnValues.status
+ }
+ }
+ return null
+}
+
export const getFinalizationEvent = async (
contract: Maybe,
eventName: string,
@@ -29,32 +60,9 @@ export const getFinalizationEvent = async (
setExecutionEventsFetched: Function
) => {
if (!contract || !web3 || !waitingBlocksResolved) return
- // Since it filters by the message id, only one event will be fetched
- // so there is no need to limit the range of the block to reduce the network traffic
- const events: EventData[] = await contract.getPastEvents(eventName, {
- fromBlock: 0,
- toBlock: 'latest',
- filter: {
- messageId: message.id
- }
- })
- if (events.length > 0) {
- const event = events[0]
- const [txReceipt, block] = await Promise.all([
- web3.eth.getTransactionReceipt(event.transactionHash),
- getBlock(web3, event.blockNumber)
- ])
-
- const blockTimestamp = typeof block.timestamp === 'string' ? parseInt(block.timestamp) : block.timestamp
- const validatorAddress = web3.utils.toChecksumAddress(txReceipt.from)
-
- setResult({
- status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
- validator: validatorAddress,
- txHash: event.transactionHash,
- timestamp: blockTimestamp,
- executionResult: event.returnValues.status
- })
+ const successExecutionData = await getSuccessExecutionData(contract, eventName, web3, message.id)
+ if (successExecutionData) {
+ setResult(successExecutionData)
} else {
setExecutionEventsFetched(true)
// If event is defined, it means it is a message from Home to Foreign