From b1356598f630bac521983f8c5e061b16fc10f66d Mon Sep 17 00:00:00 2001 From: Nikhil Saraf <1028334+nikhilsaraf@users.noreply.github.com> Date: Tue, 9 Jul 2019 13:16:48 -0400 Subject: [PATCH 01/10] 1 - remove unused chartThumb import in BotCard --- gui/web/src/components/molecules/BotCard/BotCard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/web/src/components/molecules/BotCard/BotCard.js b/gui/web/src/components/molecules/BotCard/BotCard.js index 40d8c40a4..5dac64e07 100644 --- a/gui/web/src/components/molecules/BotCard/BotCard.js +++ b/gui/web/src/components/molecules/BotCard/BotCard.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Pill from '../../atoms/Pill/Pill'; import RunStatus from '../../atoms/RunStatus/RunStatus'; -import chartThumb from '../../../assets/images/chart-thumb.png'; +// import chartThumb from '../../../assets/images/chart-thumb.png'; import styles from './BotCard.module.scss'; import PillGroup from '../PillGroup/PillGroup'; import StartStop from '../../atoms/StartStop/StartStop'; From dc1953113398d63d72cfbd6c317d8b2961807fcb Mon Sep 17 00:00:00 2001 From: Nikhil Saraf <1028334+nikhilsaraf@users.noreply.github.com> Date: Tue, 9 Jul 2019 14:44:10 -0400 Subject: [PATCH 02/10] 2 - autogen secret key --- gui/backend/new_secret_key.go | 18 ++++++ gui/backend/routes.go | 1 + .../src/components/_styles/grid.module.scss | 12 ++++ gui/web/src/components/molecules/Form/Form.js | 63 ++++++++++++++++--- gui/web/src/kelp-ops-api/newSecretKey.js | 5 ++ 5 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 gui/backend/new_secret_key.go create mode 100644 gui/web/src/kelp-ops-api/newSecretKey.js diff --git a/gui/backend/new_secret_key.go b/gui/backend/new_secret_key.go new file mode 100644 index 000000000..bba379dd0 --- /dev/null +++ b/gui/backend/new_secret_key.go @@ -0,0 +1,18 @@ +package backend + +import ( + "fmt" + "net/http" + + "github.com/stellar/go/keypair" +) + +func (s *APIServer) newSecretKey(w http.ResponseWriter, r *http.Request) { + kp, e := keypair.Random() + if e != nil { + s.writeError(w, fmt.Sprintf("error generating keypair: %s\n", e)) + return + } + seed := kp.Seed() + w.Write([]byte(seed)) +} diff --git a/gui/backend/routes.go b/gui/backend/routes.go index c2ad55007..26df41df0 100644 --- a/gui/backend/routes.go +++ b/gui/backend/routes.go @@ -14,6 +14,7 @@ func SetRoutes(r *chi.Mux, s *APIServer) { r.Get("/autogenerate", http.HandlerFunc(s.autogenerateBot)) r.Get("/genBotName", http.HandlerFunc(s.generateBotName)) r.Get("/getNewBotConfig", http.HandlerFunc(s.getNewBotConfig)) + r.Get("/newSecretKey", http.HandlerFunc(s.newSecretKey)) r.Post("/start", http.HandlerFunc(s.startBot)) r.Post("/stop", http.HandlerFunc(s.stopBot)) diff --git a/gui/web/src/components/_styles/grid.module.scss b/gui/web/src/components/_styles/grid.module.scss index 02817fc82..2d2594684 100644 --- a/gui/web/src/components/_styles/grid.module.scss +++ b/gui/web/src/components/_styles/grid.module.scss @@ -74,6 +74,18 @@ max-width: 66.667%; } +.col10p { + composes: _col; + flex-basis: 10.0%; + max-width: 10.00%; +} + +.col90p { + composes: _col; + flex-basis: 90.0%; + max-width: 90.00%; +} + .colPriceSelector { composes: _col; flex-basis: 90%; diff --git a/gui/web/src/components/molecules/Form/Form.js b/gui/web/src/components/molecules/Form/Form.js index c5e85741c..45726498d 100644 --- a/gui/web/src/components/molecules/Form/Form.js +++ b/gui/web/src/components/molecules/Form/Form.js @@ -18,6 +18,7 @@ import PriceFeedAsset from '../PriceFeedAsset/PriceFeedAsset'; import PriceFeedFormula from '../PriceFeedFormula/PriceFeedFormula'; import Levels from '../Levels/Levels'; // import ErrorMessage from '../ErrorMessage/ErrorMessage'; +import newSecretKey from '../../../kelp-ops-api/newSecretKey'; class Form extends Component { constructor(props) { @@ -36,9 +37,18 @@ class Form extends Component { this.newLevel = this.newLevel.bind(this); this.hasNewLevel = this.hasNewLevel.bind(this); this.removeLevel = this.removeLevel.bind(this); + this.newSecret = this.newSecret.bind(this); this._emptyLevel = this._emptyLevel.bind(this); this._triggerUpdateLevels = this._triggerUpdateLevels.bind(this); this._last_fill_tracker_sleep_millis = 1000; + + this._asyncRequests = {}; + } + + componentWillUnmount() { + if (this._asyncRequests["secretKey"]) { + delete this._asyncRequests["secretKey"]; + } } setLoadingFormula() { @@ -146,15 +156,54 @@ class Form extends Component { ) } + newSecret() { + var _this = this; + this._asyncRequests["secretKey"] = newSecretKey(this.props.baseUrl).then(resp => { + if (!_this._asyncRequests["secretKey"]) { + // if it has been deleted it means we don't want to process the result + return + } + + delete _this._asyncRequests["secretKey"]; + this.props.onChange("trader_config.trading_secret_seed", {target: {value: resp}}); + }); + } + render() { // let tradingPlatform = "sdex"; // if (this.props.configData.trader_config.trading_exchange && this.props.configData.trader_config.trading_exchange !== "") { // tradingPlatform = this.props.configData.trader_config.trading_exchange; // } - let network = "TestNet"; - if (!this.props.configData.trader_config.horizon_url.includes("test")) { - network = "PubNet"; + let secretKeyInput = ( + { this.props.onChange("trader_config.trading_secret_seed", event) }} + error="Please enter a valid trader account secret key" + showError={false} + /> + ); + let network = "PubNet"; + let secretKey = secretKeyInput; + if (this.props.configData.trader_config.horizon_url.includes("test")) { + network = "TestNet"; + secretKey = ( +
+
+ {secretKeyInput} +
+
+
+
+ ); } return ( @@ -225,13 +274,7 @@ class Form extends Component { - { this.props.onChange("trader_config.trading_secret_seed", event) }} - error="Please enter a valid trader account secret key" - showError={false} - /> + {secretKey} diff --git a/gui/web/src/kelp-ops-api/newSecretKey.js b/gui/web/src/kelp-ops-api/newSecretKey.js new file mode 100644 index 000000000..8bd461399 --- /dev/null +++ b/gui/web/src/kelp-ops-api/newSecretKey.js @@ -0,0 +1,5 @@ +export default (baseUrl) => { + return fetch(baseUrl + "/api/v1/newSecretKey").then(resp => { + return resp.text(); + }); +}; \ No newline at end of file From 2d0c5bfffa4c95a4b67242a90aae9e009717345c Mon Sep 17 00:00:00 2001 From: Nikhil Saraf <1028334+nikhilsaraf@users.noreply.github.com> Date: Tue, 9 Jul 2019 14:52:10 -0400 Subject: [PATCH 03/10] 3 - add secret key generatin for source account --- gui/web/src/components/molecules/Form/Form.js | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/gui/web/src/components/molecules/Form/Form.js b/gui/web/src/components/molecules/Form/Form.js index 45726498d..096d9263b 100644 --- a/gui/web/src/components/molecules/Form/Form.js +++ b/gui/web/src/components/molecules/Form/Form.js @@ -156,7 +156,7 @@ class Form extends Component { ) } - newSecret() { + newSecret(field) { var _this = this; this._asyncRequests["secretKey"] = newSecretKey(this.props.baseUrl).then(resp => { if (!_this._asyncRequests["secretKey"]) { @@ -165,7 +165,7 @@ class Form extends Component { } delete _this._asyncRequests["secretKey"]; - this.props.onChange("trader_config.trading_secret_seed", {target: {value: resp}}); + this.props.onChange(field, {target: {value: resp}}); }); } @@ -175,7 +175,7 @@ class Form extends Component { // tradingPlatform = this.props.configData.trader_config.trading_exchange; // } - let secretKeyInput = ( + let traderSecretKeyInput = ( ); + let sourceSecretKeyInput = ( + { this.props.onChange("trader_config.source_secret_seed", event) }} + /> + ); let network = "PubNet"; - let secretKey = secretKeyInput; + let traderSecretKey = traderSecretKeyInput; + let sourceSecretKey = sourceSecretKeyInput; if (this.props.configData.trader_config.horizon_url.includes("test")) { network = "TestNet"; - secretKey = ( + traderSecretKey = (
- {secretKeyInput} + {traderSecretKeyInput}
+
+ ); + sourceSecretKey = ( +
+
+ {sourceSecretKeyInput} +
+
+
@@ -274,7 +298,7 @@ class Form extends Component { - {secretKey} + {traderSecretKey} @@ -337,11 +361,7 @@ class Form extends Component { - { this.props.onChange("trader_config.source_secret_seed", event) }} - /> + {sourceSecretKey} From 2a6b8d2adc4adf11bf2bb45504395fc7ba883cda Mon Sep 17 00:00:00 2001 From: Nikhil Saraf <1028334+nikhilsaraf@users.noreply.github.com> Date: Tue, 9 Jul 2019 15:29:28 -0400 Subject: [PATCH 04/10] 4 - display errors in the UI if encountered while saving bot configs --- .../molecules/ErrorMessage/ErrorMessage.js | 3 +-- gui/web/src/components/molecules/Form/Form.js | 12 +++++++++--- gui/web/src/components/screens/NewBot/NewBot.js | 16 +++++++++++++++- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/gui/web/src/components/molecules/ErrorMessage/ErrorMessage.js b/gui/web/src/components/molecules/ErrorMessage/ErrorMessage.js index 445b2997b..65f5100d7 100644 --- a/gui/web/src/components/molecules/ErrorMessage/ErrorMessage.js +++ b/gui/web/src/components/molecules/ErrorMessage/ErrorMessage.js @@ -8,8 +8,7 @@ class ErrorMessage extends Component {

Oops, something is not right.

-

Please, review the fields marked in red and try again. -

+

{this.props.error}

); } diff --git a/gui/web/src/components/molecules/Form/Form.js b/gui/web/src/components/molecules/Form/Form.js index 096d9263b..b84fad743 100644 --- a/gui/web/src/components/molecules/Form/Form.js +++ b/gui/web/src/components/molecules/Form/Form.js @@ -17,7 +17,7 @@ import FieldGroup from '../FieldGroup/FieldGroup'; import PriceFeedAsset from '../PriceFeedAsset/PriceFeedAsset'; import PriceFeedFormula from '../PriceFeedFormula/PriceFeedFormula'; import Levels from '../Levels/Levels'; -// import ErrorMessage from '../ErrorMessage/ErrorMessage'; +import ErrorMessage from '../ErrorMessage/ErrorMessage'; import newSecretKey from '../../../kelp-ops-api/newSecretKey'; class Form extends Component { @@ -230,6 +230,11 @@ class Form extends Component { ); } + let error = ""; + if (this.props.error) { + error = (); + } + return (
@@ -238,6 +243,8 @@ class Form extends Component { */} + {error} + - {/* */} -
+ {error}
diff --git a/gui/web/src/components/molecules/Form/Form.js b/gui/web/src/components/molecules/Form/Form.js index b84fad743..1cffa7098 100644 --- a/gui/web/src/components/molecules/Form/Form.js +++ b/gui/web/src/components/molecules/Form/Form.js @@ -38,8 +38,10 @@ class Form extends Component { this.hasNewLevel = this.hasNewLevel.bind(this); this.removeLevel = this.removeLevel.bind(this); this.newSecret = this.newSecret.bind(this); + this.getError = this.getError.bind(this); this._emptyLevel = this._emptyLevel.bind(this); this._triggerUpdateLevels = this._triggerUpdateLevels.bind(this); + this._fetchDotNotation = this._fetchDotNotation.bind(this); this._last_fill_tracker_sleep_millis = 1000; this._asyncRequests = {}; @@ -71,6 +73,27 @@ class Form extends Component { } } + _fetchDotNotation(obj, path) { + let parts = path.split('.'); + for (let i = 0; i < parts.length; i++) { + obj = obj[parts[i]]; + + if (obj === undefined || obj === null || obj === "") { + return null; + } + } + + return obj + } + + getError(fieldKey) { + if (!this.props.errorResp) { + return null; + } + + return this._fetchDotNotation(this.props.errorResp.fields, fieldKey); + } + save() { this.setState({ isSaving: true, @@ -180,8 +203,7 @@ class Form extends Component { value={this.props.configData.trader_config.trading_secret_seed} type="string" onChange={(event) => { this.props.onChange("trader_config.trading_secret_seed", event) }} - error="Please enter a valid trader account secret key" - showError={false} + error={this.getError("trader_config.trading_secret_seed")} /> ); let sourceSecretKeyInput = ( @@ -189,6 +211,7 @@ class Form extends Component { value={this.props.configData.trader_config.source_secret_seed} type="string" onChange={(event) => { this.props.onChange("trader_config.source_secret_seed", event) }} + error={this.getError("trader_config.source_secret_seed")} /> ); let network = "PubNet"; @@ -231,8 +254,8 @@ class Form extends Component { } let error = ""; - if (this.props.error) { - error = (); + if (this.props.errorResp) { + error = (); } return ( @@ -252,6 +275,7 @@ class Form extends Component { type="string" onChange={(event) => { this.props.onChange("name", event) }} disabled={!this.props.isNew} + error={this.getError("name")} /> {/* Trader Settings */} @@ -319,6 +343,7 @@ class Form extends Component { value={this.props.configData.trader_config.asset_code_a} type="string" onChange={(event) => { this.props.onChange("trader_config.asset_code_a", event) }} + error={this.getError("trader_config.asset_code_a")} />
@@ -331,6 +356,7 @@ class Form extends Component { type="string" onChange={(event) => { this.props.onChange("trader_config.issuer_a", event) }} disabled={this.props.configData.trader_config.asset_code_a === "XLM"} + error={this.getError("trader_config.issuer_a")} />
@@ -344,6 +370,7 @@ class Form extends Component { value={this.props.configData.trader_config.asset_code_b} type="string" onChange={(event) => { this.props.onChange("trader_config.asset_code_b", event) }} + error={this.getError("trader_config.asset_code_b")} />
@@ -356,6 +383,7 @@ class Form extends Component { type="string" onChange={(event) => { this.props.onChange("trader_config.issuer_b", event) }} disabled={this.props.configData.trader_config.asset_code_b === "XLM"} + error={this.getError("trader_config.issuer_b")} />
@@ -378,6 +406,7 @@ class Form extends Component { value={this.props.configData.trader_config.tick_interval_seconds} type="int" onChange={(event) => { this.props.onChange("trader_config.tick_interval_seconds", event) }} + error={this.getError("trader_config.tick_interval_seconds")} /> @@ -388,6 +417,7 @@ class Form extends Component { value={this.props.configData.trader_config.max_tick_delay_millis} type="int" onChange={(event) => { this.props.onChange("trader_config.max_tick_delay_millis", event) }} + error={this.getError("trader_config.max_tick_delay_millis")} /> @@ -412,6 +442,7 @@ class Form extends Component { value={this.props.configData.trader_config.delete_cycles_threshold} type="int" onChange={(event) => { this.props.onChange("trader_config.delete_cycles_threshold", event) }} + error={this.getError("trader_config.delete_cycles_threshold")} /> @@ -437,6 +468,7 @@ class Form extends Component { value={this.props.configData.trader_config.fill_tracker_sleep_millis === 0 ? this._last_fill_tracker_sleep_millis : this.props.configData.trader_config.fill_tracker_sleep_millis} type="int" disabled={this.props.configData.trader_config.fill_tracker_sleep_millis === 0} + error={this.getError("trader_config.fill_tracker_sleep_millis")} onChange={(event) => { this.props.onChange("trader_config.fill_tracker_sleep_millis", event, { "trader_config.fill_tracker_sleep_millis": (value) => { @@ -461,7 +493,9 @@ class Form extends Component { value={this.props.configData.trader_config.fill_tracker_delete_cycles_threshold} type="int" disabled={this.props.configData.trader_config.fill_tracker_sleep_millis === 0} - onChange={(event) => { this.props.onChange("trader_config.fill_tracker_delete_cycles_threshold", event) }}/> + onChange={(event) => { this.props.onChange("trader_config.fill_tracker_delete_cycles_threshold", event) }} + error={this.getError("trader_config.fill_tracker_delete_cycles_threshold")} + /> @@ -473,6 +507,7 @@ class Form extends Component { value={this.props.configData.trader_config.fee.capacity_trigger} type="float" onChange={(event) => { this.props.onChange("trader_config.fee.capacity_trigger", event) }} + error={this.getError("trader_config.fee.capacity_trigger")} /> @@ -486,6 +521,7 @@ class Form extends Component { value={this.props.configData.trader_config.fee.percentile} type="int" // this is a selection from the fee stats endpoint onChange={(event) => { this.props.onChange("trader_config.fee.percentile", event) }} + error={this.getError("trader_config.fee.percentile")} /> @@ -499,6 +535,7 @@ class Form extends Component { value={this.props.configData.trader_config.fee.max_op_fee_stroops} type="int" onChange={(event) => { this.props.onChange("trader_config.fee.max_op_fee_stroops", event) }} + error={this.getError("trader_config.fee.max_op_fee_stroops")} /> @@ -514,6 +551,7 @@ class Form extends Component { value={this.props.configData.trader_config.centralized_price_precision_override} type="int" onChange={(event) => { this.props.onChange("trader_config.centralized_price_precision_override", event) }} + error={this.getError("trader_config.centralized_price_precision_override")} /> @@ -525,6 +563,7 @@ class Form extends Component { value={this.props.configData.trader_config.centralized_volume_precision_override} type="int" onChange={(event) => { this.props.onChange("trader_config.centralized_volume_precision_override", event) }} + error={this.getError("trader_config.centralized_volume_precision_override")} /> @@ -539,6 +578,7 @@ class Form extends Component { value={this.props.configData.trader_config.centralized_min_base_volume_override} type="float" onChange={(event) => { this.props.onChange("trader_config.centralized_min_base_volume_override", event) }} + error={this.getError("trader_config.centralized_min_base_volume_override")} /> @@ -550,6 +590,7 @@ class Form extends Component { value={this.props.configData.trader_config.centralized_min_quote_volume_override} type="float" onChange={(event) => { this.props.onChange("trader_config.centralized_min_quote_volume_override", event) }} + error={this.getError("trader_config.centralized_min_quote_volume_override")} /> @@ -611,6 +652,7 @@ class Form extends Component { newLevel={this.newLevel} hasNewLevel={this.hasNewLevel} onRemove={(levelIdx) => { this.removeLevel(levelIdx) }} + error={this.getError("strategy_config.levels")} /> @@ -630,6 +672,7 @@ class Form extends Component { value={this.props.configData.strategy_config.price_tolerance} type="percent" onChange={(event) => { this.props.onChange("strategy_config.price_tolerance", event) }} + error={this.getError("strategy_config.price_tolerance")} /> @@ -641,6 +684,7 @@ class Form extends Component { value={this.props.configData.strategy_config.amount_tolerance} type="percent" onChange={(event) => { this.props.onChange("strategy_config.amount_tolerance", event) }} + error={this.getError("strategy_config.amount_tolerance")} /> @@ -655,6 +699,7 @@ class Form extends Component { value={this.props.configData.strategy_config.rate_offset_percent} type="percent" onChange={(event) => { this.props.onChange("strategy_config.rate_offset_percent", event) }} + error={this.getError("strategy_config.rate_offset_percent")} /> @@ -665,6 +710,7 @@ class Form extends Component { value={this.props.configData.strategy_config.rate_offset} type="float" onChange={(event) => { this.props.onChange("strategy_config.rate_offset", event) }} + error={this.getError("strategy_config.rate_offset")} /> diff --git a/gui/web/src/components/molecules/Levels/Levels.js b/gui/web/src/components/molecules/Levels/Levels.js index b345b4437..a5735ac5d 100644 --- a/gui/web/src/components/molecules/Levels/Levels.js +++ b/gui/web/src/components/molecules/Levels/Levels.js @@ -5,6 +5,7 @@ import Button from '../../atoms/Button/Button'; import FieldItem from '../FieldItem/FieldItem'; import Label from '../../atoms/Label/Label'; import Input from '../../atoms/Input/Input'; +import ErrorMessage from '../ErrorMessage/ErrorMessage'; class Levels extends Component { static defaultProps = { @@ -55,9 +56,15 @@ class Levels extends Component { )); } + let error = ""; + if (this.props.error) { + error = (); + } + return (
{levels} + {error}