diff --git a/eosc/cmd/common.go b/eosc/cmd/common.go index fb80e9f9..68773877 100644 --- a/eosc/cmd/common.go +++ b/eosc/cmd/common.go @@ -11,6 +11,8 @@ import ( "strings" "time" + "go.uber.org/zap" + yaml2json "github.com/bronze1man/go-yaml2json" "github.com/eoscanada/eos-go" "github.com/eoscanada/eos-go/ecc" @@ -18,6 +20,7 @@ import ( "github.com/eoscanada/eosc/cli" eosvault "github.com/eoscanada/eosc/vault" "github.com/spf13/viper" + "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) @@ -85,6 +88,58 @@ func getAPI() *eos.API { return api } +var coreSymbolIsCached bool +var coreSymbol eos.Symbol + +func getCoreSymbol() eos.Symbol { + if coreSymbolIsCached { + return coreSymbol + } + + // In the event of a failure, we do not want to re-perform an API call, + // so let's record the fact that getCoreSymbol is cached right here. + // The init core symbol will take care of setting an approriate core + // symbol from global flag and reporting the error. + coreSymbolIsCached = true + if err := initCoreSymbol(); err != nil { + coreSymbol = eos.EOSSymbol + zlog.Debug( + "unable to retrieve core symbol from API, falling back to default", + zap.Error(err), + zap.Stringer("default", coreSymbol), + ) + } + + return coreSymbol +} + +func initCoreSymbol() error { + resp, err := getAPI().GetTableRows(eos.GetTableRowsRequest{ + Code: "eosio", + Scope: "eosio", + Table: "rammarket", + JSON: true, + }) + + if err != nil { + return fmt.Errorf("unable to fetch table: %s", err) + } + + result := gjson.GetBytes(resp.Rows, "0.quote.balance") + if !result.Exists() { + return errors.New("table has not expected format") + } + + asset, err := eos.NewAsset(result.String()) + if !result.Exists() { + return fmt.Errorf("quote balance asset %q is not valid: %s", result.String(), err) + } + + zlog.Debug("Retrieved core symbol from API, using it as default core symbol", zap.Stringer("symbol", asset.Symbol)) + coreSymbol = asset.Symbol + return nil +} + func sanitizeAPIURL(input string) string { return strings.TrimRight(input, "/") } @@ -312,32 +367,39 @@ func loadYAMLOrJSONFile(filename string, v interface{}) error { func toAccount(in, field string) eos.AccountName { acct, err := cli.ToAccountName(in) - if err != nil { - errorCheck(fmt.Sprintf("invalid account format for %q", field), err) - } + errorCheck(fmt.Sprintf("invalid account format for %q", field), err) return acct } func toAsset(symbol eos.Symbol, in, field string) eos.Asset { - asset, err := eos.NewAssetFromString(symbol, in) - errorCheck(fmt.Sprintf("invalid %s asset for %q", symbol.String(), field), err) + asset, err := eos.NewFixedSymbolAssetFromString(symbol, in) + errorCheck(fmt.Sprintf("invalid %q value %q", field, in), err) return asset } -func toEOSAsset(in, field string) eos.Asset { - asset, err := eos.NewEOSAssetFromString(in) - errorCheck(fmt.Sprintf("invalid %s asset for %q", eos.EOSSymbol, field), err) +func toAssetWithDefaultCoreSymbol(in, field string) eos.Asset { + if len(strings.Split(in, " ")) == 1 { + return toCoreAsset(in, field) + } + + asset, err := eos.NewAssetFromString(in) + errorCheck(fmt.Sprintf("invalid asset value %q for %q", in, field), err) return asset } -func toREXAsset(in, field string) eos.Asset { - asset, err := eos.NewREXAssetFromString(in) - errorCheck(fmt.Sprintf("invalid %s asset for %q", eos.REXSymbol, field), err) +func toCoreAsset(in, field string) eos.Asset { + return toAsset(getCoreSymbol(), in, field) +} - return asset +func toEOSAsset(in, field string) eos.Asset { + return toAsset(eos.EOSSymbol, in, field) +} + +func toREXAsset(in, field string) eos.Asset { + return toAsset(eos.REXSymbol, in, field) } func toName(in, field string) eos.Name { @@ -395,10 +457,3 @@ func isStubABI(abi eos.ABI) bool { abi.Structs == nil && abi.Tables == nil && abi.Types == nil } - -func NewAssetDefaultEOS(input string) (eos.Asset, error) { - if len(strings.Split(input, " ")) == 1 { - return eos.NewEOSAssetFromString(input) - } - return eos.NewAsset(input) -} diff --git a/eosc/cmd/logging.go b/eosc/cmd/logging.go new file mode 100644 index 00000000..c6ecae81 --- /dev/null +++ b/eosc/cmd/logging.go @@ -0,0 +1,11 @@ +package cmd + +import ( + "go.uber.org/zap" +) + +var zlog = zap.NewNop() + +func SetLogger(l *zap.Logger) { + zlog = l +} diff --git a/eosc/cmd/rexBuy.go b/eosc/cmd/rexBuy.go index 29c6ee02..1129b393 100644 --- a/eosc/cmd/rexBuy.go +++ b/eosc/cmd/rexBuy.go @@ -14,7 +14,7 @@ var rexBuy = &cobra.Command{ Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") - quantity := toEOSAsset(args[1], "quantity") + quantity := toCoreAsset(args[1], "quantity") pushEOSCActions(getAPI(), rex.NewBuyREX( account, diff --git a/eosc/cmd/rexDefundCPU.go b/eosc/cmd/rexDefundCPU.go index 7dd701a7..c0341e90 100644 --- a/eosc/cmd/rexDefundCPU.go +++ b/eosc/cmd/rexDefundCPU.go @@ -15,7 +15,7 @@ var rexDefundCPU = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") loanNumber := toUint64(args[1], "loan number") - quantity := toEOSAsset(args[2], "quantity") + quantity := toCoreAsset(args[2], "quantity") pushEOSCActions(getAPI(), rex.NewDefundCPULoan( account, diff --git a/eosc/cmd/rexDefundNET.go b/eosc/cmd/rexDefundNET.go index 01fb3913..4c99b3c4 100644 --- a/eosc/cmd/rexDefundNET.go +++ b/eosc/cmd/rexDefundNET.go @@ -15,7 +15,7 @@ var rexDefundNet = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") loanNumber := toUint64(args[1], "loan number") - quantity := toEOSAsset(args[2], "quantity") + quantity := toCoreAsset(args[2], "quantity") pushEOSCActions(getAPI(), rex.NewDefundNetLoan( account, diff --git a/eosc/cmd/rexDeposit.go b/eosc/cmd/rexDeposit.go index a5891b80..0b4ad031 100644 --- a/eosc/cmd/rexDeposit.go +++ b/eosc/cmd/rexDeposit.go @@ -14,7 +14,7 @@ var rexDeposit = &cobra.Command{ Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") - quantity := toEOSAsset(args[1], "quantity") + quantity := toCoreAsset(args[1], "quantity") pushEOSCActions(getAPI(), rex.NewDeposit( account, diff --git a/eosc/cmd/rexFundCPU.go b/eosc/cmd/rexFundCPU.go index d919ecc3..2dec9f78 100644 --- a/eosc/cmd/rexFundCPU.go +++ b/eosc/cmd/rexFundCPU.go @@ -15,7 +15,7 @@ var rexFundCPU = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") loanNumber := toUint64(args[1], "loan number") - quantity := toEOSAsset(args[2], "quantity") + quantity := toCoreAsset(args[2], "quantity") pushEOSCActions(getAPI(), rex.NewFundCPULoan( account, diff --git a/eosc/cmd/rexFundNet.go b/eosc/cmd/rexFundNet.go index 58919bbd..8d713544 100644 --- a/eosc/cmd/rexFundNet.go +++ b/eosc/cmd/rexFundNet.go @@ -15,7 +15,7 @@ var rexFundNet = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") loanNumber := toUint64(args[1], "loan number") - quantity := toEOSAsset(args[2], "quantity") + quantity := toCoreAsset(args[2], "quantity") pushEOSCActions(getAPI(), rex.NewFundNetLoan( account, diff --git a/eosc/cmd/rexRentCPU.go b/eosc/cmd/rexRentCPU.go index 8432eebe..502546bb 100644 --- a/eosc/cmd/rexRentCPU.go +++ b/eosc/cmd/rexRentCPU.go @@ -15,8 +15,8 @@ var rexRentCPU = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { payer := toAccount(args[0], "payer") receiver := toAccount(args[1], "receiver") - quantity := toEOSAsset(args[2], "quantity") - loanFund := toEOSAsset(args[3], "loan fund") + quantity := toCoreAsset(args[2], "quantity") + loanFund := toCoreAsset(args[3], "loan fund") pushEOSCActions(getAPI(), rex.NewRentCPU( payer, diff --git a/eosc/cmd/rexRentNet.go b/eosc/cmd/rexRentNet.go index 120f7975..b6a0f0be 100644 --- a/eosc/cmd/rexRentNet.go +++ b/eosc/cmd/rexRentNet.go @@ -15,8 +15,8 @@ var rexRentNet = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { payer := toAccount(args[0], "payer") receiver := toAccount(args[1], "receiver") - quantity := toEOSAsset(args[2], "quantity") - loanFund := toEOSAsset(args[3], "loan fund") + quantity := toCoreAsset(args[2], "quantity") + loanFund := toCoreAsset(args[3], "loan fund") pushEOSCActions(getAPI(), rex.NewRentNet( payer, diff --git a/eosc/cmd/rexUnstakeTo.go b/eosc/cmd/rexUnstakeTo.go index 04565097..d227d480 100644 --- a/eosc/cmd/rexUnstakeTo.go +++ b/eosc/cmd/rexUnstakeTo.go @@ -15,8 +15,8 @@ var rexUnstakeTo = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { staker := toAccount(args[0], "staker") stakedTo := toAccount(args[1], "staked to") - net := toEOSAsset(args[2], "net") - cpu := toEOSAsset(args[3], "cpu") + net := toCoreAsset(args[2], "net") + cpu := toCoreAsset(args[3], "cpu") pushEOSCActions(getAPI(), rex.NewUnstakeToREX( staker, diff --git a/eosc/cmd/rexWithdraw.go b/eosc/cmd/rexWithdraw.go index b1789fdf..64d210e2 100644 --- a/eosc/cmd/rexWithdraw.go +++ b/eosc/cmd/rexWithdraw.go @@ -14,7 +14,7 @@ var rexWithdraw = &cobra.Command{ Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { account := toAccount(args[0], "account") - quantity := toEOSAsset(args[1], "quantity") + quantity := toCoreAsset(args[1], "quantity") pushEOSCActions(getAPI(), rex.NewWithdraw( account, diff --git a/eosc/cmd/root.go b/eosc/cmd/root.go index aafdcc40..f28b2978 100644 --- a/eosc/cmd/root.go +++ b/eosc/cmd/root.go @@ -5,6 +5,8 @@ import ( "os" "strings" + "go.uber.org/zap" + "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -41,6 +43,7 @@ func Execute() { func init() { cobra.OnInitialize(initConfig) + RootCmd.PersistentFlags().StringP("core-symbol", "c", "", "Core symbol to use for all commands (default inferred from API if possible, 4,EOS otherwise)") RootCmd.PersistentFlags().BoolP("debug", "", false, "Enables verbose API debug messages") RootCmd.PersistentFlags().StringP("vault-file", "", "./eosc-vault.json", "Wallet file that contains encrypted key material") RootCmd.PersistentFlags().StringSliceP("wallet-url", "", []string{}, "Base URL to wallet endpoint. You can pass this multiple times to use the multi-signer (will use each wallet to sign multi-sig transactions).") @@ -65,6 +68,13 @@ func initConfig() { viper.SetEnvKeyReplacer(replacer) recurseViperCommands(RootCmd, nil) + + if viper.GetBool("global-debug") { + zlog, err := zap.NewDevelopment() + if err == nil { + SetLogger(zlog) + } + } } func recurseViperCommands(root *cobra.Command, segments []string) { diff --git a/eosc/cmd/systemBidname.go b/eosc/cmd/systemBidname.go index 1f8635a4..b2ea5d43 100644 --- a/eosc/cmd/systemBidname.go +++ b/eosc/cmd/systemBidname.go @@ -28,8 +28,7 @@ Read https://steemit.com/eos/@eos-canada/everything-you-need-to-know-about-names bidder := toAccount(args[0], "bidder_account_name") newname := toAccount(args[1], "premium_account_name") - bidAsset, err := NewAssetDefaultEOS(args[2]) - errorCheck("bid amount invalid", err) + bidAsset := toCoreAsset(args[2], "bid quantity") fmt.Printf("[%s] bidding for: %s , amount=%d precision=%d symbol=%s\n", bidder, newname, bidAsset.Amount, bidAsset.Symbol.Precision, bidAsset.Symbol.Symbol) diff --git a/eosc/cmd/systemDelegateBW.go b/eosc/cmd/systemDelegateBW.go index e5301276..d26f75bf 100644 --- a/eosc/cmd/systemDelegateBW.go +++ b/eosc/cmd/systemDelegateBW.go @@ -45,10 +45,8 @@ Alternatively, you can use the simplified: Run: func(cmd *cobra.Command, args []string) { from := toAccount(args[0], "from") receiver := toAccount(args[1], "receiver") - netStake, err := NewAssetDefaultEOS(args[2]) - errorCheck(`"network bw stake qty" invalid`, err) - cpuStake, err := NewAssetDefaultEOS(args[3]) - errorCheck(`"cpu bw stake qty" invalid`, err) + netStake := toCoreAsset(args[2], "network bw stake qty") + cpuStake := toCoreAsset(args[3], "cpu bw stake qty") transfer := viper.GetBool("system-delegatebw-cmd-transfer") api := getAPI() diff --git a/eosc/cmd/systemNewaccount.go b/eosc/cmd/systemNewaccount.go index 3cdfe77d..d6dbf073 100644 --- a/eosc/cmd/systemNewaccount.go +++ b/eosc/cmd/systemNewaccount.go @@ -104,10 +104,8 @@ active: errorCheck("missing argument", fmt.Errorf("--stake-net missing")) } - cpuStake, err := NewAssetDefaultEOS(cpuStakeStr) - errorCheck("--stake-cpu invalid", err) - netStake, err := NewAssetDefaultEOS(netStakeStr) - errorCheck("--stake-net invalid", err) + cpuStake := toCoreAsset(cpuStakeStr, "--stake-cpu") + netStake := toCoreAsset(netStakeStr, "--stake-net") doTransfer := viper.GetBool("system-newaccount-cmd-transfer") if cpuStake.Amount != 0 || netStake.Amount != 0 { @@ -118,8 +116,7 @@ active: buyRAM := viper.GetString("system-newaccount-cmd-buy-ram") if buyRAM != "" { - buyRAMAmount, err := NewAssetDefaultEOS(buyRAM) - errorCheck("--buy-ram invalid", err) + buyRAMAmount := toCoreAsset(buyRAM, "--buy-ram") actions = append(actions, system.NewBuyRAM(creator, newAccount, uint64(buyRAMAmount.Amount))) } else { diff --git a/eosc/cmd/systemUndelegateBW.go b/eosc/cmd/systemUndelegateBW.go index dfd2bc38..cbcdcf09 100644 --- a/eosc/cmd/systemUndelegateBW.go +++ b/eosc/cmd/systemUndelegateBW.go @@ -21,14 +21,10 @@ See also: the "system delegatebw" command. Run: func(cmd *cobra.Command, args []string) { from := toAccount(args[0], "from") receiver := toAccount(args[1], "receiver") - netStake, err := NewAssetDefaultEOS(args[2]) - errorCheck(`"network bw unstake qty" invalid`, err) - cpuStake, err := NewAssetDefaultEOS(args[3]) - errorCheck(`"cpu bw unstake qty" invalid`, err) + netStake := toCoreAsset(args[2], "network bw unstake qty") + cpuStake := toCoreAsset(args[3], "cpu bw unstake qty") - api := getAPI() - - pushEOSCActions(api, system.NewUndelegateBW(from, receiver, cpuStake, netStake)) + pushEOSCActions(getAPI(), system.NewUndelegateBW(from, receiver, cpuStake, netStake)) }, } diff --git a/eosc/cmd/toolsSellAccount.go b/eosc/cmd/toolsSellAccount.go index 53f59266..a5c33027 100644 --- a/eosc/cmd/toolsSellAccount.go +++ b/eosc/cmd/toolsSellAccount.go @@ -28,8 +28,7 @@ MAKE SURE TO INSPECT THE GENERATED MULTISIG TRANSACTION BEFORE APPROVING IT. soldAccount := toAccount(args[0], "sold account") buyerAccount := toAccount(args[1], "buyer account") beneficiaryAccount := toAccount(args[2], "beneficiary account") - saleAmount, err := NewAssetDefaultEOS(args[3]) - errorCheck(`sale "amount" invalid`, err) + saleAmount := toCoreAsset(args[3], "amount") proposalName := viper.GetString("tools-sell-account-cmd-proposal-name") memo := viper.GetString("tools-sell-account-cmd-memo") diff --git a/eosc/cmd/transfer.go b/eosc/cmd/transfer.go index f9bcffd8..24d7d8ae 100644 --- a/eosc/cmd/transfer.go +++ b/eosc/cmd/transfer.go @@ -17,15 +17,13 @@ var transferCmd = &cobra.Command{ from := toAccount(args[0], "from") to := toAccount(args[1], "to") - quantity, err := NewAssetDefaultEOS(args[2]) - errorCheck("invalid amount", err) + quantity := toAssetWithDefaultCoreSymbol(args[2], "quantity") memo := viper.GetString("transfer-cmd-memo") - api := getAPI() - action := token.NewTransfer(from, to, quantity, memo) action.Account = contract - pushEOSCActions(api, action) + + pushEOSCActions(getAPI(), action) }, } diff --git a/go.mod b/go.mod index 1d9d2e77..bd1560a6 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/abourget/llerrgroup v0.0.0-20161118145731-75f536392d17 github.com/bronze1man/go-yaml2json v0.0.0-20150129175009-f6f64b738964 github.com/davecgh/go-spew v1.1.1 - github.com/eoscanada/eos-go v0.8.13 + github.com/eoscanada/eos-go v0.8.14-0.20190614154328-0014a42badb5 github.com/golang/protobuf v1.3.1 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983 // indirect @@ -25,7 +25,7 @@ require ( github.com/tidwall/sjson v1.0.0 go.uber.org/atomic v1.3.2 // indirect go.uber.org/multierr v1.1.0 // indirect - go.uber.org/zap v1.9.1 // indirect + go.uber.org/zap v1.9.1 golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e // indirect golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 diff --git a/go.sum b/go.sum index 3283af15..9c5ce24f 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/eoscanada/eos-bios v1.2.0 h1:h2PC3rbOCvPVkq6+RapwUGKn0wZ41ScJmHHTwdDK github.com/eoscanada/eos-bios v1.2.0/go.mod h1:64+2vEnagr6kY1QgIexkeOXPEXn4yeLDiluCzQ2B9dY= github.com/eoscanada/eos-go v0.8.13 h1:2U6a+cj+VygmcrKP8Y2op1qukynXUTfhyhKrysjPmsY= github.com/eoscanada/eos-go v0.8.13/go.mod h1:RKrm2XzZEZWxSMTRqH5QOyJ1fb/qKEjs2ix1aQl0sk4= +github.com/eoscanada/eos-go v0.8.14-0.20190614154328-0014a42badb5 h1:6Y6xlTLOaY7Uzb/aarC4hpqkztamhpyX8ELGXFRre00= +github.com/eoscanada/eos-go v0.8.14-0.20190614154328-0014a42badb5/go.mod h1:RKrm2XzZEZWxSMTRqH5QOyJ1fb/qKEjs2ix1aQl0sk4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=