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 ; };