From 55bfc8224961558cfd820317808e83eadf84969e Mon Sep 17 00:00:00 2001
From: Volodymyr Kubiv <volodymyr.kubiv@euristiq.com>
Date: Thu, 14 Jul 2022 14:07:18 +0300
Subject: [PATCH] feat: factor walletlite.Command form vcwallet.Command.

Signed-off-by: Volodymyr Kubiv <volodymyr.kubiv@euristiq.com>
---
 .../pkg/wrappers/command/vcwallet.go          |   13 +-
 .../pkg/wrappers/rest/endpoints.go            |    7 +-
 .../pkg/wrappers/rest/vcwallet.go             |    7 +-
 .../command/didcommwallet/command.go          |  442 +++++++
 .../command/didcommwallet/command_test.go     |  991 +++++++++++++++
 .../command/didcommwallet/models.go           |  178 +++
 pkg/controller/command/vcwallet/command.go    |  386 +-----
 .../command/vcwallet/command_test.go          | 1122 +++--------------
 pkg/controller/command/vcwallet/models.go     |  163 ---
 pkg/controller/controller_test.go             |    4 +-
 pkg/controller/rest/vcwallet/models.go        |   21 +-
 pkg/controller/rest/vcwallet/operation.go     |    6 +-
 .../rest/vcwallet/operation_test.go           |   33 +-
 13 files changed, 1823 insertions(+), 1550 deletions(-)
 create mode 100644 pkg/controller/command/didcommwallet/command.go
 create mode 100644 pkg/controller/command/didcommwallet/command_test.go
 create mode 100644 pkg/controller/command/didcommwallet/models.go

diff --git a/cmd/aries-agent-mobile/pkg/wrappers/command/vcwallet.go b/cmd/aries-agent-mobile/pkg/wrappers/command/vcwallet.go
index 8e9f9d245..eabf24b71 100644
--- a/cmd/aries-agent-mobile/pkg/wrappers/command/vcwallet.go
+++ b/cmd/aries-agent-mobile/pkg/wrappers/command/vcwallet.go
@@ -11,6 +11,7 @@ import (
 
 	"github.com/hyperledger/aries-framework-go/cmd/aries-agent-mobile/pkg/wrappers/models"
 	"github.com/hyperledger/aries-framework-go/pkg/controller/command"
+	cmddidcommwallet "github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet"
 	cmdvcwallet "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet"
 )
 
