ALM: warning for auto-relayed messages (#644)
This commit is contained in:
parent
9c2d2f404c
commit
910c3759c1
@ -121,7 +121,7 @@ export const ConfirmationsContainer = ({
|
|||||||
/>
|
/>
|
||||||
{signatureCollected && (
|
{signatureCollected && (
|
||||||
<ExecutionConfirmation
|
<ExecutionConfirmation
|
||||||
messageData={message.data}
|
message={message}
|
||||||
executionData={executionData}
|
executionData={executionData}
|
||||||
isHome={!fromHome}
|
isHome={!fromHome}
|
||||||
signatureCollected={signatureCollected}
|
signatureCollected={signatureCollected}
|
||||||
|
@ -10,13 +10,14 @@ import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
|||||||
import { Thead, AgeTd, StatusTd } from './commons/Table'
|
import { Thead, AgeTd, StatusTd } from './commons/Table'
|
||||||
import { ManualExecutionButton } from './ManualExecutionButton'
|
import { ManualExecutionButton } from './ManualExecutionButton'
|
||||||
import { useStateProvider } from '../state/StateProvider'
|
import { useStateProvider } from '../state/StateProvider'
|
||||||
|
import { matchesRule, MessageObject, WarnRule } from '../utils/web3'
|
||||||
|
|
||||||
const StyledExecutionConfirmation = styled.div`
|
const StyledExecutionConfirmation = styled.div`
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
`
|
`
|
||||||
|
|
||||||
export interface ExecutionConfirmationParams {
|
export interface ExecutionConfirmationParams {
|
||||||
messageData: string
|
message: MessageObject
|
||||||
executionData: ExecutionData
|
executionData: ExecutionData
|
||||||
setExecutionData: Function
|
setExecutionData: Function
|
||||||
signatureCollected: boolean | string[]
|
signatureCollected: boolean | string[]
|
||||||
@ -26,7 +27,7 @@ export interface ExecutionConfirmationParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ExecutionConfirmation = ({
|
export const ExecutionConfirmation = ({
|
||||||
messageData,
|
message,
|
||||||
executionData,
|
executionData,
|
||||||
setExecutionData,
|
setExecutionData,
|
||||||
signatureCollected,
|
signatureCollected,
|
||||||
@ -34,7 +35,7 @@ export const ExecutionConfirmation = ({
|
|||||||
executionEventsFetched,
|
executionEventsFetched,
|
||||||
setPendingExecution
|
setPendingExecution
|
||||||
}: ExecutionConfirmationParams) => {
|
}: ExecutionConfirmationParams) => {
|
||||||
const { foreign } = useStateProvider()
|
const { foreign, setWarning } = useStateProvider()
|
||||||
const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false)
|
const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false)
|
||||||
const availableManualExecution =
|
const availableManualExecution =
|
||||||
!isHome &&
|
!isHome &&
|
||||||
@ -67,6 +68,24 @@ export const ExecutionConfirmation = ({
|
|||||||
[availableManualExecution, foreign.bridgeContract]
|
[availableManualExecution, foreign.bridgeContract]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
if (!message.data || !executionData || !availableManualExecution) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fileName = 'warnRules'
|
||||||
|
const rules: WarnRule[] = require(`../snapshots/${fileName}.json`)
|
||||||
|
for (let rule of rules) {
|
||||||
|
if (matchesRule(rule, message)) {
|
||||||
|
setWarning(rule.message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
},
|
||||||
|
[availableManualExecution, executionData, message, message.data, setWarning]
|
||||||
|
)
|
||||||
|
|
||||||
const getExecutionStatusElement = (validatorStatus = '') => {
|
const getExecutionStatusElement = (validatorStatus = '') => {
|
||||||
switch (validatorStatus) {
|
switch (validatorStatus) {
|
||||||
case VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS:
|
case VALIDATOR_CONFIRMATION_STATUS.EXECUTION_SUCCESS:
|
||||||
@ -125,7 +144,7 @@ export const ExecutionConfirmation = ({
|
|||||||
<td>
|
<td>
|
||||||
<ManualExecutionButton
|
<ManualExecutionButton
|
||||||
safeExecutionAvailable={safeExecutionAvailable}
|
safeExecutionAvailable={safeExecutionAvailable}
|
||||||
messageData={messageData}
|
messageData={message.data}
|
||||||
setExecutionData={setExecutionData}
|
setExecutionData={setExecutionData}
|
||||||
signatureCollected={signatureCollected as string[]}
|
signatureCollected={signatureCollected as string[]}
|
||||||
setPendingExecution={setPendingExecution}
|
setPendingExecution={setPendingExecution}
|
||||||
|
@ -9,6 +9,7 @@ import { InfoAlert } from './commons/InfoAlert'
|
|||||||
import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
||||||
import { FOREIGN_NETWORK_NAME, HOME_NETWORK_NAME } from '../config/constants'
|
import { FOREIGN_NETWORK_NAME, HOME_NETWORK_NAME } from '../config/constants'
|
||||||
import { ErrorAlert } from './commons/ErrorAlert'
|
import { ErrorAlert } from './commons/ErrorAlert'
|
||||||
|
import { WarningAlert } from './commons/WarningAlert'
|
||||||
|
|
||||||
const StyledMainPage = styled.div`
|
const StyledMainPage = styled.div`
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -52,7 +53,7 @@ export interface FormSubmitParams {
|
|||||||
|
|
||||||
export const MainPage = () => {
|
export const MainPage = () => {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
const { home, foreign, error, setError } = useStateProvider()
|
const { home, foreign, error, setError, warning, setWarning } = useStateProvider()
|
||||||
const [networkName, setNetworkName] = useState('')
|
const [networkName, setNetworkName] = useState('')
|
||||||
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
|
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
|
||||||
const [showInfoAlert, setShowInfoAlert] = useState(false)
|
const [showInfoAlert, setShowInfoAlert] = useState(false)
|
||||||
@ -133,6 +134,7 @@ export const MainPage = () => {
|
|||||||
</InfoAlert>
|
</InfoAlert>
|
||||||
)}
|
)}
|
||||||
{error && <ErrorAlert onClick={() => setError('')} error={error} />}
|
{error && <ErrorAlert onClick={() => setError('')} error={error} />}
|
||||||
|
{warning && <WarningAlert onClick={() => setWarning('')} error={warning} />}
|
||||||
<Route exact path={['/']} children={<Form onSubmit={onFormSubmit} />} />
|
<Route exact path={['/']} children={<Form onSubmit={onFormSubmit} />} />
|
||||||
<Route
|
<Route
|
||||||
path={['/:chainId/:txHash/:messageIdParam', '/:chainId/:txHash']}
|
path={['/:chainId/:txHash/:messageIdParam', '/:chainId/:txHash']}
|
||||||
|
34
alm/src/components/commons/WarningAlert.tsx
Normal file
34
alm/src/components/commons/WarningAlert.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { InfoIcon } from './InfoIcon'
|
||||||
|
import { CloseIcon } from './CloseIcon'
|
||||||
|
|
||||||
|
const StyledErrorAlert = styled.div`
|
||||||
|
border: 1px solid var(--warning-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-top: 10px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const CloseIconContainer = styled.div`
|
||||||
|
cursor: pointer;
|
||||||
|
`
|
||||||
|
|
||||||
|
const TextContainer = styled.div`
|
||||||
|
white-space: pre-wrap;
|
||||||
|
flex-direction: column;
|
||||||
|
`
|
||||||
|
|
||||||
|
export const WarningAlert = ({ onClick, error }: { onClick: () => void; error: string }) => {
|
||||||
|
return (
|
||||||
|
<div className="row is-center">
|
||||||
|
<StyledErrorAlert className="col-10 is-vertical-align row">
|
||||||
|
<InfoIcon color="var(--warning-color)" />
|
||||||
|
<TextContainer className="col-10">{error}</TextContainer>
|
||||||
|
<CloseIconContainer className="col-1 is-vertical-align is-center" onClick={onClick}>
|
||||||
|
<CloseIcon color="var(--warning-color)" />
|
||||||
|
</CloseIconContainer>
|
||||||
|
</StyledErrorAlert>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -27,6 +27,8 @@ export interface StateContext {
|
|||||||
loading: boolean
|
loading: boolean
|
||||||
error: string
|
error: string
|
||||||
setError: Function
|
setError: Function
|
||||||
|
warning: string
|
||||||
|
setWarning: Function
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
@ -46,7 +48,9 @@ const initialState = {
|
|||||||
},
|
},
|
||||||
loading: true,
|
loading: true,
|
||||||
error: '',
|
error: '',
|
||||||
setError: () => {}
|
setError: () => {},
|
||||||
|
warning: '',
|
||||||
|
setWarning: () => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const StateContext = createContext<StateContext>(initialState)
|
const StateContext = createContext<StateContext>(initialState)
|
||||||
@ -59,6 +63,7 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
foreignWeb3: foreignNetwork.web3
|
foreignWeb3: foreignNetwork.web3
|
||||||
})
|
})
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
|
const [warning, setWarning] = useState('')
|
||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
home: {
|
home: {
|
||||||
@ -75,7 +80,9 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
},
|
},
|
||||||
loading: homeNetwork.loading || foreignNetwork.loading,
|
loading: homeNetwork.loading || foreignNetwork.loading,
|
||||||
error,
|
error,
|
||||||
setError
|
setError,
|
||||||
|
warning,
|
||||||
|
setWarning
|
||||||
}
|
}
|
||||||
|
|
||||||
return <StateContext.Provider value={value}>{children}</StateContext.Provider>
|
return <StateContext.Provider value={value}>{children}</StateContext.Provider>
|
||||||
|
@ -28,5 +28,7 @@ export const GlobalStyle = createGlobalStyle<{ theme: ThemeType }>`
|
|||||||
--not-required-bg-color: ${props => props.theme.notRequired.backgroundColor};
|
--not-required-bg-color: ${props => props.theme.notRequired.backgroundColor};
|
||||||
--failed-color: ${props => props.theme.failed.textColor};
|
--failed-color: ${props => props.theme.failed.textColor};
|
||||||
--failed-bg-color: ${props => props.theme.failed.backgroundColor};
|
--failed-bg-color: ${props => props.theme.failed.backgroundColor};
|
||||||
|
--warning-color: ${props => props.theme.warning.textColor};
|
||||||
|
--warning-bg-color: ${props => props.theme.warning.backgroundColor};
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -17,6 +17,10 @@ const theme = {
|
|||||||
failed: {
|
failed: {
|
||||||
textColor: '#de4437',
|
textColor: '#de4437',
|
||||||
backgroundColor: 'rgba(222,68,55,.1)'
|
backgroundColor: 'rgba(222,68,55,.1)'
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
textColor: '#ffa758',
|
||||||
|
backgroundColor: 'rgba(222,68,55,.1)'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default theme
|
export default theme
|
||||||
|
@ -10,6 +10,37 @@ import { SnapshotProvider } from '../services/SnapshotProvider'
|
|||||||
export interface MessageObject {
|
export interface MessageObject {
|
||||||
id: string
|
id: string
|
||||||
data: string
|
data: string
|
||||||
|
sender?: string
|
||||||
|
executor?: string
|
||||||
|
obToken?: string
|
||||||
|
obReceiver?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarnRule {
|
||||||
|
message: string
|
||||||
|
sender?: string
|
||||||
|
executor?: string
|
||||||
|
obToken?: string
|
||||||
|
obReceiver?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const matchesRule = (rule: WarnRule, msg: MessageObject) => {
|
||||||
|
if (!msg.executor || !msg.sender) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!!rule.executor && rule.executor.toLowerCase() !== msg.executor.toLowerCase()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!!rule.sender && rule.sender.toLowerCase() !== msg.sender.toLowerCase()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!!rule.obToken && (!msg.obToken || rule.obToken.toLowerCase() !== msg.obToken.toLowerCase())) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!!rule.obReceiver && (!msg.obReceiver || rule.obReceiver.toLowerCase() !== msg.obReceiver.toLowerCase())) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const rawGetWeb3 = (url: string) => new Web3(new Web3.providers.HttpProvider(url))
|
const rawGetWeb3 = (url: string) => new Web3(new Web3.providers.HttpProvider(url))
|
||||||
@ -26,15 +57,33 @@ export const filterEventsByAbi = (
|
|||||||
const eventHash = web3.eth.abi.encodeEventSignature(eventAbi)
|
const eventHash = web3.eth.abi.encodeEventSignature(eventAbi)
|
||||||
const events = txReceipt.logs.filter(e => e.address === bridgeAddress && e.topics[0] === eventHash)
|
const events = txReceipt.logs.filter(e => e.address === bridgeAddress && e.topics[0] === eventHash)
|
||||||
|
|
||||||
|
if (!eventAbi || !eventAbi.inputs || !eventAbi.inputs.length) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const inputs = eventAbi.inputs
|
||||||
return events.map(e => {
|
return events.map(e => {
|
||||||
let decodedLogs: { [p: string]: string } = {
|
const { messageId, encodedData } = web3.eth.abi.decodeLog(inputs, e.data, [e.topics[1]])
|
||||||
messageId: '',
|
let sender, executor, obToken, obReceiver
|
||||||
encodedData: ''
|
if (encodedData.length >= 160) {
|
||||||
|
sender = `0x${encodedData.slice(66, 106)}`
|
||||||
|
executor = `0x${encodedData.slice(106, 146)}`
|
||||||
|
const dataOffset =
|
||||||
|
160 + (parseInt(encodedData.slice(154, 156), 16) + parseInt(encodedData.slice(156, 158), 16)) * 2 + 8
|
||||||
|
if (encodedData.length >= dataOffset + 64) {
|
||||||
|
obToken = `0x${encodedData.slice(dataOffset + 24, dataOffset + 64)}`
|
||||||
}
|
}
|
||||||
if (eventAbi && eventAbi.inputs && eventAbi.inputs.length) {
|
if (encodedData.length >= dataOffset + 128) {
|
||||||
decodedLogs = web3.eth.abi.decodeLog(eventAbi.inputs, e.data, [e.topics[1]])
|
obReceiver = `0x${encodedData.slice(dataOffset + 88, dataOffset + 128)}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: messageId || '',
|
||||||
|
data: encodedData || '',
|
||||||
|
sender,
|
||||||
|
executor,
|
||||||
|
obToken,
|
||||||
|
obReceiver
|
||||||
}
|
}
|
||||||
return { id: decodedLogs.messageId, data: decodedLogs.encodedData }
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user