diff --git a/modules/apps/27-interchain-accounts/keeper/account.go b/modules/apps/27-interchain-accounts/keeper/account.go
index 714b2258125..167e3d9e26f 100644
--- a/modules/apps/27-interchain-accounts/keeper/account.go
+++ b/modules/apps/27-interchain-accounts/keeper/account.go
@@ -4,51 +4,40 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+	"github.com/tendermint/tendermint/crypto/tmhash"
+
+	"github.com/cosmos/ibc-go/modules/apps/27-interchain-accounts/types"
 	channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types"
 	host "github.com/cosmos/ibc-go/modules/core/24-host"
-	"github.com/cosmos/ibc-go/modules/apps/27-interchain-accounts/types"
-	"github.com/tendermint/tendermint/crypto/tmhash"
 )
 
-// The first step in registering an interchain account
-// Binds a new port & calls OnChanOpenInit
+// InitInterchainAccount is the entry point to registering an interchain account.
+// It generates a new port identifier using the owner address, connection identifier,
+// and counterparty connection identifier. It will bind to the port identifier and
+// call 04-channel 'ChanOpenInit'. An error is returned if the port identifier is
+// already in use. Gaining access to interchain accounts whose channels have closed
+// cannot be done with this function. A regular MsgChanOpenInit must be used.
 func (k Keeper) InitInterchainAccount(ctx sdk.Context, connectionId, owner string) error {
 	portId := k.GeneratePortId(owner, connectionId)
 
-	// Check if the port is already bound
-	isBound := k.IsBound(ctx, portId)
-	if isBound == true {
+	// check if the port is already bound
+	if k.IsBound(ctx, portId) {
 		return sdkerrors.Wrap(types.ErrPortAlreadyBound, portId)
 	}
 
 	portCap := k.portKeeper.BindPort(ctx, portId)
 	err := k.ClaimCapability(ctx, portCap, host.PortPath(portId))
 	if err != nil {
+		return sdkerrors.Wrap(err, "unable to bind to newly generated portID")
+	}
+
+	msg := channeltypes.NewMsgChannelOpenInit(portId, types.Version, channeltypes.ORDERED, []string{connectionId}, types.PortID, types.ModuleName)
+	handler := k.msgRouter.Handler(msg)
+	if _, err := handler(ctx, msg); err != nil {
 		return err
 	}
 
-	counterParty := channeltypes.Counterparty{PortId: "ibcaccount", ChannelId: ""}
-	order := channeltypes.Order(2)
-	channelId, cap, err := k.channelKeeper.ChanOpenInit(ctx, order, []string{connectionId}, portId, portCap, counterParty, types.Version)
-
-	ctx.EventManager().EmitEvents(sdk.Events{
-		sdk.NewEvent(
-			channeltypes.EventTypeChannelOpenInit,
-			sdk.NewAttribute(channeltypes.AttributeKeyPortID, portId),
-			sdk.NewAttribute(channeltypes.AttributeKeyChannelID, channelId),
-			sdk.NewAttribute(channeltypes.AttributeCounterpartyPortID, "ibcaccount"),
-			sdk.NewAttribute(channeltypes.AttributeCounterpartyChannelID, ""),
-			sdk.NewAttribute(channeltypes.AttributeKeyConnectionID, connectionId),
-		),
-		sdk.NewEvent(
-			sdk.EventTypeMessage,
-			sdk.NewAttribute(sdk.AttributeKeyModule, channeltypes.AttributeValueCategory),
-		),
-	})
-
-	_ = k.OnChanOpenInit(ctx, channeltypes.Order(2), []string{connectionId}, portId, channelId, cap, counterParty, types.Version)
-
-	return err
+	return nil
 }
 
 // Register interchain account if it has not already been created
diff --git a/modules/apps/27-interchain-accounts/keeper/account_test.go b/modules/apps/27-interchain-accounts/keeper/account_test.go
new file mode 100644
index 00000000000..064a9bc6413
--- /dev/null
+++ b/modules/apps/27-interchain-accounts/keeper/account_test.go
@@ -0,0 +1,62 @@
+package keeper_test
+
+import (
+	ibctesting "github.com/cosmos/ibc-go/testing"
+)
+
+func (suite *KeeperTestSuite) TestInitInterchainAccount() {
+	var (
+		owner string
+		path  *ibctesting.Path
+		err   error
+	)
+
+	testCases := []struct {
+		name     string
+		malleate func()
+		expPass  bool
+	}{
+
+		{
+			"success", func() {}, true,
+		},
+		/*
+		   // TODO: https://github.com/cosmos/ibc-go/issues/288
+		   {
+		   			"port is already bound", func() {
+		   				// mock init interchain account
+		   				portID := suite.chainA.GetSimApp().ICAKeeper.GeneratePortId(owner, path.EndpointA.ConnectionID)
+		   				suite.chainA.GetSimApp().IBCKeeper.PortKeeper.BindPort(suite.chainA.GetContext(), portID)
+		   			}, false,
+		   		},
+		*/
+		{
+			"MsgChanOpenInit fails - channel is already active", func() {
+				portID := suite.chainA.GetSimApp().ICAKeeper.GeneratePortId(owner, path.EndpointA.ConnectionID)
+				suite.chainA.GetSimApp().ICAKeeper.SetActiveChannel(suite.chainA.GetContext(), portID, path.EndpointA.ChannelID)
+			}, false,
+		},
+	}
+
+	for _, tc := range testCases {
+		tc := tc
+
+		suite.Run(tc.name, func() {
+			suite.SetupTest() // reset
+			owner = "owner"   // must be explicitly changed
+			path = NewICAPath(suite.chainA, suite.chainB)
+			suite.coordinator.SetupConnections(path)
+
+			tc.malleate() // explicitly change fields in channel and testChannel
+
+			err = suite.chainA.GetSimApp().ICAKeeper.InitInterchainAccount(suite.chainA.GetContext(), path.EndpointA.ConnectionID, owner)
+
+			if tc.expPass {
+				suite.Require().NoError(err)
+			} else {
+				suite.Require().Error(err)
+			}
+
+		})
+	}
+}
diff --git a/modules/apps/27-interchain-accounts/keeper/handshake.go b/modules/apps/27-interchain-accounts/keeper/handshake.go
index 614ed22882d..6a8bafe57cb 100644
--- a/modules/apps/27-interchain-accounts/keeper/handshake.go
+++ b/modules/apps/27-interchain-accounts/keeper/handshake.go
@@ -4,10 +4,20 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
+
+	"github.com/cosmos/ibc-go/modules/apps/27-interchain-accounts/types"
 	channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types"
+	porttypes "github.com/cosmos/ibc-go/modules/core/05-port/types"
 	host "github.com/cosmos/ibc-go/modules/core/24-host"
 )
 
