diff --git a/cmd/neofs-adm/internal/modules/morph/deploy.go b/cmd/neofs-adm/internal/modules/morph/deploy.go
index f9c976cc9..81b6099e5 100644
--- a/cmd/neofs-adm/internal/modules/morph/deploy.go
+++ b/cmd/neofs-adm/internal/modules/morph/deploy.go
@@ -1,13 +1,16 @@
 package morph
 
 import (
+	"encoding/json"
 	"fmt"
 	"os"
 	"strings"
 
+	"github.com/nspcc-dev/neo-go/cli/cmdargs"
 	"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
 	"github.com/nspcc-dev/neo-go/pkg/core/state"
 	"github.com/nspcc-dev/neo-go/pkg/io"
+	"github.com/nspcc-dev/neo-go/pkg/services/rpcsrv/params"
 	"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
 	"github.com/nspcc-dev/neo-go/pkg/vm/emit"
 	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
@@ -24,7 +27,9 @@ var deployCmd = &cobra.Command{
 	Use:   "deploy",
 	Short: "Deploy additional smart-contracts",
 	Long: `Deploy additional smart-contract which are not related to core.
-All contracts are deployed by the committee, so access to the alphabet wallets is required.`,
+All contracts are deployed by the committee, so access to the alphabet wallets is required.
+Optionally, arguments can be provided to be passed to a contract's _deploy function.
+The syntax is the same as for 'neo-go contract testinvokefunction' command.`,
 	PreRun: func(cmd *cobra.Command, _ []string) {
 		_ = viper.BindPFlag(alphabetWalletsFlag, cmd.Flags().Lookup(alphabetWalletsFlag))
 		_ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag))
@@ -45,7 +50,7 @@ func init() {
 	ff.Bool(updateFlag, false, "Update an existing contract")
 }
 
-func deployContractCmd(cmd *cobra.Command, _ []string) error {
+func deployContractCmd(cmd *cobra.Command, args []string) error {
 	v := viper.GetViper()
 	c, err := newInitializeContext(cmd, v)
 	if err != nil {
@@ -92,10 +97,15 @@ func deployContractCmd(cmd *cobra.Command, _ []string) error {
 		return fmt.Errorf("can't sign manifest group: %v", err)
 	}
 
-	params := getContractDeployParameters(cs, nil)
-
 	w := io.NewBufBinWriter()
-	emit.AppCall(w.BinWriter, callHash, method, callflag.All, params...)
+	if err := emitDeploymentArguments(w.BinWriter, args); err != nil {
+		return err
+	}
+	emit.Bytes(w.BinWriter, cs.RawManifest)
+	emit.Bytes(w.BinWriter, cs.RawNEF)
+	emit.Int(w.BinWriter, 3)
+	emit.Opcodes(w.BinWriter, opcode.PACK)
+	emit.AppCallNoArgs(w.BinWriter, callHash, method, callflag.All)
 	emit.Opcodes(w.BinWriter, opcode.DROP) // contract state on stack
 	if !isUpdate {
 		s, err := c.nnsRegisterDomainScript(nnsCs.Hash, cs.Hash, domain)
@@ -118,6 +128,35 @@ func deployContractCmd(cmd *cobra.Command, _ []string) error {
 	return c.awaitTx()
 }
 
+func emitDeploymentArguments(w *io.BinWriter, args []string) error {
+	_, ps, err := cmdargs.ParseParams(args, true)
+	if err != nil {
+		return err
+	}
+
+	if len(ps) == 0 {
+		emit.Opcodes(w, opcode.NEWARRAY0)
+		return nil
+	}
+
+	if len(ps) != 1 {
+		return fmt.Errorf("at most one argument is expected for deploy, got %d", len(ps))
+	}
+
+	// We could emit this directly, but round-trip through JSON is more robust.
+	// This a CLI, so optimizing the conversion is not worth the effort.
+	data, err := json.Marshal(ps)
+	if err != nil {
+		return err
+	}
+
+	var pp params.Params
+	if err := json.Unmarshal(data, &pp); err != nil {
+		return err
+	}
+	return params.ExpandArrayIntoScript(w, pp)
+}
+
 func probeContractName(ctrPath string) (string, error) {
 	ds, err := os.ReadDir(ctrPath)
 	if err != nil {