@@ -261,13 +262,13 @@ func (v *VCWallet) CreateKeyPair(request *models.RequestEnvelope) *models.Respon
 
 // Connect accepts out-of-band invitations and performs DID exchange.
 func (v *VCWallet) Connect(request *models.RequestEnvelope) *models.ResponseEnvelope {
-	args := cmdvcwallet.ConnectRequest{}
+	args := cmddidcommwallet.ConnectRequest{}
 
 	if err := json.Unmarshal(request.Payload, &args); err != nil {
 		return &models.ResponseEnvelope{Error: &models.CommandError{Message: err.Error()}}
 	}
 
-	response, cmdErr := exec(v.handlers[cmdvcwallet.ConnectMethod], args)
+	response, cmdErr := exec(v.handlers[cmddidcommwallet.ConnectMethod], args)
 	if cmdErr != nil {
 		return &models.ResponseEnvelope{Error: cmdErr}
 	}
@@ -278,13 +279,13 @@ func (v *VCWallet) Connect(request *models.RequestEnvelope) *models.ResponseEnve
 // ProposePresentation accepts out-of-band invitation and sends message proposing presentation
 // from wallet to relying party.
 func (v *VCWallet) ProposePresentation(request *models.RequestEnvelope) *models.ResponseEnvelope {
-	args := cmdvcwallet.ProposePresentationRequest{}
+	args := cmddidcommwallet.ProposePresentationRequest{}
 
 	if err := json.Unmarshal(request.Payload, &args); err != nil {
 		return &models.ResponseEnvelope{Error: &models.CommandError{Message: err.Error()}}
 	}
 
-	response, cmdErr := exec(v.handlers[cmdvcwallet.ProposePresentationMethod], args)
+	response, cmdErr := exec(v.handlers[cmddidcommwallet.ProposePresentationMethod], args)
 	if cmdErr != nil {
 		return &models.ResponseEnvelope{Error: cmdErr}
 	}
@@ -294,13 +295,13 @@ func (v *VCWallet) ProposePresentation(request *models.RequestEnvelope) *models.
 
 // PresentProof sends present proof message from wallet to relying party.
 func (v *VCWallet) PresentProof(request *models.RequestEnvelope) *models.ResponseEnvelope {
-	args := cmdvcwallet.PresentProofRequest{}
+	args := cmddidcommwallet.PresentProofRequest{}
 
 	if err := json.Unmarshal(request.Payload, &args); err != nil {
 		return &models.ResponseEnvelope{Error: &models.CommandError{Message: err.Error()}}
 	}
 
-	response, cmdErr := exec(v.handlers[cmdvcwallet.PresentProofMethod], args)
+	response, cmdErr := exec(v.handlers[cmddidcommwallet.PresentProofMethod], args)
 	if cmdErr != nil {
 		return &models.ResponseEnvelope{Error: cmdErr}
 	}
diff --git a/cmd/aries-agent-mobile/pkg/wrappers/rest/endpoints.go b/cmd/aries-agent-mobile/pkg/wrappers/rest/endpoints.go
index 2ca880fbe..5bc5de8c1 100644
--- a/cmd/aries-agent-mobile/pkg/wrappers/rest/endpoints.go
+++ b/cmd/aries-agent-mobile/pkg/wrappers/rest/endpoints.go
@@ -9,6 +9,7 @@ package rest
 import (
 	"net/http"
 
+	cmddidcommwallet "github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet"
 	cmddidexch "github.com/hyperledger/aries-framework-go/pkg/controller/command/didexchange"
 	cmdintroduce "github.com/hyperledger/aries-framework-go/pkg/controller/command/introduce"
 	cmdisscred "github.com/hyperledger/aries-framework-go/pkg/controller/command/issuecredential"
@@ -518,13 +519,13 @@ func getVCWalletEndpoints() map[string]*endpoint {
 		cmdvcwallet.CreateKeyPairMethod: {
 			Path: opvcwallet.CreateKeyPairPath, Method: http.MethodPost,
 		},
-		cmdvcwallet.ConnectMethod: {
+		cmddidcommwallet.ConnectMethod: {
 			Path: opvcwallet.ConnectPath, Method: http.MethodPost,
 		},
-		cmdvcwallet.ProposePresentationMethod: {
+		cmddidcommwallet.ProposePresentationMethod: {
 			Path: opvcwallet.ProposePresentationPath, Method: http.MethodPost,
 		},
-		cmdvcwallet.PresentProofMethod: {
+		cmddidcommwallet.PresentProofMethod: {
 			Path: opvcwallet.PresentProofPath, Method: http.MethodPost,
 		},
 	}
diff --git a/cmd/aries-agent-mobile/pkg/wrappers/rest/vcwallet.go b/cmd/aries-agent-mobile/pkg/wrappers/rest/vcwallet.go
index 8816ea0a2..64b467df8 100644
--- a/cmd/aries-agent-mobile/pkg/wrappers/rest/vcwallet.go
+++ b/cmd/aries-agent-mobile/pkg/wrappers/rest/vcwallet.go
@@ -8,6 +8,7 @@ package rest
 
 import (
 	"github.com/hyperledger/aries-framework-go/cmd/aries-agent-mobile/pkg/wrappers/models"
+	cmddidcommwallet "github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet"
 	cmdvcwallet "github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet"
 )
 
@@ -97,18 +98,18 @@ func (wallet *VCWallet) CreateKeyPair(request *models.RequestEnvelope) *models.R
 
 // Connect accepts out-of-band invitations and performs DID exchange.
 func (wallet *VCWallet) Connect(request *models.RequestEnvelope) *models.ResponseEnvelope {
-	return wallet.createRespEnvelope(request, cmdvcwallet.ConnectMethod)
+	return wallet.createRespEnvelope(request, cmddidcommwallet.ConnectMethod)
 }
 
 // ProposePresentation accepts out-of-band invitation and sends message proposing presentation
 // from wallet to relying party.
 func (wallet *VCWallet) ProposePresentation(request *models.RequestEnvelope) *models.ResponseEnvelope {
-	return wallet.createRespEnvelope(request, cmdvcwallet.ProposePresentationMethod)
+	return wallet.createRespEnvelope(request, cmddidcommwallet.ProposePresentationMethod)
 }
 
 // PresentProof sends message present proof message from wallet to relying party.
 func (wallet *VCWallet) PresentProof(request *models.RequestEnvelope) *models.ResponseEnvelope {
-	return wallet.createRespEnvelope(request, cmdvcwallet.PresentProofMethod)
+	return wallet.createRespEnvelope(request, cmddidcommwallet.PresentProofMethod)
 }
 
 func (wallet *VCWallet) createRespEnvelope(request *models.RequestEnvelope, endpoint string) *models.ResponseEnvelope {
diff --git a/pkg/controller/command/didcommwallet/command.go b/pkg/controller/command/didcommwallet/command.go
new file mode 100644
index 000000000..8317fac81
--- /dev/null
+++ b/pkg/controller/command/didcommwallet/command.go
@@ -0,0 +1,442 @@
+/*
+Copyright SecureKey Technologies Inc. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package didcommwallet
+
+import (
+	"encoding/json"
+	"io"
+	"time"
+
+	"github.com/piprate/json-gold/ld"
+
+	"github.com/hyperledger/aries-framework-go/pkg/common/log"
+	"github.com/hyperledger/aries-framework-go/pkg/controller/command"
+	"github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet"
+	"github.com/hyperledger/aries-framework-go/pkg/controller/internal/cmdutil"
+	"github.com/hyperledger/aries-framework-go/pkg/crypto"
+	"github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
+	"github.com/hyperledger/aries-framework-go/pkg/internal/logutil"
+	"github.com/hyperledger/aries-framework-go/pkg/kms"
+	"github.com/hyperledger/aries-framework-go/pkg/wallet"
+	"github.com/hyperledger/aries-framework-go/spi/storage"
+)
+
+var logger = log.New("aries-framework/command/didcommwallet")
+
+// Error codes.
+const (
+	// InvalidRequestErrorCode is typically a code for invalid requests.
+	InvalidRequestErrorCode = command.Code(iota + command.VCWallet)
+
+	// DIDConnectErrorCode for errors while performing DID connect in wallet.
+	DIDConnectErrorCode
+
+	// ProposePresentationErrorCode for errors while proposing presentation.
+	ProposePresentationErrorCode
+
+	// PresentProofErrorCode for errors while presenting proof from wallet.
+	PresentProofErrorCode
+
+	// ProposeCredentialErrorCode for errors while proposing credential from wallet.
+	ProposeCredentialErrorCode
+
+	// RequestCredentialErrorCode for errors while request credential from wallet for issue credential protocol.
+	RequestCredentialErrorCode
+
+	// ResolveCredentialManifestErrorCode for errors while resolving credential manifest from wallet.
+	ResolveCredentialManifestErrorCode
+)
+
+// All command operations.
+const (
+	// command methods.
+	ConnectMethod                   = "Connect"
+	ProposePresentationMethod       = "ProposePresentation"
+	PresentProofMethod              = "PresentProof"
+	ProposeCredentialMethod         = "ProposeCredential"
+	RequestCredentialMethod         = "RequestCredential"
+	ResolveCredentialManifestMethod = "ResolveCredentialManifest"
+)
+
+// miscellaneous constants for the vc wallet command controller.
+const (
+	// log constants.
+	logSuccess         = "success"
+	logUserIDKey       = "userID"
+	connectionIDString = "connectionID"
+	invitationIDString = "invitationID"
+	LabelString        = "label"
+
+	emptyRawLength = 4
+)
+
+// AuthCapabilityProvider is for providing Authorization Capabilities (ZCAP-LD) feature for
+// wallet's EDV and WebKMS components.
+type AuthCapabilityProvider = vcwallet.AuthCapabilityProvider
+
+// HTTPHeaderSigner is for http header signing, typically used for zcapld functionality.
+type HTTPHeaderSigner = vcwallet.HTTPHeaderSigner
+
+// Config contains properties to customize verifiable credential wallet controller.
+// All properties of this config are optional, but they can be used to customize wallet's webkms and edv client's.
+type Config = vcwallet.Config
+
+// provider contains dependencies for the verifiable credential wallet command controller
+// and is typically created by using aries.Context().
+type provider interface {
+	StorageProvider() storage.Provider
+	VDRegistry() vdr.Registry
+	Crypto() crypto.Crypto
+	JSONLDDocumentLoader() ld.DocumentLoader
+	MediaTypeProfiles() []string
+	didCommProvider // to be used only if wallet needs to be participated in DIDComm.
+}
+
+// didCommProvider to be used only if wallet needs to be participated in DIDComm operation.
+// TODO: using wallet KMS instead of provider KMS.
+// TODO: reconcile Protocol storage with wallet store.
+type didCommProvider interface {
+	KMS() kms.KeyManager
+	ServiceEndpoint() string
+	ProtocolStateStorageProvider() storage.Provider
+	Service(id string) (interface{}, error)
+	KeyType() kms.KeyType
+	KeyAgreementType() kms.KeyType
+}
+
+// Command extends vcwallet.Command to add didComm functionality.
+type Command struct {
+	*vcwallet.Command
+	ctx provider
+}
+
+// New returns new verifiable credential wallet controller command instance.
+func New(p provider, config *Config) *Command {
+	cmd := &Command{
+		Command: vcwallet.New(p, config),
+		ctx:     p,
+	}
+
+	return cmd
+}
+
+// GetHandlers returns list of all commands supported by this controller command.
+func (o *Command) GetHandlers() []command.Handler {
+	return append(o.Command.GetHandlers(),
+		cmdutil.NewCommandHandler(vcwallet.CommandName, ConnectMethod, o.Connect),
+		cmdutil.NewCommandHandler(vcwallet.CommandName, ProposePresentationMethod, o.ProposePresentation),
+		cmdutil.NewCommandHandler(vcwallet.CommandName, PresentProofMethod, o.PresentProof),
+		cmdutil.NewCommandHandler(vcwallet.CommandName, ProposeCredentialMethod, o.ProposeCredential),
+		cmdutil.NewCommandHandler(vcwallet.CommandName, RequestCredentialMethod, o.RequestCredential),
+		cmdutil.NewCommandHandler(vcwallet.CommandName, ResolveCredentialManifestMethod, o.ResolveCredentialManifest),
+	)
+}
+
+// Connect accepts out-of-band invitations and performs DID exchange.
+func (o *Command) Connect(rw io.Writer, req io.Reader) command.Error {
+	request := &ConnectRequest{}
+
+	err := json.NewDecoder(req).Decode(&request)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ConnectMethod, err.Error())
+
+		return command.NewValidationError(InvalidRequestErrorCode, err)
+	}
+
+	vcWallet, err := wallet.New(request.UserID, o.ctx)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ConnectMethod, err.Error())
+
+		return command.NewExecuteError(DIDConnectErrorCode, err)
+	}
+
+	didComm, err := wallet.NewDidComm(vcWallet, o.ctx)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ConnectMethod, err.Error())
+
+		return command.NewExecuteError(DIDConnectErrorCode, err)
+	}
+
+	connectionID, err := didComm.Connect(request.Auth, request.Invitation,
+		wallet.WithConnectTimeout(request.Timeout), wallet.WithReuseDID(request.ReuseConnection),
+		wallet.WithReuseAnyConnection(request.ReuseAnyConnection), wallet.WithMyLabel(request.MyLabel),
+		wallet.WithRouterConnections(request.RouterConnections...))
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ConnectMethod, err.Error())
+
+		return command.NewExecuteError(DIDConnectErrorCode, err)
+	}
+
+	command.WriteNillableResponse(rw, &ConnectResponse{ConnectionID: connectionID}, logger)
+
+	logutil.LogDebug(logger, vcwallet.CommandName, ConnectMethod, logSuccess,
+		logutil.CreateKeyValueString(logUserIDKey, request.UserID),
+		logutil.CreateKeyValueString(invitationIDString, request.Invitation.ID),
+		logutil.CreateKeyValueString(LabelString, request.MyLabel),
+		logutil.CreateKeyValueString(connectionIDString, connectionID))
+
+	return nil
+}
+
+// ProposePresentation accepts out-of-band invitation and sends message proposing presentation
+// from wallet to relying party.
+// https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposepresentation
+//
+// Currently Supporting
+// [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2)
+func (o *Command) ProposePresentation(rw io.Writer, req io.Reader) command.Error {
+	request := &ProposePresentationRequest{}
+
+	err := json.NewDecoder(req).Decode(&request)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ProposePresentationMethod, err.Error())
+
+		return command.NewValidationError(InvalidRequestErrorCode, err)
+	}
+
+	vcWallet, err := wallet.New(request.UserID, o.ctx)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ProposePresentationMethod, err.Error())
+
+		return command.NewExecuteError(ProposePresentationErrorCode, err)
+	}
+
+	didComm, err := wallet.NewDidComm(vcWallet, o.ctx)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ProposePresentationMethod, err.Error())
+
+		return command.NewExecuteError(ProposePresentationErrorCode, err)
+	}
+
+	msg, err := didComm.ProposePresentation(request.Auth, request.Invitation,
+		wallet.WithFromDID(request.FromDID), wallet.WithInitiateTimeout(request.Timeout),
+		wallet.WithConnectOptions(wallet.WithConnectTimeout(request.ConnectionOpts.Timeout),
+			wallet.WithReuseDID(request.ConnectionOpts.ReuseConnection),
+			wallet.WithReuseAnyConnection(request.ConnectionOpts.ReuseAnyConnection),
+			wallet.WithMyLabel(request.ConnectionOpts.MyLabel),
+			wallet.WithRouterConnections(request.ConnectionOpts.RouterConnections...)))
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ProposePresentationMethod, err.Error())
+
+		return command.NewExecuteError(ProposePresentationErrorCode, err)
+	}
+
+	command.WriteNillableResponse(rw, &ProposePresentationResponse{PresentationRequest: msg}, logger)
+
+	logutil.LogDebug(logger, vcwallet.CommandName, ProposePresentationMethod, logSuccess,
+		logutil.CreateKeyValueString(logUserIDKey, request.UserID))
+
+	return nil
+}
+
+// PresentProof sends present proof message from wallet to relying party.
+// https://w3c-ccg.github.io/universal-wallet-interop-spec/#presentproof
+//
+// Currently Supporting
+// [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2)
+//
+func (o *Command) PresentProof(rw io.Writer, req io.Reader) command.Error {
+	request := &PresentProofRequest{}
+
+	err := json.NewDecoder(req).Decode(&request)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, PresentProofMethod, err.Error())
+
+		return command.NewValidationError(InvalidRequestErrorCode, err)
+	}
+
+	vcWallet, err := wallet.New(request.UserID, o.ctx)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, PresentProofMethod, err.Error())
+
+		return command.NewExecuteError(PresentProofErrorCode, err)
+	}
+
+	didComm, err := wallet.NewDidComm(vcWallet, o.ctx)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, PresentProofMethod, err.Error())
+
+		return command.NewExecuteError(PresentProofErrorCode, err)
+	}
+
+	status, err := didComm.PresentProof(request.Auth, request.ThreadID,
+		prepareConcludeInteractionOpts(request.WaitForDone, request.Timeout, request.Presentation)...)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, PresentProofMethod, err.Error())
+
+		return command.NewExecuteError(PresentProofErrorCode, err)
+	}
+
+	command.WriteNillableResponse(rw, status, logger)
+
+	logutil.LogDebug(logger, vcwallet.CommandName, PresentProofMethod, logSuccess,
+		logutil.CreateKeyValueString(logUserIDKey, request.UserID))
+
+	return nil
+}
+
+// ProposeCredential sends propose credential message from wallet to issuer.
+// https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposecredential
+//
+// Currently Supporting : 0453-issueCredentialV2
+// https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md
+//
+func (o *Command) ProposeCredential(rw io.Writer, req io.Reader) command.Error {
+	request := &ProposeCredentialRequest{}
+
+	err := json.NewDecoder(req).Decode(&request)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ProposeCredentialMethod, err.Error())
+
+		return command.NewValidationError(InvalidRequestErrorCode, err)
+	}
+
+	vcWallet, err := wallet.New(request.UserID, o.ctx)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ProposeCredentialMethod, err.Error())
+
+		return command.NewExecuteError(ProposeCredentialErrorCode, err)
+	}
+
+	didComm, err := wallet.NewDidComm(vcWallet, o.ctx)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ProposeCredentialMethod, err.Error())
+
+		return command.NewExecuteError(ProposeCredentialErrorCode, err)
+	}
+
+	msg, err := didComm.ProposeCredential(request.Auth, request.Invitation,
+		wallet.WithFromDID(request.FromDID), wallet.WithInitiateTimeout(request.Timeout),
+		wallet.WithConnectOptions(wallet.WithConnectTimeout(request.ConnectionOpts.Timeout),
+			wallet.WithReuseDID(request.ConnectionOpts.ReuseConnection),
+			wallet.WithReuseAnyConnection(request.ConnectionOpts.ReuseAnyConnection),
+			wallet.WithMyLabel(request.ConnectionOpts.MyLabel),
+			wallet.WithRouterConnections(request.ConnectionOpts.RouterConnections...)))
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ProposeCredentialMethod, err.Error())
+
+		return command.NewExecuteError(ProposeCredentialErrorCode, err)
+	}
+
+	command.WriteNillableResponse(rw, &ProposeCredentialResponse{OfferCredential: msg}, logger)
+
+	logutil.LogDebug(logger, vcwallet.CommandName, ProposeCredentialMethod, logSuccess,
+		logutil.CreateKeyValueString(logUserIDKey, request.UserID))
+
+	return nil
+}
+
+// RequestCredential sends request credential message from wallet to issuer and
+// optionally waits for credential fulfillment.
+// https://w3c-ccg.github.io/universal-wallet-interop-spec/#requestcredential
+//
+// Currently Supporting : 0453-issueCredentialV2
+// https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md
+//
+func (o *Command) RequestCredential(rw io.Writer, req io.Reader) command.Error {
+	request := &RequestCredentialRequest{}
+
+	err := json.NewDecoder(req).Decode(&request)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, RequestCredentialMethod, err.Error())
+
+		return command.NewValidationError(InvalidRequestErrorCode, err)
+	}
+
+	vcWallet, err := wallet.New(request.UserID, o.ctx)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, RequestCredentialMethod, err.Error())
+
+		return command.NewExecuteError(RequestCredentialErrorCode, err)
+	}
+
+	didComm, err := wallet.NewDidComm(vcWallet, o.ctx)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, RequestCredentialMethod, err.Error())
+
+		return command.NewExecuteError(RequestCredentialErrorCode, err)
+	}
+
+	status, err := didComm.RequestCredential(request.Auth, request.ThreadID,
+		prepareConcludeInteractionOpts(request.WaitForDone, request.Timeout, request.Presentation)...)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, RequestCredentialMethod, err.Error())
+
+		return command.NewExecuteError(RequestCredentialErrorCode, err)
+	}
+
+	command.WriteNillableResponse(rw, status, logger)
+
+	logutil.LogDebug(logger, vcwallet.CommandName, RequestCredentialMethod, logSuccess,
+		logutil.CreateKeyValueString(logUserIDKey, request.UserID))
+
+	return nil
+}
+
+// ResolveCredentialManifest resolves given credential manifest by credential fulfillment or credential.
+// Supports: https://identity.foundation/credential-manifest/
+//
+// Writes list of resolved descriptors to writer or returns error if operation fails.
+//
+func (o *Command) ResolveCredentialManifest(rw io.Writer, req io.Reader) command.Error {
+	request := &ResolveCredentialManifestRequest{}
+
+	err := json.NewDecoder(req).Decode(&request)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ResolveCredentialManifestMethod, err.Error())
+
+		return command.NewValidationError(InvalidRequestErrorCode, err)
+	}
+
+	vcWallet, err := wallet.New(request.UserID, o.ctx)
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ResolveCredentialManifestMethod, err.Error())
+
+		return command.NewExecuteError(ResolveCredentialManifestErrorCode, err)
+	}
+
+	resolved, err := vcWallet.ResolveCredentialManifest(request.Auth, request.Manifest,
+		prepareResolveManifestOption(request))
+	if err != nil {
+		logutil.LogInfo(logger, vcwallet.CommandName, ResolveCredentialManifestMethod, err.Error())
+
+		return command.NewExecuteError(ResolveCredentialManifestErrorCode, err)
+	}
+
+	command.WriteNillableResponse(rw, &ResolveCredentialManifestResponse{Resolved: resolved}, logger)
+
+	logutil.LogDebug(logger, vcwallet.CommandName, ResolveCredentialManifestMethod, logSuccess,
+		logutil.CreateKeyValueString(logUserIDKey, request.UserID))
+
+	return nil
+}
+
+func prepareConcludeInteractionOpts(waitForDone bool, timeout time.Duration, presentation json.RawMessage) []wallet.ConcludeInteractionOptions { //nolint: lll
+	var options []wallet.ConcludeInteractionOptions
+
+	if waitForDone {
+		options = append(options, wallet.WaitForDone(timeout))
+	}
+
+	return append(options, wallet.FromRawPresentation(presentation))
+}
+
+func prepareResolveManifestOption(rqst *ResolveCredentialManifestRequest) wallet.ResolveManifestOption {
+	if len(rqst.Fulfillment) > emptyRawLength {
+		return wallet.ResolveRawFulfillment(rqst.Fulfillment)
+	}
+
+	if len(rqst.Credential) > emptyRawLength {
+		return wallet.ResolveRawCredential(rqst.DescriptorID, rqst.Credential)
+	}
+
+	if rqst.CredentialID != "" {
+		return wallet.ResolveCredentialID(rqst.DescriptorID, rqst.CredentialID)
+	}
+
+	return nil
+}
diff --git a/pkg/controller/command/didcommwallet/command_test.go b/pkg/controller/command/didcommwallet/command_test.go
new file mode 100644
index 000000000..d774b26af
--- /dev/null
+++ b/pkg/controller/command/didcommwallet/command_test.go
@@ -0,0 +1,991 @@
+/*
+Copyright SecureKey Technologies Inc. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package didcommwallet
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/google/uuid"
+	"github.com/stretchr/testify/require"
+
+	"github.com/hyperledger/aries-framework-go/internal/testdata"
+	outofbandClient "github.com/hyperledger/aries-framework-go/pkg/client/outofband"
+	"github.com/hyperledger/aries-framework-go/pkg/controller/command"
+	"github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet"
+	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model"
+	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
+	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/didexchange"
+	issuecredentialsvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/issuecredential"
+	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator"
+	outofbandSvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/outofband"
+	oobv2 "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/outofbandv2"
+	presentproofSvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/presentproof"
+	"github.com/hyperledger/aries-framework-go/pkg/doc/did"
+	vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
+	mockoutofbandv2 "github.com/hyperledger/aries-framework-go/pkg/internal/gomocks/client/outofbandv2"
+	"github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil"
+	mockdidexchange "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/didexchange"
+	mockissuecredential "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/issuecredential"
+	mockmediator "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/mediator"
+	mockoutofband "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/outofband"
+	mockpresentproof "github.com/hyperledger/aries-framework-go/pkg/mock/didcomm/protocol/presentproof"
+	mockprovider "github.com/hyperledger/aries-framework-go/pkg/mock/provider"
+	mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage"
+	mockvdr "github.com/hyperledger/aries-framework-go/pkg/mock/vdr"
+	"github.com/hyperledger/aries-framework-go/pkg/store/connection"
+	"github.com/hyperledger/aries-framework-go/pkg/vdr/key"
+	"github.com/hyperledger/aries-framework-go/pkg/wallet"
+)
+
+const (
+	sampleUserID         = "sample-user01"
+	samplePassPhrase     = "fakepassphrase"
+	sampleCommandError   = "sample-command-error-01"
+	sampleFakeTkn        = "sample-fake-token-01"
+	webRedirectStatusKey = "status"
+	webRedirectURLKey    = "url"
+	exampleWebRedirect   = "http://example.com/sample"
+)
+
+func TestNew(t *testing.T) {
+	t.Run("successfully create new command instance", func(t *testing.T) {
+		cmd := New(newMockProvider(t), &Config{})
+		require.NotNil(t, cmd)
+
+		require.Len(t, cmd.GetHandlers(), 21)
+	})
+}
+
+func TestCommand_Connect(t *testing.T) {
+	const sampleDIDCommUser = "sample-didcomm-user01"
+
+	mockctx := newMockProvider(t)
+
+	createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{
+		UserID:             sampleDIDCommUser,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{
+		UserID:             sampleDIDCommUser,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	defer lock()
+
+	t.Run("successfully perform DID connect", func(t *testing.T) {
+		sampleConnID := uuid.New().String()
+
+		oobSvc := &mockoutofband.MockOobService{
+			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
+				return sampleConnID, nil
+			},
+		}
+		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
+
+		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
+			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
+				ch <- service.StateMsg{
+					Type:       service.PostState,
+					StateID:    didexchange.StateIDCompleted,
+					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
+				}
+
+				return nil
+			},
+		}
+		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
+
+		cmdDidComm := New(mockctx, &Config{})
+
+		request := &ConnectRequest{
+			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			Invitation: &outofbandClient.Invitation{},
+			ConnectOpts: ConnectOpts{
+				MyLabel: "sample-label",
+			},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmdDidComm.Connect(&b, getReader(t, &request))
+		require.NoError(t, cmdErr)
+
+		var response ConnectResponse
+		require.NoError(t, json.NewDecoder(&b).Decode(&response))
+		require.NotEmpty(t, response)
+		require.Equal(t, sampleConnID, response.ConnectionID)
+	})
+
+	t.Run("did connect failure", func(t *testing.T) {
+		oobSvc := &mockoutofband.MockOobService{
+			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
+				return "", fmt.Errorf(sampleCommandError)
+			},
+		}
+		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
+
+		cmd := New(mockctx, &Config{})
+
+		request := &ConnectRequest{
+			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			Invitation: &outofbandClient.Invitation{},
+			ConnectOpts: ConnectOpts{
+				MyLabel: "sample-label",
+			},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.Connect(&b, getReader(t, &request))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, sampleCommandError)
+		validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, "failed to accept invitation")
+		require.Empty(t, b.Bytes())
+	})
+
+	t.Run("did connect failure - invalid request", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		var b bytes.Buffer
+		cmdErr := cmd.Connect(&b, bytes.NewBufferString("--"))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
+		require.Empty(t, b.Bytes())
+	})
+
+	t.Run("attempt to didconnect with invalid profile", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		request := &ConnectRequest{
+			WalletAuth: vcwallet.WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn},
+			Invitation: &outofbandClient.Invitation{},
+			ConnectOpts: ConnectOpts{
+				MyLabel: "sample-label",
+			},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.Connect(&b, getReader(t, &request))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, "failed to get VC wallet profile")
+		require.Empty(t, b.Bytes())
+	})
+}
+
+func TestCommand_ProposePresentation(t *testing.T) {
+	const sampleDIDCommUser = "sample-didcomm-user02"
+
+	mockctx := newMockProvider(t)
+
+	createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{
+		UserID:             sampleDIDCommUser,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{
+		UserID:             sampleDIDCommUser,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	defer lock()
+
+	const (
+		myDID    = "did:mydid:123"
+		theirDID = "did:theirdid:123"
+	)
+
+	t.Run("successfully send propose presentation", func(t *testing.T) {
+		sampleConnID := uuid.New().String()
+
+		oobSvc := &mockoutofband.MockOobService{
+			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
+				return sampleConnID, nil
+			},
+		}
+		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
+
+		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
+			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
+				ch <- service.StateMsg{
+					Type:       service.PostState,
+					StateID:    didexchange.StateIDCompleted,
+					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
+				}
+
+				return nil
+			},
+		}
+		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
+
+		thID := uuid.New().String()
+		ppSvc := &mockpresentproof.MockPresentProofSvc{
+			ActionsFunc: func() ([]presentproofSvc.Action, error) {
+				return []presentproofSvc.Action{
+					{
+						PIID: thID,
+						Msg: service.NewDIDCommMsgMap(&presentproofSvc.RequestPresentationV2{
+							Comment: "mock msg",
+						}),
+						MyDID:    myDID,
+						TheirDID: theirDID,
+					},
+				}, nil
+			},
+			HandleFunc: func(service.DIDCommMsg) (string, error) {
+				return thID, nil
+			},
+		}
+		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
+
+		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
+		require.NoError(t, err)
+
+		record := &connection.Record{
+			ConnectionID: sampleConnID,
+			MyDID:        myDID,
+			TheirDID:     theirDID,
+		}
+		recordBytes, err := json.Marshal(record)
+		require.NoError(t, err)
+		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
+
+		cmd := New(mockctx, &Config{})
+
+		request := &ProposePresentationRequest{
+			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			Invitation: &wallet.GenericInvitation{},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.ProposePresentation(&b, getReader(t, &request))
+		require.NoError(t, cmdErr)
+
+		var response ProposePresentationResponse
+		require.NoError(t, json.NewDecoder(&b).Decode(&response))
+		require.NotEmpty(t, response)
+		require.NotEmpty(t, response.PresentationRequest)
+	})
+
+	t.Run("failed to send propose presentation", func(t *testing.T) {
+		oobSvc := &mockoutofband.MockOobService{
+			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
+				return "", fmt.Errorf(sampleCommandError)
+			},
+		}
+
+		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
+
+		cmd := New(mockctx, &Config{})
+
+		request := &ConnectRequest{
+			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			Invitation: &outofbandClient.Invitation{},
+			ConnectOpts: ConnectOpts{
+				MyLabel: "sample-label",
+			},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.ProposePresentation(&b, getReader(t, &request))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, sampleCommandError)
+		validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, "failed to accept invitation")
+		require.Empty(t, b.Bytes())
+	})
+
+	t.Run("failed to send propose presentation - invalid request", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		var b bytes.Buffer
+		cmdErr := cmd.ProposePresentation(&b, bytes.NewBufferString("--"))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
+		require.Empty(t, b.Bytes())
+	})
+
+	t.Run("failed to send propose presentation - invalid profile", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		request := &ConnectRequest{
+			WalletAuth: vcwallet.WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn},
+			Invitation: &outofbandClient.Invitation{},
+			ConnectOpts: ConnectOpts{
+				MyLabel: "sample-label",
+			},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.ProposePresentation(&b, getReader(t, &request))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, "failed to get VC wallet profile")
+		require.Empty(t, b.Bytes())
+	})
+}
+
+func TestCommand_PresentProof(t *testing.T) {
+	const sampleDIDCommUser = "sample-didcomm-user03"
+
+	mockctx := newMockProvider(t)
+
+	createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{
+		UserID:             sampleDIDCommUser,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{
+		UserID:             sampleDIDCommUser,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	defer lock()
+
+	t.Run("successfully present proof", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		request := &PresentProofRequest{
+			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			ThreadID:     uuid.New().String(),
+			Presentation: json.RawMessage{},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.PresentProof(&b, getReader(t, &request))
+		require.NoError(t, cmdErr)
+	})
+
+	t.Run("successfully present proof - wait for done", func(t *testing.T) {
+		thID := uuid.New().String()
+		mockPresentProofSvc := &mockpresentproof.MockPresentProofSvc{
+			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
+				ch <- service.StateMsg{
+					Type:    service.PostState,
+					StateID: presentproofSvc.StateNameDone,
+					Properties: &mockdidexchange.MockEventProperties{
+						Properties: map[string]interface{}{
+							webRedirectStatusKey: model.AckStatusOK,
+							webRedirectURLKey:    exampleWebRedirect,
+						},
+					},
+					Msg: &mockMsg{thID: thID},
+				}
+
+				return nil
+			},
+		}
+		mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc
+
+		cmd := New(mockctx, &Config{})
+
+		request := &PresentProofRequest{
+			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			ThreadID:     thID,
+			Presentation: json.RawMessage{},
+			WaitForDone:  true,
+			Timeout:      1 * time.Millisecond,
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.PresentProof(&b, getReader(t, &request))
+		require.NoError(t, cmdErr)
+
+		var response wallet.CredentialInteractionStatus
+		require.NoError(t, json.NewDecoder(&b).Decode(&response))
+		require.NotEmpty(t, response)
+		require.Equal(t, exampleWebRedirect, response.RedirectURL)
+		require.Equal(t, model.AckStatusOK, response.Status)
+	})
+
+	t.Run("failed to present proof", func(t *testing.T) {
+		ppSvc := &mockpresentproof.MockPresentProofSvc{
+			ActionContinueFunc: func(string, ...presentproofSvc.Opt) error {
+				return fmt.Errorf(sampleCommandError)
+			},
+		}
+
+		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
+
+		cmd := New(mockctx, &Config{})
+
+		request := &PresentProofRequest{
+			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			ThreadID:     uuid.New().String(),
+			Presentation: json.RawMessage{},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.PresentProof(&b, getReader(t, &request))
+		validateError(t, cmdErr, command.ExecuteError, PresentProofErrorCode, sampleCommandError)
+		require.Empty(t, b.Bytes())
+	})
+
+	t.Run("failed to present proof - invalid request", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		var b bytes.Buffer
+		cmdErr := cmd.PresentProof(&b, bytes.NewBufferString("--"))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
+		require.Empty(t, b.Bytes())
+	})
+
+	t.Run("failed to present proof - invalid profile", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		request := &PresentProofRequest{
+			WalletAuth:   vcwallet.WalletAuth{UserID: sampleUserID, Auth: token},
+			ThreadID:     uuid.New().String(),
+			Presentation: json.RawMessage{},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.PresentProof(&b, getReader(t, &request))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ExecuteError, PresentProofErrorCode, "failed to get VC wallet profile")
+		require.Empty(t, b.Bytes())
+	})
+}
+
+func TestCommand_ProposeCredential(t *testing.T) {
+	const (
+		sampleDIDCommUser = "sample-didcomm-user02"
+		sampleMsgComment  = "sample mock msg"
+		myDID             = "did:mydid:123"
+		theirDID          = "did:theirdid:123"
+	)
+
+	mockctx := newMockProvider(t)
+
+	createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{
+		UserID:             sampleDIDCommUser,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{
+		UserID:             sampleDIDCommUser,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	defer lock()
+
+	t.Run("successfully send propose credential", func(t *testing.T) {
+		sampleConnID := uuid.New().String()
+
+		oobSvc := &mockoutofband.MockOobService{
+			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
+				return sampleConnID, nil
+			},
+		}
+		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
+
+		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
+			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
+				ch <- service.StateMsg{
+					Type:       service.PostState,
+					StateID:    didexchange.StateIDCompleted,
+					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
+				}
+
+				return nil
+			},
+		}
+		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
+
+		thID := uuid.New().String()
+		icSvc := &mockissuecredential.MockIssueCredentialSvc{
+			ActionsFunc: func() ([]issuecredentialsvc.Action, error) {
+				return []issuecredentialsvc.Action{
+					{
+						PIID: thID,
+						Msg: service.NewDIDCommMsgMap(&issuecredentialsvc.OfferCredentialV2{
+							Comment: sampleMsgComment,
+						}),
+						MyDID:    myDID,
+						TheirDID: theirDID,
+					},
+				}, nil
+			},
+			HandleFunc: func(service.DIDCommMsg) (string, error) {
+				return thID, nil
+			},
+		}
+		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
+
+		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
+		require.NoError(t, err)
+
+		record := &connection.Record{
+			ConnectionID: sampleConnID,
+			MyDID:        myDID,
+			TheirDID:     theirDID,
+		}
+		recordBytes, err := json.Marshal(record)
+		require.NoError(t, err)
+		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
+
+		cmd := New(mockctx, &Config{})
+
+		request := &ProposeCredentialRequest{
+			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			Invitation: &wallet.GenericInvitation{},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.ProposeCredential(&b, getReader(t, &request))
+		require.NoError(t, cmdErr)
+
+		var response ProposeCredentialResponse
+		require.NoError(t, json.NewDecoder(&b).Decode(&response))
+		require.NotEmpty(t, response)
+		require.NotEmpty(t, response.OfferCredential)
+
+		offer := &issuecredentialsvc.OfferCredentialV2{}
+
+		err = response.OfferCredential.Decode(offer)
+		require.NoError(t, err)
+		require.NotEmpty(t, offer)
+		require.Equal(t, sampleMsgComment, offer.Comment)
+	})
+
+	t.Run("failed to send propose credential", func(t *testing.T) {
+		oobSvc := &mockoutofband.MockOobService{
+			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
+				return "", fmt.Errorf(sampleCommandError)
+			},
+		}
+
+		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
+
+		cmd := New(mockctx, &Config{})
+
+		request := &ConnectRequest{
+			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			Invitation: &outofbandClient.Invitation{},
+			ConnectOpts: ConnectOpts{
+				MyLabel: "sample-label",
+			},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.ProposeCredential(&b, getReader(t, &request))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, sampleCommandError)
+		validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, "failed to accept invitation")
+		require.Empty(t, b.Bytes())
+	})
+
+	t.Run("failed to send propose credential - invalid request", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		var b bytes.Buffer
+		cmdErr := cmd.ProposeCredential(&b, bytes.NewBufferString("--"))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
+		require.Empty(t, b.Bytes())
+	})
+
+	t.Run("failed to send propose credential - invalid profile", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		request := &ConnectRequest{
+			WalletAuth: vcwallet.WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn},
+			Invitation: &outofbandClient.Invitation{},
+			ConnectOpts: ConnectOpts{
+				MyLabel: "sample-label",
+			},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.ProposeCredential(&b, getReader(t, &request))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, "failed to get VC wallet profile")
+		require.Empty(t, b.Bytes())
+	})
+}
+
+func TestCommand_RequestCredential(t *testing.T) {
+	const sampleDIDCommUser = "sample-didcomm-user03"
+
+	mockctx := newMockProvider(t)
+
+	createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{
+		UserID:             sampleDIDCommUser,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{
+		UserID:             sampleDIDCommUser,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	defer lock()
+
+	t.Run("successfully request credential", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		request := &RequestCredentialRequest{
+			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			ThreadID:     uuid.New().String(),
+			Presentation: json.RawMessage{},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.RequestCredential(&b, getReader(t, &request))
+		require.NoError(t, cmdErr)
+	})
+
+	t.Run("successfully request credential - wait for done", func(t *testing.T) {
+		thID := uuid.New().String()
+
+		icSvc := &mockissuecredential.MockIssueCredentialSvc{
+			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
+				ch <- service.StateMsg{
+					Type:    service.PostState,
+					StateID: "done",
+					Properties: &mockdidexchange.MockEventProperties{
+						Properties: map[string]interface{}{
+							webRedirectStatusKey: model.AckStatusOK,
+							webRedirectURLKey:    exampleWebRedirect,
+						},
+					},
+					Msg: &mockMsg{thID: thID},
+				}
+
+				return nil
+			},
+		}
+		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
+
+		cmd := New(mockctx, &Config{})
+
+		request := &RequestCredentialRequest{
+			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			ThreadID:     thID,
+			Presentation: json.RawMessage{},
+			WaitForDone:  true,
+			Timeout:      600 * time.Millisecond,
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.RequestCredential(&b, getReader(t, &request))
+		require.NoError(t, cmdErr)
+
+		var response wallet.CredentialInteractionStatus
+		require.NoError(t, json.NewDecoder(&b).Decode(&response))
+		require.NotEmpty(t, response)
+		require.Equal(t, exampleWebRedirect, response.RedirectURL)
+		require.Equal(t, model.AckStatusOK, response.Status)
+	})
+
+	t.Run("failed to request credential", func(t *testing.T) {
+		icSvc := &mockissuecredential.MockIssueCredentialSvc{
+			ActionContinueFunc: func(string, ...issuecredentialsvc.Opt) error {
+				return fmt.Errorf(sampleCommandError)
+			},
+		}
+		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
+
+		cmd := New(mockctx, &Config{})
+
+		request := &RequestCredentialRequest{
+			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
+			ThreadID:     uuid.New().String(),
+			Presentation: json.RawMessage{},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.RequestCredential(&b, getReader(t, &request))
+		validateError(t, cmdErr, command.ExecuteError, RequestCredentialErrorCode, sampleCommandError)
+		require.Empty(t, b.Bytes())
+	})
+
+	t.Run("failed to request credential - invalid request", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		var b bytes.Buffer
+		cmdErr := cmd.RequestCredential(&b, bytes.NewBufferString("--"))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
+		require.Empty(t, b.Bytes())
+	})
+
+	t.Run("failed to request credential - invalid profile", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		request := &RequestCredentialRequest{
+			WalletAuth:   vcwallet.WalletAuth{UserID: sampleUserID, Auth: token},
+			ThreadID:     uuid.New().String(),
+			Presentation: json.RawMessage{},
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.RequestCredential(&b, getReader(t, &request))
+		require.Error(t, cmdErr)
+
+		validateError(t, cmdErr, command.ExecuteError, RequestCredentialErrorCode, "failed to get VC wallet profile")
+		require.Empty(t, b.Bytes())
+	})
+}
+
+func TestCommand_ResolveCredentialManifest(t *testing.T) {
+	const sampleUser1 = "sample-user-r01"
+
+	mockctx := newMockProvider(t)
+	mockctx.VDRegistryValue = getMockDIDKeyVDR()
+
+	createSampleUserProfile(t, mockctx, &vcwallet.CreateOrUpdateProfileRequest{
+		UserID:             sampleUser1,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	token, lock := unlockWallet(t, mockctx, &vcwallet.UnlockWalletRequest{
+		UserID:             sampleUser1,
+		LocalKMSPassphrase: samplePassPhrase,
+	})
+
+	defer lock()
+
+	addContent(t, mockctx, &vcwallet.AddContentRequest{
+		Content:     testdata.SampleUDCVC,
+		ContentType: "credential",
+		WalletAuth:  vcwallet.WalletAuth{UserID: sampleUser1, Auth: token},
+	})
+
+	t.Run("successfully resolve credential fulfillment", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		request := &ResolveCredentialManifestRequest{
+			WalletAuth:  vcwallet.WalletAuth{UserID: sampleUser1, Auth: token},
+			Manifest:    testdata.CredentialManifestMultipleVCs,
+			Fulfillment: testdata.CredentialFulfillmentWithMultipleVCs,
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, request))
+		require.NoError(t, cmdErr)
+
+		var response ResolveCredentialManifestResponse
+		require.NoError(t, json.NewDecoder(&b).Decode(&response))
+		require.NotEmpty(t, response)
+		require.Len(t, response.Resolved, 2)
+	})
+
+	t.Run("successfully resolve credential", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		request := &ResolveCredentialManifestRequest{
+			WalletAuth:   vcwallet.WalletAuth{UserID: sampleUser1, Auth: token},
+			Manifest:     testdata.CredentialManifestMultipleVCs,
+			Credential:   testdata.SampleUDCVC,
+			DescriptorID: "udc_output",
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request))
+		require.NoError(t, cmdErr)
+
+		var response ResolveCredentialManifestResponse
+		require.NoError(t, json.NewDecoder(&b).Decode(&response))
+		require.NotEmpty(t, response)
+		require.Len(t, response.Resolved, 1)
+	})
+
+	t.Run("successfully resolve credential ID", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		request := &ResolveCredentialManifestRequest{
+			WalletAuth:   vcwallet.WalletAuth{UserID: sampleUser1, Auth: token},
+			Manifest:     testdata.CredentialManifestMultipleVCs,
+			CredentialID: "http://example.edu/credentials/1872",
+			DescriptorID: "udc_output",
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request))
+		require.NoError(t, cmdErr)
+
+		var response ResolveCredentialManifestResponse
+		require.NoError(t, json.NewDecoder(&b).Decode(&response))
+		require.NotEmpty(t, response)
+		require.Len(t, response.Resolved, 1)
+	})
+
+	t.Run("failed to resolve - invalid requests", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
+
+		// missing manifest
+		request := &ResolveCredentialManifestRequest{
+			WalletAuth:   vcwallet.WalletAuth{UserID: sampleUser1, Auth: token},
+			Credential:   testdata.SampleUDCVC,
+			DescriptorID: "udc_output",
+		}
+
+		var b bytes.Buffer
+		cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request))
+		validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode,
+			"failed to read credential manifest")
+
+		// missing descriptor ID
+		request = &ResolveCredentialManifestRequest{
+			WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token},
+			Manifest:   testdata.CredentialManifestMultipleVCs,
+			Credential: testdata.SampleUDCVC,
+		}
+
+		b.Reset()
+		cmdErr = cmd.ResolveCredentialManifest(&b, getReader(t, &request))
+		validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode,
+			"unable to find matching descriptor")
+
+		// missing resolve option
+		request = &ResolveCredentialManifestRequest{
+			WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token},
+			Manifest:   testdata.CredentialManifestMultipleVCs,
+		}
+
+		b.Reset()
+		cmdErr = cmd.ResolveCredentialManifest(&b, getReader(t, &request))
+		validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode, "invalid option")
+
+		b.Reset()
+		cmdErr = cmd.ResolveCredentialManifest(&b, bytes.NewBufferString("=="))
+		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
+	})
+}
+
+func createSampleUserProfile(t *testing.T, ctx *mockprovider.Provider, request *vcwallet.CreateOrUpdateProfileRequest) {
+	cmd := vcwallet.New(ctx, &Config{})
+	require.NotNil(t, cmd)
+
+	var l bytes.Buffer
+	cmdErr := cmd.CreateProfile(&l, getReader(t, request))
+	require.NoError(t, cmdErr)
+}
+
+func getReader(t *testing.T, v interface{}) io.Reader {
+	vcReqBytes, err := json.Marshal(v)
+	require.NoError(t, err)
+
+	return bytes.NewBuffer(vcReqBytes)
+}
+
+func getUnlockToken(t *testing.T, b bytes.Buffer) string {
+	var response vcwallet.UnlockWalletResponse
+
+	require.NoError(t, json.NewDecoder(&b).Decode(&response))
+
+	return response.Token
+}
+
+func unlockWallet(t *testing.T, ctx *mockprovider.Provider, request *vcwallet.UnlockWalletRequest) (string, func()) {
+	cmd := vcwallet.New(ctx, nil)
+
+	var b bytes.Buffer
+
+	cmdErr := cmd.Open(&b, getReader(t, &request))
+	require.NoError(t, cmdErr)
+
+	return getUnlockToken(t, b), func() {
+		cmdErr = cmd.Close(&b, getReader(t, &vcwallet.LockWalletRequest{UserID: request.UserID}))
+		if cmdErr != nil {
+			t.Log(t, cmdErr)
+		}
+	}
+}
+
+func addContent(t *testing.T, ctx *mockprovider.Provider, request *vcwallet.AddContentRequest) {
+	cmd := vcwallet.New(ctx, &Config{})
+
+	var b bytes.Buffer
+	defer b.Reset()
+
+	cmdErr := cmd.Add(&b, getReader(t, &request))
+	require.NoError(t, cmdErr)
+}
+
+func validateError(t *testing.T, err command.Error,
+	expectedType command.Type, expectedCode command.Code, contains string) {
+	require.Error(t, err)
+	require.Equal(t, err.Type(), expectedType)
+	require.Equal(t, err.Code(), expectedCode)
+
+	if contains != "" {
+		require.Contains(t, err.Error(), contains)
+	}
+}
+
+func newMockProvider(t *testing.T) *mockprovider.Provider {
+	t.Helper()
+
+	loader, err := ldtestutil.DocumentLoader()
+	require.NoError(t, err)
+
+	serviceMap := map[string]interface{}{
+		presentproofSvc.Name:    &mockpresentproof.MockPresentProofSvc{},
+		outofbandSvc.Name:       &mockoutofband.MockOobService{},
+		didexchange.DIDExchange: &mockdidexchange.MockDIDExchangeSvc{},
+		mediator.Coordination:   &mockmediator.MockMediatorSvc{},
+		issuecredentialsvc.Name: &mockissuecredential.MockIssueCredentialSvc{},
+		oobv2.Name:              &mockoutofbandv2.MockOobService{},
+	}
+
+	return &mockprovider.Provider{
+		StorageProviderValue:              mockstorage.NewMockStoreProvider(),
+		ProtocolStateStorageProviderValue: mockstorage.NewMockStoreProvider(),
+		DocumentLoaderValue:               loader,
+		ServiceMap:                        serviceMap,
+	}
+}
+
+func getMockDIDKeyVDR() *mockvdr.MockVDRegistry {
+	return &mockvdr.MockVDRegistry{
+		ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
+			if strings.HasPrefix(didID, "did:key:") {
+				k := key.New()
+
+				d, e := k.Read(didID)
+				if e != nil {
+					return nil, e
+				}
+
+				return d, nil
+			}
+
+			return nil, fmt.Errorf("did not found")
+		},
+	}
+}
+
+// mockMsg containing custom parent thread ID.
+type mockMsg struct {
+	*service.DIDCommMsgMap
+	thID string
+}
+
+func (m *mockMsg) ParentThreadID() string {
+	return m.thID
+}
+
+func (m *mockMsg) ThreadID() (string, error) {
+	return m.thID, nil
+}
diff --git a/pkg/controller/command/didcommwallet/models.go b/pkg/controller/command/didcommwallet/models.go
new file mode 100644
index 000000000..3e537a71f
--- /dev/null
+++ b/pkg/controller/command/didcommwallet/models.go
@@ -0,0 +1,178 @@
+/*
+Copyright SecureKey Technologies Inc. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
+*/
+
+package didcommwallet
+
+import (
+	"encoding/json"
+	"time"
+
+	"github.com/hyperledger/aries-framework-go/pkg/client/outofband"
+	"github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet"
+	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
+	"github.com/hyperledger/aries-framework-go/pkg/doc/cm"
+	"github.com/hyperledger/aries-framework-go/pkg/wallet"
+)
+
+// ConnectRequest is request model for wallet DID connect operation.
+type ConnectRequest struct {
+	vcwallet.WalletAuth
+
+	// out-of-band invitation to establish connection.
+	Invitation *outofband.Invitation `json:"invitation"`
+
+	ConnectOpts
+}
+
+// ConnectOpts is option for accepting out-of-band invitation and to perform DID exchange.
+type ConnectOpts struct {
+	// Label to be shared with the other agent during the subsequent DID exchange.
+	MyLabel string `json:"myLabel,omitempty"`
+
+	// router connections to be used to establish connection.
+	RouterConnections []string `json:"routerConnections,omitempty"`
+
+	// DID to be used when reusing a connection.
+	ReuseConnection string `json:"reuseConnection,omitempty"`
+
+	// To use any recognized DID in the services array for a reusable connection.
+	ReuseAnyConnection bool `json:"reuseAnyConnection,omitempty"`
+
+	// Timeout (in milliseconds) waiting for connection status to be completed.
+	Timeout time.Duration `json:"timeout,omitempty"`
+}
+
+// ConnectResponse is response model from wallet DID connection operation.
+type ConnectResponse struct {
+	// connection ID of the connection established.
+	ConnectionID string `json:"connectionID"`
+}
+
+// ProposePresentationRequest is request model for performing propose presentation operation from wallet.
+type ProposePresentationRequest struct {
+	vcwallet.WalletAuth
+
+	// out-of-band invitation to establish connection and send propose presentation message.
+	Invitation *wallet.GenericInvitation `json:"invitation"`
+
+	// Optional From DID option to customize sender DID.
+	FromDID string `json:"from,omitempty"`
+
+	// Timeout (in milliseconds) waiting for operation to be completed.
+	Timeout time.Duration `json:"timeout,omitempty"`
+
+	// Options for accepting out-of-band invitation and to perform DID exchange (for DIDComm V1).
+	ConnectionOpts ConnectOpts `json:"connectOptions,omitempty"`
+}
+
+// ProposePresentationResponse is response model from wallet propose presentation operation.
+type ProposePresentationResponse struct {
+	// response request presentation message from  relying party.
+	PresentationRequest *service.DIDCommMsgMap `json:"presentationRequest,omitempty"`
+}
+
+// PresentProofRequest is request model from wallet present proof operation.
+// Supported attachment MIME type "application/ld+json".
+type PresentProofRequest struct {
+	vcwallet.WalletAuth
+
+	// Thread ID from request presentation response
+	ThreadID string `json:"threadID,omitempty"`
+
+	// presentation to be sent as part of present proof message.
+	Presentation json.RawMessage `json:"presentation,omitempty"`
+
+	// If true then wallet will wait for present proof protocol status to be
+	// done or abandoned till given Timeout.
+	// Also, will return web redirect info if found in acknowledgment message or problem-report.
+	WaitForDone bool `json:"waitForDone,omitempty"`
+
+	// Optional timeout (in milliseconds) waiting for present proof operation to be done.
+	// will be taken into account only when WaitForDone is enabled.
+	// If not provided then wallet will use its default timeout.
+	Timeout time.Duration `json:"WaitForDoneTimeout,omitempty"`
+}
+
+// PresentProofResponse is response model from wallet present proof operation.
+type PresentProofResponse struct {
+	wallet.CredentialInteractionStatus
+}
+
+// ProposeCredentialRequest is request model for performing propose credential operation from wallet.
+type ProposeCredentialRequest struct {
+	vcwallet.WalletAuth
+
+	// out-of-band invitation to establish connection and send propose credential message.
+	Invitation *wallet.GenericInvitation `json:"invitation"`
+
+	// Optional From DID option to customize sender DID.
+	FromDID string `json:"from,omitempty"`
+
+	// Timeout (in milliseconds) waiting for operation to be completed.
+	Timeout time.Duration `json:"timeout,omitempty"`
+
+	// Options for accepting out-of-band invitation and to perform DID exchange (for DIDComm V1).
+	ConnectionOpts ConnectOpts `json:"connectOptions,omitempty"`
+}
+
+// ProposeCredentialResponse is response model from wallet propose credential operation.
+type ProposeCredentialResponse struct {
+	// response offer credential message from issuer.
+	OfferCredential *service.DIDCommMsgMap `json:"offerCredential,omitempty"`
+}
+
+// RequestCredentialRequest is request model from wallet request credential operation.
+// Supported attachment MIME type "application/ld+json".
+type RequestCredentialRequest struct {
+	vcwallet.WalletAuth
+
+	// Thread ID from offer credential response previously received during propose credential interaction.
+	ThreadID string `json:"threadID,omitempty"`
+
+	// presentation to be sent as part of request credential message.
+	Presentation json.RawMessage `json:"presentation,omitempty"`
+
+	// If true then wallet will wait till it receives credential fulfillment response from issuer for given Timeout.
+	// Also, will return web redirect info if found in fulfillment message or problem-report.
+	WaitForDone bool `json:"waitForDone,omitempty"`
+
+	// Optional timeout (in milliseconds) waiting for credential fulfillment to arrive.
+	// will be taken into account only when WaitForDone is enabled.
+	// If not provided then wallet will use its default timeout.
+	Timeout time.Duration `json:"WaitForDoneTimeout,omitempty"`
+}
+
+// RequestCredentialResponse is response model from wallet request credential operation.
+type RequestCredentialResponse struct {
+	wallet.CredentialInteractionStatus
+}
+
+// ResolveCredentialManifestRequest is request model for resolving credential manifest from wallet.
+type ResolveCredentialManifestRequest struct {
+	vcwallet.WalletAuth
+
+	// Credential Manifest on which given credential fulfillment or credential needs to be resolved.
+	Manifest json.RawMessage `json:"manifest,omitempty"`
+
+	// Fulfillment to be be resolved.
+	// If provided, then this option takes precedence over credential resolve option.
+	Fulfillment json.RawMessage `json:"fulfillment,omitempty"`
+
+	// Credential to be be resolved, to be provided along with 'DescriptorID' to be used for resolving.
+	Credential json.RawMessage `json:"credential,omitempty"`
+
+	// ID of the Credential from wallet content to be be resolved, to be provided along with 'DescriptorID'.
+	CredentialID string `json:"credentialID,omitempty"`
+
+	// ID of the output descriptor to be used for resolving given credential.
+	DescriptorID string `json:"descriptorID,omitempty"`
+}
+
+// ResolveCredentialManifestResponse is response model from wallet credential manifest resolve operation.
+type ResolveCredentialManifestResponse struct {
+	// List of Resolved Descriptor results.
+	Resolved []*cm.ResolvedDescriptor `json:"resolved,omitempty"`
+}
diff --git a/pkg/controller/command/vcwallet/command.go b/pkg/controller/command/vcwallet/command.go
index beb9345e0..9c377a9d0 100644
--- a/pkg/controller/command/vcwallet/command.go
+++ b/pkg/controller/command/vcwallet/command.go
@@ -23,7 +23,6 @@ import (
 	"github.com/hyperledger/aries-framework-go/pkg/crypto"
 	"github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
 	"github.com/hyperledger/aries-framework-go/pkg/internal/logutil"
-	"github.com/hyperledger/aries-framework-go/pkg/kms"
 	"github.com/hyperledger/aries-framework-go/pkg/kms/webkms"
 	"github.com/hyperledger/aries-framework-go/pkg/wallet"
 	"github.com/hyperledger/aries-framework-go/spi/storage"
@@ -80,24 +79,6 @@ const (
 
 	// ProfileExistsErrorCode for errors while checking if profile exists for a wallet user.
 	ProfileExistsErrorCode
-
-	// DIDConnectErrorCode for errors while performing DID connect in wallet.
-	DIDConnectErrorCode
-
-	// ProposePresentationErrorCode for errors while proposing presentation.
-	ProposePresentationErrorCode
-
-	// PresentProofErrorCode for errors while presenting proof from wallet.
-	PresentProofErrorCode
-
-	// ProposeCredentialErrorCode for errors while proposing credential from wallet.
-	ProposeCredentialErrorCode
-
-	// RequestCredentialErrorCode for errors while request credential from wallet for issue credential protocol.
-	RequestCredentialErrorCode
-
-	// ResolveCredentialManifestErrorCode for errors while resolving credential manifest from wallet.
-	ResolveCredentialManifestErrorCode
 )
 
 // All command operations.
@@ -105,37 +86,28 @@ const (
 	CommandName = "vcwallet"
 
 	// command methods.
-	CreateProfileMethod             = "CreateProfile"
-	UpdateProfileMethod             = "UpdateProfile"
-	ProfileExistsMethod             = "ProfileExists"
-	OpenMethod                      = "Open"
-	CloseMethod                     = "Close"
-	AddMethod                       = "Add"
-	RemoveMethod                    = "Remove"
-	GetMethod                       = "Get"
-	GetAllMethod                    = "GetAll"
-	QueryMethod                     = "Query"
-	IssueMethod                     = "Issue"
-	ProveMethod                     = "Prove"
-	VerifyMethod                    = "Verify"
-	DeriveMethod                    = "Derive"
-	CreateKeyPairMethod             = "CreateKeyPair"
-	ConnectMethod                   = "Connect"
-	ProposePresentationMethod       = "ProposePresentation"
-	PresentProofMethod              = "PresentProof"
-	ProposeCredentialMethod         = "ProposeCredential"
-	RequestCredentialMethod         = "RequestCredential"
-	ResolveCredentialManifestMethod = "ResolveCredentialManifest"
+	CreateProfileMethod = "CreateProfile"
+	UpdateProfileMethod = "UpdateProfile"
+	ProfileExistsMethod = "ProfileExists"
+	OpenMethod          = "Open"
+	CloseMethod         = "Close"
+	AddMethod           = "Add"
+	RemoveMethod        = "Remove"
+	GetMethod           = "Get"
+	GetAllMethod        = "GetAll"
+	QueryMethod         = "Query"
+	IssueMethod         = "Issue"
+	ProveMethod         = "Prove"
+	VerifyMethod        = "Verify"
+	DeriveMethod        = "Derive"
+	CreateKeyPairMethod = "CreateKeyPair"
 )
 
 // miscellaneous constants for the vc wallet command controller.
 const (
 	// log constants.
-	logSuccess         = "success"
-	logUserIDKey       = "userID"
-	connectionIDString = "connectionID"
-	invitationIDString = "invitationID"
-	LabelString        = "label"
+	logSuccess   = "success"
+	logUserIDKey = "userID"
 
 	emptyRawLength = 4
 
@@ -191,19 +163,6 @@ type provider interface {
 	Crypto() crypto.Crypto
 	JSONLDDocumentLoader() ld.DocumentLoader
 	MediaTypeProfiles() []string
-	didCommProvider // to be used only if wallet needs to be participated in DIDComm.
-}
-
-// didCommProvider to be used only if wallet needs to be participated in DIDComm operation.
-// TODO: using wallet KMS instead of provider KMS.
-// TODO: reconcile Protocol storage with wallet store.
-type didCommProvider interface {
-	KMS() kms.KeyManager
-	ServiceEndpoint() string
-	ProtocolStateStorageProvider() storage.Provider
-	Service(id string) (interface{}, error)
-	KeyType() kms.KeyType
-	KeyAgreementType() kms.KeyType
 }
 
 // Command contains operations provided by verifiable credential wallet controller.
@@ -245,12 +204,6 @@ func (o *Command) GetHandlers() []command.Handler {
 		cmdutil.NewCommandHandler(CommandName, VerifyMethod, o.Verify),
 		cmdutil.NewCommandHandler(CommandName, DeriveMethod, o.Derive),
 		cmdutil.NewCommandHandler(CommandName, CreateKeyPairMethod, o.CreateKeyPair),
-		cmdutil.NewCommandHandler(CommandName, ConnectMethod, o.Connect),
-		cmdutil.NewCommandHandler(CommandName, ProposePresentationMethod, o.ProposePresentation),
-		cmdutil.NewCommandHandler(CommandName, PresentProofMethod, o.PresentProof),
-		cmdutil.NewCommandHandler(CommandName, ProposeCredentialMethod, o.ProposeCredential),
-		cmdutil.NewCommandHandler(CommandName, RequestCredentialMethod, o.RequestCredential),
-		cmdutil.NewCommandHandler(CommandName, ResolveCredentialManifestMethod, o.ResolveCredentialManifest),
 	}
 }
 
@@ -750,285 +703,6 @@ func (o *Command) CreateKeyPair(rw io.Writer, req io.Reader) command.Error {
 	return nil
 }
 
-// Connect accepts out-of-band invitations and performs DID exchange.
-func (o *Command) Connect(rw io.Writer, req io.Reader) command.Error {
-	request := &ConnectRequest{}
-
-	err := json.NewDecoder(req).Decode(&request)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ConnectMethod, err.Error())
-
-		return command.NewValidationError(InvalidRequestErrorCode, err)
-	}
-
-	vcWallet, err := wallet.New(request.UserID, o.ctx)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ConnectMethod, err.Error())
-
-		return command.NewExecuteError(DIDConnectErrorCode, err)
-	}
-
-	didComm, err := wallet.NewDidComm(vcWallet, o.ctx)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ConnectMethod, err.Error())
-
-		return command.NewExecuteError(DIDConnectErrorCode, err)
-	}
-
-	connectionID, err := didComm.Connect(request.Auth, request.Invitation,
-		wallet.WithConnectTimeout(request.Timeout), wallet.WithReuseDID(request.ReuseConnection),
-		wallet.WithReuseAnyConnection(request.ReuseAnyConnection), wallet.WithMyLabel(request.MyLabel),
-		wallet.WithRouterConnections(request.RouterConnections...))
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ConnectMethod, err.Error())
-
-		return command.NewExecuteError(DIDConnectErrorCode, err)
-	}
-
-	command.WriteNillableResponse(rw, &ConnectResponse{ConnectionID: connectionID}, logger)
-
-	logutil.LogDebug(logger, CommandName, ConnectMethod, logSuccess,
-		logutil.CreateKeyValueString(logUserIDKey, request.UserID),
-		logutil.CreateKeyValueString(invitationIDString, request.Invitation.ID),
-		logutil.CreateKeyValueString(LabelString, request.MyLabel),
-		logutil.CreateKeyValueString(connectionIDString, connectionID))
-
-	return nil
-}
-
-// ProposePresentation accepts out-of-band invitation and sends message proposing presentation
-// from wallet to relying party.
-// https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposepresentation
-//
-// Currently Supporting
-// [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2)
-func (o *Command) ProposePresentation(rw io.Writer, req io.Reader) command.Error {
-	request := &ProposePresentationRequest{}
-
-	err := json.NewDecoder(req).Decode(&request)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ProposePresentationMethod, err.Error())
-
-		return command.NewValidationError(InvalidRequestErrorCode, err)
-	}
-
-	vcWallet, err := wallet.New(request.UserID, o.ctx)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ProposePresentationMethod, err.Error())
-
-		return command.NewExecuteError(ProposePresentationErrorCode, err)
-	}
-
-	didComm, err := wallet.NewDidComm(vcWallet, o.ctx)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ProposePresentationMethod, err.Error())
-
-		return command.NewExecuteError(ProposePresentationErrorCode, err)
-	}
-
-	msg, err := didComm.ProposePresentation(request.Auth, request.Invitation,
-		wallet.WithFromDID(request.FromDID), wallet.WithInitiateTimeout(request.Timeout),
-		wallet.WithConnectOptions(wallet.WithConnectTimeout(request.ConnectionOpts.Timeout),
-			wallet.WithReuseDID(request.ConnectionOpts.ReuseConnection),
-			wallet.WithReuseAnyConnection(request.ConnectionOpts.ReuseAnyConnection),
-			wallet.WithMyLabel(request.ConnectionOpts.MyLabel),
-			wallet.WithRouterConnections(request.ConnectionOpts.RouterConnections...)))
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ProposePresentationMethod, err.Error())
-
-		return command.NewExecuteError(ProposePresentationErrorCode, err)
-	}
-
-	command.WriteNillableResponse(rw, &ProposePresentationResponse{PresentationRequest: msg}, logger)
-
-	logutil.LogDebug(logger, CommandName, ProposePresentationMethod, logSuccess,
-		logutil.CreateKeyValueString(logUserIDKey, request.UserID))
-
-	return nil
-}
-
-// PresentProof sends present proof message from wallet to relying party.
-// https://w3c-ccg.github.io/universal-wallet-interop-spec/#presentproof
-//
-// Currently Supporting
-// [0454-present-proof-v2](https://github.com/hyperledger/aries-rfcs/tree/master/features/0454-present-proof-v2)
-//
-func (o *Command) PresentProof(rw io.Writer, req io.Reader) command.Error {
-	request := &PresentProofRequest{}
-
-	err := json.NewDecoder(req).Decode(&request)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, PresentProofMethod, err.Error())
-
-		return command.NewValidationError(InvalidRequestErrorCode, err)
-	}
-
-	vcWallet, err := wallet.New(request.UserID, o.ctx)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, PresentProofMethod, err.Error())
-
-		return command.NewExecuteError(PresentProofErrorCode, err)
-	}
-
-	didComm, err := wallet.NewDidComm(vcWallet, o.ctx)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, PresentProofMethod, err.Error())
-
-		return command.NewExecuteError(PresentProofErrorCode, err)
-	}
-
-	status, err := didComm.PresentProof(request.Auth, request.ThreadID,
-		prepareConcludeInteractionOpts(request.WaitForDone, request.Timeout, request.Presentation)...)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, PresentProofMethod, err.Error())
-
-		return command.NewExecuteError(PresentProofErrorCode, err)
-	}
-
-	command.WriteNillableResponse(rw, status, logger)
-
-	logutil.LogDebug(logger, CommandName, PresentProofMethod, logSuccess,
-		logutil.CreateKeyValueString(logUserIDKey, request.UserID))
-
-	return nil
-}
-
-// ProposeCredential sends propose credential message from wallet to issuer.
-// https://w3c-ccg.github.io/universal-wallet-interop-spec/#proposecredential
-//
-// Currently Supporting : 0453-issueCredentialV2
-// https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md
-//
-func (o *Command) ProposeCredential(rw io.Writer, req io.Reader) command.Error {
-	request := &ProposeCredentialRequest{}
-
-	err := json.NewDecoder(req).Decode(&request)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ProposeCredentialMethod, err.Error())
-
-		return command.NewValidationError(InvalidRequestErrorCode, err)
-	}
-
-	vcWallet, err := wallet.New(request.UserID, o.ctx)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ProposeCredentialMethod, err.Error())
-
-		return command.NewExecuteError(ProposeCredentialErrorCode, err)
-	}
-
-	didComm, err := wallet.NewDidComm(vcWallet, o.ctx)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ProposeCredentialMethod, err.Error())
-
-		return command.NewExecuteError(ProposeCredentialErrorCode, err)
-	}
-
-	msg, err := didComm.ProposeCredential(request.Auth, request.Invitation,
-		wallet.WithFromDID(request.FromDID), wallet.WithInitiateTimeout(request.Timeout),
-		wallet.WithConnectOptions(wallet.WithConnectTimeout(request.ConnectionOpts.Timeout),
-			wallet.WithReuseDID(request.ConnectionOpts.ReuseConnection),
-			wallet.WithReuseAnyConnection(request.ConnectionOpts.ReuseAnyConnection),
-			wallet.WithMyLabel(request.ConnectionOpts.MyLabel),
-			wallet.WithRouterConnections(request.ConnectionOpts.RouterConnections...)))
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ProposeCredentialMethod, err.Error())
-
-		return command.NewExecuteError(ProposeCredentialErrorCode, err)
-	}
-
-	command.WriteNillableResponse(rw, &ProposeCredentialResponse{OfferCredential: msg}, logger)
-
-	logutil.LogDebug(logger, CommandName, ProposeCredentialMethod, logSuccess,
-		logutil.CreateKeyValueString(logUserIDKey, request.UserID))
-
-	return nil
-}
-
-// RequestCredential sends request credential message from wallet to issuer and
-// optionally waits for credential fulfillment.
-// https://w3c-ccg.github.io/universal-wallet-interop-spec/#requestcredential
-//
-// Currently Supporting : 0453-issueCredentialV2
-// https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md
-//
-func (o *Command) RequestCredential(rw io.Writer, req io.Reader) command.Error {
-	request := &RequestCredentialRequest{}
-
-	err := json.NewDecoder(req).Decode(&request)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, RequestCredentialMethod, err.Error())
-
-		return command.NewValidationError(InvalidRequestErrorCode, err)
-	}
-
-	vcWallet, err := wallet.New(request.UserID, o.ctx)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, RequestCredentialMethod, err.Error())
-
-		return command.NewExecuteError(RequestCredentialErrorCode, err)
-	}
-
-	didComm, err := wallet.NewDidComm(vcWallet, o.ctx)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, RequestCredentialMethod, err.Error())
-
-		return command.NewExecuteError(RequestCredentialErrorCode, err)
-	}
-
-	status, err := didComm.RequestCredential(request.Auth, request.ThreadID,
-		prepareConcludeInteractionOpts(request.WaitForDone, request.Timeout, request.Presentation)...)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, RequestCredentialMethod, err.Error())
-
-		return command.NewExecuteError(RequestCredentialErrorCode, err)
-	}
-
-	command.WriteNillableResponse(rw, status, logger)
-
-	logutil.LogDebug(logger, CommandName, RequestCredentialMethod, logSuccess,
-		logutil.CreateKeyValueString(logUserIDKey, request.UserID))
-
-	return nil
-}
-
-// ResolveCredentialManifest resolves given credential manifest by credential fulfillment or credential.
-// Supports: https://identity.foundation/credential-manifest/
-//
-// Writes list of resolved descriptors to writer or returns error if operation fails.
-//
-func (o *Command) ResolveCredentialManifest(rw io.Writer, req io.Reader) command.Error {
-	request := &ResolveCredentialManifestRequest{}
-
-	err := json.NewDecoder(req).Decode(&request)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ResolveCredentialManifestMethod, err.Error())
-
-		return command.NewValidationError(InvalidRequestErrorCode, err)
-	}
-
-	vcWallet, err := wallet.New(request.UserID, o.ctx)
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ResolveCredentialManifestMethod, err.Error())
-
-		return command.NewExecuteError(ResolveCredentialManifestErrorCode, err)
-	}
-
-	resolved, err := vcWallet.ResolveCredentialManifest(request.Auth, request.Manifest,
-		prepareResolveManifestOption(request))
-	if err != nil {
-		logutil.LogInfo(logger, CommandName, ResolveCredentialManifestMethod, err.Error())
-
-		return command.NewExecuteError(ResolveCredentialManifestErrorCode, err)
-	}
-
-	command.WriteNillableResponse(rw, &ResolveCredentialManifestResponse{Resolved: resolved}, logger)
-
-	logutil.LogDebug(logger, CommandName, ResolveCredentialManifestMethod, logSuccess,
-		logutil.CreateKeyValueString(logUserIDKey, request.UserID))
-
-	return nil
-}
-
 // prepareProfileOptions prepares options for creating wallet profile.
 func prepareProfileOptions(rqst *CreateOrUpdateProfileRequest) []wallet.ProfileOptions {
 	var options []wallet.ProfileOptions
@@ -1200,29 +874,3 @@ func prepareDeriveOption(rqst *DeriveRequest) wallet.CredentialToDerive {
 
 	return wallet.FromRawCredential(rqst.RawCredential)
 }
-
-func prepareConcludeInteractionOpts(waitForDone bool, timeout time.Duration, presentation json.RawMessage) []wallet.ConcludeInteractionOptions { //nolint: lll
-	var options []wallet.ConcludeInteractionOptions
-
-	if waitForDone {
-		options = append(options, wallet.WaitForDone(timeout))
-	}
-
-	return append(options, wallet.FromRawPresentation(presentation))
-}
-
-func prepareResolveManifestOption(rqst *ResolveCredentialManifestRequest) wallet.ResolveManifestOption {
-	if len(rqst.Fulfillment) > emptyRawLength {
-		return wallet.ResolveRawFulfillment(rqst.Fulfillment)
-	}
-
-	if len(rqst.Credential) > emptyRawLength {
-		return wallet.ResolveRawCredential(rqst.DescriptorID, rqst.Credential)
-	}
-
-	if rqst.CredentialID != "" {
-		return wallet.ResolveCredentialID(rqst.DescriptorID, rqst.CredentialID)
-	}
-
-	return nil
-}
diff --git a/pkg/controller/command/vcwallet/command_test.go b/pkg/controller/command/vcwallet/command_test.go
index e546c09b1..ada7e26a4 100644
--- a/pkg/controller/command/vcwallet/command_test.go
+++ b/pkg/controller/command/vcwallet/command_test.go
@@ -21,11 +21,8 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/hyperledger/aries-framework-go/internal/testdata"
-	outofbandClient "github.com/hyperledger/aries-framework-go/pkg/client/outofband"
 	"github.com/hyperledger/aries-framework-go/pkg/controller/command"
 	"github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto"
-	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model"
-	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
 	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/didexchange"
 	issuecredentialsvc "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/issuecredential"
 	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/mediator"
@@ -46,7 +43,6 @@ import (
 	mockprovider "github.com/hyperledger/aries-framework-go/pkg/mock/provider"
 	mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage"
 	mockvdr "github.com/hyperledger/aries-framework-go/pkg/mock/vdr"
-	"github.com/hyperledger/aries-framework-go/pkg/store/connection"
 	"github.com/hyperledger/aries-framework-go/pkg/vdr/key"
 	"github.com/hyperledger/aries-framework-go/pkg/wallet"
 )
@@ -63,9 +59,6 @@ const (
 	sampleFakeTkn          = "sample-fake-token-01"
 	sampleFakeCapability   = "sample-fake-capability-01"
 	sampleDIDKey           = "did:key:z6MknC1wwS6DEYwtGbZZo2QvjQjkh2qSBjb4GYmbye8dv4S5"
-	webRedirectStatusKey   = "status"
-	webRedirectURLKey      = "url"
-	exampleWebRedirect     = "http://example.com/sample"
 )
 
 func TestNew(t *testing.T) {
@@ -73,7 +66,7 @@ func TestNew(t *testing.T) {
 		cmd := New(newMockProvider(t), &Config{})
 		require.NotNil(t, cmd)
 
-		require.Len(t, cmd.GetHandlers(), 21)
+		require.Len(t, cmd.GetHandlers(), 15)
 	})
 }
 
