From ef01ee19c362a0362488212d003a7ee27fc0d060 Mon Sep 17 00:00:00 2001
From: Hussein Ait Lahcen <hussein.aitlahcen@gmail.com>
Date: Thu, 20 Mar 2025 11:54:13 +0100
Subject: [PATCH 1/2] feat(zkgm): introduce partial market maker filling on
 finality

---
 .../ibc-union/app/ucs03-zkgm/src/contract.rs  | 150 +++++++++++++-----
 cosmwasm/ibc-union/app/ucs03-zkgm/src/lib.rs  |   4 +
 cosmwasm/ibc-union/app/ucs03-zkgm/src/msg.rs  |   9 +-
 .../ibc-union/app/ucs03-zkgm/src/state.rs     |   2 +
 evm/contracts/apps/ucs/03-zkgm/Zkgm.sol       |  60 +++++--
 evm/tests/src/04-channel/IBCChannel.t.sol     |   6 +-
 evm/tests/src/04-channel/IBCPacket.t.sol      |   6 +-
 7 files changed, 172 insertions(+), 65 deletions(-)

diff --git a/cosmwasm/ibc-union/app/ucs03-zkgm/src/contract.rs b/cosmwasm/ibc-union/app/ucs03-zkgm/src/contract.rs
index e9b177a4dd..f7331ede49 100644
--- a/cosmwasm/ibc-union/app/ucs03-zkgm/src/contract.rs
+++ b/cosmwasm/ibc-union/app/ucs03-zkgm/src/contract.rs
@@ -32,7 +32,8 @@ use crate::{
     msg::{EurekaMsg, ExecuteMsg, InitMsg, PredictWrappedTokenResponse, QueryMsg},
     state::{
         BATCH_EXECUTION_ACKS, CHANNEL_BALANCE, CONFIG, EXECUTING_PACKET, EXECUTING_PACKET_IS_BATCH,
-        EXECUTION_ACK, HASH_TO_FOREIGN_TOKEN, IN_FLIGHT_PACKET, TOKEN_MINTER, TOKEN_ORIGIN,
+        EXECUTION_ACK, HASH_TO_FOREIGN_TOKEN, IN_FLIGHT_PACKET, MARKET_MAKER, TOKEN_MINTER,
+        TOKEN_ORIGIN,
     },
     ContractError,
 };
@@ -44,6 +45,7 @@ pub const TOKEN_INIT_REPLY_ID: u64 = 0xbeef;
 pub const ESCROW_REPLY_ID: u64 = 0xcafe;
 pub const FORWARD_REPLY_ID: u64 = 0xbabe;
 pub const MULTIPLEX_REPLY_ID: u64 = 0xface;
+pub const MM_FILL_REPLY_ID: u64 = 0xdead;
 
 pub const ZKGM_TOKEN_MINTER_LABEL: &str = "zkgm-token-minter";
 
@@ -177,6 +179,13 @@ pub fn execute(
                 ),
             }
         }
