Production Execution Flow
This commit is contained in:
@@ -227,5 +227,34 @@ void GeminiGetOrderQuoteFunction::OnOrderQuoteResult(
|
||||
Respond(OneArgument(std::move(quote)));
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
GeminiExecuteOrderFunction::Run() {
|
||||
if (!IsGeminiAPIAvailable(browser_context())) {
|
||||
return RespondNow(Error("Not available in Tor/incognito/guest profile"));
|
||||
}
|
||||
|
||||
std::unique_ptr<gemini::ExecuteOrder::Params> params(
|
||||
gemini::ExecuteOrder::Params::Create(*args_));
|
||||
EXTENSION_FUNCTION_VALIDATE(params.get());
|
||||
|
||||
auto* service = GetGeminiService(browser_context());
|
||||
bool balance_success = service->ExecuteOrder(
|
||||
params->symbol, params->side, params->quantity,
|
||||
params->price, params->fee, params->quote_id,
|
||||
base::BindOnce(
|
||||
&GeminiExecuteOrderFunction::OnOrderExecuted,
|
||||
this));
|
||||
|
||||
if (!balance_success) {
|
||||
return RespondNow(Error("Could not send request to execute order"));
|
||||
}
|
||||
|
||||
return RespondLater();
|
||||
}
|
||||
|
||||
void GeminiExecuteOrderFunction::OnOrderExecuted(bool success) {
|
||||
Respond(OneArgument(std::make_unique<base::Value>(success)));
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
} // namespace extensions
|
||||
|
||||
@@ -104,6 +104,18 @@ class GeminiGetOrderQuoteFunction :
|
||||
ResponseAction Run() override;
|
||||
};
|
||||
|
||||
class GeminiExecuteOrderFunction :
|
||||
public ExtensionFunction {
|
||||
public:
|
||||
DECLARE_EXTENSION_FUNCTION("gemini.executeOrder", UNKNOWN)
|
||||
|
||||
protected:
|
||||
~GeminiExecuteOrderFunction() override {}
|
||||
void OnOrderExecuted(bool success);
|
||||
|
||||
ResponseAction Run() override;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace extensions
|
||||
|
||||
|
||||
@@ -306,7 +306,7 @@ void CustomizeWebUIHTMLSource(const std::string &name,
|
||||
{ "geminiWidgetConnectTitle", IDS_GEMINI_WIDGET_CONNECT_TITLE },
|
||||
{ "geminiWidgetConnectCopy", IDS_GEMINI_WIDGET_CONNECT_COPY },
|
||||
{ "geminiWidgetConnectButton", IDS_GEMINI_WIDGET_CONNECT_BUTTON },
|
||||
{ "geminiWidgetFailedTrade", IDS_GEMINI_WIDGET_FAILED_TRADE },
|
||||
{ "geminiWidgetFailedTrade", IDS_GEMINI_WIDGET_FAILED_TRADE },
|
||||
{ "geminiWidgetInsufficientFunds", IDS_BINANCE_WIDGET_INSUFFICIENT_FUNDS }, // NOLINT
|
||||
{ "geminiWidgetError", IDS_GEMINI_WIDGET_ERROR },
|
||||
{ "geminiWidgetConfirmTrade", IDS_GEMINI_WIDGET_CONFIRM_TRADE },
|
||||
@@ -320,6 +320,13 @@ void CustomizeWebUIHTMLSource(const std::string &name,
|
||||
{ "geminiWidgetDepositLabel", IDS_BINANCE_WIDGET_DEPOSIT_LABEL },
|
||||
{ "geminiWidgetTradeLabel", IDS_GEMINI_WIDGET_TRADE_LABEL },
|
||||
{ "geminiWidgetBalanceLabel", IDS_GEMINI_WIDGET_BALANCE_LABEL },
|
||||
{ "geminiWidgetBuying", IDS_GEMINI_WIDGET_BUYING },
|
||||
{ "geminiWidgetSelling", IDS_GEMINI_WIDGET_SELLING },
|
||||
{ "geminiWidgetContinue", IDS_BINANCE_WIDGET_CONTINUE },
|
||||
{ "geminiWidgetBought", IDS_GEMINI_WIDGET_BOUGHT },
|
||||
{ "geminiWidgetSold", IDS_GEMINI_WIDGET_SOLD },
|
||||
{ "geminiWidgetFee", IDS_BINANCE_WIDGET_FEE },
|
||||
{ "geminiWidgetPrice", IDS_GEMINI_WIDGET_PRICE }
|
||||
}
|
||||
}, {
|
||||
std::string("wallet"), {
|
||||
|
||||
@@ -172,6 +172,47 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "executeOrder",
|
||||
"type": "function",
|
||||
"description": "Gets a quote for a given order",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "symbol"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "side"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "quantity"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "price"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "fee"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"name": "quoteId"
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "success"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
|
||||
@@ -68,13 +68,19 @@ import {
|
||||
DisconnectCopy,
|
||||
InvalidWrapper,
|
||||
InvalidTitle,
|
||||
InvalidCopy
|
||||
InvalidCopy,
|
||||
TradeInfoWrapper,
|
||||
TradeInfoItem,
|
||||
TradeItemLabel,
|
||||
TradeValue,
|
||||
StyledParty
|
||||
} from './style'
|
||||
import {
|
||||
SearchIcon,
|
||||
QRIcon,
|
||||
ShowIcon,
|
||||
HideIcon
|
||||
HideIcon,
|
||||
PartyIcon
|
||||
} from '../exchangeWidget/shared-assets'
|
||||
import GeminiLogo from './assets/gemini-logo'
|
||||
import { CaratLeftIcon, CaratDownIcon } from 'brave-ui/components/icons'
|
||||
@@ -239,7 +245,8 @@ class Gemini extends React.PureComponent<Props, State> {
|
||||
currentQRAsset,
|
||||
insufficientFunds,
|
||||
tradeFailed,
|
||||
showTradePreview
|
||||
showTradePreview,
|
||||
tradeSuccess
|
||||
} = this.state
|
||||
const { authInvalid, disconnectInProgress } = this.props
|
||||
|
||||
@@ -253,6 +260,8 @@ class Gemini extends React.PureComponent<Props, State> {
|
||||
return this.renderInsufficientFundsView()
|
||||
} else if (tradeFailed) {
|
||||
return this.renderUnableToTradeView()
|
||||
} else if (tradeSuccess) {
|
||||
return this.renderTradeSuccess()
|
||||
} else if (showTradePreview) {
|
||||
return this.renderTradeConfirm()
|
||||
}
|
||||
@@ -331,6 +340,10 @@ class Gemini extends React.PureComponent<Props, State> {
|
||||
USDValue += price * assetBalance
|
||||
}
|
||||
|
||||
if ('USD' in accountBalances) {
|
||||
USDValue += parseFloat(accountBalances['USD'])
|
||||
}
|
||||
|
||||
return USDValue.toFixed(2)
|
||||
}
|
||||
|
||||
@@ -579,13 +592,43 @@ class Gemini extends React.PureComponent<Props, State> {
|
||||
})
|
||||
}
|
||||
|
||||
shouldShowTradePreview () {
|
||||
processTrade = () => {
|
||||
const {
|
||||
currentTradeId,
|
||||
currentTradeAsset,
|
||||
currentTradeFee: fee,
|
||||
currentTradePrice: price,
|
||||
currentTradeMode: side,
|
||||
currentTradeQuantityLive: quantity
|
||||
} = this.state
|
||||
const quoteId = parseInt(currentTradeId, 10)
|
||||
const symbol = `${currentTradeAsset}usd`.toUpperCase()
|
||||
chrome.gemini.executeOrder(symbol, side, quantity, price, fee, quoteId, (success: boolean) => {
|
||||
if (success) {
|
||||
this.setState({ tradeSuccess: true })
|
||||
} else {
|
||||
this.setState({ tradeFailed: true })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
finishTrade = () => {
|
||||
this.cancelTrade()
|
||||
this.props.onSetSelectedView('balance')
|
||||
}
|
||||
|
||||
shouldShowTradePreview = () => {
|
||||
const {
|
||||
currentTradeMode,
|
||||
currentTradeAsset,
|
||||
currentTradeQuantity
|
||||
} = this.state
|
||||
const { accountBalances } = this.props
|
||||
|
||||
if (!currentTradeQuantity || isNaN(parseFloat(currentTradeQuantity))) {
|
||||
return
|
||||
}
|
||||
|
||||
const compare = currentTradeMode === 'buy'
|
||||
? (accountBalances['USD'] || '0')
|
||||
: (accountBalances[currentTradeAsset] || '0')
|
||||
@@ -661,21 +704,64 @@ class Gemini extends React.PureComponent<Props, State> {
|
||||
)
|
||||
}
|
||||
|
||||
renderTradeSuccess = () => {
|
||||
const {
|
||||
currentTradeAsset,
|
||||
currentTradeQuantityLive,
|
||||
currentTradeMode
|
||||
} = this.state
|
||||
const quantity = this.formatCryptoBalance(currentTradeQuantityLive)
|
||||
const actionLabel = currentTradeMode === 'buy' ? 'geminiWidgetBought' : 'geminiWidgetSold'
|
||||
|
||||
return (
|
||||
<InvalidWrapper>
|
||||
<StyledParty>
|
||||
<img src={PartyIcon} />
|
||||
</StyledParty>
|
||||
<InvalidTitle>
|
||||
{`${getLocale(actionLabel)} ${quantity} ${currentTradeAsset}!`}
|
||||
</InvalidTitle>
|
||||
<ConnectButton isSmall={true} onClick={this.finishTrade}>
|
||||
{getLocale('geminiWidgetContinue')}
|
||||
</ConnectButton>
|
||||
</InvalidWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
renderTradeConfirm = () => {
|
||||
const {
|
||||
currentTradeQuantityLive,
|
||||
currentTradeFee,
|
||||
currentTradeExpiryTime
|
||||
currentTradeAsset,
|
||||
currentTradeQuantityLive,
|
||||
currentTradeMode,
|
||||
currentTradeExpiryTime,
|
||||
currentTradePrice
|
||||
} = this.state
|
||||
console.log(currentTradeExpiryTime, currentTradeFee, currentTradeQuantityLive)
|
||||
const tradeLabel = currentTradeMode === 'buy' ? 'geminiWidgetBuying' : 'geminiWidgetSelling'
|
||||
const quantity = this.formatCryptoBalance(currentTradeQuantityLive)
|
||||
const fee = this.formatCryptoBalance(currentTradeFee)
|
||||
|
||||
return (
|
||||
<InvalidWrapper>
|
||||
<InvalidTitle>
|
||||
{getLocale('geminiWidgetConfirmTrade')}
|
||||
</InvalidTitle>
|
||||
<TradeInfoWrapper>
|
||||
<TradeInfoItem>
|
||||
<TradeItemLabel>{getLocale(tradeLabel)}</TradeItemLabel>
|
||||
<TradeValue>{`${quantity} ${currentTradeAsset}`}</TradeValue>
|
||||
</TradeInfoItem>
|
||||
<TradeInfoItem>
|
||||
<TradeItemLabel>{getLocale('geminiWidgetPrice')}</TradeItemLabel>
|
||||
<TradeValue>{`${currentTradePrice} USD`}</TradeValue>
|
||||
</TradeInfoItem>
|
||||
<TradeInfoItem isLast={true}>
|
||||
<TradeItemLabel>{getLocale('geminiWidgetFee')}</TradeItemLabel>
|
||||
<TradeValue>{`${fee} USD`}</TradeValue>
|
||||
</TradeInfoItem>
|
||||
</TradeInfoWrapper>
|
||||
<ActionsWrapper>
|
||||
<ConnectButton isSmall={true}>
|
||||
<ConnectButton isSmall={true} onClick={this.processTrade}>
|
||||
{`${getLocale('geminiWidgetConfirm')} (${currentTradeExpiryTime}s)`}
|
||||
</ConnectButton>
|
||||
<DismissAction onClick={this.cancelTrade}>
|
||||
|
||||
@@ -507,3 +507,33 @@ export const InvalidCopy = styled(DisconnectCopy)`
|
||||
export const InvalidWrapper = styled(DisconnectWrapper)`
|
||||
min-width: 244px;
|
||||
`
|
||||
|
||||
export const TradeItemLabel = styled<{}, 'span'>('span')`
|
||||
float: left;
|
||||
width: 45%;
|
||||
text-align: left;
|
||||
font-size: 15px;
|
||||
`
|
||||
|
||||
export const TradeValue = styled<{}, 'span'>('span')`
|
||||
font-weight: bold;
|
||||
float: right;
|
||||
width: 55%;
|
||||
text-align: right;
|
||||
font-size: 15px;
|
||||
`
|
||||
|
||||
export const TradeInfoWrapper = styled<StyleProps, 'div'>('div')`
|
||||
margin: 20px 0;
|
||||
overflow-y: auto;
|
||||
`
|
||||
|
||||
export const TradeInfoItem = styled<StyleProps, 'div'>('div')`
|
||||
margin: 5px 0;
|
||||
overflow-y: hidden;
|
||||
margin-top: ${p => p.isLast ? '15' : '5'}px;
|
||||
`
|
||||
|
||||
export const StyledParty = styled<{}, 'div'>('div')`
|
||||
margin: 10px 0px;
|
||||
`
|
||||
|
||||
@@ -50,7 +50,11 @@ const geminiReducer: Reducer<NewTab.State | undefined> = (state: NewTab.State, a
|
||||
case types.SET_ACCOUNT_BALANCES:
|
||||
const { balances } = payload
|
||||
state = { ...state }
|
||||
state.geminiState.accountBalances = balances
|
||||
for (let balance in balances) {
|
||||
if (balances[balance]) {
|
||||
state.geminiState.accountBalances[balance] = balances[balance]
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case types.ON_DEPOSIT_QR_FOR_ASSET:
|
||||
|
||||
Vendored
+1
@@ -175,6 +175,7 @@ declare namespace chrome.gemini {
|
||||
const getDepositInfo: (asset: string, callback: (depositAddress: string, depositTag: string) => void) => {}
|
||||
const revokeToken: (callback: (success: boolean) => void) => {}
|
||||
const getOrderQuote: (side: string, symbol: string, spend: string, callback: (quote: any) => void) => {}
|
||||
const executeOrder: (symbol: string, side: string, quantity: string, price: string, fee: string, quoteId: number, callback: (success: boolean) => void) => {}
|
||||
}
|
||||
|
||||
declare namespace chrome.braveTogether {
|
||||
|
||||
@@ -81,6 +81,26 @@ namespace {
|
||||
return encoded_payload;
|
||||
}
|
||||
|
||||
std::string GetEncodedExecutePayload(std::string symbol,
|
||||
std::string side,
|
||||
std::string quantity,
|
||||
std::string price,
|
||||
std::string fee,
|
||||
int quote_id) {
|
||||
std::string encoded_payload;
|
||||
std::string request_payload = "{\"request\": \"" +
|
||||
std::string(api_path_execute_quote) +
|
||||
"\",\"symbol\": \"" + symbol +
|
||||
"\",\"side\": \"" + side +
|
||||
"\",\"quantity\": \"" + quantity +
|
||||
"\",\"price\": \"" + price +
|
||||
"\",\"fee\": \"" + fee +
|
||||
"\",\"quoteId\": \"" + std::to_string(quote_id) +
|
||||
"\"}";
|
||||
base::Base64Encode(request_payload, &encoded_payload);
|
||||
return encoded_payload;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GeminiService::GeminiService(content::BrowserContext* context)
|
||||
@@ -259,6 +279,29 @@ void GeminiService::OnGetOrderQuote(GetOrderQuoteCallback callback,
|
||||
std::move(callback).Run(quote_id, quantity, fee, price);
|
||||
}
|
||||
|
||||
bool GeminiService::ExecuteOrder(const std::string& symbol,
|
||||
const std::string& side,
|
||||
const std::string& quantity,
|
||||
const std::string& price,
|
||||
const std::string& fee,
|
||||
const int quote_id,
|
||||
ExecuteOrderCallback callback) {
|
||||
auto internal_callback = base::BindOnce(&GeminiService::OnOrderExecuted,
|
||||
base::Unretained(this), std::move(callback));
|
||||
std::string payload = GetEncodedExecutePayload(
|
||||
symbol, side, quantity, price, fee, quote_id);
|
||||
GURL url = GetURLWithPath(api_host, api_path_execute_quote);
|
||||
return OAuthRequest(
|
||||
url, "POST", "", std::move(internal_callback), true, true, payload);
|
||||
}
|
||||
|
||||
void GeminiService::OnOrderExecuted(ExecuteOrderCallback callback,
|
||||
const int status, const std::string& body,
|
||||
const std::map<std::string, std::string>& headers) {
|
||||
bool success = (status >= 200 && status <= 299);
|
||||
std::move(callback).Run(success);
|
||||
}
|
||||
|
||||
bool GeminiService::SetAccessTokens(const std::string& access_token,
|
||||
const std::string& refresh_token) {
|
||||
access_token_ = access_token;
|
||||
|
||||
@@ -65,6 +65,7 @@ class GeminiService : public KeyedService {
|
||||
const std::string&,
|
||||
const std::string&,
|
||||
const std::string&)>;
|
||||
using ExecuteOrderCallback = base::OnceCallback<void(bool)>;
|
||||
|
||||
std::string GetOAuthClientUrl();
|
||||
void SetAuthToken(const std::string& auth_token);
|
||||
@@ -79,6 +80,13 @@ class GeminiService : public KeyedService {
|
||||
const std::string& symbol,
|
||||
const std::string& spend,
|
||||
GetOrderQuoteCallback callback);
|
||||
bool ExecuteOrder(const std::string& symbol,
|
||||
const std::string& side,
|
||||
const std::string& quantity,
|
||||
const std::string& price,
|
||||
const std::string& fee,
|
||||
const int quote_id,
|
||||
ExecuteOrderCallback callback);
|
||||
|
||||
private:
|
||||
base::SequencedTaskRunner* io_task_runner();
|
||||
@@ -109,6 +117,9 @@ class GeminiService : public KeyedService {
|
||||
void OnGetOrderQuote(GetOrderQuoteCallback callback,
|
||||
const int status, const std::string& body,
|
||||
const std::map<std::string, std::string>& headers);
|
||||
void OnOrderExecuted(ExecuteOrderCallback callback,
|
||||
const int status, const std::string& body,
|
||||
const std::map<std::string, std::string>& headers);
|
||||
|
||||
bool OAuthRequest(const GURL& url, const std::string& method,
|
||||
const std::string& post_data, URLRequestCallback callback,
|
||||
|
||||
@@ -889,7 +889,7 @@
|
||||
|
||||
<message name="IDS_GEMINI_WIDGET_CONNECT_TITLE" desc="">Purchase and trade with Gemini</message>
|
||||
<message name="IDS_GEMINI_WIDGET_CONNECT_COPY" desc="">Enable a Gemini connection to view your Gemini account balance and trade crypto.</message>
|
||||
<message name="IDS_GEMINI_WIDGET_CONNECT_BUTTON" desc="">Connect to Gemini.</message>
|
||||
<message name="IDS_GEMINI_WIDGET_CONNECT_BUTTON" desc="">Connect to Gemini</message>
|
||||
<message name="IDS_GEMINI_WIDGET_AUTH_INVALID_COPY" desc="">Your Gemini access token has expired and could not be refreshed. Please reconnect.</message>
|
||||
<message name="IDS_GEMINI_WIDGET_FAILED_TRADE" desc="">Unable to perform trade</message>
|
||||
<message name="IDS_GEMINI_WIDGET_ERROR" desc="">Something went wrong</message>
|
||||
@@ -898,6 +898,11 @@
|
||||
<message name="IDS_GEMINI_WIDGET_GET_QUOTE" desc="">Get a Quote</message>
|
||||
<message name="IDS_GEMINI_WIDGET_TRADE_LABEL" desc="">Trade</message>
|
||||
<message name="IDS_GEMINI_WIDGET_BALANCE_LABEL" desc="">Balance</message>
|
||||
<message name="IDS_GEMINI_WIDGET_BUYING" desc="">Buying</message>
|
||||
<message name="IDS_GEMINI_WIDGET_SELLING" desc="">Selling</message>
|
||||
<message name="IDS_GEMINI_WIDGET_BOUGHT" desc="">You bought</message>
|
||||
<message name="IDS_GEMINI_WIDGET_SOLD" desc="">You sold</message>
|
||||
<message name="IDS_GEMINI_WIDGET_PRICE" desc="">Price</message>
|
||||
|
||||
<!-- WebUI Brave Toolbar resources -->
|
||||
<message name="IDS_WALLETS_TITLE" desc="The toolbar title for the brave://wallets page">Crypto Wallets</message>
|
||||
|
||||
Reference in New Issue
Block a user