+// OnChanOpenInit performs basic validation of channel initialization.
+// The channel order must be ORDERED, the counterparty port identifier
+// must be the host chain representation as defined in the types package,
+// the channel version must be equal to the version in the types package,
+// there must not be an active channel for the specfied port identifier,
+// and the interchain accounts module must be able to claim the channel
+// capability.
 func (k Keeper) OnChanOpenInit(
 	ctx sdk.Context,
 	order channeltypes.Order,
@@ -18,11 +28,19 @@ func (k Keeper) OnChanOpenInit(
 	counterparty channeltypes.Counterparty,
 	version string,
 ) error {
-	//TODO:
-	// check version string
 	if order != channeltypes.ORDERED {
 		return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelOrdering, "invalid channel ordering: %s, expected %s", order.String(), channeltypes.ORDERED.String())
 	}
+	if counterparty.PortId != types.PortID {
+		return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "counterparty port-id must be '%s', (%s != %s)", types.PortID, counterparty.PortId, types.PortID)
+	}
+	if version != types.Version {
+		return sdkerrors.Wrapf(channeltypes.ErrInvalidChannelVersion, "channel version must be '%s' (%s != %s)", types.Version, version, types.Version)
+	}
+	channelID, found := k.GetActiveChannel(ctx, portID)
+	if found {
+		return sdkerrors.Wrapf(porttypes.ErrInvalidPort, "existing active channel (%s) for portID (%s)", channelID, portID)
+	}
 
 	// Claim channel capability passed back by IBC module
 	if err := k.ClaimCapability(ctx, chanCap, host.ChannelCapabilityPath(portID, channelID)); err != nil {
diff --git a/modules/apps/27-interchain-accounts/keeper/handshake_test.go b/modules/apps/27-interchain-accounts/keeper/handshake_test.go
index 9429264902a..7ac2b6ac0d0 100644
--- a/modules/apps/27-interchain-accounts/keeper/handshake_test.go
+++ b/modules/apps/27-interchain-accounts/keeper/handshake_test.go
@@ -1 +1,98 @@
 package keeper_test
+
+import (
+	capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
+
+	"github.com/cosmos/ibc-go/modules/apps/27-interchain-accounts/types"
+	channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types"
+	host "github.com/cosmos/ibc-go/modules/core/24-host"
+	ibctesting "github.com/cosmos/ibc-go/testing"
+)
+
+func (suite *KeeperTestSuite) TestOnChanOpenInit() {
+	var (
+		channel *channeltypes.Channel
+		path    *ibctesting.Path
+		chanCap *capabilitytypes.Capability
+		err     error
+	)
+
+	testCases := []struct {
+		name     string
+		malleate func()
+		expPass  bool
+	}{
+
+		{
+			"success", func() {}, true,
+		},
+		{
+			"invalid order - UNORDERED", func() {
+				channel.Ordering = channeltypes.UNORDERED
+			}, false,
+		},
+		{
+			"invalid counterparty port ID", func() {
+				channel.Counterparty.PortId = ibctesting.MockPort
+			}, false,
+		},
+		{
+			"invalid version", func() {
+				channel.Version = "version"
+			}, false,
+		},
+		{
+			"channel is already active", func() {
+				suite.chainA.GetSimApp().ICAKeeper.SetActiveChannel(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID)
+			}, false,
+		},
+		{
+			"capability already claimed", func() {
+				err := suite.chainA.GetSimApp().ScopedICAKeeper.ClaimCapability(suite.chainA.GetContext(), chanCap, host.ChannelCapabilityPath(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID))
+				suite.Require().NoError(err)
+			}, false,
+		},
+	}
+
+	for _, tc := range testCases {
+		tc := tc
+
+		suite.Run(tc.name, func() {
+			suite.SetupTest() // reset
+			path = NewICAPath(suite.chainA, suite.chainB)
+			suite.coordinator.SetupConnections(path)
+
+			// mock init interchain account
+			portID := suite.chainA.GetSimApp().ICAKeeper.GeneratePortId("owner", path.EndpointA.ConnectionID)
+			portCap := suite.chainA.GetSimApp().IBCKeeper.PortKeeper.BindPort(suite.chainA.GetContext(), portID)
+			suite.chainA.GetSimApp().ICAKeeper.ClaimCapability(suite.chainA.GetContext(), portCap, host.PortPath(portID))
+			path.EndpointA.ChannelConfig.PortID = portID
+
+			// default values
+			counterparty := channeltypes.NewCounterparty(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID)
+			channel = &channeltypes.Channel{
+				State:          channeltypes.INIT,
+				Ordering:       channeltypes.ORDERED,
+				Counterparty:   counterparty,
+				ConnectionHops: []string{path.EndpointA.ConnectionID},
+				Version:        types.Version,
+			}
+
+			chanCap, err = suite.chainA.App.GetScopedIBCKeeper().NewCapability(suite.chainA.GetContext(), host.ChannelCapabilityPath(portID, path.EndpointA.ChannelID))
+			suite.Require().NoError(err)
+
+			tc.malleate() // explicitly change fields in channel and testChannel
+
+			err = suite.chainA.GetSimApp().ICAKeeper.OnChanOpenInit(suite.chainA.GetContext(), channel.Ordering, channel.GetConnectionHops(),
+				path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, chanCap, channel.Counterparty, channel.GetVersion(),
+			)
+
+			if tc.expPass {
+				suite.Require().NoError(err)
+			} else {
+				suite.Require().Error(err)
+			}
+
+		})
+	}
+}
diff --git a/modules/apps/27-interchain-accounts/keeper/keeper.go b/modules/apps/27-interchain-accounts/keeper/keeper.go
index 1dd0cd99110..e8459b7cf85 100644
--- a/modules/apps/27-interchain-accounts/keeper/keeper.go
+++ b/modules/apps/27-interchain-accounts/keeper/keeper.go
@@ -8,7 +8,6 @@ import (
 	"github.com/cosmos/cosmos-sdk/codec"
 	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
 	sdk "github.com/cosmos/cosmos-sdk/types"
-	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 	capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper"
 	capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
 	host "github.com/cosmos/ibc-go/modules/core/24-host"
@@ -145,13 +144,20 @@ func (k Keeper) SetActiveChannel(ctx sdk.Context, portId, channelId string) erro
 	return nil
 }
 
-func (k Keeper) GetActiveChannel(ctx sdk.Context, portId string) (string, error) {
+func (k Keeper) GetActiveChannel(ctx sdk.Context, portId string) (string, bool) {
 	store := ctx.KVStore(k.storeKey)
 	key := types.KeyActiveChannel(portId)
 	if !store.Has(key) {
-		return "", sdkerrors.Wrap(types.ErrActiveChannelNotFound, portId)
+		return "", false
 	}
 
 	activeChannel := string(store.Get(key))
-	return activeChannel, nil
+	return activeChannel, true
+}
+
+// IsActiveChannel returns true if there exists an active channel for
+// the provided portID and false otherwise.
+func (k Keeper) IsActiveChannel(ctx sdk.Context, portId string) bool {
+	_, found := k.GetActiveChannel(ctx, portId)
+	return found
 }
diff --git a/modules/apps/27-interchain-accounts/keeper/relay.go b/modules/apps/27-interchain-accounts/keeper/relay.go
index d434fcfac9e..daefcc8754a 100644
--- a/modules/apps/27-interchain-accounts/keeper/relay.go
+++ b/modules/apps/27-interchain-accounts/keeper/relay.go
@@ -5,19 +5,19 @@ import (
 
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	"github.com/cosmos/ibc-go/modules/apps/27-interchain-accounts/types"
 	clienttypes "github.com/cosmos/ibc-go/modules/core/02-client/types"
 	channeltypes "github.com/cosmos/ibc-go/modules/core/04-channel/types"
 	host "github.com/cosmos/ibc-go/modules/core/24-host"
-	"github.com/cosmos/ibc-go/modules/apps/27-interchain-accounts/types"
 	"github.com/tendermint/tendermint/crypto/tmhash"
 )
 
 func (k Keeper) TrySendTx(ctx sdk.Context, accountOwner sdk.AccAddress, connectionId string, data interface{}) ([]byte, error) {
 	portId := k.GeneratePortId(accountOwner.String(), connectionId)
 	// Check for the active channel
-	activeChannelId, err := k.GetActiveChannel(ctx, portId)
-	if err != nil {
-		return nil, err
+	activeChannelId, found := k.GetActiveChannel(ctx, portId)
+	if !found {
+		return nil, types.ErrActiveChannelNotFound
 	}
 
 	sourceChannelEnd, found := k.channelKeeper.GetChannel(ctx, portId, activeChannelId)
diff --git a/modules/core/04-channel/types/errors.go b/modules/core/04-channel/types/errors.go
index 30293eddf07..6a6d608fb29 100644
--- a/modules/core/04-channel/types/errors.go
+++ b/modules/core/04-channel/types/errors.go
@@ -30,4 +30,6 @@ var (
 
 	// ORDERED channel error
 	ErrPacketSequenceOutOfOrder = sdkerrors.Register(SubModuleName, 21, "packet sequence is out of order")
+
+	ErrInvalidChannelVersion = sdkerrors.Register(SubModuleName, 24, "invalid channel version")
 )