Repository: onflow/flow-cli
Pull request #1457 (2024-03-15 17:46:07)

Merge `master` into `feature/stable-cadence`


Patch:
diff --git a/go.mod b/go.mod
index 8361c0ef..476f4843 100644
--- a/go.mod
+++ b/go.mod
@@ -28,14 +28,14 @@ require (
 	github.com/pkg/errors v0.9.1
 	github.com/psiemens/sconfig v0.1.0
 	github.com/radovskyb/watcher v1.0.7
-	github.com/rs/zerolog v1.31.0
+	github.com/rs/zerolog v1.32.0
 	github.com/sergi/go-diff v1.3.1
 	github.com/spf13/afero v1.10.0
 	github.com/spf13/cobra v1.8.0
 	github.com/spf13/viper v1.16.0
 	github.com/stretchr/testify v1.8.4
 	golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
-	google.golang.org/grpc v1.61.0
+	google.golang.org/grpc v1.62.0
 )
 
 require (
@@ -105,7 +105,7 @@ require (
 	github.com/goccy/go-json v0.10.2 // indirect
 	github.com/gofrs/flock v0.8.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang/glog v1.1.2 // indirect
+	github.com/golang/glog v1.2.0 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
@@ -263,9 +263,9 @@ require (
 	gonum.org/v1/gonum v0.14.0 // indirect
 	google.golang.org/api v0.151.0 // indirect
 	google.golang.org/appengine v1.6.8 // indirect
-	google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect
+	google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
 	google.golang.org/protobuf v1.32.0 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
diff --git a/go.sum b/go.sum
index cd8bed45..ac0f7b6f 100644
--- a/go.sum
+++ b/go.sum
@@ -44,7 +44,7 @@ cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5x
 cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
 cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
 cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk=
-cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM=
+cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
 cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4=
 cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw=
 cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E=
@@ -1467,8 +1467,9 @@ github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgR
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
 github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
-github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
 github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
+github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
+github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -2272,8 +2273,8 @@ github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM=
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
-github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
-github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
+github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
+github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
 github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -3321,8 +3322,8 @@ google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9Rm
 google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk=
 google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:EMfReVxb80Dq1hhioy0sOsY9jCE46YDgHlJ7fWVUWRE=
 google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI=
-google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg=
-google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0=
+google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
+google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
 google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8=
 google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
 google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
@@ -3338,8 +3339,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.
 google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0=
 google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww=
 google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870=
-google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 h1:OPXtXn7fNMaXwO3JvOmF1QyTc00jsSFFz1vXXBOdCDo=
-google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg=
+google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU=
+google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
 google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA=
 google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c=
 google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405 h1:o4S3HvTUEXgRsNSUQsALDVog0O9F/U1JJlHmmUN8Uas=
@@ -3360,8 +3361,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.
 google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
 google.golang.org/grpc v0.0.0-20170208002647-2a6bf6142e96/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
@@ -3418,8 +3419,8 @@ google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt
 google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
 google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
 google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
-google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
-google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
+google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
+google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
diff --git a/internal/super/dev.go b/internal/super/dev.go
index 7ab0f32c..5f4df414 100644
--- a/internal/super/dev.go
+++ b/internal/super/dev.go
@@ -19,21 +19,27 @@
 package super
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"os"
+	"os/signal"
 	"strings"
+	"syscall"
 
 	"github.com/onflow/cadence/runtime/parser"
-	"github.com/spf13/cobra"
-
+	"github.com/onflow/flow-emulator/server"
 	"github.com/onflow/flowkit/v2"
 	"github.com/onflow/flowkit/v2/output"
+	"github.com/rs/zerolog"
+	"github.com/spf13/cobra"
 
 	"github.com/onflow/flow-cli/internal/command"
 )
 
-type flagsDev struct{}
+type flagsDev struct {
+	StartEmulator bool `default:"false" flag:"start-emulator" info:"Start emulator if not already running"`
+}
 
 var devFlags = flagsDev{}
 
@@ -61,22 +67,60 @@ func dev(
 		return nil, err
 	}
 
-	err = flow.Ping()
+	serviceAccount, err := state.EmulatorServiceAccount()
 	if err != nil {
-		logger.Error("Error connecting to emulator. Make sure you started an emulator using 'flow emulator' command.")
-		logger.Info(fmt.Sprintf("%s This tool requires emulator to function. Emulator needs to be run inside the project root folder where the configuration file ('flow.json') exists.\n\n", output.TryEmoji()))
-		return nil, nil
+		return nil, err
 	}
 
-	service, err := state.EmulatorServiceAccount()
-	if err != nil {
-		return nil, err
+	if devFlags.StartEmulator {
+		privateKey, err := serviceAccount.Key.PrivateKey()
+		if err != nil {
+			return nil, fmt.Errorf("failed to get private key: %s", err)
+		}
+
+		consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}
+		log := zerolog.New(consoleWriter).With().Timestamp().Logger()
+
+		emulatorServer := server.NewEmulatorServer(&log, &server.Config{
+			ServicePublicKey:   (*privateKey).PublicKey(),
+			ServicePrivateKey:  *privateKey,
+			ServiceKeySigAlgo:  serviceAccount.Key.SigAlgo(),
+			ServiceKeyHashAlgo: serviceAccount.Key.HashAlgo(),
+		})
+
+		emuErr := emulatorServer.Listen()
+		if emuErr != nil {
+			return nil, fmt.Errorf("failed to prepare the emulator for connections: %s", emuErr)
+		}
+
+		c := make(chan os.Signal, 1)
+		signal.Notify(c, os.Interrupt, syscall.SIGTERM)
+		go func() {
+			emulatorServer.Start()
+			<-c
+			os.Exit(1)
+		}()
+
+		ctx := context.Background()
+		err = flow.WaitServer(ctx)
+		if err != nil {
+			logger.Error("Error connecting to emulator. Make sure you started an emulator using 'flow emulator' command.")
+			logger.Info(fmt.Sprintf("%s This tool requires emulator to function. Emulator needs to be run inside the project root folder where the configuration file ('flow.json') exists.\n\n", output.TryEmoji()))
+			return nil, err
+		}
+	} else {
+		err = flow.Ping()
+		if err != nil {
+			logger.Error("Error connecting to emulator. Make sure you started an emulator using 'flow emulator' command.")
+			logger.Info(fmt.Sprintf("%s This tool requires emulator to function. Emulator needs to be run inside the project root folder where the configuration file ('flow.json') exists.\n\n", output.TryEmoji()))
+			return nil, err
+		}
 	}
 
 	flow.SetLogger(output.NewStdoutLogger(output.NoneLog))
 
 	project, err := newProject(
-		*service,
+		*serviceAccount,
 		flow,
 		state,
 		newProjectFiles(dir),
diff --git a/internal/super/generate.go b/internal/super/generate.go
index e045ac8d..8324d5cb 100644
--- a/internal/super/generate.go
+++ b/internal/super/generate.go
@@ -19,9 +19,14 @@
 package super
 
 import (
+	"bytes"
+	"embed"
 	"fmt"
 	"path/filepath"
 	"strings"
+	"text/template"
+
+	flowsdk "github.com/onflow/flow-go-sdk"
 
 	"github.com/onflow/flowkit/v2/config"
 
@@ -34,8 +39,12 @@ import (
 	"github.com/spf13/cobra"
 )
 
+//go:embed templates/*.tmpl
+var templatesFS embed.FS
+
 type generateFlagsDef struct {
 	Directory string `default:"" flag:"dir" info:"Directory to generate files in"`
+	SkipTests bool   `default:"false" flag:"skip-tests" info:"Skip generating test files"`
 }
 
 var generateFlags = generateFlagsDef{}
@@ -116,6 +125,39 @@ func generateScript(
 	return generateNew(args, "script", logger, state)
 }
 
+func addCDCExtension(name string) string {
+	if strings.HasSuffix(name, ".cdc") {
+		return name
+	}
+	return fmt.Sprintf("%s.cdc", name)
+}
+
+func stripCDCExtension(name string) string {
+	return strings.TrimSuffix(name, filepath.Ext(name))
+}
+
+// processTemplate reads a template file from the embedded filesystem and processes it with the provided data
+// If you don't need to provide data, pass nil
+func processTemplate(templatePath string, data map[string]interface{}) (string, error) {
+	templateData, err := templatesFS.ReadFile(templatePath)
+	if err != nil {
+		return "", fmt.Errorf("failed to read template file: %w", err)
+	}
+
+	tmpl, err := template.New("template").Parse(string(templateData))
+	if err != nil {
+		return "", fmt.Errorf("failed to parse template: %w", err)
+	}
+
+	var executedTemplate bytes.Buffer
+	// Execute the template with the provided data or nil if no data is needed
+	if err = tmpl.Execute(&executedTemplate, data); err != nil {
+		return "", fmt.Errorf("failed to execute template: %w", err)
+	}
+
+	return executedTemplate.String(), nil
+}
+
 func generateNew(
 	args []string,
 	templateType string,
@@ -126,17 +168,11 @@ func generateNew(
 		return nil, fmt.Errorf("invalid number of arguments")
 	}
 
-	name := args[0]
-	var filename string
-
-	// Don't add .cdc extension if it's already there
-	if strings.HasSuffix(name, ".cdc") {
-		filename = name
-	} else {
-		filename = fmt.Sprintf("%s.cdc", name)
-	}
+	name := stripCDCExtension(args[0])
+	filename := addCDCExtension(name)
 
 	var fileToWrite string
+	var testFileToWrite string
 	var basePath string
 
 	if generateFlags.Directory != "" {
@@ -156,22 +192,26 @@ func generateNew(
 
 	switch templateType {
 	case "contract":
-		fileToWrite = fmt.Sprintf(`
-access(all)
-contract %s {
-    init() {}
-}`, name)
+		nameData := map[string]interface{}{"Name": name}
+		fileToWrite, err = processTemplate("templates/contract_init.cdc.tmpl", nameData)
+		if err != nil {
+			return nil, fmt.Errorf("error generating contract template: %w", err)
+		}
+
+		testFileToWrite, err = processTemplate("templates/contract_init_test.cdc.tmpl", nameData)
+		if err != nil {
+			return nil, fmt.Errorf("error generating contract test template: %w", err)
+		}
 	case "script":
-		fileToWrite = `access(all)
-fun main() {
-    // Script details here
-}`
+		fileToWrite, err = processTemplate("templates/script_init.cdc.tmpl", nil)
+		if err != nil {
+			return nil, fmt.Errorf("error generating script template: %w", err)
+		}
 	case "transaction":
-		fileToWrite = `transaction() {
-    prepare(account: &Account) {}
-
-    execute {}
-}`
+		fileToWrite, err = processTemplate("templates/transaction_init.cdc.tmpl", nil)
+		if err != nil {
+			return nil, fmt.Errorf("error generating transaction template: %w", err)
+		}
 	default:
 		return nil, fmt.Errorf("invalid template type: %s", templateType)
 	}
@@ -188,6 +228,7 @@ fun main() {
 		return nil, fmt.Errorf("error creating directories: %w", err)
 	}
 
+	// Write files
 	err = state.ReaderWriter().WriteFile(filenameWithBasePath, []byte(fileToWrite), 0644)
 	if err != nil {
 		return nil, fmt.Errorf("error writing file: %w", err)
@@ -195,8 +236,42 @@ fun main() {
 
 	logger.Info(fmt.Sprintf("Generated new %s: %s at %s", templateType, name, filenameWithBasePath))
 
+	if generateFlags.SkipTests != true && templateType == "contract" {
+		testsBasePath := "cadence/tests"
+		testFilenameWithBasePath := filepath.Join(testsBasePath, addCDCExtension(fmt.Sprintf("%s_test", name)))
+
+		if _, err := state.ReaderWriter().ReadFile(testFilenameWithBasePath); err == nil {
+			return nil, fmt.Errorf("file already exists: %s", testFilenameWithBasePath)
+		}
+
+		if err := state.ReaderWriter().MkdirAll(testsBasePath, 0755); err != nil {
+			return nil, fmt.Errorf("error creating test directory: %w", err)
+		}
+
+		err = state.ReaderWriter().WriteFile(testFilenameWithBasePath, []byte(testFileToWrite), 0644)
+		if err != nil {
+			return nil, fmt.Errorf("error writing test file: %w", err)
+		}
+
+		logger.Info(fmt.Sprintf("Generated new test file: %s at %s", name, testFilenameWithBasePath))
+	}
+
 	if templateType == "contract" {
-		state.Contracts().AddOrUpdate(config.Contract{Name: name, Location: filenameWithBasePath})
+		var aliases config.Aliases
+
+		if generateFlags.SkipTests != true {
+			aliases = config.Aliases{{
+				Network: config.TestingNetwork.Name,
+				Address: flowsdk.HexToAddress("0x0000000000000007"),
+			}}
+		}
+
+		contract := config.Contract{
+			Name:     name,
+			Location: filenameWithBasePath,
+			Aliases:  aliases,
+		}
+		state.Contracts().AddOrUpdate(contract)
 		err = state.SaveDefault()
 		if err != nil {
 			return nil, fmt.Errorf("error saving to flow.json: %w", err)
diff --git a/internal/super/templates/contract_init.cdc.tmpl b/internal/super/templates/contract_init.cdc.tmpl
new file mode 100644
index 00000000..ff79993e
--- /dev/null
+++ b/internal/super/templates/contract_init.cdc.tmpl
@@ -0,0 +1,4 @@
+access(all)
+contract {{ .Name }} {
+    init() {}
+}
\ No newline at end of file
diff --git a/internal/super/templates/script_init.cdc.tmpl b/internal/super/templates/script_init.cdc.tmpl
new file mode 100644
index 00000000..191f77c5
--- /dev/null
+++ b/internal/super/templates/script_init.cdc.tmpl
@@ -0,0 +1,4 @@
+access(all)
+fun main() {
+    // Script details here
+}
\ No newline at end of file
diff --git a/internal/super/templates/transaction_init.cdc.tmpl b/internal/super/templates/transaction_init.cdc.tmpl
new file mode 100644
index 00000000..a6168109
--- /dev/null
+++ b/internal/super/templates/transaction_init.cdc.tmpl
@@ -0,0 +1,5 @@
+transaction() {
+    prepare(account: &Account) {}
+
+    execute {}
+}
\ No newline at end of file
diff --git a/version.txt b/version.txt
index fb844899..5dbf859d 100644
--- a/version.txt
+++ b/version.txt
@@ -1,1 +1,1 @@
-v1.13.2
+v1.14.1


Test patch:
diff --git a/internal/super/generate_test.go b/internal/super/generate_test.go
index bf1f9808..21a8e2a8 100644
--- a/internal/super/generate_test.go
+++ b/internal/super/generate_test.go
@@ -40,13 +40,32 @@ func TestGenerateNewContract(t *testing.T) {
 	assert.NoError(t, err, "Failed to read generated file")
 	assert.NotNil(t, fileContent)
 
+	testContent, err := state.ReaderWriter().ReadFile("cadence/tests/TestContract_test.cdc")
+	assert.NoError(t, err, "Failed to read generated file")
+	assert.NotNil(t, testContent)
+
 	// Check content is correct
-	expectedContent := `
-access(all)
+	expectedContent := `access(all)
 contract TestContract {
     init() {}
 }`
+
+	expectedTestContent := `import Test
+
+access(all) let account = Test.createAccount()
+
+access(all) fun testContract() {
+    let err = Test.deployContract(
+        name: "TestContract",
+        path: "../contracts/TestContract.cdc",
+        arguments: [],
+    )
+
+    Test.expect(err, Test.beNil())
+}`
+
 	assert.Equal(t, expectedContent, string(fileContent))
+	assert.Equal(t, expectedTestContent, string(testContent))
 
 	// Test file already exists scenario
 	_, err = generateNew([]string{"TestContract"}, "contract", logger, state)
@@ -54,6 +73,46 @@ contract TestContract {
 	assert.Equal(t, "file already exists: cadence/contracts/TestContract.cdc", err.Error())
 }
 
+func TestGenerateNewContractSkipTests(t *testing.T) {
+	logger := output.NewStdoutLogger(output.NoneLog)
+	_, state, _ := util.TestMocks(t)
+
+	generateFlags.SkipTests = true
+
+	t.Cleanup(func() {
+		generateFlags.SkipTests = false
+	})
+
+	// Test contract generation
+	_, err := generateNew([]string{"TestContract"}, "contract", logger, state)
+	assert.NoError(t, err, "Failed to generate contract")
+
+	fileContent, err := state.ReaderWriter().ReadFile("cadence/contracts/TestContract.cdc")
+	assert.NoError(t, err, "Failed to read generated file")
+	assert.NotNil(t, fileContent)
+
+	testContent, err := state.ReaderWriter().ReadFile("cadence/tests/TestContract_test.cdc")
+	assert.Error(t, err, "Failed to read generated file")
+	assert.Nil(t, testContent)
+}
+
+func TestGenerateNewContractWithCDCExtension(t *testing.T) {
+	logger := output.NewStdoutLogger(output.NoneLog)
+	_, state, _ := util.TestMocks(t)
+
+	// Test contract generation
+	_, err := generateNew([]string{"Tester.cdc"}, "contract", logger, state)
+	assert.NoError(t, err, "Failed to generate contract")
+
+	fileContent, err := state.ReaderWriter().ReadFile("cadence/contracts/Tester.cdc")
+	assert.NoError(t, err, "Failed to read generated file")
+	assert.NotNil(t, fileContent)
+
+	testContent, err := state.ReaderWriter().ReadFile("cadence/tests/Tester_test.cdc")
+	assert.NoError(t, err, "Failed to read generated file")
+	assert.NotNil(t, testContent)
+}
+
 func TestGenerateNewContractFileAlreadyExists(t *testing.T) {
 	logger := output.NewStdoutLogger(output.NoneLog)
 	_, state, _ := util.TestMocks(t)
@@ -137,8 +196,7 @@ func TestGenerateNewWithDirFlag(t *testing.T) {
 	assert.NoError(t, err, "Failed to read generated file")
 	assert.NotNil(t, content)
 
-	expectedContent := `
-access(all)
+	expectedContent := `access(all)
 contract TestContract {
     init() {}
 }`
diff --git a/internal/super/templates/contract_init_test.cdc.tmpl b/internal/super/templates/contract_init_test.cdc.tmpl
new file mode 100644
index 00000000..dc8cc25f
--- /dev/null
+++ b/internal/super/templates/contract_init_test.cdc.tmpl
@@ -0,0 +1,13 @@
+import Test
+
+access(all) let account = Test.createAccount()
+
+access(all) fun testContract() {
+    let err = Test.deployContract(
+        name: "{{ .Name }}",
+        path: "../contracts/{{ .Name }}.cdc",
+        arguments: [],
+    )
+
+    Test.expect(err, Test.beNil())
+}
\ No newline at end of file