@@ -404,12 +397,20 @@ func TestCommand_OpenAndClose(t *testing.T) {
 		},
 	})
 
-	t.Run("successfully unlock & lock wallet (local kms)", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
+	t.Run("successfully unlock & lock wallet (gnap auth, no custom header func)", func(t *testing.T) {
+		cmd := New(mockctx, &Config{
+			WebKMSCacheSize: 99,
+		})
 
 		request := &UnlockWalletRequest{
-			UserID:             sampleUser1,
-			LocalKMSPassphrase: samplePassPhrase,
+			UserID: sampleUser2,
+			WebKMSAuth: &UnlockAuth{
+				GNAPToken: sampleFakeTkn,
+			},
+			EDVUnlock: &UnlockAuth{
+				GNAPToken: sampleFakeTkn,
+			},
+			Expiry: 10 * time.Second,
 		}
 
 		// unlock wallet
@@ -427,7 +428,7 @@ func TestCommand_OpenAndClose(t *testing.T) {
 		b.Reset()
 
 		// lock wallet
-		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser1}))
+		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2}))
 		require.NoError(t, cmdErr)
 		var lockResponse LockWalletResponse
 		require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse))
@@ -435,19 +436,33 @@ func TestCommand_OpenAndClose(t *testing.T) {
 		b.Reset()
 
 		// lock wallet again
-		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser1}))
+		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2}))
 		require.NoError(t, cmdErr)
 		require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse))
 		require.False(t, lockResponse.Closed)
 		b.Reset()
 	})
 
