diff --git a/alm/src/components/ConfirmationsContainer.tsx b/alm/src/components/ConfirmationsContainer.tsx index a307fec97..e7e2befa3 100644 --- a/alm/src/components/ConfirmationsContainer.tsx +++ b/alm/src/components/ConfirmationsContainer.tsx @@ -121,7 +121,7 @@ export const ConfirmationsContainer = ({ /> {signatureCollected && ( { - const { foreign } = useStateProvider() + const { foreign, setWarning } = useStateProvider() const [safeExecutionAvailable, setSafeExecutionAvailable] = useState(false) const availableManualExecution = !isHome && @@ -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: @@ -125,7 +144,7 @@ export const ExecutionConfirmation = ({ { 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>(null) const [showInfoAlert, setShowInfoAlert] = useState(false) @@ -133,6 +134,7 @@ export const MainPage = () => { )} {error && setError('')} error={error} />} + {warning && setWarning('')} error={warning} />} } /> void; error: string }) => { + return ( +
+ + + {error} + + + + +
+ ) +} diff --git a/alm/src/state/StateProvider.tsx b/alm/src/state/StateProvider.tsx index e324c9b64..f753a91d5 100644 --- a/alm/src/state/StateProvider.tsx +++ b/alm/src/state/StateProvider.tsx @@ -27,6 +27,8 @@ export interface StateContext { loading: boolean error: string setError: Function + warning: string + setWarning: Function } const initialState = { @@ -46,7 +48,9 @@ const initialState = { }, loading: true, error: '', - setError: () => {} + setError: () => {}, + warning: '', + setWarning: () => {} } const StateContext = createContext(initialState) @@ -59,6 +63,7 @@ export const StateProvider = ({ children }: { children: ReactNode }) => { foreignWeb3: foreignNetwork.web3 }) const [error, setError] = useState('') + const [warning, setWarning] = useState('') const value = { home: { @@ -75,7 +80,9 @@ export const StateProvider = ({ children }: { children: ReactNode }) => { }, loading: homeNetwork.loading || foreignNetwork.loading, error, - setError + setError, + warning, + setWarning } return {children} diff --git a/alm/src/themes/GlobalStyle.tsx b/alm/src/themes/GlobalStyle.tsx index a8e62da9a..ca7af0d73 100644 --- a/alm/src/themes/GlobalStyle.tsx +++ b/alm/src/themes/GlobalStyle.tsx @@ -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}; } ` diff --git a/alm/src/themes/Light.ts b/alm/src/themes/Light.ts index 0c972e841..1548dedef 100644 --- a/alm/src/themes/Light.ts +++ b/alm/src/themes/Light.ts @@ -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 diff --git a/alm/src/utils/web3.ts b/alm/src/utils/web3.ts index 09d129b72..ff3fd197c 100644 --- a/alm/src/utils/web3.ts +++ b/alm/src/utils/web3.ts @@ -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)) @@ -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 } }) }