Implement ica body decoding and basic msg details ui

pull/11/head
J M Rossy 2 years ago
parent 45dba4e74b
commit 957b348cf0
  1. 85
      src/features/messages/MessageDetails.tsx
  2. 53
      src/features/messages/ica.ts
  3. 2
      src/features/messages/placeholderMessages.ts

@ -27,7 +27,7 @@ import { debugStatusToDesc } from '../debugger/strings';
import { MessageDebugStatus } from '../debugger/types';
import { useMessageDeliveryStatus } from '../deliveryStatus/useMessageDeliveryStatus';
import { isIcaMessage } from './ica';
import { decodeIcaBody, isIcaMessage } from './ica';
import { PLACEHOLDER_MESSAGES } from './placeholderMessages';
import { parseMessageQueryResult } from './query';
import type { MessagesQueryResult } from './types';
@ -105,7 +105,9 @@ export function MessageDetails({ messageId }: { messageId: string }) {
return (
<>
<div className="flex items-center justify-between px-1">
<h2 className="text-white text-lg">Message</h2>
<h2 className="text-white text-lg">{`${
isIcaMsg ? 'ICA ' : ''
} Message to ${getChainDisplayName(destChainId)}`}</h2>
{isMessageFound && resolvedMsgStatus === MessageStatus.Pending && (
<StatusHeader text="Status: Pending" fetching={isFetching} />
)}
@ -217,14 +219,14 @@ function TransactionCard({
</div>
{transaction && (
<>
<ValueRow
<KeyValueRow
label="Chain:"
labelWidth="w-16"
display={`${getChainDisplayName(chainId)} (${chainId} / ${chainToDomain[chainId]})`}
displayWidth="w-60 sm:w-64"
blurValue={shouldBlur}
/>
<ValueRow
<KeyValueRow
label="Tx hash:"
labelWidth="w-16"
display={transaction.transactionHash}
@ -232,7 +234,7 @@ function TransactionCard({
showCopy={true}
blurValue={shouldBlur}
/>
<ValueRow
<KeyValueRow
label="From:"
labelWidth="w-16"
display={transaction.from}
@ -240,7 +242,7 @@ function TransactionCard({
showCopy={true}
blurValue={shouldBlur}
/>
<ValueRow
<KeyValueRow
label="Time:"
labelWidth="w-16"
display={getHumanReadableTimeString(transaction.timestamp)}
@ -248,7 +250,7 @@ function TransactionCard({
displayWidth="w-60 sm:w-64"
blurValue={shouldBlur}
/>
<ValueRow
<KeyValueRow
label="Block:"
labelWidth="w-16"
display={transaction.blockNumber.toString()}
@ -337,7 +339,7 @@ function DetailsCard({
<HelpIcon size={16} text={helpText.details} />
</div>
</div>
<ValueRow
<KeyValueRow
label="Sender:"
labelWidth="w-20"
display={sender}
@ -345,7 +347,7 @@ function DetailsCard({
showCopy={true}
blurValue={shouldBlur}
/>
<ValueRow
<KeyValueRow
label="Recipient:"
labelWidth="w-20"
display={recipient}
@ -353,7 +355,7 @@ function DetailsCard({
showCopy={true}
blurValue={shouldBlur}
/>
<ValueRow
<KeyValueRow
label="Leaf index:"
labelWidth="w-20"
display={leafIndex.toString()}
@ -372,7 +374,9 @@ interface IcaDetailsCardProps {
shouldBlur: boolean;
}
function IcaDetailsCard({ message: { sender }, shouldBlur }: IcaDetailsCardProps) {
function IcaDetailsCard({ message: { body }, shouldBlur }: IcaDetailsCardProps) {
const decodeResult = useMemo(() => decodeIcaBody(body), [body]);
return (
<Card classes="mt-2 space-y-4" width="w-full">
<div className="flex items-center justify-between">
@ -384,18 +388,59 @@ function IcaDetailsCard({ message: { sender }, shouldBlur }: IcaDetailsCardProps
<HelpIcon size={16} text={helpText.icaDetails} />
</div>
</div>
<ValueRow
label="Sender:"
labelWidth="w-20"
display={sender}
displayWidth="w-60 sm:w-80"
blurValue={shouldBlur}
/>
{decodeResult ? (
<>
<KeyValueRow
label="Original sender:"
labelWidth="w-28"
display={decodeResult.sender}
displayWidth="w-60 sm:w-80"
showCopy={true}
blurValue={shouldBlur}
/>
{decodeResult.calls.length ? (
decodeResult.calls.map((c, i) => (
<div key={`ica-call-${i}`}>
<label className="text-sm text-gray-500">{`Function call ${i + 1} of ${
decodeResult.calls.length
}:`}</label>
<div className="mt-1.5 pl-4 border-l-2 border-gray-300 space-y-2">
<KeyValueRow
label="Destination address:"
labelWidth="w-32"
display={c.destinationAddress}
displayWidth="w-60 sm:w-80"
showCopy={true}
blurValue={shouldBlur}
/>
<KeyValueRow
label="Raw call bytes:"
labelWidth="w-32"
display={c.callBytes}
displayWidth="w-60 sm:w-96 lg:w-112"
showCopy={true}
blurValue={shouldBlur}
/>
</div>
</div>
))
) : (
<div>
<label className="text-sm text-gray-500">Call List:</label>
<div className="mt-1 text-sm italic">No calls found for this message.</div>
</div>
)}
</>
) : (
<div className="py-4 text-red-500 italic">
Unable to decode ICA message body, no details currently available.
</div>
)}
</Card>
);
}
function ValueRow({
function KeyValueRow({
label,
labelWidth,
display,
@ -508,5 +553,5 @@ const helpText = {
destination:
'Info about the transaction that triggered the delivery of the message from an inbox.',
details: 'Immutable information about the message itself such as its contents.',
icaDetails: 'Extra information for messages from Interchain Accounts.',
icaDetails: 'Extra information for messages from/to Interchain Accounts.',
};

@ -1,7 +1,14 @@
import { BigNumber, utils } from 'ethers';
import { hyperlaneCoreAddresses } from '@hyperlane-xyz/sdk';
import { Message } from '../../types';
import { areAddressesEqual } from '../../utils/addresses';
import { areAddressesEqual, isValidAddress } from '../../utils/addresses';
import { logger } from '../../utils/logger';
// This assumes all chains have the same ICA address
const ICA_ADDRESS = Object.values(hyperlaneCoreAddresses)[0].interchainAccountRouter;
export function isIcaMessage({ hash, sender, recipient }: Message) {
const isSenderIca = isAddressIcaRouter(sender);
const isRecipIca = isAddressIcaRouter(recipient);
@ -16,7 +23,45 @@ export function isIcaMessage({ hash, sender, recipient }: Message) {
}
function isAddressIcaRouter(addr: string) {
// TODO use address from sdk
const icaRouterAddr = '0xffD17672d47E7bB6192d5dBc12A096e00D1a206F';
return areAddressesEqual(addr, icaRouterAddr);
return areAddressesEqual(addr, ICA_ADDRESS);
}
export function decodeIcaBody(body: string) {
if (!body || BigNumber.from(body).isZero()) return null;
try {
const decoder = utils.defaultAbiCoder;
const decodedBody = decoder.decode(['address sender', 'tuple(address, bytes)[] calls'], body);
const { sender, calls } = decodedBody as unknown as {
sender: string;
calls: Array<[string, string]>;
};
if (typeof sender !== 'string' || !isValidAddress(sender)) {
throw new Error(`Invalid sender address: ${sender}`);
}
if (!Array.isArray(calls)) {
throw new Error(`Invalid call list: ${JSON.stringify(calls)}`);
}
const formattedCalls = calls.map((c) => {
const [destinationAddress, callBytes] = c;
if (typeof destinationAddress !== 'string' || !isValidAddress(destinationAddress)) {
throw new Error(`Invalid call dest address: ${destinationAddress}`);
}
if (typeof callBytes !== 'string') {
throw new Error(`Invalid call bytes: ${callBytes}`);
}
return {
destinationAddress,
callBytes,
};
});
return {
sender,
calls: formattedCalls,
};
} catch (error) {
logger.error('Error decoding ICA body', error);
return null;
}
}

@ -15,7 +15,7 @@ export const TX_ZERO: PartialTransactionReceipt = {
};
const BODY_ZERO =
'0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000';
'0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000';
export const PLACEHOLDER_MESSAGES: Message[] = [
{

Loading…
Cancel
Save