Skip to content

Commit

Permalink
Update Flixkit & Add Error Message for Missing Dependencies (#1852)
Browse files Browse the repository at this point in the history
* update to use latest version of flixkit-go, pass in core contracts as dependencies

* fix lint issue

* add example in usage, add abstraction for getting core contracts

* add user's dependencies from flow.json as deps for generating flix

* Replace automatic core contract resolution from `flow flix generate` with dependency manager prompts (#1853)

* fix tests

* fix test

* format

---------

Co-authored-by: Jordan Ribbink <[email protected]>
Co-authored-by: Jordan Ribbink <[email protected]>
  • Loading branch information
3 people authored Dec 6, 2024
1 parent 308f7f1 commit 8b1c89e
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 77 deletions.
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/onflow/cadence-tools/lint v1.0.0
github.com/onflow/cadence-tools/test v1.0.0
github.com/onflow/fcl-dev-wallet v0.8.0
github.com/onflow/flixkit-go/v2 v2.0.1
github.com/onflow/flixkit-go/v2 v2.2.1
github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0
github.com/onflow/flow-emulator v1.0.1
github.com/onflow/flow-evm-gateway v0.36.5-0.20241005010031-1f3f5439d553
Expand Down Expand Up @@ -203,7 +203,7 @@ require (
github.com/multiformats/go-multistream v0.5.0 // indirect
github.com/multiformats/go-varint v0.0.7 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onflow/atree v0.8.0-rc.6 // indirect
github.com/onflow/atree v0.8.0 // indirect
github.com/onflow/crypto v0.25.2 // indirect
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1 // indirect
github.com/onflow/flow-ft/lib/go/contracts v1.0.0 // indirect
Expand Down Expand Up @@ -273,13 +273,13 @@ require (
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/term v0.25.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gonum.org/v1/gonum v0.14.0 // indirect
Expand Down
24 changes: 12 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2154,8 +2154,8 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs95K7IYdIYRnNcYQGf4nbF/uuJ0tHf4DRuM=
github.com/onflow/atree v0.8.0-rc.6 h1:GWgaylK24b5ta2Hq+TvyOF7X5tZLiLzMMn7lEt59fsA=
github.com/onflow/atree v0.8.0-rc.6/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo=
github.com/onflow/atree v0.8.0 h1:qg5c6J1gVDNObughpEeWm8oxqhPGdEyGrda121GM4u0=
github.com/onflow/atree v0.8.0/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo=
github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8=
github.com/onflow/cadence v1.0.0 h1:bvT75F2LZJvDCBmmajAv7QLISK6Qp30FAKcSwqNNH+o=
github.com/onflow/cadence v1.0.0/go.mod h1:7wvvecnAZtYOspLOS3Lh+FuAmMeSrXhAWiycC3kQ1UU=
Expand All @@ -2170,8 +2170,8 @@ github.com/onflow/crypto v0.25.2 h1:GjHunqVt+vPcdqhxxhAXiMIF3YiLX7gTuTR5O+VG2ns=
github.com/onflow/crypto v0.25.2/go.mod h1:fY7eLqUdMKV8EGOw301unP8h7PvLVy8/6gVR++/g0BY=
github.com/onflow/fcl-dev-wallet v0.8.0 h1:8TWHhJBWrzS6RCZI3eVjRT+SaUBqO6eygUNDaJV/B7s=
github.com/onflow/fcl-dev-wallet v0.8.0/go.mod h1:kc42jkiuoPJmxMRFjfbRO9XvnR/3XLheaOerxVMDTiw=
github.com/onflow/flixkit-go/v2 v2.0.1 h1:hbGiP5mdi9HPlsGSSnPtsTmQiaZXDkcpV3Cfn4+ffUA=
github.com/onflow/flixkit-go/v2 v2.0.1/go.mod h1:TVF6tdM5AMVhRtq1LcYqjOJDoz54TUyrMZvZUHNrX+o=
github.com/onflow/flixkit-go/v2 v2.2.1 h1:DXE55Ckrfgfr6V+Bn5ci0RSb/+Fi3n65N8Ogv91D24Y=
github.com/onflow/flixkit-go/v2 v2.2.1/go.mod h1:iSyTybGXubZ6gv0NRzveSeI5PvOmShMqS3WYVT7tLAg=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1 h1:q9tXLIALwQ76bO4bmSrhtTkyc2cZF4/gH11ix9E3F5k=
github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1/go.mod h1:u/mkP/B+PbV33tEG3qfkhhBlydSvAKxfLZSfB4lsJHg=
github.com/onflow/flow-core-contracts/lib/go/templates v1.4.0 h1:u2DAG8pk0xFH7TwS70t1gSZ/FtIIZWMSNyiu4SeXBYg=
Expand Down Expand Up @@ -2672,8 +2672,8 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
Expand Down Expand Up @@ -3029,8 +3029,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Expand All @@ -3048,8 +3048,8 @@ golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -3071,8 +3071,8 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down
15 changes: 2 additions & 13 deletions internal/dependencymanager/dependencyinstaller.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,17 +392,6 @@ func (di *DependencyInstaller) handleFileSystem(contractAddr, contractName, cont
return nil
}

func isCoreContract(contractName string) bool {
sc := systemcontracts.SystemContractsForChain(flowGo.Emulator)

for _, coreContract := range sc.All() {
if coreContract.Name == contractName {
return true
}
}
return false
}

func (di *DependencyInstaller) handleFoundContract(networkName, contractAddr, assignedName, contractName string, program *project.Program) error {
hash := sha256.New()
hash.Write(program.CodeWithUnprocessedImports())
Expand Down Expand Up @@ -455,7 +444,7 @@ func (di *DependencyInstaller) handleFoundContract(networkName, contractAddr, as

func (di *DependencyInstaller) handleAdditionalDependencyTasks(networkName, contractName string) error {
// If the contract is not a core contract and the user does not want to skip deployments, then prompt for a deployment
if !di.SkipDeployments && !isCoreContract(contractName) {
if !di.SkipDeployments && !util.IsCoreContract(contractName) {
err := di.updateDependencyDeployment(contractName)
if err != nil {
di.Logger.Error(fmt.Sprintf("Error updating deployment: %v", err))
Expand All @@ -467,7 +456,7 @@ func (di *DependencyInstaller) handleAdditionalDependencyTasks(networkName, cont
}

// If the contract is not a core contract and the user does not want to skip aliasing, then prompt for an alias
if !di.SkipAlias && !isCoreContract(contractName) {
if !di.SkipAlias && !util.IsCoreContract(contractName) {
err := di.updateDependencyAlias(contractName, networkName)
if err != nil {
di.Logger.Error(fmt.Sprintf("Error updating alias: %v", err))
Expand Down
106 changes: 87 additions & 19 deletions internal/super/flix.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"os"

"github.com/onflow/cadence/runtime/parser"
"github.com/onflow/flixkit-go/v2/flixkit"

"github.com/spf13/cobra"
Expand All @@ -31,9 +32,12 @@ import (
"github.com/onflow/flowkit/v2/config"
"github.com/onflow/flowkit/v2/output"

"golang.org/x/exp/slices"

"github.com/onflow/flow-cli/internal/command"
"github.com/onflow/flow-cli/internal/scripts"
"github.com/onflow/flow-cli/internal/transactions"
"github.com/onflow/flow-cli/internal/util"
)

type flixFlags struct {
Expand All @@ -57,13 +61,16 @@ type flixResult struct {
result string
}

var flags = flixFlags{}
var FlixCmd = &cobra.Command{
Use: "flix",
Short: "execute, generate, package",
TraverseChildren: true,
GroupID: "tools",
}
var (
flags = flixFlags{}
FlixCmd = &cobra.Command{
Use: "flix",
Short: "Commands to execute, generate, package FLIX templates",
TraverseChildren: true,
GroupID: "tools",
Example: "flow flix execute transfer-flow 1 0x123456789",
}
)

var executeCommand = &command.Command{
Cmd: &cobra.Command{
Expand Down Expand Up @@ -217,7 +224,10 @@ func generateFlixCmd(
flags flixFlags,
) (result command.Result, err error) {
cadenceFile := args[0]
depContracts := getDeployedContracts(state)
depContracts := getContractsFromState(state, flags.ExcludeNetworks)
if err != nil {
return nil, fmt.Errorf("could not get core contracts %w", err)
}

if cadenceFile == "" {
return nil, fmt.Errorf("no cadence code found")
Expand All @@ -228,10 +238,6 @@ func generateFlixCmd(
return nil, fmt.Errorf("could not read cadence file %s: %w", cadenceFile, err)
}

if err != nil {
return nil, fmt.Errorf("could not create flix generator %w", err)
}

// get user's configured networks
depNetworks := getNetworks(state)

Expand All @@ -241,7 +247,7 @@ func generateFlixCmd(
excludeMap[net] = true
}

var filteredNetworks []config.Network
var filteredNetworks []flixkit.NetworkConfig
for _, network := range depNetworks {
if !excludeMap[network.Name] {
filteredNetworks = append(filteredNetworks, network)
Expand All @@ -254,8 +260,12 @@ func generateFlixCmd(
}
}

ctx := context.Background()
err = validateImports(string(code), depContracts, depNetworks)
if err != nil {
return nil, fmt.Errorf("could not validate imported contracts: %w", err)
}

ctx := context.Background()
prettyJSON, err := flixService.CreateTemplate(ctx, depContracts, string(code), flags.PreFill, depNetworks)
if err != nil {
return nil, fmt.Errorf("could not generate flix %w", err)
Expand All @@ -265,7 +275,6 @@ func generateFlixCmd(
flixQuery: cadenceFile,
result: prettyJSON,
}, err

}

func (fr *flixResult) JSON() any {
Expand All @@ -283,7 +292,40 @@ func (fr *flixResult) Oneliner() string {
return fr.result
}

func getDeployedContracts(state *flowkit.State) flixkit.ContractInfos {
func validateImports(code string, depContracts flixkit.ContractInfos, depNetworks []flixkit.NetworkConfig) error {
// Check all imported contracts in the cadence code
astProgram, err := parser.ParseProgram(nil, []byte(code), parser.Config{})
if err != nil {
return fmt.Errorf("could not parse Cadence code %w", err)
}

// Check for any missing string imports
for _, imp := range astProgram.ImportDeclarations() {
if len(imp.Identifiers) > 0 || imp.Location == nil {
return fmt.Errorf("only string imports of the form `import \"ContractName\"` are supported")
}

contractName := imp.Location.String()

if depContracts[contractName] == nil {
if util.IsCoreContract(contractName) {
return fmt.Errorf("contract %[1]s is not found in the flow.json configuration, if this refers to the %[1]s core contract, please add it using `flow deps install %[1]s`", contractName)
}

return fmt.Errorf("contract %[1]s is not found in the flow.json configuration, if it refers to an external contract, please add it using `flow deps install <network>://<address>.%[1]s`", contractName)
}

for _, network := range depNetworks {
if depContracts[contractName][network.Name] == "" {
return fmt.Errorf("contract %s was found in the flow.json configuration, but is missing an alias for network %s", contractName, network.Name)
}
}
}

return nil
}

func getContractsFromState(state *flowkit.State, excludeNetworks []string) flixkit.ContractInfos {
allContracts := make(flixkit.ContractInfos)
depNetworks := make([]string, 0)
accountAddresses := make(map[string]string)
Expand All @@ -303,6 +345,9 @@ func getDeployedContracts(state *flowkit.State) flixkit.ContractInfos {
if _, ok := allContracts[c.Name]; !ok {
allContracts[c.Name] = make(flixkit.NetworkAddressMap)
}
if slices.Contains(excludeNetworks, d.Network) {
continue
}
allContracts[c.Name][d.Network] = addr
}
}
Expand All @@ -318,6 +363,9 @@ func getDeployedContracts(state *flowkit.State) flixkit.ContractInfos {
if _, ok := allContracts[c.Name]; !ok {
allContracts[c.Name] = make(flixkit.NetworkAddressMap)
}
if slices.Contains(excludeNetworks, network) {
continue
}
allContracts[c.Name][network] = c.AccountAddress.HexWithPrefix()
}
locAliases := state.AliasesForNetwork(cfg)
Expand All @@ -328,17 +376,37 @@ func getDeployedContracts(state *flowkit.State) flixkit.ContractInfos {
if _, ok := allContracts[name]; !ok {
allContracts[name] = make(flixkit.NetworkAddressMap)
}
if slices.Contains(excludeNetworks, network) {
continue
}
allContracts[name][network] = addr
}
}

// add contracts that have not been deployed
for _, c := range *state.Contracts() {
if _, ok := allContracts[c.Name]; !ok {
allContracts[c.Name] = make(flixkit.NetworkAddressMap)
}
for _, alias := range c.Aliases {
if slices.Contains(excludeNetworks, alias.Network) {
continue
}
allContracts[c.Name][alias.Network] = alias.Address.HexWithPrefix()
}
}

return allContracts
}

func getNetworks(state *flowkit.State) []config.Network {
networks := make([]config.Network, 0)
func getNetworks(state *flowkit.State) []flixkit.NetworkConfig {
networks := make([]flixkit.NetworkConfig, 0)
for _, n := range *state.Networks() {
networks = append(networks, n)
networks = append(networks, flixkit.NetworkConfig{
Name: n.Name,
Host: n.Host,
Key: n.Key,
})
}
return networks
}
Expand Down
Loading

0 comments on commit 8b1c89e

Please sign in to comment.