├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md ├── dependabot.yml ├── labels.yml ├── pull_request_template.md ├── release-drafter.yml └── workflows │ ├── add-issues-to-devx-project.yml │ ├── build-release.yaml │ ├── ci.yml │ ├── dependent-issues.yml │ ├── publish-release.yml │ ├── release-drafter.yml │ └── sync-labels.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yaml ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── NOTICE ├── README.md ├── SECURITY.md ├── build └── build.go ├── check-headers.sh ├── cli-banner.svg ├── cli.gif ├── cmd └── flow │ └── main.go ├── docs ├── README.md ├── account-add-contract.md ├── account-remove-contract.md ├── account-staking-info.md ├── account-update-contract.md ├── build-transactions.md ├── complex-transactions.md ├── configuration.md ├── create-accounts.md ├── data-collection.md ├── decode-keys.md ├── decode-transactions.md ├── deploy-project-contracts.md ├── derive-keys.md ├── developer-updates │ ├── release-notes-v17.md │ ├── release-notes-v18.md │ ├── release-notes-v19.md │ ├── release-notes-v21.md │ ├── release-notes-v24.md │ ├── release-notes-v26.md │ └── release-notes-v28.md ├── emulator-snapshot.md ├── execute-scripts.md ├── generate-keys.md ├── get-accounts.md ├── get-blocks.md ├── get-collections.md ├── get-events.md ├── get-status.md ├── get-transactions.md ├── index.md ├── initialize-configuration.md ├── install.md ├── manage-configuration.md ├── project-app.md ├── project-contracts.md ├── run-tests.md ├── security.md ├── send-signed-transactions.md ├── send-transactions.md ├── sign-transaction.md ├── signature-generate.md ├── signature-verify.md ├── snapshot-save.md ├── start-emulator.md ├── super-commands.md ├── template.md └── tools.md ├── flowkit ├── README.md └── schema.json ├── go.mod ├── go.sum ├── install.ps1 ├── install.sh ├── internal ├── accounts │ ├── accounts.go │ ├── accounts_test.go │ ├── contract-add.go │ ├── contract-remove.go │ ├── contract-update.go │ ├── create-interactive.go │ ├── create.go │ ├── fund.go │ ├── get.go │ └── staking-info.go ├── blocks │ ├── blocks.go │ ├── blocks_test.go │ └── get.go ├── cadence │ ├── cadence.go │ ├── languageserver │ │ └── languageserver.go │ ├── lint.go │ ├── lint_test.go │ └── linter.go ├── collections │ ├── collections.go │ ├── collections_test.go │ └── get.go ├── command │ ├── command.go │ ├── global_flags.go │ ├── global_flags_test.go │ ├── result.go │ └── template.go ├── config │ ├── add-account.go │ ├── add-contract.go │ ├── add-deployment.go │ ├── add-network.go │ ├── add.go │ ├── config.go │ ├── init.go │ ├── remove-account.go │ ├── remove-contract.go │ ├── remove-deployment.go │ ├── remove-network.go │ └── remove.go ├── dependencymanager │ ├── add.go │ ├── dependencies.go │ ├── dependencyinstaller.go │ ├── dependencyinstaller_test.go │ ├── discover.go │ └── install.go ├── emulator │ ├── snapshot.go │ └── start.go ├── events │ ├── events.go │ ├── events_test.go │ └── get.go ├── evm │ └── gateway.go ├── keys │ ├── decode.go │ ├── derive.go │ ├── generate.go │ ├── keys.go │ └── keys_test.go ├── project │ ├── deploy.go │ ├── project.go │ └── project_test.go ├── prompt │ ├── prompt.go │ ├── select-options.go │ └── text-input.go ├── quick │ ├── deploy.go │ └── run.go ├── scripts │ ├── execute.go │ ├── scripts.go │ └── scripts_test.go ├── settings │ ├── cmd.go │ ├── defaults.go │ ├── metrics.go │ └── settings.go ├── signatures │ ├── generate.go │ ├── signatures.go │ ├── signatures_test.go │ └── verify.go ├── snapshot │ ├── save.go │ └── snapshot.go ├── status │ └── status.go ├── super │ ├── dev.go │ ├── files.go │ ├── files_test.go │ ├── flix.go │ ├── flix_test.go │ ├── generate.go │ ├── generator │ │ ├── contract_template.go │ │ ├── file_template.go │ │ ├── fixtures │ │ │ ├── README_no_deps.md │ │ │ └── README_with_deps.md │ │ ├── generator.go │ │ ├── generator_test.go │ │ ├── script_template.go │ │ ├── templates │ │ │ ├── README.md.tmpl │ │ │ ├── contract_counter.cdc.tmpl │ │ │ ├── contract_init.cdc.tmpl │ │ │ ├── contract_init_test.cdc.tmpl │ │ │ ├── empty_test.cdc.tmpl │ │ │ ├── script_counter.cdc.tmpl │ │ │ ├── script_init.cdc.tmpl │ │ │ ├── transaction_counter.cdc.tmpl │ │ │ └── transaction_init.cdc.tmpl │ │ ├── test_template.go │ │ └── transaction_template.go │ ├── output.go │ ├── project.go │ ├── scaffolds.go │ └── setup.go ├── test │ ├── test.go │ └── test_test.go ├── tools │ ├── flowser.go │ └── wallet.go ├── transactions │ ├── build.go │ ├── decode.go │ ├── get-system.go │ ├── get.go │ ├── send-signed.go │ ├── send.go │ ├── sign.go │ ├── transactions.go │ └── transactions_test.go ├── util │ ├── checker_environment.go │ ├── emoji.go │ ├── files.go │ ├── test.go │ └── util.go └── version │ └── version.go ├── scaffolds.json ├── testing └── better │ └── README.md └── version.txt /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Reporting a Problem/Bug 3 | about: Reporting a Problem/Bug 4 | title: '' 5 | labels: bug 6 | --- 7 | ### Instructions 8 | 9 | Please fill out the template below to the best of your ability and include a label indicating which tool/service you were working with when you encountered the problem. 10 | 11 | ### Problem 12 | 13 | 14 | 15 | ### Steps to Reproduce 16 | 17 | 18 | 19 | ### Acceptance Criteria 20 | 21 | 22 | 23 | ### Context 24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Requesting a Feature or Improvement 3 | about: "For feature requests. Please search for existing issues first. Also see CONTRIBUTING.md" 4 | title: '' 5 | labels: Feature 6 | --- 7 | 8 | ## Instructions 9 | 10 | Please fill out the template below to the best of your ability. 11 | 12 | ### Issue To Be Solved 13 | 14 | (Replace this text: 15 | Please present a concise description of the problem to be addressed by this feature request. 16 | Please be clear what parts of the problem are considered to be in-scope and out-of-scope.) 17 | 18 | ### (Optional): Suggest A Solution 19 | 20 | (Replace this text: A concise description of your preferred solution. Things to address include: 21 | 22 | * Details of the technical implementation 23 | * Tradeoffs made in design decisions 24 | * Caveats and considerations for the future 25 | 26 | If there are multiple solutions, please present each one separately. Save comparisons for the very end.) 27 | 28 | ### (Optional): Context 29 | 30 | (Replace this text: 31 | What are you currently working on that this is blocking?) 32 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "custom" 7 | interval-count: 14 8 | time: "12:00" # Adjust the time to your preferred schedule 9 | 10 | - package-ecosystem: "gomod" 11 | allow: 12 | - dependency-type: "direct" 13 | update-type: "semver-minor" 14 | - dependency-type: "direct" 15 | update-type: "semver-major" 16 | directory: "/" 17 | schedule: 18 | interval: "custom" 19 | interval-count: 14 20 | time: "12:00" # Adjust the time to your preferred schedule 21 | ignore: 22 | - dependency-name: "github.com/onflow/flow-go" 23 | - dependency-name: "github.com/onflow/cadence" 24 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | - color: 3E4B9E 2 | description: "" 3 | name: Epic 4 | - color: 0e8a16 5 | description: A new user feature or a new package API 6 | name: Feature 7 | - color: d4c5f9 8 | description: "" 9 | name: Feedback 10 | - color: 1d76db 11 | description: Technical work without new features, refactoring, improving tests 12 | name: Improvement 13 | - color: d73a4a 14 | description: The issue represents a bug, malfunction, regression 15 | name: Bug 16 | - color: 0075ca 17 | description: The issue in documentation, missing docs, invalid docs, outdated docs 18 | name: Documentation 19 | - color: c2e0c6 20 | description: A feature we want the help from community 21 | name: Help wanted 22 | - color: cccccc 23 | description: The issue was not well defined, waiting for more information from the submittor 24 | name: Needs information 25 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Closes #??? 2 | 3 | ## Description 4 | 5 | 9 | 10 | ______ 11 | 12 | For contributor use: 13 | 14 | - [ ] Targeted PR against `master` branch 15 | - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work 16 | - [ ] Code follows the [standards mentioned here](https://github.com/onflow/flow-cli/blob/master/CONTRIBUTING.md#styleguides) 17 | - [ ] Updated relevant documentation 18 | - [ ] Re-reviewed `Files changed` in the Github PR explorer 19 | - [ ] Added appropriate labels 20 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'Version $NEXT_PATCH_VERSION' 2 | tag-template: 'v$NEXT_PATCH_VERSION' 3 | change-template: '- $TITLE (#$NUMBER) @$AUTHOR' 4 | template: | 5 | ## ⬆️ Install or Upgrade 6 | Follow the [Flow CLI installation guide](https://developers.flow.com/tools/flow-cli/install) for instructions on how to install or upgrade the CLI. 7 | 8 | $CHANGES 9 | categories: 10 | - title: 💥 Breaking Changes 11 | label: Breaking Change 12 | - title: ⭐ Features 13 | label: Feature 14 | - title: 🛠 Improvements 15 | label: Improvement 16 | - title: 🐞 Bug Fixes 17 | label: Bugfix 18 | - title: 📖 Documentation 19 | label: Documentation 20 | -------------------------------------------------------------------------------- /.github/workflows/add-issues-to-devx-project.yml: -------------------------------------------------------------------------------- 1 | name: Adds all issues to the DevEx project board. 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/add-to-project@v1.0.2 14 | with: 15 | project-url: https://github.com/orgs/onflow/projects/85 16 | github-token: ${{ secrets.GH_ACTION_FOR_PROJECTS }} 17 | -------------------------------------------------------------------------------- /.github/workflows/build-release.yaml: -------------------------------------------------------------------------------- 1 | name: Build Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | env: 8 | GO_VERSION: "1.24" 9 | 10 | jobs: 11 | release: 12 | name: Release Go Binary 13 | runs-on: ubuntu-latest 14 | steps: 15 | # See https://github.com/onflow/flow-cli/pull/1431 for more information 16 | - name: Delete unnecessary cache 17 | run: rm -rf ${RUNNER_TOOL_CACHE} 18 | - uses: actions/checkout@v3 19 | - name: Codebase security check 20 | continue-on-error: true 21 | uses: snyk/actions/golang@master 22 | with: 23 | go-version: ${{ env.GO_VERSION }} 24 | env: 25 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} 26 | - name: Setup Release Environment 27 | run: |- 28 | echo 'MIXPANEL_PROJECT_TOKEN=${{ secrets.MIXPANEL_PROJECT_TOKEN }}' > .release-env 29 | echo 'LILICO_TOKEN=${{ secrets.LILICO_TOKEN }}' >> .release-env 30 | echo 'APP_VERSION=$(basename ${GITHUB_REF})' >> .release-env 31 | echo 'BUILD_TIME=$(date --iso-8601=seconds)' >> .release-env 32 | echo 'VERSION=${{ github.event.release.tag_name }}' >> .release-env 33 | echo 'GITHUB_TOKEN=${{ secrets.FLOW_CLI_RELEASE }}' >> .release-env 34 | - name: Build and Release 35 | run: make release -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # Controls when the action will run. Triggers the workflow on push or pull request 4 | # events but only for the master branch 5 | on: 6 | push: 7 | branches: 8 | - master 9 | - 'feature/**' 10 | pull_request: 11 | branches: 12 | - master 13 | - 'feature/**' 14 | 15 | # We need to set this explicitly to make sure the CI works on Windows 16 | # Default shell does not terminate on error in GitHub Actions 17 | # https://github.com/actions/runner-images/issues/6668 18 | defaults: 19 | run: 20 | shell: bash 21 | 22 | env: 23 | GO_VERSION: "1.24" 24 | 25 | jobs: 26 | test: 27 | strategy: 28 | matrix: 29 | os: [ubuntu-latest, windows-latest] 30 | runs-on: ${{ matrix.os }} 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: actions/setup-go@v5 34 | with: 35 | go-version: ${{ env.GO_VERSION }} 36 | - name: Run tests 37 | run: | 38 | make ci 39 | make check-tidy 40 | make check-headers 41 | - name: Upload coverage report 42 | uses: codecov/codecov-action@v5 43 | with: 44 | file: ./coverage.txt 45 | flags: unittests 46 | token: ${{ secrets.CODECOV_TOKEN }} 47 | if: matrix.os == 'ubuntu-latest' 48 | 49 | lint: 50 | runs-on: ubuntu-latest 51 | steps: 52 | - uses: actions/checkout@v4 53 | - uses: actions/setup-go@v5 54 | with: 55 | go-version: ${{ env.GO_VERSION }} 56 | - name: generate 57 | run: make generate 58 | - uses: golangci/golangci-lint-action@v6.5.0 59 | with: 60 | version: v1.64.8 61 | only-new-issues: true 62 | skip-pkg-cache: true 63 | args: --timeout=3m 64 | -------------------------------------------------------------------------------- /.github/workflows/dependent-issues.yml: -------------------------------------------------------------------------------- 1 | name: Dependent Issues 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | - edited 8 | - closed 9 | - reopened 10 | pull_request_target: 11 | types: 12 | - opened 13 | - edited 14 | - closed 15 | - reopened 16 | - synchronize 17 | schedule: 18 | - cron: '0 0 * * *' 19 | 20 | jobs: 21 | check: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: z0al/dependent-issues@v1 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | with: 28 | label: dependent 29 | comment: > 30 | This PR/issue depends on: 31 | 32 | {{ dependencies }} 33 | 34 | By **[Dependent Issues](https://github.com/z0al/dependent-issues)** (🤖). 35 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | jobs: 7 | homebrew: 8 | if: "!github.event.release.prerelease" 9 | name: Bump Homebrew formula 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: mislav/bump-homebrew-formula-action@v3 13 | with: 14 | formula-name: flow-cli 15 | env: 16 | COMMITTER_TOKEN: ${{ secrets.COMMITTER_TOKEN }} 17 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | update_release_draft: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: release-drafter/release-drafter@v5 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.github/workflows/sync-labels.yml: -------------------------------------------------------------------------------- 1 | name: Sync Labels 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - .github/labels.yml 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: micnncim/action-label-syncer@v1 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | with: 19 | manifest: .github/labels.yml 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Test binary, build with `go test -c` 2 | *.test 3 | 4 | # Output of the go coverage tool, specifically when used with LiteIDE 5 | *.out 6 | 7 | # Coverage artifacts 8 | coverage.zip 9 | coverage.txt 10 | cover.json 11 | cover-summary 12 | index.html 13 | 14 | # Binaries 15 | /cmd/flow/flow* 16 | 17 | .DS_Store 18 | 19 | # Ignore flow json config 20 | flow.json 21 | flow*.json 22 | !tests/flow.json 23 | 24 | # IDE related files 25 | .idea 26 | .vscode 27 | git 28 | 29 | # Ignore built CLI 30 | main 31 | *.pkey 32 | 33 | # Local development file generation folder 34 | imports 35 | 36 | # Goreleaser .env 37 | .release-env -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | disable-all: true 3 | enable: 4 | - govet 5 | - errcheck 6 | - ineffassign 7 | - typecheck 8 | - misspell 9 | - goimports 10 | linters-settings: 11 | goimports: 12 | # put imports beginning with prefix after 3rd-party package 13 | local-prefixes: github.com/onflow/flow-cli 14 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - make generate 4 | 5 | builds: 6 | - id: flow-cli 7 | main: ./cmd/flow 8 | goos: 9 | - darwin 10 | - linux 11 | - windows 12 | goarch: 13 | - amd64 14 | - arm64 15 | env: 16 | - CGO_CFLAGS=-O2 -D__BLST_PORTABLE__ 17 | - CGO_ENABLED=1 18 | - CC_darwin_amd64=o64-clang 19 | - CXX_darwin_amd64=o64-clang+ 20 | - CC_darwin_arm64=oa64-clang 21 | - CXX_darwin_arm64=oa64-clang++ 22 | - CC_linux_amd64=x86_64-linux-gnu-gcc 23 | - CXX_linux_amd64=x86_64-linux-gnu-g++ 24 | - CC_linux_arm64=aarch64-linux-gnu-gcc 25 | - CXX_linux_arm64=aarch64-linux-gnu-g++ 26 | - CC_windows_amd64=x86_64-w64-mingw32-gcc 27 | - CXX_windows_amd64=x86_64-w64-mingw32-g++ 28 | - CC_windows_arm64=/llvm-mingw/bin/aarch64-w64-mingw32-gcc 29 | - CXX_windows_arm64=/llvm-mingw/bin/aarch64-w64-mingw32-g++ 30 | - 'CC={{ index .Env (print "CC_" .Os "_" .Arch) }}' 31 | - 'CXX={{ index .Env (print "CXX_" .Os "_" .Arch) }}' 32 | flags: 33 | - -mod=readonly 34 | ldflags: 35 | - -X github.com/onflow/flow-cli/build.semver={{ .Env.VERSION }} -X github.com/onflow/flow-cli/internal/command.MixpanelToken={{ .Env.MIXPANEL_PROJECT_TOKEN }} -X github.com/onflow/flow-cli/internal/accounts.accountToken={{ .Env.LILICO_TOKEN }} 36 | 37 | archives: 38 | - id: golang-cross 39 | builds: 40 | - flow-cli 41 | name_template: "{{ .ProjectName }}-v{{ .Version }}-{{ .Os }}-{{ .Arch }}" 42 | format: tar.gz 43 | format_overrides: 44 | - goos: windows 45 | format: zip 46 | wrap_in_directory: false 47 | snapshot: 48 | name_template: "{{ .Tag }}" 49 | changelog: 50 | skip: true 51 | checksum: 52 | name_template: "checksums.txt" 53 | release: 54 | github: 55 | owner: onflow 56 | name: flow-cli 57 | prerelease: auto 58 | draft: false 59 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @Kay-Zee @janezpodhostnik @chasefleming @nvdtf @bluesign @bjartek @jribbink @mfbz 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Configuration for goreleaser 2 | PACKAGE_NAME := github.com/onflow/flow-cli 3 | GOLANG_CROSS_VERSION ?= v1.22.0 4 | 5 | # The tag of the current commit, otherwise empty 6 | VERSION := $(shell git describe --tags --abbrev=0 --exact-match 2>/dev/null) 7 | # Name of the cover profile 8 | COVER_PROFILE := coverage.txt 9 | # Disable go sum database lookup for private repos 10 | GOPRIVATE := github.com/dapperlabs/* 11 | # Ensure go bin path is in path (Especially for CI) 12 | GOPATH ?= $(HOME)/go 13 | PATH := $(PATH):$(GOPATH)/bin 14 | # OS 15 | UNAME := $(shell uname) 16 | 17 | MIXPANEL_PROJECT_TOKEN := 3fae49de272be1ceb8cf34119f747073 18 | ACCOUNT_TOKEN := lilico:sF60s3wughJBmNh2 19 | 20 | BINARY ?= ./cmd/flow/flow 21 | 22 | .PHONY: binary 23 | binary: $(BINARY) 24 | 25 | .PHONY: install-tools 26 | install-tools: 27 | cd '${GOPATH}'; \ 28 | mkdir -p '${GOPATH}'; \ 29 | GO111MODULE=on go install github.com/axw/gocov/gocov@latest; \ 30 | GO111MODULE=on go install github.com/matm/gocov-html/cmd/gocov-html@latest; \ 31 | GO111MODULE=on go install github.com/sanderhahn/gozip/cmd/gozip@latest; \ 32 | GO111MODULE=on go install github.com/vektra/mockery/v2@v2.43.2; 33 | 34 | .PHONY: test 35 | test: 36 | GO111MODULE=on go test -coverprofile=$(COVER_PROFILE) $(if $(JSON_OUTPUT),-json,) ./... 37 | 38 | .PHONY: test-e2e-emulator 39 | test-e2e-emulator: 40 | flow -f tests/flow.json emulator start 41 | 42 | .PHONY: coverage 43 | coverage: 44 | ifeq ($(COVER), true) 45 | # file has to be called index.html 46 | gocov convert $(COVER_PROFILE) > cover.json 47 | ./cover-summary.sh 48 | gocov-html cover.json > index.html 49 | # coverage.zip will automatically be picked up by teamcity 50 | gozip -c coverage.zip index.html 51 | endif 52 | 53 | .PHONY: ci 54 | ci: install-tools generate test coverage 55 | 56 | $(BINARY): 57 | CGO_ENABLED=1 \ 58 | CGO_CFLAGS="-O2 -D__BLST_PORTABLE__" \ 59 | GO111MODULE=on go build \ 60 | -trimpath \ 61 | -ldflags \ 62 | "-X github.com/onflow/flow-cli/build.semver=$(VERSION) -X github.com/onflow/flow-cli/internal/accounts.accountToken=${ACCOUNT_TOKEN} -X github.com/onflow/flow-cli/internal/command.MixpanelToken=${MIXPANEL_PROJECT_TOKEN}" \ 63 | -o $(BINARY) ./cmd/flow 64 | 65 | .PHONY: versioned-binaries 66 | versioned-binaries: generate 67 | $(MAKE) OS=linux ARCH=amd64 ARCHNAME=x86_64 versioned-binary 68 | $(MAKE) OS=linux ARCH=arm64 versioned-binary 69 | $(MAKE) OS=darwin ARCH=amd64 ARCHNAME=x86_64 versioned-binary 70 | $(MAKE) OS=darwin ARCH=arm64 versioned-binary 71 | $(MAKE) OS=windows ARCH=amd64 ARCHNAME=x86_64 versioned-binary 72 | 73 | .PHONY: versioned-binary 74 | versioned-binary: 75 | GOOS=$(OS) GOARCH=$(ARCH) $(MAKE) BINARY=./cmd/flow/flow-$(or ${ARCHNAME},${ARCHNAME},${ARCH})-$(OS)-$(VERSION) binary 76 | 77 | .PHONY: publish 78 | publish: 79 | gsutil -m cp cmd/flow/flow-*-$(VERSION) gs://flow-cli 80 | 81 | .PHONY: clean 82 | clean: 83 | rm ./cmd/flow/flow* 84 | 85 | .PHONY: lint 86 | lint: generate 87 | golangci-lint run -v ./... 88 | 89 | .PHONY: fix-lint 90 | fix-lint: 91 | golangci-lint run -v --fix ./... 92 | 93 | .PHONY: check-headers 94 | check-headers: 95 | @./check-headers.sh 96 | 97 | .PHONY: check-tidy 98 | check-tidy: 99 | go mod tidy 100 | 101 | .PHONY: generate 102 | generate: install-tools 103 | go generate ./... 104 | 105 | .PHONY: release 106 | release: 107 | docker run \ 108 | --rm \ 109 | --env-file .release-env \ 110 | -v /var/run/docker.sock:/var/run/docker.sock \ 111 | -v `pwd`:/go/src/$(PACKAGE_NAME) \ 112 | -v `pwd`/sysroot:/sysroot \ 113 | -w /go/src/$(PACKAGE_NAME) \ 114 | ghcr.io/goreleaser/goreleaser-cross:${GOLANG_CROSS_VERSION} \ 115 | release --clean -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Flow CLI 2 | Copyright 2019-2024 Flow Foundation 3 | 4 | This product includes software developed at the Flow Foundation (https://flow.com/flow-foundation). -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 | Logo 5 | 6 | 7 |

