diff --git a/modules/core/04-channel/v2/keeper/packet.go b/modules/core/04-channel/v2/keeper/packet.go
index a0254d9ca64..f760f3fe5b5 100644
--- a/modules/core/04-channel/v2/keeper/packet.go
+++ b/modules/core/04-channel/v2/keeper/packet.go
@@ -12,7 +12,6 @@ import (
 
 	clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types"
 	"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
-	commitmenttypes "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types"
 	hostv2 "github.com/cosmos/ibc-go/v9/modules/core/24-host/v2"
 	"github.com/cosmos/ibc-go/v9/modules/core/exported"
 )
@@ -121,8 +120,7 @@ func (k *Keeper) recvPacket(
 	}
 
 	path := hostv2.PacketCommitmentKey(packet.SourceClient, packet.Sequence)
-	merklePrefix := commitmenttypes.NewMerklePath(counterparty.MerklePrefix...)
-	merklePath := types.BuildMerklePath(merklePrefix, path)
+	merklePath := types.BuildMerklePath(counterparty.MerklePrefix, path)
 
 	commitment := types.CommitPacket(packet)
 
@@ -219,8 +217,7 @@ func (k *Keeper) acknowledgePacket(ctx context.Context, packet types.Packet, ack
 	}
 
 	path := hostv2.PacketAcknowledgementKey(packet.DestinationClient, packet.Sequence)