-	t.Run("successfully unlock & lock wallet (remote kms)", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
+	t.Run("successfully unlock & lock wallet (gnap auth, with custom header func)", func(t *testing.T) {
+		headerFunc := func(r *http.Request) (*http.Header, error) {
+			return &r.Header, nil
+		}
+
+		cmd := New(mockctx, &Config{
+			WebKMSCacheSize:  99,
+			WebKMSGNAPSigner: headerFunc,
+			EDVGNAPSigner:    headerFunc,
+		})
 
 		request := &UnlockWalletRequest{
-			UserID:     sampleUser2,
-			WebKMSAuth: &UnlockAuth{AuthToken: sampleFakeTkn},
+			UserID: sampleUser2,
+			WebKMSAuth: &UnlockAuth{
+				GNAPToken: sampleFakeTkn,
+			},
+			EDVUnlock: &UnlockAuth{
+				GNAPToken: sampleFakeTkn,
+			},
+			Expiry: 10 * time.Second,
 		}
 
 		// unlock wallet
@@ -480,16 +495,12 @@ func TestCommand_OpenAndClose(t *testing.T) {
 		b.Reset()
 	})
 
-	t.Run("successfully unlock & lock wallet (remote kms, capability)", func(t *testing.T) {
-		cmd := New(mockctx, &Config{
-			WebKMSCacheSize:     99,
-			WebKMSAuthzProvider: &mockAuthZCapability{},
-		})
+	t.Run("successfully unlock & lock wallet (local kms)", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
 
 		request := &UnlockWalletRequest{
-			UserID:     sampleUser2,
-			WebKMSAuth: &UnlockAuth{Capability: sampleFakeCapability},
-			Expiry:     10 * time.Second,
+			UserID:             sampleUser1,
+			LocalKMSPassphrase: samplePassPhrase,
 		}
 
 		// unlock wallet
@@ -507,7 +518,7 @@ func TestCommand_OpenAndClose(t *testing.T) {
 		b.Reset()
 
 		// lock wallet
-		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2}))
+		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser1}))
 		require.NoError(t, cmdErr)
 		var lockResponse LockWalletResponse
 		require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse))