+        ExecuteMsg::InternalBatch { messages } => {
+            if info.sender != env.contract.address {
+                Err(ContractError::OnlySelf)
+            } else {
+                Ok(Response::new().add_messages(messages))
+            }
+        }
         ExecuteMsg::InternalExecutePacket {
             caller,
             packet,
@@ -777,6 +786,7 @@ fn execute_internal(
                 deps,
                 env,
                 info,
+                caller,
                 packet,
                 relayer,
                 relayer_msg,
@@ -1072,10 +1082,11 @@ fn execute_batch(
 fn execute_fungible_asset_order(
     deps: DepsMut,
     env: Env,
-    _info: MessageInfo,
+    info: MessageInfo,
+    caller: Addr,
     packet: Packet,
     relayer: Addr,
-    _relayer_msg: Bytes,
+    relayer_msg: Bytes,
     _salt: H256,
     path: U256,
     order: FungibleAssetOrder,
@@ -1112,7 +1123,7 @@ fn execute_fungible_asset_order(
     let base_covers_quote = base_amount >= quote_amount;
     let fee_amount = base_amount.saturating_sub(quote_amount);
 
-    let mut messages = Vec::<SubMsg>::new();
+    let mut messages = Vec::new();
 
     // Protocol Fill - If the quote token matches the wrapped version of the base token and base amount >= quote amount
     if quote_token_str == wrapped_denom && base_covers_quote {
@@ -1126,7 +1137,7 @@ fn execute_fungible_asset_order(
             )?;
 
             // Create the token with metadata
-            messages.push(SubMsg::new(make_wasm_msg(
+            messages.push(SubMsg::reply_never(make_wasm_msg(
                 WrappedTokenMsg::CreateDenom {
                     subdenom: wrapped_denom.clone(),
                     metadata: Metadata {
@@ -1154,7 +1165,7 @@ fn execute_fungible_asset_order(
 
         // Mint the quote amount to the receiver
         if quote_amount > 0 {
-            messages.push(SubMsg::new(make_wasm_msg(
+            messages.push(SubMsg::reply_never(make_wasm_msg(
                 WrappedTokenMsg::MintTokens {
                     denom: wrapped_denom.clone(),
                     amount: quote_amount.into(),
@@ -1167,7 +1178,7 @@ fn execute_fungible_asset_order(
 
         // Mint any fee to the relayer
         if fee_amount > 0 {
-            messages.push(SubMsg::new(make_wasm_msg(
+            messages.push(SubMsg::reply_never(make_wasm_msg(
                 WrappedTokenMsg::MintTokens {
                     denom: wrapped_denom,
                     amount: fee_amount.into(),
@@ -1191,7 +1202,7 @@ fn execute_fungible_asset_order(
 
         // Transfer the quote amount to the receiver
         if quote_amount > 0 {
-            messages.push(SubMsg::new(make_wasm_msg(
+            messages.push(SubMsg::reply_never(make_wasm_msg(
                 LocalTokenMsg::Unescrow {
                     denom: quote_token_str.clone(),
                     recipient: receiver.into_string(),
@@ -1204,7 +1215,7 @@ fn execute_fungible_asset_order(
 
         // Transfer any fee to the relayer
         if fee_amount > 0 {
-            messages.push(SubMsg::new(make_wasm_msg(
+            messages.push(SubMsg::reply_never(make_wasm_msg(
                 LocalTokenMsg::Unescrow {
                     denom: quote_token_str,
                     recipient: relayer.into_string(),
@@ -1217,14 +1228,42 @@ fn execute_fungible_asset_order(
     }
     // Market Maker Fill - Any party can fill the order by providing the quote token
     else {
-        // Return a special acknowledgement that indicates this order needs a market maker
-        return Ok(Response::new().add_message(wasm_execute(
-            env.contract.address,
-            &ExecuteMsg::InternalWriteAck {
-                ack: ACK_ERR_ONLY_MAKER.into(),
-            },
-            vec![],
-        )?));
+        if quote_amount > 0 {
+            MARKET_MAKER.save(deps.storage, &relayer_msg)?;
+            messages.push(SubMsg::reply_always(
+                wasm_execute(
+                    &env.contract.address,
+                    &ExecuteMsg::InternalBatch {
+                        messages: vec![
+                            // Make sure the marker provide the funds
+                            make_wasm_msg(
+                                LocalTokenMsg::Escrow {
+                                    from: caller.to_string(),
+                                    denom: quote_token_str.clone(),
+                                    recipient: minter.to_string(),
+                                    amount: quote_amount.into(),
+                                },
+                                &minter,
+                                info.funds,
+                            )?,
+                            // Release the funds to the user
+                            make_wasm_msg(
+                                LocalTokenMsg::Unescrow {
+                                    denom: quote_token_str,
+                                    recipient: receiver.to_string(),
+                                    amount: quote_amount.into(),
+                                },
+                                minter,
+                                vec![],
+                            )?,
+                        ],
+                    },
+                    vec![],
+                )?,
+                MM_FILL_REPLY_ID,
+            ));
+        }
+        return Ok(Response::new().add_submessages(messages));
     }
 
     // Return success acknowledgement with protocol fill type
@@ -1289,7 +1328,7 @@ pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> Result<Response, Contract
             let packet = EXECUTING_PACKET.load(deps.storage)?;
             EXECUTING_PACKET.remove(deps.storage);
             match reply.result {
-                SubMsgResult::Ok(_) => {
+                SubMsgResult::Ok(msg) => {
                     // If the execution succedeed one of the acks is guaranteed to exist.
                     let execution_ack = (|| -> Result<Bytes, ContractError> {
                         match EXECUTING_PACKET_IS_BATCH.may_load(deps.storage)? {
@@ -1337,19 +1376,21 @@ pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> Result<Response, Contract
                             inner_ack: Vec::from(execution_ack).into(),
                         }
                         .abi_encode_params();
-                        Ok(Response::new().add_message(wasm_execute(
-                            &ibc_host,
-                            &ibc_union_msg::msg::ExecuteMsg::WriteAcknowledgement(
-                                MsgWriteAcknowledgement {
-                                    packet,
-                                    acknowledgement: zkgm_ack.into(),
-                                },
-                            ),
-                            vec![],
-                        )?))
+                        Ok(Response::new()
+                            .add_events(msg.events)
+                            .add_message(wasm_execute(
+                                &ibc_host,
+                                &ibc_union_msg::msg::ExecuteMsg::WriteAcknowledgement(
+                                    MsgWriteAcknowledgement {
+                                        packet,
+                                        acknowledgement: zkgm_ack.into(),
+                                    },
+                                ),
+                                vec![],
+                            )?))
                     } else {
                         // Async acknowledgement, we don't write anything
-                        Ok(Response::new())
+                        Ok(Response::new().add_events(msg.events))
                     }
                 }
                 // Something went horribly wrong.
@@ -1411,26 +1452,55 @@ pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> Result<Response, Contract
         // eureka mode of multiplex operations.
         MULTIPLEX_REPLY_ID => {
             // Extract the acknowledgement from the reply
-            if let SubMsgResult::Ok(reply_data) = reply.result {
-                #[allow(deprecated)]
-                let acknowledgement = reply_data.data.unwrap_or_default();
+            match reply.result {
+                SubMsgResult::Ok(reply_data) => {
+                    #[allow(deprecated)]
+                    let acknowledgement = reply_data.data.unwrap_or_default();
 
-                // If the acknowledgement is empty, we can't proceed
-                if acknowledgement.is_empty() {
-                    return Err(ContractError::AsyncMultiplexUnsupported);
-                }
+                    // If the acknowledgement is empty, we can't proceed
+                    if acknowledgement.is_empty() {
+                        return Err(ContractError::AsyncMultiplexUnsupported);
+                    }
 
+                    Ok(Response::new().add_message(wasm_execute(
+                        env.contract.address,
+                        &ExecuteMsg::InternalWriteAck {
+                            ack: Vec::from(acknowledgement).into(),
+                        },
+                        vec![],
+                    )?))
+                }
+                SubMsgResult::Err(error) => Err(ContractError::MultiplexError { error }),
+            }
+        }
+        MM_FILL_REPLY_ID => match reply.result {
+            SubMsgResult::Ok(_) => {
+                let market_maker = MARKET_MAKER.load(deps.storage)?;
+                MARKET_MAKER.remove(deps.storage);
                 Ok(Response::new().add_message(wasm_execute(
                     env.contract.address,
                     &ExecuteMsg::InternalWriteAck {
-                        ack: Vec::from(acknowledgement).into(),
+                        ack: FungibleAssetOrderAck {
+                            fill_type: FILL_TYPE_MARKETMAKER,
+                            market_maker: Vec::from(market_maker).into(),
+                        }
+                        .abi_encode_params()
+                        .into(),
                     },
                     vec![],
                 )?))
-            } else {
-                Err(ContractError::AsyncMultiplexUnsupported)
             }
-        }
+            // Leave a chance for another MM to fill by telling the top level handler to revert.
+            SubMsgResult::Err(error) => Ok(Response::new()
+                .add_attribute("maker_execution_failure", error)
+                .add_message(wasm_execute(
+                    env.contract.address,
+                    &ExecuteMsg::InternalWriteAck {
+                        ack: ACK_ERR_ONLY_MAKER.into(),
+                    },
+                    vec![],
+                )?)),
+        },
         // For any other reply ID, we don't know how to handle it, so we return an error.
         // This is a safety measure to ensure we don't silently ignore unexpected replies,
         // which could indicate a bug in the contract or an attempt to exploit it.
diff --git a/cosmwasm/ibc-union/app/ucs03-zkgm/src/lib.rs b/cosmwasm/ibc-union/app/ucs03-zkgm/src/lib.rs
index b99b519a30..e9b52ca99c 100644
--- a/cosmwasm/ibc-union/app/ucs03-zkgm/src/lib.rs
+++ b/cosmwasm/ibc-union/app/ucs03-zkgm/src/lib.rs
@@ -33,6 +33,8 @@ pub enum ContractError {
     UnknownReply { id: u64 },
     #[error("invalid operation, can only be executed by a market maker")]
     OnlyMaker,
+    #[error("market maker failed to fill: {error}")]
+    MarketMakerFailed { error: String },
     #[error("packet execution reentrancy not allowed")]
     AlreadyExecuting,
     #[error("order amount must be u128")]
@@ -92,6 +94,8 @@ pub enum ContractError {
     },
     #[error("asynchronous multiplexing is not supported")]
     AsyncMultiplexUnsupported,
+    #[error("an error happened while calling the destination contract: {error}")]
+    MultiplexError { error: String },
     #[error("channel path is full and can't be updated, too many hops? path: {path}, next_hop_index: {next_hop_index}")]
     ChannelPathIsFull { path: U256, next_hop_index: usize },
     #[error("invalid asset origin path")]
diff --git a/cosmwasm/ibc-union/app/ucs03-zkgm/src/msg.rs b/cosmwasm/ibc-union/app/ucs03-zkgm/src/msg.rs
index 5bc1e32fe3..41563ae127 100644
--- a/cosmwasm/ibc-union/app/ucs03-zkgm/src/msg.rs
+++ b/cosmwasm/ibc-union/app/ucs03-zkgm/src/msg.rs
@@ -1,5 +1,5 @@
 use cosmwasm_schema::cw_serde;
-use cosmwasm_std::{Addr, Uint256};
+use cosmwasm_std::{Addr, CosmosMsg, Uint256};
 use ibc_union_spec::{ChannelId, Packet};
 use unionlabs::primitives::{Bytes, H256};
 
@@ -62,7 +62,12 @@ pub enum ExecuteMsg {
     },
     /// Write an acknowledgement for an Zkgm packet.
     /// Can only be called by the contract itself after packet execution.
-    InternalWriteAck { ack: Bytes },
+    InternalWriteAck {
+        ack: Bytes,
+    },
+    InternalBatch {
+        messages: Vec<CosmosMsg>,
+    },
 }
 
 #[derive(serde::Serialize, serde::Deserialize)]
diff --git a/cosmwasm/ibc-union/app/ucs03-zkgm/src/state.rs b/cosmwasm/ibc-union/app/ucs03-zkgm/src/state.rs
index 6539cdb632..7d8546400d 100644
--- a/cosmwasm/ibc-union/app/ucs03-zkgm/src/state.rs
+++ b/cosmwasm/ibc-union/app/ucs03-zkgm/src/state.rs
@@ -48,3 +48,5 @@ pub const HASH_TO_FOREIGN_TOKEN: Map<String, Bytes> = Map::new("hash_to_foreign_
 /// we need to find the original packet that initiated the forward to properly
 /// propagate the acknowledgement or timeout back to the source.
 pub const IN_FLIGHT_PACKET: Map<Vec<u8>, Packet> = Map::new("in_flight_packet");
+
+pub const MARKET_MAKER: Item<Bytes> = Item::new("market_maker");
diff --git a/evm/contracts/apps/ucs/03-zkgm/Zkgm.sol b/evm/contracts/apps/ucs/03-zkgm/Zkgm.sol
index 2173283051..f1e1191123 100644
--- a/evm/contracts/apps/ucs/03-zkgm/Zkgm.sol
+++ b/evm/contracts/apps/ucs/03-zkgm/Zkgm.sol
@@ -52,6 +52,7 @@ contract UCS03Zkgm is
     using ZkgmLib for *;
     using LibString for *;
     using LibBytes for *;
+    using SafeERC20 for *;
 
     IIBCModulePacket public ibcHandler;
     mapping(bytes32 => IBCPacket) public inFlightPacket;
@@ -210,8 +211,8 @@ contract UCS03Zkgm is
             increaseOutstanding(
                 channelId, path, address(baseToken), order.baseAmount
             );
-            SafeERC20.safeTransferFrom(
-                baseToken, msg.sender, address(this), order.baseAmount
+            baseToken.safeTransferFrom(
+                msg.sender, address(this), order.baseAmount
             );
         }
     }
@@ -324,7 +325,9 @@ contract UCS03Zkgm is
             }
             FungibleAssetOrder calldata order =
                 ZkgmLib.decodeFungibleAssetOrder(instruction.operand);
-            return executeFungibleAssetOrder(ibcPacket, relayer, path, order);
+            return executeFungibleAssetOrder(
+                caller, ibcPacket, relayer, relayerMsg, path, order
+            );
         } else if (instruction.opcode == ZkgmLib.OP_BATCH) {
             if (instruction.version > ZkgmLib.INSTR_VERSION_0) {
                 revert ZkgmLib.ErrUnsupportedVersion();
@@ -551,15 +554,13 @@ contract UCS03Zkgm is
                 channelId,
                 ZkgmLib.reverseChannelPath(path),
                 quoteToken,
-                baseAmount
+                quoteAmount + fee
             );
             if (quoteAmount > 0) {
-                SafeERC20.safeTransfer(
-                    IERC20(quoteToken), receiver, quoteAmount
-                );
+                IERC20(quoteToken).safeTransfer(receiver, quoteAmount);
             }
             if (fee > 0) {
-                SafeERC20.safeTransfer(IERC20(quoteToken), relayer, fee);
+                IERC20(quoteToken).safeTransfer(relayer, fee);
             }
         }
         return ZkgmLib.encodeFungibleAssetOrderAck(
@@ -570,6 +571,35 @@ contract UCS03Zkgm is
         );
     }
 
+    function internalMarketMakerFill(
+        address caller,
+        bytes calldata relayerMsg,
+        address quoteToken,
+        address receiver,
+        uint256 quoteAmount
+    ) internal returns (bytes memory) {
+        if (quoteAmount != 0) {
+            // We want the top level handler in onRecvPacket to know we need to
+            // revert for another MM to get a chance to fill. If we revert now
+            // the entire packet would be considered to be "failed" and refunded
+            // at origin, which we want to avoid.
+            if (!IERC20(quoteToken).transferFrom(caller, receiver, quoteAmount))
+            {
+                return ZkgmLib.ACK_ERR_ONLYMAKER;
+            }
+        }
+        return ZkgmLib.encodeFungibleAssetOrderAck(
+            FungibleAssetOrderAck({
+                fillType: ZkgmLib.FILL_TYPE_MARKETMAKER,
+                // The relayer has to provide it's maker address using the
+                // relayerMsg. This address is specific to the counterparty
+                // chain and is where the protocol will pay back the base amount
+                // on acknowledgement.
+                marketMaker: relayerMsg
+            })
+        );
+    }
+
     function internalDeployWrappedToken(
         uint32 channelId,
         uint256 path,
@@ -598,8 +628,10 @@ contract UCS03Zkgm is
     }
 
     function executeFungibleAssetOrder(
+        address caller,
         IBCPacket calldata ibcPacket,
         address relayer,
+        bytes calldata relayerMsg,
         uint256 path,
         FungibleAssetOrder calldata order
     ) internal returns (bytes memory) {
@@ -644,9 +676,9 @@ contract UCS03Zkgm is
                 false
             );
         } else {
-            // TODO: allow for MM filling after having added the caller to the
-            // interface (from which we extract funds)
-            return ZkgmLib.ACK_ERR_ONLYMAKER;
+            return internalMarketMakerFill(
+                caller, relayerMsg, quoteToken, receiver, order.quoteAmount
+            );
         }
     }
 
@@ -865,9 +897,7 @@ contract UCS03Zkgm is
                         baseToken,
                         orderBaseAmount
                     );
-                    SafeERC20.safeTransfer(
-                        IERC20(baseToken), marketMaker, orderBaseAmount
-                    );
+                    IERC20(baseToken).safeTransfer(marketMaker, orderBaseAmount);
                 }
             } else {
                 revert ZkgmLib.ErrInvalidFillType();
@@ -1043,7 +1073,7 @@ contract UCS03Zkgm is
             decreaseOutstanding(
                 sourceChannelId, path, baseToken, orderBaseAmount
             );
-            SafeERC20.safeTransfer(IERC20(baseToken), sender, orderBaseAmount);
+            IERC20(baseToken).safeTransfer(sender, orderBaseAmount);
         }
     }
 
diff --git a/evm/tests/src/04-channel/IBCChannel.t.sol b/evm/tests/src/04-channel/IBCChannel.t.sol
index 6ec755d491..e55ffe35e3 100644
--- a/evm/tests/src/04-channel/IBCChannel.t.sol
+++ b/evm/tests/src/04-channel/IBCChannel.t.sol
@@ -40,8 +40,7 @@ contract IBCChannelTests is Test {
             counterpartyClientId: 0xDEADC0DE,
             clientId: clientId,
             proofInit: hex"",
-            proofHeight: 0,
-            relayer: address(this)
+            proofHeight: 0
         });
         lightClient.pushValidMembership();
         connectionId = handler.connectionOpenTry(msgTry_);
@@ -49,8 +48,7 @@ contract IBCChannelTests is Test {
             .MsgConnectionOpenConfirm({
             connectionId: connectionId,
             proofAck: hex"",
-            proofHeight: 0,
-            relayer: address(this)
+            proofHeight: 0
         });
         lightClient.pushValidMembership();
         handler.connectionOpenConfirm(msgConfirm_);
diff --git a/evm/tests/src/04-channel/IBCPacket.t.sol b/evm/tests/src/04-channel/IBCPacket.t.sol
index 3b974a90cf..94dacbc0f0 100644
--- a/evm/tests/src/04-channel/IBCPacket.t.sol
+++ b/evm/tests/src/04-channel/IBCPacket.t.sol
@@ -45,8 +45,7 @@ contract IBCPacketTests is Test {
             counterpartyClientId: 0xDEADC0DE,
             clientId: clientId,
             proofInit: hex"",
-            proofHeight: 0,
-            relayer: address(this)
+            proofHeight: 0
         });
         lightClient.pushValidMembership();
         connectionId = handler.connectionOpenTry(msgTry_);
@@ -54,8 +53,7 @@ contract IBCPacketTests is Test {
             .MsgConnectionOpenConfirm({
             connectionId: connectionId,
             proofAck: hex"",
-            proofHeight: 0,
-            relayer: address(this)
+            proofHeight: 0
         });
         lightClient.pushValidMembership();
         handler.connectionOpenConfirm(msgConfirm_);

From 025c70acc1cc9e2e63d3df091bcb810a1025a7bd Mon Sep 17 00:00:00 2001
From: Hussein Ait Lahcen <hussein.aitlahcen@gmail.com>
Date: Thu, 20 Mar 2025 13:03:20 +0100
Subject: [PATCH 2/2] fix(ibc-union): unused relayer in connection handshake
 callbacks

---
 cosmwasm/ibc-union/core/msg/src/msg.rs        |  4 --
 cosmwasm/ibc-union/core/src/contract.rs       | 67 ++++++-------------
 cosmwasm/ibc-union/core/src/tests.rs          |  3 -
 .../core/src/tests/connection/ibc.rs          |  7 --
 evm/contracts/core/25-handler/IBCMsgs.sol     |  4 --
 lib/ibc-solidity/src/lib.rs                   |  4 --
 .../transaction/cosmos-sdk/src/main.rs        |  4 --
 .../plugins/transaction/ethereum/src/main.rs  |  4 --
 8 files changed, 22 insertions(+), 75 deletions(-)

diff --git a/cosmwasm/ibc-union/core/msg/src/msg.rs b/cosmwasm/ibc-union/core/msg/src/msg.rs
index 51c5aa4b02..eb6b18f56d 100644
--- a/cosmwasm/ibc-union/core/msg/src/msg.rs
+++ b/cosmwasm/ibc-union/core/msg/src/msg.rs
@@ -71,7 +71,6 @@ pub struct MsgUpdateClient {
 pub struct MsgConnectionOpenInit {
     pub client_id: ClientId,
     pub counterparty_client_id: ClientId,
-    pub relayer: String,
 }
 
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -82,7 +81,6 @@ pub struct MsgConnectionOpenTry {
     pub client_id: ClientId,
     pub proof_init: Bytes,
     pub proof_height: u64,
-    pub relayer: String,
 }
 
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -92,7 +90,6 @@ pub struct MsgConnectionOpenAck {
     pub counterparty_connection_id: ConnectionId,
     pub proof_try: Bytes,
     pub proof_height: u64,
-    pub relayer: String,
 }
 
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -101,7 +98,6 @@ pub struct MsgConnectionOpenConfirm {
     pub connection_id: ConnectionId,
     pub proof_ack: Bytes,
     pub proof_height: u64,
-    pub relayer: String,
 }
 
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
diff --git a/cosmwasm/ibc-union/core/src/contract.rs b/cosmwasm/ibc-union/core/src/contract.rs
index 19f0dc3812..04532ae468 100644
--- a/cosmwasm/ibc-union/core/src/contract.rs
+++ b/cosmwasm/ibc-union/core/src/contract.rs
@@ -191,62 +191,43 @@ pub fn execute(
         ExecuteMsg::ConnectionOpenInit(MsgConnectionOpenInit {
             client_id,
             counterparty_client_id,
-            relayer,
-        }) => {
-            let relayer = deps.api.addr_validate(&relayer)?;
-            connection_open_init(deps.branch(), client_id, counterparty_client_id, relayer)
-        }
+        }) => connection_open_init(deps.branch(), client_id, counterparty_client_id),
         ExecuteMsg::ConnectionOpenTry(MsgConnectionOpenTry {
             counterparty_client_id,
             counterparty_connection_id,
             client_id,
             proof_init,
             proof_height,
-            relayer,
-        }) => {
-            let relayer = deps.api.addr_validate(&relayer)?;
-            connection_open_try(
-                deps.branch(),
-                counterparty_client_id,
-                counterparty_connection_id,
-                client_id,
-                proof_init.to_vec(),
-                proof_height,
-                relayer,
-            )
-        }
+        }) => connection_open_try(
+            deps.branch(),
+            counterparty_client_id,
+            counterparty_connection_id,
+            client_id,
+            proof_init.to_vec(),
+            proof_height,
+        ),
         ExecuteMsg::ConnectionOpenAck(MsgConnectionOpenAck {
             connection_id,
             counterparty_connection_id,
             proof_try,
             proof_height,
-            relayer,
-        }) => {
-            let relayer = deps.api.addr_validate(&relayer)?;
-            connection_open_ack(
-                deps.branch(),
-                connection_id,
-                counterparty_connection_id,
-                proof_try.to_vec(),
-                proof_height,
-                relayer,
-            )
-        }
+        }) => connection_open_ack(
+            deps.branch(),
+            connection_id,
+            counterparty_connection_id,
+            proof_try.to_vec(),
+            proof_height,
+        ),
         ExecuteMsg::ConnectionOpenConfirm(MsgConnectionOpenConfirm {
             connection_id,
             proof_ack,
             proof_height,
-            relayer,
-        }) => {
-            let relayer = deps.api.addr_validate(&relayer)?;
-            connection_open_confirm(
-                deps.branch(),
-                connection_id,
-                proof_ack.to_vec(),
-                proof_height,
-                relayer,
-            )
-        }
+        }) => connection_open_confirm(
+            deps.branch(),
+            connection_id,
+            proof_ack.to_vec(),
+            proof_height,
+        ),
         ExecuteMsg::ChannelOpenInit(MsgChannelOpenInit {
             port_id,
             counterparty_port_id,
@@ -916,7 +897,6 @@ fn connection_open_init(
     mut deps: DepsMut,
     client_id: ClientId,
     counterparty_client_id: ClientId,
-    _relayer: Addr,
 ) -> ContractResult {
     let connection_id = next_connection_id(deps.branch())?;
     let connection = Connection {
@@ -945,7 +925,6 @@ fn connection_open_try(
     client_id: ClientId,
     proof_init: Vec<u8>,
     proof_height: u64,
-    _relayer: Addr,
 ) -> ContractResult {
     let connection_id = next_connection_id(deps.branch())?;
     let connection = Connection {
@@ -1001,7 +980,6 @@ fn connection_open_ack(
     counterparty_connection_id: ConnectionId,
     proof_try: Vec<u8>,
     proof_height: u64,
-    _relayer: Addr,
 ) -> ContractResult {
     let mut connection = deps.storage.read::<Connections>(&connection_id)?;
     if connection.state != ConnectionState::Init {
@@ -1059,7 +1037,6 @@ fn connection_open_confirm(
     connection_id: ConnectionId,
     proof_ack: Vec<u8>,
     proof_height: u64,
-    _relayer: Addr,
 ) -> ContractResult {
     let mut connection = deps.storage.read::<Connections>(&connection_id)?;
     if connection.state != ConnectionState::TryOpen {
diff --git a/cosmwasm/ibc-union/core/src/tests.rs b/cosmwasm/ibc-union/core/src/tests.rs
index b67a254fd5..3f4cf34775 100644
--- a/cosmwasm/ibc-union/core/src/tests.rs
+++ b/cosmwasm/ibc-union/core/src/tests.rs
@@ -74,7 +74,6 @@ fn connection_open_init(deps: DepsMut) -> Result<Response, ContractError> {
     let msg = MsgConnectionOpenInit {
         client_id: ClientId!(1),
         counterparty_client_id: ClientId!(2),
-        relayer: mock_addr(RELAYER).into_string(),
     };
     execute(
         deps,
@@ -91,7 +90,6 @@ fn connection_open_try(deps: DepsMut) -> Result<Response, ContractError> {
         client_id: ClientId!(1),
         proof_init: vec![1, 2, 3].into(),
         proof_height: 1,
-        relayer: mock_addr(RELAYER).into_string(),
     };
 
     execute(
@@ -107,7 +105,6 @@ fn connection_open_confirm(deps: DepsMut) -> Result<Response, ContractError> {
         connection_id: ConnectionId!(1),
         proof_ack: vec![1, 2, 3].into(),
         proof_height: 1,
-        relayer: mock_addr(RELAYER).into_string(),
     };
 
     execute(
diff --git a/cosmwasm/ibc-union/core/src/tests/connection/ibc.rs b/cosmwasm/ibc-union/core/src/tests/connection/ibc.rs
index dd86593c8c..2ccf3a8dd1 100644
--- a/cosmwasm/ibc-union/core/src/tests/connection/ibc.rs
+++ b/cosmwasm/ibc-union/core/src/tests/connection/ibc.rs
@@ -33,7 +33,6 @@ fn connection_open_init_ok() {
     let msg = MsgConnectionOpenInit {
         client_id: ClientId!(1),
         counterparty_client_id: ClientId!(2),
-        relayer: mock_addr(RELAYER).into_string(),
     };
     assert!(execute(
         deps.as_mut(),
@@ -99,7 +98,6 @@ fn connection_open_try_ok() {
         client_id: ClientId!(1),
         proof_init: vec![1, 2, 3].into(),
         proof_height: 1,
-        relayer: mock_addr(RELAYER).into_string(),
     };
 
     assert!(execute(
@@ -135,7 +133,6 @@ fn connection_open_try_client_not_found() {
         client_id: ClientId!(1),
         proof_init: vec![1, 2, 3].into(),
         proof_height: 1,
-        relayer: mock_addr(RELAYER).into_string(),
     };
 
     assert_eq!(
@@ -181,7 +178,6 @@ fn connection_open_try_commitment_saved() {
         client_id: ClientId!(1),
         proof_init: vec![1, 2, 3].into(),
         proof_height: 1,
-        relayer: mock_addr(RELAYER).into_string(),
     };
 
     execute(
@@ -228,7 +224,6 @@ fn connection_open_ack_ok() {
         counterparty_connection_id: ConnectionId!(1),
         proof_try: vec![1, 2, 3].into(),
         proof_height: 1,
-        relayer: mock_addr(RELAYER).into_string(),
     };
 
     assert!(execute(
@@ -270,7 +265,6 @@ fn connection_open_ack_commitment_saved() {
         counterparty_connection_id: ConnectionId!(1),
         proof_try: vec![1, 2, 3].into(),
         proof_height: 1,
-        relayer: mock_addr(RELAYER).into_string(),
     };
 
     execute(
@@ -316,7 +310,6 @@ fn connection_open_confirm_ok() {
         connection_id: ConnectionId!(1),
         proof_ack: vec![1, 2, 3].into(),
         proof_height: 1,
-        relayer: mock_addr(RELAYER).into_string(),
     };
 
     assert!(execute(
diff --git a/evm/contracts/core/25-handler/IBCMsgs.sol b/evm/contracts/core/25-handler/IBCMsgs.sol
index e2522cbe72..c67158bcf1 100644
--- a/evm/contracts/core/25-handler/IBCMsgs.sol
+++ b/evm/contracts/core/25-handler/IBCMsgs.sol
@@ -22,7 +22,6 @@ library IBCMsgs {
     struct MsgConnectionOpenInit {
         uint32 clientId;
         uint32 counterpartyClientId;
-        address relayer;
     }
 
     struct MsgConnectionOpenTry {
@@ -31,7 +30,6 @@ library IBCMsgs {
         uint32 clientId;
         bytes proofInit;
         uint64 proofHeight;
-        address relayer;
     }
 
     struct MsgConnectionOpenAck {
@@ -39,14 +37,12 @@ library IBCMsgs {
         uint32 counterpartyConnectionId;
         bytes proofTry;
         uint64 proofHeight;
-        address relayer;
     }
 
     struct MsgConnectionOpenConfirm {
         uint32 connectionId;
         bytes proofAck;
         uint64 proofHeight;
-        address relayer;
     }
 
     struct MsgChannelOpenInit {
diff --git a/lib/ibc-solidity/src/lib.rs b/lib/ibc-solidity/src/lib.rs
index be0157d5ae..464f4e62d7 100644
--- a/lib/ibc-solidity/src/lib.rs
+++ b/lib/ibc-solidity/src/lib.rs
@@ -425,7 +425,6 @@ maybe_sol_attr! {
         struct MsgConnectionOpenInit {
             uint32 client_id;
             uint32 counterparty_client_id;
-            address relayer;
         }
 
         struct MsgConnectionOpenTry {
@@ -434,7 +433,6 @@ maybe_sol_attr! {
             uint32 client_id;
             bytes proof_init;
             uint64 proof_height;
-            address relayer;
         }
 
         struct MsgConnectionOpenAck {
@@ -442,14 +440,12 @@ maybe_sol_attr! {
             uint32 counterparty_connection_id;
             bytes proof_try;
             uint64 proof_height;
-            address relayer;
         }
 
         struct MsgConnectionOpenConfirm {
             uint32 connection_id;
             bytes proof_ack;
             uint64 proof_height;
-            address relayer;
         }
 
         struct MsgChannelOpenInit {
diff --git a/voyager/plugins/transaction/cosmos-sdk/src/main.rs b/voyager/plugins/transaction/cosmos-sdk/src/main.rs
index 46c89760fd..01777c9568 100644
--- a/voyager/plugins/transaction/cosmos-sdk/src/main.rs
+++ b/voyager/plugins/transaction/cosmos-sdk/src/main.rs
@@ -715,7 +715,6 @@ fn process_msgs(
                                     client_id: msg_connection_open_init.client_id,
                                     counterparty_client_id: msg_connection_open_init
                                         .counterparty_client_id,
-                                    relayer: signer.to_string(),
                                 },
                             ),
                         )
@@ -737,7 +736,6 @@ fn process_msgs(
                                     client_id: msg_connection_open_try.client_id,
                                     proof_init: msg_connection_open_try.proof_init,
                                     proof_height: msg_connection_open_try.proof_height,
-                                    relayer: signer.to_string(),
                                 },
                             ),
                         )
@@ -757,7 +755,6 @@ fn process_msgs(
                                         .counterparty_connection_id,
                                     proof_try: msg_connection_open_ack.proof_try,
                                     proof_height: msg_connection_open_ack.proof_height,
-                                    relayer: signer.to_string(),
                                 },
                             ),
                         )
@@ -775,7 +772,6 @@ fn process_msgs(
                                     connection_id: msg_connection_open_confirm.connection_id,
                                     proof_ack: msg_connection_open_confirm.proof_ack,
                                     proof_height: msg_connection_open_confirm.proof_height,
-                                    relayer: signer.to_string(),
                                 },
                             ),
                         )
diff --git a/voyager/plugins/transaction/ethereum/src/main.rs b/voyager/plugins/transaction/ethereum/src/main.rs
index 00826dc817..020ad447a7 100644
--- a/voyager/plugins/transaction/ethereum/src/main.rs
+++ b/voyager/plugins/transaction/ethereum/src/main.rs
@@ -552,7 +552,6 @@ fn process_msgs<'a>(
                         .connectionOpenInit(ibc_solidity::MsgConnectionOpenInit {
                             client_id: data.client_id.raw(),
                             counterparty_client_id: data.counterparty_client_id.raw(),
-                            relayer: relayer.into(),
                         })
                         .clear_decoder(),
                 ),
@@ -565,7 +564,6 @@ fn process_msgs<'a>(
                             client_id: data.client_id.raw(),
                             proof_init: data.proof_init.into(),
                             proof_height: data.proof_height,
-                            relayer: relayer.into(),
                         })
                         .clear_decoder(),
                 ),
@@ -577,7 +575,6 @@ fn process_msgs<'a>(
                             counterparty_connection_id: data.counterparty_connection_id.raw(),
                             proof_height: data.proof_height,
                             proof_try: data.proof_try.into(),
-                            relayer: relayer.into(),
                         })
                         .clear_decoder(),
                 ),
@@ -588,7 +585,6 @@ fn process_msgs<'a>(
                             connection_id: data.connection_id.raw(),
                             proof_ack: data.proof_ack.into(),
                             proof_height: data.proof_height,
-                            relayer: relayer.into(),
                         })
                         .clear_decoder(),
                 ),