Skip to content

Commit

Permalink
Merge branch 'feature/stable-cadence' into jribbink/c1-dev-wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
jribbink committed Feb 1, 2024
2 parents 1430bf1 + 64f7944 commit c37cfef
Show file tree
Hide file tree
Showing 36 changed files with 3,643 additions and 469 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ git
# Ignore built CLI
main
*.pkey

# Local development file generation folder
imports
7 changes: 7 additions & 0 deletions cmd/flow/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"os"
"strings"

"github.com/onflow/flow-cli/internal/dependencymanager"

"github.com/spf13/cobra"

"github.com/onflow/flow-cli/internal/accounts"
Expand Down Expand Up @@ -117,6 +119,7 @@ func main() {
cmd.AddCommand(snapshot.Cmd)
cmd.AddCommand(super.FlixCmd)
cmd.AddCommand(super.GenerateCommand)
cmd.AddCommand(dependencymanager.Cmd)

command.InitFlags(cmd)
cmd.AddGroup(&cobra.Group{
Expand All @@ -143,6 +146,10 @@ func main() {
ID: "security",
Title: "🔒 Flow Security",
})
cmd.AddGroup(&cobra.Group{
ID: "manager",
Title: "🔗 Dependency Manager",
})

cmd.SetUsageTemplate(command.UsageTemplate)

Expand Down
11 changes: 6 additions & 5 deletions flowkit/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,12 @@ import (
// Accounts defines Flow accounts and their addresses, private key and more properties
// Deployments describes which contracts should be deployed to which accounts
type Config struct {
Emulators Emulators
Contracts Contracts
Networks Networks
Accounts Accounts
Deployments Deployments
Emulators Emulators
Contracts Contracts
Dependencies Dependencies
Networks Networks
Accounts Accounts
Deployments Deployments
}

type KeyType string
Expand Down
106 changes: 103 additions & 3 deletions flowkit/config/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,21 @@ package config

import (
"fmt"
"path/filepath"

"github.com/onflow/flow-go/fvm/systemcontracts"
flowGo "github.com/onflow/flow-go/model/flow"

"github.com/onflow/flow-go-sdk"
"golang.org/x/exp/slices"
)

// Contract defines the configuration for a Cadence contract.
type Contract struct {
Name string
Location string
Aliases Aliases
Name string
Location string
Aliases Aliases
IsDependency bool
}

// Alias defines an existing pre-deployed contract address for specific network.
Expand Down Expand Up @@ -106,3 +111,98 @@ func (c *Contracts) Remove(name string) error {

return nil
}

const dependencyManagerDirectory = "imports"

const (
networkEmulator = "emulator"
networkTestnet = "testnet"
networkMainnet = "mainnet"
)

func supportedNetworks() []string {
return []string{
networkEmulator,
networkTestnet,
networkMainnet,
}
}

var networkToChainID = map[string]flowGo.ChainID{
networkEmulator: flowGo.Emulator,
networkTestnet: flowGo.Testnet,
networkMainnet: flowGo.Mainnet,
}

func isCoreContract(networkName, contractName, contractAddress string) bool {
sc := systemcontracts.SystemContractsForChain(networkToChainID[networkName])

for _, coreContract := range sc.All() {
if coreContract.Name == contractName && coreContract.Address.String() == contractAddress {
return true
}
}

return false
}

func getCoreContractByName(networkName, contractName string) *systemcontracts.SystemContract {
sc := systemcontracts.SystemContractsForChain(networkToChainID[networkName])

for i, coreContract := range sc.All() {
if coreContract.Name == contractName {
return &sc.All()[i]
}
}

return nil
}

// AddDependencyAsContract adds a dependency as a contract if it doesn't already exist.
func (c *Contracts) AddDependencyAsContract(dependency Dependency, networkName string) {
var aliases []Alias

// If core contract found by name and address matches, then use all core contract aliases across networks
if isCoreContract(networkName, dependency.Source.ContractName, dependency.Source.Address.String()) {
for _, networkStr := range supportedNetworks() {
coreContract := getCoreContractByName(networkStr, dependency.Source.ContractName)
if coreContract != nil {
aliases = append(aliases, Alias{
Network: networkStr,
Address: flow.HexToAddress(coreContract.Address.String()),
})
}
}
} else {
aliases = append(aliases, dependency.Aliases...)
}

// If no core contract match, then use the address in source as alias
if len(aliases) == 0 {
aliases = append(aliases, Alias{
Network: dependency.Source.NetworkName,
Address: dependency.Source.Address,
})
}

contract := Contract{
Name: dependency.Name,
Location: filepath.ToSlash(fmt.Sprintf("%s/%s/%s.cdc", dependencyManagerDirectory, dependency.Source.Address, dependency.Source.ContractName)),
Aliases: aliases,
IsDependency: true,
}

if _, err := c.ByName(contract.Name); err != nil {
c.AddOrUpdate(contract)
}
}

func (c *Contracts) DependencyContractByName(name string) *Contract {
for i, contract := range *c {
if contract.Name == name && contract.IsDependency {
return &(*c)[i]
}
}

return nil
}
19 changes: 19 additions & 0 deletions flowkit/config/contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,22 @@ func TestContracts_Remove_NotFound(t *testing.T) {
_, err = contracts.ByName("mycontract2")
assert.EqualError(t, err, "contract mycontract2 does not exist")
}

func TestContracts_AddDependencyAsContract(t *testing.T) {
contracts := Contracts{}
contracts.AddDependencyAsContract(Dependency{
Name: "testcontract",
Source: Source{
NetworkName: "testnet",
Address: flow.HexToAddress("0x0000000000abcdef"),
ContractName: "TestContract",
},
}, "testnet")

assert.Len(t, contracts, 1)

contract, err := contracts.ByName("testcontract")
assert.NoError(t, err)
assert.Equal(t, "imports/0000000000abcdef/TestContract.cdc", contract.Location)
assert.Len(t, contract.Aliases, 1)
}
79 changes: 79 additions & 0 deletions flowkit/config/dependency.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Flow CLI
*
* Copyright 2019 Dapper Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package config

import (
"fmt"
"strings"

"github.com/onflow/flow-go-sdk"
)

func ParseSourceString(s string) (network, address, contractName string, err error) {
parts := strings.Split(s, "://")
if len(parts) != 2 {
return "", "", "", fmt.Errorf("invalid dependency source format: %s", s)
}
network = parts[0]

subParts := strings.Split(parts[1], ".")
if len(subParts) != 2 {
return "", "", "", fmt.Errorf("invalid dependency source format: %s", s)
}
address = subParts[0]
contractName = subParts[1]

return network, address, contractName, nil
}

type Source struct {
NetworkName string
Address flow.Address
ContractName string
}

type Dependency struct {
Name string
Source Source
Hash string
Aliases Aliases
}

type Dependencies []Dependency

func (d *Dependencies) ByName(name string) *Dependency {
for i, dep := range *d {
if dep.Name == name {
return &(*d)[i]
}
}

return nil
}

func (d *Dependencies) AddOrUpdate(dep Dependency) {
for i, dependency := range *d {
if dependency.Name == dep.Name {
(*d)[i] = dep
return
}
}

*d = append(*d, dep)
}
44 changes: 44 additions & 0 deletions flowkit/config/dependency_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Flow CLI
*
* Copyright 2019 Dapper Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package config

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestDependencies_ByName(t *testing.T) {
dependencies := Dependencies{
Dependency{Name: "mydep"},
}

dep := dependencies.ByName("mydep")
assert.NotNil(t, dep)
}

func TestDependencies_AddOrUpdate(t *testing.T) {
dependencies := Dependencies{}
dependencies.AddOrUpdate(Dependency{Name: "mydep"})

assert.Len(t, dependencies, 1)

dep := dependencies.ByName("mydep")
assert.NotNil(t, dep)
}
43 changes: 28 additions & 15 deletions flowkit/config/json/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ import (

// jsonConfig implements JSON format for persisting and parsing configuration.
type jsonConfig struct {
Emulators jsonEmulators `json:"emulators,omitempty"`
Contracts jsonContracts `json:"contracts,omitempty"`
Networks jsonNetworks `json:"networks,omitempty"`
Accounts jsonAccounts `json:"accounts,omitempty"`
Deployments jsonDeployments `json:"deployments,omitempty"`
Emulators jsonEmulators `json:"emulators,omitempty"`
Contracts jsonContracts `json:"contracts,omitempty"`
Dependencies jsonDependencies `json:"dependencies,omitempty"`
Networks jsonNetworks `json:"networks,omitempty"`
Accounts jsonAccounts `json:"accounts,omitempty"`
Deployments jsonDeployments `json:"deployments,omitempty"`
}

func (j *jsonConfig) transformToConfig() (*config.Config, error) {
Expand All @@ -45,6 +46,11 @@ func (j *jsonConfig) transformToConfig() (*config.Config, error) {
return nil, err
}

dependencies, err := j.Dependencies.transformToConfig()
if err != nil {
return nil, err
}

networks, err := j.Networks.transformToConfig()
if err != nil {
return nil, err
Expand All @@ -61,23 +67,30 @@ func (j *jsonConfig) transformToConfig() (*config.Config, error) {
}

conf := &config.Config{
Emulators: emulators,
Contracts: contracts,
Networks: networks,
Accounts: accounts,
Deployments: deployments,
Emulators: emulators,
Contracts: contracts,
Dependencies: dependencies,
Networks: networks,
Accounts: accounts,
Deployments: deployments,
}

// Add dependencies as contracts so they can be used in the project just like any other contract.
for _, dep := range dependencies {
conf.Contracts.AddDependencyAsContract(dep, dep.Source.NetworkName)
}

return conf, nil
}

func transformConfigToJSON(config *config.Config) jsonConfig {
return jsonConfig{
Emulators: transformEmulatorsToJSON(config.Emulators),
Contracts: transformContractsToJSON(config.Contracts),
Networks: transformNetworksToJSON(config.Networks),
Accounts: transformAccountsToJSON(config.Accounts),
Deployments: transformDeploymentsToJSON(config.Deployments),
Emulators: transformEmulatorsToJSON(config.Emulators),
Contracts: transformContractsToJSON(config.Contracts),
Dependencies: transformDependenciesToJSON(config.Dependencies, config.Contracts),
Networks: transformNetworksToJSON(config.Networks),
Accounts: transformAccountsToJSON(config.Accounts),
Deployments: transformDeploymentsToJSON(config.Deployments),
}
}

Expand Down
Loading

0 comments on commit c37cfef

Please sign in to comment.