@@ -515,22 +526,19 @@ func TestCommand_OpenAndClose(t *testing.T) {
 		b.Reset()
 
 		// lock wallet again
-		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2}))
+		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser1}))
 		require.NoError(t, cmdErr)
 		require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse))
 		require.False(t, lockResponse.Closed)
 		b.Reset()
 	})
 
-	t.Run("successfully unlock & lock wallet (local kms, edv user)", func(t *testing.T) {
+	t.Run("successfully unlock & lock wallet (remote kms)", func(t *testing.T) {
 		cmd := New(mockctx, &Config{})
 
 		request := &UnlockWalletRequest{
-			UserID:             sampleUser3,
-			LocalKMSPassphrase: samplePassPhrase,
-			EDVUnlock: &UnlockAuth{
-				AuthToken: sampleFakeTkn,
-			},
+			UserID:     sampleUser2,
+			WebKMSAuth: &UnlockAuth{AuthToken: sampleFakeTkn},
 		}
 
 		// unlock wallet
@@ -548,7 +556,7 @@ func TestCommand_OpenAndClose(t *testing.T) {
 		b.Reset()
 
 		// lock wallet
-		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser3}))
+		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2}))
 		require.NoError(t, cmdErr)
 		var lockResponse LockWalletResponse
 		require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse))
@@ -556,27 +564,23 @@ func TestCommand_OpenAndClose(t *testing.T) {
 		b.Reset()
 
 		// lock wallet again
-		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser3}))
+		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2}))
 		require.NoError(t, cmdErr)
 		require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse))
 		require.False(t, lockResponse.Closed)
 		b.Reset()
 	})
 
-	t.Run("successfully unlock & lock wallet (gnap auth, no custom header func)", func(t *testing.T) {
+	t.Run("successfully unlock & lock wallet (remote kms, capability)", func(t *testing.T) {
 		cmd := New(mockctx, &Config{
-			WebKMSCacheSize: 99,
+			WebKMSCacheSize:     99,
+			WebKMSAuthzProvider: &mockAuthZCapability{},
 		})
 
 		request := &UnlockWalletRequest{
-			UserID: sampleUser2,
-			WebKMSAuth: &UnlockAuth{
-				GNAPToken: sampleFakeTkn,
-			},
-			EDVUnlock: &UnlockAuth{
-				GNAPToken: sampleFakeTkn,
-			},
-			Expiry: 10 * time.Second,
+			UserID:     sampleUser2,
+			WebKMSAuth: &UnlockAuth{Capability: sampleFakeCapability},
+			Expiry:     10 * time.Second,
 		}
 
 		// unlock wallet
@@ -609,26 +613,15 @@ func TestCommand_OpenAndClose(t *testing.T) {
 		b.Reset()
 	})
 
-	t.Run("successfully unlock & lock wallet (gnap auth, with custom header func)", func(t *testing.T) {
-		headerFunc := func(r *http.Request) (*http.Header, error) {
-			return &r.Header, nil
-		}
-
-		cmd := New(mockctx, &Config{
-			WebKMSCacheSize:  99,
-			WebKMSGNAPSigner: headerFunc,
-			EDVGNAPSigner:    headerFunc,
-		})
+	t.Run("successfully unlock & lock wallet (local kms, edv user)", func(t *testing.T) {
+		cmd := New(mockctx, &Config{})
 
 		request := &UnlockWalletRequest{
-			UserID: sampleUser2,
-			WebKMSAuth: &UnlockAuth{
-				GNAPToken: sampleFakeTkn,
-			},
+			UserID:             sampleUser3,
+			LocalKMSPassphrase: samplePassPhrase,
 			EDVUnlock: &UnlockAuth{
-				GNAPToken: sampleFakeTkn,
+				AuthToken: sampleFakeTkn,
 			},
-			Expiry: 10 * time.Second,
 		}
 
 		// unlock wallet
@@ -646,7 +639,7 @@ func TestCommand_OpenAndClose(t *testing.T) {
 		b.Reset()
 
 		// lock wallet
-		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2}))
+		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser3}))
 		require.NoError(t, cmdErr)
 		var lockResponse LockWalletResponse
 		require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse))
