diff --git a/packages/ui/components/ui/tx/actions-views/ibc-relay.tsx b/packages/ui/components/ui/tx/actions-views/ibc-relay.tsx
index c911f6e49..6ba7a781d 100644
--- a/packages/ui/components/ui/tx/actions-views/ibc-relay.tsx
+++ b/packages/ui/components/ui/tx/actions-views/ibc-relay.tsx
@@ -4,93 +4,119 @@ import {
FungibleTokenPacketData,
IbcRelay,
} from '@penumbra-zone/protobuf/penumbra/core/component/ibc/v1/ibc_pb';
-import { MsgRecvPacket } from '@penumbra-zone/protobuf/ibc/core/channel/v1/tx_pb';
+import {
+ MsgAcknowledgement,
+ MsgRecvPacket,
+ MsgTimeout,
+ MsgTimeoutOnClose,
+} from '@penumbra-zone/protobuf/ibc/core/channel/v1/tx_pb';
import { MsgUpdateClient } from '@penumbra-zone/protobuf/ibc/core/client/v1/tx_pb';
import { UnimplementedView } from './unimplemented-view.tsx';
import { uint8ArrayToBase64 } from '@penumbra-zone/types/base64';
+import { ReactElement } from 'react';
+import { Packet } from '@penumbra-zone/protobuf/ibc/core/channel/v1/channel_pb';
import { getUtcTime } from './isc20-withdrawal.tsx';
-import { useMemo } from 'react';
-// Packet data stored as json string encoded into bytes
-const parsePacket = ({ packet }: MsgRecvPacket): FungibleTokenPacketData | undefined => {
- if (!packet?.data) {
+// Attempt to parse data w/ fallbacks if unknown or failures
+const ParsedPacketData = ({
+ data,
+ dataParser,
+}: {
+ data: Uint8Array;
+ dataParser?: (arg: Uint8Array) => ReactElement;
+}) => {
+ if (!data.length) {
return undefined;
}
+ if (!dataParser) {
+ return Unknown packet data;
+ }
+
try {
- const dataString = new TextDecoder().decode(packet.data);
- return FungibleTokenPacketData.fromJsonString(dataString);
+ return dataParser(data);
} catch (e) {
- return undefined;
+ return Error while parsing data;
}
};
-const MsgResvComponent = ({ packet }: { packet: MsgRecvPacket }) => {
- const packetData = useMemo(() => parsePacket(packet), [packet]);
+const PacketRows = ({
+ packet,
+ dataParser,
+}: {
+ packet: Packet;
+ dataParser?: (arg: Uint8Array) => ReactElement;
+}) => {
+ return (
+ <>
+
+ {!!packet.sequence && (
+ {Number(packet.sequence)}
+ )}
+ {!!packet.sourcePort && (
+ {packet.sourcePort}
+ )}
+ {!!packet.sourceChannel && (
+ {packet.sourceChannel}
+ )}
+ {!!packet.destinationPort && (
+ {packet.destinationPort}
+ )}
+ {!!packet.destinationChannel && (
+
+ {packet.destinationChannel}
+
+ )}
+ {!!packet.timeoutHeight?.revisionHeight && (
+
+ {Number(packet.timeoutHeight.revisionHeight)}
+
+ )}
+ {!!packet.timeoutHeight?.revisionNumber && (
+
+ {Number(packet.timeoutHeight.revisionNumber)}
+
+ )}
+ {!!packet.timeoutTimestamp && (
+
+ {getUtcTime(packet.timeoutTimestamp)}
+
+ )}
+ >
+ );
+};
+
+// Packet data stored as json string encoded into bytes
+const parseRecvPacket = (packetData: Uint8Array) => {
+ const dataString = new TextDecoder().decode(packetData);
+ const parsed = FungibleTokenPacketData.fromJsonString(dataString);
+ return (
+ <>
+ {!!parsed.sender && (
+
+ {parsed.sender}
+
+ )}
+ {!!parsed.receiver && (
+
+ {parsed.receiver}
+
+ )}
+ {!!parsed.denom && {parsed.denom}}
+ {!!parsed.amount && {parsed.amount}}
+ {'memo' in parsed && {parsed.memo}}
+ >
+ );
+};
+const MsgResvComponent = ({ packet }: { packet: MsgRecvPacket }) => {
return (
- {packetData === undefined && (
- Unknown packet data
- )}
- {!!packetData?.sender && (
-
- {packetData.sender}
-
- )}
- {!!packetData?.receiver && (
-
- {packetData.receiver}
-
- )}
- {!!packetData?.denom && (
- {packetData.denom}
- )}
- {!!packetData?.amount && (
- {packetData.amount}
- )}
- {packetData && 'memo' in packetData && (
- {packetData.memo}
- )}
- {!!packet.packet?.sequence && (
- {Number(packet.packet.sequence)}
- )}
- {!!packet.packet?.sourcePort && (
- {packet.packet.sourcePort}
- )}
- {!!packet.packet?.sourceChannel && (
-
- {packet.packet.sourceChannel}
-
- )}
- {!!packet.packet?.destinationPort && (
-
- {packet.packet.destinationPort}
-
- )}
- {!!packet.packet?.destinationChannel && (
-
- {packet.packet.destinationChannel}
-
- )}
- {!!packet.packet?.timeoutHeight?.revisionHeight && (
-
- {Number(packet.packet.timeoutHeight.revisionHeight)}
-
- )}
- {!!packet.packet?.timeoutHeight?.revisionNumber && (
-
- {Number(packet.packet.timeoutHeight.revisionNumber)}
-
- )}
- {!!packet.packet?.timeoutTimestamp && (
-
- {getUtcTime(packet.packet.timeoutTimestamp)}
-
- )}
+ {!!packet.packet && }
+
{packet.signer}
{!!packet.proofHeight?.revisionHeight && (
@@ -127,6 +153,72 @@ const UpdateClientComponent = ({ update }: { update: MsgUpdateClient }) => {
);
};
+const MsgTimeoutComponent = ({ timeout }: { timeout: MsgTimeout }) => {
+ return (
+
+ {!!timeout.packet && }
+ {!!timeout.proofHeight?.revisionHeight && (
+
+ {Number(timeout.proofHeight.revisionHeight)}
+
+ )}
+ {!!timeout.proofHeight?.revisionNumber && (
+
+ {Number(timeout.proofHeight.revisionNumber)}
+
+ )}
+
+ {Number(timeout.nextSequenceRecv)}
+
+ {timeout.signer}
+
+
+ {uint8ArrayToBase64(timeout.proofUnreceived)}
+
+
+
+ }
+ />
+ );
+};
+
+const MsgAckComponent = ({ ack }: { ack: MsgAcknowledgement }) => {
+ return (
+
+ {!!ack.packet && }
+ {!!ack.proofHeight?.revisionHeight && (
+
+ {Number(ack.proofHeight.revisionHeight)}
+
+ )}
+ {!!ack.proofHeight?.revisionNumber && (
+
+ {Number(ack.proofHeight.revisionNumber)}
+
+ )}
+ {ack.signer}
+
+
+ {uint8ArrayToBase64(ack.acknowledgement)}
+
+
+
+
+ {uint8ArrayToBase64(ack.proofAcked)}
+
+
+
+ }
+ />
+ );
+};
+
export const IbcRelayComponent = ({ value }: { value: IbcRelay }) => {
if (value.rawAction?.is(MsgRecvPacket.typeName)) {
const packet = new MsgRecvPacket();
@@ -140,5 +232,23 @@ export const IbcRelayComponent = ({ value }: { value: IbcRelay }) => {
return ;
}
+ if (value.rawAction?.is(MsgTimeout.typeName)) {
+ const timeout = new MsgTimeout();
+ value.rawAction.unpackTo(timeout);
+ return ;
+ }
+
+ if (value.rawAction?.is(MsgTimeoutOnClose.typeName)) {
+ const timeout = new MsgTimeoutOnClose();
+ value.rawAction.unpackTo(timeout);
+ return ;
+ }
+
+ if (value.rawAction?.is(MsgAcknowledgement.typeName)) {
+ const ack = new MsgAcknowledgement();
+ value.rawAction.unpackTo(ack);
+ return ;
+ }
+
return ;
};