Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ALM: warning for auto-relayed messages #644

Merged
merged 2 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion alm/src/components/ConfirmationsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export const ConfirmationsContainer = ({
/>
{signatureCollected && (
<ExecutionConfirmation
messageData={message.data}
message={message}
executionData={executionData}
isHome={!fromHome}
signatureCollected={signatureCollected}
Expand Down
27 changes: 23 additions & 4 deletions alm/src/components/ExecutionConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import { ExplorerTxLink } from './commons/ExplorerTxLink'
import { Thead, AgeTd, StatusTd } from './commons/Table'
import { ManualExecutionButton } from './ManualExecutionButton'
import { useStateProvider } from '../state/StateProvider'
import { matchesRule, MessageObject, WarnRule } from '../utils/web3'

const StyledExecutionConfirmation = styled.div`
margin-top: 30px;
`

export interface ExecutionConfirmationParams {
messageData: string
message: MessageObject
executionData: ExecutionData
setExecutionData: Function
signatureCollected: boolean | string[]
Expand All @@ -26,15 +27,15 @@ export interface ExecutionConfirmationParams {
}

export const ExecutionConfirmation = ({
messageData,
message,
executionData,
setExecutionData,
signatureCollected,
isHome,
executionEventsFetched,
setPendingExecution
}: ExecutionConfirmationParams) => {
const { foreign } = useStateProvider()
const { foreign, setWarning } = useStateProvider()
const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false)
const availableManualExecution =
!isHome &&
Expand Down Expand Up @@ -67,6 +68,24 @@ export const ExecutionConfirmation = ({
[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 = '') => {
switch (validatorStatus) {
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
Expand Down Expand Up @@ -125,7 +144,7 @@ export const ExecutionConfirmation = ({
<td>
<ManualExecutionButton
safeExecutionAvailable={safeExecutionAvailable}
messageData={messageData}
messageData={message.data}
setExecutionData={setExecutionData}
signatureCollected={signatureCollected as string[]}
setPendingExecution={setPendingExecution}
Expand Down
4 changes: 3 additions & 1 deletion alm/src/components/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { InfoAlert } from './commons/InfoAlert'
import { ExplorerTxLink } from './commons/ExplorerTxLink'
import { FOREIGN_NETWORK_NAME, HOME_NETWORK_NAME } from '../config/constants'
import { ErrorAlert } from './commons/ErrorAlert'
import { WarningAlert } from './commons/WarningAlert'

const StyledMainPage = styled.div`
text-align: center;
Expand Down Expand Up @@ -52,7 +53,7 @@ export interface FormSubmitParams {

export const MainPage = () => {
const history = useHistory()
const { home, foreign, error, setError } = useStateProvider()
const { home, foreign, error, setError, warning, setWarning } = useStateProvider()
const [networkName, setNetworkName] = useState('')
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
const [showInfoAlert, setShowInfoAlert] = useState(false)
Expand Down Expand Up @@ -133,6 +134,7 @@ export const MainPage = () => {
</InfoAlert>
)}
{error && <ErrorAlert onClick={() => setError('')} error={error} />}
{warning && <WarningAlert onClick={() => setWarning('')} error={warning} />}
<Route exact path={['/']} children={<Form onSubmit={onFormSubmit} />} />
<Route
path={['/:chainId/:txHash/:messageIdParam', '/:chainId/:txHash']}
Expand Down
34 changes: 34 additions & 0 deletions alm/src/components/commons/WarningAlert.tsx
Original file line number Diff line number Diff line change
@@ -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>
)
}
11 changes: 9 additions & 2 deletions alm/src/state/StateProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export interface StateContext {
loading: boolean
error: string
setError: Function
warning: string
setWarning: Function
}

const initialState = {
Expand All @@ -46,7 +48,9 @@ const initialState = {
},
loading: true,
error: '',
setError: () => {}
setError: () => {},
warning: '',
setWarning: () => {}
}

const StateContext = createContext<StateContext>(initialState)
Expand All @@ -59,6 +63,7 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
foreignWeb3: foreignNetwork.web3
})
const [error, setError] = useState('')
const [warning, setWarning] = useState('')

const value = {
home: {
Expand All @@ -75,7 +80,9 @@ export const StateProvider = ({ children }: { children: ReactNode }) => {
},
loading: homeNetwork.loading || foreignNetwork.loading,
error,
setError
setError,
warning,
setWarning
}

return <StateContext.Provider value={value}>{children}</StateContext.Provider>
Expand Down
2 changes: 2 additions & 0 deletions alm/src/themes/GlobalStyle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,7 @@ export const GlobalStyle = createGlobalStyle<{ theme: ThemeType }>`
--not-required-bg-color: ${props => props.theme.notRequired.backgroundColor};
--failed-color: ${props => props.theme.failed.textColor};
--failed-bg-color: ${props => props.theme.failed.backgroundColor};
--warning-color: ${props => props.theme.warning.textColor};
--warning-bg-color: ${props => props.theme.warning.backgroundColor};
}
`
4 changes: 4 additions & 0 deletions alm/src/themes/Light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const theme = {
failed: {
textColor: '#de4437',
backgroundColor: 'rgba(222,68,55,.1)'
},
warning: {
textColor: '#ffa758',
backgroundColor: 'rgba(222,68,55,.1)'
}
}
export default theme
61 changes: 55 additions & 6 deletions alm/src/utils/web3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,37 @@ import { SnapshotProvider } from '../services/SnapshotProvider'
export interface MessageObject {
id: 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))
Expand All @@ -26,15 +57,33 @@ export const filterEventsByAbi = (
const eventHash = web3.eth.abi.encodeEventSignature(eventAbi)
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 => {
let decodedLogs: { [p: string]: string } = {
messageId: '',
encodedData: ''
const { messageId, encodedData } = web3.eth.abi.decodeLog(inputs, e.data, [e.topics[1]])
let sender, executor, obToken, obReceiver
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 (encodedData.length >= dataOffset + 128) {
obReceiver = `0x${encodedData.slice(dataOffset + 88, dataOffset + 128)}`
}
}
if (eventAbi && eventAbi.inputs && eventAbi.inputs.length) {
decodedLogs = web3.eth.abi.decodeLog(eventAbi.inputs, e.data, [e.topics[1]])
return {
id: messageId || '',
data: encodedData || '',
sender,
executor,
obToken,
obReceiver
}
return { id: decodedLogs.messageId, data: decodedLogs.encodedData }
})
}

Expand Down