@@ -654,7 +647,7 @@ func TestCommand_OpenAndClose(t *testing.T) {
 		b.Reset()
 
 		// lock wallet again
-		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser2}))
+		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: sampleUser3}))
 		require.NoError(t, cmdErr)
 		require.NoError(t, json.NewDecoder(&b).Decode(&lockResponse))
 		require.False(t, lockResponse.Closed)
@@ -1776,932 +1769,125 @@ func TestCommand_CreateKeyPair(t *testing.T) {
 	})
 }
 
-func TestCommand_Connect(t *testing.T) {
-	const sampleDIDCommUser = "sample-didcomm-user01"
+func createSampleUserProfile(t *testing.T, ctx *mockprovider.Provider, request *CreateOrUpdateProfileRequest) {
+	cmd := New(ctx, &Config{})
+	require.NotNil(t, cmd)
 
-	mockctx := newMockProvider(t)
+	var l bytes.Buffer
+	cmdErr := cmd.CreateProfile(&l, getReader(t, request))
+	require.NoError(t, cmdErr)
+}
 
-	createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{
-		UserID:             sampleDIDCommUser,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
+func getReader(t *testing.T, v interface{}) io.Reader {
+	vcReqBytes, err := json.Marshal(v)
+	require.NoError(t, err)
 
-	token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{
-		UserID:             sampleDIDCommUser,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
+	return bytes.NewBuffer(vcReqBytes)
+}
 
-	defer lock()
+func getUnlockToken(t *testing.T, b bytes.Buffer) string {
+	var response UnlockWalletResponse
 
-	t.Run("successfully perform DID connect", func(t *testing.T) {
-		sampleConnID := uuid.New().String()
+	require.NoError(t, json.NewDecoder(&b).Decode(&response))
 
-		oobSvc := &mockoutofband.MockOobService{
-			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
-				return sampleConnID, nil
-			},
-		}
-		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
-
-		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
-			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
-				ch <- service.StateMsg{
-					Type:       service.PostState,
-					StateID:    didexchange.StateIDCompleted,
-					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
-				}
+	return response.Token
+}
 
-				return nil
-			},
-		}
-		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
+func unlockWallet(t *testing.T, ctx *mockprovider.Provider, request *UnlockWalletRequest) (string, func()) {
+	cmd := New(ctx, nil)
 
-		cmd := New(mockctx, &Config{})
+	var b bytes.Buffer
 
-		request := &ConnectRequest{
-			WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			Invitation: &outofbandClient.Invitation{},
-			ConnectOpts: ConnectOpts{
-				MyLabel: "sample-label",
-			},
-		}
+	cmdErr := cmd.Open(&b, getReader(t, &request))
+	require.NoError(t, cmdErr)
 
-		var b bytes.Buffer
-		cmdErr := cmd.Connect(&b, getReader(t, &request))
-		require.NoError(t, cmdErr)
+	return getUnlockToken(t, b), func() {
+		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: request.UserID}))
+		if cmdErr != nil {
+			t.Log(t, cmdErr)
+		}
+	}
+}
 
-		var response ConnectResponse
-		require.NoError(t, json.NewDecoder(&b).Decode(&response))
-		require.NotEmpty(t, response)
-		require.Equal(t, sampleConnID, response.ConnectionID)
-	})
+func addContent(t *testing.T, ctx *mockprovider.Provider, request *AddContentRequest) {
+	cmd := New(ctx, &Config{})
 
-	t.Run("did connect failure", func(t *testing.T) {
-		oobSvc := &mockoutofband.MockOobService{
-			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
-				return "", fmt.Errorf(sampleCommandError)
-			},
-		}
-		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
+	var b bytes.Buffer
+	defer b.Reset()
 
-		cmd := New(mockctx, &Config{})
+	cmdErr := cmd.Add(&b, getReader(t, &request))
+	require.NoError(t, cmdErr)
+}
 
-		request := &ConnectRequest{
-			WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			Invitation: &outofbandClient.Invitation{},
-			ConnectOpts: ConnectOpts{
-				MyLabel: "sample-label",
-			},
-		}
+func validateError(t *testing.T, err command.Error,
+	expectedType command.Type, expectedCode command.Code, contains string) {
+	require.Error(t, err)
+	require.Equal(t, err.Type(), expectedType)
+	require.Equal(t, err.Code(), expectedCode)
 
-		var b bytes.Buffer
-		cmdErr := cmd.Connect(&b, getReader(t, &request))
-		require.Error(t, cmdErr)
+	if contains != "" {
+		require.Contains(t, err.Error(), contains)
+	}
+}
 
-		validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, sampleCommandError)
-		validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, "failed to accept invitation")
-		require.Empty(t, b.Bytes())
-	})
+func newMockProvider(t *testing.T) *mockprovider.Provider {
+	t.Helper()
 
-	t.Run("did connect failure - invalid request", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
+	loader, err := ldtestutil.DocumentLoader()
+	require.NoError(t, err)
 
-		var b bytes.Buffer
-		cmdErr := cmd.Connect(&b, bytes.NewBufferString("--"))
-		require.Error(t, cmdErr)
+	serviceMap := map[string]interface{}{
+		presentproofSvc.Name:    &mockpresentproof.MockPresentProofSvc{},
+		outofbandSvc.Name:       &mockoutofband.MockOobService{},
+		didexchange.DIDExchange: &mockdidexchange.MockDIDExchangeSvc{},
+		mediator.Coordination:   &mockmediator.MockMediatorSvc{},
+		issuecredentialsvc.Name: &mockissuecredential.MockIssueCredentialSvc{},
+		oobv2.Name:              &mockoutofbandv2.MockOobService{},
+	}
 
-		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
-		require.Empty(t, b.Bytes())
-	})
+	return &mockprovider.Provider{
+		StorageProviderValue:              mockstorage.NewMockStoreProvider(),
+		ProtocolStateStorageProviderValue: mockstorage.NewMockStoreProvider(),
+		DocumentLoaderValue:               loader,
+		ServiceMap:                        serviceMap,
+	}
+}
 
-	t.Run("attempt to didconnect with invalid profile", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
+func getMockDIDKeyVDR() *mockvdr.MockVDRegistry {
+	return &mockvdr.MockVDRegistry{
+		ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
+			if strings.HasPrefix(didID, "did:key:") {
+				k := key.New()
 
-		request := &ConnectRequest{
-			WalletAuth: WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn},
-			Invitation: &outofbandClient.Invitation{},
-			ConnectOpts: ConnectOpts{
-				MyLabel: "sample-label",
-			},
-		}
+				d, e := k.Read(didID)
+				if e != nil {
+					return nil, e
+				}
 
-		var b bytes.Buffer
-		cmdErr := cmd.Connect(&b, getReader(t, &request))
-		require.Error(t, cmdErr)
+				return d, nil
+			}
 
-		validateError(t, cmdErr, command.ExecuteError, DIDConnectErrorCode, "failed to get VC wallet profile")
-		require.Empty(t, b.Bytes())
-	})
+			return nil, fmt.Errorf("did not found")
+		},
+	}
 }
 