8 | Flow CLI brings Flow to your terminal. Easily interact with the network and build your dapps. 9 |
10 | Read the docs» 11 |
12 |
13 | Report Bug 14 | · 15 | Contribute 16 | · 17 | Read Guidelines 18 |

19 |

20 |
21 |
22 | 23 | ## Installation 24 | 25 | To install the Flow CLI, follow the [installation instructions](https://developers.flow.com/tools/flow-cli/install) on the Flow documentation website. 26 | 27 | ## Documentation 28 | 29 | You can find the CLI documentation on the [CLI documentation website](https://developers.flow.com/tools/flow-cli). 30 | 31 | ## Features 32 | The Flow CLI is a command line tool that allows you to interact with the Flow blockchain. 33 | Read about supported commands in the [CLI documentation website](https://developers.flow.com/tools/flow-cli). 34 | 35 | ``` 36 | Usage: 37 | flow [command] 38 | 39 | 👋 Welcome Flow developer! 40 | If you are starting a new flow project use our super commands, start by running 'flow init'. 41 | 42 | 🔥 Super Commands 43 | generate Generate template files for common Cadence code 44 | init Start a new Flow project 45 | 46 | 📦 Flow Entities 47 | accounts Create and retrieve accounts and deploy contracts 48 | blocks Retrieve blocks 49 | collections Retrieve collections 50 | events Retrieve events 51 | 52 | 💬 Flow Interactions 53 | scripts Execute Cadence scripts 54 | transactions Build, sign, send and retrieve transactions 55 | 56 | 🔨 Flow Tools 57 | cadence Execute Cadence code 58 | dev-wallet Run a development wallet 59 | emulator Run Flow network for development 60 | flix execute, generate, package 61 | flowser Run Flowser project explorer 62 | test Run Cadence tests 63 | 64 | 🏄 Flow Project 65 | deploy Deploy all project contracts 66 | project Manage your Cadence project 67 | run Start emulator and deploy all project contracts 68 | 69 | 🔒 Flow Security 70 | keys Generate and decode Flow keys 71 | signatures Signature verification and creation 72 | 73 | 🔗 Dependency Manager 74 | dependencies Manage contracts and dependencies 75 | ``` 76 | 77 | The Flow CLI includes several commands to interact with Flow networks, such as querying account information, or sending transactions. It also includes the [Flow Emulator](https://developers.flow.com/tools/emulator). 78 | 79 | 80 | ![Alt Text](./cli.gif) 81 | 82 | ## Contributing 83 | 84 | Read [contributing](./CONTRIBUTING.md) document. 85 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | # Responsible Disclosure Policy 3 | 4 | Flow was built from the ground up with security in mind. Our code, infrastructure, and development methodology helps us keep our users safe. 5 | 6 | We really appreciate the community's help. Responsible disclosure of vulnerabilities helps to maintain the security and privacy of everyone. 7 | 8 | If you care about making a difference, please follow the guidelines below. 9 | 10 | # **Guidelines For Responsible Disclosure** 11 | 12 | We ask that all researchers adhere to these guidelines [here](https://flow.com/flow-responsible-disclosure) 13 | -------------------------------------------------------------------------------- /build/build.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | // Package build contains information about the build that injected at build-time. 20 | // 21 | // To use this package, simply import it in your program, then add build 22 | // arguments like the following: 23 | // 24 | // go build -ldflags "-X github.com/onflow/flow-go/version.semver=v1.0.0" 25 | package build 26 | 27 | // Default value for build-time-injected version strings. 28 | const undefined = "undefined" 29 | 30 | // The following variables are injected at build-time using ldflags. 31 | var ( 32 | semver string 33 | commit string 34 | ) 35 | 36 | // Semver returns the semantic version of this build. 37 | func Semver() string { 38 | return semver 39 | } 40 | 41 | // Commit returns the commit at which this build was created. 42 | func Commit() string { 43 | return commit 44 | } 45 | 46 | // IsDefined determines whether a version string is defined. Inputs should 47 | // have been produced from this package. 48 | func IsDefined(v string) bool { 49 | return v != undefined 50 | } 51 | 52 | // If any of the build-time-injected variables are empty at initialization, 53 | // mark them as undefined. 54 | func init() { 55 | if len(semver) == 0 { 56 | semver = undefined 57 | } 58 | if len(commit) == 0 { 59 | commit = undefined 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /check-headers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | files=$(find . -name \*.go -type f -print0 | xargs -0 grep -L -E '(Licensed under the Apache License)|(Code generated (from|by))') 4 | if [ -n "$files" ]; then 5 | echo "Missing license header in:" 6 | echo "$files" 7 | exit 1 8 | fi -------------------------------------------------------------------------------- /cli.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onflow/flow-cli/89109c41db1d6132f26637c16a4293dd51c60e01/cli.gif -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/README.md 4 | -------------------------------------------------------------------------------- /docs/account-add-contract.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/account-add-contract.md 4 | -------------------------------------------------------------------------------- /docs/account-remove-contract.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/account-remove-contract.md 4 | -------------------------------------------------------------------------------- /docs/account-staking-info.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/account-staking-info.md 4 | -------------------------------------------------------------------------------- /docs/account-update-contract.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/account-update-contract.md 4 | -------------------------------------------------------------------------------- /docs/build-transactions.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/build-transactions.md 4 | -------------------------------------------------------------------------------- /docs/complex-transactions.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/complex-transactions.md 4 | -------------------------------------------------------------------------------- /docs/configuration.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/configuration.md 4 | -------------------------------------------------------------------------------- /docs/create-accounts.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/create-accounts.md 4 | -------------------------------------------------------------------------------- /docs/data-collection.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/data-collection.md 4 | -------------------------------------------------------------------------------- /docs/decode-keys.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/decode-keys.md 4 | -------------------------------------------------------------------------------- /docs/decode-transactions.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/decode-transactions.md 4 | -------------------------------------------------------------------------------- /docs/deploy-project-contracts.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/deploy-project-contracts.md 4 | -------------------------------------------------------------------------------- /docs/derive-keys.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/derive-keys.md 4 | -------------------------------------------------------------------------------- /docs/developer-updates/release-notes-v17.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/developer-updates/release-notes-v17.md 4 | -------------------------------------------------------------------------------- /docs/developer-updates/release-notes-v18.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/developer-updates/release-notes-v18.md 4 | -------------------------------------------------------------------------------- /docs/developer-updates/release-notes-v19.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/developer-updates/release-notes-v19.md 4 | -------------------------------------------------------------------------------- /docs/developer-updates/release-notes-v21.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/developer-updates/release-notes-v21.md 4 | -------------------------------------------------------------------------------- /docs/developer-updates/release-notes-v24.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/developer-updates/release-notes-v24.md 4 | -------------------------------------------------------------------------------- /docs/developer-updates/release-notes-v26.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/developer-updates/release-notes-v26.md 4 | -------------------------------------------------------------------------------- /docs/developer-updates/release-notes-v28.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/developer-updates/release-notes-v28.md 4 | -------------------------------------------------------------------------------- /docs/emulator-snapshot.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/emulator-snapshot.md 4 | -------------------------------------------------------------------------------- /docs/execute-scripts.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/execute-scripts.md 4 | -------------------------------------------------------------------------------- /docs/generate-keys.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/generate-keys.md 4 | -------------------------------------------------------------------------------- /docs/get-accounts.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/get-accounts.md 4 | -------------------------------------------------------------------------------- /docs/get-blocks.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/get-blocks.md 4 | -------------------------------------------------------------------------------- /docs/get-collections.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/get-collections.md 4 | -------------------------------------------------------------------------------- /docs/get-events.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/get-events.md 4 | -------------------------------------------------------------------------------- /docs/get-status.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/get-status.md 4 | -------------------------------------------------------------------------------- /docs/get-transactions.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/get-transactions.md 4 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/index.md 4 | -------------------------------------------------------------------------------- /docs/initialize-configuration.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/initialize-configuration.md 4 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/install.md 4 | -------------------------------------------------------------------------------- /docs/manage-configuration.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/manage-configuration.md 4 | -------------------------------------------------------------------------------- /docs/project-app.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/project-app.md 4 | -------------------------------------------------------------------------------- /docs/project-contracts.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/project-contracts.md 4 | -------------------------------------------------------------------------------- /docs/run-tests.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/run-tests.md 4 | -------------------------------------------------------------------------------- /docs/security.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/security.md 4 | -------------------------------------------------------------------------------- /docs/send-signed-transactions.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/send-signed-transactions.md 4 | -------------------------------------------------------------------------------- /docs/send-transactions.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/send-transactions.md 4 | -------------------------------------------------------------------------------- /docs/sign-transaction.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/sign-transaction.md 4 | -------------------------------------------------------------------------------- /docs/signature-generate.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/signature-generate.md 4 | -------------------------------------------------------------------------------- /docs/signature-verify.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/signature-verify.md 4 | -------------------------------------------------------------------------------- /docs/snapshot-save.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/snapshot-save.md 4 | -------------------------------------------------------------------------------- /docs/start-emulator.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/start-emulator.md 4 | -------------------------------------------------------------------------------- /docs/super-commands.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/super-commands.md 4 | -------------------------------------------------------------------------------- /docs/template.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/template.md 4 | -------------------------------------------------------------------------------- /docs/tools.md: -------------------------------------------------------------------------------- 1 | # This document has been moved to a new location: 2 | 3 | https://github.com/onflow/docs/tree/main/docs/tooling/flow-cli/tools.md 4 | -------------------------------------------------------------------------------- /flowkit/README.md: -------------------------------------------------------------------------------- 1 | ## Flowkit 2 | 3 | Note: This module has been migrated to github.com/onflow/flowkit. The latest supported version is v1.13.0. Please use the new module github.com/onflow/flowkit instead for any future updates. Version v1.13.0 is equivalent to version v1.13.0 on the new module. 4 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Exit as soon as any command fails 4 | set -e 5 | 6 | REPO="onflow/flow-cli" 7 | ASSETS_URL="https://github.com/$REPO/releases/download/" 8 | # The version to install (defaults to args[1]) 9 | VERSION="$1" 10 | # The architecture string, set by get_architecture 11 | ARCH="" 12 | 13 | # Optional environment variable for Github API token 14 | # If GITHUB_TOKEN is set, use it in the curl requests to avoid rate limiting 15 | github_token_header="" 16 | if [ -n "$GITHUB_TOKEN" ]; then 17 | github_token_header="Authorization: Bearer $GITHUB_TOKEN" 18 | fi 19 | 20 | # Get the architecture (CPU, OS) of the current system as a string. 21 | # Only MacOS/x86_64/ARM64 and Linux/x86_64/ARM64 architectures are supported. 22 | get_architecture() { 23 | _ostype="$(uname -s)" 24 | _cputype="$(uname -m)" 25 | _targetpath="" 26 | if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then 27 | if sysctl hw.optional.x86_64 | grep -q ': 1'; then 28 | _cputype=x86_64 29 | fi 30 | fi 31 | case "$_ostype" in 32 | Linux) 33 | _ostype=linux 34 | _targetpath=$HOME/.local/bin 35 | ;; 36 | Darwin) 37 | _ostype=darwin 38 | _targetpath=/usr/local/bin 39 | ;; 40 | *) 41 | echo "unrecognized OS type: $_ostype" 42 | return 1 43 | ;; 44 | esac 45 | case "$_cputype" in 46 | x86_64 | x86-64 | x64 | amd64) 47 | _cputype=amd64 48 | ;; 49 | arm64 | aarch64) 50 | _cputype=arm64 51 | ;; 52 | *) 53 | echo "unknown CPU type: $_cputype" 54 | return 1 55 | ;; 56 | esac 57 | _arch="${_ostype}-${_cputype}" 58 | ARCH="${_arch}" 59 | TARGET_PATH="${_targetpath}" 60 | } 61 | 62 | get_latest() { 63 | local version="" 64 | 65 | response=$(curl -H "$github_token_header" -s "https://api.github.com/repos/$REPO/releases/latest" -w "%{http_code}") 66 | 67 | status=$(echo "$response" | tail -n 1) 68 | if [ "$status" -eq "403" ] && [ -n "$github_token_header" ] 69 | then 70 | echo "Failed to get latest release from Github API, is your GITHUB_TOKEN valid? Re-trying without authentication ..." 71 | github_token_header="" 72 | get_latest 73 | fi 74 | 75 | if [ "$status" -ne "200" ] 76 | then 77 | echo "Failed to get latest release from Github API, please manually specify a version to install as an argument to this script." 78 | return 1 79 | fi 80 | 81 | echo "$response" | grep -E 'tag_name' | grep -E "$search_term" | head -n 1 | cut -d '"' -f 4 82 | } 83 | 84 | # Function to download and install a specified version 85 | install_version() { 86 | local version="$1" 87 | local target_name="$2" 88 | 89 | echo "Installing version $version ..." 90 | 91 | tmpfile=$(mktemp 2>/dev/null || mktemp -t flow) 92 | url="$ASSETS_URL$version/flow-cli-$version-$ARCH.tar.gz" 93 | curl -H "$github_token_header" -L --progress-bar "$url" -o "$tmpfile" 94 | 95 | # Ensure we don't receive a not found error as response. 96 | if grep -q "Not Found" "$tmpfile" 97 | then 98 | echo "Version $version could not be found" 99 | exit 1 100 | fi 101 | 102 | [ -d "$TARGET_PATH" ] || mkdir -p "$TARGET_PATH" 103 | 104 | tar -xf "$tmpfile" -C "$TARGET_PATH" 105 | mv "$TARGET_PATH/flow-cli" "$TARGET_PATH/$target_name" 106 | chmod +x "$TARGET_PATH/$target_name" 107 | } 108 | 109 | # Determine the system architecture, download the appropriate binaries, and 110 | # install them in `/usr/local/bin` on macOS and `~/.local/bin` on Linux 111 | # with executable permissions. 112 | main() { 113 | get_architecture || exit 1 114 | 115 | if [ -z "$VERSION" ] 116 | then 117 | echo "Getting version of latest stable release ..." 118 | 119 | VERSION=$(get_latest || exit 1) 120 | fi 121 | 122 | install_version "$VERSION" "flow" 123 | 124 | echo "Successfully installed Flow CLI $VERSION as 'flow' in $TARGET_PATH." 125 | echo "Make sure $TARGET_PATH is in your \$PATH environment variable." 126 | } 127 | 128 | main -------------------------------------------------------------------------------- /internal/accounts/contract-remove.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package accounts 20 | 21 | import ( 22 | "context" 23 | "fmt" 24 | 25 | "github.com/onflow/flow-cli/internal/prompt" 26 | 27 | "github.com/spf13/cobra" 28 | 29 | "github.com/onflow/flowkit/v2" 30 | "github.com/onflow/flowkit/v2/output" 31 | 32 | "github.com/onflow/flow-cli/internal/command" 33 | ) 34 | 35 | type flagsRemoveContract struct { 36 | Signer string `default:"emulator-account" flag:"signer" info:"Account name from configuration used to sign the transaction"` 37 | Include []string `default:"" flag:"include" info:"Fields to include in the output. Valid values: contracts."` 38 | Network string `default:"" flag:"network" info:"Network name from configuration to use"` 39 | } 40 | 41 | var flagsRemove = flagsRemoveContract{} 42 | 43 | var removeCommand = &command.Command{ 44 | Cmd: &cobra.Command{ 45 | Use: "remove-contract ", 46 | Short: "Remove a contract deployed to an account", 47 | Example: `flow accounts remove-contract FungibleToken`, 48 | Args: cobra.ExactArgs(1), 49 | }, 50 | Flags: &flagsRemove, 51 | RunS: removeContract, 52 | } 53 | 54 | func removeContract( 55 | args []string, 56 | _ command.GlobalFlags, 57 | logger output.Logger, 58 | flow flowkit.Services, 59 | state *flowkit.State, 60 | ) (command.Result, error) { 61 | contractName := args[0] 62 | 63 | from, err := state.Accounts().ByName(flagsRemove.Signer) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | id, err := flow.RemoveContract(context.Background(), from, contractName) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | removeFromState := prompt.RemoveContractFromFlowJSONPrompt(contractName) 74 | 75 | if removeFromState { 76 | // If a network flag is provided, remove from that networks deployments 77 | // Otherwise, remove from all deployments 78 | if flagsRemove.Network != "" { 79 | state.Deployments().ByAccountAndNetwork(from.Name, flagsRemove.Network).RemoveContract(contractName) 80 | } else { 81 | for i := range state.Deployments().All() { 82 | if state.Deployments().All()[i].Account == from.Name { 83 | state.Deployments().All()[i].RemoveContract(contractName) 84 | } 85 | } 86 | } 87 | 88 | err = state.SaveDefault() 89 | if err != nil { 90 | return nil, err 91 | } 92 | } 93 | 94 | logger.Info(fmt.Sprintf( 95 | "Contract %s removed from account %s with transaction ID: %s.", 96 | contractName, 97 | from.Address, 98 | id.String(), 99 | )) 100 | 101 | account, err := flow.GetAccount(context.Background(), from.Address) 102 | if err != nil { 103 | return nil, err 104 | } 105 | return &accountResult{ 106 | Account: account, 107 | include: flagsRemove.Include, 108 | }, nil 109 | } 110 | -------------------------------------------------------------------------------- /internal/accounts/contract-update.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package accounts 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | 24 | "github.com/onflow/flow-cli/internal/command" 25 | ) 26 | 27 | var updateContractFlags = deployContractFlags{} 28 | 29 | var updateCommand = &command.Command{ 30 | Cmd: &cobra.Command{ 31 | Use: "update-contract ", 32 | Short: "Update a contract deployed to an account", 33 | Example: `flow accounts update-contract ./FungibleToken.cdc helloArg`, 34 | Args: cobra.MinimumNArgs(1), 35 | }, 36 | Flags: &updateContractFlags, 37 | RunS: deployContract(true, &updateContractFlags), 38 | } 39 | -------------------------------------------------------------------------------- /internal/accounts/fund.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package accounts 20 | 21 | import ( 22 | "fmt" 23 | "time" 24 | 25 | flowsdk "github.com/onflow/flow-go-sdk" 26 | 27 | "github.com/pkg/browser" 28 | "github.com/spf13/cobra" 29 | 30 | "github.com/onflow/flowkit/v2" 31 | "github.com/onflow/flowkit/v2/output" 32 | 33 | "github.com/onflow/flow-cli/internal/command" 34 | ) 35 | 36 | type flagsFund struct { 37 | Include []string `default:"" flag:"include" info:"Fields to include in the output. Valid values: contracts."` 38 | } 39 | 40 | var fundFlags = flagsFund{} 41 | 42 | var fundCommand = &command.Command{ 43 | Cmd: &cobra.Command{ 44 | Use: "fund
", 45 | Short: "Funds an account by address through the Testnet Faucet", 46 | Example: "flow accounts fund 8e94eaa81771313a", 47 | Args: cobra.ExactArgs(1), 48 | }, 49 | Flags: &fundFlags, 50 | Run: fund, 51 | } 52 | 53 | func fund( 54 | args []string, 55 | _ command.GlobalFlags, 56 | logger output.Logger, 57 | _ flowkit.ReaderWriter, 58 | flow flowkit.Services, 59 | ) (command.Result, error) { 60 | address := flowsdk.HexToAddress(args[0]) 61 | if !address.IsValid(flowsdk.Testnet) { 62 | return nil, fmt.Errorf("unsupported address %s, faucet can only work for valid Testnet addresses", address.String()) 63 | } 64 | 65 | logger.Info( 66 | fmt.Sprintf( 67 | "Opening the Testnet faucet to fund 0x%s on your native browser."+ 68 | "\n\nIf there is an issue, please use this link instead: %s", 69 | address.String(), 70 | testnetFaucetURL(address), 71 | )) 72 | // wait for the user to read the message 73 | time.Sleep(5 * time.Second) 74 | 75 | if err := browser.OpenURL(testnetFaucetURL(address)); err != nil { 76 | return nil, err 77 | } 78 | 79 | return nil, nil 80 | } 81 | -------------------------------------------------------------------------------- /internal/accounts/get.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package accounts 20 | 21 | import ( 22 | "context" 23 | "fmt" 24 | 25 | flowsdk "github.com/onflow/flow-go-sdk" 26 | "github.com/spf13/cobra" 27 | 28 | "github.com/onflow/flowkit/v2" 29 | "github.com/onflow/flowkit/v2/output" 30 | 31 | "github.com/onflow/flow-cli/internal/command" 32 | ) 33 | 34 | type flagsGet struct { 35 | Include []string `default:"" flag:"include" info:"Fields to include in the output. Valid values: contracts."` 36 | } 37 | 38 | var getFlags = flagsGet{} 39 | 40 | var getCommand = &command.Command{ 41 | Cmd: &cobra.Command{ 42 | Use: "get
", 43 | Short: "Gets an account by address", 44 | Example: "flow accounts get f8d6e0586b0a20c7", 45 | Args: cobra.ExactArgs(1), 46 | }, 47 | Flags: &getFlags, 48 | Run: get, 49 | } 50 | 51 | func get( 52 | args []string, 53 | _ command.GlobalFlags, 54 | logger output.Logger, 55 | _ flowkit.ReaderWriter, 56 | flow flowkit.Services, 57 | ) (command.Result, error) { 58 | address := flowsdk.HexToAddress(args[0]) 59 | 60 | logger.StartProgress(fmt.Sprintf("Loading account %s...", address)) 61 | defer logger.StopProgress() 62 | 63 | account, err := flow.GetAccount(context.Background(), address) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | return &accountResult{ 69 | Account: account, 70 | include: getFlags.Include, 71 | }, nil 72 | } 73 | -------------------------------------------------------------------------------- /internal/blocks/blocks.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package blocks 20 | 21 | import ( 22 | "bytes" 23 | "fmt" 24 | 25 | "github.com/onflow/flow-go-sdk" 26 | "github.com/spf13/cobra" 27 | 28 | "github.com/onflow/flow-cli/internal/command" 29 | "github.com/onflow/flow-cli/internal/events" 30 | "github.com/onflow/flow-cli/internal/util" 31 | ) 32 | 33 | var Cmd = &cobra.Command{ 34 | Use: "blocks", 35 | Short: "Retrieve blocks", 36 | TraverseChildren: true, 37 | GroupID: "resources", 38 | } 39 | 40 | func init() { 41 | getCommand.AddToParent(Cmd) 42 | } 43 | 44 | type blockResult struct { 45 | block *flow.Block 46 | events []flow.BlockEvents 47 | collections []*flow.Collection 48 | included []string 49 | } 50 | 51 | func (r *blockResult) JSON() any { 52 | result := make(map[string]any) 53 | result["blockId"] = r.block.ID.String() 54 | result["parentId"] = r.block.ParentID.String() 55 | result["height"] = r.block.Height 56 | result["totalSeals"] = len(r.block.Seals) 57 | result["totalCollections"] = len(r.block.CollectionGuarantees) 58 | 59 | collections := make([]any, 0, len(r.block.CollectionGuarantees)) 60 | for i, guarantee := range r.block.CollectionGuarantees { 61 | collection := make(map[string]any) 62 | collection["id"] = guarantee.CollectionID.String() 63 | 64 | if command.ContainsFlag(r.included, "transactions") { 65 | txs := make([]string, 0) 66 | for _, tx := range r.collections[i].TransactionIDs { 67 | txs = append(txs, tx.String()) 68 | } 69 | collection["transactions"] = txs 70 | } 71 | 72 | collections = append(collections, collection) 73 | } 74 | 75 | result["collection"] = collections 76 | return result 77 | } 78 | 79 | func blockStatusToString(code flow.BlockStatus) string { 80 | switch code { 81 | case 1: 82 | return "Finalized" 83 | case 2: 84 | return "Sealed" 85 | default: 86 | return "Unknown" 87 | } 88 | } 89 | 90 | func (r *blockResult) String() string { 91 | var b bytes.Buffer 92 | writer := util.CreateTabWriter(&b) 93 | 94 | _, _ = fmt.Fprintf(writer, "Block ID\t%s\n", r.block.ID) 95 | _, _ = fmt.Fprintf(writer, "Parent ID\t%s\n", r.block.ParentID) 96 | _, _ = fmt.Fprintf(writer, "Proposal Timestamp\t%s\n", r.block.Timestamp) 97 | _, _ = fmt.Fprintf(writer, "Proposal Timestamp Unix\t%d\n", r.block.Timestamp.Unix()) 98 | _, _ = fmt.Fprintf(writer, "Height\t%v\n", r.block.Height) 99 | _, _ = fmt.Fprintf(writer, "Status\t%s\n", blockStatusToString(r.block.Status)) 100 | 101 | _, _ = fmt.Fprintf(writer, "Total Seals\t%v\n", len(r.block.Seals)) 102 | 103 | _, _ = fmt.Fprintf(writer, "Total Collections\t%v\n", len(r.block.CollectionGuarantees)) 104 | 105 | for i, guarantee := range r.block.CollectionGuarantees { 106 | _, _ = fmt.Fprintf(writer, " Collection %d:\t%s\n", i, guarantee.CollectionID) 107 | 108 | if command.ContainsFlag(r.included, "transactions") { 109 | for x, tx := range r.collections[i].TransactionIDs { 110 | _, _ = fmt.Fprintf(writer, " Transaction %d: %s\n", x, tx) 111 | } 112 | } 113 | } 114 | 115 | if len(r.events) > 0 { 116 | _, _ = fmt.Fprintf(writer, "\n") 117 | 118 | e := events.EventResult{BlockEvents: r.events} 119 | _, _ = fmt.Fprintf(writer, "%s", e.String()) 120 | } 121 | 122 | _ = writer.Flush() 123 | return b.String() 124 | } 125 | 126 | func (r *blockResult) Oneliner() string { 127 | return r.block.ID.String() 128 | } 129 | -------------------------------------------------------------------------------- /internal/blocks/blocks_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package blocks 20 | 21 | import ( 22 | "strings" 23 | "testing" 24 | 25 | "github.com/onflow/flow-go-sdk" 26 | "github.com/stretchr/testify/assert" 27 | "github.com/stretchr/testify/mock" 28 | 29 | "github.com/onflow/flowkit/v2" 30 | "github.com/onflow/flowkit/v2/tests" 31 | 32 | "github.com/onflow/flow-cli/internal/command" 33 | "github.com/onflow/flow-cli/internal/util" 34 | ) 35 | 36 | func Test_GetBlock(t *testing.T) { 37 | srv, _, rw := util.TestMocks(t) 38 | 39 | t.Run("Success", func(t *testing.T) { 40 | inArgs := []string{"100"} 41 | blockFlags.Events = "A.foo" 42 | blockFlags.Include = []string{"transactions"} 43 | 44 | srv.GetEvents.Run(func(args mock.Arguments) { 45 | assert.Equal(t, "A.foo", args.Get(1).([]string)[0]) 46 | assert.Equal(t, uint64(100), args.Get(2).(uint64)) 47 | assert.Equal(t, uint64(100), args.Get(3).(uint64)) 48 | }).Return(nil, nil) 49 | 50 | srv.GetCollection.Return(nil, nil) 51 | 52 | returnBlock := tests.NewBlock() 53 | returnBlock.Height = uint64(100) 54 | 55 | srv.GetBlock.Run(func(args mock.Arguments) { 56 | assert.Equal(t, uint64(100), args.Get(1).(flowkit.BlockQuery).Height) 57 | }).Return(returnBlock, nil) 58 | 59 | result, err := get(inArgs, command.GlobalFlags{}, util.NoLogger, rw, srv.Mock) 60 | assert.NotNil(t, result) 61 | assert.NoError(t, err) 62 | }) 63 | } 64 | 65 | func Test_Result(t *testing.T) { 66 | result := blockResult{ 67 | block: tests.NewBlock(), 68 | collections: []*flow.Collection{tests.NewCollection()}, 69 | } 70 | 71 | assert.Equal(t, strings.TrimPrefix(` 72 | Block ID 0303030303030303030303030303030303030303030303030303030303030303 73 | Parent ID 0404040404040404040404040404040404040404040404040404040404040404 74 | Proposal Timestamp 2020-06-04 16:43:21 +0000 UTC 75 | Proposal Timestamp Unix 1591289001 76 | Height 1 77 | Status Unknown 78 | Total Seals 1 79 | Total Collections 3 80 | Collection 0: 0202020202020202020202020202020202020202020202020202020202020202 81 | Collection 1: 0404040404040404040404040404040404040404040404040404040404040404 82 | Collection 2: 0606060606060606060606060606060606060606060606060606060606060606 83 | `, "\n"), result.String()) 84 | 85 | assert.Equal( 86 | t, 87 | map[string]any{ 88 | "blockId": "0303030303030303030303030303030303030303030303030303030303030303", 89 | "collection": []any{ 90 | map[string]any{"id": "0202020202020202020202020202020202020202020202020202020202020202"}, 91 | map[string]any{"id": "0404040404040404040404040404040404040404040404040404040404040404"}, 92 | map[string]any{"id": "0606060606060606060606060606060606060606060606060606060606060606"}, 93 | }, 94 | "height": uint64(1), 95 | "parentId": "0404040404040404040404040404040404040404040404040404040404040404", 96 | "totalCollections": 3, 97 | "totalSeals": 1, 98 | }, 99 | result.JSON(), 100 | ) 101 | } 102 | -------------------------------------------------------------------------------- /internal/blocks/get.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package blocks 20 | 21 | import ( 22 | "context" 23 | 24 | flowsdk "github.com/onflow/flow-go-sdk" 25 | "github.com/spf13/cobra" 26 | 27 | "github.com/onflow/flowkit/v2" 28 | "github.com/onflow/flowkit/v2/output" 29 | 30 | "github.com/onflow/flow-cli/internal/command" 31 | ) 32 | 33 | type flagsBlocks struct { 34 | Events string `default:"" flag:"events" info:"List events of this type for the block"` 35 | Include []string `default:"" flag:"include" info:"Fields to include in the output. Valid values: transactions."` 36 | } 37 | 38 | var blockFlags = flagsBlocks{} 39 | 40 | var getCommand = &command.Command{ 41 | Cmd: &cobra.Command{ 42 | Use: "get ", 43 | Short: "Get block info", 44 | Example: "flow blocks get latest --network testnet", 45 | Args: cobra.ExactArgs(1), 46 | }, 47 | Flags: &blockFlags, 48 | Run: get, 49 | } 50 | 51 | func get( 52 | args []string, 53 | _ command.GlobalFlags, 54 | logger output.Logger, 55 | _ flowkit.ReaderWriter, 56 | flow flowkit.Services, 57 | ) (command.Result, error) { 58 | 59 | query, err := flowkit.NewBlockQuery(args[0]) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | logger.StartProgress("Fetching Block...") 65 | defer logger.StopProgress() 66 | block, err := flow.GetBlock(context.Background(), query) 67 | if err != nil { 68 | return nil, err 69 | } 70 | 71 | var events []flowsdk.BlockEvents 72 | if blockFlags.Events != "" { 73 | events, err = flow.GetEvents( 74 | context.Background(), 75 | []string{blockFlags.Events}, 76 | block.Height, 77 | block.Height, 78 | nil, 79 | ) 80 | if err != nil { 81 | return nil, err 82 | } 83 | } 84 | 85 | collections := make([]*flowsdk.Collection, 0) 86 | if command.ContainsFlag(blockFlags.Include, "transactions") { 87 | for _, guarantee := range block.CollectionGuarantees { 88 | collection, err := flow.GetCollection(context.Background(), guarantee.CollectionID) 89 | if err != nil { 90 | return nil, err 91 | } 92 | collections = append(collections, collection) 93 | } 94 | } 95 | 96 | return &blockResult{ 97 | block: block, 98 | events: events, 99 | collections: collections, 100 | included: blockFlags.Include, 101 | }, nil 102 | } 103 | -------------------------------------------------------------------------------- /internal/cadence/cadence.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package cadence 20 | 21 | import ( 22 | "github.com/onflow/cadence/cmd/execute" 23 | "github.com/spf13/cobra" 24 | 25 | "github.com/onflow/flow-cli/internal/cadence/languageserver" 26 | ) 27 | 28 | var Cmd = &cobra.Command{ 29 | Use: "cadence", 30 | Short: "Execute Cadence code", 31 | GroupID: "tools", 32 | Run: func(cmd *cobra.Command, args []string) { 33 | if len(args) > 0 { 34 | execute.Execute(args, nil) 35 | } else { 36 | repl, err := execute.NewConsoleREPL() 37 | if err != nil { 38 | panic(err) 39 | } 40 | repl.Run() 41 | } 42 | }, 43 | } 44 | 45 | func init() { 46 | Cmd.AddCommand(languageserver.Cmd) 47 | lintCommand.AddToParent(Cmd) 48 | } 49 | -------------------------------------------------------------------------------- /internal/cadence/languageserver/languageserver.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package languageserver 20 | 21 | import ( 22 | "log" 23 | 24 | "github.com/onflow/cadence-tools/languageserver" 25 | "github.com/psiemens/sconfig" 26 | "github.com/spf13/cobra" 27 | 28 | "github.com/onflow/flow-cli/internal/util" 29 | ) 30 | 31 | type config struct { 32 | EnableFlowClient bool `default:"true" flag:"enable-flow-client" info:"Enable Flow client functionality"` 33 | } 34 | 35 | var conf config 36 | 37 | var Cmd = &cobra.Command{ 38 | Use: "language-server", 39 | Short: "Start the Cadence language server", 40 | Run: func(cmd *cobra.Command, args []string) { 41 | languageserver.RunWithStdio(conf.EnableFlowClient) 42 | }, 43 | } 44 | 45 | func init() { 46 | initConfig() 47 | } 48 | 49 | func initConfig() { 50 | err := sconfig.New(&conf). 51 | FromEnvironment(util.EnvPrefix). 52 | BindFlags(Cmd.PersistentFlags()). 53 | Parse() 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /internal/collections/collections.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package collections 20 | 21 | import ( 22 | "bytes" 23 | "fmt" 24 | "strings" 25 | 26 | "github.com/onflow/flow-go-sdk" 27 | "github.com/spf13/cobra" 28 | 29 | "github.com/onflow/flow-cli/internal/util" 30 | ) 31 | 32 | var Cmd = &cobra.Command{ 33 | Use: "collections", 34 | Short: "Retrieve collections", 35 | TraverseChildren: true, 36 | GroupID: "resources", 37 | } 38 | 39 | func init() { 40 | getCommand.AddToParent(Cmd) 41 | } 42 | 43 | type collectionResult struct { 44 | *flow.Collection 45 | } 46 | 47 | func (c *collectionResult) JSON() any { 48 | txIDs := make([]string, 0) 49 | 50 | for _, tx := range c.Collection.TransactionIDs { 51 | txIDs = append(txIDs, tx.String()) 52 | } 53 | 54 | return txIDs 55 | } 56 | 57 | func (c *collectionResult) String() string { 58 | var b bytes.Buffer 59 | writer := util.CreateTabWriter(&b) 60 | 61 | _, _ = fmt.Fprintf(writer, "Collection ID %s:\n", c.Collection.ID()) 62 | 63 | for _, tx := range c.Collection.TransactionIDs { 64 | _, _ = fmt.Fprintf(writer, "%s\n", tx.String()) 65 | } 66 | 67 | _ = writer.Flush() 68 | 69 | return b.String() 70 | } 71 | 72 | func (c *collectionResult) Oneliner() string { 73 | return strings.Join(c.JSON().([]string), ",") 74 | } 75 | -------------------------------------------------------------------------------- /internal/collections/collections_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package collections 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/onflow/flow-go-sdk" 25 | "github.com/stretchr/testify/assert" 26 | "github.com/stretchr/testify/mock" 27 | "github.com/stretchr/testify/require" 28 | 29 | "github.com/onflow/flow-cli/internal/command" 30 | "github.com/onflow/flow-cli/internal/util" 31 | ) 32 | 33 | func Test_Get(t *testing.T) { 34 | srv, _, rw := util.TestMocks(t) 35 | 36 | t.Run("Success", func(t *testing.T) { 37 | inArgs := []string{util.TestID.String()} 38 | 39 | srv.GetCollection.Run(func(args mock.Arguments) { 40 | id := args.Get(1).(flow.Identifier) 41 | assert.Equal(t, inArgs[0], id.String()) 42 | }) 43 | 44 | result, err := get(inArgs, command.GlobalFlags{}, util.NoLogger, rw, srv.Mock) 45 | require.NoError(t, err) 46 | require.NotNil(t, result) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /internal/collections/get.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package collections 20 | 21 | import ( 22 | "context" 23 | "fmt" 24 | 25 | flowsdk "github.com/onflow/flow-go-sdk" 26 | "github.com/spf13/cobra" 27 | 28 | "github.com/onflow/flowkit/v2" 29 | "github.com/onflow/flowkit/v2/output" 30 | 31 | "github.com/onflow/flow-cli/internal/command" 32 | ) 33 | 34 | type flagsCollections struct{} 35 | 36 | var collectionFlags = flagsCollections{} 37 | 38 | var getCommand = &command.Command{ 39 | Cmd: &cobra.Command{ 40 | Use: "get ", 41 | Short: "Get collection info", 42 | Example: "flow collections get 270d...9c31e", 43 | Args: cobra.ExactArgs(1), 44 | }, 45 | Flags: &collectionFlags, 46 | Run: get, 47 | } 48 | 49 | func get( 50 | args []string, 51 | _ command.GlobalFlags, 52 | logger output.Logger, 53 | _ flowkit.ReaderWriter, 54 | flow flowkit.Services, 55 | ) (command.Result, error) { 56 | id := flowsdk.HexToID(args[0]) 57 | 58 | logger.StartProgress(fmt.Sprintf("Loading collection %s", id)) 59 | defer logger.StopProgress() 60 | 61 | collection, err := flow.GetCollection(context.Background(), id) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | return &collectionResult{collection}, nil 67 | } 68 | -------------------------------------------------------------------------------- /internal/command/global_flags.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package command 20 | 21 | import ( 22 | "fmt" 23 | "os" 24 | 25 | "github.com/psiemens/sconfig" 26 | "github.com/spf13/cobra" 27 | 28 | "github.com/onflow/flowkit/v2/config" 29 | 30 | "github.com/onflow/flow-cli/internal/util" 31 | ) 32 | 33 | // Flags initialized to default values. 34 | var Flags = GlobalFlags{ 35 | Filter: "", 36 | Format: FormatText, 37 | Save: "", 38 | Host: "", 39 | HostNetworkKey: "", 40 | Network: config.EmulatorNetwork.Name, 41 | Log: logLevelInfo, 42 | Yes: false, 43 | ConfigPaths: config.DefaultPaths(), 44 | SkipVersionCheck: false, 45 | } 46 | 47 | // InitFlags init all the global persistent flags. 48 | func InitFlags(cmd *cobra.Command) { 49 | cmd.PersistentFlags().StringVarP( 50 | &Flags.Filter, 51 | "filter", 52 | "x", 53 | Flags.Filter, 54 | "Filter result values by property name", 55 | ) 56 | 57 | cmd.PersistentFlags().StringVarP( 58 | &Flags.Format, 59 | "format", 60 | "", 61 | Flags.Format, 62 | "Format result values", 63 | ) 64 | 65 | cmd.PersistentFlags().StringVarP( 66 | &Flags.Host, 67 | "host", 68 | "", 69 | Flags.Host, 70 | "Flow Access API host address", 71 | ) 72 | 73 | cmd.PersistentFlags().StringVarP( 74 | &Flags.HostNetworkKey, 75 | "network-key", 76 | "", 77 | Flags.HostNetworkKey, 78 | "Flow Access API host network key for secure client connections", 79 | ) 80 | 81 | cmd.PersistentFlags().StringVarP( 82 | &Flags.Format, 83 | "output", 84 | "o", 85 | Flags.Format, 86 | "Output format, options: \"text\", \"json\", \"inline\"", 87 | ) 88 | 89 | cmd.PersistentFlags().StringVarP( 90 | &Flags.Save, 91 | "save", 92 | "s", 93 | Flags.Save, 94 | "Save result to a filename", 95 | ) 96 | 97 | cmd.PersistentFlags().StringVarP( 98 | &Flags.Log, 99 | "log", 100 | "l", 101 | Flags.Log, 102 | "Log level, options: \"debug\", \"info\", \"error\", \"none\"", 103 | ) 104 | 105 | cmd.PersistentFlags().StringSliceVarP( 106 | &Flags.ConfigPaths, 107 | "config-path", 108 | "f", 109 | Flags.ConfigPaths, 110 | "Path to flow configuration file", 111 | ) 112 | 113 | cmd.PersistentFlags().StringVarP( 114 | &Flags.Network, 115 | "network", 116 | "n", 117 | Flags.Network, 118 | "Network from configuration file", 119 | ) 120 | 121 | cmd.PersistentFlags().BoolVarP( 122 | &Flags.Yes, 123 | "yes", 124 | "y", 125 | Flags.Yes, 126 | "Approve any prompts", 127 | ) 128 | 129 | cmd.PersistentFlags().BoolVarP( 130 | &Flags.SkipVersionCheck, 131 | "skip-version-check", 132 | "", 133 | Flags.SkipVersionCheck, 134 | "Skip version check during start up", 135 | ) 136 | } 137 | 138 | // bindFlags bind all the flags needed. 139 | func bindFlags(command Command) { 140 | err := sconfig.New(command.Flags). 141 | FromEnvironment(util.EnvPrefix). 142 | BindFlags(command.Cmd.PersistentFlags()). 143 | Parse() 144 | if err != nil { 145 | fmt.Fprintln(os.Stderr, err) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /internal/command/global_flags_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package command_test 20 | 21 | import ( 22 | "fmt" 23 | "strconv" 24 | "strings" 25 | "testing" 26 | 27 | "github.com/spf13/cobra" 28 | 29 | "github.com/onflow/flow-cli/internal/command" 30 | ) 31 | 32 | func TestInitFlags(t *testing.T) { 33 | cmd := &cobra.Command{} 34 | command.InitFlags(cmd) 35 | 36 | flags := []struct { 37 | name string 38 | expected string 39 | }{ 40 | {"filter", command.Flags.Filter}, 41 | {"format", command.Flags.Format}, 42 | {"save", command.Flags.Save}, 43 | {"host", command.Flags.Host}, 44 | {"network-key", command.Flags.HostNetworkKey}, 45 | {"network", command.Flags.Network}, 46 | {"log", command.Flags.Log}, 47 | {"yes", strconv.FormatBool(command.Flags.Yes)}, 48 | {"config-path", fmt.Sprintf("[%s]", strings.Join(command.Flags.ConfigPaths, ","))}, 49 | {"skip-version-check", strconv.FormatBool(command.Flags.SkipVersionCheck)}, 50 | } 51 | 52 | for _, flag := range flags { 53 | f := cmd.PersistentFlags().Lookup(flag.name) 54 | if f == nil { 55 | t.Errorf("Flag %s was not initialized", flag.name) 56 | } else if f.DefValue != flag.expected { 57 | t.Errorf("Flag %s was not initialized with correct default value. Value: %s, Expected: %s", flag.name, f.Value.String(), flag.expected) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /internal/command/template.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package command 20 | 21 | var UsageTemplate = `Usage:{{if .Runnable}} 22 | {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} 23 | {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} 24 | 25 | Aliases: 26 | {{.NameAndAliases}}{{end}}{{if .HasExample}} 27 | 28 | Examples: 29 | {{.Example}}{{end}} 30 | {{if .HasAvailableSubCommands}}{{if (eq .Name "flow")}} 31 | 👋 Welcome Flow developer! 32 | If you are starting a new flow project use our super commands, start by running 'flow init'. {{end}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}} 33 | Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}} 34 | {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}} 35 | 36 | {{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}} 37 | {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}} 38 | 39 | Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}} 40 | {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} 41 | 42 | Flags: 43 | {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} 44 | 45 | Global Flags: 46 | {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} 47 | 48 | Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} 49 | {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} 50 | 51 | Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} 52 | ` 53 | -------------------------------------------------------------------------------- /internal/config/add-deployment.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package config 20 | 21 | import ( 22 | "fmt" 23 | 24 | "github.com/onflow/flow-cli/internal/prompt" 25 | 26 | "github.com/spf13/cobra" 27 | 28 | "github.com/onflow/flowkit/v2" 29 | "github.com/onflow/flowkit/v2/config" 30 | "github.com/onflow/flowkit/v2/output" 31 | 32 | "github.com/onflow/flow-cli/internal/command" 33 | ) 34 | 35 | type flagsAddDeployment struct { 36 | Network string `flag:"network" info:"Network name used for deployment"` 37 | Account string `flag:"account" info:"Account name used for deployment"` 38 | Contracts []string `flag:"contract" info:"Name of the contract to be deployed"` 39 | } 40 | 41 | var addDeploymentFlags = flagsAddDeployment{} 42 | 43 | var addDeploymentCommand = &command.Command{ 44 | Cmd: &cobra.Command{ 45 | Use: "deployment", 46 | Short: "Add deployment to configuration", 47 | Example: "flow config add deployment", 48 | Args: cobra.NoArgs, 49 | }, 50 | Flags: &addDeploymentFlags, 51 | RunS: addDeployment, 52 | } 53 | 54 | func addDeployment( 55 | _ []string, 56 | globalFlags command.GlobalFlags, 57 | _ output.Logger, 58 | _ flowkit.Services, 59 | state *flowkit.State, 60 | ) (command.Result, error) { 61 | raw, flagsProvided, err := flagsToDeploymentData(addDeploymentFlags) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | if !flagsProvided { 67 | raw = prompt.NewDeploymentPrompt(*state.Networks(), state.Config().Accounts, *state.Contracts()) 68 | } 69 | 70 | deployment := state.Deployments().ByAccountAndNetwork(raw.Account, raw.Network) 71 | if deployment == nil { 72 | // add deployment if non-existing 73 | state.Deployments().AddOrUpdate(config.Deployment{ 74 | Network: raw.Network, 75 | Account: raw.Account, 76 | }) 77 | deployment = state.Deployments().ByAccountAndNetwork(raw.Account, raw.Network) 78 | } 79 | 80 | for _, c := range raw.Contracts { 81 | deployment.AddContract(config.ContractDeployment{Name: c}) 82 | } 83 | 84 | err = state.SaveEdited(globalFlags.ConfigPaths) 85 | if err != nil { 86 | return nil, err 87 | } 88 | 89 | return &result{ 90 | result: "Deployment added to the configuration.\nYou can deploy using 'flow project deploy' command", 91 | }, nil 92 | } 93 | 94 | func flagsToDeploymentData(flags flagsAddDeployment) (*prompt.DeploymentData, bool, error) { 95 | if flags.Network == "" && flags.Account == "" && len(flags.Contracts) == 0 { 96 | return nil, false, nil 97 | } 98 | 99 | if flags.Network == "" { 100 | return nil, true, fmt.Errorf("network name must be provided") 101 | } else if flags.Account == "" { 102 | return nil, true, fmt.Errorf("account name must be provided") 103 | } else if len(flags.Contracts) == 0 { 104 | return nil, true, fmt.Errorf("at least one contract name must be provided") 105 | } 106 | 107 | return &prompt.DeploymentData{ 108 | Network: flags.Network, 109 | Account: flags.Account, 110 | Contracts: flags.Contracts, 111 | }, true, nil 112 | } 113 | -------------------------------------------------------------------------------- /internal/config/add-network.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package config 20 | 21 | import ( 22 | "fmt" 23 | "net/url" 24 | 25 | "github.com/onflow/flow-cli/internal/prompt" 26 | 27 | "github.com/spf13/cobra" 28 | 29 | "github.com/onflow/flowkit/v2" 30 | "github.com/onflow/flowkit/v2/config" 31 | "github.com/onflow/flowkit/v2/output" 32 | 33 | "github.com/onflow/flow-cli/internal/command" 34 | "github.com/onflow/flow-cli/internal/util" 35 | ) 36 | 37 | type flagsAddNetwork struct { 38 | Name string `flag:"name" info:"Network name"` 39 | Host string `flag:"host" info:"Flow Access API host address"` 40 | Key string `flag:"network-key" info:"Flow Access API host network key for secure client connections"` 41 | } 42 | 43 | var addNetworkFlags = flagsAddNetwork{} 44 | 45 | var addNetworkCommand = &command.Command{ 46 | Cmd: &cobra.Command{ 47 | Use: "network", 48 | Short: "Add network to configuration", 49 | Example: "flow config add network", 50 | Args: cobra.NoArgs, 51 | }, 52 | Flags: &addNetworkFlags, 53 | RunS: addNetwork, 54 | } 55 | 56 | func addNetwork( 57 | _ []string, 58 | globalFlags command.GlobalFlags, 59 | _ output.Logger, 60 | _ flowkit.Services, 61 | state *flowkit.State, 62 | ) (command.Result, error) { 63 | raw, flagsProvided, err := flagsToNetworkData(addNetworkFlags) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | if !flagsProvided { 69 | raw = prompt.NewNetworkPrompt() 70 | } 71 | 72 | state.Networks().AddOrUpdate(config.Network{ 73 | Name: raw["name"], 74 | Host: raw["host"], 75 | Key: raw["key"], 76 | }) 77 | 78 | err = state.SaveEdited(globalFlags.ConfigPaths) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | return &result{ 84 | result: fmt.Sprintf("Network %s added to the configuration", raw["name"]), 85 | }, nil 86 | } 87 | 88 | func flagsToNetworkData(flags flagsAddNetwork) (map[string]string, bool, error) { 89 | if flags.Name == "" && flags.Host == "" { 90 | return nil, false, nil 91 | } 92 | 93 | if flags.Name == "" { 94 | return nil, true, fmt.Errorf("name must be provided") 95 | } else if flags.Host == "" { 96 | return nil, true, fmt.Errorf("host must be provided") 97 | } 98 | 99 | _, err := url.ParseRequestURI(flags.Host) 100 | if err != nil { 101 | return nil, true, err 102 | } 103 | 104 | err = util.ValidateECDSAP256Pub(flags.Key) 105 | if err != nil { 106 | return nil, true, fmt.Errorf("invalid network-key provided") 107 | } 108 | 109 | return map[string]string{ 110 | "name": flags.Name, 111 | "host": flags.Host, 112 | "key": flags.Key, 113 | }, true, nil 114 | } 115 | -------------------------------------------------------------------------------- /internal/config/add.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package config 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | var addCmd = &cobra.Command{ 26 | Use: "add ", 27 | Short: "Add resource to configuration", 28 | Example: "flow config add account", 29 | Args: cobra.ExactArgs(1), 30 | TraverseChildren: true, 31 | } 32 | 33 | func init() { 34 | addAccountCommand.AddToParent(addCmd) 35 | addContractCommand.AddToParent(addCmd) 36 | addDeploymentCommand.AddToParent(addCmd) 37 | addNetworkCommand.AddToParent(addCmd) 38 | } 39 | -------------------------------------------------------------------------------- /internal/config/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package config 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | var Cmd = &cobra.Command{ 26 | Use: "config", 27 | Short: "Utilities to manage configuration", 28 | TraverseChildren: true, 29 | } 30 | 31 | func init() { 32 | Cmd.AddCommand(addCmd) 33 | Cmd.AddCommand(removeCmd) 34 | } 35 | 36 | type result struct { 37 | result string 38 | } 39 | 40 | func (r *result) JSON() any { 41 | return nil 42 | } 43 | 44 | func (r *result) String() string { 45 | if r.result != "" { 46 | return r.result 47 | } 48 | 49 | return "" 50 | } 51 | 52 | func (r *result) Oneliner() string { 53 | return "" 54 | } 55 | -------------------------------------------------------------------------------- /internal/config/init.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package config 20 | 21 | import ( 22 | "fmt" 23 | "os" 24 | 25 | "github.com/onflow/flowkit/v2/accounts" 26 | 27 | "github.com/onflow/flow-go-sdk/crypto" 28 | 29 | "github.com/onflow/flowkit/v2" 30 | "github.com/onflow/flowkit/v2/config" 31 | ) 32 | 33 | // InitConfigParameters holds all necessary parameters for initializing the configuration. 34 | type InitConfigParameters struct { 35 | ServicePrivateKey string 36 | ServiceKeySigAlgo string 37 | ServiceKeyHashAlgo string 38 | Reset bool 39 | Global bool 40 | TargetDirectory string 41 | } 42 | 43 | // InitializeConfiguration creates the Flow configuration json file based on the provided parameters. 44 | func InitializeConfiguration(params InitConfigParameters, readerWriter flowkit.ReaderWriter) (*flowkit.State, error) { 45 | var path string 46 | if params.TargetDirectory != "" { 47 | path = fmt.Sprintf("%s/flow.json", params.TargetDirectory) 48 | 49 | // Create the directory if it doesn't exist 50 | err := readerWriter.MkdirAll(params.TargetDirectory, os.ModePerm) 51 | if err != nil { 52 | return nil, fmt.Errorf("failed to create target directory: %w", err) 53 | } 54 | } else { 55 | // Otherwise, choose between the default and global paths 56 | if params.Global { 57 | path = config.GlobalPath() 58 | } else { 59 | path = config.DefaultPath 60 | } 61 | } 62 | 63 | sigAlgo := crypto.StringToSignatureAlgorithm(params.ServiceKeySigAlgo) 64 | if sigAlgo == crypto.UnknownSignatureAlgorithm { 65 | return nil, fmt.Errorf("invalid signature algorithm: %s", params.ServiceKeySigAlgo) 66 | } 67 | 68 | hashAlgo := crypto.StringToHashAlgorithm(params.ServiceKeyHashAlgo) 69 | if hashAlgo == crypto.UnknownHashAlgorithm { 70 | return nil, fmt.Errorf("invalid hash algorithm: %s", params.ServiceKeyHashAlgo) 71 | } 72 | 73 | state, err := flowkit.Init(readerWriter) 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | emulatorAccount, err := accounts.NewEmulatorAccount(readerWriter, crypto.ECDSA_P256, crypto.SHA3_256, params.TargetDirectory) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | state.Accounts().AddOrUpdate(emulatorAccount) 84 | 85 | if params.ServicePrivateKey != "" { 86 | privateKey, err := crypto.DecodePrivateKeyHex(sigAlgo, params.ServicePrivateKey) 87 | if err != nil { 88 | return nil, fmt.Errorf("invalid private key: %w", err) 89 | } 90 | 91 | state.SetEmulatorKey(privateKey) 92 | } 93 | 94 | if config.Exists(path) && !params.Reset { 95 | return nil, fmt.Errorf( 96 | "configuration already exists at: %s, if you want to reset configuration use the reset flag", 97 | path, 98 | ) 99 | } 100 | 101 | return state, nil 102 | } 103 | -------------------------------------------------------------------------------- /internal/config/remove-account.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package config 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | 24 | "github.com/onflow/flow-cli/internal/prompt" 25 | 26 | "github.com/onflow/flowkit/v2" 27 | "github.com/onflow/flowkit/v2/output" 28 | 29 | "github.com/onflow/flow-cli/internal/command" 30 | ) 31 | 32 | type flagsRemoveAccount struct{} 33 | 34 | var removeAccountFlags = flagsRemoveAccount{} 35 | 36 | var removeAccountCommand = &command.Command{ 37 | Cmd: &cobra.Command{ 38 | Use: "account ", 39 | Short: "Remove account from configuration", 40 | Example: "flow config remove account Foo", 41 | Args: cobra.MaximumNArgs(1), 42 | }, 43 | Flags: &removeAccountFlags, 44 | RunS: removeAccount, 45 | } 46 | 47 | func removeAccount( 48 | args []string, 49 | globalFlags command.GlobalFlags, 50 | _ output.Logger, 51 | _ flowkit.Services, 52 | state *flowkit.State, 53 | ) (command.Result, error) { 54 | name := "" 55 | if len(args) == 1 { 56 | name = args[0] 57 | } else { 58 | name = prompt.RemoveAccountPrompt(state.Config().Accounts) 59 | } 60 | 61 | err := state.Accounts().Remove(name) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | err = state.SaveEdited(globalFlags.ConfigPaths) 67 | if err != nil { 68 | return nil, err 69 | } 70 | 71 | return &result{ 72 | result: "account removed", 73 | }, nil 74 | } 75 | -------------------------------------------------------------------------------- /internal/config/remove-contract.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package config 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | 24 | "github.com/onflow/flow-cli/internal/prompt" 25 | 26 | "github.com/onflow/flowkit/v2" 27 | "github.com/onflow/flowkit/v2/output" 28 | 29 | "github.com/onflow/flow-cli/internal/command" 30 | ) 31 | 32 | type flagsRemoveContract struct{} 33 | 34 | var removeContractFlags = flagsRemoveContract{} 35 | 36 | var removeContractCommand = &command.Command{ 37 | Cmd: &cobra.Command{ 38 | Use: "contract ", 39 | Short: "Remove contract from configuration", 40 | Example: "flow config remove contract Foo", 41 | Args: cobra.MaximumNArgs(1), 42 | }, 43 | Flags: &removeContractFlags, 44 | RunS: removeContract, 45 | } 46 | 47 | func removeContract( 48 | args []string, 49 | globalFlags command.GlobalFlags, 50 | _ output.Logger, 51 | _ flowkit.Services, 52 | state *flowkit.State, 53 | ) (command.Result, error) { 54 | name := "" 55 | if len(args) == 1 { 56 | name = args[0] 57 | } else { 58 | name = prompt.RemoveContractPrompt(*state.Contracts()) 59 | } 60 | 61 | err := state.Contracts().Remove(name) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | err = state.SaveEdited(globalFlags.ConfigPaths) 67 | if err != nil { 68 | return nil, err 69 | } 70 | 71 | return &result{ 72 | result: "contract removed", 73 | }, nil 74 | } 75 | -------------------------------------------------------------------------------- /internal/config/remove-deployment.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package config 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | 24 | "github.com/onflow/flow-cli/internal/prompt" 25 | 26 | "github.com/onflow/flowkit/v2" 27 | "github.com/onflow/flowkit/v2/output" 28 | 29 | "github.com/onflow/flow-cli/internal/command" 30 | ) 31 | 32 | type flagsRemoveDeployment struct{} 33 | 34 | var removeDeploymentFlags = flagsRemoveDeployment{} 35 | 36 | var removeDeploymentCommand = &command.Command{ 37 | Cmd: &cobra.Command{ 38 | Use: "deployment ", 39 | Short: "Remove deployment from configuration", 40 | Example: "flow config remove deployment Foo testnet", 41 | Args: cobra.MaximumNArgs(2), 42 | }, 43 | Flags: &removeDeploymentFlags, 44 | RunS: removeDeployment, 45 | } 46 | 47 | func removeDeployment( 48 | args []string, 49 | globalFlags command.GlobalFlags, 50 | _ output.Logger, 51 | _ flowkit.Services, 52 | state *flowkit.State, 53 | ) (command.Result, error) { 54 | account := "" 55 | network := "" 56 | if len(args) == 2 { 57 | account = args[0] 58 | network = args[1] 59 | } else { 60 | account, network = prompt.RemoveDeploymentPrompt(*state.Deployments()) 61 | } 62 | 63 | err := state.Deployments().Remove(account, network) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | err = state.SaveEdited(globalFlags.ConfigPaths) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | return &result{ 74 | result: "deployment removed", 75 | }, nil 76 | } 77 | -------------------------------------------------------------------------------- /internal/config/remove-network.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package config 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | 24 | "github.com/onflow/flow-cli/internal/prompt" 25 | 26 | "github.com/onflow/flowkit/v2" 27 | "github.com/onflow/flowkit/v2/output" 28 | 29 | "github.com/onflow/flow-cli/internal/command" 30 | ) 31 | 32 | type flagsRemoveNetwork struct{} 33 | 34 | var removeNetworkFlags = flagsRemoveNetwork{} 35 | 36 | var removeNetworkCommand = &command.Command{ 37 | Cmd: &cobra.Command{ 38 | Use: "network ", 39 | Short: "Remove network from configuration", 40 | Example: "flow config remove network Foo", 41 | Args: cobra.MaximumNArgs(1), 42 | }, 43 | Flags: &removeNetworkFlags, 44 | RunS: removeNetwork, 45 | } 46 | 47 | func removeNetwork( 48 | args []string, 49 | globalFlags command.GlobalFlags, 50 | _ output.Logger, 51 | _ flowkit.Services, 52 | state *flowkit.State, 53 | ) (command.Result, error) { 54 | name := "" 55 | if len(args) == 1 { 56 | name = args[0] 57 | } else { 58 | name = prompt.RemoveNetworkPrompt(*state.Networks()) 59 | } 60 | 61 | err := state.Networks().Remove(name) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | err = state.SaveEdited(globalFlags.ConfigPaths) 67 | if err != nil { 68 | return nil, err 69 | } 70 | 71 | return &result{ 72 | result: "network removed", 73 | }, nil 74 | } 75 | -------------------------------------------------------------------------------- /internal/config/remove.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package config 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | var removeCmd = &cobra.Command{ 26 | Use: "remove ", 27 | Short: "Remove resource from configuration", 28 | Example: "flow config remove account", 29 | Args: cobra.ExactArgs(1), 30 | TraverseChildren: true, 31 | } 32 | 33 | func init() { 34 | removeAccountCommand.AddToParent(removeCmd) 35 | removeContractCommand.AddToParent(removeCmd) 36 | removeDeploymentCommand.AddToParent(removeCmd) 37 | removeNetworkCommand.AddToParent(removeCmd) 38 | } 39 | -------------------------------------------------------------------------------- /internal/dependencymanager/add.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package dependencymanager 20 | 21 | import ( 22 | "fmt" 23 | 24 | "github.com/onflow/flowkit/v2" 25 | "github.com/spf13/cobra" 26 | 27 | "github.com/onflow/flow-cli/internal/util" 28 | 29 | "github.com/onflow/flowkit/v2/output" 30 | 31 | "github.com/onflow/flow-cli/internal/command" 32 | ) 33 | 34 | var addCommand = &command.Command{ 35 | Cmd: &cobra.Command{ 36 | Use: "add", 37 | Short: "This command has been deprecated.", 38 | Long: "The 'add' command has been deprecated. Please use the 'install' command instead.", 39 | Deprecated: "This command is deprecated. Use 'install' to manage dependencies.", 40 | }, 41 | RunS: add, 42 | Flags: &struct{}{}, 43 | } 44 | 45 | func add( 46 | _ []string, 47 | _ command.GlobalFlags, 48 | logger output.Logger, 49 | _ flowkit.Services, 50 | _ *flowkit.State, 51 | ) (command.Result, error) { 52 | logger.Info(fmt.Sprintf("%s The 'add' command has been deprecated. Please use 'install' instead.", util.PrintEmoji("⚠️"))) 53 | return nil, nil 54 | } 55 | -------------------------------------------------------------------------------- /internal/dependencymanager/dependencies.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package dependencymanager 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | var Cmd = &cobra.Command{ 26 | Use: "dependencies", 27 | Short: "Manage contracts and dependencies", 28 | TraverseChildren: true, 29 | GroupID: "manager", 30 | Aliases: []string{"deps"}, 31 | } 32 | 33 | func init() { 34 | addCommand.AddToParent(Cmd) 35 | installCommand.AddToParent(Cmd) 36 | discoverCommand.AddToParent(Cmd) 37 | } 38 | -------------------------------------------------------------------------------- /internal/dependencymanager/discover.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package dependencymanager 20 | 21 | import ( 22 | "fmt" 23 | "slices" 24 | 25 | flowsdk "github.com/onflow/flow-go-sdk" 26 | "github.com/onflow/flow-go/fvm/systemcontracts" 27 | 28 | "github.com/spf13/cobra" 29 | 30 | "github.com/onflow/flowkit/v2" 31 | "github.com/onflow/flowkit/v2/output" 32 | 33 | flowGo "github.com/onflow/flow-go/model/flow" 34 | flowkitConfig "github.com/onflow/flowkit/v2/config" 35 | 36 | "github.com/onflow/flow-cli/internal/command" 37 | "github.com/onflow/flow-cli/internal/prompt" 38 | "github.com/onflow/flow-cli/internal/util" 39 | ) 40 | 41 | type DiscoverResult struct { 42 | Contracts []string `json:"contracts"` 43 | } 44 | 45 | var discoverCommand = &command.Command{ 46 | Cmd: &cobra.Command{ 47 | Use: "discover", 48 | Short: "Discover available contracts to add to your project.", 49 | Example: "flow dependencies discover", 50 | Args: cobra.NoArgs, 51 | }, 52 | RunS: discover, 53 | Flags: &struct{}{}, 54 | } 55 | 56 | func discover( 57 | _ []string, 58 | globalFlags command.GlobalFlags, 59 | logger output.Logger, 60 | flow flowkit.Services, 61 | state *flowkit.State, 62 | ) (command.Result, error) { 63 | installedDeps := state.Dependencies() 64 | if installedDeps == nil { 65 | installedDeps = new(flowkitConfig.Dependencies) 66 | } 67 | 68 | installedContracts := make([]string, 0) 69 | for _, dep := range *installedDeps { 70 | installedContracts = append(installedContracts, dep.Name) 71 | } 72 | 73 | err := PromptInstallCoreContracts(logger, state, "", installedContracts) 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | err = state.SaveDefault() 79 | return nil, err 80 | } 81 | 82 | func PromptInstallCoreContracts(logger output.Logger, state *flowkit.State, targetDir string, excludeContracts []string) error { 83 | // Prompt to ask which core contracts should be installed 84 | sc := systemcontracts.SystemContractsForChain(flowGo.Mainnet) 85 | promptMessage := "Select any core contracts you would like to install or skip to continue." 86 | 87 | contractNames := make([]string, 0) 88 | 89 | for _, contract := range sc.All() { 90 | if slices.Contains(excludeContracts, contract.Name) { 91 | continue 92 | } 93 | contractNames = append(contractNames, contract.Name) 94 | } 95 | 96 | selectedContractNames, err := prompt.RunSelectOptions(contractNames, promptMessage) 97 | if err != nil { 98 | return fmt.Errorf("error running dependency selection: %v\n", err) 99 | } 100 | 101 | var dependencies []flowkitConfig.Dependency 102 | 103 | // Loop standard contracts and add them to the dependencies if selected 104 | for _, contract := range sc.All() { 105 | if slices.Contains(selectedContractNames, contract.Name) { 106 | dependencies = append(dependencies, flowkitConfig.Dependency{ 107 | Name: contract.Name, 108 | Source: flowkitConfig.Source{ 109 | NetworkName: flowkitConfig.MainnetNetwork.Name, 110 | Address: flowsdk.HexToAddress(contract.Address.String()), 111 | ContractName: contract.Name, 112 | }, 113 | }) 114 | } 115 | } 116 | 117 | logger.Info("") 118 | logger.Info(util.MessageWithEmojiPrefix("🔄", "Installing selected core contracts and dependencies...")) 119 | 120 | // Add the selected core contracts as dependencies 121 | installer, err := NewDependencyInstaller(logger, state, false, targetDir, Flags{}) 122 | if err != nil { 123 | return err 124 | } 125 | 126 | if err := installer.AddMany(dependencies); err != nil { 127 | return err 128 | } 129 | 130 | return nil 131 | } 132 | -------------------------------------------------------------------------------- /internal/events/events_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package events 20 | 21 | import ( 22 | "encoding/json" 23 | "strings" 24 | "testing" 25 | 26 | "github.com/onflow/cadence" 27 | "github.com/onflow/flow-go-sdk" 28 | "github.com/stretchr/testify/assert" 29 | "github.com/stretchr/testify/mock" 30 | 31 | "github.com/onflow/flowkit/v2" 32 | "github.com/onflow/flowkit/v2/tests" 33 | 34 | "github.com/onflow/flow-cli/internal/command" 35 | "github.com/onflow/flow-cli/internal/util" 36 | ) 37 | 38 | func Test_Get(t *testing.T) { 39 | srv, _, rw := util.TestMocks(t) 40 | 41 | t.Run("Success", func(t *testing.T) { 42 | inArgs := []string{"test.event"} 43 | eventsFlags.Start = 10 44 | eventsFlags.End = 20 45 | 46 | result, err := get(inArgs, command.GlobalFlags{}, util.NoLogger, rw, srv.Mock) 47 | assert.NoError(t, err) 48 | assert.NotNil(t, result) 49 | }) 50 | 51 | t.Run("Success not passed start end", func(t *testing.T) { 52 | inArgs := []string{"test.event"} 53 | eventsFlags.Start = 0 54 | eventsFlags.End = 0 55 | 56 | srv.GetBlock.Run(func(args mock.Arguments) { 57 | query := args.Get(1).(flowkit.BlockQuery) 58 | assert.True(t, query.Latest) 59 | }).Return(tests.NewBlock(), nil) 60 | 61 | result, err := get(inArgs, command.GlobalFlags{}, util.NoLogger, rw, srv.Mock) 62 | assert.NoError(t, err) 63 | assert.NotNil(t, result) 64 | }) 65 | 66 | t.Run("Fail invalid range", func(t *testing.T) { 67 | inArgs := []string{"test.event"} 68 | eventsFlags.Start = 20 69 | eventsFlags.End = 0 70 | 71 | result, err := get(inArgs, command.GlobalFlags{}, util.NoLogger, rw, srv.Mock) 72 | assert.EqualError(t, err, "please provide either both start and end for range or only last flag") 73 | assert.Nil(t, result) 74 | }) 75 | 76 | } 77 | 78 | func Test_Result(t *testing.T) { 79 | block := tests.NewBlock() 80 | event := EventResult{ 81 | BlockEvents: []flow.BlockEvents{{ 82 | BlockID: block.ID, 83 | Height: block.Height, 84 | BlockTimestamp: block.Timestamp, 85 | Events: []flow.Event{ 86 | *tests.NewEvent( 87 | 0, 88 | "A.foo", 89 | []cadence.Field{{Type: cadence.StringType, Identifier: "bar"}}, 90 | []cadence.Value{cadence.NewInt(1)}, 91 | ), 92 | }, 93 | }}, 94 | } 95 | 96 | assert.Equal(t, strings.TrimPrefix(` 97 | Events Block #1: 98 | Index 0 99 | Type A.foo 100 | Tx ID 0000000000000000000000000000000000000000000000000000000000000000 101 | Values 102 | - bar (String): 1 103 | 104 | `, "\n"), event.String()) 105 | 106 | assert.Equal(t, []any{map[string]any{ 107 | "blockID": uint64(1), 108 | "index": 0, 109 | "transactionId": "0000000000000000000000000000000000000000000000000000000000000000", 110 | "type": "A.foo", 111 | "values": json.RawMessage{0x7b, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x69, 0x64, 0x22, 0x3a, 0x22, 0x41, 0x2e, 0x66, 0x6f, 0x6f, 0x22, 0x2c, 0x22, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x7b, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a, 0x22, 0x31, 0x22, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x22, 0x49, 0x6e, 0x74, 0x22, 0x7d, 0x2c, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3a, 0x22, 0x62, 0x61, 0x72, 0x22, 0x7d, 0x5d, 0x7d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, 0x22, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x7d, 0xa}, 112 | }}, event.JSON()) 113 | } 114 | -------------------------------------------------------------------------------- /internal/events/get.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package events 20 | 21 | import ( 22 | "context" 23 | "fmt" 24 | 25 | "github.com/spf13/cobra" 26 | 27 | "github.com/onflow/flowkit/v2" 28 | "github.com/onflow/flowkit/v2/output" 29 | 30 | "github.com/onflow/flow-cli/internal/command" 31 | ) 32 | 33 | type flagsEvents struct { 34 | Start uint64 `flag:"start" info:"Start block height"` 35 | End uint64 `flag:"end" info:"End block height"` 36 | Last uint64 `default:"10" flag:"last" info:"Fetch number of blocks relative to the last block. Ignored if the start flag is set. Used as a default if no flags are provided"` 37 | Workers int `default:"10" flag:"workers" info:"Number of workers to use when fetching events in parallel"` 38 | Batch uint64 `default:"25" flag:"batch" info:"Number of blocks each worker will fetch"` 39 | } 40 | 41 | var eventsFlags = flagsEvents{} 42 | 43 | var getCommand = &command.Command{ 44 | Cmd: &cobra.Command{ 45 | Use: "get ", 46 | Short: "Get events in a block range", 47 | Args: cobra.MinimumNArgs(1), 48 | Example: `#fetch events from the latest 10 blocks is the default behavior 49 | flow events get A.1654653399040a61.FlowToken.TokensDeposited 50 | 51 | #specify manual start and stop blocks 52 | flow events get A.1654653399040a61.FlowToken.TokensDeposited --start 11559500 --end 11559600 53 | 54 | #in order to get and event from the 20 latest blocks on a network run 55 | flow events get A.1654653399040a61.FlowToken.TokensDeposited --last 20 --network mainnet 56 | 57 | #if you want to fetch multiple event types that is done by sending in more events. Even fetching will be done in parallel. 58 | flow events get A.1654653399040a61.FlowToken.TokensDeposited A.1654653399040a61.FlowToken.TokensWithdrawn 59 | `, 60 | }, 61 | Flags: &eventsFlags, 62 | Run: get, 63 | } 64 | 65 | func get( 66 | args []string, 67 | _ command.GlobalFlags, 68 | logger output.Logger, 69 | _ flowkit.ReaderWriter, 70 | flow flowkit.Services, 71 | ) (command.Result, error) { 72 | var err error 73 | start := eventsFlags.Start 74 | end := eventsFlags.End 75 | last := eventsFlags.Last 76 | 77 | // handle if not passing start and end 78 | if start == 0 && end == 0 { 79 | latest, err := flow.GetBlock( 80 | context.Background(), 81 | flowkit.BlockQuery{Latest: true}, 82 | ) 83 | if err != nil { 84 | return nil, err 85 | } 86 | end = latest.Height 87 | 88 | start = end - last 89 | if end < last { 90 | start = 0 91 | } 92 | } else if start == 0 || end == 0 { 93 | return nil, fmt.Errorf("please provide either both start and end for range or only last flag") 94 | } 95 | 96 | logger.StartProgress("Fetching events...") 97 | defer logger.StopProgress() 98 | 99 | events, err := flow.GetEvents( 100 | context.Background(), 101 | args, 102 | start, 103 | end, 104 | &flowkit.EventWorker{ 105 | Count: eventsFlags.Workers, 106 | BlocksPerWorker: eventsFlags.Batch, 107 | }, 108 | ) 109 | if err != nil { 110 | return nil, err 111 | } 112 | 113 | return &EventResult{BlockEvents: events}, nil 114 | } 115 | -------------------------------------------------------------------------------- /internal/keys/decode.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package keys 20 | 21 | import ( 22 | "encoding/hex" 23 | "fmt" 24 | "strings" 25 | 26 | "github.com/onflow/flow-go-sdk" 27 | "github.com/onflow/flow-go-sdk/crypto" 28 | "github.com/spf13/cobra" 29 | 30 | "github.com/onflow/flowkit/v2" 31 | "github.com/onflow/flowkit/v2/output" 32 | 33 | "github.com/onflow/flow-cli/internal/command" 34 | ) 35 | 36 | type flagsDecode struct { 37 | SigAlgo string `default:"ECDSA_P256" flag:"sig-algo" info:"Signature algorithm"` 38 | FromFile string `default:"" flag:"from-file" info:"Load key from file"` 39 | } 40 | 41 | var decodeFlags = flagsDecode{} 42 | 43 | var decodeCommand = &command.Command{ 44 | Cmd: &cobra.Command{ 45 | Use: "decode ", 46 | Short: "Decode an encoded public key", 47 | Args: cobra.RangeArgs(1, 2), 48 | ValidArgs: []string{"rlp", "pem"}, 49 | Example: "flow keys decode rlp f847b8408...2402038203e8", 50 | }, 51 | Flags: &decodeFlags, 52 | Run: decode, 53 | } 54 | 55 | func decode( 56 | args []string, 57 | _ command.GlobalFlags, 58 | _ output.Logger, 59 | reader flowkit.ReaderWriter, 60 | _ flowkit.Services, 61 | ) (command.Result, error) { 62 | encoding := args[0] 63 | fromFile := decodeFlags.FromFile 64 | 65 | var encoded string 66 | if len(args) > 1 { 67 | encoded = args[1] 68 | } 69 | 70 | /* TODO(sideninja) from file flag should be remove and should be replaced with $(echo file) 71 | but cobra has an issue with parsing pem content as it recognize it as flag due to ---- characters */ 72 | if encoded != "" && fromFile != "" { 73 | return nil, fmt.Errorf("can not pass both command argument and from file flag") 74 | } 75 | if encoded == "" && fromFile == "" { 76 | return nil, fmt.Errorf("provide argument for encoded key or use from file flag") 77 | } 78 | 79 | if fromFile != "" { 80 | e, err := reader.ReadFile(fromFile) 81 | if err != nil { 82 | return nil, err 83 | } 84 | encoded = strings.TrimSpace(string(e)) 85 | } 86 | 87 | var accountKey *flow.AccountKey 88 | var err error 89 | switch strings.ToLower(encoding) { 90 | case "pem": 91 | sigAlgo := crypto.StringToSignatureAlgorithm(decodeFlags.SigAlgo) 92 | if sigAlgo == crypto.UnknownSignatureAlgorithm { 93 | return nil, fmt.Errorf("invalid signature algorithm: %s", decodeFlags.SigAlgo) 94 | } 95 | 96 | accountKey, err = decodePEM(encoded, sigAlgo) 97 | case "rlp": 98 | accountKey, err = decodeRLP(encoded) 99 | default: 100 | return nil, fmt.Errorf("encoding type not supported. Valid encoding: RLP and PEM") 101 | } 102 | 103 | if err != nil { 104 | return nil, err 105 | } 106 | 107 | return &keyResult{ 108 | publicKey: accountKey.PublicKey, 109 | sigAlgo: accountKey.SigAlgo, 110 | hashAlgo: accountKey.HashAlgo, 111 | weight: accountKey.Weight, 112 | }, err 113 | } 114 | 115 | func decodePEM(pubKey string, sigAlgo crypto.SignatureAlgorithm) (*flow.AccountKey, error) { 116 | pk, err := crypto.DecodePublicKeyPEM(sigAlgo, pubKey) 117 | if err != nil { 118 | return nil, err 119 | } 120 | 121 | return &flow.AccountKey{ 122 | PublicKey: pk, 123 | SigAlgo: sigAlgo, 124 | Weight: -1, 125 | }, nil 126 | } 127 | 128 | func decodeRLP(pubKey string) (*flow.AccountKey, error) { 129 | publicKeyBytes, err := hex.DecodeString(pubKey) 130 | if err != nil { 131 | return nil, fmt.Errorf("failed to decode public key: %w", err) 132 | } 133 | 134 | accountKey, err := flow.DecodeAccountKey(publicKeyBytes) 135 | if err != nil { 136 | return nil, fmt.Errorf("failed to decode: %w", err) 137 | } 138 | 139 | return accountKey, nil 140 | } 141 | -------------------------------------------------------------------------------- /internal/keys/derive.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package keys 20 | 21 | import ( 22 | "fmt" 23 | 24 | "github.com/onflow/flow-go-sdk/crypto" 25 | "github.com/spf13/cobra" 26 | 27 | "github.com/onflow/flowkit/v2" 28 | "github.com/onflow/flowkit/v2/output" 29 | 30 | "github.com/onflow/flow-cli/internal/command" 31 | ) 32 | 33 | type flagsDerive struct { 34 | KeySigAlgo string `default:"ECDSA_P256" flag:"sig-algo" info:"Signature algorithm"` 35 | } 36 | 37 | var deriveFlags = flagsDerive{} 38 | 39 | var deriveCommand = &command.Command{ 40 | Cmd: &cobra.Command{ 41 | Use: "derive ", 42 | Short: "Derive public key from a private key", 43 | Args: cobra.ExactArgs(1), 44 | Example: "flow keys derive 4247b8408...2402038203e8", 45 | }, 46 | Flags: &deriveFlags, 47 | Run: derive, 48 | } 49 | 50 | func derive( 51 | args []string, 52 | _ command.GlobalFlags, 53 | _ output.Logger, 54 | _ flowkit.ReaderWriter, 55 | _ flowkit.Services, 56 | ) (command.Result, error) { 57 | 58 | sigAlgo := crypto.StringToSignatureAlgorithm(deriveFlags.KeySigAlgo) 59 | if sigAlgo == crypto.UnknownSignatureAlgorithm { 60 | return nil, fmt.Errorf("invalid signature algorithm: %s", deriveFlags.KeySigAlgo) 61 | } 62 | 63 | parsedPrivateKey, err := crypto.DecodePrivateKeyHex(sigAlgo, args[0]) 64 | if err != nil { 65 | return nil, fmt.Errorf("failed to decode private key: %w", err) 66 | } 67 | 68 | return &keyResult{privateKey: parsedPrivateKey, publicKey: parsedPrivateKey.PublicKey()}, nil 69 | } 70 | -------------------------------------------------------------------------------- /internal/keys/generate.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package keys 20 | 21 | import ( 22 | "context" 23 | "fmt" 24 | 25 | "github.com/onflow/flow-go-sdk/crypto" 26 | "github.com/spf13/cobra" 27 | 28 | "github.com/onflow/flowkit/v2" 29 | "github.com/onflow/flowkit/v2/output" 30 | 31 | "github.com/onflow/flow-cli/internal/command" 32 | ) 33 | 34 | type flagsGenerate struct { 35 | Mnemonic string `flag:"mnemonic" info:"Mnemonic seed to use"` 36 | DerivationPath string `default:"m/44'/539'/0'/0/0" flag:"derivationPath" info:"Derivation path"` 37 | KeySigAlgo string `default:"ECDSA_P256" flag:"sig-algo" info:"Signature algorithm"` 38 | } 39 | 40 | var generateFlags = flagsGenerate{} 41 | 42 | var generateCommand = &command.Command{ 43 | Cmd: &cobra.Command{ 44 | Use: "generate", 45 | Short: "Generate a new key-pair", 46 | Example: "flow keys generate", 47 | }, 48 | Flags: &generateFlags, 49 | Run: generate, 50 | } 51 | 52 | func generate( 53 | _ []string, 54 | _ command.GlobalFlags, 55 | _ output.Logger, 56 | _ flowkit.ReaderWriter, 57 | flow flowkit.Services, 58 | ) (command.Result, error) { 59 | sigAlgo := crypto.StringToSignatureAlgorithm(generateFlags.KeySigAlgo) 60 | if sigAlgo == crypto.UnknownSignatureAlgorithm { 61 | return nil, fmt.Errorf("invalid signature algorithm: %s", generateFlags.KeySigAlgo) 62 | } 63 | 64 | var err error 65 | mnemonic := generateFlags.Mnemonic 66 | if mnemonic == "" { 67 | _, mnemonic, err = flow.GenerateMnemonicKey(context.Background(), sigAlgo, generateFlags.DerivationPath) 68 | if err != nil { 69 | return nil, err 70 | } 71 | } 72 | 73 | privateKey, err := flow.DerivePrivateKeyFromMnemonic( 74 | context.Background(), 75 | mnemonic, 76 | sigAlgo, 77 | generateFlags.DerivationPath, 78 | ) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | return &keyResult{ 84 | privateKey: privateKey, 85 | publicKey: privateKey.PublicKey(), 86 | sigAlgo: sigAlgo, 87 | mnemonic: mnemonic, 88 | derivationPath: generateFlags.DerivationPath, 89 | }, nil 90 | } 91 | -------------------------------------------------------------------------------- /internal/keys/keys.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package keys 20 | 21 | import ( 22 | "bytes" 23 | "encoding/hex" 24 | "fmt" 25 | 26 | "github.com/onflow/flow-go-sdk/crypto" 27 | "github.com/spf13/cobra" 28 | 29 | "github.com/onflow/flowkit/v2/output" 30 | 31 | "github.com/onflow/flow-cli/internal/util" 32 | ) 33 | 34 | var Cmd = &cobra.Command{ 35 | Use: "keys", 36 | Short: "Generate and decode Flow keys", 37 | TraverseChildren: true, 38 | GroupID: "security", 39 | } 40 | 41 | func init() { 42 | generateCommand.AddToParent(Cmd) 43 | decodeCommand.AddToParent(Cmd) 44 | deriveCommand.AddToParent(Cmd) 45 | } 46 | 47 | type keyResult struct { 48 | privateKey crypto.PrivateKey 49 | publicKey crypto.PublicKey 50 | sigAlgo crypto.SignatureAlgorithm 51 | hashAlgo crypto.HashAlgorithm 52 | weight int 53 | mnemonic string 54 | derivationPath string 55 | } 56 | 57 | func (k *keyResult) JSON() any { 58 | result := make(map[string]any) 59 | result["public"] = hex.EncodeToString(k.privateKey.PublicKey().Encode()) 60 | 61 | if k.privateKey != nil { 62 | result["private"] = hex.EncodeToString(k.privateKey.Encode()) 63 | } 64 | 65 | if k.mnemonic != "" { 66 | result["mnemonic"] = k.mnemonic 67 | } 68 | 69 | if k.derivationPath != "" { 70 | result["derivationPath"] = k.derivationPath 71 | } 72 | 73 | return result 74 | } 75 | 76 | func (k *keyResult) String() string { 77 | var b bytes.Buffer 78 | writer := util.CreateTabWriter(&b) 79 | 80 | if k.privateKey != nil { 81 | _, _ = fmt.Fprintf(writer, "%s Store private key safely and don't share with anyone! \n", output.StopEmoji()) 82 | _, _ = fmt.Fprintf(writer, "Private Key \t %x \n", k.privateKey.Encode()) 83 | } 84 | 85 | _, _ = fmt.Fprintf(writer, "Public Key \t %x \n", k.publicKey.Encode()) 86 | 87 | if k.mnemonic != "" { 88 | _, _ = fmt.Fprintf(writer, "Mnemonic \t %s \n", k.mnemonic) 89 | } 90 | 91 | if k.derivationPath != "" { 92 | _, _ = fmt.Fprintf(writer, "Derivation Path \t %s \n", k.derivationPath) 93 | } 94 | 95 | if k.sigAlgo != crypto.UnknownSignatureAlgorithm { 96 | _, _ = fmt.Fprintf(writer, "Signature Algorithm \t %s\n", k.sigAlgo) 97 | } 98 | 99 | if k.hashAlgo != crypto.UnknownHashAlgorithm { 100 | _, _ = fmt.Fprintf(writer, "Hash Algorithm \t %s\n", k.hashAlgo) 101 | } 102 | 103 | if k.weight > 0 { 104 | _, _ = fmt.Fprintf(writer, "Weight \t %d\n", k.weight) 105 | } 106 | 107 | _ = writer.Flush() 108 | 109 | return b.String() 110 | } 111 | 112 | func (k *keyResult) Oneliner() string { 113 | result := fmt.Sprintf("Public Key: %x, ", k.publicKey.Encode()) 114 | 115 | if k.privateKey != nil { 116 | result += fmt.Sprintf("Private Key: %x, ", k.privateKey.Encode()) 117 | } 118 | 119 | if k.mnemonic != "" { 120 | result += fmt.Sprintf("Mnemonic: %s, ", k.mnemonic) 121 | } 122 | 123 | if k.derivationPath != "" { 124 | result += fmt.Sprintf("Derivation Path: %s", k.derivationPath) 125 | } 126 | 127 | return result 128 | } 129 | -------------------------------------------------------------------------------- /internal/project/project.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package project 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | var Cmd = &cobra.Command{ 26 | Use: "project", 27 | Short: "Manage your Cadence project", 28 | TraverseChildren: true, 29 | GroupID: "project", 30 | } 31 | 32 | func init() { 33 | DeployCommand.AddToParent(Cmd) 34 | } 35 | -------------------------------------------------------------------------------- /internal/project/project_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package project 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/onflow/flow-go-sdk" 25 | "github.com/stretchr/testify/assert" 26 | "github.com/stretchr/testify/require" 27 | 28 | "github.com/onflow/flowkit/v2" 29 | "github.com/onflow/flowkit/v2/accounts" 30 | "github.com/onflow/flowkit/v2/config" 31 | 32 | "github.com/onflow/flow-cli/internal/command" 33 | "github.com/onflow/flow-cli/internal/util" 34 | ) 35 | 36 | func Test_ProjectDeploy(t *testing.T) { 37 | srv, state, rw := util.TestMocks(t) 38 | 39 | t.Run("Fail contract errors", func(t *testing.T) { 40 | srv.DeployProject.Return(nil, &flowkit.ProjectDeploymentError{}) 41 | _, err := deploy([]string{}, command.GlobalFlags{}, util.NoLogger, srv.Mock, state) 42 | assert.EqualError(t, err, "failed deploying all contracts") 43 | }) 44 | 45 | t.Run("Success replace standard contracts", func(t *testing.T) { 46 | const ft = "FungibleToken" 47 | const acc = "mainnet-account" 48 | state.Contracts().AddOrUpdate(config.Contract{ 49 | Name: ft, 50 | Location: "./ft.cdc", 51 | }) 52 | _ = rw.WriteFile("./ft.cdc", []byte("test"), 0677) // mock the file 53 | state.Accounts().AddOrUpdate(&accounts.Account{Name: acc, Address: flow.HexToAddress("0x01")}) 54 | 55 | state.Deployments().AddOrUpdate(config.Deployment{ 56 | Network: config.MainnetNetwork.Name, 57 | Account: acc, 58 | Contracts: []config.ContractDeployment{{Name: ft}}, 59 | }) 60 | 61 | state.Deployments().AddOrUpdate(config.Deployment{ 62 | Network: config.EmulatorNetwork.Name, 63 | Account: config.DefaultEmulator.ServiceAccount, 64 | Contracts: []config.ContractDeployment{{Name: ft}}, 65 | }) 66 | 67 | err := checkForStandardContractUsageOnMainnet(state, util.NoLogger, true) 68 | require.NoError(t, err) 69 | 70 | assert.Len(t, state.Deployments().ByNetwork(config.MainnetNetwork.Name), 0) // should remove it 71 | assert.Len(t, state.Deployments().ByNetwork(config.EmulatorNetwork.Name), 1) // should not remove it 72 | c, err := state.Contracts().ByName(ft) 73 | assert.NoError(t, err) 74 | assert.NotNil(t, c.Aliases) 75 | 76 | assert.Equal(t, "f233dcee88fe0abe", c.Aliases.ByNetwork(config.MainnetNetwork.Name).Address.String()) 77 | }) 78 | 79 | } 80 | -------------------------------------------------------------------------------- /internal/prompt/select-options.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package prompt 20 | 21 | import ( 22 | "fmt" 23 | "strings" 24 | 25 | tea "github.com/charmbracelet/bubbletea" 26 | ) 27 | 28 | // optionSelectModel represents the prompt state but is now private 29 | type optionSelectModel struct { 30 | message string // message to display 31 | cursor int // position of the cursor 32 | choices []string // items on the list 33 | selected map[int]struct{} // which items are selected 34 | } 35 | 36 | // selectOptions creates a prompt for selecting multiple options but is now private 37 | func selectOptions(options []string, message string) optionSelectModel { 38 | return optionSelectModel{ 39 | message: message, 40 | choices: options, 41 | selected: make(map[int]struct{}), 42 | } 43 | } 44 | 45 | func (m optionSelectModel) Init() tea.Cmd { 46 | return nil // No initial command 47 | } 48 | 49 | func (m optionSelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 50 | switch msg := msg.(type) { 51 | case tea.KeyMsg: 52 | switch msg.Type { 53 | case tea.KeyCtrlC, tea.KeyEsc: // Quit the program 54 | return m, tea.Quit 55 | 56 | case tea.KeyUp: // Navigate up 57 | if m.cursor > 0 { 58 | m.cursor-- 59 | } 60 | 61 | case tea.KeyDown: // Navigate down 62 | if m.cursor < len(m.choices)-1 { 63 | m.cursor++ 64 | } 65 | 66 | case tea.KeySpace: // Select an item 67 | // Toggle selection 68 | if _, ok := m.selected[m.cursor]; ok { 69 | delete(m.selected, m.cursor) // Deselect 70 | } else { 71 | m.selected[m.cursor] = struct{}{} // Select 72 | } 73 | 74 | case tea.KeyEnter: // Confirm selection 75 | return m, tea.Quit // Quit and process selections in main 76 | } 77 | } 78 | 79 | return m, nil 80 | } 81 | 82 | func (m optionSelectModel) View() string { 83 | var b strings.Builder 84 | b.WriteString(fmt.Sprintf("%s\n", m.message)) 85 | b.WriteString("Use arrow keys to navigate, space to select, enter to confirm or skip, q to quit:\n\n") 86 | for i, choice := range m.choices { 87 | if m.cursor == i { 88 | b.WriteString("> ") 89 | } else { 90 | b.WriteString(" ") 91 | } 92 | // Mark selected items 93 | if _, ok := m.selected[i]; ok { 94 | b.WriteString("[x] ") 95 | } else { 96 | b.WriteString("[ ] ") 97 | } 98 | b.WriteString(choice + "\n") 99 | } 100 | return b.String() 101 | } 102 | 103 | // RunSelectOptions remains public and is the interface for external usage. 104 | func RunSelectOptions(options []string, message string) ([]string, error) { 105 | model := selectOptions(options, message) 106 | p := tea.NewProgram(model) 107 | finalModel, err := p.Run() 108 | if err != nil { 109 | return nil, err 110 | } 111 | 112 | final := finalModel.(optionSelectModel) 113 | selectedChoices := make([]string, 0) 114 | for i := range final.selected { 115 | selectedChoices = append(selectedChoices, final.choices[i]) 116 | } 117 | return selectedChoices, nil 118 | } 119 | -------------------------------------------------------------------------------- /internal/prompt/text-input.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package prompt 20 | 21 | import ( 22 | "fmt" 23 | 24 | "github.com/charmbracelet/bubbles/textinput" 25 | tea "github.com/charmbracelet/bubbletea" 26 | ) 27 | 28 | // textInputModel is now private, only accessible within the 'prompt' package. 29 | type textInputModel struct { 30 | textInput textinput.Model 31 | err error 32 | customMsg string 33 | } 34 | 35 | // newTextInput is a private function that initializes a new text input model. 36 | func newTextInput(customMsg, placeholder string) textInputModel { 37 | ti := textinput.New() 38 | ti.Placeholder = placeholder 39 | ti.Focus() 40 | ti.CharLimit = 256 41 | ti.Width = 30 42 | 43 | return textInputModel{ 44 | textInput: ti, 45 | customMsg: customMsg, 46 | } 47 | } 48 | 49 | func (m textInputModel) Init() tea.Cmd { 50 | return textinput.Blink 51 | } 52 | 53 | func (m textInputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 54 | switch msg := msg.(type) { 55 | case tea.KeyMsg: 56 | switch msg.Type { 57 | case tea.KeyEnter, tea.KeyCtrlC, tea.KeyEsc: 58 | return m, tea.Quit 59 | } 60 | var cmd tea.Cmd 61 | m.textInput, cmd = m.textInput.Update(msg) 62 | return m, cmd 63 | } 64 | 65 | return m, nil 66 | } 67 | 68 | func (m textInputModel) View() string { 69 | return fmt.Sprintf("%s\n\n%s\n\n%s", m.customMsg, m.textInput.View(), "(Enter to submit, Esc to quit)") 70 | } 71 | 72 | // RunTextInput remains public. It's the entry point for external usage. 73 | func RunTextInput(customMsg, placeholder string) (string, error) { 74 | model := newTextInput(customMsg, placeholder) 75 | p := tea.NewProgram(model) 76 | 77 | if finalModel, err := p.Run(); err != nil { 78 | return "", err 79 | } else { 80 | final := finalModel.(textInputModel) 81 | return final.textInput.Value(), nil 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /internal/quick/deploy.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package quick 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | 24 | "github.com/onflow/flow-cli/internal/command" 25 | "github.com/onflow/flow-cli/internal/project" 26 | ) 27 | 28 | var DeployCommand = &command.Command{ 29 | Cmd: &cobra.Command{ 30 | Use: "deploy", 31 | Short: "Deploy all project contracts", 32 | Example: "flow deploy", 33 | GroupID: "project", 34 | }, 35 | Flags: project.DeployCommand.Flags, 36 | RunS: project.DeployCommand.RunS, 37 | } 38 | -------------------------------------------------------------------------------- /internal/quick/run.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package quick 20 | 21 | import ( 22 | "fmt" 23 | 24 | "github.com/spf13/cobra" 25 | 26 | "github.com/onflow/flowkit/v2" 27 | "github.com/onflow/flowkit/v2/output" 28 | 29 | "github.com/onflow/flow-cli/internal/command" 30 | ) 31 | 32 | type flagsRun struct { 33 | } 34 | 35 | var runFlags = flagsRun{} 36 | 37 | // RunCommand This command will act as an alias for running the emulator and deploying the contracts 38 | var RunCommand = &command.Command{ 39 | Cmd: &cobra.Command{ 40 | Use: "run", 41 | Short: "Start emulator and deploy all project contracts", 42 | Example: "flow run", 43 | GroupID: "project", 44 | }, 45 | Flags: &runFlags, 46 | Run: func( 47 | _ []string, 48 | _ command.GlobalFlags, 49 | _ output.Logger, 50 | _ flowkit.ReaderWriter, 51 | _ flowkit.Services, 52 | ) (command.Result, error) { 53 | fmt.Println("⚠️Deprecation notice: Use 'flow dev' command.") 54 | return &runResult{}, nil 55 | }, 56 | } 57 | 58 | type runResult struct{} 59 | 60 | func (r *runResult) JSON() any { 61 | return nil 62 | } 63 | 64 | func (r *runResult) String() string { 65 | return "" 66 | } 67 | 68 | func (r *runResult) Oneliner() string { 69 | return "" 70 | } 71 | -------------------------------------------------------------------------------- /internal/scripts/execute.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package scripts 20 | 21 | import ( 22 | "context" 23 | "fmt" 24 | 25 | "github.com/onflow/cadence" 26 | flowsdk "github.com/onflow/flow-go-sdk" 27 | "github.com/spf13/cobra" 28 | 29 | "github.com/onflow/flowkit/v2" 30 | "github.com/onflow/flowkit/v2/arguments" 31 | "github.com/onflow/flowkit/v2/output" 32 | 33 | "github.com/onflow/flow-cli/internal/command" 34 | ) 35 | 36 | type Flags struct { 37 | ArgsJSON string `default:"" flag:"args-json" info:"arguments in JSON-Cadence format"` 38 | BlockID string `default:"" flag:"block-id" info:"block ID to execute the script at"` 39 | BlockHeight uint64 `default:"" flag:"block-height" info:"block height to execute the script at"` 40 | } 41 | 42 | var flags = Flags{} 43 | 44 | var executeCommand = &command.Command{ 45 | Cmd: &cobra.Command{ 46 | Use: "execute [ ...]", 47 | Short: "Execute a script", 48 | Example: `flow scripts execute script.cdc "Meow" "Woof"`, 49 | Args: cobra.MinimumNArgs(1), 50 | }, 51 | Flags: &flags, 52 | Run: execute, 53 | } 54 | 55 | func execute( 56 | args []string, 57 | _ command.GlobalFlags, 58 | _ output.Logger, 59 | readerWriter flowkit.ReaderWriter, 60 | flow flowkit.Services, 61 | ) (command.Result, error) { 62 | filename := args[0] 63 | 64 | code, err := readerWriter.ReadFile(filename) 65 | if err != nil { 66 | return nil, fmt.Errorf("error loading script file: %w", err) 67 | } 68 | 69 | return SendScript(code, args[1:], filename, flow, flags) 70 | } 71 | 72 | func SendScript(code []byte, argsArr []string, location string, flow flowkit.Services, scriptFlags Flags) (command.Result, error) { 73 | var cadenceArgs []cadence.Value 74 | var err error 75 | if scriptFlags.ArgsJSON != "" { 76 | cadenceArgs, err = arguments.ParseJSON(scriptFlags.ArgsJSON) 77 | } else { 78 | cadenceArgs, err = arguments.ParseWithoutType(argsArr, code, location) 79 | } 80 | 81 | if err != nil { 82 | return nil, fmt.Errorf("error parsing script arguments: %w", err) 83 | } 84 | 85 | query := flowkit.ScriptQuery{} 86 | if scriptFlags.BlockHeight != 0 { 87 | query.Height = scriptFlags.BlockHeight 88 | } else if scriptFlags.BlockID != "" { 89 | query.ID = flowsdk.HexToID(scriptFlags.BlockID) 90 | } else { 91 | query.Latest = true 92 | } 93 | 94 | value, err := flow.ExecuteScript( 95 | context.Background(), 96 | flowkit.Script{ 97 | Code: code, 98 | Args: cadenceArgs, 99 | Location: location, 100 | }, 101 | query, 102 | ) 103 | if err != nil { 104 | return nil, err 105 | } 106 | 107 | return &scriptResult{value}, nil 108 | } 109 | -------------------------------------------------------------------------------- /internal/scripts/scripts.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package scripts 20 | 21 | import ( 22 | "bytes" 23 | "encoding/json" 24 | "fmt" 25 | 26 | "github.com/onflow/cadence" 27 | jsoncdc "github.com/onflow/cadence/encoding/json" 28 | "github.com/spf13/cobra" 29 | 30 | "github.com/onflow/flow-cli/internal/util" 31 | ) 32 | 33 | var Cmd = &cobra.Command{ 34 | Use: "scripts", 35 | Short: "Execute Cadence scripts", 36 | TraverseChildren: true, 37 | GroupID: "interactions", 38 | } 39 | 40 | func init() { 41 | executeCommand.AddToParent(Cmd) 42 | } 43 | 44 | type scriptResult struct { 45 | cadence.Value 46 | } 47 | 48 | func NewScriptResult(value cadence.Value) *scriptResult { 49 | return &scriptResult{Value: value} 50 | } 51 | 52 | func (r *scriptResult) JSON() any { 53 | return json.RawMessage( 54 | jsoncdc.MustEncode(r.Value), 55 | ) 56 | } 57 | 58 | func (r *scriptResult) String() string { 59 | var b bytes.Buffer 60 | writer := util.CreateTabWriter(&b) 61 | 62 | _, _ = fmt.Fprintf(writer, "Result: %s\n", r.Value) 63 | 64 | _ = writer.Flush() 65 | 66 | return b.String() 67 | } 68 | 69 | func (r *scriptResult) Oneliner() string { 70 | return r.Value.String() 71 | } 72 | -------------------------------------------------------------------------------- /internal/scripts/scripts_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package scripts 20 | 21 | import ( 22 | "fmt" 23 | "testing" 24 | 25 | "github.com/onflow/cadence" 26 | "github.com/stretchr/testify/assert" 27 | "github.com/stretchr/testify/mock" 28 | 29 | "github.com/onflow/flowkit/v2" 30 | "github.com/onflow/flowkit/v2/tests" 31 | 32 | "github.com/onflow/flow-cli/internal/command" 33 | "github.com/onflow/flow-cli/internal/util" 34 | ) 35 | 36 | func Test_Execute(t *testing.T) { 37 | srv, _, rw := util.TestMocks(t) 38 | 39 | t.Run("Success", func(t *testing.T) { 40 | inArgs := []string{tests.ScriptArgString.Filename, "foo"} 41 | 42 | srv.ExecuteScript.Run(func(args mock.Arguments) { 43 | script := args.Get(1).(flowkit.Script) 44 | assert.Equal(t, fmt.Sprintf("\"%s\"", inArgs[1]), script.Args[0].String()) 45 | assert.Equal(t, tests.ScriptArgString.Filename, script.Location) 46 | }).Return(cadence.NewInt(1), nil) 47 | 48 | result, err := execute(inArgs, command.GlobalFlags{}, util.NoLogger, rw, srv.Mock) 49 | assert.NotNil(t, result) 50 | assert.NoError(t, err) 51 | }) 52 | 53 | t.Run("Fail non-existing file", func(t *testing.T) { 54 | inArgs := []string{"non-existing"} 55 | result, err := execute(inArgs, command.GlobalFlags{}, util.NoLogger, rw, srv.Mock) 56 | assert.Nil(t, result) 57 | assert.EqualError(t, err, "error loading script file: open non-existing: file does not exist") 58 | }) 59 | 60 | t.Run("Fail parsing invalid JSON args", func(t *testing.T) { 61 | inArgs := []string{tests.TestScriptSimple.Filename} 62 | flags.ArgsJSON = "invalid" 63 | 64 | result, err := execute(inArgs, command.GlobalFlags{}, util.NoLogger, rw, srv.Mock) 65 | assert.Nil(t, result) 66 | assert.EqualError(t, err, "error parsing script arguments: invalid character 'i' looking for beginning of value") 67 | }) 68 | 69 | } 70 | -------------------------------------------------------------------------------- /internal/settings/cmd.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package settings 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | var Cmd = &cobra.Command{ 26 | Use: "settings", 27 | Short: "Manage persisted global settings", 28 | TraverseChildren: true, 29 | } 30 | 31 | func init() { 32 | Cmd.AddCommand(metricsSettings) 33 | } 34 | -------------------------------------------------------------------------------- /internal/settings/defaults.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package settings 20 | 21 | import ( 22 | "fmt" 23 | "os/user" 24 | "runtime" 25 | ) 26 | 27 | const ( 28 | metricsEnabled = "MetricsEnabled" 29 | flowserPath = "FlowserPath" 30 | ) 31 | 32 | // defaults holds the default values for global settings 33 | var defaults = map[string]any{ 34 | metricsEnabled: true, 35 | flowserPath: getDefaultInstallDir(), 36 | } 37 | 38 | const ( 39 | Darwin = "darwin" 40 | Windows = "windows" 41 | Linux = "linux" 42 | ) 43 | 44 | // getDefaultInstallDir returns default installation directory based on the OS. 45 | func getDefaultInstallDir() string { 46 | switch runtime.GOOS { 47 | case Darwin: 48 | return "/Applications" 49 | case Windows: 50 | // https://superuser.com/questions/1327037/what-choices-do-i-have-about-where-to-install-software-on-windows-10 51 | usr, _ := user.Current() // safe to ignore cache errors 52 | return fmt.Sprintf(`%s\AppData\Local\Programs`, usr.HomeDir) 53 | case Linux: 54 | // https://unix.stackexchange.com/questions/127076/into-which-directory-should-i-install-programs-in-linux 55 | usr, _ := user.Current() // safe to ignore cache errors 56 | // Use path in users home folder to not require sudo permissions for installation 57 | return fmt.Sprintf(`%s/.local/bin`, usr.HomeDir) 58 | default: 59 | return "" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /internal/settings/metrics.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package settings 20 | 21 | import ( 22 | "fmt" 23 | 24 | "github.com/pkg/errors" 25 | "github.com/spf13/cobra" 26 | ) 27 | 28 | const enable = "enable" 29 | 30 | const disable = "disable" 31 | 32 | var metricsSettings = &cobra.Command{ 33 | Use: "metrics", 34 | Short: "Configure command usage metrics settings", 35 | Example: "flow settings metrics disable \nflow settings metrics enable", 36 | Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), 37 | ValidArgs: []string{enable, disable}, 38 | RunE: handleMetricsSettings, 39 | } 40 | 41 | // handleMetricsSettings sets global settings for metrics 42 | func handleMetricsSettings( 43 | _ *cobra.Command, 44 | args []string, 45 | ) error { 46 | enabled := args[0] == enable 47 | if err := Set(metricsEnabled, enabled); err != nil { 48 | return errors.Wrap(err, "failed to update metrics settings") 49 | } 50 | 51 | fmt.Println(fmt.Sprintf( 52 | "Command usage tracking is %sd. Settings were updated in %s \n", 53 | args[0], 54 | FileName())) 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /internal/settings/settings.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package settings 20 | 21 | import ( 22 | "errors" 23 | "fmt" 24 | "os" 25 | "path/filepath" 26 | 27 | "github.com/spf13/viper" 28 | ) 29 | 30 | const settingsFile = "flow-cli.settings" 31 | 32 | const settingsDir = "flow-cli" 33 | 34 | const settingsType = "yaml" 35 | 36 | // viperLoaded only load settings file once 37 | var viperLoaded = false 38 | 39 | func init() { 40 | viper.SetConfigName(settingsFile) 41 | viper.SetConfigType(settingsType) 42 | viper.AddConfigPath(FileDir()) 43 | } 44 | 45 | func FileName() string { 46 | return fmt.Sprintf("%s.%s", settingsFile, settingsType) 47 | } 48 | 49 | func FileDir() string { 50 | dir, err := os.UserConfigDir() 51 | if err != nil { 52 | dir = "." 53 | } 54 | return filepath.Join(dir, settingsDir) 55 | } 56 | 57 | // Set updates settings file with new value for provided key 58 | func Set(key string, val any) error { 59 | if err := loadViper(); err != nil { 60 | return err 61 | } 62 | 63 | viper.Set(key, val) 64 | if err := viper.WriteConfig(); err != nil { 65 | return err 66 | } 67 | 68 | return nil 69 | } 70 | 71 | // loadViper loads the global settings file 72 | func loadViper() error { 73 | if viperLoaded { 74 | return nil 75 | } 76 | viperLoaded = true 77 | 78 | if err := createSettingsDir(); err != nil { 79 | return err 80 | } 81 | 82 | err := viper.MergeConfigMap(defaults) 83 | if err != nil { 84 | return err 85 | } 86 | 87 | // Load settings file 88 | if err := viper.MergeInConfig(); err != nil { 89 | switch err.(type) { 90 | case viper.ConfigFileNotFoundError: 91 | // Create settings file for the first time 92 | if err = viper.SafeWriteConfig(); err != nil { 93 | return err 94 | } 95 | default: 96 | return err 97 | } 98 | } 99 | 100 | return nil 101 | } 102 | 103 | // createSettingsDir creates settings dir if it doesn't exist 104 | func createSettingsDir() error { 105 | if _, err := os.Stat(FileDir()); errors.Is(err, os.ErrNotExist) { 106 | err = os.Mkdir(FileDir(), os.ModePerm) 107 | if err != nil { 108 | return err 109 | } 110 | } 111 | return nil 112 | } 113 | 114 | // GetFlowserPath gets set Flowser install path with sensible default. 115 | func GetFlowserPath() (string, error) { 116 | if err := loadViper(); err != nil { 117 | return "", err 118 | } 119 | return viper.GetString(flowserPath), nil 120 | } 121 | 122 | func SetFlowserPath(path string) error { 123 | return Set(flowserPath, path) 124 | } 125 | 126 | // MetricsEnabled checks whether metric tracking is enabled. 127 | func MetricsEnabled() bool { 128 | if err := loadViper(); err != nil { 129 | return true 130 | } 131 | return viper.GetBool(metricsEnabled) 132 | } 133 | -------------------------------------------------------------------------------- /internal/signatures/generate.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package signatures 20 | 21 | import ( 22 | "bytes" 23 | "context" 24 | "fmt" 25 | 26 | "github.com/onflow/flowkit/v2/accounts" 27 | 28 | "github.com/spf13/cobra" 29 | 30 | "github.com/onflow/flowkit/v2" 31 | "github.com/onflow/flowkit/v2/output" 32 | 33 | "github.com/onflow/flow-cli/internal/command" 34 | "github.com/onflow/flow-cli/internal/util" 35 | ) 36 | 37 | type flagsGenerate struct { 38 | Signer string `default:"emulator-account" flag:"signer" info:"name of the account used to sign"` 39 | } 40 | 41 | var generateFlags = flagsGenerate{} 42 | 43 | var generateCommand = &command.Command{ 44 | Cmd: &cobra.Command{ 45 | Use: "generate ", 46 | Short: "Generate the message signature", 47 | Example: "flow signatures generate 'The quick brown fox jumps over the lazy dog' --signer alice", 48 | Args: cobra.ExactArgs(1), 49 | }, 50 | Flags: &generateFlags, 51 | RunS: sign, 52 | } 53 | 54 | func sign( 55 | args []string, 56 | _ command.GlobalFlags, 57 | _ output.Logger, 58 | _ flowkit.Services, 59 | state *flowkit.State, 60 | ) (command.Result, error) { 61 | message := []byte(args[0]) 62 | accountName := generateFlags.Signer 63 | acc, err := state.Accounts().ByName(accountName) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | s, err := acc.Key.Signer(context.Background()) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | signed, err := s.Sign(message) 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | return &signatureResult{ 79 | result: string(signed), 80 | message: string(message), 81 | key: acc.Key, 82 | }, nil 83 | } 84 | 85 | type signatureResult struct { 86 | result string 87 | message string 88 | key accounts.Key 89 | } 90 | 91 | func (s *signatureResult) pubKey() string { 92 | pkey, err := s.key.PrivateKey() 93 | if err == nil { 94 | return (*pkey).PublicKey().String() 95 | } 96 | 97 | return "ERR" 98 | } 99 | 100 | func (s *signatureResult) JSON() any { 101 | return map[string]string{ 102 | "signature": fmt.Sprintf("%x", s.result), 103 | "message": s.message, 104 | "hashAlgo": s.key.HashAlgo().String(), 105 | "sigAlgo": s.key.SigAlgo().String(), 106 | "pubKey": s.pubKey(), 107 | } 108 | } 109 | 110 | func (s *signatureResult) String() string { 111 | var b bytes.Buffer 112 | writer := util.CreateTabWriter(&b) 113 | 114 | _, _ = fmt.Fprintf(writer, "Signature \t %x\n", s.result) 115 | _, _ = fmt.Fprintf(writer, "Message \t %s\n", s.message) 116 | _, _ = fmt.Fprintf(writer, "Public Key \t %s\n", s.pubKey()) 117 | _, _ = fmt.Fprintf(writer, "Hash Algorithm \t %s\n", s.key.HashAlgo()) 118 | _, _ = fmt.Fprintf(writer, "Signature Algorithm \t %s\n", s.key.SigAlgo()) 119 | 120 | _ = writer.Flush() 121 | return b.String() 122 | } 123 | 124 | func (s *signatureResult) Oneliner() string { 125 | 126 | return fmt.Sprintf( 127 | "signature: %x, message: %s, hashAlgo: %s, sigAlgo: %s, pubKey: %s", 128 | s.result, s.message, s.key.HashAlgo(), s.key.SigAlgo(), s.pubKey(), 129 | ) 130 | } 131 | -------------------------------------------------------------------------------- /internal/signatures/signatures.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package signatures 20 | 21 | import ( 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | var Cmd = &cobra.Command{ 26 | Use: "signatures", 27 | Short: "Signature verification and creation", 28 | TraverseChildren: true, 29 | GroupID: "security", 30 | } 31 | 32 | func init() { 33 | generateCommand.AddToParent(Cmd) 34 | verifyCommand.AddToParent(Cmd) 35 | } 36 | -------------------------------------------------------------------------------- /internal/signatures/signatures_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package signatures 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/stretchr/testify/assert" 25 | 26 | "github.com/onflow/flow-cli/internal/command" 27 | "github.com/onflow/flow-cli/internal/util" 28 | ) 29 | 30 | func Test_Verify(t *testing.T) { 31 | srv, state, _ := util.TestMocks(t) 32 | 33 | t.Run("Success", func(t *testing.T) { 34 | inArgs := []string{ 35 | "test signature", 36 | "f80f6007dbe6795bcf343e5586d40d0ba26a6c1d7edda5653cbdb377c9c20034cdbf899bb20fa2388d4993f6c88b5c97cbe05963d6d9799e6868902c2c14bc22", 37 | "0xab70e9e341a38861fd7f9fb1cda4c560465cfeb3ce4abcd2be552550c85ebbef9a2a9cac731c6bfa73b10c701c93038f0c18253487d4962d3bc6d5291f9c5eae", 38 | } 39 | 40 | result, err := verify(inArgs, command.GlobalFlags{}, util.NoLogger, srv.Mock, state) 41 | assert.NoError(t, err) 42 | assert.NotNil(t, result) 43 | }) 44 | 45 | t.Run("Invalid signature", func(t *testing.T) { 46 | inArgsTests := []struct { 47 | args []string 48 | err string 49 | }{{ 50 | args: []string{"invalid", "invalid"}, 51 | err: "invalid message signature: encoding/hex: invalid byte: U+0069 'i'", 52 | }, { 53 | args: []string{"invalid", "0xaaaa", "invalid"}, 54 | err: "invalid public key: encoding/hex: invalid byte: U+0069 'i'", 55 | }, { 56 | args: []string{"invalid", "0xaaaa", "0x1234"}, 57 | err: "invalid public key: input has incorrect ECDSA_P256 key size, got 2, expects 64", 58 | }} 59 | 60 | for _, test := range inArgsTests { 61 | result, err := verify(test.args, command.GlobalFlags{}, util.NoLogger, srv.Mock, state) 62 | assert.EqualError(t, err, test.err) 63 | assert.Nil(t, result) 64 | } 65 | }) 66 | 67 | } 68 | 69 | func Test_Sign(t *testing.T) { 70 | srv, state, _ := util.TestMocks(t) 71 | 72 | t.Run("Success", func(t *testing.T) { 73 | inArgs := []string{"test message"} 74 | 75 | result, err := sign(inArgs, command.GlobalFlags{}, util.NoLogger, srv.Mock, state) 76 | assert.NoError(t, err) 77 | assert.NotNil(t, result) 78 | }) 79 | 80 | t.Run("Fail unknown signer", func(t *testing.T) { 81 | inArgs := []string{"test message"} 82 | generateFlags.Signer = "invalid" 83 | result, err := sign(inArgs, command.GlobalFlags{}, util.NoLogger, srv.Mock, state) 84 | assert.EqualError(t, err, "could not find account with name invalid in the configuration") 85 | assert.Nil(t, result) 86 | }) 87 | } 88 | -------------------------------------------------------------------------------- /internal/snapshot/save.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package snapshot 20 | 21 | import ( 22 | "context" 23 | "fmt" 24 | "path/filepath" 25 | 26 | "github.com/spf13/cobra" 27 | 28 | "github.com/onflow/flowkit/v2" 29 | "github.com/onflow/flowkit/v2/output" 30 | 31 | "github.com/onflow/flow-cli/internal/command" 32 | ) 33 | 34 | var saveCommand = &command.Command{ 35 | Cmd: &cobra.Command{ 36 | Use: "save", 37 | Short: "Get the latest finalized protocol snapshot", 38 | Example: "flow snapshot save /tmp/snapshot.json", 39 | Args: cobra.ExactArgs(1), 40 | }, 41 | Flags: &struct{}{}, 42 | Run: save, 43 | } 44 | 45 | func save( 46 | args []string, 47 | _ command.GlobalFlags, 48 | logger output.Logger, 49 | writer flowkit.ReaderWriter, 50 | flow flowkit.Services, 51 | ) (command.Result, error) { 52 | fileName := args[0] 53 | 54 | logger.StartProgress("Downloading protocol snapshot...") 55 | defer logger.StopProgress() 56 | if !flow.Gateway().SecureConnection() { 57 | logger.Info(fmt.Sprintf("%s warning: using insecure client connection to download snapshot, you should use a secure network configuration...", output.WarningEmoji())) 58 | } 59 | ctx := context.Background() 60 | snapshotBytes, err := flow.Gateway().GetLatestProtocolStateSnapshot(ctx) 61 | if err != nil { 62 | return nil, fmt.Errorf("failed to get latest finalized protocol snapshot from gateway: %w", err) 63 | } 64 | 65 | logger.StopProgress() 66 | 67 | outputPath, err := filepath.Abs(fileName) 68 | if err != nil { 69 | return nil, fmt.Errorf("failed to get absolute output path for protocol snapshot") 70 | } 71 | 72 | err = writer.WriteFile(outputPath, snapshotBytes, 0644) 73 | if err != nil { 74 | return nil, fmt.Errorf("failed to write protocol snapshot file to %s: %w", outputPath, err) 75 | } 76 | 77 | return &saveResult{OutputPath: outputPath}, nil 78 | } 79 | -------------------------------------------------------------------------------- /internal/snapshot/snapshot.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package snapshot 20 | 21 | import ( 22 | "fmt" 23 | 24 | "github.com/spf13/cobra" 25 | ) 26 | 27 | var Cmd = &cobra.Command{ 28 | Use: "snapshot", 29 | Short: "Retrieve the protocol state snapshot", 30 | TraverseChildren: true, 31 | } 32 | 33 | func init() { 34 | saveCommand.AddToParent(Cmd) 35 | } 36 | 37 | // saveResult represents the result of the snapshot save command. 38 | type saveResult struct { 39 | OutputPath string 40 | } 41 | 42 | func (r *saveResult) JSON() any { 43 | return map[string]string{"path": r.OutputPath} 44 | } 45 | 46 | func (r *saveResult) String() string { 47 | return fmt.Sprintf("snapshot saved: %s", r.OutputPath) 48 | } 49 | 50 | func (r *saveResult) Oneliner() string { 51 | return fmt.Sprintf("snapshot saved: %s", r.OutputPath) 52 | } 53 | -------------------------------------------------------------------------------- /internal/status/status.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package status 20 | 21 | import ( 22 | "bytes" 23 | "fmt" 24 | 25 | "github.com/spf13/cobra" 26 | 27 | "github.com/onflow/flowkit/v2" 28 | "github.com/onflow/flowkit/v2/output" 29 | 30 | "github.com/onflow/flow-cli/internal/command" 31 | "github.com/onflow/flow-cli/internal/util" 32 | ) 33 | 34 | type flagsStatus struct { 35 | } 36 | 37 | var statusFlags = flagsStatus{} 38 | 39 | var Command = &command.Command{ 40 | Cmd: &cobra.Command{ 41 | Use: "status", 42 | Short: "Display the status of the Flow network", 43 | }, 44 | Flags: &statusFlags, 45 | RunS: status, 46 | } 47 | 48 | func status( 49 | _ []string, 50 | _ command.GlobalFlags, 51 | _ output.Logger, 52 | flow flowkit.Services, 53 | _ *flowkit.State, 54 | ) (command.Result, error) { 55 | err := flow.Ping() 56 | 57 | return &result{ 58 | network: flow.Network().Name, 59 | accessNode: flow.Network().Host, 60 | err: err, 61 | }, nil 62 | } 63 | 64 | type result struct { 65 | network string 66 | accessNode string 67 | err error 68 | } 69 | 70 | // getStatus returns string representation for Flow network status. 71 | func (r *result) getStatus() string { 72 | if r.err == nil { 73 | return "ONLINE" 74 | } 75 | 76 | return "OFFLINE" 77 | } 78 | 79 | // getColoredStatus returns colored string representation for Flow network status. 80 | func (r *result) getColoredStatus() string { 81 | if r.err == nil { 82 | return output.Green(r.getStatus()) 83 | } 84 | 85 | return output.Red(output.Red(r.getStatus())) 86 | } 87 | 88 | // getIcon returns emoji icon representing Flow network status. 89 | func (r *result) getIcon() string { 90 | if r.err == nil { 91 | return output.GoEmoji() 92 | } 93 | 94 | return output.StopEmoji() 95 | } 96 | 97 | // String converts result to a string. 98 | func (r *result) String() string { 99 | var b bytes.Buffer 100 | writer := util.CreateTabWriter(&b) 101 | 102 | _, _ = fmt.Fprintf(writer, "Status:\t %s %s\n", r.getIcon(), r.getColoredStatus()) 103 | _, _ = fmt.Fprintf(writer, "Network:\t %s\n", r.network) 104 | _, _ = fmt.Fprintf(writer, "Access Node:\t %s\n", r.accessNode) 105 | 106 | _ = writer.Flush() 107 | return b.String() 108 | } 109 | 110 | // JSON converts result to a JSON. 111 | func (r *result) JSON() any { 112 | result := make(map[string]string) 113 | 114 | result["network"] = r.network 115 | result["accessNode"] = r.accessNode 116 | result["status"] = r.getStatus() 117 | 118 | return result 119 | } 120 | 121 | // Oneliner returns result as one liner grep friendly. 122 | func (r *result) Oneliner() string { 123 | return r.getStatus() 124 | } 125 | -------------------------------------------------------------------------------- /internal/super/dev.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package super 20 | 21 | import ( 22 | "errors" 23 | "fmt" 24 | "os" 25 | "strings" 26 | 27 | "github.com/onflow/cadence/parser" 28 | "github.com/onflow/flowkit/v2" 29 | "github.com/onflow/flowkit/v2/output" 30 | "github.com/spf13/cobra" 31 | 32 | "github.com/onflow/flow-cli/internal/command" 33 | ) 34 | 35 | type flagsDev struct{} 36 | 37 | var devFlags = flagsDev{} 38 | 39 | var DevCommand = &command.Command{ 40 | Cmd: &cobra.Command{ 41 | Use: "dev", 42 | Short: "Build your Flow project", 43 | Args: cobra.ExactArgs(0), 44 | Example: "flow dev", 45 | GroupID: "super", 46 | Deprecated: "The 'dev' command is deprecated and will be removed in a future release.", 47 | }, 48 | Flags: &devFlags, 49 | RunS: dev, 50 | } 51 | 52 | func dev( 53 | _ []string, 54 | _ command.GlobalFlags, 55 | logger output.Logger, 56 | flow flowkit.Services, 57 | state *flowkit.State, 58 | ) (command.Result, error) { 59 | dir, err := os.Getwd() 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | err = flow.Ping() 65 | if err != nil { 66 | logger.Error("Error connecting to emulator. Make sure you started an emulator using 'flow emulator' command.") 67 | 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())) 68 | return nil, nil 69 | } 70 | 71 | serviceAccount, err := state.EmulatorServiceAccount() 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | flow.SetLogger(output.NewStdoutLogger(output.NoneLog)) 77 | 78 | project, err := newProject( 79 | *serviceAccount, 80 | flow, 81 | state, 82 | newProjectFiles(dir), 83 | ) 84 | if err != nil { 85 | fmt.Printf("%s Failed to run the command, please make sure you ran 'flow setup' command first and that you are running this command inside the project ROOT folder.\n\n", output.TryEmoji()) 86 | return nil, err 87 | } 88 | 89 | err = project.startup() 90 | if err != nil { 91 | if strings.Contains(err.Error(), "does not have a valid signature") { 92 | fmt.Printf("%s Failed to run the command, please make sure you started the emulator inside the project ROOT folder by running 'flow emulator'.\n\n", output.TryEmoji()) 93 | return nil, nil 94 | } 95 | 96 | var parseErr parser.Error 97 | if errors.As(err, &parseErr) { 98 | fmt.Println(err) // we just print the error but keep watching files for changes, since they might fix the issue 99 | } else { 100 | return nil, err 101 | } 102 | } 103 | 104 | err = project.watch() 105 | if err != nil { 106 | return nil, err 107 | } 108 | 109 | return nil, nil 110 | } 111 | -------------------------------------------------------------------------------- /internal/super/files_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package super 20 | 21 | import ( 22 | "fmt" 23 | "path/filepath" 24 | "testing" 25 | 26 | "github.com/stretchr/testify/assert" 27 | ) 28 | 29 | func Test_AccountFromPath(t *testing.T) { 30 | paths := [][]string{ // first is path, second is account name 31 | {"cadence/contracts/alice/foo.cdc", "alice"}, 32 | {"cadence/contracts/alice", "alice"}, 33 | {"cadence/contracts/alice/boo/foo.cdc", ""}, 34 | {"cadence/contracts/foo.cdc", ""}, 35 | {"cadence/contracts/foo/bar/goo/foo", ""}, 36 | } 37 | 38 | for i, test := range paths { 39 | name, ok := accountFromPath(filepath.FromSlash(test[0])) 40 | assert.Equal(t, test[1] != "", ok) // if we don't provide a name we mean it shouldn't be returned 41 | assert.Equal(t, test[1], name, fmt.Sprintf("failed test %d", i)) 42 | } 43 | } 44 | 45 | func Test_RelativeProjectPath(t *testing.T) { 46 | cdcDir := "/Users/Mike/Dev/my-project/cadence" 47 | paths := [][]string{ 48 | {filepath.Join(cdcDir, "/contracts/foo.cdc"), "cadence/contracts/foo.cdc"}, 49 | {filepath.Join(cdcDir, "/contracts/alice/foo.cdc"), "cadence/contracts/alice/foo.cdc"}, 50 | {filepath.Join(cdcDir, "/scripts/bar.cdc"), "cadence/scripts/bar.cdc"}, 51 | {filepath.Join(cdcDir, "/bar.cdc"), "cadence/bar.cdc"}, 52 | } 53 | 54 | f := &projectFiles{ 55 | cadencePath: cdcDir, 56 | } 57 | 58 | for i, test := range paths { 59 | rel, err := f.relProjectPath(filepath.FromSlash(test[0])) 60 | assert.NoError(t, err) 61 | assert.Equal(t, filepath.FromSlash(test[1]), rel, fmt.Sprintf("test %d failed", i)) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /internal/super/generator/contract_template.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package generator 20 | 21 | import ( 22 | "fmt" 23 | "maps" 24 | "path/filepath" 25 | 26 | flowsdk "github.com/onflow/flow-go-sdk" 27 | "github.com/onflow/flowkit/v2" 28 | "github.com/onflow/flowkit/v2/config" 29 | 30 | "github.com/onflow/flow-cli/internal/util" 31 | ) 32 | 33 | const ( 34 | DefaultContractDirectory = "contracts" 35 | DefaultTestAddress = "0x0000000000000007" 36 | ) 37 | 38 | // Contract contains properties for contracts 39 | type ContractTemplate struct { 40 | Name string 41 | Account string 42 | TemplatePath string 43 | Data map[string]interface{} 44 | SkipTests bool 45 | SaveState bool 46 | } 47 | 48 | var _ TemplateItem = ContractTemplate{} 49 | var _ TemplateItemWithStateUpdate = ContractTemplate{} 50 | var _ TemplateItemWithChildren = ContractTemplate{} 51 | 52 | func (c ContractTemplate) GetType() string { 53 | return "contract" 54 | } 55 | 56 | func (c ContractTemplate) GetTemplatePath() string { 57 | if c.TemplatePath == "" { 58 | return "contract_init.cdc.tmpl" 59 | } 60 | 61 | return c.TemplatePath 62 | } 63 | 64 | func (c ContractTemplate) GetData() map[string]interface{} { 65 | data := map[string]interface{}{ 66 | "Name": c.Name, 67 | } 68 | maps.Copy(data, c.Data) 69 | return data 70 | } 71 | 72 | func (c ContractTemplate) GetTargetPath() string { 73 | return filepath.Join(DefaultCadenceDirectory, DefaultContractDirectory, c.Account, util.AddCDCExtension(c.Name)) 74 | } 75 | 76 | func (c ContractTemplate) UpdateState(state *flowkit.State) error { 77 | var aliases config.Aliases 78 | 79 | if c.SkipTests != true { 80 | aliases = config.Aliases{{ 81 | Network: config.TestingNetwork.Name, 82 | Address: flowsdk.HexToAddress(DefaultTestAddress), 83 | }} 84 | } 85 | 86 | contract := config.Contract{ 87 | Name: c.Name, 88 | Location: c.GetTargetPath(), 89 | Aliases: aliases, 90 | } 91 | 92 | state.Contracts().AddOrUpdate(contract) 93 | 94 | if c.SaveState { 95 | err := state.SaveDefault() // TODO: Support adding a target project directory 96 | if err != nil { 97 | return fmt.Errorf("error saving to flow.json: %w", err) 98 | } 99 | } 100 | 101 | return nil 102 | } 103 | 104 | func (c ContractTemplate) GetChildren() []TemplateItem { 105 | if c.SkipTests { 106 | return []TemplateItem{} 107 | } 108 | 109 | return []TemplateItem{ 110 | TestTemplate{ 111 | Name: c.Name, 112 | TemplatePath: "contract_init_test.cdc.tmpl", 113 | Data: map[string]interface{}{ 114 | "ContractName": c.Name, 115 | }, 116 | }, 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /internal/super/generator/file_template.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package generator 20 | 21 | import ( 22 | "github.com/onflow/flowkit/v2" 23 | ) 24 | 25 | // FileTemplate is a template for raw 26 | type FileTemplate struct { 27 | TemplatePath string 28 | TargetPath string 29 | Data map[string]interface{} 30 | } 31 | 32 | func NewFileTemplate( 33 | templatePath string, 34 | targetPath string, 35 | data map[string]interface{}, 36 | ) FileTemplate { 37 | return FileTemplate{ 38 | TemplatePath: templatePath, 39 | TargetPath: targetPath, 40 | Data: data, 41 | } 42 | } 43 | 44 | var _ TemplateItem = FileTemplate{} 45 | 46 | // GetType returns the type of the contract 47 | func (c FileTemplate) GetType() string { 48 | return "file" 49 | } 50 | 51 | func (c FileTemplate) GetTemplatePath() string { 52 | return c.TemplatePath 53 | } 54 | 55 | // GetData returns the data of the contract 56 | func (c FileTemplate) GetData() map[string]interface{} { 57 | return c.Data 58 | } 59 | 60 | func (c FileTemplate) GetTargetPath() string { 61 | return c.TargetPath 62 | } 63 | 64 | func (c FileTemplate) UpdateState(state *flowkit.State) error { 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /internal/super/generator/script_template.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package generator 20 | 21 | import ( 22 | "path/filepath" 23 | 24 | "github.com/onflow/flow-cli/internal/util" 25 | ) 26 | 27 | const ( 28 | DefaultScriptDirectory = "scripts" 29 | ) 30 | 31 | type ScriptTemplate struct { 32 | Name string 33 | TemplatePath string 34 | Data map[string]interface{} 35 | } 36 | 37 | var _ TemplateItem = ScriptTemplate{} 38 | 39 | func (o ScriptTemplate) GetType() string { 40 | return "script" 41 | } 42 | 43 | func (o ScriptTemplate) GetTemplatePath() string { 44 | if o.TemplatePath == "" { 45 | return "script_init.cdc.tmpl" 46 | } 47 | 48 | return o.TemplatePath 49 | } 50 | 51 | func (o ScriptTemplate) GetData() map[string]interface{} { 52 | return o.Data 53 | } 54 | 55 | func (o ScriptTemplate) GetTargetPath() string { 56 | return filepath.Join(DefaultCadenceDirectory, DefaultScriptDirectory, util.AddCDCExtension(o.Name)) 57 | } 58 | -------------------------------------------------------------------------------- /internal/super/generator/templates/contract_counter.cdc.tmpl: -------------------------------------------------------------------------------- 1 | access(all) contract {{ .Name }} { 2 | 3 | access(all) var count: Int 4 | 5 | // Event to be emitted when the counter is incremented 6 | access(all) event CounterIncremented(newCount: Int) 7 | 8 | // Event to be emitted when the counter is decremented 9 | access(all) event CounterDecremented(newCount: Int) 10 | 11 | init() { 12 | self.count = 0 13 | } 14 | 15 | // Public function to increment the counter 16 | access(all) fun increment() { 17 | self.count = self.count + 1 18 | emit CounterIncremented(newCount: self.count) 19 | } 20 | 21 | // Public function to decrement the counter 22 | access(all) fun decrement() { 23 | self.count = self.count - 1 24 | emit CounterDecremented(newCount: self.count) 25 | } 26 | 27 | // Public function to get the current count 28 | view access(all) fun getCount(): Int { 29 | return self.count 30 | } 31 | } -------------------------------------------------------------------------------- /internal/super/generator/templates/contract_init.cdc.tmpl: -------------------------------------------------------------------------------- 1 | access(all) 2 | contract {{ .Name }} { 3 | init() {} 4 | } -------------------------------------------------------------------------------- /internal/super/generator/templates/contract_init_test.cdc.tmpl: -------------------------------------------------------------------------------- 1 | import Test 2 | 3 | access(all) let account = Test.createAccount() 4 | 5 | access(all) fun testContract() { 6 | let err = Test.deployContract( 7 | name: "{{ .ContractName }}", 8 | path: "../contracts/{{ .ContractName }}.cdc", 9 | arguments: [], 10 | ) 11 | 12 | Test.expect(err, Test.beNil()) 13 | } -------------------------------------------------------------------------------- /internal/super/generator/templates/empty_test.cdc.tmpl: -------------------------------------------------------------------------------- 1 | import Test 2 | 3 | access(all) let account = Test.createAccount() 4 | 5 | access(all) fun testExample() { 6 | // Test something 7 | Test.expect(true, true) 8 | } -------------------------------------------------------------------------------- /internal/super/generator/templates/script_counter.cdc.tmpl: -------------------------------------------------------------------------------- 1 | import "{{ .ContractName }}" 2 | 3 | access(all) 4 | fun main(): Int { 5 | return {{ .ContractName }}.getCount() 6 | } 7 | -------------------------------------------------------------------------------- /internal/super/generator/templates/script_init.cdc.tmpl: -------------------------------------------------------------------------------- 1 | access(all) 2 | fun main() { 3 | // Script details here 4 | } -------------------------------------------------------------------------------- /internal/super/generator/templates/transaction_counter.cdc.tmpl: -------------------------------------------------------------------------------- 1 | import "{{ .ContractName }}" 2 | 3 | transaction { 4 | 5 | prepare(acct: &Account) { 6 | // Authorizes the transaction 7 | } 8 | 9 | execute { 10 | // Increment the counter 11 | Counter.increment() 12 | 13 | // Retrieve the new count and log it 14 | let newCount = Counter.getCount() 15 | log("New count after incrementing: ".concat(newCount.toString())) 16 | } 17 | } -------------------------------------------------------------------------------- /internal/super/generator/templates/transaction_init.cdc.tmpl: -------------------------------------------------------------------------------- 1 | transaction() { 2 | prepare(account: &Account) {} 3 | 4 | execute {} 5 | } -------------------------------------------------------------------------------- /internal/super/generator/test_template.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package generator 20 | 21 | import ( 22 | "path/filepath" 23 | 24 | "github.com/onflow/flow-cli/internal/util" 25 | ) 26 | 27 | const ( 28 | DefaultTestDirectory = "tests" 29 | ) 30 | 31 | type TestTemplate struct { 32 | Name string 33 | TemplatePath string 34 | Data map[string]interface{} 35 | } 36 | 37 | var _ TemplateItem = TestTemplate{} 38 | 39 | func (t TestTemplate) GetType() string { 40 | return "test" 41 | } 42 | 43 | // GetTemplatePath returns an empty string for scripts and transactions 44 | func (t TestTemplate) GetTemplatePath() string { 45 | if t.TemplatePath == "" { 46 | return "empty_test.cdc.tmpl" 47 | } 48 | 49 | return t.TemplatePath 50 | } 51 | 52 | // GetData returns the data of the script or transaction 53 | func (t TestTemplate) GetData() map[string]interface{} { 54 | return t.Data 55 | } 56 | 57 | func (t TestTemplate) GetTargetPath() string { 58 | baseName := t.Name + "_test" 59 | return filepath.Join(DefaultCadenceDirectory, DefaultTestDirectory, util.AddCDCExtension(baseName)) 60 | } 61 | -------------------------------------------------------------------------------- /internal/super/generator/transaction_template.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package generator 20 | 21 | import ( 22 | "path/filepath" 23 | 24 | "github.com/onflow/flow-cli/internal/util" 25 | ) 26 | 27 | const ( 28 | DefaultTransactionDirectory = "transactions" 29 | ) 30 | 31 | // TransactionTemplate contains only a name property for scripts and transactions 32 | type TransactionTemplate struct { 33 | Name string 34 | TemplatePath string 35 | Data map[string]interface{} 36 | } 37 | 38 | var _ TemplateItem = TransactionTemplate{} 39 | 40 | func (o TransactionTemplate) GetType() string { 41 | return "transaction" 42 | } 43 | 44 | // GetTemplatePath returns an empty string for scripts and transactions 45 | func (o TransactionTemplate) GetTemplatePath() string { 46 | if o.TemplatePath == "" { 47 | return "transaction_init.cdc.tmpl" 48 | } 49 | 50 | return o.TemplatePath 51 | } 52 | 53 | // GetData returns the data of the script or transaction 54 | func (o TransactionTemplate) GetData() map[string]interface{} { 55 | return o.Data 56 | } 57 | 58 | func (o TransactionTemplate) GetTargetPath() string { 59 | return filepath.Join(DefaultCadenceDirectory, DefaultTransactionDirectory, util.AddCDCExtension(o.Name)) 60 | } 61 | -------------------------------------------------------------------------------- /internal/tools/flowser.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package tools 20 | 21 | import ( 22 | "fmt" 23 | "os" 24 | "runtime" 25 | 26 | "github.com/onflow/flow-cli/internal/prompt" 27 | 28 | "github.com/onflowser/flowser/v3/pkg/flowser" 29 | "github.com/spf13/cobra" 30 | 31 | "github.com/onflow/flowkit/v2" 32 | "github.com/onflow/flowkit/v2/config" 33 | "github.com/onflow/flowkit/v2/output" 34 | 35 | "github.com/onflow/flow-cli/internal/command" 36 | "github.com/onflow/flow-cli/internal/settings" 37 | ) 38 | 39 | type flagsFlowser struct{} 40 | 41 | var flowserFlags = flagsWallet{} 42 | 43 | var Flowser = &command.Command{ 44 | Cmd: &cobra.Command{ 45 | Use: "flowser", 46 | Short: "Run Flowser project explorer", 47 | Example: "flow flowser", 48 | Args: cobra.ExactArgs(0), 49 | GroupID: "tools", 50 | }, 51 | Flags: &flowserFlags, 52 | Run: runFlowser, 53 | } 54 | 55 | func runFlowser( 56 | _ []string, 57 | _ command.GlobalFlags, 58 | _ output.Logger, 59 | reader flowkit.ReaderWriter, 60 | _ flowkit.Services, 61 | ) (command.Result, error) { 62 | flowser := flowser.New() 63 | 64 | installPath, err := settings.GetFlowserPath() 65 | if err != nil { 66 | return nil, fmt.Errorf("failure reading setting: %w", err) 67 | } 68 | 69 | if !flowser.Installed(installPath) { 70 | installPath, err = installFlowser(flowser, installPath) 71 | if err != nil { 72 | return nil, err 73 | } 74 | } 75 | 76 | projectPath, err := os.Getwd() 77 | if err != nil { 78 | return nil, err 79 | } 80 | 81 | // check if current directory is existing flow project if not then don't pass project path to Flowser, so user can choose a project 82 | _, err = reader.ReadFile(config.DefaultPath) 83 | if os.IsNotExist(err) { 84 | projectPath = "" 85 | } 86 | 87 | fmt.Printf("%s Starting up Flowser, please wait...\n", output.SuccessEmoji()) 88 | err = flowser.Run(installPath, projectPath) 89 | if err != nil { 90 | return nil, err 91 | } 92 | 93 | return nil, nil 94 | } 95 | 96 | func installFlowser(flowser *flowser.App, installPath string) (string, error) { 97 | fmt.Println("It looks like Flowser is not yet installed on your system.") 98 | installChoice := prompt.InstallPrompt() 99 | if installChoice == prompt.CancelInstall { 100 | return "", fmt.Errorf("user denied install") 101 | } 102 | 103 | // if user says it already installed it we only ask for path and return it 104 | if installChoice == prompt.AlreadyInstalled { 105 | installPath = prompt.InstallPathPrompt(installPath) 106 | _ = settings.SetFlowserPath(installPath) 107 | return installPath, nil 108 | } 109 | 110 | // MacOS apps must always be installed inside Application folder 111 | if runtime.GOOS != settings.Darwin { 112 | installPath = prompt.InstallPathPrompt(installPath) 113 | _ = settings.SetFlowserPath(installPath) 114 | } 115 | 116 | logger := output.NewStdoutLogger(output.InfoLog) 117 | logger.StartProgress(fmt.Sprintf("%s Installing Flowser, this may take few minutes, please wait ", output.TryEmoji())) 118 | defer logger.StopProgress() 119 | 120 | // create all folders if they don't exist, does nothing if they exist 121 | err := os.MkdirAll(installPath, os.ModePerm) 122 | if err != nil { 123 | return "", err 124 | } 125 | 126 | err = flowser.Install(installPath) 127 | if err != nil { 128 | return "", fmt.Errorf("could not install Flowser: %w", err) 129 | } 130 | 131 | return installPath, nil 132 | } 133 | -------------------------------------------------------------------------------- /internal/tools/wallet.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package tools 20 | 21 | import ( 22 | "fmt" 23 | "strings" 24 | 25 | devWallet "github.com/onflow/fcl-dev-wallet/go/wallet" 26 | "github.com/spf13/cobra" 27 | 28 | "github.com/onflow/flowkit/v2" 29 | "github.com/onflow/flowkit/v2/output" 30 | 31 | "github.com/onflow/flow-cli/internal/command" 32 | ) 33 | 34 | type flagsWallet struct { 35 | Port uint `default:"8701" flag:"port" info:"Dev wallet port to listen on"` 36 | Host string `default:"http://localhost:8888" flag:"emulator-host" info:"Host for access node connection"` 37 | } 38 | 39 | var walletFlags = flagsWallet{} 40 | 41 | var DevWallet = &command.Command{ 42 | Cmd: &cobra.Command{ 43 | Use: "dev-wallet", 44 | Short: "Run a development wallet", 45 | Example: "flow dev-wallet", 46 | Args: cobra.ExactArgs(0), 47 | GroupID: "tools", 48 | }, 49 | Flags: &walletFlags, 50 | RunS: wallet, 51 | } 52 | 53 | func wallet( 54 | _ []string, 55 | _ command.GlobalFlags, 56 | _ output.Logger, 57 | _ flowkit.Services, 58 | state *flowkit.State, 59 | ) (command.Result, error) { 60 | service, err := state.EmulatorServiceAccount() 61 | if err != nil { 62 | return nil, err 63 | } 64 | 65 | privateKey, err := service.Key.PrivateKey() 66 | if err != nil { 67 | return nil, err 68 | } 69 | 70 | conf := devWallet.FlowConfig{ 71 | Address: fmt.Sprintf("0x%s", service.Address.String()), 72 | PrivateKey: strings.TrimPrefix((*privateKey).String(), "0x"), 73 | PublicKey: strings.TrimPrefix((*privateKey).PublicKey().String(), "0x"), 74 | AccessNode: walletFlags.Host, 75 | } 76 | 77 | srv, err := devWallet.NewHTTPServer(walletFlags.Port, &conf) 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | fmt.Printf("%s Starting dev wallet server on port %d\n", output.SuccessEmoji(), walletFlags.Port) 83 | fmt.Printf("%s Make sure the emulator is running\n", output.WarningEmoji()) 84 | 85 | srv.Start() 86 | return nil, nil 87 | } 88 | -------------------------------------------------------------------------------- /internal/transactions/decode.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package transactions 20 | 21 | import ( 22 | "fmt" 23 | 24 | "github.com/onflow/flowkit/v2/transactions" 25 | 26 | "github.com/spf13/cobra" 27 | 28 | "github.com/onflow/flowkit/v2" 29 | "github.com/onflow/flowkit/v2/output" 30 | 31 | "github.com/onflow/flow-cli/internal/command" 32 | ) 33 | 34 | type flagsDecode struct { 35 | Include []string `default:"" flag:"include" info:"Fields to include in the output. Valid values: signatures, code, payload."` 36 | } 37 | 38 | var decodeFlags = flagsDecode{} 39 | 40 | var decodeCommand = &command.Command{ 41 | Cmd: &cobra.Command{ 42 | Use: "decode ", 43 | Short: "Decode a transaction", 44 | Example: "flow transactions decode ./transaction.rlp", 45 | Args: cobra.ExactArgs(1), 46 | }, 47 | Flags: &decodeFlags, 48 | Run: decode, 49 | } 50 | 51 | func decode( 52 | args []string, 53 | _ command.GlobalFlags, 54 | _ output.Logger, 55 | reader flowkit.ReaderWriter, 56 | _ flowkit.Services, 57 | ) (command.Result, error) { 58 | filename := args[0] 59 | payload, err := reader.ReadFile(filename) 60 | if err != nil { 61 | return nil, fmt.Errorf("failed to read transaction from %s: %v", filename, err) 62 | } 63 | 64 | tx, err := transactions.NewFromPayload(payload) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | return &transactionResult{ 70 | tx: tx.FlowTransaction(), 71 | include: decodeFlags.Include, 72 | }, nil 73 | } 74 | -------------------------------------------------------------------------------- /internal/transactions/get-system.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package transactions 20 | 21 | import ( 22 | "context" 23 | 24 | "github.com/spf13/cobra" 25 | 26 | "github.com/onflow/flowkit/v2" 27 | "github.com/onflow/flowkit/v2/output" 28 | 29 | "github.com/onflow/flow-cli/internal/command" 30 | ) 31 | 32 | type flagsGetSystem struct { 33 | Include []string `default:"" flag:"include" info:"Fields to include in the output. Valid values: signatures, code, payload, fee-events."` 34 | Exclude []string `default:"" flag:"exclude" info:"Fields to exclude from the output. Valid values: events."` 35 | } 36 | 37 | var getSystemFlags = flagsGetSystem{} 38 | 39 | var getSystemCommand = &command.Command{ 40 | Cmd: &cobra.Command{ 41 | Use: "get-system ", 42 | Short: "Get the system transaction by block info", 43 | Example: "flow transactions get-system a1b2c3...", 44 | Args: cobra.ExactArgs(1), 45 | }, 46 | Flags: &getSystemFlags, 47 | Run: getSystemTransaction, 48 | } 49 | 50 | func getSystemTransaction( 51 | args []string, 52 | _ command.GlobalFlags, 53 | logger output.Logger, 54 | _ flowkit.ReaderWriter, 55 | flow flowkit.Services, 56 | ) (command.Result, error) { 57 | query, err := flowkit.NewBlockQuery(args[0]) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | logger.StartProgress("Fetching Block...") 63 | defer logger.StopProgress() 64 | block, err := flow.GetBlock(context.Background(), query) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | tx, result, err := flow.GetSystemTransaction(context.Background(), block.ID) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | return &transactionResult{ 75 | result: result, 76 | tx: tx, 77 | include: getSystemFlags.Include, 78 | exclude: getSystemFlags.Exclude, 79 | }, nil 80 | } 81 | -------------------------------------------------------------------------------- /internal/transactions/get.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package transactions 20 | 21 | import ( 22 | "context" 23 | "strings" 24 | 25 | flowsdk "github.com/onflow/flow-go-sdk" 26 | "github.com/spf13/cobra" 27 | 28 | "github.com/onflow/flowkit/v2" 29 | "github.com/onflow/flowkit/v2/output" 30 | 31 | "github.com/onflow/flow-cli/internal/command" 32 | ) 33 | 34 | type flagsGet struct { 35 | Sealed bool `default:"true" flag:"sealed" info:"Wait for a sealed result"` 36 | Include []string `default:"" flag:"include" info:"Fields to include in the output. Valid values: signatures, code, payload, fee-events."` 37 | Exclude []string `default:"" flag:"exclude" info:"Fields to exclude from the output. Valid values: events."` 38 | } 39 | 40 | var getFlags = flagsGet{} 41 | 42 | var getCommand = &command.Command{ 43 | Cmd: &cobra.Command{ 44 | Use: "get ", 45 | Aliases: []string{"status"}, 46 | Short: "Get the transaction by ID", 47 | Example: "flow transactions get 07a8...b433", 48 | Args: cobra.ExactArgs(1), 49 | }, 50 | Flags: &getFlags, 51 | Run: get, 52 | } 53 | 54 | func get( 55 | args []string, 56 | _ command.GlobalFlags, 57 | _ output.Logger, 58 | _ flowkit.ReaderWriter, 59 | flow flowkit.Services, 60 | ) (command.Result, error) { 61 | id := flowsdk.HexToID(strings.TrimPrefix(args[0], "0x")) 62 | 63 | tx, result, err := flow.GetTransactionByID(context.Background(), id, getFlags.Sealed) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | return &transactionResult{ 69 | result: result, 70 | tx: tx, 71 | include: getFlags.Include, 72 | exclude: getFlags.Exclude, 73 | }, nil 74 | } 75 | -------------------------------------------------------------------------------- /internal/transactions/send-signed.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package transactions 20 | 21 | import ( 22 | "context" 23 | "fmt" 24 | 25 | "github.com/onflow/flow-cli/internal/prompt" 26 | 27 | "github.com/onflow/flowkit/v2/transactions" 28 | 29 | "github.com/spf13/cobra" 30 | 31 | "github.com/onflow/flowkit/v2" 32 | "github.com/onflow/flowkit/v2/output" 33 | 34 | "github.com/onflow/flow-cli/internal/command" 35 | ) 36 | 37 | type flagsSendSigned struct { 38 | Include []string `default:"" flag:"include" info:"Fields to include in the output. Valid values: signatures, code, payload."` 39 | Exclude []string `default:"" flag:"exclude" info:"Fields to exclude from the output (events)"` 40 | } 41 | 42 | var sendSignedFlags = flagsSendSigned{} 43 | 44 | var sendSignedCommand = &command.Command{ 45 | Cmd: &cobra.Command{ 46 | Use: "send-signed ", 47 | Short: "Send signed transaction", 48 | Args: cobra.ExactArgs(1), 49 | Example: `flow transactions send-signed signed.rlp`, 50 | }, 51 | Flags: &sendSignedFlags, 52 | Run: sendSigned, 53 | } 54 | 55 | func sendSigned( 56 | args []string, 57 | globalFlags command.GlobalFlags, 58 | logger output.Logger, 59 | reader flowkit.ReaderWriter, 60 | flow flowkit.Services, 61 | ) (command.Result, error) { 62 | filename := args[0] 63 | 64 | code, err := reader.ReadFile(filename) 65 | if err != nil { 66 | return nil, fmt.Errorf("error loading transaction payload: %w", err) 67 | } 68 | 69 | tx, err := transactions.NewFromPayload(code) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | if !globalFlags.Yes && !prompt.ApproveTransactionForSendingPrompt(tx.FlowTransaction()) { 75 | return nil, fmt.Errorf("transaction was not approved for sending") 76 | } 77 | 78 | logger.StartProgress(fmt.Sprintf("Sending transaction with ID: %s", tx.FlowTransaction().ID())) 79 | defer logger.StopProgress() 80 | 81 | sentTx, result, err := flow.SendSignedTransaction(context.Background(), tx) 82 | if err != nil { 83 | return nil, err 84 | } 85 | 86 | return &transactionResult{ 87 | result: result, 88 | tx: sentTx, 89 | include: sendSignedFlags.Include, 90 | exclude: sendSignedFlags.Exclude, 91 | }, nil 92 | } 93 | -------------------------------------------------------------------------------- /internal/util/emoji.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package util 20 | 21 | import ( 22 | "fmt" 23 | "runtime" 24 | ) 25 | 26 | func PrintEmoji(emoji string) string { 27 | if runtime.GOOS == "windows" { 28 | return "" 29 | } 30 | 31 | return emoji 32 | } 33 | 34 | func MessageWithEmojiPrefix(emoji string, message string) string { 35 | return fmt.Sprintf("%s%s", PrintEmoji(emoji+" "), message) 36 | } 37 | -------------------------------------------------------------------------------- /internal/util/files.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package util 20 | 21 | import ( 22 | "fmt" 23 | "path/filepath" 24 | "strings" 25 | ) 26 | 27 | func AddCDCExtension(name string) string { 28 | if strings.HasSuffix(name, ".cdc") { 29 | return name 30 | } 31 | return fmt.Sprintf("%s.cdc", name) 32 | } 33 | 34 | func StripCDCExtension(name string) string { 35 | return strings.TrimSuffix(name, filepath.Ext(name)) 36 | } 37 | -------------------------------------------------------------------------------- /internal/util/test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package util 20 | 21 | import ( 22 | "testing" 23 | 24 | "github.com/onflow/flow-go-sdk/crypto" 25 | "github.com/onflow/flowkit/v2/accounts" 26 | 27 | "github.com/onflow/flow-go-sdk" 28 | "github.com/stretchr/testify/require" 29 | 30 | "github.com/onflow/flowkit/v2" 31 | "github.com/onflow/flowkit/v2/mocks" 32 | "github.com/onflow/flowkit/v2/output" 33 | "github.com/onflow/flowkit/v2/tests" 34 | ) 35 | 36 | var NoLogger = output.NewStdoutLogger(output.NoneLog) 37 | 38 | var TestID = flow.HexToID("24993fc99f81641c45c0afa307e683b4f08d407d90041aa9439f487acb33d633") 39 | 40 | // TestMocks creates mock flowkit services, an empty state and a mock reader writer 41 | func TestMocks(t *testing.T) (*mocks.MockServices, *flowkit.State, flowkit.ReaderWriter) { 42 | services := mocks.DefaultMockServices() 43 | rw, _ := tests.ReaderWriter() 44 | state, err := flowkit.Init(rw) 45 | require.NoError(t, err) 46 | 47 | emulatorAccount, _ := accounts.NewEmulatorAccount(rw, crypto.ECDSA_P256, crypto.SHA3_256, "") 48 | state.Accounts().AddOrUpdate(emulatorAccount) 49 | 50 | return services, state, rw 51 | } 52 | -------------------------------------------------------------------------------- /internal/version/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Flow CLI 3 | * 4 | * Copyright Flow Foundation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package version 20 | 21 | import ( 22 | "encoding/json" 23 | "fmt" 24 | "runtime/debug" 25 | "strings" 26 | 27 | "github.com/spf13/cobra" 28 | 29 | "github.com/onflow/flow-cli/build" 30 | "github.com/onflow/flow-cli/internal/command" 31 | ) 32 | 33 | var verboseFlag bool 34 | 35 | func init() { 36 | Cmd.Flags().BoolVarP(&verboseFlag, "verbose", "v", false, "Show detailed dependency information") 37 | } 38 | 39 | type versionCmd struct { 40 | Version string 41 | Dependencies []debug.Module 42 | } 43 | 44 | // Print prints the version information in the given format. 45 | func (c versionCmd) Print(format string) error { 46 | switch format { 47 | case command.FormatInline, command.FormatText: 48 | var txtBuilder strings.Builder 49 | txtBuilder.WriteString(fmt.Sprintf("Version: %s\n", c.Version)) 50 | 51 | if verboseFlag { 52 | txtBuilder.WriteString("\nFlow Package Dependencies \n") 53 | for _, dep := range c.Dependencies { 54 | txtBuilder.WriteString(fmt.Sprintf("%s %s\n", dep.Path, dep.Version)) 55 | } 56 | } 57 | 58 | fmt.Println(txtBuilder.String()) 59 | 60 | return nil 61 | 62 | case command.FormatJSON: 63 | jsonRes, err := c.MarshalJSON() 64 | if err != nil { 65 | return err 66 | } 67 | 68 | fmt.Println(string(jsonRes)) 69 | 70 | return nil 71 | 72 | default: 73 | return fmt.Errorf("unsupported format: %s", format) 74 | } 75 | } 76 | 77 | // MarshalJSON returns the JSON encoding of the cmdPrint. 78 | func (c *versionCmd) MarshalJSON() ([]byte, error) { 79 | js := struct { 80 | Version string `json:"version"` 81 | Dependencies []struct { 82 | Package string `json:"package"` 83 | Version string `json:"version"` 84 | } `json:"dependencies"` 85 | }{ 86 | Version: c.Version, 87 | } 88 | 89 | for _, dep := range c.Dependencies { 90 | js.Dependencies = append(js.Dependencies, struct { 91 | Package string `json:"package"` 92 | Version string `json:"version"` 93 | }{ 94 | Package: dep.Path, 95 | Version: dep.Version, 96 | }) 97 | } 98 | 99 | return json.Marshal(js) 100 | } 101 | 102 | var Cmd = &cobra.Command{ 103 | Use: "version", 104 | Short: "View version information", 105 | RunE: func(cmd *cobra.Command, args []string) error { 106 | semver := build.Semver() 107 | 108 | v := &versionCmd{ 109 | Version: semver, 110 | } 111 | 112 | bi, ok := debug.ReadBuildInfo() 113 | if !ok { 114 | return fmt.Errorf("failed to read build info") 115 | } 116 | 117 | // only add dependencies from github.com/onflow 118 | for _, dep := range bi.Deps { 119 | if strings.Contains(dep.Path, "github.com/onflow/") { 120 | v.Dependencies = append(v.Dependencies, *dep) 121 | } 122 | } 123 | 124 | if err := v.Print(command.Flags.Format); err != nil { 125 | return err 126 | } 127 | 128 | return nil 129 | }, 130 | } 131 | -------------------------------------------------------------------------------- /scaffolds.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "FCL Web Dapp", 4 | "repo": "https://github.com/chasefleming/fcl-next-scaffold.git", 5 | "description": "Simple TypeScript web application using next.js, FCL, and Cadence.", 6 | "commit": "7a2b4347471f96d02d90c80ca8c76148d9ce1a70", 7 | "type": "web" 8 | }, 9 | { 10 | "name": "Simple Cadence Project", 11 | "repo": "https://github.com/sideninja/flow-basic-scaffold.git", 12 | "description": "Scaffold contains required folder structure as well as some example Cadence code.", 13 | "commit": "8065287e55bc298d776665bef98a577ceb4a226c" 14 | }, 15 | { 16 | "name": "Cadence NFT Project", 17 | "repo": "https://github.com/nvdtf/flow-nft-scaffold.git", 18 | "description": "Scaffold contains the ExampleNFT sample NFT contract.", 19 | "commit": "ef9437c05549e3f581cf619dec9d59d8c7f6e3ec" 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | v1.18.0 2 | --------------------------------------------------------------------------------