-	merklePrefix := commitmenttypes.NewMerklePath(counterparty.MerklePrefix...)
-	merklePath := types.BuildMerklePath(merklePrefix, path)
+	merklePath := types.BuildMerklePath(counterparty.MerklePrefix, path)
 
 	if err := k.ClientKeeper.VerifyMembership(
 		ctx,
@@ -298,8 +295,7 @@ func (k *Keeper) timeoutPacket(
 
 	// verify packet receipt absence
 	path := hostv2.PacketReceiptKey(packet.DestinationClient, packet.Sequence)
-	merklePrefix := commitmenttypes.NewMerklePath(counterparty.MerklePrefix...)
-	merklePath := types.BuildMerklePath(merklePrefix, path)
+	merklePath := types.BuildMerklePath(counterparty.MerklePrefix, path)
 
 	if err := k.ClientKeeper.VerifyNonMembership(
 		ctx,
diff --git a/modules/core/04-channel/v2/types/merkle.go b/modules/core/04-channel/v2/types/merkle.go
index 59be153c960..30d6cbfc000 100644
--- a/modules/core/04-channel/v2/types/merkle.go
+++ b/modules/core/04-channel/v2/types/merkle.go
@@ -1,31 +1,20 @@
 package types
 
 import (
-	commitmenttypes "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types"
 	commitmenttypesv2 "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types/v2"
 )
 
 // BuildMerklePath takes the merkle path prefix and an ICS24 path
 // and builds a new path by appending the ICS24 path to the last element of the merkle path prefix.
-func BuildMerklePath(prefix commitmenttypesv2.MerklePath, path []byte) commitmenttypesv2.MerklePath {
-	if prefix.Empty() {
-		return commitmenttypes.NewMerklePath(path)
+func BuildMerklePath(prefix [][]byte, path []byte) commitmenttypesv2.MerklePath {
+	prefixLength := len(prefix)
+	if prefixLength == 0 {
+		panic("cannot build merkle path with empty prefix")
 	}
 
-	// avoid mutating the provided prefix
-	prefixKeys := make([][]byte, len(prefix.KeyPath))
-	copy(prefixKeys, prefix.KeyPath)
-
-	lastElement := prefixKeys[len(prefixKeys)-1]
+	// copy prefix to avoid modifying the original slice
+	fullPath := append([][]byte(nil), prefix...)
 	// append path to last element
-	newLastElement := cloneAppend(lastElement, path)
-	prefixKeys[len(prefixKeys)-1] = newLastElement
-	return commitmenttypes.NewMerklePath(prefixKeys...)
-}
-
-func cloneAppend(bz []byte, tail []byte) []byte {
-	res := make([]byte, len(bz)+len(tail))
-	copy(res, bz)
-	copy(res[len(bz):], tail)
-	return res
+	fullPath[prefixLength-1] = append(fullPath[prefixLength-1], path...)
+	return commitmenttypesv2.NewMerklePath(fullPath...)
 }
diff --git a/modules/core/04-channel/v2/types/merkle_test.go b/modules/core/04-channel/v2/types/merkle_test.go
index df239bc17d7..0eac1e68428 100644
--- a/modules/core/04-channel/v2/types/merkle_test.go
+++ b/modules/core/04-channel/v2/types/merkle_test.go
@@ -2,7 +2,6 @@ package types_test
 
 import (
 	"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
-	commitmenttypes "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types"
 	commitmenttypesv2 "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types/v2"
 	host "github.com/cosmos/ibc-go/v9/modules/core/24-host"
 	ibctesting "github.com/cosmos/ibc-go/v9/testing"
@@ -12,14 +11,16 @@ func (s *TypesTestSuite) TestBuildMerklePath() {
 	path := ibctesting.NewPath(s.chainA, s.chainB)
 	path.SetupV2()
 
-	prefixPath := commitmenttypes.NewMerklePath([]byte("ibc"), []byte(""))
+	prefixPath := [][]byte{[]byte("ibc"), []byte("")}
 	packetCommitmentKey := host.PacketCommitmentKey(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, 1)
+	emptyPrefixPanicMsg := "cannot build merkle path with empty prefix"
 
 	testCases := []struct {
-		name    string
-		prefix  commitmenttypesv2.MerklePath
-		path    []byte
-		expPath commitmenttypesv2.MerklePath
+		name      string
+		prefix    [][]byte
+		path      []byte
+		expPath   commitmenttypesv2.MerklePath
+		expPanics *string
 	}{
 		{
 			name:    "standard ibc path",
@@ -29,21 +30,21 @@ func (s *TypesTestSuite) TestBuildMerklePath() {
 		},
 		{
 			name:    "non-empty last element prefix path",
-			prefix:  commitmenttypes.NewMerklePath([]byte("ibc"), []byte("abc")),
+			prefix:  [][]byte{[]byte("ibc"), []byte("abc")},
 			path:    packetCommitmentKey,
 			expPath: commitmenttypesv2.NewMerklePath([]byte("ibc"), append([]byte("abc"), packetCommitmentKey...)),
 		},
 		{
 			name:    "many elements in prefix path",
-			prefix:  commitmenttypes.NewMerklePath([]byte("ibc"), []byte("a"), []byte("b"), []byte("c"), []byte("d")),
+			prefix:  [][]byte{[]byte("ibc"), []byte("a"), []byte("b"), []byte("c"), []byte("d")},
 			path:    packetCommitmentKey,
 			expPath: commitmenttypesv2.NewMerklePath([]byte("ibc"), []byte("a"), []byte("b"), []byte("c"), append([]byte("d"), packetCommitmentKey...)),
 		},
 		{
-			name:    "empty prefix",
-			prefix:  commitmenttypesv2.MerklePath{},
-			path:    packetCommitmentKey,
-			expPath: commitmenttypesv2.NewMerklePath(packetCommitmentKey),
+			name:      "empty prefix",
+			prefix:    [][]byte{},
+			path:      packetCommitmentKey,
+			expPanics: &emptyPrefixPanicMsg,
 		},
 		{
 			name:    "empty path",
@@ -57,8 +58,14 @@ func (s *TypesTestSuite) TestBuildMerklePath() {
 		tc := tc
 
 		s.Run(tc.name, func() {
-			merklePath := types.BuildMerklePath(tc.prefix, tc.path)
-			s.Require().Equal(tc.expPath, merklePath)
+			if tc.expPanics == nil {
+				merklePath := types.BuildMerklePath(tc.prefix, tc.path)
+				s.Require().Equal(tc.expPath, merklePath)
+			} else {
+				s.Require().PanicsWithValue(*tc.expPanics, func() {
+					_ = types.BuildMerklePath(tc.prefix, tc.path)
+				})
+			}
 		})
 	}
 }