diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 332dee36c..642d7fb35 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -242,6 +242,9 @@ Default command response should be to the stdout and not saved to a file. Anytim the output to be saved to a file we should explicitly specify so by using `--save filename.txt` flag and providing the path. +Result output should include only information that is commonly used and relevant, +don't use too much of user screen drowning whatโ€™s truly important, +instead provide a way to include that data when the user requests by having include, exclude flags. ``` Address 179b6b1cb6755e31 diff --git a/README.md b/README.md index 24f9241d0..a93577416 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +![](./logo-cli.svg) + # Flow CLI The Flow CLI is a command-line interface that provides useful utilities for building Flow applications. diff --git a/cmd/flow/main.go b/cmd/flow/main.go index 45af7a606..e6c803707 100644 --- a/cmd/flow/main.go +++ b/cmd/flow/main.go @@ -32,6 +32,7 @@ import ( "github.com/onflow/flow-cli/internal/events" "github.com/onflow/flow-cli/internal/keys" "github.com/onflow/flow-cli/internal/project" + "github.com/onflow/flow-cli/internal/quick" "github.com/onflow/flow-cli/internal/scripts" "github.com/onflow/flow-cli/internal/status" "github.com/onflow/flow-cli/internal/transactions" @@ -45,8 +46,8 @@ func main() { TraverseChildren: true, } - // hot commands - config.InitCommand.AddToParent(cmd) + // quick commands + quick.InitCommand.AddToParent(cmd) status.Command.AddToParent(cmd) // structured commands @@ -61,6 +62,7 @@ func main() { cmd.AddCommand(blocks.Cmd) cmd.AddCommand(collections.Cmd) cmd.AddCommand(project.Cmd) + cmd.AddCommand(config.Cmd) command.InitFlags(cmd) diff --git a/docs/build-transactions.md b/docs/build-transactions.md index 7a50772c5..2b2cca6e6 100644 --- a/docs/build-transactions.md +++ b/docs/build-transactions.md @@ -124,7 +124,7 @@ The `Type` must be the same as type in the transaction source code for that argu ### Arguments JSON -- Flag: `--argsJSON` +- Flag: `--args-json` - Valid inputs: arguments in JSON-Cadence form. Arguments passed to the Cadence transaction in Cadence JSON format. diff --git a/docs/create-accounts.md b/docs/create-accounts.md index e2bc77119..a916d4e2c 100644 --- a/docs/create-accounts.md +++ b/docs/create-accounts.md @@ -103,6 +103,13 @@ and pay the account creation fee. Specify one or more contracts to be deployed during account creation. +### Include Fields + +- Flag: `--include` +- Valid inputs: `contracts` + +Specify fields to include in the result output. Applies only to the text output. + ### Host - Flag: `--host` diff --git a/docs/decode-keys.md b/docs/decode-keys.md new file mode 100644 index 000000000..a80a76f40 --- /dev/null +++ b/docs/decode-keys.md @@ -0,0 +1,49 @@ +--- +title: Decode Account Key with the Flow CLI +sidebar_title: Decode Account Keys +description: How to decode rlp encoded key pair from the command line +--- + +The Flow CLI provides a command to decode RLP encoded account key. + +```shell +flow keys decode +``` + +## Example Usage + +```shell +> flow keys decode f847b84084d716c14b051ad6b001624f738f5d302636e6b07cc75e4530af7776a4368a2b586dbefc0564ee28384c2696f178cbed52e62811bcc9ecb59568c996d342db2402038203e8 + +Public Key 84d716c14b051ad6b001624f738f5d302636e6b07cc75e4530af7776a4368a2b586dbefc0564ee28384c2696f178cbed52e62811bcc9ecb59568c996d342db24 +Signature algorithm ECDSA_P256 +Hash algorithm SHA3_256 +Weight 1000 +Revoked false +``` + +## Flags + +### Filter + +- Flag: `--filter` +- Short Flag: `-x` +- Valid inputs: a case-sensitive name of the result property. + +Specify any property name from the result you want to return as the only value. + +### Output + +- Flag: `--output` +- Short Flag: `-o` +- Valid inputs: `json`, `inline` + +Specify the format of the command results. + +### Save + +- Flag: `--save` +- Short Flag: `-s` +- Valid inputs: a path in the current filesystem. + +Specify the filename where you want the result to be saved diff --git a/docs/developer-updates/release-notes-v21.md b/docs/developer-updates/release-notes-v21.md new file mode 100644 index 000000000..c407dc57b --- /dev/null +++ b/docs/developer-updates/release-notes-v21.md @@ -0,0 +1,72 @@ +## โฌ†๏ธ Install or Upgrade + +Follow the [Flow CLI installation guide](https://docs.onflow.org/flow-cli/install/) for instructions on how to install or upgrade the CLI. + + +## ๐Ÿ’ฅ Breaking Changes + +### Flow Go SDK Update +Update Flow Go SDK version from v0.19.0 to v0.20.0. +Read more about new version in the [release notes](https://github.com/onflow/flow-go-sdk/releases/tag/v0.20.0). + +## โญ Features + +### New Command To Manage Configuration +Add or remove resources from the configuration using the new `flow config` command. +Usage is possible via interactive prompt or by using flags. Command syntax is as follows: +```js +flow config +``` + +Example for adding an account to the config via interactive prompt: + +```bash +Name: Foo +Address: f8d6e0586b0a20c7 +โœ” ECDSA_P256 +โœ” SHA3_256 +Private key: 1286...01afc +Key index (Default: 0): 0 + +Account Foo added to the configuration +``` + +Example for adding an account to the config without interactive prompt: + +```bash +./main config add account --address f8d6e0586b0a20c7 --name Foo --private-key 1286...01afc + +Account Foo added to the configuration +``` + +We recommend using manage command to do any changes in the configuration as it will also +validate input values for you and will abstract any changes in the configuration format. + +### Decode Keys +Command for decoding public keys in the RLP encoded format. + +Example of using the command: + +```bash +> flow keys decode f847b84084d716c14b051ad6b001624f738f5d302636e6b07cc75e4530af7776a4368a2b586dbefc0564ee28384c2696f178cbed52e62811bcc9ecb59568c996d342db2402038203e8 + +Public Key 84d716c14b051ad6b001624f738f5d302636e6b07cc75e4530af7776a4368a2b586dbefc0564ee28384c2696f178cbed52e62811bcc9ecb59568c996d342db24 +Signature algorithm ECDSA_P256 +Hash algorithm SHA3_256 +Weight 1000 +Revoked false +``` + +## ๐ŸŽ‰ Improvements + +### Include And Exclude Flags +Include and Exclude flags were added to the transaction and account resource thus +allowing you to further specify verbosity of the output. + +### Documentation Changes +Multiple reported documentation fixes. + +## ๐Ÿž Bug Fixes + +### Import Detection Fix +Fix for a reported bug: An error occurs when executing a script that imports a built-in contract (Crypto contract) with Flow CLI command. diff --git a/docs/execute-scripts.md b/docs/execute-scripts.md index 0e27d26e5..3000762ef 100644 --- a/docs/execute-scripts.md +++ b/docs/execute-scripts.md @@ -42,7 +42,7 @@ The `Type` must be the same as type in the script source code for that argument. ### Arguments JSON -- Flag: `--argsJSON` +- Flag: `--args-json` - Valid inputs: arguments in JSON-Cadence form. Arguments passed to the Cadence script in `Type:Value` format. diff --git a/docs/get-accounts.md b/docs/get-accounts.md index 67c83ae17..9d99d725b 100644 --- a/docs/get-accounts.md +++ b/docs/get-accounts.md @@ -49,11 +49,18 @@ Flow [account address](https://docs.onflow.org/concepts/accounts-and-keys/) (pre ## Flags +### Include Fields + +- Flag: `--include` +- Valid inputs: `contracts` + +Specify fields to include in the result output. Applies only to the text output. + ### Contracts - Flag: `--contracts` -Display contracts deployed to the account. +โš ๏ธ Deprecated: use include flag. ### Code โš ๏ธ No longer supported: use contracts flag instead. diff --git a/docs/get-transactions.md b/docs/get-transactions.md index 7cd04c9ae..8f4373f4a 100644 --- a/docs/get-transactions.md +++ b/docs/get-transactions.md @@ -14,24 +14,50 @@ flow transactions get ## Example Usage ```shell -> flow transactions get ff35821007322405608c0d3da79312617f8d16e118afe63e764b5e68edc96dd5 --host access.mainnet.nodes.onflow.org:9000 - -ID ff35821007322405608c0d3da79312617f8d16e118afe63e764b5e68edc96dd5 -Status SEALED -Payer 12e354a23e4f791d -Events - Index 0 - Type flow.AccountCreated - Tx ID ff35821007322405608c0d3da79312617f8d16e118afe63e764b5e68edc96dd5 - Values - address (Address) 18c4931b5f3c7151 - - Index 1 - Type flow.AccountKeyAdded - Tx ID ff35821007322405608c0d3da79312617f8d16e118afe63e764b5e68edc96dd5 - Values - address (Address) 18c4931b5f3c7151 - publicKey (Unknown) f847b8404c296679364d2...7b168678cc762bc08f342d8d92e0a36e6ecfdcf15850721821823e8 +> flow transactions get 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa --network mainnet + +Status โœ… SEALED +ID 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa +Payer 18eb4ee6b3c026d2 +Authorizers [18eb4ee6b3c026d2] + +Proposal Key: + Address 18eb4ee6b3c026d2 + Index 11 + Sequence 17930 + +Payload Signature 0: 18eb4ee6b3c026d2 +Payload Signature 1: 18eb4ee6b3c026d2 +Envelope Signature 0: 18eb4ee6b3c026d2 +Signatures (minimized, use --include signatures) + +Events: + Index 0 + Type A.1654653399040a61.FlowToken.TokensWithdrawn + Tx ID 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa + Values + - amount (UFix64): 0.00100000 + - from ({}?): 18eb4ee6b3c026d2 + + Index 1 + Type A.1654653399040a61.FlowToken.TokensDeposited + Tx ID 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa + Values + - amount (UFix64): 0.00100000 + - to ({}?): 5068e27f275c546c + + Index 2 + Type A.18eb4ee6b3c026d2.PrivateReceiverForwarder.PrivateDeposit + Tx ID 40bc4b100c1930c61381c22e0f4c10a7f5827975ee25715527c1061b8d71e5aa + Values + - amount (UFix64): 0.00100000 + - to ({}?): 5068e27f275c546c + + + +Code (hidden, use --include code) + +Payload (hidden, use --include payload) ``` ## Arguments @@ -45,12 +71,18 @@ The first argument is the ID (hash) of the transaction. ## Flags -### Display Transaction Code +### Include Fields + +- Flag: `--include` +- Valid inputs: `code`, `payload`, `signatures` + +Specify fields to include in the result output. Applies only to the text output. + +### Code - Flag: `--code` -- Default: `false` -Indicate whether to print the transaction Cadence code. +โš ๏ธ Deprecated: use include flag. ### Wait for Seal @@ -60,6 +92,13 @@ Indicate whether to print the transaction Cadence code. Indicate whether to wait for the transaction to be sealed before displaying the result. +### Exclude Fields + +- Flag: `--exclude` +- Valid inputs: `events` + +Specify fields to exclude from the result output. Applies only to the text output. + ### Host - Flag: `--host` diff --git a/docs/send-signed-transactions.md b/docs/send-signed-transactions.md index 7d62d9a89..7747c5da5 100644 --- a/docs/send-signed-transactions.md +++ b/docs/send-signed-transactions.md @@ -22,47 +22,25 @@ flow transactions send-signed > flow transactions send-signed ./signed.rlp Status โœ… SEALED -ID b6430b35ba23849a8acb4fa1a4a1d5cce3ed4589111ecbb3984de1b6bd1ba39e -Payer a2c4941b5f3c7151 -Authorizers [a2c4941b5f3c7151] +ID 528332aceb288cdfe4d11d6522aa27bed94fb3266b812cb350eb3526ed489d99 +Payer f8d6e0586b0a20c7 +Authorizers [f8d6e0586b0a20c7] Proposal Key: - Address a2c4941b5f3c7151 + Address f8d6e0586b0a20c7 Index 0 - Sequence 9 + Sequence 0 No Payload Signatures -Envelope Signature 0: - Address a2c4941b5f3c7151 - Signature 5391a6fed0fe...2742048166f9d5c925a8dcb78a6d8c710921d67 - Key Index 0 - +Envelope Signature 0: f8d6e0586b0a20c7 +Signatures (minimized, use --include signatures) Events: None +Code (hidden, use --include code) -Arguments (1): - - Argument 0: {"type":"String","value":"Meow"} - - -Code - -transaction(greeting: String) { - let guest: Address - - prepare(authorizer: AuthAccount) { - self.guest = authorizer.address - } - - execute { - log(greeting.concat(",").concat(self.guest.toString())) - } -} - - -Payload: -f90184f90138...8a9462751237da2742048166f9d5c925a8dcb78a6d8c710921d67 +Payload (hidden, use --include payload) ``` @@ -78,6 +56,28 @@ transaction to be executed. ## Flags +### Include Fields + +- Flag: `--include` +- Valid inputs: `code`, `payload` + +Specify fields to include in the result output. Applies only to the text output. + +### Exclude Fields + +- Flag: `--exclude` +- Valid inputs: `events` + +Specify fields to exclude from the result output. Applies only to the text output. + +### Filter + +- Flag: `--filter` +- Short Flag: `-x` +- Valid inputs: a case-sensitive name of the result property. + +Specify any property name from the result you want to return as the only value. + ### Host - Flag: `--host` @@ -97,14 +97,6 @@ any host defined by the `--network` flag. Specify which network you want the command to use for execution. -### Filter - -- Flag: `--filter` -- Short Flag: `-x` -- Valid inputs: a case-sensitive name of the result property. - -Specify any property name from the result you want to return as the only value. - ### Output - Flag: `--output` diff --git a/docs/send-transactions.md b/docs/send-transactions.md index 1e1ed71be..2d3509f0a 100644 --- a/docs/send-transactions.md +++ b/docs/send-transactions.md @@ -14,52 +14,28 @@ flow transactions send ## Example Usage ```shell -> flow transactions send ./transaction.cdc \ - --args-json '[{"type": "String","value": "Hello World"}]' \ - --signer my-testnet-account +> flow transactions send ./tx.cdc --arg String:Hello Status โœ… SEALED -ID b6430b35ba23849a8acb4fa1a4a1d5cce3ed4589111ecbb3984de1b6bd1ba39e -Payer a2c4941b5f3c7151 -Authorizers [a2c4941b5f3c7151] +ID b04b6bcc3164f5ee6b77fa502c3a682e0db57fc47e5b8a8ef3b56aae50ad49c8 +Payer f8d6e0586b0a20c7 +Authorizers [f8d6e0586b0a20c7] Proposal Key: - Address a2c4941b5f3c7151 + Address f8d6e0586b0a20c7 Index 0 - Sequence 9 + Sequence 0 No Payload Signatures -Envelope Signature 0: - Address a2c4941b5f3c7151 - Signature 5391a6fed0fe...2742048166f9d5c925a8dcb78a6d8c710921d67 - Key Index 0 - +Envelope Signature 0: f8d6e0586b0a20c7 +Signatures (minimized, use --include signatures) Events: None +Code (hidden, use --include code) -Arguments (1): - - Argument 0: {"type":"String","value":"Hello World"} - - -Code - -transaction(greeting: String) { - let guest: Address - - prepare(authorizer: AuthAccount) { - self.guest = authorizer.address - } - - execute { - log(greeting.concat(",").concat(self.guest.toString())) - } -} - - -Payload: -f90184f90138...8a9462751237da2742048166f9d5c925a8dcb78a6d8c710921d67 +Payload (hidden, use --include payload) ``` @@ -87,6 +63,13 @@ transaction to be executed. ## Flags +### Include Fields + +- Flag: `--include` +- Valid inputs: `code`, `payload` + +Specify fields to include in the result output. Applies only to the text output. + ### Code - Flag: `--code` @@ -99,6 +82,13 @@ transaction to be executed. โš ๏ธ No longer supported: all transactions will provide result. +### Exclude Fields + +- Flag: `--exclude` +- Valid inputs: `events` + +Specify fields to exclude from the result output. Applies only to the text output. + ### Signer - Flag: `--signer` diff --git a/internal/accounts/accounts.go b/internal/accounts/accounts.go index ed4bb1ca5..3f3d8c68d 100644 --- a/internal/accounts/accounts.go +++ b/internal/accounts/accounts.go @@ -22,6 +22,8 @@ import ( "bytes" "fmt" + "github.com/onflow/flow-cli/internal/command" + "github.com/onflow/cadence" "github.com/onflow/flow-go-sdk" "github.com/spf13/cobra" @@ -48,6 +50,7 @@ func init() { type AccountResult struct { *flow.Account showCode bool + include []string } // JSON convert result to JSON @@ -97,24 +100,32 @@ func (r *AccountResult) String() string { _, _ = fmt.Fprintf(writer, "\tSignature Algorithm\t %s\n", key.SigAlgo) _, _ = fmt.Fprintf(writer, "\tHash Algorithm\t %s\n", key.HashAlgo) _, _ = fmt.Fprintf(writer, "\tRevoked \t %t\n", key.Revoked) - fmt.Fprintf(writer, "\tSequence Number \t %d\n", key.SequenceNumber) - fmt.Fprintf(writer, "\tIndex \t %d\n", key.Index) - fmt.Fprintf(writer, "\n") + _, _ = fmt.Fprintf(writer, "\tSequence Number \t %d\n", key.SequenceNumber) + _, _ = fmt.Fprintf(writer, "\tIndex \t %d\n", key.Index) + _, _ = fmt.Fprintf(writer, "\n") + + // only show up to 3 keys and then show label to expand more info + if i == 3 && !command.ContainsFlag(r.include, "keys") { + _, _ = fmt.Fprint(writer, "...keys minimized, use --include keys flag if you want to view all\n\n") + break + } } - fmt.Fprintf(writer, "Contracts Deployed: %d\n", len(r.Contracts)) + _, _ = fmt.Fprintf(writer, "Contracts Deployed: %d\n", len(r.Contracts)) for name := range r.Contracts { - fmt.Fprintf(writer, "Contract: '%s'\n", name) + _, _ = fmt.Fprintf(writer, "Contract: '%s'\n", name) } - if r.showCode { + if r.showCode || command.ContainsFlag(r.include, "contracts") { for name, code := range r.Contracts { - fmt.Fprintf(writer, "Contracts '%s':\n", name) - fmt.Fprintln(writer, string(code)) + _, _ = fmt.Fprintf(writer, "Contracts '%s':\n", name) + _, _ = fmt.Fprintln(writer, string(code)) } + } else { + _, _ = fmt.Fprint(writer, "\n\nContracts (hidden, use --include contracts)") } - writer.Flush() + _ = writer.Flush() return b.String() } diff --git a/internal/accounts/contract-add.go b/internal/accounts/contract-add.go index 19643c5f8..34f9006b7 100644 --- a/internal/accounts/contract-add.go +++ b/internal/accounts/contract-add.go @@ -28,8 +28,9 @@ import ( ) type flagsAddContract struct { - Signer string `default:"emulator-account" flag:"signer" info:"Account name from configuration used to sign the transaction"` - Results bool `default:"false" flag:"results" info:"โš ๏ธ Deprecated: results are provided by default"` + Signer string `default:"emulator-account" flag:"signer" info:"Account name from configuration used to sign the transaction"` + Results bool `default:"false" flag:"results" info:"โš ๏ธ Deprecated: results are provided by default"` + Include []string `default:"" flag:"include" info:"Fields to include in the output"` } var addContractFlags = flagsAddContract{} @@ -66,6 +67,7 @@ var AddContractCommand = &command.Command{ return &AccountResult{ Account: account, showCode: false, + include: addContractFlags.Include, }, nil }, } diff --git a/internal/accounts/contract-remove.go b/internal/accounts/contract-remove.go index e41787c44..9cc16c547 100644 --- a/internal/accounts/contract-remove.go +++ b/internal/accounts/contract-remove.go @@ -28,8 +28,9 @@ import ( ) type flagsRemoveContract struct { - Signer string `default:"emulator-account" flag:"signer" info:"Account name from configuration used to sign the transaction"` - Results bool `default:"false" flag:"results" info:"โš ๏ธ Deprecated: results are provided by default"` + Signer string `default:"emulator-account" flag:"signer" info:"Account name from configuration used to sign the transaction"` + Results bool `default:"false" flag:"results" info:"โš ๏ธ Deprecated: results are provided by default"` + Include []string `default:"" flag:"include" info:"Fields to include in the output"` } var flagsRemove = flagsRemoveContract{} @@ -63,6 +64,7 @@ var RemoveCommand = &command.Command{ return &AccountResult{ Account: account, showCode: false, + include: flagsRemove.Include, }, nil }, } diff --git a/internal/accounts/contract-update.go b/internal/accounts/contract-update.go index a411ef952..9e23337f0 100644 --- a/internal/accounts/contract-update.go +++ b/internal/accounts/contract-update.go @@ -28,8 +28,9 @@ import ( ) type flagsUpdateContract struct { - Signer string `default:"emulator-account" flag:"signer" info:"Account name from configuration used to sign the transaction"` - Results bool `default:"false" flag:"results" info:"โš ๏ธ Deprecated: results are provided by default"` + Signer string `default:"emulator-account" flag:"signer" info:"Account name from configuration used to sign the transaction"` + Results bool `default:"false" flag:"results" info:"โš ๏ธ Deprecated: results are provided by default"` + Include []string `default:"" flag:"include" info:"Fields to include in the output"` } var updateFlags = flagsUpdateContract{} @@ -65,6 +66,7 @@ var UpdateCommand = &command.Command{ return &AccountResult{ Account: account, showCode: false, + include: updateFlags.Include, }, nil }, } diff --git a/internal/accounts/create.go b/internal/accounts/create.go index feb5ecfb7..27e5a3d7c 100644 --- a/internal/accounts/create.go +++ b/internal/accounts/create.go @@ -35,6 +35,7 @@ type flagsCreate struct { HashAlgo string `default:"SHA3_256" flag:"hash-algo" info:"Hash used for the digest"` Contracts []string `flag:"contract" info:"Contract to be deployed during account creation. "` Results bool `default:"false" flag:"results" info:"โš ๏ธ Deprecated: results are provided by default"` + Include []string `default:"" flag:"include" info:"Fields to include in the output"` } var createFlags = flagsCreate{} @@ -71,6 +72,7 @@ var CreateCommand = &command.Command{ return &AccountResult{ Account: account, + include: createFlags.Include, }, nil }, } diff --git a/internal/accounts/get.go b/internal/accounts/get.go index 26433c52a..4c2cac4a8 100644 --- a/internal/accounts/get.go +++ b/internal/accounts/get.go @@ -28,8 +28,9 @@ import ( ) type flagsGet struct { - Contracts bool `default:"false" flag:"contracts" info:"Display contracts deployed to the account"` - Code bool `default:"false" flag:"code" info:"โš ๏ธ Deprecated: use contracts flag instead"` + Contracts bool `default:"false" flag:"contracts" info:"โš ๏ธ Deprecated: use include flag instead"` + Code bool `default:"false" flag:"code" info:"โš ๏ธ Deprecated: use contracts flag instead"` + Include []string `default:"" flag:"include" info:"Fields to include in the output"` } var getFlags = flagsGet{} @@ -52,6 +53,10 @@ var GetCommand = &command.Command{ fmt.Println("โš ๏ธ DEPRECATION WARNING: use contracts flag instead") } + if getFlags.Contracts { + fmt.Println("โš ๏ธ DEPRECATION WARNING: use include contracts flag instead") + } + account, err := services.Accounts.Get(args[0]) // address if err != nil { return nil, err @@ -60,6 +65,7 @@ var GetCommand = &command.Command{ return &AccountResult{ Account: account, showCode: getFlags.Contracts || getFlags.Code, + include: getFlags.Include, }, nil }, } diff --git a/internal/blocks/blocks.go b/internal/blocks/blocks.go index b86a88cc7..d0e51733c 100644 --- a/internal/blocks/blocks.go +++ b/internal/blocks/blocks.go @@ -22,6 +22,8 @@ import ( "bytes" "fmt" + "github.com/onflow/flow-cli/internal/command" + "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/client" "github.com/spf13/cobra" @@ -46,6 +48,7 @@ type BlockResult struct { events []client.BlockEvents collections []*flow.Collection verbose bool + included []string } // JSON convert result to JSON @@ -94,7 +97,7 @@ func (r *BlockResult) String() string { for i, guarantee := range r.block.CollectionGuarantees { _, _ = fmt.Fprintf(writer, " Collection %d:\t%s\n", i, guarantee.CollectionID) - if r.verbose { + if r.verbose || command.ContainsFlag(r.included, "transactions") { for x, tx := range r.collections[i].TransactionIDs { _, _ = fmt.Fprintf(writer, " Transaction %d: %s\n", x, tx) } @@ -108,7 +111,7 @@ func (r *BlockResult) String() string { _, _ = fmt.Fprintf(writer, "%s", e.String()) } - writer.Flush() + _ = writer.Flush() return b.String() } diff --git a/internal/blocks/get.go b/internal/blocks/get.go index f11e99575..2a58691c5 100644 --- a/internal/blocks/get.go +++ b/internal/blocks/get.go @@ -28,11 +28,12 @@ import ( ) type flagsBlocks struct { - Events string `default:"" flag:"events" info:"List events of this type for the block"` - Verbose bool `default:"false" flag:"verbose" info:"Display transactions in block"` - Latest bool `default:"false" flag:"latest" info:"โš ๏ธ No longer supported: use command argument"` - BlockID string `default:"" flag:"id" info:"โš ๏ธ No longer supported: use command argument"` - BlockHeight uint64 `default:"0" flag:"height" info:"โš ๏ธ No longer supported: use command argument"` + Events string `default:"" flag:"events" info:"List events of this type for the block"` + Include []string `default:"" flag:"include" info:"Fields to include in the output"` + Verbose bool `default:"false" flag:"verbose" info:"โš ๏ธ Deprecated: use include transactions flag instead"` + Latest bool `default:"false" flag:"latest" info:"โš ๏ธ No longer supported: use command argument"` + BlockID string `default:"" flag:"id" info:"โš ๏ธ No longer supported: use command argument"` + BlockHeight uint64 `default:"0" flag:"height" info:"โš ๏ธ No longer supported: use command argument"` } var blockFlags = flagsBlocks{} @@ -55,6 +56,10 @@ var GetCommand = &command.Command{ return nil, fmt.Errorf("โš ๏ธ No longer supported: use command argument.") } + if blockFlags.Verbose { + fmt.Println("โš ๏ธ DEPRECATION WARNING: use include transactions flag instead") + } + block, events, collections, err := services.Blocks.GetBlock( args[0], // block id blockFlags.Events, diff --git a/internal/command/command.go b/internal/command/command.go index ea8ff954c..e6d3b5b06 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -301,7 +301,7 @@ func outputResult(result string, saveFlag string, formatFlag string, filterFlag Fs: afero.NewOsFs(), } - fmt.Printf("๐Ÿ’พ result saved to: %s \n", saveFlag) + fmt.Printf("%s result saved to: %s \n", output.SaveEmoji(), saveFlag) return af.WriteFile(saveFlag, []byte(result), 0644) } @@ -331,7 +331,10 @@ func filterResultValue(result Result, filter string) (interface{}, error) { possibleFilters = append(possibleFilters, key) } - value := jsonResult[strings.ToLower(filter)] + value := jsonResult[filter] + if value == nil { + value = jsonResult[strings.ToLower(filter)] + } if value == nil { return nil, fmt.Errorf("value for filter: '%s' doesn't exists, possible values to filter by: %s", filter, possibleFilters) @@ -350,32 +353,32 @@ func handleError(description string, err error) { // handle rpc error switch t := err.(type) { case *client.RPCError: - _, _ = fmt.Fprintf(os.Stderr, "โŒ Grpc Error: %s \n", t.GRPCStatus().Err().Error()) + _, _ = fmt.Fprintf(os.Stderr, "%s Grpc Error: %s \n", output.ErrorEmoji(), t.GRPCStatus().Err().Error()) default: if errors.Is(err, config.ErrOutdatedFormat) { - _, _ = fmt.Fprintf(os.Stderr, "โŒ Config Error: %s \n", err.Error()) - _, _ = fmt.Fprintf(os.Stderr, "๐Ÿ™ Please reset configuration using: 'flow init --reset'. Read more about new configuration here: https://github.com/onflow/flow-cli/releases/tag/v0.17.0") + _, _ = fmt.Fprintf(os.Stderr, "%s Config Error: %s \n", output.ErrorEmoji(), err.Error()) + _, _ = fmt.Fprintf(os.Stderr, "%s Please reset configuration using: 'flow init --reset'. Read more about new configuration here: https://github.com/onflow/flow-cli/releases/tag/v0.17.0", output.TryEmoji()) } else if strings.Contains(err.Error(), "transport:") { - _, _ = fmt.Fprintf(os.Stderr, "โŒ %s \n", strings.Split(err.Error(), "transport:")[1]) - _, _ = fmt.Fprintf(os.Stderr, "๐Ÿ™ Make sure your emulator is running or connection address is correct.") + _, _ = fmt.Fprintf(os.Stderr, "%s %s \n", output.ErrorEmoji(), strings.Split(err.Error(), "transport:")[1]) + _, _ = fmt.Fprintf(os.Stderr, "%s Make sure your emulator is running or connection address is correct.", output.TryEmoji()) } else if strings.Contains(err.Error(), "NotFound desc =") { - _, _ = fmt.Fprintf(os.Stderr, "โŒ Not Found:%s \n", strings.Split(err.Error(), "NotFound desc =")[1]) + _, _ = fmt.Fprintf(os.Stderr, "%s Not Found:%s \n", output.ErrorEmoji(), strings.Split(err.Error(), "NotFound desc =")[1]) } else if strings.Contains(err.Error(), "code = InvalidArgument desc = ") { desc := strings.Split(err.Error(), "code = InvalidArgument desc = ") - _, _ = fmt.Fprintf(os.Stderr, "โŒ Invalid argument: %s \n", desc[len(desc)-1]) + _, _ = fmt.Fprintf(os.Stderr, "%s Invalid argument: %s \n", output.ErrorEmoji(), desc[len(desc)-1]) if strings.Contains(err.Error(), "is invalid for chain") { - _, _ = fmt.Fprintf(os.Stderr, "๐Ÿ™ Check you are connecting to the correct network or account address you use is correct.") + _, _ = fmt.Fprintf(os.Stderr, "%s Check you are connecting to the correct network or account address you use is correct.", output.TryEmoji()) } else { - _, _ = fmt.Fprintf(os.Stderr, "๐Ÿ™ Check your argument and flags value, you can use --help.") + _, _ = fmt.Fprintf(os.Stderr, "%s Check your argument and flags value, you can use --help.", output.TryEmoji()) } } else if strings.Contains(err.Error(), "invalid signature:") { - _, _ = fmt.Fprintf(os.Stderr, "โŒ Invalid signature: %s \n", strings.Split(err.Error(), "invalid signature:")[1]) - _, _ = fmt.Fprintf(os.Stderr, "๐Ÿ™ Check the signer private key is provided or is in the correct format. If running emulator, make sure it's using the same configuration as this command.") + _, _ = fmt.Fprintf(os.Stderr, "%s Invalid signature: %s \n", output.ErrorEmoji(), strings.Split(err.Error(), "invalid signature:")[1]) + _, _ = fmt.Fprintf(os.Stderr, "%s Check the signer private key is provided or is in the correct format. If running emulator, make sure it's using the same configuration as this command.", output.TryEmoji()) } else if strings.Contains(err.Error(), "signature could not be verified using public key with") { - _, _ = fmt.Fprintf(os.Stderr, "โŒ %s: %s \n", description, err) - _, _ = fmt.Fprintf(os.Stderr, "๐Ÿ™ If you are running emulator locally make sure that the emulator was started with the same config as used in this command. \nTry restarting the emulator.") + _, _ = fmt.Fprintf(os.Stderr, "%s %s: %s \n", output.ErrorEmoji(), description, err) + _, _ = fmt.Fprintf(os.Stderr, "%s If you are running emulator locally make sure that the emulator was started with the same config as used in this command. \nTry restarting the emulator.", output.TryEmoji()) } else { - _, _ = fmt.Fprintf(os.Stderr, "โŒ %s: %s", description, err) + _, _ = fmt.Fprintf(os.Stderr, "%s %s: %s", output.ErrorEmoji(), description, err) } } @@ -394,10 +397,16 @@ func checkVersion(logger output.Logger) { body, _ := ioutil.ReadAll(resp.Body) latestVersion := strings.TrimSpace(string(body)) - if latestVersion != build.Semver() { + currentVersion := build.Semver() + if currentVersion == "undefined" { + return // avoid warning in local development + } + + if currentVersion != latestVersion { logger.Info(fmt.Sprintf( - "\nโš ๏ธ Version warning: a new version of Flow CLI is available (%s).\n"+ - "Read the installation guide for upgrade instructions: https://docs.onflow.org/flow-cli/install", + "\n%s Version warning: a new version of Flow CLI is available (%s).\n"+ + " Read the installation guide for upgrade instructions: https://docs.onflow.org/flow-cli/install\n", + output.WarningEmoji(), strings.ReplaceAll(string(latestVersion), "\n", ""), )) } diff --git a/internal/command/result.go b/internal/command/result.go index 994e7d462..13723dd40 100644 --- a/internal/command/result.go +++ b/internal/command/result.go @@ -18,8 +18,23 @@ package command +import ( + "strings" +) + type Result interface { String() string Oneliner() string JSON() interface{} } + +// ContainsFlag checks if output flag is present for the provided field +func ContainsFlag(flags []string, field string) bool { + for _, n := range flags { + if strings.ToLower(n) == field { + return true + } + } + + return false +} diff --git a/internal/config/config.go b/internal/config/config.go index 97be054e5..7f29371ff 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -29,6 +29,7 @@ var Cmd = &cobra.Command{ } func init() { + InitCommand.AddToParent(Cmd) Cmd.AddCommand(AddCmd) Cmd.AddCommand(RemoveCmd) } diff --git a/internal/config/init.go b/internal/config/init.go index 0937c0559..990e1f241 100644 --- a/internal/config/init.go +++ b/internal/config/init.go @@ -22,6 +22,8 @@ import ( "bytes" "fmt" + "github.com/onflow/flow-cli/pkg/flowcli/output" + "github.com/spf13/cobra" "github.com/onflow/flow-cli/internal/command" @@ -84,11 +86,11 @@ func (r *InitResult) String() string { account, _ := r.Project.EmulatorServiceAccount() _, _ = fmt.Fprintf(writer, "Configuration initialized\n") - _, _ = fmt.Fprintf(writer, "Service account: %s\n\n", util.Bold("0x"+account.Address().String())) + _, _ = fmt.Fprintf(writer, "Service account: %s\n\n", output.Bold("0x"+account.Address().String())) _, _ = fmt.Fprintf(writer, "Start emulator by running: %s \nReset configuration using: %s\n", - util.Bold("'flow emulator'"), - util.Bold("'flow init --reset'"), + output.Bold("'flow emulator'"), + output.Bold("'flow init --reset'"), ) writer.Flush() diff --git a/internal/keys/decode.go b/internal/keys/decode.go index 3c1b07dd7..cf6c34e80 100644 --- a/internal/keys/decode.go +++ b/internal/keys/decode.go @@ -31,10 +31,10 @@ var decodeFlags = flagsDecode{} var DecodeCommand = &command.Command{ Cmd: &cobra.Command{ - Use: "decode ", - Short: "Decode a public account key hex string", + Use: "decode ", + Short: "Decode a rlp encoded account key", Args: cobra.ExactArgs(1), - Example: "flow keys decode 4a22246...31bce1e71a7b6d11", + Example: "flow keys decode f847b8408...2402038203e8", }, Flags: &decodeFlags, Run: func( @@ -43,9 +43,9 @@ var DecodeCommand = &command.Command{ globalFlags command.GlobalFlags, services *services.Services, ) (command.Result, error) { - accountKey, err := services.Keys.Decode( - args[0], // public key - ) + rlpEncoded := args[0] + + accountKey, err := services.Keys.Decode(rlpEncoded) if err != nil { return nil, err } diff --git a/internal/keys/keys.go b/internal/keys/keys.go index 69b602881..7a779f231 100644 --- a/internal/keys/keys.go +++ b/internal/keys/keys.go @@ -23,6 +23,8 @@ import ( "encoding/hex" "fmt" + "github.com/onflow/flow-cli/pkg/flowcli/output" + "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/crypto" "github.com/spf13/cobra" @@ -38,6 +40,7 @@ var Cmd = &cobra.Command{ func init() { GenerateCommand.AddToParent(Cmd) + DecodeCommand.AddToParent(Cmd) } // KeyResult represent result from all account commands @@ -65,7 +68,7 @@ func (k *KeyResult) String() string { writer := util.CreateTabWriter(&b) if k.privateKey != nil { - _, _ = fmt.Fprintf(writer, "๐Ÿ”ด๏ธ Store private key safely and don't share with anyone! \n") + _, _ = fmt.Fprintf(writer, "%s Store private key safely and don't share with anyone! \n", output.StopEmoji()) _, _ = fmt.Fprintf(writer, "Private Key \t %x \n", k.privateKey.Encode()) } diff --git a/internal/project/project.go b/internal/project/project.go index 1d766474f..ed25a8404 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -29,7 +29,6 @@ var Cmd = &cobra.Command{ } func init() { - InitCommand.AddToParent(Cmd) DeployCommand.AddToParent(Cmd) Cmd.AddCommand(EmulatorCommand) } diff --git a/internal/project/init.go b/internal/quick/init.go similarity index 92% rename from internal/project/init.go rename to internal/quick/init.go index a25d1b1aa..19b4fb90b 100644 --- a/internal/project/init.go +++ b/internal/quick/init.go @@ -16,7 +16,7 @@ * limitations under the License. */ -package project +package quick import ( "fmt" @@ -28,6 +28,8 @@ import ( "github.com/onflow/flow-cli/pkg/flowcli/services" ) +// TODO(sideninja) workaround - init needed to be copied in order to work else there is flag duplicate error + var initFlag = config.FlagsInit{} var InitCommand = &command.Command{ diff --git a/internal/status/status.go b/internal/status/status.go index 5b1a33ea0..ee1a45095 100644 --- a/internal/status/status.go +++ b/internal/status/status.go @@ -22,6 +22,8 @@ import ( "bytes" "fmt" + "github.com/onflow/flow-cli/pkg/flowcli/output" + "github.com/spf13/cobra" "github.com/onflow/flow-cli/internal/command" @@ -62,39 +64,31 @@ type Result struct { err error } -const ( - OnlineIcon = "๐ŸŸข" - OnlineStatus = "ONLINE" - - OfflineIcon = "๐Ÿ”ด" - OfflineStatus = "OFFLINE" -) - // getStatus returns string representation for Flow network status. func (r *Result) getStatus() string { if r.err == nil { - return OnlineStatus + return "ONLINE" } - return OfflineStatus + return "OFFLINE" } // getColoredStatus returns colored string representation for Flow network status. func (r *Result) getColoredStatus() string { if r.err == nil { - return util.Green(OnlineStatus) + return output.Green(r.getStatus()) } - return util.Red(OfflineStatus) + return output.Red(output.Red(r.getStatus())) } // getIcon returns emoji icon representing Flow network status. func (r *Result) getIcon() string { if r.err == nil { - return OnlineIcon + return output.GoEmoji() } - return OfflineIcon + return output.StopEmoji() } // String converts result to a string. diff --git a/internal/transactions/build.go b/internal/transactions/build.go index 0eceefbc5..4123e0d2c 100644 --- a/internal/transactions/build.go +++ b/internal/transactions/build.go @@ -69,7 +69,8 @@ var BuildCommand = &command.Command{ } return &TransactionResult{ - tx: build.FlowTransaction(), + tx: build.FlowTransaction(), + include: []string{"code", "payload", "signatures"}, }, nil }, } diff --git a/internal/transactions/get.go b/internal/transactions/get.go index 60dc5b4f9..ad44855ca 100644 --- a/internal/transactions/get.go +++ b/internal/transactions/get.go @@ -28,8 +28,10 @@ import ( ) type flagsGet struct { - Sealed bool `default:"true" flag:"sealed" info:"Wait for a sealed result"` - Code bool `default:"false" flag:"code" info:"Display transaction code"` + Sealed bool `default:"true" flag:"sealed" info:"Wait for a sealed result"` + Code bool `default:"false" flag:"code" info:"โš ๏ธ Deprecated: use include flag"` + Include []string `default:"" flag:"include" info:"Fields to include in the output"` + Exclude []string `default:"" flag:"exclude" info:"Fields to exclude from the output (events)"` } var getFlags = flagsGet{} @@ -53,6 +55,10 @@ var GetCommand = &command.Command{ fmt.Println("โš ๏ธ DEPRECATION WARNING: use \"flow transactions get\" instead") } + if getFlags.Code { + fmt.Println("โš ๏ธ DEPRECATION WARNING: use include flag instead") + } + tx, result, err := services.Transactions.GetStatus( args[0], // transaction id getFlags.Sealed, @@ -62,9 +68,11 @@ var GetCommand = &command.Command{ } return &TransactionResult{ - result: result, - tx: tx, - code: getFlags.Code, + result: result, + tx: tx, + code: getFlags.Code, + include: getFlags.Include, + exclude: getFlags.Exclude, }, nil }, } diff --git a/internal/transactions/send-signed.go b/internal/transactions/send-signed.go index 0dbac74df..61de3e88f 100644 --- a/internal/transactions/send-signed.go +++ b/internal/transactions/send-signed.go @@ -26,6 +26,8 @@ import ( ) type flagsSendSigned struct { + Include []string `default:"" flag:"include" info:"Fields to include in the output"` + Exclude []string `default:"" flag:"exclude" info:"Fields to exclude from the output (events)"` } var sendSignedFlags = flagsSendSigned{} @@ -53,8 +55,10 @@ var SendSignedCommand = &command.Command{ } return &TransactionResult{ - result: result, - tx: tx, + result: result, + tx: tx, + include: sendSignedFlags.Include, + exclude: sendSignedFlags.Exclude, }, nil }, } diff --git a/internal/transactions/send.go b/internal/transactions/send.go index 5b861229f..1dfc07be2 100644 --- a/internal/transactions/send.go +++ b/internal/transactions/send.go @@ -32,6 +32,8 @@ type flagsSend struct { Arg []string `default:"" flag:"arg" info:"argument in Type:Value format"` Signer string `default:"emulator-account" flag:"signer" info:"Account name from configuration used to sign the transaction"` GasLimit uint64 `default:"1000" flag:"gas-limit" info:"transaction gas limit"` + Include []string `default:"" flag:"include" info:"Fields to include in the output"` + Exclude []string `default:"" flag:"exclude" info:"Fields to exclude from the output (events)"` Code string `default:"" flag:"code" info:"โš ๏ธ Deprecated: use filename argument"` Results bool `default:"" flag:"results" info:"โš ๏ธ Deprecated: all transactions will provide result"` Args string `default:"" flag:"args" info:"โš ๏ธ Deprecated: use arg or args-json flag"` @@ -86,8 +88,10 @@ var SendCommand = &command.Command{ } return &TransactionResult{ - result: result, - tx: tx, + result: result, + tx: tx, + include: sendFlags.Include, + exclude: sendFlags.Exclude, }, nil }, } diff --git a/internal/transactions/transactions.go b/internal/transactions/transactions.go index d19575fc5..e1d862b49 100644 --- a/internal/transactions/transactions.go +++ b/internal/transactions/transactions.go @@ -23,6 +23,10 @@ import ( "encoding/json" "fmt" + "github.com/onflow/flow-cli/pkg/flowcli/output" + + "github.com/onflow/flow-cli/internal/command" + jsoncdc "github.com/onflow/cadence/encoding/json" "github.com/onflow/flow-go-sdk" "github.com/spf13/cobra" @@ -47,9 +51,11 @@ func init() { // TransactionResult represent result from all account commands type TransactionResult struct { - result *flow.TransactionResult - tx *flow.Transaction - code bool + result *flow.TransactionResult + tx *flow.Transaction + code bool + include []string + exclude []string } // JSON convert result to JSON @@ -90,12 +96,12 @@ func (r *TransactionResult) String() string { if r.result != nil { if r.result.Error != nil { - _, _ = fmt.Fprintf(writer, "โŒ Transaction Error \n%s\n\n\n", r.result.Error.Error()) + _, _ = fmt.Fprintf(writer, "%s Transaction Error \n%s\n\n\n", output.ErrorEmoji(), r.result.Error.Error()) } statusBadge := "" if r.result.Status == flow.TransactionStatusSealed { - statusBadge = "โœ…" + statusBadge = output.OkEmoji() } _, _ = fmt.Fprintf(writer, "Status\t%s %s\n", statusBadge, r.result.Status) } @@ -118,20 +124,32 @@ func (r *TransactionResult) String() string { } for i, e := range r.tx.PayloadSignatures { - _, _ = fmt.Fprintf(writer, "\nPayload Signature %v:\n", i) - _, _ = fmt.Fprintf(writer, " Address\t%s\n", e.Address) - _, _ = fmt.Fprintf(writer, " Signature\t%x\n", e.Signature) - _, _ = fmt.Fprintf(writer, " Key Index\t%d\n", e.KeyIndex) + if command.ContainsFlag(r.include, "signatures") { + _, _ = fmt.Fprintf(writer, "\nPayload Signature %v:\n", i) + _, _ = fmt.Fprintf(writer, " Address\t%s\n", e.Address) + _, _ = fmt.Fprintf(writer, " Signature\t%x\n", e.Signature) + _, _ = fmt.Fprintf(writer, " Key Index\t%d\n", e.KeyIndex) + } else { + _, _ = fmt.Fprintf(writer, "\nPayload Signature %v: %s", i, e.Address) + } } for i, e := range r.tx.EnvelopeSignatures { - _, _ = fmt.Fprintf(writer, "\nEnvelope Signature %v:\n", i) - _, _ = fmt.Fprintf(writer, " Address\t%s\n", e.Address) - _, _ = fmt.Fprintf(writer, " Signature\t%x\n", e.Signature) - _, _ = fmt.Fprintf(writer, " Key Index\t%d\n", e.KeyIndex) + if command.ContainsFlag(r.include, "signatures") { + _, _ = fmt.Fprintf(writer, "\nEnvelope Signature %v:\n", i) + _, _ = fmt.Fprintf(writer, " Address\t%s\n", e.Address) + _, _ = fmt.Fprintf(writer, " Signature\t%x\n", e.Signature) + _, _ = fmt.Fprintf(writer, " Key Index\t%d\n", e.KeyIndex) + } else { + _, _ = fmt.Fprintf(writer, "\nEnvelope Signature %v: %s", i, e.Address) + } } - if r.result != nil { + if !command.ContainsFlag(r.include, "signatures") { + _, _ = fmt.Fprintf(writer, "\nSignatures (minimized, use --include signatures)") + } + + if r.result != nil && !command.ContainsFlag(r.exclude, "events") { e := events.EventResult{ Events: r.result.Events, } @@ -145,19 +163,27 @@ func (r *TransactionResult) String() string { } if r.tx.Script != nil { - if len(r.tx.Arguments) == 0 { - _, _ = fmt.Fprintf(writer, "\n\nArguments\tNo arguments\n") - } else { - _, _ = fmt.Fprintf(writer, "\n\nArguments (%d):\n", len(r.tx.Arguments)) - for i, argument := range r.tx.Arguments { - _, _ = fmt.Fprintf(writer, " - Argument %d: %s\n", i, argument) + if command.ContainsFlag(r.include, "code") || r.code { + if len(r.tx.Arguments) == 0 { + _, _ = fmt.Fprintf(writer, "\n\nArguments\tNo arguments\n") + } else { + _, _ = fmt.Fprintf(writer, "\n\nArguments (%d):\n", len(r.tx.Arguments)) + for i, argument := range r.tx.Arguments { + _, _ = fmt.Fprintf(writer, " - Argument %d: %s\n", i, argument) + } } - } - _, _ = fmt.Fprintf(writer, "\nCode\n\n%s\n", r.tx.Script) + _, _ = fmt.Fprintf(writer, "\nCode\n\n%s\n", r.tx.Script) + } else { + _, _ = fmt.Fprint(writer, "\n\nCode (hidden, use --include code)") + } } - _, _ = fmt.Fprintf(writer, "\n\nPayload:\n%x", r.tx.Encode()) + if command.ContainsFlag(r.include, "payload") { + _, _ = fmt.Fprintf(writer, "\n\nPayload:\n%x", r.tx.Encode()) + } else { + _, _ = fmt.Fprint(writer, "\n\nPayload (hidden, use --include payload)") + } _ = writer.Flush() return b.String() diff --git a/logo-cli.svg b/logo-cli.svg new file mode 100644 index 000000000..d1c389e11 --- /dev/null +++ b/logo-cli.svg @@ -0,0 +1,26 @@ + + + Group 3 + + + + + + + CLI + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pkg/flowcli/config/json/account.go b/pkg/flowcli/config/json/account.go index 02b66205c..dda32c33c 100644 --- a/pkg/flowcli/config/json/account.go +++ b/pkg/flowcli/config/json/account.go @@ -139,6 +139,7 @@ func transformAccountsToJSON(accounts config.Accounts) jsonAccounts { return jsonAccounts } +// TODO(sideninja) refactor keys to use json.RawMessage and decide if unmarshal into simple or advance structure based on that field type jsonAccountSimple struct { Address string `json:"address"` Keys string `json:"keys"` diff --git a/pkg/flowcli/contracts/resolver.go b/pkg/flowcli/contracts/resolver.go index 3963e6114..42d74db4d 100644 --- a/pkg/flowcli/contracts/resolver.go +++ b/pkg/flowcli/contracts/resolver.go @@ -112,8 +112,9 @@ func (r *Resolver) getFileImports() []string { imports := make([]string, 0) for _, importDeclaration := range r.program.ImportDeclarations() { - _, ok := importDeclaration.Location.(common.AddressLocation) - if !ok { + _, isFileImport := importDeclaration.Location.(common.StringLocation) + + if isFileImport { imports = append(imports, importDeclaration.Location.String()) } } diff --git a/pkg/flowcli/contracts/resolver_test.go b/pkg/flowcli/contracts/resolver_test.go index d769d5721..2cc671573 100644 --- a/pkg/flowcli/contracts/resolver_test.go +++ b/pkg/flowcli/contracts/resolver_test.go @@ -53,22 +53,28 @@ func TestResolver(t *testing.T) { "./tests/foo.cdc", "./scripts/bar/foo.cdc", "./scripts/bar/foo.cdc", + "./tests/foo.cdc", } scripts := [][]byte{ []byte(` import Kibble from "./Kibble.cdc" - import FT from "./FT.cdc" + import FT from "./FT.cdc" pub fun main() {} `), []byte(` import Kibble from "../../tests/Kibble.cdc" - import FT from "../../tests/FT.cdc" + import FT from "../../tests/FT.cdc" pub fun main() {} `), []byte(` import Kibble from "../../tests/Kibble.cdc" - import NFT from "../../tests/NFT.cdc" + import NFT from "../../tests/NFT.cdc" + pub fun main() {} + `), []byte(` + import Kibble from "./Kibble.cdc" + import crypto + import Foo from 0x0000000000000001 pub fun main() {} - `), + `), } resolved := [][]byte{ @@ -84,38 +90,43 @@ func TestResolver(t *testing.T) { import Kibble from 0x0000000000000001 import NFT from 0x0000000000000004 pub fun main() {} - `), + `), []byte(` + import Kibble from 0x0000000000000001 + import crypto + import Foo from 0x0000000000000001 + pub fun main() {} + `), } t.Run("Import exists", func(t *testing.T) { resolver, err := NewResolver([]byte(` - import Kibble from "./Kibble.cdc" - pub fun main() {} - `)) + import Kibble from "./Kibble.cdc" + pub fun main() {} + `)) assert.NoError(t, err) assert.True(t, resolver.HasFileImports()) }) t.Run("Import doesn't exists", func(t *testing.T) { resolver, err := NewResolver([]byte(` - pub fun main() {} - `)) + pub fun main() {} + `)) assert.NoError(t, err) assert.False(t, resolver.HasFileImports()) resolver, err = NewResolver([]byte(` import Foo from 0xf8d6e0586b0a20c7 - pub fun main() {} - `)) + pub fun main() {} + `)) assert.NoError(t, err) assert.False(t, resolver.HasFileImports()) }) t.Run("Parse imports", func(t *testing.T) { - resolver, err := NewResolver(scripts[0]) + resolver, err := NewResolver(scripts[3]) assert.NoError(t, err) assert.Equal(t, resolver.getFileImports(), []string{ - "./Kibble.cdc", "./FT.cdc", + "./Kibble.cdc", }) }) diff --git a/pkg/flowcli/output/colors.go b/pkg/flowcli/output/colors.go new file mode 100644 index 000000000..8eeffe4fa --- /dev/null +++ b/pkg/flowcli/output/colors.go @@ -0,0 +1,49 @@ +/* + * Flow CLI + * + * Copyright 2019-2021 Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package output + +import ( + "fmt" + "runtime" +) + +var red = "\033[31m" +var green = "\033[32m" +var bold = "\033[1m" +var reset = "\033[0m" + +func printColor(msg string, color string) string { + if runtime.GOOS == "windows" { + return msg + } + + return fmt.Sprintf("%s%s%s", color, msg, reset) +} + +func Red(msg string) string { + return printColor(msg, red) +} + +func Green(msg string) string { + return printColor(msg, green) +} + +func Bold(msg string) string { + return printColor(msg, bold) +} diff --git a/pkg/flowcli/output/emoji.go b/pkg/flowcli/output/emoji.go new file mode 100644 index 000000000..5a760f8b6 --- /dev/null +++ b/pkg/flowcli/output/emoji.go @@ -0,0 +1,61 @@ +/* + * Flow CLI + * + * Copyright 2019-2021 Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package output + +import "runtime" + +func printEmoji(emoji string) string { + if runtime.GOOS == "windows" { + return "" + } + + return emoji +} + +func ErrorEmoji() string { + return printEmoji("โŒ") +} + +func TryEmoji() string { + return printEmoji("๐Ÿ™") +} + +func WarningEmoji() string { + return printEmoji("โš ๏ธ") +} + +func SaveEmoji() string { + return printEmoji("๐Ÿ’พ") +} + +func StopEmoji() string { + return printEmoji("๐Ÿ”ด๏ธ") +} + +func GoEmoji() string { + return printEmoji("๐ŸŸข") +} + +func OkEmoji() string { + return printEmoji("โœ…") +} + +func SuccessEmoji() string { + return printEmoji("โœจ") +} diff --git a/pkg/flowcli/output/logger.go b/pkg/flowcli/output/logger.go index cba9a6c73..3cb717637 100644 --- a/pkg/flowcli/output/logger.go +++ b/pkg/flowcli/output/logger.go @@ -20,8 +20,6 @@ package output import ( "fmt" - - "github.com/onflow/flow-cli/pkg/flowcli/util" ) const ( @@ -69,7 +67,7 @@ func (s *StdoutLogger) Debug(msg string) { } func (s *StdoutLogger) Error(msg string) { - s.log(fmt.Sprintf("โŒ %s", util.Red(msg)), ErrorLog) + s.log(fmt.Sprintf("%s %s", ErrorEmoji(), Red(msg)), ErrorLog) } func (s *StdoutLogger) StartProgress(msg string) { diff --git a/pkg/flowcli/project/project.go b/pkg/flowcli/project/project.go index e153974f7..e89d43146 100644 --- a/pkg/flowcli/project/project.go +++ b/pkg/flowcli/project/project.go @@ -219,7 +219,7 @@ func (p *Project) AccountNamesForNetwork(network string) []string { for _, account := range p.accounts { if len(p.conf.Deployments.GetByAccountAndNetwork(account.name, network)) > 0 { - if !util.StringContains(names, account.name) { + if !util.ContainsString(names, account.name) { names = append(names, account.name) } } diff --git a/pkg/flowcli/services/accounts.go b/pkg/flowcli/services/accounts.go index 52556a4d7..15c5f6aec 100644 --- a/pkg/flowcli/services/accounts.go +++ b/pkg/flowcli/services/accounts.go @@ -343,15 +343,17 @@ func (a *Accounts) addContract( if updateExisting { a.logger.Info(fmt.Sprintf( - "Contract '%s' updated on the account '%s'.", + "Contract '%s' updated on the account '%s'.\nTransaction ID: %s.", contractName, account.Address(), + sentTx.ID().String(), )) } else { a.logger.Info(fmt.Sprintf( - "Contract '%s' deployed to the account '%s'.", + "Contract '%s' deployed to the account '%s'.\nTransaction ID: %s.", contractName, account.Address(), + sentTx.ID().String(), )) } @@ -398,7 +400,12 @@ func (a *Accounts) RemoveContract( } a.logger.StopProgress() - a.logger.Info(fmt.Sprintf("Contract %s removed from account %s\n", contractName, account.Address())) + a.logger.Info(fmt.Sprintf( + "Contract %s removed from account %s.\nTransaction ID: %s.", + contractName, + account.Address(), + sentTx.ID().String(), + )) return a.gateway.GetAccount(account.Address()) } diff --git a/pkg/flowcli/services/keys_test.go b/pkg/flowcli/services/keys_test.go index 372792b60..09edaead0 100644 --- a/pkg/flowcli/services/keys_test.go +++ b/pkg/flowcli/services/keys_test.go @@ -62,4 +62,12 @@ func TestKeys(t *testing.T) { assert.Equal(t, err.Error(), "invalid signature algorithm: JUSTNO") }) + + t.Run("RLP decode keys", func(t *testing.T) { + dkey, err := keys.Decode("f847b84084d716c14b051ad6b001624f738f5d302636e6b07cc75e4530af7776a4368a2b586dbefc0564ee28384c2696f178cbed52e62811bcc9ecb59568c996d342db2402038203e8") + + assert.NoError(t, err) + assert.Equal(t, dkey.PublicKey.String(), "0x84d716c14b051ad6b001624f738f5d302636e6b07cc75e4530af7776a4368a2b586dbefc0564ee28384c2696f178cbed52e62811bcc9ecb59568c996d342db24") + assert.Equal(t, dkey.SigAlgo.String(), "ECDSA_P256") + }) } diff --git a/pkg/flowcli/services/project.go b/pkg/flowcli/services/project.go index 021df62dc..374434403 100644 --- a/pkg/flowcli/services/project.go +++ b/pkg/flowcli/services/project.go @@ -221,7 +221,7 @@ func (p *Project) Deploy(network string, update bool) ([]*contracts.Contract, er } p.logger.StartProgress( - fmt.Sprintf("%s deploying...", util.Bold(contract.Name())), + fmt.Sprintf("%s deploying...", output.Bold(contract.Name())), ) result, err := p.gateway.GetTransactionResult(sentTx, true) @@ -232,7 +232,7 @@ func (p *Project) Deploy(network string, update bool) ([]*contracts.Contract, er if result.Error == nil && !deployErr { p.logger.StopProgress() - fmt.Printf("%s -> 0x%s\n", util.Green(contract.Name()), contract.Target()) + fmt.Printf("%s -> 0x%s\n", output.Green(contract.Name()), contract.Target()) } else { p.logger.StopProgress() p.logger.Error( @@ -242,7 +242,7 @@ func (p *Project) Deploy(network string, update bool) ([]*contracts.Contract, er } if !deployErr { - p.logger.Info("\nโœจ All contracts deployed successfully") + p.logger.Info(fmt.Sprintf("\n%s All contracts deployed successfully", output.SuccessEmoji())) } else { err = fmt.Errorf("failed to deploy contracts") p.logger.Error(err.Error()) diff --git a/pkg/flowcli/util/utilities.go b/pkg/flowcli/util/utilities.go index 6831d5b49..f25d57099 100644 --- a/pkg/flowcli/util/utilities.go +++ b/pkg/flowcli/util/utilities.go @@ -24,16 +24,10 @@ import ( "io/ioutil" "text/tabwriter" - "github.com/fatih/color" - "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/crypto" ) -var Green = color.New(color.FgGreen, color.Bold).SprintfFunc() -var Red = color.New(color.FgRed, color.Bold).SprintfFunc() -var Bold = color.New(color.FgCyan).SprintfFunc() - // LoadFile loads a file by filename. func LoadFile(filename string) ([]byte, error) { var code []byte @@ -80,8 +74,8 @@ func ConvertSigAndHashAlgo( return sigAlgo, hashAlgo, nil } -// StringContains returns true if the slice contains the given string. -func StringContains(s []string, e string) bool { +// ContainsString returns true if the slice contains the given string. +func ContainsString(s []string, e string) bool { for _, a := range s { if a == e { return true @@ -111,15 +105,6 @@ func CreateTabWriter(b *bytes.Buffer) *tabwriter.Writer { return tabwriter.NewWriter(b, 0, 8, 1, '\t', tabwriter.AlignRight) } -func ContainsString(s []string, e string) bool { - for _, a := range s { - if a == e { - return true - } - } - return false -} - func ParseAddress(value string) (flow.Address, bool) { address := flow.HexToAddress(value)