-func TestCommand_ProposePresentation(t *testing.T) {
-	const sampleDIDCommUser = "sample-didcomm-user02"
+func parseCredential(t *testing.T, b bytes.Buffer) *verifiable.Credential {
+	var response struct {
+		Credential json.RawMessage
+	}
 
-	mockctx := newMockProvider(t)
+	require.NoError(t, json.NewDecoder(&b).Decode(&response))
 
-	createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{
-		UserID:             sampleDIDCommUser,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
+	loader, err := ldtestutil.DocumentLoader()
+	require.NoError(t, err)
 
-	token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{
-		UserID:             sampleDIDCommUser,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
+	vc, err := verifiable.ParseCredential(response.Credential, verifiable.WithDisabledProofCheck(),
+		verifiable.WithJSONLDDocumentLoader(loader))
+	require.NoError(t, err)
 
-	defer lock()
-
-	const (
-		myDID    = "did:mydid:123"
-		theirDID = "did:theirdid:123"
-	)
-
-	t.Run("successfully send propose presentation", func(t *testing.T) {
-		sampleConnID := uuid.New().String()
-
-		oobSvc := &mockoutofband.MockOobService{
-			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
-				return sampleConnID, nil
-			},
-		}
-		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
-
-		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
-			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
-				ch <- service.StateMsg{
-					Type:       service.PostState,
-					StateID:    didexchange.StateIDCompleted,
-					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
-				}
-
-				return nil
-			},
-		}
-		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
-
-		thID := uuid.New().String()
-		ppSvc := &mockpresentproof.MockPresentProofSvc{
-			ActionsFunc: func() ([]presentproofSvc.Action, error) {
-				return []presentproofSvc.Action{
-					{
-						PIID: thID,
-						Msg: service.NewDIDCommMsgMap(&presentproofSvc.RequestPresentationV2{
-							Comment: "mock msg",
-						}),
-						MyDID:    myDID,
-						TheirDID: theirDID,
-					},
-				}, nil
-			},
-			HandleFunc: func(service.DIDCommMsg) (string, error) {
-				return thID, nil
-			},
-		}
-		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
-
-		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
-		require.NoError(t, err)
-
-		record := &connection.Record{
-			ConnectionID: sampleConnID,
-			MyDID:        myDID,
-			TheirDID:     theirDID,
-		}
-		recordBytes, err := json.Marshal(record)
-		require.NoError(t, err)
-		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
-
-		cmd := New(mockctx, &Config{})
-
-		request := &ProposePresentationRequest{
-			WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			Invitation: &wallet.GenericInvitation{},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.ProposePresentation(&b, getReader(t, &request))
-		require.NoError(t, cmdErr)
-
-		var response ProposePresentationResponse
-		require.NoError(t, json.NewDecoder(&b).Decode(&response))
-		require.NotEmpty(t, response)
-		require.NotEmpty(t, response.PresentationRequest)
-	})
-
-	t.Run("failed to send propose presentation", func(t *testing.T) {
-		oobSvc := &mockoutofband.MockOobService{
-			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
-				return "", fmt.Errorf(sampleCommandError)
-			},
-		}
-
-		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
-
-		cmd := New(mockctx, &Config{})
-
-		request := &ConnectRequest{
-			WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			Invitation: &outofbandClient.Invitation{},
-			ConnectOpts: ConnectOpts{
-				MyLabel: "sample-label",
-			},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.ProposePresentation(&b, getReader(t, &request))
-		require.Error(t, cmdErr)
-
-		validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, sampleCommandError)
-		validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, "failed to accept invitation")
-		require.Empty(t, b.Bytes())
-	})
-
-	t.Run("failed to send propose presentation - invalid request", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		var b bytes.Buffer
-		cmdErr := cmd.ProposePresentation(&b, bytes.NewBufferString("--"))
-		require.Error(t, cmdErr)
-
-		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
-		require.Empty(t, b.Bytes())
-	})
-
-	t.Run("failed to send propose presentation - invalid profile", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		request := &ConnectRequest{
-			WalletAuth: WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn},
-			Invitation: &outofbandClient.Invitation{},
-			ConnectOpts: ConnectOpts{
-				MyLabel: "sample-label",
-			},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.ProposePresentation(&b, getReader(t, &request))
-		require.Error(t, cmdErr)
-
-		validateError(t, cmdErr, command.ExecuteError, ProposePresentationErrorCode, "failed to get VC wallet profile")
-		require.Empty(t, b.Bytes())
-	})
-}
-
-func TestCommand_PresentProof(t *testing.T) {
-	const sampleDIDCommUser = "sample-didcomm-user03"
-
-	mockctx := newMockProvider(t)
-
-	createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{
-		UserID:             sampleDIDCommUser,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
-
-	token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{
-		UserID:             sampleDIDCommUser,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
-
-	defer lock()
-
-	t.Run("successfully present proof", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		request := &PresentProofRequest{
-			WalletAuth:   WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			ThreadID:     uuid.New().String(),
-			Presentation: json.RawMessage{},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.PresentProof(&b, getReader(t, &request))
-		require.NoError(t, cmdErr)
-	})
-
-	t.Run("successfully present proof - wait for done", func(t *testing.T) {
-		thID := uuid.New().String()
-		mockPresentProofSvc := &mockpresentproof.MockPresentProofSvc{
-			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
-				ch <- service.StateMsg{
-					Type:    service.PostState,
-					StateID: presentproofSvc.StateNameDone,
-					Properties: &mockdidexchange.MockEventProperties{
-						Properties: map[string]interface{}{
-							webRedirectStatusKey: model.AckStatusOK,
-							webRedirectURLKey:    exampleWebRedirect,
-						},
-					},
-					Msg: &mockMsg{thID: thID},
-				}
-
-				return nil
-			},
-		}
-		mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc
-
-		cmd := New(mockctx, &Config{})
-
-		request := &PresentProofRequest{
-			WalletAuth:   WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			ThreadID:     thID,
-			Presentation: json.RawMessage{},
-			WaitForDone:  true,
-			Timeout:      1 * time.Millisecond,
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.PresentProof(&b, getReader(t, &request))
-		require.NoError(t, cmdErr)
-
-		var response wallet.CredentialInteractionStatus
-		require.NoError(t, json.NewDecoder(&b).Decode(&response))
-		require.NotEmpty(t, response)
-		require.Equal(t, exampleWebRedirect, response.RedirectURL)
-		require.Equal(t, model.AckStatusOK, response.Status)
-	})
-
-	t.Run("failed to present proof", func(t *testing.T) {
-		ppSvc := &mockpresentproof.MockPresentProofSvc{
-			ActionContinueFunc: func(string, ...presentproofSvc.Opt) error {
-				return fmt.Errorf(sampleCommandError)
-			},
-		}
-
-		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
-
-		cmd := New(mockctx, &Config{})
-
-		request := &PresentProofRequest{
-			WalletAuth:   WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			ThreadID:     uuid.New().String(),
-			Presentation: json.RawMessage{},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.PresentProof(&b, getReader(t, &request))
-		validateError(t, cmdErr, command.ExecuteError, PresentProofErrorCode, sampleCommandError)
-		require.Empty(t, b.Bytes())
-	})
-
-	t.Run("failed to present proof - invalid request", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		var b bytes.Buffer
-		cmdErr := cmd.PresentProof(&b, bytes.NewBufferString("--"))
-		require.Error(t, cmdErr)
-
-		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
-		require.Empty(t, b.Bytes())
-	})
-
-	t.Run("failed to present proof - invalid profile", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		request := &PresentProofRequest{
-			WalletAuth:   WalletAuth{UserID: sampleUserID, Auth: token},
-			ThreadID:     uuid.New().String(),
-			Presentation: json.RawMessage{},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.PresentProof(&b, getReader(t, &request))
-		require.Error(t, cmdErr)
-
-		validateError(t, cmdErr, command.ExecuteError, PresentProofErrorCode, "failed to get VC wallet profile")
-		require.Empty(t, b.Bytes())
-	})
-}
-
-func TestCommand_ProposeCredential(t *testing.T) {
-	const (
-		sampleDIDCommUser = "sample-didcomm-user02"
-		sampleMsgComment  = "sample mock msg"
-		myDID             = "did:mydid:123"
-		theirDID          = "did:theirdid:123"
-	)
-
-	mockctx := newMockProvider(t)
-
-	createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{
-		UserID:             sampleDIDCommUser,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
-
-	token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{
-		UserID:             sampleDIDCommUser,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
-
-	defer lock()
-
-	t.Run("successfully send propose credential", func(t *testing.T) {
-		sampleConnID := uuid.New().String()
-
-		oobSvc := &mockoutofband.MockOobService{
-			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
-				return sampleConnID, nil
-			},
-		}
-		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
-
-		didexSvc := &mockdidexchange.MockDIDExchangeSvc{
-			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
-				ch <- service.StateMsg{
-					Type:       service.PostState,
-					StateID:    didexchange.StateIDCompleted,
-					Properties: &mockdidexchange.MockEventProperties{ConnID: sampleConnID},
-				}
-
-				return nil
-			},
-		}
-		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
-
-		thID := uuid.New().String()
-		icSvc := &mockissuecredential.MockIssueCredentialSvc{
-			ActionsFunc: func() ([]issuecredentialsvc.Action, error) {
-				return []issuecredentialsvc.Action{
-					{
-						PIID: thID,
-						Msg: service.NewDIDCommMsgMap(&issuecredentialsvc.OfferCredentialV2{
-							Comment: sampleMsgComment,
-						}),
-						MyDID:    myDID,
-						TheirDID: theirDID,
-					},
-				}, nil
-			},
-			HandleFunc: func(service.DIDCommMsg) (string, error) {
-				return thID, nil
-			},
-		}
-		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
-
-		store, err := mockctx.StorageProvider().OpenStore(connection.Namespace)
-		require.NoError(t, err)
-
-		record := &connection.Record{
-			ConnectionID: sampleConnID,
-			MyDID:        myDID,
-			TheirDID:     theirDID,
-		}
-		recordBytes, err := json.Marshal(record)
-		require.NoError(t, err)
-		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
-
-		cmd := New(mockctx, &Config{})
-
-		request := &ProposeCredentialRequest{
-			WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			Invitation: &wallet.GenericInvitation{},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.ProposeCredential(&b, getReader(t, &request))
-		require.NoError(t, cmdErr)
-
-		var response ProposeCredentialResponse
-		require.NoError(t, json.NewDecoder(&b).Decode(&response))
-		require.NotEmpty(t, response)
-		require.NotEmpty(t, response.OfferCredential)
-
-		offer := &issuecredentialsvc.OfferCredentialV2{}
-
-		err = response.OfferCredential.Decode(offer)
-		require.NoError(t, err)
-		require.NotEmpty(t, offer)
-		require.Equal(t, sampleMsgComment, offer.Comment)
-	})
-
-	t.Run("failed to send propose credential", func(t *testing.T) {
-		oobSvc := &mockoutofband.MockOobService{
-			AcceptInvitationHandle: func(*outofbandSvc.Invitation, outofbandSvc.Options) (string, error) {
-				return "", fmt.Errorf(sampleCommandError)
-			},
-		}
-
-		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
-
-		cmd := New(mockctx, &Config{})
-
-		request := &ConnectRequest{
-			WalletAuth: WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			Invitation: &outofbandClient.Invitation{},
-			ConnectOpts: ConnectOpts{
-				MyLabel: "sample-label",
-			},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.ProposeCredential(&b, getReader(t, &request))
-		require.Error(t, cmdErr)
-
-		validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, sampleCommandError)
-		validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, "failed to accept invitation")
-		require.Empty(t, b.Bytes())
-	})
-
-	t.Run("failed to send propose credential - invalid request", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		var b bytes.Buffer
-		cmdErr := cmd.ProposeCredential(&b, bytes.NewBufferString("--"))
-		require.Error(t, cmdErr)
-
-		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
-		require.Empty(t, b.Bytes())
-	})
-
-	t.Run("failed to send propose credential - invalid profile", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		request := &ConnectRequest{
-			WalletAuth: WalletAuth{UserID: sampleUserID, Auth: sampleFakeTkn},
-			Invitation: &outofbandClient.Invitation{},
-			ConnectOpts: ConnectOpts{
-				MyLabel: "sample-label",
-			},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.ProposeCredential(&b, getReader(t, &request))
-		require.Error(t, cmdErr)
-
-		validateError(t, cmdErr, command.ExecuteError, ProposeCredentialErrorCode, "failed to get VC wallet profile")
-		require.Empty(t, b.Bytes())
-	})
-}
-
-func TestCommand_RequestCredential(t *testing.T) {
-	const sampleDIDCommUser = "sample-didcomm-user03"
-
-	mockctx := newMockProvider(t)
-
-	createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{
-		UserID:             sampleDIDCommUser,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
-
-	token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{
-		UserID:             sampleDIDCommUser,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
-
-	defer lock()
-
-	t.Run("successfully request credential", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		request := &RequestCredentialRequest{
-			WalletAuth:   WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			ThreadID:     uuid.New().String(),
-			Presentation: json.RawMessage{},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.RequestCredential(&b, getReader(t, &request))
-		require.NoError(t, cmdErr)
-	})
-
-	t.Run("successfully request credential - wait for done", func(t *testing.T) {
-		thID := uuid.New().String()
-
-		icSvc := &mockissuecredential.MockIssueCredentialSvc{
-			RegisterMsgEventHandle: func(ch chan<- service.StateMsg) error {
-				ch <- service.StateMsg{
-					Type:    service.PostState,
-					StateID: "done",
-					Properties: &mockdidexchange.MockEventProperties{
-						Properties: map[string]interface{}{
-							webRedirectStatusKey: model.AckStatusOK,
-							webRedirectURLKey:    exampleWebRedirect,
-						},
-					},
-					Msg: &mockMsg{thID: thID},
-				}
-
-				return nil
-			},
-		}
-		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
-
-		cmd := New(mockctx, &Config{})
-
-		request := &RequestCredentialRequest{
-			WalletAuth:   WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			ThreadID:     thID,
-			Presentation: json.RawMessage{},
-			WaitForDone:  true,
-			Timeout:      600 * time.Millisecond,
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.RequestCredential(&b, getReader(t, &request))
-		require.NoError(t, cmdErr)
-
-		var response wallet.CredentialInteractionStatus
-		require.NoError(t, json.NewDecoder(&b).Decode(&response))
-		require.NotEmpty(t, response)
-		require.Equal(t, exampleWebRedirect, response.RedirectURL)
-		require.Equal(t, model.AckStatusOK, response.Status)
-	})
-
-	t.Run("failed to request credential", func(t *testing.T) {
-		icSvc := &mockissuecredential.MockIssueCredentialSvc{
-			ActionContinueFunc: func(string, ...issuecredentialsvc.Opt) error {
-				return fmt.Errorf(sampleCommandError)
-			},
-		}
-		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
-
-		cmd := New(mockctx, &Config{})
-
-		request := &RequestCredentialRequest{
-			WalletAuth:   WalletAuth{UserID: sampleDIDCommUser, Auth: token},
-			ThreadID:     uuid.New().String(),
-			Presentation: json.RawMessage{},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.RequestCredential(&b, getReader(t, &request))
-		validateError(t, cmdErr, command.ExecuteError, RequestCredentialErrorCode, sampleCommandError)
-		require.Empty(t, b.Bytes())
-	})
-
-	t.Run("failed to request credential - invalid request", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		var b bytes.Buffer
-		cmdErr := cmd.RequestCredential(&b, bytes.NewBufferString("--"))
-		require.Error(t, cmdErr)
-
-		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
-		require.Empty(t, b.Bytes())
-	})
-
-	t.Run("failed to request credential - invalid profile", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		request := &RequestCredentialRequest{
-			WalletAuth:   WalletAuth{UserID: sampleUserID, Auth: token},
-			ThreadID:     uuid.New().String(),
-			Presentation: json.RawMessage{},
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.RequestCredential(&b, getReader(t, &request))
-		require.Error(t, cmdErr)
-
-		validateError(t, cmdErr, command.ExecuteError, RequestCredentialErrorCode, "failed to get VC wallet profile")
-		require.Empty(t, b.Bytes())
-	})
-}
-
-func TestCommand_ResolveCredentialManifest(t *testing.T) {
-	const sampleUser1 = "sample-user-r01"
-
-	mockctx := newMockProvider(t)
-	mockctx.VDRegistryValue = getMockDIDKeyVDR()
-
-	createSampleUserProfile(t, mockctx, &CreateOrUpdateProfileRequest{
-		UserID:             sampleUser1,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
-
-	token, lock := unlockWallet(t, mockctx, &UnlockWalletRequest{
-		UserID:             sampleUser1,
-		LocalKMSPassphrase: samplePassPhrase,
-	})
-
-	defer lock()
-
-	addContent(t, mockctx, &AddContentRequest{
-		Content:     testdata.SampleUDCVC,
-		ContentType: "credential",
-		WalletAuth:  WalletAuth{UserID: sampleUser1, Auth: token},
-	})
-
-	t.Run("successfully resolve credential fulfillment", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		request := &ResolveCredentialManifestRequest{
-			WalletAuth:  WalletAuth{UserID: sampleUser1, Auth: token},
-			Manifest:    testdata.CredentialManifestMultipleVCs,
-			Fulfillment: testdata.CredentialFulfillmentWithMultipleVCs,
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, request))
-		require.NoError(t, cmdErr)
-
-		var response ResolveCredentialManifestResponse
-		require.NoError(t, json.NewDecoder(&b).Decode(&response))
-		require.NotEmpty(t, response)
-		require.Len(t, response.Resolved, 2)
-	})
-
-	t.Run("successfully resolve credential", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		request := &ResolveCredentialManifestRequest{
-			WalletAuth:   WalletAuth{UserID: sampleUser1, Auth: token},
-			Manifest:     testdata.CredentialManifestMultipleVCs,
-			Credential:   testdata.SampleUDCVC,
-			DescriptorID: "udc_output",
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request))
-		require.NoError(t, cmdErr)
-
-		var response ResolveCredentialManifestResponse
-		require.NoError(t, json.NewDecoder(&b).Decode(&response))
-		require.NotEmpty(t, response)
-		require.Len(t, response.Resolved, 1)
-	})
-
-	t.Run("successfully resolve credential ID", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		request := &ResolveCredentialManifestRequest{
-			WalletAuth:   WalletAuth{UserID: sampleUser1, Auth: token},
-			Manifest:     testdata.CredentialManifestMultipleVCs,
-			CredentialID: "http://example.edu/credentials/1872",
-			DescriptorID: "udc_output",
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request))
-		require.NoError(t, cmdErr)
-
-		var response ResolveCredentialManifestResponse
-		require.NoError(t, json.NewDecoder(&b).Decode(&response))
-		require.NotEmpty(t, response)
-		require.Len(t, response.Resolved, 1)
-	})
-
-	t.Run("failed to resolve - invalid requests", func(t *testing.T) {
-		cmd := New(mockctx, &Config{})
-
-		// missing manifest
-		request := &ResolveCredentialManifestRequest{
-			WalletAuth:   WalletAuth{UserID: sampleUser1, Auth: token},
-			Credential:   testdata.SampleUDCVC,
-			DescriptorID: "udc_output",
-		}
-
-		var b bytes.Buffer
-		cmdErr := cmd.ResolveCredentialManifest(&b, getReader(t, &request))
-		validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode,
-			"failed to read credential manifest")
-
-		// missing descriptor ID
-		request = &ResolveCredentialManifestRequest{
-			WalletAuth: WalletAuth{UserID: sampleUser1, Auth: token},
-			Manifest:   testdata.CredentialManifestMultipleVCs,
-			Credential: testdata.SampleUDCVC,
-		}
-
-		b.Reset()
-		cmdErr = cmd.ResolveCredentialManifest(&b, getReader(t, &request))
-		validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode,
-			"unable to find matching descriptor")
-
-		// missing resolve option
-		request = &ResolveCredentialManifestRequest{
-			WalletAuth: WalletAuth{UserID: sampleUser1, Auth: token},
-			Manifest:   testdata.CredentialManifestMultipleVCs,
-		}
-
-		b.Reset()
-		cmdErr = cmd.ResolveCredentialManifest(&b, getReader(t, &request))
-		validateError(t, cmdErr, command.ExecuteError, ResolveCredentialManifestErrorCode, "invalid option")
-
-		b.Reset()
-		cmdErr = cmd.ResolveCredentialManifest(&b, bytes.NewBufferString("=="))
-		validateError(t, cmdErr, command.ValidationError, InvalidRequestErrorCode, "invalid character")
-	})
-}
-
-func createSampleUserProfile(t *testing.T, ctx *mockprovider.Provider, request *CreateOrUpdateProfileRequest) {
-	cmd := New(ctx, &Config{})
-	require.NotNil(t, cmd)
-
-	var l bytes.Buffer
-	cmdErr := cmd.CreateProfile(&l, getReader(t, request))
-	require.NoError(t, cmdErr)
-}
-
-func getReader(t *testing.T, v interface{}) io.Reader {
-	vcReqBytes, err := json.Marshal(v)
-	require.NoError(t, err)
-
-	return bytes.NewBuffer(vcReqBytes)
-}
-
-func getUnlockToken(t *testing.T, b bytes.Buffer) string {
-	var response UnlockWalletResponse
-
-	require.NoError(t, json.NewDecoder(&b).Decode(&response))
-
-	return response.Token
-}
-
-func unlockWallet(t *testing.T, ctx *mockprovider.Provider, request *UnlockWalletRequest) (string, func()) {
-	cmd := New(ctx, nil)
-
-	var b bytes.Buffer
-
-	cmdErr := cmd.Open(&b, getReader(t, &request))
-	require.NoError(t, cmdErr)
-
-	return getUnlockToken(t, b), func() {
-		cmdErr = cmd.Close(&b, getReader(t, &LockWalletRequest{UserID: request.UserID}))
-		if cmdErr != nil {
-			t.Log(t, cmdErr)
-		}
-	}
-}
-
-func addContent(t *testing.T, ctx *mockprovider.Provider, request *AddContentRequest) {
-	cmd := New(ctx, &Config{})
-
-	var b bytes.Buffer
-	defer b.Reset()
-
-	cmdErr := cmd.Add(&b, getReader(t, &request))
-	require.NoError(t, cmdErr)
-}
-
-func validateError(t *testing.T, err command.Error,
-	expectedType command.Type, expectedCode command.Code, contains string) {
-	require.Error(t, err)
-	require.Equal(t, err.Type(), expectedType)
-	require.Equal(t, err.Code(), expectedCode)
-
-	if contains != "" {
-		require.Contains(t, err.Error(), contains)
-	}
-}
-
-func newMockProvider(t *testing.T) *mockprovider.Provider {
-	t.Helper()
-
-	loader, err := ldtestutil.DocumentLoader()
-	require.NoError(t, err)
-
-	serviceMap := map[string]interface{}{
-		presentproofSvc.Name:    &mockpresentproof.MockPresentProofSvc{},
-		outofbandSvc.Name:       &mockoutofband.MockOobService{},
-		didexchange.DIDExchange: &mockdidexchange.MockDIDExchangeSvc{},
-		mediator.Coordination:   &mockmediator.MockMediatorSvc{},
-		issuecredentialsvc.Name: &mockissuecredential.MockIssueCredentialSvc{},
-		oobv2.Name:              &mockoutofbandv2.MockOobService{},
-	}
-
-	return &mockprovider.Provider{
-		StorageProviderValue:              mockstorage.NewMockStoreProvider(),
-		ProtocolStateStorageProviderValue: mockstorage.NewMockStoreProvider(),
-		DocumentLoaderValue:               loader,
-		ServiceMap:                        serviceMap,
-	}
-}
-
-func getMockDIDKeyVDR() *mockvdr.MockVDRegistry {
-	return &mockvdr.MockVDRegistry{
-		ResolveFunc: func(didID string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
-			if strings.HasPrefix(didID, "did:key:") {
-				k := key.New()
-
-				d, e := k.Read(didID)
-				if e != nil {
-					return nil, e
-				}
-
-				return d, nil
-			}
-
-			return nil, fmt.Errorf("did not found")
-		},
-	}
-}
-
-func parseCredential(t *testing.T, b bytes.Buffer) *verifiable.Credential {
-	var response struct {
-		Credential json.RawMessage
-	}
-
-	require.NoError(t, json.NewDecoder(&b).Decode(&response))
-
-	loader, err := ldtestutil.DocumentLoader()
-	require.NoError(t, err)
-
-	vc, err := verifiable.ParseCredential(response.Credential, verifiable.WithDisabledProofCheck(),
-		verifiable.WithJSONLDDocumentLoader(loader))
-	require.NoError(t, err)
-
-	return vc
-}
+	return vc
+}
 
 func parsePresentation(t *testing.T, b bytes.Buffer) *verifiable.Presentation {
 	var response struct {
@@ -2735,17 +1921,3 @@ type mockHeaderSigner struct{}
 func (s *mockHeaderSigner) SignHeader(req *http.Request, capabilityBytes []byte) (*http.Header, error) {
 	return &http.Header{}, nil
 }
-
-// mockMsg containing custom parent thread ID.
-type mockMsg struct {
-	*service.DIDCommMsgMap
-	thID string
-}
-
-func (m *mockMsg) ParentThreadID() string {
-	return m.thID
-}
-
-func (m *mockMsg) ThreadID() (string, error) {
-	return m.thID, nil
-}
diff --git a/pkg/controller/command/vcwallet/models.go b/pkg/controller/command/vcwallet/models.go
index 030948d0b..b8011e385 100644
--- a/pkg/controller/command/vcwallet/models.go
+++ b/pkg/controller/command/vcwallet/models.go
@@ -10,9 +10,6 @@ import (
 	"encoding/json"
 	"time"
 
-	"github.com/hyperledger/aries-framework-go/pkg/client/outofband"
-	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
-	"github.com/hyperledger/aries-framework-go/pkg/doc/cm"
 	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
 	"github.com/hyperledger/aries-framework-go/pkg/kms"
 	"github.com/hyperledger/aries-framework-go/pkg/wallet"
@@ -313,163 +310,3 @@ type CreateKeyPairRequest struct {
 type CreateKeyPairResponse struct {
 	*wallet.KeyPair
 }
-
-// ConnectRequest is request model for wallet DID connect operation.
-type ConnectRequest struct {
-	WalletAuth
-
-	// out-of-band invitation to establish connection.
-	Invitation *outofband.Invitation `json:"invitation"`
-
-	ConnectOpts
-}
-
-// ConnectOpts is option for accepting out-of-band invitation and to perform DID exchange.
-type ConnectOpts struct {
-	// Label to be shared with the other agent during the subsequent DID exchange.
-	MyLabel string `json:"myLabel,omitempty"`
-
-	// router connections to be used to establish connection.
-	RouterConnections []string `json:"routerConnections,omitempty"`
-
-	// DID to be used when reusing a connection.
-	ReuseConnection string `json:"reuseConnection,omitempty"`
-
-	// To use any recognized DID in the services array for a reusable connection.
-	ReuseAnyConnection bool `json:"reuseAnyConnection,omitempty"`
-
-	// Timeout (in milliseconds) waiting for connection status to be completed.
-	Timeout time.Duration `json:"timeout,omitempty"`
-}
-
-// ConnectResponse is response model from wallet DID connection operation.
-type ConnectResponse struct {
-	// connection ID of the connection established.
-	ConnectionID string `json:"connectionID"`
-}
-
-// ProposePresentationRequest is request model for performing propose presentation operation from wallet.
-type ProposePresentationRequest struct {
-	WalletAuth
-
-	// out-of-band invitation to establish connection and send propose presentation message.
-	Invitation *wallet.GenericInvitation `json:"invitation"`
-
-	// Optional From DID option to customize sender DID.
-	FromDID string `json:"from,omitempty"`
-
-	// Timeout (in milliseconds) waiting for operation to be completed.
-	Timeout time.Duration `json:"timeout,omitempty"`
-
-	// Options for accepting out-of-band invitation and to perform DID exchange (for DIDComm V1).
-	ConnectionOpts ConnectOpts `json:"connectOptions,omitempty"`
-}
-
-// ProposePresentationResponse is response model from wallet propose presentation operation.
-type ProposePresentationResponse struct {
-	// response request presentation message from  relying party.
-	PresentationRequest *service.DIDCommMsgMap `json:"presentationRequest,omitempty"`
-}
-
-// PresentProofRequest is request model from wallet present proof operation.
-// Supported attachment MIME type "application/ld+json".
-type PresentProofRequest struct {
-	WalletAuth
-
-	// Thread ID from request presentation response
-	ThreadID string `json:"threadID,omitempty"`
-
-	// presentation to be sent as part of present proof message.
-	Presentation json.RawMessage `json:"presentation,omitempty"`
-
-	// If true then wallet will wait for present proof protocol status to be
-	// done or abandoned till given Timeout.
-	// Also, will return web redirect info if found in acknowledgment message or problem-report.
-	WaitForDone bool `json:"waitForDone,omitempty"`
-
-	// Optional timeout (in milliseconds) waiting for present proof operation to be done.
-	// will be taken into account only when WaitForDone is enabled.
-	// If not provided then wallet will use its default timeout.
-	Timeout time.Duration `json:"WaitForDoneTimeout,omitempty"`
-}
-
-// PresentProofResponse is response model from wallet present proof operation.
-type PresentProofResponse struct {
-	wallet.CredentialInteractionStatus
-}
-
-// ProposeCredentialRequest is request model for performing propose credential operation from wallet.
-type ProposeCredentialRequest struct {
-	WalletAuth
-
-	// out-of-band invitation to establish connection and send propose credential message.
-	Invitation *wallet.GenericInvitation `json:"invitation"`
-
-	// Optional From DID option to customize sender DID.
-	FromDID string `json:"from,omitempty"`
-
-	// Timeout (in milliseconds) waiting for operation to be completed.
-	Timeout time.Duration `json:"timeout,omitempty"`
-
-	// Options for accepting out-of-band invitation and to perform DID exchange (for DIDComm V1).
-	ConnectionOpts ConnectOpts `json:"connectOptions,omitempty"`
-}
-
-// ProposeCredentialResponse is response model from wallet propose credential operation.
-type ProposeCredentialResponse struct {
-	// response offer credential message from issuer.
-	OfferCredential *service.DIDCommMsgMap `json:"offerCredential,omitempty"`
-}
-
-// RequestCredentialRequest is request model from wallet request credential operation.
-// Supported attachment MIME type "application/ld+json".
-type RequestCredentialRequest struct {
-	WalletAuth
-
-	// Thread ID from offer credential response previously received during propose credential interaction.
-	ThreadID string `json:"threadID,omitempty"`
-
-	// presentation to be sent as part of request credential message.
-	Presentation json.RawMessage `json:"presentation,omitempty"`
-
-	// If true then wallet will wait till it receives credential fulfillment response from issuer for given Timeout.
-	// Also, will return web redirect info if found in fulfillment message or problem-report.
-	WaitForDone bool `json:"waitForDone,omitempty"`
-
-	// Optional timeout (in milliseconds) waiting for credential fulfillment to arrive.
-	// will be taken into account only when WaitForDone is enabled.
-	// If not provided then wallet will use its default timeout.
-	Timeout time.Duration `json:"WaitForDoneTimeout,omitempty"`
-}
-
-// RequestCredentialResponse is response model from wallet request credential operation.
-type RequestCredentialResponse struct {
-	wallet.CredentialInteractionStatus
-}
-
-// ResolveCredentialManifestRequest is request model for resolving credential manifest from wallet.
-type ResolveCredentialManifestRequest struct {
-	WalletAuth
-
-	// Credential Manifest on which given credential fulfillment or credential needs to be resolved.
-	Manifest json.RawMessage `json:"manifest,omitempty"`
-
-	// Fulfillment to be be resolved.
-	// If provided, then this option takes precedence over credential resolve option.
-	Fulfillment json.RawMessage `json:"fulfillment,omitempty"`
-
-	// Credential to be be resolved, to be provided along with 'DescriptorID' to be used for resolving.
-	Credential json.RawMessage `json:"credential,omitempty"`
-
-	// ID of the Credential from wallet content to be be resolved, to be provided along with 'DescriptorID'.
-	CredentialID string `json:"credentialID,omitempty"`
-
-	// ID of the output descriptor to be used for resolving given credential.
-	DescriptorID string `json:"descriptorID,omitempty"`
-}
-
-// ResolveCredentialManifestResponse is response model from wallet credential manifest resolve operation.
-type ResolveCredentialManifestResponse struct {
-	// List of Resolved Descriptor results.
-	Resolved []*cm.ResolvedDescriptor `json:"resolved,omitempty"`
-}
diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go
index 8e0d001ed..6fc45ff51 100644
--- a/pkg/controller/controller_test.go
+++ b/pkg/controller/controller_test.go
@@ -13,7 +13,7 @@ import (
 
 	"github.com/stretchr/testify/require"
 
-	"github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet"
+	"github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet"
 	"github.com/hyperledger/aries-framework-go/pkg/controller/internal/mocks/webhook"
 	"github.com/hyperledger/aries-framework-go/pkg/framework/aries"
 	"github.com/hyperledger/aries-framework-go/pkg/framework/aries/api"
@@ -159,7 +159,7 @@ func TestWithMessageHandler(t *testing.T) {
 func TestWithWalletConfiguration(t *testing.T) {
 	controllerOpts := &allOpts{}
 
-	opt := WithWalletConfiguration(&vcwallet.Config{WebKMSCacheSize: 99})
+	opt := WithWalletConfiguration(&didcommwallet.Config{WebKMSCacheSize: 99})
 
 	opt(controllerOpts)
 
diff --git a/pkg/controller/rest/vcwallet/models.go b/pkg/controller/rest/vcwallet/models.go
index 7df30891a..29441d1e7 100644
--- a/pkg/controller/rest/vcwallet/models.go
+++ b/pkg/controller/rest/vcwallet/models.go
@@ -9,6 +9,7 @@ package vcwallet
 import (
 	"encoding/json"
 
+	"github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet"
 	"github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet"
 	"github.com/hyperledger/aries-framework-go/pkg/wallet"
 )
@@ -270,7 +271,7 @@ type connectRequest struct { // nolint: unused,deadcode
 	// Params for connecting to wallet for DIDComm.
 	//
 	// in: body
-	Params *vcwallet.ConnectRequest
+	Params *didcommwallet.ConnectRequest
 }
 
 // connectResponse is response model from wallet DID connect operation.
@@ -280,7 +281,7 @@ type connectResponse struct {
 	// wallet connect response.
 	//
 	// in: body
-	Response *vcwallet.ConnectResponse `json:"response"`
+	Response *didcommwallet.ConnectResponse `json:"response"`
 }
 
 // proposePresentationRequest is request model for performing propose presentation operation from wallet.
@@ -290,7 +291,7 @@ type proposePresentationRequest struct { // nolint: unused,deadcode
 	// Params for proposing presentation from wallet.
 	//
 	// in: body
-	Params *vcwallet.ProposePresentationRequest
+	Params *didcommwallet.ProposePresentationRequest
 }
 
 // proposePresentationResponse is response model from wallet propose presentation operation.
@@ -300,7 +301,7 @@ type proposePresentationResponse struct {
 	// response containing request presentation message from relyinig party.
 	//
 	// in: body
-	Response *vcwallet.ProposePresentationResponse `json:"response"`
+	Response *didcommwallet.ProposePresentationResponse `json:"response"`
 }
 
 // presentProofRequest is request model for performing present proof operation from wallet.
@@ -310,7 +311,7 @@ type presentProofRequest struct { // nolint: unused,deadcode
 	// Params for accepting presentation request and sending present proof message to relying party.
 	//
 	// in: body
-	Params *vcwallet.PresentProofRequest
+	Params *didcommwallet.PresentProofRequest
 }
 
 // presentProofResponse is response model from wallet present proof operation.
@@ -331,7 +332,7 @@ type proposeCredentialRequest struct { // nolint: unused,deadcode
 	// Params for proposing credential from wallet.
 	//
 	// in: body
-	Params *vcwallet.ProposeCredentialRequest
+	Params *didcommwallet.ProposeCredentialRequest
 }
 
 // proposePresentationResponse is response model from wallet propose credential operation.
@@ -341,7 +342,7 @@ type proposeCredentialResponse struct {
 	// response containing offer credential response from issuer.
 	//
 	// in: body
-	Response *vcwallet.ProposeCredentialResponse `json:"response"`
+	Response *didcommwallet.ProposeCredentialResponse `json:"response"`
 }
 
 // requestCredentialRequest is request model for performing request credential operation from wallet to conclude
@@ -352,7 +353,7 @@ type requestCredentialRequest struct { // nolint: unused,deadcode
 	// Params for sending request credential message from wallet and optionally wait for credential fulfillment.
 	//
 	// in: body
-	Params *vcwallet.RequestCredentialRequest
+	Params *didcommwallet.RequestCredentialRequest
 }
 
 // requestCredentialResponse is response model from wallet request credential operation which may contain
@@ -378,7 +379,7 @@ type resolveCredentialManifestRequest struct { // nolint: unused,deadcode
 	// Params for resolving credential manifests from wallet.
 	//
 	// in: body
-	Params *vcwallet.ResolveCredentialManifestRequest
+	Params *didcommwallet.ResolveCredentialManifestRequest
 }
 
 // resolveCredentialManifestResponse is response model for resolving credential manifests from wallet.
@@ -388,5 +389,5 @@ type resolveCredentialManifestResponse struct {
 	// Response containing resolve credential manifest descriptors.
 	//
 	// in: body
-	Response *vcwallet.ResolveCredentialManifestResponse `json:"response"`
+	Response *didcommwallet.ResolveCredentialManifestResponse `json:"response"`
 }
diff --git a/pkg/controller/rest/vcwallet/operation.go b/pkg/controller/rest/vcwallet/operation.go
index ee2a56117..4a0600a88 100644
--- a/pkg/controller/rest/vcwallet/operation.go
+++ b/pkg/controller/rest/vcwallet/operation.go
@@ -14,6 +14,7 @@ import (
 	"github.com/gorilla/mux"
 	"github.com/piprate/json-gold/ld"
 
+	"github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet"
 	"github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet"
 	"github.com/hyperledger/aries-framework-go/pkg/controller/internal/cmdutil"
 	"github.com/hyperledger/aries-framework-go/pkg/controller/rest"
@@ -77,13 +78,12 @@ type didCommProvider interface {
 // Operation contains REST operations provided by verifiable credential wallet.
 type Operation struct {
 	handlers []rest.Handler
-	command  *vcwallet.Command
+	command  *didcommwallet.Command
 }
 
 // New returns new verfiable credential wallet REST controller.
 func New(p provider, config *vcwallet.Config) *Operation {
-	cmd := vcwallet.New(p, config)
-
+	cmd := didcommwallet.New(p, config)
 	o := &Operation{command: cmd}
 
 	o.registerHandler()
diff --git a/pkg/controller/rest/vcwallet/operation_test.go b/pkg/controller/rest/vcwallet/operation_test.go
index e711916c0..e87dd9f36 100644
--- a/pkg/controller/rest/vcwallet/operation_test.go
+++ b/pkg/controller/rest/vcwallet/operation_test.go
@@ -24,6 +24,7 @@ import (
 
 	"github.com/hyperledger/aries-framework-go/internal/testdata"
 	outofbandClient "github.com/hyperledger/aries-framework-go/pkg/client/outofband"
+	"github.com/hyperledger/aries-framework-go/pkg/controller/command/didcommwallet"
 	"github.com/hyperledger/aries-framework-go/pkg/controller/command/vcwallet"
 	"github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto"
 	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model"
@@ -1253,10 +1254,10 @@ func TestOperation_Connect(t *testing.T) {
 		}
 		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
 
-		request := &vcwallet.ConnectRequest{
+		request := &didcommwallet.ConnectRequest{
 			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			Invitation: &outofbandClient.Invitation{},
-			ConnectOpts: vcwallet.ConnectOpts{
+			ConnectOpts: didcommwallet.ConnectOpts{
 				MyLabel: "sample-label",
 			},
 		}
@@ -1283,10 +1284,10 @@ func TestOperation_Connect(t *testing.T) {
 		}
 		mockctx.ServiceMap[didexchange.DIDExchange] = didexSvc
 
-		request := &vcwallet.ConnectRequest{
+		request := &didcommwallet.ConnectRequest{
 			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			Invitation: &outofbandClient.Invitation{},
-			ConnectOpts: vcwallet.ConnectOpts{
+			ConnectOpts: didcommwallet.ConnectOpts{
 				MyLabel: "sample-label",
 			},
 		}
@@ -1379,7 +1380,7 @@ func TestOperation_ProposePresentation(t *testing.T) {
 		require.NoError(t, err)
 		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
 
-		request := &vcwallet.ProposePresentationRequest{
+		request := &didcommwallet.ProposePresentationRequest{
 			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			Invitation: &wallet.GenericInvitation{},
 		}
@@ -1407,7 +1408,7 @@ func TestOperation_ProposePresentation(t *testing.T) {
 
 		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
 
-		request := &vcwallet.ProposePresentationRequest{
+		request := &didcommwallet.ProposePresentationRequest{
 			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			Invitation: &wallet.GenericInvitation{},
 		}
@@ -1460,7 +1461,7 @@ func TestOperation_PresentProof(t *testing.T) {
 		}
 		mockctx.ServiceMap[presentproofSvc.Name] = mockPresentProofSvc
 
-		request := &vcwallet.PresentProofRequest{
+		request := &didcommwallet.PresentProofRequest{
 			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			ThreadID:     thID,
 			Presentation: json.RawMessage{},
@@ -1484,7 +1485,7 @@ func TestOperation_PresentProof(t *testing.T) {
 	})
 
 	t.Run("wallet present proof success", func(t *testing.T) {
-		request := &vcwallet.PresentProofRequest{
+		request := &didcommwallet.PresentProofRequest{
 			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			ThreadID:     uuid.New().String(),
 			Presentation: json.RawMessage{},
@@ -1507,7 +1508,7 @@ func TestOperation_PresentProof(t *testing.T) {
 
 		mockctx.ServiceMap[presentproofSvc.Name] = ppSvc
 
-		request := &vcwallet.PresentProofRequest{
+		request := &didcommwallet.PresentProofRequest{
 			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			ThreadID:     uuid.New().String(),
 			Presentation: json.RawMessage{},
@@ -1599,7 +1600,7 @@ func TestOperation_ProposeCredential(t *testing.T) {
 		require.NoError(t, err)
 		require.NoError(t, store.Put(fmt.Sprintf("conn_%s", sampleConnID), recordBytes))
 
-		request := &vcwallet.ProposeCredentialRequest{
+		request := &didcommwallet.ProposeCredentialRequest{
 			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			Invitation: &wallet.GenericInvitation{},
 		}
@@ -1634,7 +1635,7 @@ func TestOperation_ProposeCredential(t *testing.T) {
 
 		mockctx.ServiceMap[outofbandSvc.Name] = oobSvc
 
-		request := &vcwallet.ProposeCredentialRequest{
+		request := &didcommwallet.ProposeCredentialRequest{
 			WalletAuth: vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			Invitation: &wallet.GenericInvitation{},
 		}
@@ -1690,7 +1691,7 @@ func TestOperation_RequestCredential(t *testing.T) {
 		}
 		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
 
-		request := &vcwallet.RequestCredentialRequest{
+		request := &didcommwallet.RequestCredentialRequest{
 			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			ThreadID:     thID,
 			Presentation: json.RawMessage{},
@@ -1714,7 +1715,7 @@ func TestOperation_RequestCredential(t *testing.T) {
 	})
 
 	t.Run("wallet request credential success", func(t *testing.T) {
-		request := &vcwallet.RequestCredentialRequest{
+		request := &didcommwallet.RequestCredentialRequest{
 			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			ThreadID:     uuid.New().String(),
 			Presentation: json.RawMessage{},
@@ -1736,7 +1737,7 @@ func TestOperation_RequestCredential(t *testing.T) {
 		}
 		mockctx.ServiceMap[issuecredentialsvc.Name] = icSvc
 
-		request := &vcwallet.PresentProofRequest{
+		request := &didcommwallet.PresentProofRequest{
 			WalletAuth:   vcwallet.WalletAuth{UserID: sampleDIDCommUser, Auth: token},
 			ThreadID:     uuid.New().String(),
 			Presentation: json.RawMessage{},
@@ -1771,7 +1772,7 @@ func TestOperation_ResolveCredentialManifest(t *testing.T) {
 	defer lock()
 
 	t.Run("resolve credential manifest", func(t *testing.T) {
-		request := &vcwallet.ResolveCredentialManifestRequest{
+		request := &didcommwallet.ResolveCredentialManifestRequest{
 			WalletAuth:  vcwallet.WalletAuth{UserID: sampleUser1, Auth: token},
 			Manifest:    testdata.CredentialManifestMultipleVCs,
 			Fulfillment: testdata.CredentialFulfillmentWithMultipleVCs,
@@ -1792,7 +1793,7 @@ func TestOperation_ResolveCredentialManifest(t *testing.T) {
 	})
 
 	t.Run("resolve credential manifest failure", func(t *testing.T) {
-		request := &vcwallet.ResolveCredentialManifestRequest{
+		request := &didcommwallet.ResolveCredentialManifestRequest{
 			WalletAuth: vcwallet.WalletAuth{UserID: sampleUser1, Auth: token},
 			Manifest:   testdata.CredentialManifestMultipleVCs,
 			Credential: testdata.SampleUDCVC,