From 247da918a8025901eb798c99daf9595dd7a1cc54 Mon Sep 17 00:00:00 2001 From: Jaydeep Rajpurohit Date: Mon, 8 Aug 2022 06:59:43 +0530 Subject: [PATCH] exchanges: Add ByBit support (#887) * few fixes and add ratelimiter * adds test * revert configtest.json changes * configtest updated * WIP: adds public endpoint support * WIP: adds public endpoint support * adds public endpoint support * WIP: adds auth. endpoint support * adds test for auth. endpoint * fixes * adds auth. endpoint support * WIP: ws support * WIP * WIP * WIP * WIP * WIP * WIP * WIP * Testing * Complete WS spot testing * adds support for ws events * minor change * WIP: adds REST support for CoinMarginedFutures * Fixes * WIP: adds REST support for CoinMarginedFutures * Fixes * improvement in SPOT REST * Typo fix * WIP: add REST support for CMF Account API * minor fixes * WIP: add support for CMF conditional orders and few minor fixes * complete support for CMF conditional orders * adds support for public CMF endpoint * adds support for CMF position API * Complete REST CMF support * WIP * Testing REST CMF support * Testing REST CMF support * Testing REST CMF support completed * WIP: add support for UMF * completed non-auth UMF * WIP: add support for REST Auth. UMF * WIP: add support for REST Auth. UMF and some improvements * WIP * WIP * WIP * completed REST UMF * renaming * adds REST support for futures * add testcases for UMF and some optimizations * add testcases for futures * Testing UMF, futures and its changes * Fixes * Fixes after testing * WIP * WIP * WIP * completed ws USDT futures support * WIP: ws support for futures * fixes in WS futures * fixes in WS support * roll back changes made for WS CMF, USDT and Futures * fixes * WIP * WIP * fixes * Steps for new PR * WIP * WIP * WIP * WIP * complete PR setup * fixes for successfully running tests * update in symbol for futures pair in test file * WIP * Fixes in test file and other minor fix * fix testdata/configtest.json * reset CONTRIBUTORS file * review changes * remove unwanted file * remove redundant code * improvisation * adds comment for exported functions * remove unwanted TODO and commented code * fix * improvisation * fix * defined errors * improvisation * improvisation * improvisation * updates test * adds comment for exported types * review changes * review changes * fix * fixes * Changes for making BYBIT compatible with existing code base * Test file changes * Changes for making BYBIT compatible with existing code base * Changes for making BYBIT compatible with existing code base * fix lint issues * fix * review changes * review changes * review changes * review changes * review changes * review changes * review changes * review changes * review changes * review changes * WIP * add test cases for new API's * minor improvements * add missing API and their tests * minor fixes * add bybitTime * add bybitTimeSec, bybitTimeMilliSec, bybitTimeNanoSec and necessary support * fix GetTradeHistory function * error handling * test fixes * add GetServerTime API * adds GetHistoricCandlesExtended and review changes * test fixes * minor fix * integrating CMF Bybit recent change log * minor fixes * adds extractCurrencyPair * minor fixes * minor fix * review changes * adds variable declaration of error * review commit * adds embeddable type in API response for all API and integrate it * fixes * adds authentication WS connection * review changes * review changes * compatible changes * adds asset to GetWithdrawalsHistory * adds asset_type in rpc.proto * adds asset argument in gctcli withdrawal request command * improve error handling in exchange API error * web socket fix * review changes * improvements * improvements * minor fix * review changes * fixing wrapper issues * fixes * fixes * review changes * add test cases * fix for GetActiveOrders * lint fixes * fixes in websocket * adds wrapper testcases * adds wrapper testcases * adds wrapper testcases * fixes * fix issue with GetHistoricCandlesExtended * fix merge issues * improving error reporting * adds wrapper testcases and a minor fix * gctrpc changes * adds test cases fixes in websocket * review changes for ws * review changes in WS * fix gctrpc * merge fixes * review changes * WIP * updates pair in configs * adds new asset USDCMarginedFutures * adds URL const for USDCMarginedFutures * adds API support * minor fixes * adds kline API * minor fix * adds API * adds API * adds API * WIP * WIP * WIP * adds support for USDC auth requests to SendAuthHTTPRequest * adds SendUSDCAuthHTTPRequest * run test and fix them * rollback support added for Auth. USDC request inside SendAuthHTTPRequest * adds API and test cases * adds API and test cases * adds APIs and test cases * adds APIs * adds rate limit for USDC * adds USDCMarginedFutures to wrapper * adds USDC testcases in wrapper and fix few issues * minor test fixes * minor test fixes * fix lint issues * WIP * Merge changes * minor fixes * remove "else" and optimize * review changes * review changes * review changes * fix lint issue * merge fix * fix test * fix templates and run them * changes after merge * review changes and improvements * code improvement * fixes with respect to changes in API response in documentation * fixed review change in test * adds check in CancelExistingOrder * update exchange template * review changes * adds GetDepositAddress API * WIP: adds GetOrderHistory * complete GetOrderHistory * fixes * adds test case * fixes and add WithdrawFund API * WIP * WIP * updating all SendAuthHTTPRequest call * adds WithdrawCryptocurrencyFunds * update test cases * fix lint issues * fixes after merge * adds GetAvailableTransferChains and few fixes * minor fix in GetDepositAddress * minor fix with WS ping/pong handling * add ping handler for WS Auth. * fix typo mistake * update doc --- CONTRIBUTORS | 2 +- README.md | 7 +- .../exchanges_templates/bybit.tmpl | 106 + .../exchanges_trade_readme.tmpl | 1 + .../root_templates/root_readme.tmpl | 1 + cmd/exchange_template/wrapper_file.tmpl | 2 +- cmd/gctcli/commands.go | 24 +- config_example.json | 76 + docs/MULTICHAIN_TRANSFER_SUPPORT.md | 1 + docs/OHLCV.md | 1 + engine/currency_state_manager.md | 72 +- engine/exchange_manager.go | 3 + engine/exchange_manager_test.go | 2 +- engine/rpcserver.go | 6 +- exchanges/alphapoint/alphapoint_wrapper.go | 2 +- exchanges/asset/asset.go | 12 +- exchanges/asset/asset_test.go | 5 + exchanges/binance/binance_test.go | 2 +- exchanges/binance/binance_wrapper.go | 2 +- exchanges/bitfinex/bitfinex_wrapper.go | 2 +- exchanges/bitflyer/bitflyer_wrapper.go | 2 +- exchanges/bithumb/bithumb_wrapper.go | 2 +- exchanges/bitmex/bitmex_wrapper.go | 2 +- exchanges/bitstamp/bitstamp_wrapper.go | 2 +- exchanges/bittrex/bittrex_wrapper.go | 2 +- exchanges/btcmarkets/btcmarkets_wrapper.go | 2 +- exchanges/btse/btse_wrapper.go | 2 +- exchanges/bybit/README.md | 140 + exchanges/bybit/bybit.go | 998 +++++ exchanges/bybit/bybit_cfutures.go | 1310 +++++++ exchanges/bybit/bybit_futures.go | 757 ++++ exchanges/bybit/bybit_test.go | 3448 +++++++++++++++++ exchanges/bybit/bybit_types.go | 917 +++++ exchanges/bybit/bybit_ufutures.go | 1048 +++++ exchanges/bybit/bybit_usdcfutures.go | 1114 ++++++ exchanges/bybit/bybit_websocket.go | 555 +++ exchanges/bybit/bybit_wrapper.go | 2059 ++++++++++ exchanges/bybit/bybit_ws_cfutures.go | 789 ++++ exchanges/bybit/bybit_ws_futures.go | 645 +++ exchanges/bybit/bybit_ws_ufutures.go | 654 ++++ exchanges/bybit/futures_type.go | 965 +++++ exchanges/bybit/ratelimit.go | 403 ++ exchanges/coinbasepro/coinbasepro_wrapper.go | 2 +- exchanges/coinut/coinut_wrapper.go | 2 +- exchanges/exchange.go | 4 + exchanges/exchange_test.go | 4 + exchanges/exchange_types.go | 3 + exchanges/exmo/exmo_wrapper.go | 2 +- exchanges/ftx/ftx_wrapper.go | 2 +- exchanges/gateio/gateio_wrapper.go | 2 +- exchanges/gemini/gemini_wrapper.go | 2 +- exchanges/hitbtc/hitbtc_wrapper.go | 2 +- exchanges/huobi/huobi_wrapper.go | 2 +- exchanges/interfaces.go | 2 +- exchanges/itbit/itbit_wrapper.go | 2 +- exchanges/kraken/kraken_wrapper.go | 2 +- exchanges/lbank/lbank_wrapper.go | 2 +- .../localbitcoins/localbitcoins_wrapper.go | 2 +- exchanges/okgroup/okgroup_wrapper.go | 2 +- exchanges/poloniex/poloniex_wrapper.go | 2 +- exchanges/sharedtestvalues/customex.go | 2 +- exchanges/support.go | 1 + exchanges/trade/README.md | 1 + exchanges/yobit/yobit_wrapper.go | 2 +- exchanges/zb/zb_wrapper.go | 2 +- gctrpc/buf.lock | 4 +- gctrpc/rpc.pb.go | 3392 ++++++++-------- gctrpc/rpc.proto | 1 + gctrpc/rpc.swagger.json | 3 + testdata/configtest.json | 106 + 70 files changed, 17927 insertions(+), 1773 deletions(-) create mode 100644 cmd/documentation/exchanges_templates/bybit.tmpl create mode 100644 exchanges/bybit/README.md create mode 100644 exchanges/bybit/bybit.go create mode 100644 exchanges/bybit/bybit_cfutures.go create mode 100644 exchanges/bybit/bybit_futures.go create mode 100644 exchanges/bybit/bybit_test.go create mode 100644 exchanges/bybit/bybit_types.go create mode 100644 exchanges/bybit/bybit_ufutures.go create mode 100644 exchanges/bybit/bybit_usdcfutures.go create mode 100644 exchanges/bybit/bybit_websocket.go create mode 100644 exchanges/bybit/bybit_wrapper.go create mode 100644 exchanges/bybit/bybit_ws_cfutures.go create mode 100644 exchanges/bybit/bybit_ws_futures.go create mode 100644 exchanges/bybit/bybit_ws_ufutures.go create mode 100644 exchanges/bybit/futures_type.go create mode 100644 exchanges/bybit/ratelimit.go diff --git a/CONTRIBUTORS b/CONTRIBUTORS index d7c1b56f..941c0fde 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -3,8 +3,8 @@ Thanks to the following contributors: thrasher- | https://github.com/thrasher- shazbert | https://github.com/shazbert gloriousCode | https://github.com/gloriousCode -dependabot-preview[bot] | https://github.com/apps/dependabot-preview dependabot[bot] | https://github.com/apps/dependabot +dependabot-preview[bot] | https://github.com/apps/dependabot-preview xtda | https://github.com/xtda lrascao | https://github.com/lrascao Rots | https://github.com/Rots diff --git a/README.md b/README.md index aa84ada1..4e63bbc6 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader | Bittrex | Yes | Yes | NA | | BTCMarkets | Yes | Yes | NA | | BTSE | Yes | Yes | NA | +| Bybit | Yes | Yes | NA | | CoinbasePro | Yes | Yes | No| | COINUT | Yes | Yes | NA | | Exmo | Yes | NA | NA | @@ -144,10 +145,10 @@ Binaries will be published once the codebase reaches a stable condition. |User|Contribution Amount| |--|--| | [thrasher-](https://github.com/thrasher-) | 666 | -| [shazbert](https://github.com/shazbert) | 256 | -| [gloriousCode](https://github.com/gloriousCode) | 196 | +| [shazbert](https://github.com/shazbert) | 258 | +| [gloriousCode](https://github.com/gloriousCode) | 197 | +| [dependabot[bot]](https://github.com/apps/dependabot) | 89 | | [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 88 | -| [dependabot[bot]](https://github.com/apps/dependabot) | 88 | | [xtda](https://github.com/xtda) | 47 | | [lrascao](https://github.com/lrascao) | 27 | | [Rots](https://github.com/Rots) | 15 | diff --git a/cmd/documentation/exchanges_templates/bybit.tmpl b/cmd/documentation/exchanges_templates/bybit.tmpl new file mode 100644 index 00000000..ceabf2df --- /dev/null +++ b/cmd/documentation/exchanges_templates/bybit.tmpl @@ -0,0 +1,106 @@ +{{define "exchanges bybit" -}} +{{template "header" .}} +## Bybit Exchange + +### Current Features + ++ REST Support ++ Websocket Support + +### How to enable + ++ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example) + ++ Individual package example below: + +```go + // Exchanges will be abstracted out in further updates and examples will be + // supplied then +``` + +### How to do REST public/private calls + ++ If enabled via "configuration".json file the exchange will be added to the +IBotExchange array in the ```go var bot Bot``` and you will only be able to use +the wrapper interface functions for accessing exchange data. View routines.go +for an example of integration usage with GoCryptoTrader. Rudimentary example +below: + +main.go +```go +var b exchange.IBotExchange + +for i := range bot.Exchanges { + if bot.Exchanges[i].GetName() == "Bybit" { + b = bot.Exchanges[i] + } +} + +// Public calls - wrapper functions + +// Fetches current ticker information +tick, err := b.FetchTicker() +if err != nil { + // Handle error +} + +// Fetches current orderbook information +ob, err := b.FetchOrderbook() +if err != nil { + // Handle error +} + +// Private calls - wrapper functions - make sure your APIKEY and APISECRET are +// set and AuthenticatedAPISupport is set to true + +// Fetches current account information +accountInfo, err := b.GetAccountInfo() +if err != nil { + // Handle error +} +``` + ++ If enabled via individually importing package, rudimentary example below: + +```go +// Public calls + +// Fetches current ticker information +ticker, err := b.GetTicker() +if err != nil { + // Handle error +} + +// Fetches current orderbook information +ob, err := b.GetOrderBook() +if err != nil { + // Handle error +} + +// Private calls - make sure your APIKEY and APISECRET are set and +// AuthenticatedAPISupport is set to true + +// GetUserInfo returns account info +accountInfo, err := b.GetUserInfo(...) +if err != nil { + // Handle error +} + +// Submits an order and the exchange and returns its tradeID +tradeID, err := b.Trade(...) +if err != nil { + // Handle error +} +``` + +### How to do Websocket public/private calls + +```go + // Exchanges will be abstracted out in further updates and examples will be + // supplied then +``` + +### Please click GoDocs chevron above to view current GoDoc information for this package +{{template "contributions"}} +{{template "donations" .}} +{{end}} diff --git a/cmd/documentation/exchanges_templates/exchanges_trade_readme.tmpl b/cmd/documentation/exchanges_templates/exchanges_trade_readme.tmpl index 629e9a6a..6cbce671 100644 --- a/cmd/documentation/exchanges_templates/exchanges_trade_readme.tmpl +++ b/cmd/documentation/exchanges_templates/exchanges_trade_readme.tmpl @@ -51,6 +51,7 @@ _b in this context is an `IBotExchange` implemented struct_ | Bittrex | Yes | Yes | No | | BTCMarkets | Yes | Yes | No | | BTSE | Yes | Yes | No | +| Bybit | Yes | Yes | Yes | | CoinbasePro | Yes | Yes | No| | COINUT | Yes | Yes | No | | Exmo | Yes | NA | No | diff --git a/cmd/documentation/root_templates/root_readme.tmpl b/cmd/documentation/root_templates/root_readme.tmpl index 8a1b47e2..97a84f20 100644 --- a/cmd/documentation/root_templates/root_readme.tmpl +++ b/cmd/documentation/root_templates/root_readme.tmpl @@ -29,6 +29,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader | Bittrex | Yes | Yes | NA | | BTCMarkets | Yes | Yes | NA | | BTSE | Yes | Yes | NA | +| Bybit | Yes | Yes | NA | | CoinbasePro | Yes | Yes | No| | COINUT | Yes | Yes | NA | | Exmo | Yes | NA | NA | diff --git a/cmd/exchange_template/wrapper_file.tmpl b/cmd/exchange_template/wrapper_file.tmpl index 908b13c6..0bb59561 100644 --- a/cmd/exchange_template/wrapper_file.tmpl +++ b/cmd/exchange_template/wrapper_file.tmpl @@ -388,7 +388,7 @@ func ({{.Variable}} *{{.CapitalName}}) GetFundingHistory(ctx context.Context) ([ } // GetWithdrawalsHistory returns previous withdrawals data -func ({{.Variable}} *{{.CapitalName}}) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func ({{.Variable}} *{{.CapitalName}}) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index 64042692..af3bbaa5 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -3014,6 +3014,10 @@ var withdrawalRequestCommand = &cli.Command{ Name: "currency", Usage: "", }, + &cli.StringFlag{ + Name: "asset", + Usage: "the asset type of the currency pair", + }, }, Action: withdrawlRequestByExchangeID, }, @@ -3089,7 +3093,7 @@ func withdrawlRequestByExchangeID(c *cli.Context) error { return cli.ShowSubcommandHelp(c) } - var exchange, currency string + var exchange, currency, assetType string if c.IsSet("exchange") { exchange = c.String("exchange") } else { @@ -3126,6 +3130,15 @@ func withdrawlRequestByExchangeID(c *cli.Context) error { if c.IsSet("currency") { currency = c.String("currency") } + + if c.IsSet("asset") { + assetType = c.String("asset") + } + + assetType = strings.ToLower(assetType) + if !validAsset(assetType) { + return errInvalidAsset + } } conn, cancel, err := setupClient(c) @@ -3138,10 +3151,11 @@ func withdrawlRequestByExchangeID(c *cli.Context) error { result, err := client.WithdrawalEventsByExchange(c.Context, &gctrpc.WithdrawalEventsByExchangeRequest{ - Exchange: exchange, - Id: ID, - Limit: int32(limit), - Currency: currency, + Exchange: exchange, + Id: ID, + Limit: int32(limit), + Currency: currency, + AssetType: assetType, }, ) if err != nil { diff --git a/config_example.json b/config_example.json index 44a9c091..98c4de98 100644 --- a/config_example.json +++ b/config_example.json @@ -972,6 +972,82 @@ } ] }, + { + "name": "Bybit", + "enabled": true, + "verbose": false, + "httpTimeout": 15000000000, + "websocketResponseCheckTimeout": 30000000, + "websocketResponseMaxLimit": 7000000000, + "websocketTrafficTimeout": 30000000000, + "websocketOrderbookBufferLimit": 5, + "baseCurrencies": "USD", + "currencyPairs": { + "requestFormat": { + "uppercase": true + }, + "configFormat": { + "uppercase": true + }, + "useGlobalFormat": true, + "assetTypes": [ + "spot" + ], + "pairs": { + "spot": { + "enabled": "BTC-USDT,ETH-USDT", + "available": "BTC-USDT,ETH-USDT,XRP-USDT,EOS-USDT,ETH-BTC,XRP-BTC,DOT-USDT,XLM-USDT,LTC-USDT,DOGE-USDT,BIT-USDT,CHZ-USDT,AXS-USDT,MANA-USDT,DYDX-USDT,MKR-USDT,COMP-USDT" + } + } + }, + "api": { + "authenticatedSupport": false, + "authenticatedWebsocketApiSupport": false, + "endpoints": { + "url": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API", + "urlSecondary": "NON_DEFAULT_HTTP_LINK_TO_EXCHANGE_API", + "websocketURL": "NON_DEFAULT_HTTP_LINK_TO_WEBSOCKET_EXCHANGE_API" + }, + "credentials": { + "key": "Key", + "secret": "Secret" + }, + "credentialsValidator": { + "requiresKey": true, + "requiresSecret": true + } + }, + "features": { + "supports": { + "restAPI": true, + "restCapabilities": { + "tickerBatching": true, + "autoPairUpdates": true + }, + "websocketAPI": true, + "websocketCapabilities": {} + }, + "enabled": { + "autoPairUpdates": true, + "websocketAPI": true + } + }, + "bankAccounts": [ + { + "enabled": false, + "bankName": "", + "bankAddress": "", + "bankPostalCode": "", + "bankPostalCity": "", + "bankCountry": "", + "accountName": "", + "accountNumber": "", + "swiftCode": "", + "iban": "", + "supportedCurrencies": "" + } + ] + }, { "name": "COINUT", "enabled": true, diff --git a/docs/MULTICHAIN_TRANSFER_SUPPORT.md b/docs/MULTICHAIN_TRANSFER_SUPPORT.md index 066bce44..95f7b390 100644 --- a/docs/MULTICHAIN_TRANSFER_SUPPORT.md +++ b/docs/MULTICHAIN_TRANSFER_SUPPORT.md @@ -52,6 +52,7 @@ $ ./gctcli withdrawcryptofunds --exchange=ftx --currency=USDT --address=TJU9piX2 | Bittrex | No | No | NA | | BTCMarkets | No | No| NA | | BTSE | No | No | Only through website | +| Bybit | Yes | Yes | | | CoinbasePro | No | No | No| | COINUT | No | No | NA | | Exmo | Yes | Yes | Addresses must be created via their website first | diff --git a/docs/OHLCV.md b/docs/OHLCV.md index d0e81128..f2a20ff2 100644 --- a/docs/OHLCV.md +++ b/docs/OHLCV.md @@ -74,6 +74,7 @@ A helper tool [cmd/dbseed](../cmd/dbseed/README.md) has been created for assisti | BTC Markets | Y | | Bittrex | | | BTSE | Y | +| Bybit | Y | | Coinbase Pro | Y | | Coinut | | | Exmo | | diff --git a/engine/currency_state_manager.md b/engine/currency_state_manager.md index b3ec29aa..e84b08aa 100644 --- a/engine/currency_state_manager.md +++ b/engine/currency_state_manager.md @@ -1,22 +1,22 @@ -# GoCryptoTrader package Currency state manager - - - - -[![Build Status](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml) -[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE) -[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/engine/currency_state_manager) -[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master) -[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader) - - -This currency_state_manager package is part of the GoCryptoTrader codebase. - -## This is still in active development - -You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader). - -Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk) +# GoCryptoTrader package Currency state manager + + + + +[![Build Status](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml) +[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE) +[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/engine/currency_state_manager) +[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader) + + +This currency_state_manager package is part of the GoCryptoTrader codebase. + +## This is still in active development + +You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader). + +Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk) ## Current Features for Currency state manager + The state manager keeps currency states up to date, which include: @@ -27,22 +27,22 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader + This allows for an internal state check to compliment internal and external strategies. + +## Contribution + +Please feel free to submit any pull requests or suggest any desired features to be added. + +When submitting a PR, please abide by our coding guidelines: + ++ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). ++ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. ++ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md). ++ Pull requests need to be based on and opened against the `master` branch. -## Contribution - -Please feel free to submit any pull requests or suggest any desired features to be added. - -When submitting a PR, please abide by our coding guidelines: - -+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). -+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. -+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md). -+ Pull requests need to be based on and opened against the `master` branch. - -## Donations - - - -If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to: - +## Donations + + + +If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to: + ***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc*** diff --git a/engine/exchange_manager.go b/engine/exchange_manager.go index 3bf2d3ed..0d0e7e4f 100644 --- a/engine/exchange_manager.go +++ b/engine/exchange_manager.go @@ -16,6 +16,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/bittrex" "github.com/thrasher-corp/gocryptotrader/exchanges/btcmarkets" "github.com/thrasher-corp/gocryptotrader/exchanges/btse" + "github.com/thrasher-corp/gocryptotrader/exchanges/bybit" "github.com/thrasher-corp/gocryptotrader/exchanges/coinbasepro" "github.com/thrasher-corp/gocryptotrader/exchanges/coinut" "github.com/thrasher-corp/gocryptotrader/exchanges/exmo" @@ -163,6 +164,8 @@ func (m *ExchangeManager) NewExchangeByName(name string) (exchange.IBotExchange, exch = new(btcmarkets.BTCMarkets) case "btse": exch = new(btse.BTSE) + case "bybit": + exch = new(bybit.Bybit) case "coinut": exch = new(coinut.COINUT) case "exmo": diff --git a/engine/exchange_manager_test.go b/engine/exchange_manager_test.go index 14e9593a..69ce9bb0 100644 --- a/engine/exchange_manager_test.go +++ b/engine/exchange_manager_test.go @@ -82,7 +82,7 @@ func TestExchangeManagerRemoveExchange(t *testing.T) { func TestNewExchangeByName(t *testing.T) { m := SetupExchangeManager() - exchanges := []string{"binance", "bitfinex", "bitflyer", "bithumb", "bitmex", "bitstamp", "bittrex", "btc markets", "btse", "coinut", "exmo", "coinbasepro", "ftx", "gateio", "gemini", "hitbtc", "huobi", "itbit", "kraken", "lbank", "localbitcoins", "okcoin international", "okex", "poloniex", "yobit", "zb", "fake"} + exchanges := []string{"binance", "bitfinex", "bitflyer", "bithumb", "bitmex", "bitstamp", "bittrex", "btc markets", "btse", "bybit", "coinut", "exmo", "coinbasepro", "ftx", "gateio", "gemini", "hitbtc", "huobi", "itbit", "kraken", "lbank", "localbitcoins", "okcoin international", "okex", "poloniex", "yobit", "zb", "fake"} for i := range exchanges { exch, err := m.NewExchangeByName(exchanges[i]) if err != nil && exchanges[i] != "fake" { diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 1642111a..9907489c 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -1863,7 +1863,11 @@ func (s *RPCServer) WithdrawalEventsByExchange(ctx context.Context, r *gctrpc.Wi } c := currency.NewCode(strings.ToUpper(r.Currency)) - ret, err := exch.GetWithdrawalsHistory(ctx, c) + a, err := asset.New(r.AssetType) + if err != nil { + return nil, err + } + ret, err := exch.GetWithdrawalsHistory(ctx, c, a) if err != nil { return nil, err } diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go index b5f4a4b3..45c1389f 100644 --- a/exchanges/alphapoint/alphapoint_wrapper.go +++ b/exchanges/alphapoint/alphapoint_wrapper.go @@ -236,7 +236,7 @@ func (a *Alphapoint) GetFundingHistory(ctx context.Context) ([]exchange.FundHist } // GetWithdrawalsHistory returns previous withdrawals data -func (a *Alphapoint) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (a *Alphapoint) GetWithdrawalsHistory(ctx context.Context, c currency.Code, as asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/asset/asset.go b/exchanges/asset/asset.go index 580db288..b1bd9947 100644 --- a/exchanges/asset/asset.go +++ b/exchanges/asset/asset.go @@ -33,9 +33,10 @@ const ( DownsideProfitContract CoinMarginedFutures USDTMarginedFutures + USDCMarginedFutures - futuresFlag = PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures - supportedFlag = Spot | Margin | MarginFunding | Index | Binary | PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures + futuresFlag = PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures | USDCMarginedFutures + supportedFlag = Spot | Margin | MarginFunding | Index | Binary | PerpetualContract | PerpetualSwap | Futures | UpsideProfitContract | DownsideProfitContract | CoinMarginedFutures | USDTMarginedFutures | USDCMarginedFutures spot = "spot" margin = "margin" @@ -49,10 +50,11 @@ const ( downsideProfitContract = "downsideprofitcontract" coinMarginedFutures = "coinmarginedfutures" usdtMarginedFutures = "usdtmarginedfutures" + usdcMarginedFutures = "usdcmarginedfutures" ) var ( - supportedList = Items{Spot, Margin, MarginFunding, Index, Binary, PerpetualContract, PerpetualSwap, Futures, UpsideProfitContract, DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures} + supportedList = Items{Spot, Margin, MarginFunding, Index, Binary, PerpetualContract, PerpetualSwap, Futures, UpsideProfitContract, DownsideProfitContract, CoinMarginedFutures, USDTMarginedFutures, USDCMarginedFutures} ) // Supported returns a list of supported asset types @@ -87,6 +89,8 @@ func (a Item) String() string { return coinMarginedFutures case USDTMarginedFutures: return usdtMarginedFutures + case USDCMarginedFutures: + return usdcMarginedFutures default: return "" } @@ -180,6 +184,8 @@ func New(input string) (Item, error) { return CoinMarginedFutures, nil case usdtMarginedFutures: return USDTMarginedFutures, nil + case usdcMarginedFutures: + return USDCMarginedFutures, nil default: return 0, fmt.Errorf("%w '%v', only supports %s", ErrNotSupported, diff --git a/exchanges/asset/asset_test.go b/exchanges/asset/asset_test.go index 46429085..9a575fa1 100644 --- a/exchanges/asset/asset_test.go +++ b/exchanges/asset/asset_test.go @@ -92,6 +92,7 @@ func TestNew(t *testing.T) { {Input: "DownsideProfitContract", Expected: DownsideProfitContract}, {Input: "CoinMarginedFutures", Expected: CoinMarginedFutures}, {Input: "USDTMarginedFutures", Expected: USDTMarginedFutures}, + {Input: "USDCMarginedFutures", Expected: USDCMarginedFutures}, } for x := range cases { @@ -177,6 +178,10 @@ func TestIsFutures(t *testing.T) { item: USDTMarginedFutures, isFutures: true, }, + { + item: USDCMarginedFutures, + isFutures: true, + }, } for _, s := range scenarios { testScenario := s diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index 329a963a..99b6fe0c 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -2073,7 +2073,7 @@ func TestWithdrawHistory(t *testing.T) { if areTestAPIKeysSet() && !canManipulateRealOrders && !mockTests { t.Skip("API keys set, canManipulateRealOrders false, skipping test") } - _, err := b.GetWithdrawalsHistory(context.Background(), currency.ETH) + _, err := b.GetWithdrawalsHistory(context.Background(), currency.ETH, asset.Spot) switch { case areTestAPIKeysSet() && err != nil: t.Error("GetWithdrawalsHistory() error", err) diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index 40bef77d..20fe8bee 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -818,7 +818,7 @@ func (b *Binance) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory } // GetWithdrawalsHistory returns previous withdrawals data -func (b *Binance) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (b *Binance) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { w, err := b.WithdrawHistory(ctx, c, "", time.Time{}, time.Time{}, 0, 10000) if err != nil { return nil, err diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index ff70771e..0e6fe7aa 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -551,7 +551,7 @@ func (b *Bitfinex) GetFundingHistory(ctx context.Context) ([]exchange.FundHistor } // GetWithdrawalsHistory returns previous withdrawals data -func (b *Bitfinex) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (b *Bitfinex) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/bitflyer/bitflyer_wrapper.go b/exchanges/bitflyer/bitflyer_wrapper.go index 11bd7827..b35a82a8 100644 --- a/exchanges/bitflyer/bitflyer_wrapper.go +++ b/exchanges/bitflyer/bitflyer_wrapper.go @@ -338,7 +338,7 @@ func (b *Bitflyer) GetFundingHistory(_ context.Context) ([]exchange.FundHistory, } // GetWithdrawalsHistory returns previous withdrawals data -func (b *Bitflyer) GetWithdrawalsHistory(_ context.Context, _ currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (b *Bitflyer) GetWithdrawalsHistory(_ context.Context, _ currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index b4b869a1..ffd45711 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -426,7 +426,7 @@ func (b *Bithumb) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory } // GetWithdrawalsHistory returns previous withdrawals data -func (b *Bithumb) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (b *Bithumb) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index f2bc1fd7..590533c4 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -488,7 +488,7 @@ func (b *Bitmex) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, } // GetWithdrawalsHistory returns previous withdrawals data -func (b *Bitmex) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (b *Bitmex) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index fbe71100..0e34d486 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -484,7 +484,7 @@ func (b *Bitstamp) GetFundingHistory(ctx context.Context) ([]exchange.FundHistor } // GetWithdrawalsHistory returns previous withdrawals data -func (b *Bitstamp) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (b *Bitstamp) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index dad979f8..dafabf4c 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -506,7 +506,7 @@ func (b *Bittrex) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory } // GetWithdrawalsHistory returns previous withdrawals data -func (b *Bittrex) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (b *Bittrex) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index 5e4e9683..dd8b306c 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -490,7 +490,7 @@ func (b *BTCMarkets) GetFundingHistory(ctx context.Context) ([]exchange.FundHist } // GetWithdrawalsHistory returns previous withdrawals data -func (b *BTCMarkets) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (b *BTCMarkets) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/btse/btse_wrapper.go b/exchanges/btse/btse_wrapper.go index d4021021..db2d9b9d 100644 --- a/exchanges/btse/btse_wrapper.go +++ b/exchanges/btse/btse_wrapper.go @@ -459,7 +459,7 @@ func (b *BTSE) withinLimits(pair currency.Pair, amount float64) bool { } // GetWithdrawalsHistory returns previous withdrawals data -func (b *BTSE) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (b *BTSE) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/bybit/README.md b/exchanges/bybit/README.md new file mode 100644 index 00000000..2bb42dc0 --- /dev/null +++ b/exchanges/bybit/README.md @@ -0,0 +1,140 @@ +# GoCryptoTrader package Bybit + + + + +[![Build Status](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/thrasher-corp/gocryptotrader/actions/workflows/tests.yml) +[![Software License](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE) +[![GoDoc](https://godoc.org/github.com/thrasher-corp/gocryptotrader?status.svg)](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/bybit) +[![Coverage Status](http://codecov.io/github/thrasher-corp/gocryptotrader/coverage.svg?branch=master)](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/thrasher-corp/gocryptotrader)](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader) + + +This bybit package is part of the GoCryptoTrader codebase. + +## This is still in active development + +You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader). + +Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk) + +## Bybit Exchange + +### Current Features + ++ REST Support ++ Websocket Support + +### How to enable + ++ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example) + ++ Individual package example below: + +```go + // Exchanges will be abstracted out in further updates and examples will be + // supplied then +``` + +### How to do REST public/private calls + ++ If enabled via "configuration".json file the exchange will be added to the +IBotExchange array in the ```go var bot Bot``` and you will only be able to use +the wrapper interface functions for accessing exchange data. View routines.go +for an example of integration usage with GoCryptoTrader. Rudimentary example +below: + +main.go +```go +var b exchange.IBotExchange + +for i := range bot.Exchanges { + if bot.Exchanges[i].GetName() == "Bybit" { + b = bot.Exchanges[i] + } +} + +// Public calls - wrapper functions + +// Fetches current ticker information +tick, err := b.FetchTicker() +if err != nil { + // Handle error +} + +// Fetches current orderbook information +ob, err := b.FetchOrderbook() +if err != nil { + // Handle error +} + +// Private calls - wrapper functions - make sure your APIKEY and APISECRET are +// set and AuthenticatedAPISupport is set to true + +// Fetches current account information +accountInfo, err := b.GetAccountInfo() +if err != nil { + // Handle error +} +``` + ++ If enabled via individually importing package, rudimentary example below: + +```go +// Public calls + +// Fetches current ticker information +ticker, err := b.GetTicker() +if err != nil { + // Handle error +} + +// Fetches current orderbook information +ob, err := b.GetOrderBook() +if err != nil { + // Handle error +} + +// Private calls - make sure your APIKEY and APISECRET are set and +// AuthenticatedAPISupport is set to true + +// GetUserInfo returns account info +accountInfo, err := b.GetUserInfo(...) +if err != nil { + // Handle error +} + +// Submits an order and the exchange and returns its tradeID +tradeID, err := b.Trade(...) +if err != nil { + // Handle error +} +``` + +### How to do Websocket public/private calls + +```go + // Exchanges will be abstracted out in further updates and examples will be + // supplied then +``` + +### Please click GoDocs chevron above to view current GoDoc information for this package + +## Contribution + +Please feel free to submit any pull requests or suggest any desired features to be added. + +When submitting a PR, please abide by our coding guidelines: + ++ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)). ++ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. ++ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md). ++ Pull requests need to be based on and opened against the `master` branch. + +## Donations + + + +If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to: + +***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc*** diff --git a/exchanges/bybit/bybit.go b/exchanges/bybit/bybit.go new file mode 100644 index 00000000..0458ccbd --- /dev/null +++ b/exchanges/bybit/bybit.go @@ -0,0 +1,998 @@ +package bybit + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "sort" + "strconv" + "strings" + "time" + + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/crypto" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/request" +) + +// Bybit is the overarching type across this package +type Bybit struct { + exchange.Base +} + +const ( + bybitAPIURL = "https://api.bybit.com" + defaultRecvWindow = "5000" // 5000 milli second + + sideBuy = "Buy" + sideSell = "Sell" + + // Public endpoints + bybitSpotGetSymbols = "/spot/v1/symbols" + bybitOrderBook = "/spot/quote/v1/depth" + bybitMergedOrderBook = "/spot/quote/v1/depth/merged" + bybitRecentTrades = "/spot/quote/v1/trades" + bybitCandlestickChart = "/spot/quote/v1/kline" + bybit24HrsChange = "/spot/quote/v1/ticker/24hr" + bybitLastTradedPrice = "/spot/quote/v1/ticker/price" + bybitBestBidAskPrice = "/spot/quote/v1/ticker/book_ticker" + + // Authenticated endpoints + bybitSpotOrder = "/spot/v1/order" // create, query, cancel + bybitFastCancelSpotOrder = "/spot/v1/order/fast" + bybitBatchCancelSpotOrder = "/spot/order/batch-cancel" + bybitFastBatchCancelSpotOrder = "/spot/order/batch-fast-cancel" + bybitOpenOrder = "/spot/v1/open-orders" + bybitPastOrder = "/spot/v1/history-orders" + bybitTradeHistory = "/spot/v1/myTrades" + bybitWalletBalance = "/spot/v1/account" + bybitServerTime = "/spot/v1/time" + + // Account asset endpoint + bybitGetDepositAddress = "/asset/v1/private/deposit/address" + bybitWithdrawFund = "/asset/v1/private/withdraw" +) + +// GetAllSpotPairs gets all pairs on the exchange +func (by *Bybit) GetAllSpotPairs(ctx context.Context) ([]PairData, error) { + resp := struct { + Data []PairData `json:"result"` + Error + }{} + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestSpot, bybitSpotGetSymbols, publicSpotRate, &resp) +} + +func processOB(ob [][2]string) ([]orderbook.Item, error) { + o := make([]orderbook.Item, len(ob)) + for x := range ob { + var price, amount float64 + amount, err := strconv.ParseFloat(ob[x][1], 64) + if err != nil { + return nil, err + } + price, err = strconv.ParseFloat(ob[x][0], 64) + if err != nil { + return nil, err + } + o[x] = orderbook.Item{ + Price: price, + Amount: amount, + } + } + return o, nil +} + +func constructOrderbook(o *orderbookResponse) (*Orderbook, error) { + var ( + s Orderbook + err error + ) + s.Bids, err = processOB(o.Data.Bids) + if err != nil { + return nil, err + } + s.Asks, err = processOB(o.Data.Asks) + if err != nil { + return nil, err + } + s.Time = o.Data.Time.Time() + return &s, err +} + +// GetOrderBook gets orderbook for a given market with a given depth (default depth 100) +func (by *Bybit) GetOrderBook(ctx context.Context, symbol string, depth int64) (*Orderbook, error) { + var o orderbookResponse + strDepth := "100" // default depth + if depth > 0 && depth < 100 { + strDepth = strconv.FormatInt(depth, 10) + } + + params := url.Values{} + params.Set("symbol", symbol) + params.Set("limit", strDepth) + path := common.EncodeURLValues(bybitOrderBook, params) + err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &o) + if err != nil { + return nil, err + } + + return constructOrderbook(&o) +} + +// GetMergedOrderBook gets orderbook for a given market with a given depth (default depth 100) +func (by *Bybit) GetMergedOrderBook(ctx context.Context, symbol string, scale, depth int64) (*Orderbook, error) { + var o orderbookResponse + params := url.Values{} + if scale > 0 { + params.Set("scale", strconv.FormatInt(scale, 10)) + } + + strDepth := "100" // default depth + if depth > 0 && depth <= 200 { + strDepth = strconv.FormatInt(depth, 10) + } + + params.Set("symbol", symbol) + params.Set("limit", strDepth) + path := common.EncodeURLValues(bybitMergedOrderBook, params) + err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &o) + if err != nil { + return nil, err + } + + return constructOrderbook(&o) +} + +// GetTrades gets recent trades from the exchange +func (by *Bybit) GetTrades(ctx context.Context, symbol string, limit int64) ([]TradeItem, error) { + resp := struct { + Data []struct { + Price float64 `json:"price,string"` + Time bybitTimeMilliSec `json:"time"` + Quantity float64 `json:"qty,string"` + IsBuyerMaker bool `json:"isBuyerMaker"` + } `json:"result"` + Error + }{} + + params := url.Values{} + params.Set("symbol", symbol) + + strLimit := "60" // default limit + if limit > 0 && limit < 60 { + strLimit = strconv.FormatInt(limit, 10) + } + params.Set("limit", strLimit) + path := common.EncodeURLValues(bybitRecentTrades, params) + err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &resp) + if err != nil { + return nil, err + } + + trades := make([]TradeItem, len(resp.Data)) + for x := range resp.Data { + var tradeSide string + if resp.Data[x].IsBuyerMaker { + tradeSide = order.Buy.String() + } else { + tradeSide = order.Sell.String() + } + + trades[x] = TradeItem{ + CurrencyPair: symbol, + Price: resp.Data[x].Price, + Side: tradeSide, + Volume: resp.Data[x].Quantity, + Time: resp.Data[x].Time.Time(), + } + } + return trades, nil +} + +// GetKlines data returns the kline data for a specific symbol. Limitation: It only returns latest 3500 candles irrespective of interval passed +func (by *Bybit) GetKlines(ctx context.Context, symbol, period string, limit int64, start, end time.Time) ([]KlineItem, error) { + resp := struct { + Data [][]interface{} `json:"result"` + Error + }{} + + v := url.Values{} + v.Add("symbol", symbol) + v.Add("interval", period) + if !start.IsZero() { + v.Add("startTime", strconv.FormatInt(start.UnixMilli(), 10)) + } + if !end.IsZero() { + v.Add("endTime", strconv.FormatInt(end.UnixMilli(), 10)) + } + if limit <= 0 || limit > 1000 { + limit = 1000 + } + v.Add("limit", strconv.FormatInt(limit, 10)) + + path := common.EncodeURLValues(bybitCandlestickChart, v) + if err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &resp); err != nil { + return nil, err + } + + klines := make([]KlineItem, len(resp.Data)) + for x := range resp.Data { + if len(resp.Data[x]) != 11 { + return klines, fmt.Errorf("%v GetKlines: invalid response, array length not as expected, check api docs for updates", by.Name) + } + var kline KlineItem + var err error + startTime, ok := resp.Data[x][0].(float64) + if !ok { + return klines, fmt.Errorf("%v GetKlines: %w for StartTime", by.Name, errTypeAssert) + } + kline.StartTime = time.UnixMilli(int64(startTime)) + + open, ok := resp.Data[x][1].(string) + if !ok { + return klines, fmt.Errorf("%v GetKlines: %w for Open", by.Name, errTypeAssert) + } + kline.Open, err = strconv.ParseFloat(open, 64) + if err != nil { + return klines, fmt.Errorf("%v GetKlines: %w for Open", by.Name, errStrParsing) + } + + high, ok := resp.Data[x][2].(string) + if !ok { + return klines, fmt.Errorf("%v GetKlines: %w for High", by.Name, errTypeAssert) + } + kline.High, err = strconv.ParseFloat(high, 64) + if err != nil { + return klines, fmt.Errorf("%v GetKlines: %w for High", by.Name, errStrParsing) + } + + low, ok := resp.Data[x][3].(string) + if !ok { + return klines, fmt.Errorf("%v GetKlines: %w for Low", by.Name, errTypeAssert) + } + kline.Low, err = strconv.ParseFloat(low, 64) + if err != nil { + return klines, fmt.Errorf("%v GetKlines: %w for Low", by.Name, errStrParsing) + } + + c, ok := resp.Data[x][4].(string) + if !ok { + return klines, fmt.Errorf("%v GetKlines: %w for Close", by.Name, errTypeAssert) + } + kline.Close, err = strconv.ParseFloat(c, 64) + if err != nil { + return klines, fmt.Errorf("%v GetKlines: %w for Close", by.Name, errStrParsing) + } + + volume, ok := resp.Data[x][5].(string) + if !ok { + return klines, fmt.Errorf("%v GetKlines: %w for Volume", by.Name, errTypeAssert) + } + kline.Volume, err = strconv.ParseFloat(volume, 64) + if err != nil { + return klines, fmt.Errorf("%v GetKlines: %w for Volume", by.Name, errStrParsing) + } + + endTime, ok := resp.Data[x][6].(float64) + if !ok { + return klines, fmt.Errorf("%v GetKlines: %w for EndTime", by.Name, errTypeAssert) + } + kline.EndTime = time.UnixMilli(int64(endTime)) + quoteAssetVolume, ok := resp.Data[x][7].(string) + if !ok { + return klines, fmt.Errorf("%v GetKlines: %w for QuoteAssetVolume", by.Name, errTypeAssert) + } + kline.QuoteAssetVolume, err = strconv.ParseFloat(quoteAssetVolume, 64) + if err != nil { + return klines, fmt.Errorf("%v GetKlines: %w for QuoteAssetVolume", by.Name, errStrParsing) + } + + tradesCount, ok := resp.Data[x][8].(float64) + if !ok { + return klines, fmt.Errorf("%v GetKlines: %w for TradesCount", by.Name, errTypeAssert) + } + kline.TradesCount = int64(tradesCount) + + takerBaseVolume, ok := resp.Data[x][9].(string) + if !ok { + return klines, fmt.Errorf("%v GetKlines: %w for TakerBaseVolume", by.Name, errTypeAssert) + } + kline.TakerBaseVolume, err = strconv.ParseFloat(takerBaseVolume, 64) + if err != nil { + return klines, fmt.Errorf("%v GetKlines: %w for TakerBaseVolume", by.Name, errStrParsing) + } + + takerQuoteVolume, ok := resp.Data[x][10].(string) + if !ok { + return klines, fmt.Errorf("%v GetKlines: %w for TakerQuoteVolume", by.Name, errTypeAssert) + } + kline.TakerQuoteVolume, err = strconv.ParseFloat(takerQuoteVolume, 64) + if err != nil { + return klines, fmt.Errorf("%v GetKlines: %w for TakerQuoteVolume", by.Name, errStrParsing) + } + + klines[x] = kline + } + return klines, nil +} + +// Get24HrsChange returns price change statistics for the last 24 hours +// If symbol not passed then it will return price change statistics for all pairs +func (by *Bybit) Get24HrsChange(ctx context.Context, symbol string) ([]PriceChangeStats, error) { + type priceChangeStats struct { + Time bybitTimeMilliSec `json:"time"` + Symbol string `json:"symbol"` + BestBidPrice float64 `json:"bestBidPrice,string"` + BestAskPrice float64 `json:"bestAskPrice,string"` + LastPrice float64 `json:"lastPrice,string"` + OpenPrice float64 `json:"openPrice,string"` + HighPrice float64 `json:"highPrice,string"` + LowPrice float64 `json:"lowPrice,string"` + Volume float64 `json:"volume,string"` + QuoteVolume float64 `json:"quoteVolume,string"` + } + + var stats []PriceChangeStats + if symbol != "" { + resp := struct { + Data priceChangeStats `json:"result"` + Error + }{} + + params := url.Values{} + params.Set("symbol", symbol) + path := common.EncodeURLValues(bybit24HrsChange, params) + err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &resp) + if err != nil { + return nil, err + } + + stats = append(stats, PriceChangeStats{ + resp.Data.Time.Time(), + resp.Data.Symbol, + resp.Data.BestAskPrice, + resp.Data.BestAskPrice, + resp.Data.LastPrice, + resp.Data.OpenPrice, + resp.Data.HighPrice, + resp.Data.LowPrice, + resp.Data.Volume, + resp.Data.QuoteVolume, + }) + } else { + resp := struct { + Data []priceChangeStats `json:"result"` + Error + }{} + + err := by.SendHTTPRequest(ctx, exchange.RestSpot, bybit24HrsChange, publicSpotRate, &resp) + if err != nil { + return nil, err + } + + for x := range resp.Data { + stats = append(stats, PriceChangeStats{ + resp.Data[x].Time.Time(), + resp.Data[x].Symbol, + resp.Data[x].BestAskPrice, + resp.Data[x].BestAskPrice, + resp.Data[x].LastPrice, + resp.Data[x].OpenPrice, + resp.Data[x].HighPrice, + resp.Data[x].LowPrice, + resp.Data[x].Volume, + resp.Data[x].QuoteVolume, + }) + } + } + return stats, nil +} + +// GetLastTradedPrice returns last trading price +// If symbol not passed then it will return last trading price for all pairs +func (by *Bybit) GetLastTradedPrice(ctx context.Context, symbol string) ([]LastTradePrice, error) { + var lastTradePrices []LastTradePrice + if symbol != "" { + resp := struct { + Data LastTradePrice `json:"result"` + Error + }{} + + params := url.Values{} + params.Set("symbol", symbol) + path := common.EncodeURLValues(bybitLastTradedPrice, params) + err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &resp) + if err != nil { + return nil, err + } + lastTradePrices = append(lastTradePrices, LastTradePrice{ + resp.Data.Symbol, + resp.Data.Price, + }) + } else { + resp := struct { + Data []LastTradePrice `json:"result"` + Error + }{} + + err := by.SendHTTPRequest(ctx, exchange.RestSpot, bybitLastTradedPrice, publicSpotRate, &resp) + if err != nil { + return nil, err + } + for x := range resp.Data { + lastTradePrices = append(lastTradePrices, LastTradePrice{ + resp.Data[x].Symbol, + resp.Data[x].Price, + }) + } + } + return lastTradePrices, nil +} + +// GetBestBidAskPrice returns best BID and ASK price +// If symbol not passed then it will return best BID and ASK price for all pairs +func (by *Bybit) GetBestBidAskPrice(ctx context.Context, symbol string) ([]TickerData, error) { + type bestTicker struct { + Symbol string `json:"symbol"` + BidPrice float64 `json:"bidPrice,string"` + BidQuantity float64 `json:"bidQty,string"` + AskPrice float64 `json:"askPrice,string"` + AskQuantity float64 `json:"askQty,string"` + Time bybitTimeMilliSec `json:"time"` + } + + var tickers []TickerData + if symbol != "" { + resp := struct { + Data bestTicker `json:"result"` + Error + }{} + + params := url.Values{} + params.Set("symbol", symbol) + path := common.EncodeURLValues(bybitBestBidAskPrice, params) + err := by.SendHTTPRequest(ctx, exchange.RestSpot, path, publicSpotRate, &resp) + if err != nil { + return nil, err + } + tickers = append(tickers, TickerData{ + resp.Data.Symbol, + resp.Data.BidPrice, + resp.Data.BidQuantity, + resp.Data.AskPrice, + resp.Data.AskQuantity, + resp.Data.Time.Time(), + }) + } else { + resp := struct { + Data []bestTicker `json:"result"` + Error + }{} + + err := by.SendHTTPRequest(ctx, exchange.RestSpot, bybitBestBidAskPrice, publicSpotRate, &resp) + if err != nil { + return nil, err + } + for x := range resp.Data { + tickers = append(tickers, TickerData{ + resp.Data[x].Symbol, + resp.Data[x].BidPrice, + resp.Data[x].BidQuantity, + resp.Data[x].AskPrice, + resp.Data[x].AskQuantity, + resp.Data[x].Time.Time(), + }) + } + } + return tickers, nil +} + +// CreatePostOrder create and post order +func (by *Bybit) CreatePostOrder(ctx context.Context, o *PlaceOrderRequest) (*PlaceOrderResponse, error) { + if o == nil { + return nil, errInvalidOrderRequest + } + + params := url.Values{} + params.Set("symbol", o.Symbol) + params.Set("qty", strconv.FormatFloat(o.Quantity, 'f', -1, 64)) + params.Set("side", o.Side) + params.Set("type", o.TradeType) + + if o.TimeInForce != "" { + params.Set("timeInForce", o.TimeInForce) + } + if (o.TradeType == BybitRequestParamsOrderLimit || o.TradeType == BybitRequestParamsOrderLimitMaker) && o.Price == 0 { + return nil, errMissingPrice + } + if o.Price != 0 { + params.Set("price", strconv.FormatFloat(o.Price, 'f', -1, 64)) + } + if o.OrderLinkID != "" { + params.Set("orderLinkId", o.OrderLinkID) + } + + resp := struct { + Data PlaceOrderResponse `json:"result"` + Error + }{} + return &resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, bybitSpotOrder, params, nil, &resp, privateSpotRate) +} + +// QueryOrder returns order data based upon orderID or orderLinkID +func (by *Bybit) QueryOrder(ctx context.Context, orderID, orderLinkID string) (*QueryOrderResponse, error) { + if orderID == "" && orderLinkID == "" { + return nil, errOrderOrOrderLinkIDMissing + } + + params := url.Values{} + if orderID != "" { + params.Set("orderId", orderID) + } + if orderLinkID != "" { + params.Set("orderLinkId", orderLinkID) + } + + resp := struct { + Data QueryOrderResponse `json:"result"` + Error + }{} + return &resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitSpotOrder, params, nil, &resp, privateSpotRate) +} + +// CancelExistingOrder cancels existing order based upon orderID or orderLinkID +func (by *Bybit) CancelExistingOrder(ctx context.Context, orderID, orderLinkID string) (*CancelOrderResponse, error) { + if orderID == "" && orderLinkID == "" { + return nil, errOrderOrOrderLinkIDMissing + } + + params := url.Values{} + if orderID != "" { + params.Set("orderId", orderID) + } + if orderLinkID != "" { + params.Set("orderLinkId", orderLinkID) + } + + resp := struct { + Data CancelOrderResponse `json:"result"` + Error + }{} + err := by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, bybitSpotOrder, params, nil, &resp, privateSpotRate) + if err != nil { + return nil, err + } + + // In case open order is cancelled, this endpoint return status as NEW whereas if we try to cancel a already cancelled order then it's status is returned as CANCELED without any error. So this check is added to prevent this obscurity. + if resp.Data.Status == "CANCELED" { + return nil, fmt.Errorf("%s order already cancelled", resp.Data.OrderID) + } + return &resp.Data, nil +} + +// FastCancelExistingOrder cancels existing order based upon orderID or orderLinkID +func (by *Bybit) FastCancelExistingOrder(ctx context.Context, symbol, orderID, orderLinkID string) (bool, error) { + resp := struct { + Data struct { + IsCancelled bool `json:"isCancelled"` + } `json:"result"` + Error + }{} + + if orderID == "" && orderLinkID == "" { + return resp.Data.IsCancelled, errOrderOrOrderLinkIDMissing + } + + params := url.Values{} + if symbol == "" { + return resp.Data.IsCancelled, errSymbolMissing + } + params.Set("symbolId", symbol) + + if orderID != "" { + params.Set("orderId", orderID) + } + if orderLinkID != "" { + params.Set("orderLinkId", orderLinkID) + } + + return resp.Data.IsCancelled, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, bybitFastCancelSpotOrder, params, nil, &resp, privateSpotRate) +} + +// BatchCancelOrder cancels orders in batch based upon symbol, side or orderType +func (by *Bybit) BatchCancelOrder(ctx context.Context, symbol, side, orderTypes string) (bool, error) { + params := url.Values{} + if symbol != "" { + params.Set("symbol", symbol) + } + if side != "" { + params.Set("side", side) + } + if orderTypes != "" { + params.Set("orderTypes", orderTypes) + } + + resp := struct { + Result struct { + Success bool `json:"success"` + } `json:"result"` + Error + }{} + return resp.Result.Success, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, bybitBatchCancelSpotOrder, params, nil, &resp, privateSpotRate) +} + +// BatchFastCancelOrder cancels orders in batch based upon symbol, side or orderType +func (by *Bybit) BatchFastCancelOrder(ctx context.Context, symbol, side, orderTypes string) (bool, error) { + params := url.Values{} + if symbol != "" { + params.Set("symbol", symbol) + } + if side != "" { + params.Set("side", side) + } + if orderTypes != "" { + params.Set("orderTypes", orderTypes) + } + + resp := struct { + Result struct { + Success bool `json:"success"` + } `json:"result"` + Error + }{} + return resp.Result.Success, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, bybitFastBatchCancelSpotOrder, params, nil, &resp, privateSpotRate) +} + +// BatchCancelOrderByIDs cancels orders in batch based on comma separated order id's +func (by *Bybit) BatchCancelOrderByIDs(ctx context.Context, orderIDs []string) (bool, error) { + params := url.Values{} + if len(orderIDs) == 0 { + return false, errEmptyOrderIDs + } + params.Set("orderIds", strings.Join(orderIDs, ",")) + + resp := struct { + Result struct { + Success bool `json:"success"` + } `json:"result"` + Error + }{} + return resp.Result.Success, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodDelete, bybitFastBatchCancelSpotOrder, params, nil, &resp, privateSpotRate) +} + +// ListOpenOrders returns all open orders +func (by *Bybit) ListOpenOrders(ctx context.Context, symbol, orderID string, limit int64) ([]QueryOrderResponse, error) { + params := url.Values{} + if symbol != "" { + params.Set("symbol", symbol) + } + if orderID != "" { + params.Set("orderId", orderID) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + resp := struct { + Data []QueryOrderResponse `json:"result"` + Error + }{} + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitOpenOrder, params, nil, &resp, privateSpotRate) +} + +// GetPastOrders returns all past orders from history +func (by *Bybit) GetPastOrders(ctx context.Context, symbol, orderID string, limit int64, startTime, endTime time.Time) ([]QueryOrderResponse, error) { + params := url.Values{} + if symbol != "" { + params.Set("symbol", symbol) + } + if orderID != "" { + params.Set("orderId", orderID) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if !startTime.IsZero() { + params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10)) + } + if !endTime.IsZero() { + params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10)) + } + resp := struct { + Data []QueryOrderResponse `json:"result"` + Error + }{} + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitPastOrder, params, nil, &resp, privateSpotRate) +} + +// GetTradeHistory returns user trades +func (by *Bybit) GetTradeHistory(ctx context.Context, limit int64, symbol, fromID, toID, orderID string, startTime, endTime time.Time) ([]HistoricalTrade, error) { + params := url.Values{} + if symbol != "" { + params.Set("symbol", symbol) + } + if limit != 0 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if fromID != "" { + params.Set("fromTicketId", fromID) + } + if toID != "" { + params.Set("toTicketId", toID) + } + if orderID != "" { + params.Set("orderId", orderID) + } + if !startTime.IsZero() { + params.Set("startTime", strconv.FormatInt(startTime.UnixMilli(), 10)) + } + if !endTime.IsZero() { + params.Set("endTime", strconv.FormatInt(endTime.UnixMilli(), 10)) + } + resp := struct { + Data []HistoricalTrade `json:"result"` + Error + }{} + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitTradeHistory, params, nil, &resp, privateSpotRate) +} + +// GetWalletBalance returns user wallet balance +func (by *Bybit) GetWalletBalance(ctx context.Context) ([]Balance, error) { + resp := struct { + Data struct { + Balances []Balance `json:"balances"` + } `json:"result"` + Error + }{} + return resp.Data.Balances, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitWalletBalance, url.Values{}, nil, &resp, privateSpotRate) +} + +// GetSpotServerTime returns server time +func (by *Bybit) GetSpotServerTime(ctx context.Context) (time.Time, error) { + resp := struct { + Result struct { + ServerTime int64 `json:"serverTime"` + } `json:"result"` + Error + }{} + err := by.SendHTTPRequest(ctx, exchange.RestSpot, bybitServerTime, publicSpotRate, &resp) + return time.UnixMilli(resp.Result.ServerTime), err +} + +// GetDepositAddressForCurrency returns deposit wallet address based upon the coin. +func (by *Bybit) GetDepositAddressForCurrency(ctx context.Context, coin string) (DepositWalletInfo, error) { + resp := struct { + Result DepositWalletInfo `json:"result"` + Error + }{} + + params := url.Values{} + if coin == "" { + return resp.Result, errInvalidCoin + } + params.Set("coin", strings.ToUpper(coin)) + return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, bybitGetDepositAddress, params, nil, &resp, publicSpotRate) +} + +// WithdrawFund creates request for fund withdrawal. +func (by *Bybit) WithdrawFund(ctx context.Context, coin, chain, address, tag, amount string) (string, error) { + resp := struct { + Data struct { + ID string `json:"id"` + } `json:"result"` + Error + }{} + + params := make(map[string]interface{}) + params["coin"] = coin + params["chain"] = chain + params["address"] = address + params["amount"] = amount + if tag != "" { + params["tag"] = tag + } + return resp.Data.ID, by.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, bybitWithdrawFund, nil, params, &resp, privateSpotRate) +} + +// SendHTTPRequest sends an unauthenticated request +func (by *Bybit) SendHTTPRequest(ctx context.Context, ePath exchange.URL, path string, f request.EndpointLimit, result UnmarshalTo) error { + endpointPath, err := by.API.Endpoints.GetURL(ePath) + if err != nil { + return err + } + + err = by.SendPayload(ctx, f, func() (*request.Item, error) { + return &request.Item{ + Method: http.MethodGet, + Path: endpointPath + path, + Result: result, + Verbose: by.Verbose, + HTTPDebugging: by.HTTPDebugging, + HTTPRecording: by.HTTPRecording}, nil + }) + if err != nil { + return err + } + return result.GetError() +} + +// SendAuthHTTPRequest sends an authenticated HTTP request +// If payload is non-nil then request is considered to be JSON +func (by *Bybit) SendAuthHTTPRequest(ctx context.Context, ePath exchange.URL, method, path string, params url.Values, jsonPayload map[string]interface{}, result UnmarshalTo, f request.EndpointLimit) error { + creds, err := by.GetCredentials(ctx) + if err != nil { + return err + } + + if result == nil { + result = &Error{} + } + + endpointPath, err := by.API.Endpoints.GetURL(ePath) + if err != nil { + return err + } + + if params == nil && jsonPayload == nil { + params = url.Values{} + } + + if jsonPayload != nil { + jsonPayload["recvWindow"] = defaultRecvWindow + } else if params.Get("recvWindow") == "" { + params.Set("recvWindow", defaultRecvWindow) + } + + err = by.SendPayload(ctx, f, func() (*request.Item, error) { + var ( + payload []byte + hmacSignedStr string + ) + headers := make(map[string]string) + + if jsonPayload != nil { + headers["Content-Type"] = "application/json" + jsonPayload["timestamp"] = strconv.FormatInt(time.Now().UnixMilli(), 10) + jsonPayload["api_key"] = creds.Key + hmacSignedStr, err = getJSONRequestSignature(jsonPayload, creds.Secret) + if err != nil { + return nil, err + } + jsonPayload["sign"] = hmacSignedStr + payload, err = json.Marshal(jsonPayload) + if err != nil { + return nil, err + } + } else { + params.Set("timestamp", strconv.FormatInt(time.Now().UnixMilli(), 10)) + params.Set("api_key", creds.Key) + hmacSignedStr, err = getSign(params.Encode(), creds.Secret) + if err != nil { + return nil, err + } + headers["Content-Type"] = "application/x-www-form-urlencoded" + switch method { + case http.MethodPost: + params.Set("sign", hmacSignedStr) + payload = []byte(params.Encode()) + default: + path = common.EncodeURLValues(path, params) + path += "&sign=" + hmacSignedStr + } + } + + return &request.Item{ + Method: method, + Path: endpointPath + path, + Headers: headers, + Body: bytes.NewBuffer(payload), + Result: &result, + AuthRequest: true, + Verbose: by.Verbose, + HTTPDebugging: by.HTTPDebugging, + HTTPRecording: by.HTTPRecording}, nil + }) + if err != nil { + return err + } + return result.GetError() +} + +// Error defines all error information for each request +type Error struct { + ReturnCode int64 `json:"ret_code"` + ReturnMsg string `json:"ret_msg"` + ExtCode string `json:"ext_code"` + ExtMsg string `json:"ext_info"` +} + +// GetError checks and returns an error if it is supplied. +func (e Error) GetError() error { + if e.ReturnCode != 0 && e.ReturnMsg != "" { + return errors.New(e.ReturnMsg) + } + if e.ExtCode != "" && e.ExtMsg != "" { + return errors.New(e.ExtMsg) + } + return nil +} + +func getSide(side string) order.Side { + switch side { + case sideBuy: + return order.Buy + case sideSell: + return order.Sell + default: + return order.UnknownSide + } +} + +func getTradeType(tradeType string) order.Type { + switch tradeType { + case BybitRequestParamsOrderLimit: + return order.Limit + case BybitRequestParamsOrderMarket: + return order.Market + case BybitRequestParamsOrderLimitMaker: + return order.Limit + default: + return order.UnknownType + } +} + +func getOrderStatus(status string) order.Status { + switch status { + case "NEW": + return order.New + case "PARTIALLY_FILLED": + return order.PartiallyFilled + case "FILLED": + return order.Filled + case "CANCELED": + return order.Cancelled + case "PENDING_CANCEL": + return order.PendingCancel + case "PENDING_NEW": + return order.Pending + case "REJECTED": + return order.Rejected + default: + return order.UnknownStatus + } +} + +func getJSONRequestSignature(payload map[string]interface{}, secret string) (string, error) { + payloadArr := make([]string, len(payload)) + var i int + for p := range payload { + payloadArr[i] = p + i++ + } + sort.Strings(payloadArr) + var signStr string + for _, key := range payloadArr { + if value, found := payload[key]; found { + if v, ok := value.(string); ok { + signStr += key + "=" + v + "&" + } + } else { + return "", errors.New("non-string payload parameter not expected") + } + } + return getSign(signStr[:len(signStr)-1], secret) +} + +func getSign(sign, secret string) (string, error) { + hmacSigned, err := crypto.GetHMAC(crypto.HashSHA256, []byte(sign), []byte(secret)) + if err != nil { + return "", err + } + return crypto.HexEncodeToString(hmacSigned), nil +} diff --git a/exchanges/bybit/bybit_cfutures.go b/exchanges/bybit/bybit_cfutures.go new file mode 100644 index 00000000..a8c54bda --- /dev/null +++ b/exchanges/bybit/bybit_cfutures.go @@ -0,0 +1,1310 @@ +package bybit + +import ( + "context" + "math" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/currency" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" +) + +const ( + bybitFuturesAPIVersion = "/v2" + + // public endpoint + cfuturesOrderbook = "/public/orderBook/L2" + cfuturesKline = "/public/kline/list" + cfuturesSymbolPriceTicker = "/public/tickers" + cfuturesRecentTrades = "/public/trading-records" + cfuturesSymbolInfo = "/public/symbols" + cfuturesMarkPriceKline = "/public/mark-price-kline" + cfuturesIndexKline = "/public/index-price-kline" + cfuturesIndexPremiumKline = "/public/premium-index-kline" + cfuturesOpenInterest = "/public/open-interest" + cfuturesBigDeal = "/public/big-deal" + cfuturesAccountRatio = "/public/account-ratio" + cfuturesGetRiskLimit = "/public/risk-limit/list" + cfuturesGetLastFundingRate = "/public/funding/prev-funding-rate" + cfuturesGetServerTime = "/public/time" + cfuturesGetAnnouncement = "/public/announcement" + + // auth endpoint + cfuturesCreateOrder = "/private/order/create" + cfuturesGetActiveOrders = "/private/order/list" + cfuturesCancelActiveOrder = "/private/order/cancel" + cfuturesCancelAllActiveOrders = "/private/order/cancelAll" + cfuturesReplaceActiveOrder = "/private/order/replace" + cfuturesGetActiveRealtimeOrders = "/private/order" + + cfuturesCreateConditionalOrder = "/private/stop-order/create" + cfuturesGetConditionalOrders = "/private/stop-order/list" + cfuturesCancelConditionalOrder = "/private/stop-order/cancel" + cfuturesCancelAllConditionalOrders = "/private/stop-order/cancelAll" + cfuturesReplaceConditionalOrder = "/private/stop-order/replace" + cfuturesGetConditionalRealtimeOrders = "/private/stop-order" + + cfuturesPosition = "/private/position/list" + cfuturesUpdateMargin = "/private/position/change-position-margin" + cfuturesSetTrading = "/private/position/trading-stop" + cfuturesSetLeverage = "/private/position/leverage/save" + cfuturesGetTrades = "/private/execution/list" + cfuturesGetClosedTrades = "/private/trade/closed-pnl/list" + cfuturesSwitchPosition = "/private/tpsl/switch-mode" + cfuturesSwitchMargin = "/private/position/switch-isolated" + cfuturesGetTradingFeeRate = "/private/position/fee-rate" + + cfuturesSetRiskLimit = "/private/position/risk-limit" + cfuturesGetMyLastFundingFee = "/private/funding/prev-funding" + cfuturesPredictFundingRate = "/private/funding/predicted-funding" + cfuturesGetAPIKeyInfo = "/private/account/api-key" + cfuturesGetLiquidityContributionPoints = "/private/account/lcp" + + cfuturesGetWalletBalance = "/private/wallet/balance" + cfuturesGetWalletFundRecords = "/private/wallet/fund/records" + cfuturesGetWalletWithdrawalRecords = "/private/wallet/withdraw/list" + cfuturesGetAssetExchangeRecords = "/private/exchange-order/list" +) + +// GetFuturesOrderbook gets orderbook data for CoinMarginedFutures. +func (by *Bybit) GetFuturesOrderbook(ctx context.Context, symbol currency.Pair) (*Orderbook, error) { + var resp Orderbook + data := struct { + Result []OrderbookData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return nil, err + } + params.Set("symbol", symbolValue) + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesOrderbook, params) + err = by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &data) + if err != nil { + return nil, err + } + + for x := range data.Result { + switch data.Result[x].Side { + case sideBuy: + resp.Bids = append(resp.Bids, orderbook.Item{ + Price: data.Result[x].Price, + Amount: data.Result[x].Size, + }) + case sideSell: + resp.Asks = append(resp.Asks, orderbook.Item{ + Price: data.Result[x].Price, + Amount: data.Result[x].Size, + }) + default: + return nil, errInvalidSide + } + } + return &resp, nil +} + +// GetFuturesKlineData gets futures kline data for CoinMarginedFutures. +func (by *Bybit) GetFuturesKlineData(ctx context.Context, symbol currency.Pair, interval string, limit int64, startTime time.Time) ([]FuturesCandleStickWithStringParam, error) { + resp := struct { + Data []FuturesCandleStickWithStringParam `json:"result"` + Error + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return resp.Data, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if !common.StringDataCompare(validFuturesIntervals, interval) { + return resp.Data, errInvalidInterval + } + params.Set("interval", interval) + params.Set("from", strconv.FormatInt(startTime.Unix(), 10)) + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesKline, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &resp) +} + +// GetFuturesSymbolPriceTicker gets price ticker for symbol. +func (by *Bybit) GetFuturesSymbolPriceTicker(ctx context.Context, symbol currency.Pair) ([]SymbolPriceTicker, error) { + resp := struct { + Data []SymbolPriceTicker `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesSymbolPriceTicker, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &resp) +} + +// GetPublicTrades gets past public trades for CoinMarginedFutures. +func (by *Bybit) GetPublicTrades(ctx context.Context, symbol currency.Pair, limit int64) ([]FuturesPublicTradesData, error) { + resp := struct { + Data []FuturesPublicTradesData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if limit > 0 && limit <= 1000 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesRecentTrades, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &resp) +} + +// GetSymbolsInfo gets all symbol pair information for CoinMarginedFutures. +func (by *Bybit) GetSymbolsInfo(ctx context.Context) ([]SymbolInfo, error) { + resp := struct { + Data []SymbolInfo `json:"result"` + Error + }{} + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, bybitFuturesAPIVersion+cfuturesSymbolInfo, publicFuturesRate, &resp) +} + +// GetMarkPriceKline gets mark price kline data +func (by *Bybit) GetMarkPriceKline(ctx context.Context, symbol currency.Pair, interval string, limit int64, startTime time.Time) ([]MarkPriceKlineData, error) { + resp := struct { + Data []MarkPriceKlineData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if !common.StringDataCompare(validFuturesIntervals, interval) { + return resp.Data, errInvalidInterval + } + params.Set("interval", interval) + if startTime.IsZero() { + return resp.Data, errInvalidStartTime + } + params.Set("from", strconv.FormatInt(startTime.Unix(), 10)) + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesMarkPriceKline, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &resp) +} + +// GetIndexPriceKline gets index price kline data +func (by *Bybit) GetIndexPriceKline(ctx context.Context, symbol currency.Pair, interval string, limit int64, startTime time.Time) ([]IndexPriceKlineData, error) { + resp := struct { + Data []IndexPriceKlineData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if !common.StringDataCompare(validFuturesIntervals, interval) { + return resp.Data, errInvalidInterval + } + params.Set("interval", interval) + if startTime.IsZero() { + return resp.Data, errInvalidStartTime + } + params.Set("from", strconv.FormatInt(startTime.Unix(), 10)) + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesIndexKline, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &resp) +} + +// GetPremiumIndexPriceKline gets premium index price kline data +func (by *Bybit) GetPremiumIndexPriceKline(ctx context.Context, symbol currency.Pair, interval string, limit int64, startTime time.Time) ([]IndexPriceKlineData, error) { + resp := struct { + Data []IndexPriceKlineData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if !common.StringDataCompare(validFuturesIntervals, interval) { + return resp.Data, errInvalidInterval + } + params.Set("interval", interval) + if startTime.IsZero() { + return resp.Data, errInvalidStartTime + } + params.Set("from", strconv.FormatInt(startTime.Unix(), 10)) + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesIndexPremiumKline, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &resp) +} + +// GetOpenInterest gets open interest data for a symbol. +func (by *Bybit) GetOpenInterest(ctx context.Context, symbol currency.Pair, period string, limit int64) ([]OpenInterestData, error) { + resp := struct { + Data []OpenInterestData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if !common.StringDataCompare(validFuturesPeriods, period) { + return resp.Data, errInvalidPeriod + } + params.Set("period", period) + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesOpenInterest, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &resp) +} + +// GetLatestBigDeal gets filled orders worth more than 500,000 USD within the last 24h for symbol. +func (by *Bybit) GetLatestBigDeal(ctx context.Context, symbol currency.Pair, limit int64) ([]BigDealData, error) { + resp := struct { + Data []BigDealData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if limit > 0 && limit <= 1000 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesBigDeal, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &resp) +} + +// GetAccountRatio gets user accounts long-short ratio. +func (by *Bybit) GetAccountRatio(ctx context.Context, symbol currency.Pair, period string, limit int64) ([]AccountRatioData, error) { + resp := struct { + Data []AccountRatioData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if limit > 0 && limit <= 500 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if !common.StringDataCompare(validFuturesPeriods, period) { + return resp.Data, errInvalidPeriod + } + params.Set("period", period) + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesAccountRatio, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &resp) +} + +// GetRiskLimit returns risk limit +func (by *Bybit) GetRiskLimit(ctx context.Context, symbol currency.Pair) ([]RiskInfoWithStringParam, error) { + resp := struct { + Data []RiskInfoWithStringParam `json:"result"` + Error + }{} + + params := url.Values{} + if !symbol.IsEmpty() { + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + } + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesGetRiskLimit, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &resp) +} + +// GetLastFundingRate returns latest generated funding fee +func (by *Bybit) GetLastFundingRate(ctx context.Context, symbol currency.Pair) (FundingInfo, error) { + resp := struct { + Data FundingInfo `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + path := common.EncodeURLValues(bybitFuturesAPIVersion+cfuturesGetLastFundingRate, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, path, publicFuturesRate, &resp) +} + +// GetFuturesServerTime returns Bybit server time in seconds +func (by *Bybit) GetFuturesServerTime(ctx context.Context) (time.Time, error) { + resp := struct { + TimeNow float64 `json:"time_now,string"` + Error + }{} + + err := by.SendHTTPRequest(ctx, exchange.RestCoinMargined, bybitFuturesAPIVersion+cfuturesGetServerTime, publicFuturesRate, &resp) + if err != nil { + return time.Time{}, err + } + sec, dec := math.Modf(resp.TimeNow) + return time.Unix(int64(sec), int64(dec*(1e9))), nil +} + +// GetAnnouncement returns announcements in the last 30 days in reverse order +func (by *Bybit) GetAnnouncement(ctx context.Context) ([]AnnouncementInfo, error) { + resp := struct { + Data []AnnouncementInfo `json:"result"` + Error + }{} + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestCoinMargined, bybitFuturesAPIVersion+cfuturesGetAnnouncement, publicFuturesRate, &resp) +} + +// CreateCoinFuturesOrder sends a new futures order to the exchange +func (by *Bybit) CreateCoinFuturesOrder(ctx context.Context, symbol currency.Pair, side, orderType, timeInForce, + orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string, + quantity, price, takeProfit, stopLoss float64, closeOnTrigger, reduceOnly bool) (FuturesOrderDataResp, error) { + resp := struct { + Data FuturesOrderDataResp `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + params.Set("side", side) + params.Set("order_type", orderType) + if quantity <= 0 { + return resp.Data, errInvalidQuantity + } + params.Set("qty", strconv.FormatFloat(quantity, 'f', -1, 64)) + + if price != 0 { + params.Set("price", strconv.FormatFloat(price, 'f', -1, 64)) + } + if timeInForce == "" { + return resp.Data, errInvalidTimeInForce + } + params.Set("time_in_force", timeInForce) + + if closeOnTrigger { + params.Set("close_on_trigger", "true") + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if takeProfit != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64)) + } + if stopLoss != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + if reduceOnly { + params.Set("reduce_only", "true") + } + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesCreateOrder, params, nil, &resp, cFuturesCreateOrderRate) +} + +// GetActiveCoinFuturesOrders gets list of futures active orders +func (by *Bybit) GetActiveCoinFuturesOrders(ctx context.Context, symbol currency.Pair, orderStatus, direction, cursor string, limit int64) ([]FuturesActiveOrderResp, error) { + resp := struct { + Result struct { + Data []FuturesActiveOrderResp `json:"data"` + Cursor string `json:"cursor"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Result.Data, err + } + params.Set("symbol", symbolValue) + if orderStatus != "" { + params.Set("order_status", orderStatus) + } + if direction != "" { + params.Set("direction", direction) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if cursor != "" { + params.Set("cursor", cursor) + } + return resp.Result.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetActiveOrders, params, nil, &resp, cFuturesGetActiveOrderRate) +} + +// CancelActiveCoinFuturesOrders cancels futures unfilled or partially filled orders +func (by *Bybit) CancelActiveCoinFuturesOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID string) (FuturesOrderDataResp, error) { + resp := struct { + Data FuturesOrderDataResp `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if orderID == "" && orderLinkID == "" { + return resp.Data, errOrderOrOrderLinkIDMissing + } + if orderID != "" { + params.Set("order_id", orderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesCancelActiveOrder, params, nil, &resp, cFuturesCancelActiveOrderRate) +} + +// CancelAllActiveCoinFuturesOrders cancels all futures unfilled or partially filled orders +func (by *Bybit) CancelAllActiveCoinFuturesOrders(ctx context.Context, symbol currency.Pair) ([]FuturesOrderDataResp, error) { + resp := struct { + Data []FuturesOrderDataResp `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesCancelAllActiveOrders, params, nil, &resp, cFuturesCancelAllActiveOrderRate) +} + +// ReplaceActiveCoinFuturesOrders modify unfilled or partially filled orders +func (by *Bybit) ReplaceActiveCoinFuturesOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string, + updatedQty int64, updatedPrice, takeProfitPrice, stopLossPrice float64) (string, error) { + resp := struct { + Data struct { + OrderID string `json:"order_id"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return "", err + } + params.Set("symbol", symbolValue) + if orderID == "" && orderLinkID == "" { + return "", errOrderOrOrderLinkIDMissing + } + if orderID != "" { + params.Set("order_id", orderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if updatedQty != 0 { + params.Set("p_r_qty", strconv.FormatInt(updatedQty, 10)) + } + if updatedPrice != 0 { + params.Set("p_r_price", strconv.FormatFloat(updatedPrice, 'f', -1, 64)) + } + if takeProfitPrice != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfitPrice, 'f', -1, 64)) + } + if stopLossPrice != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLossPrice, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + return resp.Data.OrderID, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesReplaceActiveOrder, params, nil, &resp, cFuturesReplaceActiveOrderRate) +} + +// GetActiveRealtimeCoinOrders query real time order data +func (by *Bybit) GetActiveRealtimeCoinOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID string) ([]FuturesActiveRealtimeOrder, error) { + var data []FuturesActiveRealtimeOrder + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return data, err + } + params.Set("symbol", symbolValue) + if orderID != "" { + params.Set("order_id", orderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + + if orderID == "" && orderLinkID == "" { + resp := struct { + Data []FuturesActiveRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetActiveRealtimeOrders, params, nil, &resp, cFuturesGetRealtimeOrderRate) + if err != nil { + return data, err + } + data = append(data, resp.Data...) + } else { + resp := struct { + Data FuturesActiveRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetActiveRealtimeOrders, params, nil, &resp, cFuturesGetRealtimeOrderRate) + if err != nil { + return data, err + } + data = append(data, resp.Data) + } + return data, nil +} + +// CreateConditionalCoinFuturesOrder sends a new conditional futures order to the exchange +func (by *Bybit) CreateConditionalCoinFuturesOrder(ctx context.Context, symbol currency.Pair, side, orderType, timeInForce, + orderLinkID, takeProfitTriggerBy, stopLossTriggerBy, triggerBy string, + quantity, price, takeProfit, stopLoss, basePrice, stopPrice float64, closeOnTrigger bool) (FuturesConditionalOrderResp, error) { + resp := struct { + Data FuturesConditionalOrderResp `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + params.Set("side", side) + params.Set("order_type", orderType) + if quantity <= 0 { + return resp.Data, errInvalidQuantity + } + params.Set("qty", strconv.FormatFloat(quantity, 'f', -1, 64)) + + if price != 0 { + params.Set("price", strconv.FormatFloat(price, 'f', -1, 64)) + } + if basePrice <= 0 { + return resp.Data, errInvalidBasePrice + } + params.Set("base_price", strconv.FormatFloat(basePrice, 'f', -1, 64)) + + if stopPrice <= 0 { + return resp.Data, errInvalidStopPrice + } + params.Set("stop_px", strconv.FormatFloat(stopPrice, 'f', -1, 64)) + + if timeInForce == "" { + return resp.Data, errInvalidTimeInForce + } + params.Set("time_in_force", timeInForce) + + if triggerBy != "" { + params.Set("trigger_by", triggerBy) + } + if closeOnTrigger { + params.Set("close_on_trigger", "true") + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if takeProfit != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64)) + } + if stopLoss != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesCreateConditionalOrder, params, nil, &resp, cFuturesCreateConditionalOrderRate) +} + +// GetConditionalCoinFuturesOrders gets list of futures conditional orders +func (by *Bybit) GetConditionalCoinFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderStatus, direction, cursor string, limit int64) ([]CoinFuturesConditionalOrders, error) { + resp := struct { + Result struct { + Data []CoinFuturesConditionalOrders `json:"data"` + Cursor string `json:"cursor"` + } `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Result.Data, err + } + params.Set("symbol", symbolValue) + if stopOrderStatus != "" { + params.Set("stop_order_status", stopOrderStatus) + } + if direction != "" { + params.Set("direction", direction) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if cursor != "" { + params.Set("cursor", cursor) + } + return resp.Result.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetConditionalOrders, params, nil, &resp, cFuturesGetConditionalOrderRate) +} + +// CancelConditionalCoinFuturesOrders cancels untriggered conditional orders +func (by *Bybit) CancelConditionalCoinFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID string) (string, error) { + resp := struct { + Data struct { + StopOrderID string `json:"stop_order_id"` + } `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return "", err + } + params.Set("symbol", symbolValue) + if stopOrderID == "" && orderLinkID == "" { + return "", errStopOrderOrOrderLinkIDMissing + } + if stopOrderID != "" { + params.Set("stop_order_id", stopOrderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + return resp.Data.StopOrderID, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesCancelConditionalOrder, params, nil, &resp, cFuturesCancelConditionalOrderRate) +} + +// CancelAllConditionalCoinFuturesOrders cancels all untriggered conditional orders +func (by *Bybit) CancelAllConditionalCoinFuturesOrders(ctx context.Context, symbol currency.Pair) ([]FuturesCancelOrderResp, error) { + resp := struct { + Data []FuturesCancelOrderResp `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesCancelAllConditionalOrders, params, nil, &resp, cFuturesCancelAllConditionalOrderRate) +} + +// ReplaceConditionalCoinFuturesOrders modify unfilled or partially filled conditional orders +func (by *Bybit) ReplaceConditionalCoinFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string, + updatedQty, updatedPrice, takeProfitPrice, stopLossPrice, orderTriggerPrice float64) (string, error) { + resp := struct { + Data struct { + OrderID string `json:"stop_order_id"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return "", err + } + params.Set("symbol", symbolValue) + if stopOrderID == "" && orderLinkID == "" { + return "", errStopOrderOrOrderLinkIDMissing + } + if stopOrderID != "" { + params.Set("stop_order_id", stopOrderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if updatedQty != 0 { + params.Set("p_r_qty", strconv.FormatFloat(updatedQty, 'f', -1, 64)) + } + if updatedPrice != 0 { + params.Set("p_r_price", strconv.FormatFloat(updatedPrice, 'f', -1, 64)) + } + if orderTriggerPrice != 0 { + params.Set("p_r_trigger_price", strconv.FormatFloat(orderTriggerPrice, 'f', -1, 64)) + } + if takeProfitPrice != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfitPrice, 'f', -1, 64)) + } + if stopLossPrice != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLossPrice, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + return resp.Data.OrderID, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesReplaceConditionalOrder, params, nil, &resp, cFuturesReplaceConditionalOrderRate) +} + +// GetConditionalRealtimeCoinOrders query real time conditional order data +func (by *Bybit) GetConditionalRealtimeCoinOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID string) ([]CoinFuturesConditionalRealtimeOrder, error) { + var data []CoinFuturesConditionalRealtimeOrder + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return data, err + } + params.Set("symbol", symbolValue) + if stopOrderID != "" { + params.Set("stop_order_id", stopOrderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + + if stopOrderID == "" && orderLinkID == "" { + resp := struct { + Data []CoinFuturesConditionalRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetConditionalRealtimeOrders, params, nil, &resp, cFuturesDefaultRate) + if err != nil { + return data, err + } + data = append(data, resp.Data...) + } else { + resp := struct { + Data CoinFuturesConditionalRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetConditionalRealtimeOrders, params, nil, &resp, cFuturesDefaultRate) + if err != nil { + return data, err + } + data = append(data, resp.Data) + } + return data, nil +} + +// GetCoinPositions returns list of user positions +func (by *Bybit) GetCoinPositions(ctx context.Context, symbol currency.Pair) ([]PositionResp, error) { + var data []PositionResp + params := url.Values{} + + if !symbol.IsEmpty() { + resp := struct { + Data PositionResp `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return data, err + } + params.Set("symbol", symbolValue) + + err = by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesPosition, params, nil, &resp, cFuturesPositionRate) + if err != nil { + return data, err + } + data = append(data, resp.Data) + } else { + resp := struct { + Data []PositionResp `json:"result"` + Error + }{} + err := by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesPosition, params, nil, &resp, cFuturesPositionRate) + if err != nil { + return data, err + } + data = append(data, resp.Data...) + } + return data, nil +} + +// SetCoinMargin updates margin +func (by *Bybit) SetCoinMargin(ctx context.Context, symbol currency.Pair, margin string) (float64, error) { + resp := struct { + Data float64 `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if margin == "" { + return resp.Data, errInvalidMargin + } + params.Set("margin", margin) + + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesUpdateMargin, params, nil, &resp, cFuturesUpdateMarginRate) +} + +// SetCoinTradingAndStop sets take profit, stop loss, and trailing stop for your open position +func (by *Bybit) SetCoinTradingAndStop(ctx context.Context, symbol currency.Pair, takeProfit, stopLoss, trailingStop, newTrailingActive, stopLossQty, takeProfitQty float64, takeProfitTriggerBy, stopLossTriggerBy string) (SetTradingAndStopResp, error) { + resp := struct { + Data SetTradingAndStopResp `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if takeProfit >= 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64)) + } + if stopLoss >= 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64)) + } + if trailingStop >= 0 { + params.Set("trailing_stop", strconv.FormatFloat(trailingStop, 'f', -1, 64)) + } + if newTrailingActive != 0 { + params.Set("new_trailing_active", strconv.FormatFloat(newTrailingActive, 'f', -1, 64)) + } + if stopLossQty != 0 { + params.Set("sl_size", strconv.FormatFloat(stopLossQty, 'f', -1, 64)) + } + if takeProfitQty != 0 { + params.Set("tp_size", strconv.FormatFloat(takeProfitQty, 'f', -1, 64)) + } + + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesSetTrading, params, nil, &resp, cFuturesDefaultRate) +} + +// SetCoinLeverage sets leverage +func (by *Bybit) SetCoinLeverage(ctx context.Context, symbol currency.Pair, leverage float64, leverageOnly bool) (float64, error) { + resp := struct { + Data float64 `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if leverage <= 0 { + return resp.Data, errInvalidLeverage + } + params.Set("leverage", strconv.FormatFloat(leverage, 'f', -1, 64)) + + if leverageOnly { + params.Set("leverage_only", "true") + } + + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesSetLeverage, params, nil, &resp, cFuturesSetLeverageRate) +} + +// GetCoinTradeRecords returns list of user trades +func (by *Bybit) GetCoinTradeRecords(ctx context.Context, symbol currency.Pair, orderID, order string, startTime, page, limit int64) ([]TradeResp, error) { + params := url.Values{} + + resp := struct { + Data struct { + OrderID string `json:"order_id"` + Trades []TradeResp `json:"trade_list"` + } `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data.Trades, err + } + params.Set("symbol", symbolValue) + + if orderID != "" { + params.Set("order_id", orderID) + } + if order != "" { + params.Set("order", order) + } + if startTime != 0 { + params.Set("start_time", strconv.FormatInt(startTime, 10)) + } + if page != 0 { + params.Set("page", strconv.FormatInt(page, 10)) + } + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + return resp.Data.Trades, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetTrades, params, nil, &resp, cFuturesTradeRate) +} + +// GetClosedCoinTrades returns closed profit and loss records +func (by *Bybit) GetClosedCoinTrades(ctx context.Context, symbol currency.Pair, executionType string, startTime, endTime time.Time, page, limit int64) ([]ClosedTrades, error) { + params := url.Values{} + + resp := struct { + Data struct { + CurrentPage int64 `json:"current_page"` + Trades []ClosedTrades `json:"data"` + } `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data.Trades, err + } + params.Set("symbol", symbolValue) + + if executionType != "" { + params.Set("execution_type", executionType) + } + if !startTime.IsZero() { + params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) + } + if !endTime.IsZero() { + params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) + } + if page > 0 && page <= 50 { + params.Set("page", strconv.FormatInt(page, 10)) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + return resp.Data.Trades, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetClosedTrades, params, nil, &resp, cFuturesDefaultRate) +} + +// ChangeCoinMode switches mode between full or partial position +func (by *Bybit) ChangeCoinMode(ctx context.Context, symbol currency.Pair, takeProfitStopLoss string) (string, error) { + resp := struct { + Data struct { + Mode string `json:"tp_sl_mode"` + } `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data.Mode, err + } + params.Set("symbol", symbolValue) + if takeProfitStopLoss == "" { + return resp.Data.Mode, errInvalidTakeProfitStopLoss + } + params.Set("tp_sl_mode", takeProfitStopLoss) + + return resp.Data.Mode, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesSwitchPosition, params, nil, &resp, cFuturesSwitchPositionRate) +} + +// ChangeCoinMargin switches margin between cross or isolated +func (by *Bybit) ChangeCoinMargin(ctx context.Context, symbol currency.Pair, buyLeverage, sellLeverage float64, isIsolated bool) error { + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + params.Set("symbol", symbolValue) + params.Set("buy_leverage", strconv.FormatFloat(buyLeverage, 'f', -1, 64)) + params.Set("sell_leverage", strconv.FormatFloat(sellLeverage, 'f', -1, 64)) + + if isIsolated { + params.Set("is_isolated", "true") + } else { + params.Set("is_isolated", "false") + } + + return by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesSwitchMargin, params, nil, nil, cFuturesDefaultRate) +} + +// GetTradingFeeRate returns trading taker and maker fee rate +func (by *Bybit) GetTradingFeeRate(ctx context.Context, symbol currency.Pair) (takerFee, makerFee float64, err error) { + params := url.Values{} + resp := struct { + Result struct { + TakerFeeRate float64 `json:"taker_fee_rate,string"` + MakerFeeRate float64 `json:"maker_fee_rate,string"` + } `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Result.TakerFeeRate, resp.Result.MakerFeeRate, err + } + params.Set("symbol", symbolValue) + + takerFee, makerFee, err = resp.Result.TakerFeeRate, resp.Result.MakerFeeRate, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetTradingFeeRate, params, nil, &resp, cFuturesGetTradingFeeRate) + return +} + +// SetCoinRiskLimit sets risk limit +func (by *Bybit) SetCoinRiskLimit(ctx context.Context, symbol currency.Pair, riskID int64) (int64, error) { + resp := struct { + Data struct { + RiskID int64 `json:"risk_id"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data.RiskID, err + } + params.Set("symbol", symbolValue) + + if riskID <= 0 { + return resp.Data.RiskID, errInvalidRiskID + } + params.Set("risk_id", strconv.FormatInt(riskID, 10)) + + return resp.Data.RiskID, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodPost, bybitFuturesAPIVersion+cfuturesSetRiskLimit, params, nil, &resp, cFuturesDefaultRate) +} + +// GetCoinLastFundingFee returns last funding fees +func (by *Bybit) GetCoinLastFundingFee(ctx context.Context, symbol currency.Pair) (FundingFee, error) { + params := url.Values{} + + resp := struct { + Data FundingFee `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetMyLastFundingFee, params, nil, &resp, cFuturesLastFundingFeeRate) +} + +// GetCoinPredictedFundingRate returns predicted funding rates and fees +func (by *Bybit) GetCoinPredictedFundingRate(ctx context.Context, symbol currency.Pair) (fundingRate, fundingFee float64, err error) { + params := url.Values{} + + resp := struct { + Data struct { + PredictedFundingRate float64 `json:"predicted_funding_rate"` + PredictedFundingFee float64 `json:"predicted_funding_fee"` + } `json:"result"` + Error + }{} + + var symbolValue string + symbolValue, err = by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data.PredictedFundingRate, resp.Data.PredictedFundingFee, err + } + params.Set("symbol", symbolValue) + + err = by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesPredictFundingRate, params, nil, &resp, cFuturesPredictFundingRate) + fundingRate = resp.Data.PredictedFundingRate + fundingFee = resp.Data.PredictedFundingFee + return +} + +// GetAPIKeyInfo returns user API Key information +func (by *Bybit) GetAPIKeyInfo(ctx context.Context) ([]APIKeyData, error) { + params := url.Values{} + + resp := struct { + Data []APIKeyData `json:"result"` + Error + }{} + + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetAPIKeyInfo, params, nil, &resp, cFuturesAPIKeyInfoRate) +} + +// GetLiquidityContributionPointsInfo returns latest LCP information +func (by *Bybit) GetLiquidityContributionPointsInfo(ctx context.Context, symbol currency.Pair) ([]LCPData, error) { + params := url.Values{} + + resp := struct { + Data struct { + LCPList []LCPData `json:"lcp_list"` + } `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.CoinMarginedFutures) + if err != nil { + return resp.Data.LCPList, err + } + params.Set("symbol", symbolValue) + + return resp.Data.LCPList, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetLiquidityContributionPoints, params, nil, &resp, cFuturesDefaultRate) +} + +// GetFutureWalletBalance returns wallet balance +func (by *Bybit) GetFutureWalletBalance(ctx context.Context, coin string) (map[string]WalletData, error) { + params := url.Values{} + + resp := struct { + Wallets map[string]WalletData `json:"result"` + Error + }{} + + if coin != "" { + params.Set("coin", coin) + } + + return resp.Wallets, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetWalletBalance, params, nil, &resp, cFuturesWalletBalanceRate) +} + +// GetWalletFundRecords returns wallet fund records +func (by *Bybit) GetWalletFundRecords(ctx context.Context, startDate, endDate, currency, coin, walletFundType string, page, limit int64) ([]FundRecord, error) { + params := url.Values{} + + resp := struct { + Data struct { + Records []FundRecord `json:"data"` + } `json:"result"` + Error + }{} + + if startDate != "" { + params.Set("start_date", startDate) + } + if endDate != "" { + params.Set("end_date", endDate) + } + if currency != "" { + params.Set("currency", currency) + } + if coin != "" { + params.Set("coin", coin) + } + if walletFundType != "" { + params.Set("wallet_fund_type", walletFundType) + } + if page != 0 { + params.Set("page", strconv.FormatInt(page, 10)) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + return resp.Data.Records, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetWalletFundRecords, params, nil, &resp, cFuturesWalletFundRecordRate) +} + +// GetWalletWithdrawalRecords returns wallet withdrawal records +func (by *Bybit) GetWalletWithdrawalRecords(ctx context.Context, startDate, endDate, status string, coin currency.Code, page, limit int64) ([]FundWithdrawalRecord, error) { + params := url.Values{} + + resp := struct { + Data struct { + Records []FundWithdrawalRecord `json:"data"` + } `json:"result"` + Error + }{} + + if startDate != "" { + params.Set("start_date", startDate) + } + if endDate != "" { + params.Set("end_date", endDate) + } + if !coin.IsEmpty() { + params.Set("coin", strings.ToUpper(coin.String())) + } + if status != "" { + params.Set("status", status) + } + if page != 0 { + params.Set("page", strconv.FormatInt(page, 10)) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + return resp.Data.Records, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetWalletWithdrawalRecords, params, nil, &resp, cFuturesWalletWithdrawalRate) +} + +// GetAssetExchangeRecords returns wallet asset exchange records +func (by *Bybit) GetAssetExchangeRecords(ctx context.Context, direction string, from, limit int64) ([]AssetExchangeRecord, error) { + params := url.Values{} + + resp := struct { + Data []AssetExchangeRecord `json:"result"` + Error + }{} + + if direction != "" { + params.Set("direction", direction) + } + + if from != 0 { + params.Set("from", strconv.FormatInt(from, 10)) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestCoinMargined, http.MethodGet, bybitFuturesAPIVersion+cfuturesGetAssetExchangeRecords, params, nil, &resp, cFuturesDefaultRate) +} diff --git a/exchanges/bybit/bybit_futures.go b/exchanges/bybit/bybit_futures.go new file mode 100644 index 00000000..dc7f44dc --- /dev/null +++ b/exchanges/bybit/bybit_futures.go @@ -0,0 +1,757 @@ +package bybit + +import ( + "context" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/thrasher-corp/gocryptotrader/currency" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" +) + +const ( + + // auth endpoint + futuresCreateOrder = "/futures/private/order/create" + futuresGetActiveOrders = "/futures/private/order/list" + futuresCancelActiveOrder = "/futures/private/order/cancel" + futuresCancelAllActiveOrders = "/futures/private/order/cancelAll" + futuresReplaceActiveOrder = "/futures/private/order/replace" + futuresGetActiveRealtimeOrders = "/futures/private/order" + + futuresCreateConditionalOrder = "/futures/private/stop-order/create" + futuresGetConditionalOrders = "/futures/private/stop-order/list" + futuresCancelConditionalOrder = "/futures/private/stop-order/cancel" + futuresCancelAllConditionalOrders = "/futures/private/stop-order/cancelAll" + futuresReplaceConditionalOrder = "/futures/private/stop-order/replace" + futuresGetConditionalRealtimeOrders = "/futures/private/stop-order" + + futuresPosition = "/futures/private/position/list" + futuresUpdateMargin = "/futures/private/position/change-position-margin" + futuresSetTradingStop = "/futures/private/position/trading-stop" + futuresSetLeverage = "/futures/private/position/leverage/save" + futuresSwitchPositionMode = "/futures/private/position/switch-mode" + futuresSwitchPosition = "/futures/private/tpsl/switch-mode" + futuresSwitchMargin = "/futures/private/position/switch-isolated" + futuresGetTrades = "/futures/private/execution/list" + futuresGetClosedTrades = "/futures/private/trade/closed-pnl/list" + futuresSetRiskLimit = "/futures/private/position/risk-limit" +) + +// CreateFuturesOrder sends a new futures order to the exchange +func (by *Bybit) CreateFuturesOrder(ctx context.Context, positionMode int64, symbol currency.Pair, side, orderType, timeInForce, + orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string, + quantity, price, takeProfit, stopLoss float64, closeOnTrigger, reduceOnly bool) (FuturesOrderDataResp, error) { + resp := struct { + Result FuturesOrderDataResp `json:"result"` + Error + }{} + + params := url.Values{} + if positionMode < 0 || positionMode > 2 { + return resp.Result, errInvalidPositionMode + } + params.Set("position_idx", strconv.FormatInt(positionMode, 10)) + + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result, err + } + params.Set("symbol", symbolValue) + params.Set("side", side) + params.Set("order_type", orderType) + if quantity <= 0 { + return resp.Result, errInvalidQuantity + } + params.Set("qty", strconv.FormatFloat(quantity, 'f', -1, 64)) + + if price != 0 { + params.Set("price", strconv.FormatFloat(price, 'f', -1, 64)) + } + if timeInForce == "" { + return resp.Result, errInvalidTimeInForce + } + params.Set("time_in_force", timeInForce) + + if closeOnTrigger { + params.Set("close_on_trigger", "true") + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if takeProfit != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64)) + } + if stopLoss != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + if reduceOnly { + params.Set("reduce_only", "true") + } + return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCreateOrder, params, nil, &resp, futuresCreateOrderRate) +} + +// GetActiveFuturesOrders gets list of futures active orders +func (by *Bybit) GetActiveFuturesOrders(ctx context.Context, symbol currency.Pair, orderStatus, direction, cursor string, limit int64) ([]FuturesActiveOrder, error) { + resp := struct { + Result struct { + Data []FuturesActiveOrder `json:"data"` + Cursor string `json:"cursor"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result.Data, err + } + params.Set("symbol", symbolValue) + if orderStatus != "" { + params.Set("order_status", orderStatus) + } + if direction != "" { + params.Set("direction", direction) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if cursor != "" { + params.Set("cursor", cursor) + } + return resp.Result.Data, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetActiveOrders, params, nil, &resp, futuresGetActiveOrderRate) +} + +// CancelActiveFuturesOrders cancels futures unfilled or partially filled orders +func (by *Bybit) CancelActiveFuturesOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID string) (FuturesOrderCancelResp, error) { + resp := struct { + Result FuturesOrderCancelResp `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result, err + } + params.Set("symbol", symbolValue) + if orderID == "" && orderLinkID == "" { + return resp.Result, errOrderOrOrderLinkIDMissing + } + if orderID != "" { + params.Set("order_id", orderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCancelActiveOrder, params, nil, &resp, futuresCancelOrderRate) +} + +// CancelAllActiveFuturesOrders cancels all futures unfilled or partially filled orders +func (by *Bybit) CancelAllActiveFuturesOrders(ctx context.Context, symbol currency.Pair) ([]FuturesCancelOrderData, error) { + resp := struct { + Result []FuturesCancelOrderData `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result, err + } + params.Set("symbol", symbolValue) + return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCancelAllActiveOrders, params, nil, &resp, futuresCancelAllOrderRate) +} + +// ReplaceActiveFuturesOrders modify unfilled or partially filled orders +func (by *Bybit) ReplaceActiveFuturesOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string, + updatedQty, updatedPrice, takeProfitPrice, stopLossPrice float64) (string, error) { + resp := struct { + Result struct { + OrderID string `json:"order_id"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return "", err + } + params.Set("symbol", symbolValue) + if orderID == "" && orderLinkID == "" { + return "", errOrderOrOrderLinkIDMissing + } + if orderID != "" { + params.Set("order_id", orderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if updatedQty != 0 { + params.Set("p_r_qty", strconv.FormatFloat(updatedQty, 'f', -1, 64)) + } + if updatedPrice != 0 { + params.Set("p_r_price", strconv.FormatFloat(updatedPrice, 'f', -1, 64)) + } + if takeProfitPrice != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfitPrice, 'f', -1, 64)) + } + if stopLossPrice != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLossPrice, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + return resp.Result.OrderID, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresReplaceActiveOrder, params, nil, &resp, futuresReplaceOrderRate) +} + +// GetActiveRealtimeOrders query real time order data +func (by *Bybit) GetActiveRealtimeOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID string) ([]FuturesActiveRealtimeOrder, error) { + var data []FuturesActiveRealtimeOrder + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return data, err + } + params.Set("symbol", symbolValue) + if orderID != "" { + params.Set("order_id", orderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + + if orderID == "" && orderLinkID == "" { + resp := struct { + Result []FuturesActiveRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetActiveRealtimeOrders, params, nil, &resp, futuresGetActiveRealtimeOrderRate) + if err != nil { + return data, err + } + data = append(data, resp.Result...) + } else { + resp := struct { + Result FuturesActiveRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetActiveRealtimeOrders, params, nil, &resp, futuresGetActiveRealtimeOrderRate) + if err != nil { + return data, err + } + data = append(data, resp.Result) + } + return data, nil +} + +// CreateConditionalFuturesOrder sends a new conditional futures order to the exchange +func (by *Bybit) CreateConditionalFuturesOrder(ctx context.Context, positionMode int64, symbol currency.Pair, side, orderType, timeInForce, + orderLinkID, takeProfitTriggerBy, stopLossTriggerBy, triggerBy string, + quantity, price, takeProfit, stopLoss, basePrice, stopPrice float64, closeOnTrigger bool) (FuturesConditionalOrderResp, error) { + resp := struct { + Result FuturesConditionalOrderResp `json:"result"` + Error + }{} + params := url.Values{} + if positionMode < 0 || positionMode > 2 { + return resp.Result, errInvalidPositionMode + } + params.Set("position_idx", strconv.FormatInt(positionMode, 10)) + + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result, err + } + params.Set("symbol", symbolValue) + params.Set("side", side) + params.Set("order_type", orderType) + if quantity <= 0 { + return resp.Result, errInvalidQuantity + } + params.Set("qty", strconv.FormatFloat(quantity, 'f', -1, 64)) + + if price != 0 { + params.Set("price", strconv.FormatFloat(price, 'f', -1, 64)) + } + if basePrice <= 0 { + return resp.Result, errInvalidBasePrice + } + params.Set("base_price", strconv.FormatFloat(basePrice, 'f', -1, 64)) + + if stopPrice <= 0 { + return resp.Result, errInvalidStopPrice + } + params.Set("stop_px", strconv.FormatFloat(stopPrice, 'f', -1, 64)) + + if timeInForce == "" { + return resp.Result, errInvalidTimeInForce + } + params.Set("time_in_force", timeInForce) + + if triggerBy != "" { + params.Set("trigger_by", triggerBy) + } + if closeOnTrigger { + params.Set("close_on_trigger", "true") + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if takeProfit != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64)) + } + if stopLoss != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCreateConditionalOrder, params, nil, &resp, futuresCreateConditionalOrderRate) +} + +// GetConditionalFuturesOrders gets list of futures conditional orders +func (by *Bybit) GetConditionalFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderStatus, direction, cursor string, limit int64) ([]FuturesConditionalOrders, error) { + resp := struct { + Result struct { + Result []FuturesConditionalOrders `json:"data"` + Cursor string `json:"cursor"` + } `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result.Result, err + } + params.Set("symbol", symbolValue) + if stopOrderStatus != "" { + params.Set("stop_order_status", stopOrderStatus) + } + if direction != "" { + params.Set("direction", direction) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if cursor != "" { + params.Set("cursor", cursor) + } + return resp.Result.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetConditionalOrders, params, nil, &resp, futuresGetConditionalOrderRate) +} + +// CancelConditionalFuturesOrders cancels untriggered conditional orders +func (by *Bybit) CancelConditionalFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID string) (string, error) { + resp := struct { + Result struct { + StopOrderID string `json:"stop_order_id"` + } `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return "", err + } + params.Set("symbol", symbolValue) + if stopOrderID == "" && orderLinkID == "" { + return "", errStopOrderOrOrderLinkIDMissing + } + if stopOrderID != "" { + params.Set("stop_order_id", stopOrderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + return resp.Result.StopOrderID, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCancelConditionalOrder, params, nil, &resp, futuresCancelConditionalOrderRate) +} + +// CancelAllConditionalFuturesOrders cancels all untriggered conditional orders +func (by *Bybit) CancelAllConditionalFuturesOrders(ctx context.Context, symbol currency.Pair) ([]FuturesCancelOrderResp, error) { + resp := struct { + Result []FuturesCancelOrderResp `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result, err + } + params.Set("symbol", symbolValue) + return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresCancelAllConditionalOrders, params, nil, &resp, futuresCancelAllConditionalOrderRate) +} + +// ReplaceConditionalFuturesOrders modify unfilled or partially filled conditional orders +func (by *Bybit) ReplaceConditionalFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string, + updatedQty, updatedPrice, takeProfitPrice, stopLossPrice, orderTriggerPrice float64) (string, error) { + resp := struct { + Result struct { + OrderID string `json:"stop_order_id"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return "", err + } + params.Set("symbol", symbolValue) + if stopOrderID == "" && orderLinkID == "" { + return "", errStopOrderOrOrderLinkIDMissing + } + if stopOrderID != "" { + params.Set("stop_order_id", stopOrderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if updatedQty != 0 { + params.Set("p_r_qty", strconv.FormatFloat(updatedQty, 'f', -1, 64)) + } + if updatedPrice != 0 { + params.Set("p_r_price", strconv.FormatFloat(updatedPrice, 'f', -1, 64)) + } + if orderTriggerPrice != 0 { + params.Set("p_r_trigger_price", strconv.FormatFloat(orderTriggerPrice, 'f', -1, 64)) + } + if takeProfitPrice != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfitPrice, 'f', -1, 64)) + } + if stopLossPrice != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLossPrice, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + return resp.Result.OrderID, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresReplaceConditionalOrder, params, nil, &resp, futuresReplaceConditionalOrderRate) +} + +// GetConditionalRealtimeOrders query real time conditional order data +func (by *Bybit) GetConditionalRealtimeOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID string) ([]FuturesConditionalRealtimeOrder, error) { + var data []FuturesConditionalRealtimeOrder + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return data, err + } + params.Set("symbol", symbolValue) + if stopOrderID != "" { + params.Set("stop_order_id", stopOrderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + + if stopOrderID == "" && orderLinkID == "" { + resp := struct { + Result []FuturesConditionalRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetConditionalRealtimeOrders, params, nil, &resp, futuresGetConditionalRealtimeOrderRate) + if err != nil { + return data, err + } + data = append(data, resp.Result...) + } else { + resp := struct { + Result FuturesConditionalRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetConditionalRealtimeOrders, params, nil, &resp, futuresGetConditionalRealtimeOrderRate) + if err != nil { + return data, err + } + data = append(data, resp.Result) + } + return data, nil +} + +// GetPositions returns list of user positions +func (by *Bybit) GetPositions(ctx context.Context, symbol currency.Pair) ([]PositionResp, error) { + params := url.Values{} + resp := struct { + Result []struct { + Data PositionResp `json:"data"` + IsValid bool `json:"is_valid"` + } `json:"result"` + Error + }{} + + if !symbol.IsEmpty() { + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return nil, err + } + params.Set("symbol", symbolValue) + } + err := by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresPosition, params, nil, &resp, futuresPositionRate) + if err != nil { + return nil, err + } + + data := make([]PositionResp, len(resp.Result)) + for x := range resp.Result { + data[x] = resp.Result[x].Data + } + return data, nil +} + +// SetMargin updates margin +func (by *Bybit) SetMargin(ctx context.Context, positionMode int64, symbol currency.Pair, margin string) (float64, error) { + resp := struct { + Result float64 `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result, err + } + params.Set("symbol", symbolValue) + if positionMode < 0 || positionMode > 2 { + return resp.Result, errInvalidPositionMode + } + params.Set("position_idx", strconv.FormatInt(positionMode, 10)) + + if margin == "" { + return resp.Result, errInvalidMargin + } + params.Set("margin", margin) + + return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresUpdateMargin, params, nil, &resp, futuresUpdateMarginRate) +} + +// SetTradingAndStop sets take profit, stop loss, and trailing stop for your open position +func (by *Bybit) SetTradingAndStop(ctx context.Context, positionMode int64, symbol currency.Pair, takeProfit, stopLoss, trailingStop, newTrailingActive, stopLossQty, takeProfitQty float64, takeProfitTriggerBy, stopLossTriggerBy string) (SetTradingAndStopResp, error) { + resp := struct { + Result SetTradingAndStopResp `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result, err + } + params.Set("symbol", symbolValue) + if positionMode < 0 || positionMode > 2 { + return resp.Result, errInvalidPositionMode + } + params.Set("position_idx", strconv.FormatInt(positionMode, 10)) + + if takeProfit >= 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64)) + } + if stopLoss >= 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64)) + } + if trailingStop >= 0 { + params.Set("trailing_stop", strconv.FormatFloat(trailingStop, 'f', -1, 64)) + } + if newTrailingActive != 0 { + params.Set("new_trailing_active", strconv.FormatFloat(newTrailingActive, 'f', -1, 64)) + } + if stopLossQty != 0 { + params.Set("sl_size", strconv.FormatFloat(stopLossQty, 'f', -1, 64)) + } + if takeProfitQty != 0 { + params.Set("tp_size", strconv.FormatFloat(takeProfitQty, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSetTradingStop, params, nil, &resp, futuresSetTradingStopRate) +} + +// SetLeverage sets leverage +func (by *Bybit) SetLeverage(ctx context.Context, symbol currency.Pair, buyLeverage, sellLeverage float64) (float64, error) { + resp := struct { + Result float64 `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result, err + } + params.Set("symbol", symbolValue) + params.Set("buy_leverage", strconv.FormatFloat(buyLeverage, 'f', -1, 64)) + params.Set("sell_leverage", strconv.FormatFloat(sellLeverage, 'f', -1, 64)) + + return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSetLeverage, params, nil, &resp, futuresSetLeverageRate) +} + +// ChangePositionMode switches mode between One-Way or Hedge Mode +func (by *Bybit) ChangePositionMode(ctx context.Context, symbol currency.Pair, mode int64) error { + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return err + } + params.Set("symbol", symbolValue) + params.Set("mode", strconv.FormatInt(mode, 10)) + + return by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSwitchPositionMode, params, nil, nil, futuresSwitchPositionModeRate) +} + +// ChangeMode switches mode between full or partial position +func (by *Bybit) ChangeMode(ctx context.Context, symbol currency.Pair, takeProfitStopLoss string) (string, error) { + resp := struct { + Result struct { + Mode string `json:"tp_sl_mode"` + } `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result.Mode, err + } + params.Set("symbol", symbolValue) + if takeProfitStopLoss == "" { + return resp.Result.Mode, errInvalidTakeProfitStopLoss + } + params.Set("tp_sl_mode", takeProfitStopLoss) + + return resp.Result.Mode, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSwitchPosition, params, nil, &resp, futuresSwitchPositionRate) +} + +// ChangeMargin switches margin between cross or isolated +func (by *Bybit) ChangeMargin(ctx context.Context, symbol currency.Pair, buyLeverage, sellLeverage float64, isIsolated bool) error { + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return err + } + params.Set("symbol", symbolValue) + params.Set("buy_leverage", strconv.FormatFloat(buyLeverage, 'f', -1, 64)) + params.Set("sell_leverage", strconv.FormatFloat(sellLeverage, 'f', -1, 64)) + + if isIsolated { + params.Set("is_isolated", "true") + } else { + params.Set("is_isolated", "false") + } + + return by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSwitchMargin, params, nil, nil, futuresSwitchMarginRate) +} + +// GetTradeRecords returns list of user trades +func (by *Bybit) GetTradeRecords(ctx context.Context, symbol currency.Pair, orderID, order string, startTime, page, limit int64) ([]TradeResp, error) { + params := url.Values{} + resp := struct { + Data struct { + OrderID string `json:"order_id"` + Trades []TradeResp `json:"trade_list"` + } `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Data.Trades, err + } + params.Set("symbol", symbolValue) + + if orderID != "" { + params.Set("order_id", orderID) + } + if order != "" { + params.Set("order", order) + } + if startTime != 0 { + params.Set("start_time", strconv.FormatInt(startTime, 10)) + } + if page != 0 { + params.Set("page", strconv.FormatInt(page, 10)) + } + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + return resp.Data.Trades, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetTrades, params, nil, &resp, futuresGetTradeRate) +} + +// GetClosedTrades returns closed profit and loss records +func (by *Bybit) GetClosedTrades(ctx context.Context, symbol currency.Pair, executionType string, startTime, endTime time.Time, page, limit int64) ([]ClosedTrades, error) { + params := url.Values{} + resp := struct { + Data struct { + CurrentPage int64 `json:"current_page"` + Trades []ClosedTrades `json:"data"` + } `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Data.Trades, err + } + params.Set("symbol", symbolValue) + + if executionType != "" { + params.Set("execution_type", executionType) + } + if !startTime.IsZero() { + params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) + } + if !endTime.IsZero() { + params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) + } + if page > 0 && page <= 50 { + params.Set("page", strconv.FormatInt(page, 10)) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + return resp.Data.Trades, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodGet, futuresGetClosedTrades, params, nil, &resp, futuresDefaultRate) +} + +// SetRiskLimit sets risk limit +func (by *Bybit) SetRiskLimit(ctx context.Context, symbol currency.Pair, riskID, positionMode int64) (int64, error) { + resp := struct { + Result struct { + RiskID int64 `json:"risk_id"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.Futures) + if err != nil { + return resp.Result.RiskID, err + } + params.Set("symbol", symbolValue) + + if riskID <= 0 { + return resp.Result.RiskID, errInvalidRiskID + } + params.Set("risk_id", strconv.FormatInt(riskID, 10)) + + if positionMode < 0 || positionMode > 2 { + return resp.Result.RiskID, errInvalidPositionMode + } + params.Set("position_idx", strconv.FormatInt(positionMode, 10)) + return resp.Result.RiskID, by.SendAuthHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, futuresSetRiskLimit, params, nil, &resp, futuresDefaultRate) +} diff --git a/exchanges/bybit/bybit_test.go b/exchanges/bybit/bybit_test.go new file mode 100644 index 00000000..6cb21b5a --- /dev/null +++ b/exchanges/bybit/bybit_test.go @@ -0,0 +1,3448 @@ +package bybit + +import ( + "context" + "errors" + "log" + "os" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/config" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" + "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" +) + +// Please supply your own keys here to do authenticated endpoint testing +const ( + apiKey = "" + apiSecret = "" + canManipulateRealOrders = false +) + +var b Bybit + +func TestMain(m *testing.M) { + b.SetDefaults() + cfg := config.GetConfig() + err := cfg.LoadConfig("../../testdata/configtest.json", true) + if err != nil { + log.Fatal(err) + } + + exchCfg, err := cfg.GetExchangeConfig("Bybit") + if err != nil { + log.Fatal(err) + } + + exchCfg.API.AuthenticatedSupport = true + exchCfg.API.AuthenticatedWebsocketSupport = false + exchCfg.API.Credentials.Key = apiKey + exchCfg.API.Credentials.Secret = apiSecret + b.Websocket = sharedtestvalues.NewTestWebsocket() + err = b.Setup(exchCfg) + if err != nil { + log.Fatal(err) + } + + os.Exit(m.Run()) +} + +func areTestAPIKeysSet() bool { + return b.ValidateAPICredentials(b.GetDefaultCredentials()) == nil +} + +func TestStart(t *testing.T) { + t.Parallel() + err := b.Start(nil) + if !errors.Is(err, common.ErrNilPointer) { + t.Fatalf("received: '%v' but expected: '%v'", err, common.ErrNilPointer) + } + var testWg sync.WaitGroup + err = b.Start(&testWg) + if err != nil { + t.Fatal(err) + } + testWg.Wait() +} + +// test cases for SPOT + +func TestGetAllSpotPairs(t *testing.T) { + t.Parallel() + _, err := b.GetAllSpotPairs(context.Background()) + if err != nil { + t.Fatal(err) + } +} + +func TestGetOrderBook(t *testing.T) { + t.Parallel() + _, err := b.GetOrderBook(context.Background(), "BTCUSDT", 100) + if err != nil { + t.Fatal(err) + } +} + +func TestGetMergedOrderBook(t *testing.T) { + t.Parallel() + _, err := b.GetMergedOrderBook(context.Background(), "BTCUSDT", 2, 100) + if err != nil { + t.Fatal(err) + } +} + +func TestGetTrades(t *testing.T) { + t.Parallel() + _, err := b.GetTrades(context.Background(), "BTCUSDT", 100) + if err != nil { + t.Fatal(err) + } +} + +func TestGetKlines(t *testing.T) { + t.Parallel() + _, err := b.GetKlines(context.Background(), "BTCUSDT", "5m", 2000, time.Now().Add(-time.Hour*1), time.Now()) + if err != nil { + t.Fatal(err) + } +} + +func TestGet24HrsChange(t *testing.T) { + t.Parallel() + _, err := b.Get24HrsChange(context.Background(), "BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.Get24HrsChange(context.Background(), "") + if err != nil { + t.Fatal(err) + } +} + +func TestGetLastTradedPrice(t *testing.T) { + t.Parallel() + _, err := b.GetLastTradedPrice(context.Background(), "BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetLastTradedPrice(context.Background(), "") + if err != nil { + t.Fatal(err) + } +} + +func TestGetBestBidAskPrice(t *testing.T) { + t.Parallel() + _, err := b.GetBestBidAskPrice(context.Background(), "BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetBestBidAskPrice(context.Background(), "") + if err != nil { + t.Fatal(err) + } +} + +func TestCreatePostOrder(t *testing.T) { + t.Parallel() + + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + _, err := b.CreatePostOrder(context.Background(), &PlaceOrderRequest{ + Symbol: "BTCUSDT", + Quantity: 1, + Side: "Buy", + TradeType: "LIMIT", + TimeInForce: "GTC", + Price: 100, + }) + if err != nil { + t.Fatal(err) + } +} + +func TestQueryOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { // Note: here !canManipulateRealOrders added as we don't have orderID + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + _, err := b.QueryOrder(context.Background(), "0", "") + if err != nil { + t.Fatal(err) + } +} + +func TestCancelExistingOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + _, err := b.CancelExistingOrder(context.Background(), "", "") + if err != nil { + t.Fatal(err) + } +} + +func TestBatchCancelOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + _, err := b.BatchCancelOrder(context.Background(), "", "Buy", "") + if err != nil { + t.Fatal(err) + } +} + +func TestFastCancelExistingOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + _, err := b.FastCancelExistingOrder(context.Background(), "BTCUSDT", "889208273689997824", "") + if err != nil { + t.Fatal(err) + } + + _, err = b.FastCancelExistingOrder(context.Background(), "BTCUSDT", "", "162081160171552") + if err != nil { + t.Fatal(err) + } +} + +func TestBatchFastCancelOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + _, err := b.BatchFastCancelOrder(context.Background(), "BTCUSDT", "Buy", "") + if err != nil { + t.Fatal(err) + } + + _, err = b.BatchFastCancelOrder(context.Background(), "BTCUSDT", "", "Limit") + if err != nil { + t.Fatal(err) + } +} + +func TestBatchCancelOrderByIDs(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + _, err := b.BatchCancelOrderByIDs(context.Background(), []string{"889208273689997824", "889208273689997825"}) + if err != nil { + t.Fatal(err) + } +} + +func TestListOpenOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + _, err := b.ListOpenOrders(context.Background(), "BTCUSDT", "", 0) + if err != nil { + t.Fatal(err) + } +} + +func TestGetPastOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + _, err := b.GetPastOrders(context.Background(), "BTCUSDT", "", 0, time.Now().Add(-time.Hour), time.Now()) + if err != nil { + t.Fatal(err) + } +} + +func TestGetTradeHistory(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + _, err := b.GetTradeHistory(context.Background(), 0, "", "", "", "", time.Now().Add(-time.Hour), time.Now()) + if err != nil { + t.Fatal(err) + } +} + +func TestGetWalletBalance(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + _, err := b.GetWalletBalance(context.Background()) + if err != nil { + t.Fatal(err) + } +} + +func TestGetSpotServerTime(t *testing.T) { + t.Parallel() + _, err := b.GetSpotServerTime(context.Background()) + if err != nil { + t.Fatal(err) + } +} + +func TestGetDepositAddressForCurrency(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + _, err := b.GetDepositAddressForCurrency(context.Background(), currency.BTC.String()) + if err != nil { + t.Fatal(err) + } +} + +func TestWithdrawFund(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + _, err := b.WithdrawFund(context.Background(), currency.ETH.String(), currency.ETH.String(), "0xEA13A385BcB74e631AAF1B424d7a01c61bF27Fe0", "", "10") + if err != nil && err.Error() != "Withdraw address chain or destination tag are not equal" { + t.Fatal(err) + } +} + +// test cases for WS SPOT + +func TestWsSubscription(t *testing.T) { + t.Parallel() + pressXToJSON := []byte(`{ + "symbol": "BTCUSDT", + "event": "sub", + "topic": "trade", + "params": { + "binary": false + } + }`) + err := b.wsHandleData(pressXToJSON) + if err != nil { + t.Fatal(err) + } +} + +func TestWsUnsubscribe(t *testing.T) { + t.Parallel() + pressXToJSON := []byte(`{ + "symbol":"BTCUSDT", + "event": "cancel", + "topic":"trade", + "params": { + "binary": false + } + }`) + err := b.wsHandleData(pressXToJSON) + if err != nil { + t.Fatal(err) + } +} + +func TestWsTrade(t *testing.T) { + t.Parallel() + b.SetSaveTradeDataStatus(true) + + pressXToJSON := []byte(`{ + "topic": "trade", + "params": { + "symbol": "BTCUSDT", + "binary": "false", + "symbolName": "BTCUSDT" + }, + "data": { + "v": "564265886622695424", + "t": 1582001735462, + "p": "9787.5", + "q": "0.195009", + "m": true + } + }`) + err := b.wsHandleData(pressXToJSON) + if err != nil { + t.Fatal(err) + } +} + +func TestWsOrderbook(t *testing.T) { + t.Parallel() + pressXToJSON := []byte(`{ + "topic": "depth", + "params": { + "symbol": "BTCUSDT", + "binary": "false", + "symbolName": "BTCUSDT" + }, + "data": { + "s": "BTCUSDT", + "t": 1582001376853, + "v": "13850022_2", + "b": [ + [ + "9780.79", + "0.01" + ], + [ + "9780.5", + "0.1" + ], + [ + "9780.4", + "0.517813" + ] + ], + "a": [ + [ + "9781.21", + "0.042842" + ], + [ + "9782", + "0.3" + ], + [ + "9782.1", + "0.226" + ] + ] + } + }`) + err := b.wsHandleData(pressXToJSON) + if err != nil { + t.Fatal(err) + } +} + +func TestWsTicker(t *testing.T) { + t.Parallel() + pressXToJSON := []byte(`{ + "topic": "bookTicker", + "params": { + "symbol": "BTCUSDT", + "binary": "false", + "symbolName": "BTCUSDT" + }, + "data": { + "symbol": "BTCUSDT", + "bidPrice": "9797.79", + "bidQty": "0.177976", + "askPrice": "9799", + "askQty": "0.65", + "time": 1582001830346 + } + }`) + err := b.wsHandleData(pressXToJSON) + if err != nil { + t.Fatal(err) + } +} + +func TestWsKline(t *testing.T) { + t.Parallel() + pressXToJSON := []byte(`{ + "topic": "kline", + "params": { + "symbol": "BTCUSDT", + "binary": "false", + "klineType": "1m", + "symbolName": "BTCUSDT" + }, + "data": { + "t": 1582001880000, + "s": "BTCUSDT", + "sn": "BTCUSDT", + "c": "9799.4", + "h": "9801.4", + "l": "9798.91", + "o": "9799.4", + "v": "15.917433" + } + }`) + err := b.wsHandleData(pressXToJSON) + if err != nil { + t.Fatal(err) + } +} + +func TestWSAccountInfo(t *testing.T) { + t.Parallel() + pressXToJSON := []byte(`[{ + "e":"outboundAccountInfo", + "E":"1629969654753", + "T":true, + "W":true, + "D":true, + "B":[{ + "a":"BTC", + "f":"10000000097.1982823144", + "l":"0" + }] + }]`) + err := b.wsHandleData(pressXToJSON) + if err != nil { + t.Fatal(err) + } +} + +func TestWSOrderExecution(t *testing.T) { + t.Parallel() + pressXToJSON := []byte(`[{ + "e": "executionReport", + "E": "1499405658658", + "s": "BTCUSDT", + "c": "1000087761", + "S": "BUY", + "o": "LIMIT", + "f": "GTC", + "q": "1.00000000", + "p": "0.10264410", + "X": "NEW", + "i": "4293153", + "M": "0", + "l": "0.00000000", + "z": "0.00000000", + "L": "0.00000000", + "n": "0", + "N": "BTC", + "u": true, + "w": true, + "m": false, + "O": "1499405658657", + "Z": "473.199", + "A": "0", + "C": false, + "v": "0" + }]`) + err := b.wsHandleData(pressXToJSON) + if err != nil { + t.Fatal(err) + } +} + +func TestWSTickerInfo(t *testing.T) { + t.Parallel() + pressXToJSON := []byte(`[{ + "e":"ticketInfo", + "E":"1621912542359", + "s":"BTCUSDT", + "q":"0.001639", + "t":"1621912542314", + "p":"61000.0", + "T":"899062000267837441", + "o":"899048013515737344", + "c":"1621910874883", + "O":"899062000118679808", + "a":"10043", + "A":"10024", + "m":true, + "S":"BUY" + }]`) + err := b.wsHandleData(pressXToJSON) + if err != nil { + t.Fatal(err) + } +} + +// test cases for CoinMarginedFutures + +func TestGetFuturesOrderbook(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + _, err = b.GetFuturesOrderbook(context.Background(), pair) + if err != nil { + t.Fatal(err) + } +} + +func TestGetFuturesKlineData(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + _, err = b.GetFuturesKlineData(context.Background(), pair, "M", 5, time.Time{}) + if err != nil { + t.Error(err) + } + + _, err = b.GetFuturesKlineData(context.Background(), pair, "60", 5, time.Unix(1577836800, 0)) + if err != nil { + t.Error(err) + } +} + +func TestGetFuturesSymbolPriceTicker(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetFuturesSymbolPriceTicker(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestGetPublicTrades(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetPublicTrades(context.Background(), pair, 0) + if err != nil { + t.Error(err) + } + + _, err = b.GetPublicTrades(context.Background(), pair, 10000) + if err != nil { + t.Error(err) + } +} + +func TestGetSymbolsInfo(t *testing.T) { + t.Parallel() + _, err := b.GetSymbolsInfo(context.Background()) + if err != nil { + t.Error(err) + } +} + +func TestGetMarkPriceKline(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetMarkPriceKline(context.Background(), pair, "D", 0, time.Unix(1577836800, 0)) + if err != nil { + t.Error(err) + } +} + +func TestGetIndexPriceKline(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetIndexPriceKline(context.Background(), pair, "D", 0, time.Unix(1577836800, 0)) + if err != nil { + t.Error(err) + } +} + +func TestGetPremiumIndexPriceKline(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetPremiumIndexPriceKline(context.Background(), pair, "D", 0, time.Unix(1577836800, 0)) + if err != nil { + t.Error(err) + } +} + +func TestGetOpenInterest(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetOpenInterest(context.Background(), pair, "5min", 0) + if err != nil { + t.Error(err) + } +} + +func TestGetLatestBigDeal(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetLatestBigDeal(context.Background(), pair, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetAccountRatio(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetAccountRatio(context.Background(), pair, "1d", 0) + if err != nil { + t.Error(err) + } +} + +func TestGetRiskLimit(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetRiskLimit(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestGetLastFundingRate(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetLastFundingRate(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestGetFutureServerTime(t *testing.T) { + t.Parallel() + _, err := b.GetFuturesServerTime(context.Background()) + if err != nil { + t.Error(err) + } +} + +func TestGetAnnouncement(t *testing.T) { + t.Parallel() + _, err := b.GetAnnouncement(context.Background()) + if err != nil { + t.Error(err) + } +} + +func TestCreateCoinFuturesOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.CreateCoinFuturesOrder(context.Background(), pair, "Buy", "Limit", "GoodTillCancel", "", "", "", 1, 20000, 0, 0, false, false) + if err != nil { + t.Error(err) + } +} + +func TestGetActiveCoinFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetActiveCoinFuturesOrders(context.Background(), pair, "", "", "", 0) + if err != nil { + t.Error(err) + } + + _, err = b.GetActiveCoinFuturesOrders(context.Background(), pair, "Filled", "", "", 0) + if err != nil { + t.Error(err) + } +} + +func TestCancelActiveCoinFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelActiveCoinFuturesOrders(context.Background(), pair, "3bd1844f-f3c0-4e10-8c25-10fea03763f6", "") + if err != nil { + t.Error(err) + } +} + +func TestCancelAllActiveCoinFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelAllActiveCoinFuturesOrders(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestReplaceActiveCoinFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.ReplaceActiveCoinFuturesOrders(context.Background(), pair, "3bd1844f-f3c0-4e10-8c25-10fea03763f6", "", "", "", 1, 2, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetActiveRealtimeCoinOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetActiveRealtimeCoinOrders(context.Background(), pair, "", "") + if err != nil { + t.Error(err) + } +} + +func TestCreateConditionalCoinFuturesOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.CreateConditionalCoinFuturesOrder(context.Background(), pair, "Buy", "Limit", "GoodTillCancel", "", "", "", "", 1, 20000, 0, 0, 1, 1, false) + if err != nil { + t.Error(err) + } +} + +func TestGetConditionalCoinFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetConditionalCoinFuturesOrders(context.Background(), pair, "", "", "", 0) + if err != nil { + t.Error(err) + } +} + +func TestCancelConditionalCoinFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelConditionalCoinFuturesOrders(context.Background(), pair, "c1025629-e85b-4c26-b4f3-76e86ad9f8c", "") + if err != nil { + t.Error(err) + } +} + +func TestCancelAllConditionalCoinFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelAllConditionalCoinFuturesOrders(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestReplaceConditionalCoinFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.ReplaceConditionalCoinFuturesOrders(context.Background(), pair, "c1025629-e85b-4c26-b4f3-76e86ad9f8c", "", "", "", 0, 0, 0, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetConditionalRealtimeCoinOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetConditionalRealtimeCoinOrders(context.Background(), pair, "", "") + if err != nil { + t.Error(err) + } +} + +func TestGetCoinPositions(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetCoinPositions(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestSetCoinMargin(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetCoinMargin(context.Background(), pair, "10") + if err != nil { + t.Error(err) + } +} + +func TestSetCoinTradingAndStop(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetCoinTradingAndStop(context.Background(), pair, 0, 0, 0, 0, 0, 0, "", "") + if err != nil { + t.Error(err) + } +} + +func TestSetCoinLeverage(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetCoinLeverage(context.Background(), pair, 10, false) + if err != nil { + t.Error(err) + } +} + +func TestGetCoinTradeRecords(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetCoinTradeRecords(context.Background(), pair, "", "", 0, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetClosedCoinTrades(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetClosedCoinTrades(context.Background(), pair, "", time.Time{}, time.Time{}, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestChangeCoinMode(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.ChangeCoinMode(context.Background(), pair, "Partial") + if err != nil { + t.Error(err) + } +} + +func TestChangeCoinMargin(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + err = b.ChangeCoinMargin(context.Background(), pair, 1, 1, false) + if err != nil { + t.Error(err) + } +} + +func TestGetTradingFeeRate(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, _, err = b.GetTradingFeeRate(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestSetCoinRiskLimit(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetCoinRiskLimit(context.Background(), pair, 2) + if err != nil { + t.Error(err) + } +} + +func TestGetCoinLastFundingFee(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetCoinLastFundingFee(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestGetCoinPredictedFundingRate(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, _, err = b.GetCoinPredictedFundingRate(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestGetAPIKeyInfo(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + _, err := b.GetAPIKeyInfo(context.Background()) + if err != nil { + t.Error(err) + } +} + +func TestGetLCPInfo(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetLiquidityContributionPointsInfo(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestGetFutureWalletBalance(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + _, err := b.GetFutureWalletBalance(context.Background(), "BTC") + if err != nil { + t.Error(err) + } +} + +func TestGetWalletFundRecords(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + _, err := b.GetWalletFundRecords(context.Background(), "2021-09-11", "2021-10-09", "ETH", "", "", 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetWalletWithdrawalRecords(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + _, err := b.GetWalletWithdrawalRecords(context.Background(), "2021-09-11", "2021-10-09", "", currency.ETH, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetAssetExchangeRecords(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + _, err := b.GetAssetExchangeRecords(context.Background(), "", 0, 0) + if err != nil { + t.Error(err) + } +} + +// test cases for USDTMarginedFutures + +func TestGetUSDTFuturesKlineData(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + _, err = b.GetUSDTFuturesKlineData(context.Background(), pair, "M", 5, time.Time{}) + if err != nil { + t.Error(err) + } + + _, err = b.GetUSDTFuturesKlineData(context.Background(), pair, "60", 5, time.Unix(1577836800, 0)) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDTPublicTrades(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDTPublicTrades(context.Background(), pair, 1000) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDTMarkPriceKline(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDTMarkPriceKline(context.Background(), pair, "D", 0, time.Unix(1577836800, 0)) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDTIndexPriceKline(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDTIndexPriceKline(context.Background(), pair, "D", 0, time.Unix(1577836800, 0)) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDTPremiumIndexPriceKline(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDTPremiumIndexPriceKline(context.Background(), pair, "D", 0, time.Unix(1577836800, 0)) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDTLastFundingRate(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDTLastFundingRate(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDTRiskLimit(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDTRiskLimit(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestCreateUSDTFuturesOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.CreateUSDTFuturesOrder(context.Background(), pair, "Buy", "Limit", "GoodTillCancel", "", "", "", 1, 10000, 0, 0, false, false) + if err != nil { + t.Error(err) + } +} + +func TestGetActiveUSDTFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetActiveUSDTFuturesOrders(context.Background(), pair, "", "", "", "", 0, 0) + if err != nil { + t.Error(err) + } + + _, err = b.GetActiveUSDTFuturesOrders(context.Background(), pair, "Filled", "", "", "", 0, 50) + if err != nil { + t.Error(err) + } +} + +func TestCancelActiveUSDTFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelActiveUSDTFuturesOrders(context.Background(), pair, "3bd1844f-f3c0-4e10-8c25-10fea03763f6", "") + if err != nil { + t.Error(err) + } +} + +func TestCancelAllActiveUSDTFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelAllActiveUSDTFuturesOrders(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestReplaceActiveUSDTFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.ReplaceActiveUSDTFuturesOrders(context.Background(), pair, "3bd1844f-f3c0-4e10-8c25-10fea03763f6", "", "", "", 1, 2, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetActiveUSDTRealtimeOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetActiveUSDTRealtimeOrders(context.Background(), pair, "", "") + if err != nil { + t.Error(err) + } +} + +func TestCreateConditionalUSDTFuturesOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.CreateConditionalUSDTFuturesOrder(context.Background(), pair, "Buy", "Limit", "GoodTillCancel", "", "", "", "", 1, 0.5, 0, 0, 1, 1, false, false) + if err != nil { + t.Error(err) + } +} + +func TestGetConditionalUSDTFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetConditionalUSDTFuturesOrders(context.Background(), pair, "", "", "", "", 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestCancelConditionalUSDTFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelConditionalUSDTFuturesOrders(context.Background(), pair, "c1025629-e85b-4c26-b4f3-76e86ad9f8c", "") + if err != nil { + t.Error(err) + } +} + +func TestCancelAllConditionalUSDTFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelAllConditionalUSDTFuturesOrders(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestReplaceConditionalUSDTFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.ReplaceConditionalUSDTFuturesOrders(context.Background(), pair, "c1025629-e85b-4c26-b4f3-76e86ad9f8c", "", "", "", 0, 0, 0, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetConditionalUSDTRealtimeOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetConditionalUSDTRealtimeOrders(context.Background(), pair, "", "") + if err != nil { + t.Error(err) + } + + expectedErr := "Order not exists" + _, err = b.GetConditionalUSDTRealtimeOrders(context.Background(), pair, "1234", "") + if err != nil && err.Error() != expectedErr { + t.Error(err) + } + + _, err = b.GetConditionalUSDTRealtimeOrders(context.Background(), pair, "", "1234") + if err != nil && err.Error() != expectedErr { + t.Error(err) + } +} + +func TestGetUSDTPositions(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDTPositions(context.Background(), pair) + if err != nil { + t.Error(err) + } + + _, err = b.GetUSDTPositions(context.Background(), currency.EMPTYPAIR) + if err != nil { + t.Error(err) + } +} + +func TestSetAutoAddMargin(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + err = b.SetAutoAddMargin(context.Background(), pair, true, "Sell") + if err != nil { + t.Error(err) + } +} + +func TestChangeUSDTMargin(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + err = b.ChangeUSDTMargin(context.Background(), pair, 1, 1, true) + if err != nil { + t.Error(err) + } +} + +func TestSwitchPositionMode(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + err = b.SwitchPositionMode(context.Background(), pair, "BothSide") + if err != nil { + t.Error(err) + } +} + +func TestChangeUSDTMode(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.ChangeUSDTMode(context.Background(), pair, "Partial") + if err != nil { + t.Error(err) + } +} + +func TestSetUSDTMargin(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetUSDTMargin(context.Background(), pair, "Buy", "10") + if err != nil { + t.Error(err) + } +} + +func TestSetUSDTLeverage(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + err = b.SetUSDTLeverage(context.Background(), pair, 10, 10) + if err != nil { + t.Error(err) + } +} + +func TestSetUSDTTradingAndStop(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + err = b.SetUSDTTradingAndStop(context.Background(), pair, 0, 0, 0, 0, 0, "Buy", "", "") + if err != nil { + t.Error(err) + } +} + +func TestGetUSDTTradeRecords(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDTTradeRecords(context.Background(), pair, "", 0, 0, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetClosedUSDTTrades(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetClosedUSDTTrades(context.Background(), pair, "", time.Time{}, time.Time{}, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestSetUSDTRiskLimit(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetUSDTRiskLimit(context.Background(), pair, "Buy", 2) + if err != nil { + t.Error(err) + } +} + +func TestGetPredictedUSDTFundingRate(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, _, err = b.GetPredictedUSDTFundingRate(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestGetLastUSDTFundingFee(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetLastUSDTFundingFee(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +// test cases for Futures + +func TestCreateFuturesOrderr(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.CreateFuturesOrder(context.Background(), 1, pair, "Buy", "Market", "GoodTillCancel", "", "", "", 10, 1, 0, 0, false, false) + if err != nil { + t.Error(err) + } +} + +func TestGetActiveFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetActiveFuturesOrders(context.Background(), pair, "", "", "", 0) + if err != nil { + t.Error(err) + } + + _, err = b.GetActiveFuturesOrders(context.Background(), pair, "Filled", "", "", 0) + if err != nil { + t.Error(err) + } +} + +func TestCancelActiveFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelActiveFuturesOrders(context.Background(), pair, "3bd1844f-f3c0-4e10-8c25-10fea03763f6", "") + if err != nil { + t.Error(err) + } +} + +func TestCancelAllActiveFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelAllActiveFuturesOrders(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestReplaceActiveFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.ReplaceActiveFuturesOrders(context.Background(), pair, "3bd1844f-f3c0-4e10-8c25-10fea03763f6", "", "", "", 1, 2, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetActiveRealtimeOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetActiveRealtimeOrders(context.Background(), pair, "", "") + if err != nil { + t.Error(err) + } +} + +func TestCreateConditionalFuturesOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.CreateConditionalFuturesOrder(context.Background(), 0, pair, "Buy", "Limit", "GoodTillCancel", "", "", "", "", 1, 0.5, 0, 0, 1, 1, false) + if err != nil { + t.Error(err) + } +} + +func TestGetConditionalFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetConditionalFuturesOrders(context.Background(), pair, "", "", "", 0) + if err != nil { + t.Error(err) + } +} + +func TestCancelConditionalFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelConditionalFuturesOrders(context.Background(), pair, "c1025629-e85b-4c26-b4f3-76e86ad9f8c", "") + if err != nil { + t.Error(err) + } +} + +func TestCancelAllConditionalFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelAllConditionalFuturesOrders(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestReplaceConditionalFuturesOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.ReplaceConditionalFuturesOrders(context.Background(), pair, "c1025629-e85b-4c26-b4f3-76e86ad9f8c", "", "", "", 0, 0, 0, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetConditionalRealtimeOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetConditionalRealtimeOrders(context.Background(), pair, "", "") + if err != nil { + t.Error(err) + } +} + +func TestGetPositions(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetPositions(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestSetMargin(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetMargin(context.Background(), 0, pair, "10") + if err != nil { + t.Error(err) + } +} + +func TestSetTradingAndStop(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetTradingAndStop(context.Background(), 0, pair, 0, 0, 0, 0, 0, 0, "", "") + if err != nil { + t.Error(err) + } +} + +func TestSetLeverage(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetLeverage(context.Background(), pair, 10, 10) + if err != nil { + t.Error(err) + } +} + +func TestChangePositionMode(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + err = b.ChangePositionMode(context.Background(), pair, 3) + if err != nil { + t.Error(err) + } +} + +func TestChangeMode(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.ChangeMode(context.Background(), pair, "Partial") + if err != nil { + t.Error(err) + } +} + +func TestChangeMargin(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + err = b.ChangeMargin(context.Background(), pair, 1, 1, false) + if err != nil { + t.Error(err) + } +} + +func TestGetTradeRecords(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetTradeRecords(context.Background(), pair, "", "", 0, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetClosedTrades(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetClosedTrades(context.Background(), pair, "", time.Time{}, time.Time{}, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestSetRiskLimit(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetRiskLimit(context.Background(), pair, 2, 0) + if err != nil { + t.Error(err) + } +} + +// Miscellaneous + +func TestTimeSecUnmarshalJSON(t *testing.T) { + t.Parallel() + tInSec := time.Now().Unix() + + var ts bybitTimeSec + err := ts.UnmarshalJSON([]byte(strconv.FormatInt(tInSec, 10))) + if err != nil { + t.Fatal(err) + } + + if !time.Unix(tInSec, 0).Equal(ts.Time()) { + t.Errorf("TestTimeSecUnmarshalJSON failed") + } +} + +func TestTimeMilliSecUnmarshalJSON(t *testing.T) { + t.Parallel() + tInMilliSec := time.Now().UnixMilli() + + var tms bybitTimeMilliSec + err := tms.UnmarshalJSON([]byte(strconv.FormatInt(tInMilliSec, 10))) + if err != nil { + t.Fatal(err) + } + + if !time.UnixMilli(tInMilliSec).Equal(tms.Time()) { + t.Errorf("TestTimeMilliSecUnmarshalJSON failed") + } +} + +func TestTimeNanoSecUnmarshalJSON(t *testing.T) { + t.Parallel() + tInNanoSec := time.Now().UnixNano() + + var tns bybitTimeNanoSec + err := tns.UnmarshalJSON([]byte(strconv.FormatInt(tInNanoSec, 10))) + if err != nil { + t.Fatal(err) + } + + if !time.Unix(0, tInNanoSec).Equal(tns.Time()) { + t.Errorf("TestTimeNanoSecUnmarshalJSON failed") + } +} + +// test cases for Wrapper +func TestUpdateTicker(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.UpdateTicker(context.Background(), pair, asset.Spot) + if err != nil { + t.Error(err) + } + + _, err = b.UpdateTicker(context.Background(), pair, asset.USDTMarginedFutures) + if err != nil { + t.Error(err) + } + + pair1, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.UpdateTicker(context.Background(), pair1, asset.CoinMarginedFutures) + if err != nil { + t.Error(err) + } + + pair2, err := currency.NewPairFromString("BTCUSD-Z22") + if err != nil { + t.Fatal(err) + } + + _, err = b.UpdateTicker(context.Background(), pair2, asset.Futures) + if err != nil { + t.Error(err) + } + + pair3, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.UpdateTicker(context.Background(), pair3, asset.USDCMarginedFutures) + if err != nil { + t.Error(err) + } +} + +func TestUpdateOrderbook(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.UpdateOrderbook(context.Background(), pair, asset.Spot) + if err != nil { + t.Error(err) + } + + _, err = b.UpdateOrderbook(context.Background(), pair, asset.CoinMarginedFutures) + if err != nil { + t.Error(err) + } + + _, err = b.UpdateOrderbook(context.Background(), pair, asset.USDTMarginedFutures) + if err != nil { + t.Error(err) + } + + _, err = b.UpdateOrderbook(context.Background(), pair, asset.Futures) + if err != nil { + t.Error(err) + } + + pair1, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.UpdateOrderbook(context.Background(), pair1, asset.USDCMarginedFutures) + if err != nil { + t.Error(err) + } +} + +func TestFetchTradablePairs(t *testing.T) { + t.Parallel() + _, err := b.FetchTradablePairs(context.Background(), asset.Spot) + if err != nil { + t.Error(err) + } + + _, err = b.FetchTradablePairs(context.Background(), asset.CoinMarginedFutures) + if err != nil { + t.Error(err) + } + + _, err = b.FetchTradablePairs(context.Background(), asset.USDTMarginedFutures) + if err != nil { + t.Error(err) + } + + _, err = b.FetchTradablePairs(context.Background(), asset.Futures) + if err != nil { + t.Error(err) + } + + _, err = b.FetchTradablePairs(context.Background(), asset.USDCMarginedFutures) + if err != nil { + t.Error(err) + } +} + +func TestUpdateTradablePairs(t *testing.T) { + t.Parallel() + err := b.UpdateTradablePairs(context.Background(), false) + if err != nil { + t.Error(err) + } + + err = b.UpdateTradablePairs(context.Background(), true) + if err != nil { + t.Error(err) + } +} + +func TestGetRecentTrades(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetRecentTrades(context.Background(), pair, asset.Spot) + if err != nil { + t.Error(err) + } + + _, err = b.GetRecentTrades(context.Background(), pair, asset.USDTMarginedFutures) + if err != nil { + t.Error(err) + } + + pair1, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetRecentTrades(context.Background(), pair1, asset.CoinMarginedFutures) + if err != nil { + t.Error(err) + } + + _, err = b.GetRecentTrades(context.Background(), pair1, asset.Futures) + if err != nil { + t.Error(err) + } + + pair2, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetRecentTrades(context.Background(), pair2, asset.USDCMarginedFutures) + if err != nil { + t.Error(err) + } +} + +func TestGetHistoricCandles(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + startTime := time.Unix(1546300800, 0) + end := time.Unix(1577836799, 0) + + _, err = b.GetHistoricCandles(context.Background(), pair, asset.Spot, startTime, end, kline.OneDay) + if err != nil { + t.Error(err) + } + + _, err = b.GetHistoricCandles(context.Background(), pair, asset.USDTMarginedFutures, startTime, end, kline.OneDay) + if err != nil { + t.Error(err) + } + + pair1, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetHistoricCandles(context.Background(), pair1, asset.CoinMarginedFutures, startTime, end, kline.OneMin) + if err != nil { + t.Error(err) + } + + pair2, err := currency.NewPairFromString("BTCUSD-Z22") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetHistoricCandles(context.Background(), pair2, asset.Futures, startTime, end, kline.OneMin) + if err != nil { + t.Error(err) + } + + pair3, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetHistoricCandles(context.Background(), pair3, asset.USDCMarginedFutures, startTime, end, kline.OneDay) + if err != nil { + t.Error(err) + } +} + +func TestGetHistoricCandlesExtended(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + startTime := time.Now().Add(-time.Hour * 24 * 3) + end := time.Now().Add(-time.Hour * 1) + + _, err = b.GetHistoricCandlesExtended(context.Background(), pair, asset.Spot, startTime, end, kline.OneMin) + if err != nil { + t.Error(err) + } + + _, err = b.GetHistoricCandlesExtended(context.Background(), pair, asset.USDTMarginedFutures, startTime, end, kline.OneMin) + if err != nil { + t.Error(err) + } + + pair1, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetHistoricCandlesExtended(context.Background(), pair1, asset.CoinMarginedFutures, startTime, end, kline.OneHour) + if err != nil { + t.Error(err) + } + + pair2, err := currency.NewPairFromString("BTCUSD-Z22") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetHistoricCandlesExtended(context.Background(), pair2, asset.Futures, startTime, end, kline.OneDay) + if err != nil { + t.Error(err) + } + + pair3, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetHistoricCandlesExtended(context.Background(), pair3, asset.USDCMarginedFutures, startTime, end, kline.FiveMin) + if err != nil { + t.Error(err) + } +} + +func TestFetchAccountInfo(t *testing.T) { + if !areTestAPIKeysSet() { + t.SkipNow() + } + t.Parallel() + + _, err := b.FetchAccountInfo(context.Background(), asset.Spot) + if err != nil { + t.Error(err) + } + + _, err = b.FetchAccountInfo(context.Background(), asset.CoinMarginedFutures) + if err != nil { + t.Error(err) + } + + _, err = b.FetchAccountInfo(context.Background(), asset.USDTMarginedFutures) + if err != nil { + t.Error(err) + } + + _, err = b.FetchAccountInfo(context.Background(), asset.Futures) + if err != nil { + t.Error(err) + } + + _, err = b.FetchAccountInfo(context.Background(), asset.USDCMarginedFutures) + if err != nil && err.Error() != "System error. Please try again later." { + t.Error(err) + } +} + +func TestSubmitOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + + var oSpot = &order.Submit{ + Exchange: "Bybit", + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.LTC, + Quote: currency.BTC, + }, + Side: order.Buy, + Type: order.Limit, + Price: 0.0001, + Amount: 10, + ClientID: "newOrder", + AssetType: asset.Spot, + } + _, err := b.SubmitOrder(context.Background(), oSpot) + if err != nil { + if strings.TrimSpace(err.Error()) != "Balance insufficient" { + t.Error(err) + } + } + + var oCMF = &order.Submit{ + Exchange: "Bybit", + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.BTC, + Quote: currency.USD, + }, + Side: order.Buy, + Type: order.Limit, + Price: 10000, + Amount: 1, + ClientID: "newOrder", + AssetType: asset.CoinMarginedFutures, + } + _, err = b.SubmitOrder(context.Background(), oCMF) + if err == nil { + t.Error("SubmitOrder() Expected error") + } + + var oUMF = &order.Submit{ + Exchange: "Bybit", + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.BTC, + Quote: currency.USDT, + }, + Side: order.Buy, + Type: order.Limit, + Price: 10000, + Amount: 1, + ClientID: "newOrder", + AssetType: asset.USDTMarginedFutures, + } + _, err = b.SubmitOrder(context.Background(), oUMF) + if err == nil { + t.Error("SubmitOrder() Expected error") + } + + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + var oFutures = &order.Submit{ + Exchange: "Bybit", + Pair: pair, + Side: order.Buy, + Type: order.Limit, + Price: 10000, + Amount: 1, + ClientID: "newOrder", + AssetType: asset.Futures, + } + _, err = b.SubmitOrder(context.Background(), oFutures) + if err != nil { + t.Error(err) + } + + pair1, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + var oUSDC = &order.Submit{ + Exchange: "Bybit", + Pair: pair1, + Side: order.Buy, + Type: order.Limit, + Price: 10000, + Amount: 1, + ClientID: "newOrder", + AssetType: asset.USDCMarginedFutures, + } + _, err = b.SubmitOrder(context.Background(), oUSDC) + if err != nil && err.Error() != "margin account not exist" { + t.Error(err) + } +} + +func TestModifyOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + + _, err := b.ModifyOrder(context.Background(), &order.Modify{ + Exchange: "Bybit", + OrderID: "1337", + Price: 10000, + Amount: 10, + Side: order.Sell, + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.BTC, + Quote: currency.USD, + }, + AssetType: asset.CoinMarginedFutures, + }) + if err == nil { + t.Error("ModifyOrder() Expected error") + } +} + +func TestCancelOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + + err := b.CancelOrder(context.Background(), &order.Cancel{ + Exchange: "Bybit", + AssetType: asset.Spot, + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.BTC, + Quote: currency.USD, + }, + OrderID: "1234", + }) + if err == nil { + t.Error("CancelOrder() Spot Expected error") + } + + err = b.CancelOrder(context.Background(), &order.Cancel{ + Exchange: "Bybit", + AssetType: asset.CoinMarginedFutures, + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.BTC, + Quote: currency.USD, + }, + OrderID: "1234", + }) + if err == nil { + t.Error("CancelOrder() CMF Expected error") + } + + err = b.CancelOrder(context.Background(), &order.Cancel{ + Exchange: "Bybit", + AssetType: asset.USDTMarginedFutures, + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.BTC, + Quote: currency.USDT, + }, + OrderID: "1234", + }) + if err == nil { + t.Error("CancelOrder() USDT Expected error") + } + + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + err = b.CancelOrder(context.Background(), &order.Cancel{ + Exchange: "Bybit", + AssetType: asset.Futures, + Pair: pair, + OrderID: "1234", + }) + if err == nil { + t.Error("CancelOrder() Futures Expected error") + } + + pair1, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + err = b.CancelOrder(context.Background(), &order.Cancel{ + Exchange: "Bybit", + AssetType: asset.Futures, + Pair: pair1, + OrderID: "1234", + }) + if err == nil { + t.Error("CancelOrder() USDC Expected error") + } +} + +func TestCancelAllOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + + _, err := b.CancelAllOrders(context.Background(), + &order.Cancel{AssetType: asset.Spot}) + if err != nil { + t.Error(err) + } + + _, err = b.CancelAllOrders(context.Background(), + &order.Cancel{ + Exchange: "Bybit", + AssetType: asset.CoinMarginedFutures, + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.BTC, + Quote: currency.USD, + }, + }) + if err != nil { + t.Error(err) + } + + _, err = b.CancelAllOrders(context.Background(), + &order.Cancel{ + Exchange: "Bybit", + AssetType: asset.USDTMarginedFutures, + Pair: currency.Pair{ + Delimiter: "-", + Base: currency.BTC, + Quote: currency.USDT, + }, + }) + if err != nil { + t.Error(err) + } + + pair, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelAllOrders(context.Background(), + &order.Cancel{ + Exchange: "Bybit", + AssetType: asset.Futures, + Pair: pair, + }) + if err != nil { + t.Error(err) + } + + pair1, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelAllOrders(context.Background(), + &order.Cancel{ + Exchange: "Bybit", + AssetType: asset.USDCMarginedFutures, + Pair: pair1, + }) + if err != nil { + t.Error(err) + } +} + +func TestGetOrderInfo(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetOrderInfo(context.Background(), + "12234", pair, asset.Spot) + if err == nil { + t.Error("GetOrderInfo() Spot Expected error") + } + + _, err = b.GetOrderInfo(context.Background(), + "12234", pair, asset.USDTMarginedFutures) + if err == nil { + t.Error("GetOrderInfo() USDT Expected error") + } + + pair1, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetOrderInfo(context.Background(), + "12234", pair1, asset.CoinMarginedFutures) + if err == nil { + t.Error("GetOrderInfo() CMF Expected error") + } + + pair2, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetOrderInfo(context.Background(), + "12234", pair2, asset.Futures) + if err == nil { + t.Error("GetOrderInfo() Futures Expected error") + } + + pair3, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetOrderInfo(context.Background(), + "12234", pair3, asset.USDCMarginedFutures) + if err == nil { + t.Error("GetOrderInfo() USDC Expected error") + } +} + +func TestGetActiveOrders(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + var getOrdersRequestSpot = order.GetOrdersRequest{ + Pairs: currency.Pairs{pair}, + AssetType: asset.Spot, + } + + _, err = b.GetActiveOrders(context.Background(), &getOrdersRequestSpot) + if err != nil { + t.Error(err) + } + + var getOrdersRequestUMF = order.GetOrdersRequest{ + Pairs: currency.Pairs{pair}, + AssetType: asset.USDTMarginedFutures, + } + + _, err = b.GetActiveOrders(context.Background(), &getOrdersRequestUMF) + if err != nil { + t.Error(err) + } + + pair1, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + var getOrdersRequestCMF = order.GetOrdersRequest{ + Pairs: currency.Pairs{pair1}, + AssetType: asset.CoinMarginedFutures, + } + + _, err = b.GetActiveOrders(context.Background(), &getOrdersRequestCMF) + if err != nil { + t.Error(err) + } + + pair2, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + var getOrdersRequestFutures = order.GetOrdersRequest{ + Pairs: currency.Pairs{pair2}, + AssetType: asset.Futures, + } + + _, err = b.GetActiveOrders(context.Background(), &getOrdersRequestFutures) + if err != nil { + t.Error(err) + } + + pair3, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + var getOrdersRequestUSDC = order.GetOrdersRequest{ + Pairs: currency.Pairs{pair3}, + AssetType: asset.USDCMarginedFutures, + } + + _, err = b.GetActiveOrders(context.Background(), &getOrdersRequestUSDC) + if err != nil { + t.Error(err) + } +} + +func TestGetOrderHistory(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + pair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } + + var getOrdersRequestSpot = order.GetOrdersRequest{ + Pairs: currency.Pairs{pair}, + AssetType: asset.Spot, + } + + _, err = b.GetOrderHistory(context.Background(), &getOrdersRequestSpot) + if err != nil { + t.Error(err) + } + + var getOrdersRequestUMF = order.GetOrdersRequest{ + Pairs: currency.Pairs{pair}, + AssetType: asset.USDTMarginedFutures, + } + + _, err = b.GetOrderHistory(context.Background(), &getOrdersRequestUMF) + if err != nil { + t.Error(err) + } + + pair1, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + var getOrdersRequestCMF = order.GetOrdersRequest{ + Pairs: currency.Pairs{pair1}, + AssetType: asset.CoinMarginedFutures, + } + + _, err = b.GetOrderHistory(context.Background(), &getOrdersRequestCMF) + if err != nil { + t.Error(err) + } + + pair2, err := currency.NewPairFromString("BTCUSDZ22") + if err != nil { + t.Fatal(err) + } + + var getOrdersRequestFutures = order.GetOrdersRequest{ + Pairs: currency.Pairs{pair2}, + AssetType: asset.Futures, + } + + _, err = b.GetOrderHistory(context.Background(), &getOrdersRequestFutures) + if err != nil { + t.Error(err) + } + + pair3, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + var getOrdersRequestUSDC = order.GetOrdersRequest{ + Pairs: currency.Pairs{pair3}, + AssetType: asset.USDCMarginedFutures, + } + + _, err = b.GetOrderHistory(context.Background(), &getOrdersRequestUSDC) + if err != nil { + t.Error(err) + } +} + +func TestGetWithdrawalsHistory(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + _, err := b.GetWithdrawalsHistory(context.Background(), currency.BTC, asset.CoinMarginedFutures) + if err != nil { + t.Error(err) + } + + _, err = b.GetWithdrawalsHistory(context.Background(), currency.BTC, asset.Spot) + if err == nil { + t.Error("GetWithdrawalsHistory() Spot Expected error") + } +} + +func TestGetServerTime(t *testing.T) { + t.Parallel() + + _, err := b.GetServerTime(context.Background(), asset.CoinMarginedFutures) + if err != nil { + t.Error(err) + } + + _, err = b.GetServerTime(context.Background(), asset.Spot) + if err != nil { + t.Error(err) + } +} + +func TestGetDepositAddress(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + _, err := b.GetDepositAddress(context.Background(), currency.USDT, "", currency.ETH.String()) + if err != nil { + t.Error(err) + } +} + +func TestGetAvailableTransferChains(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + _, err := b.GetAvailableTransferChains(context.Background(), currency.USDT) + if err != nil { + t.Error(err) + } +} + +func TestWithdrawCryptocurrencyFunds(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + _, err := b.WithdrawCryptocurrencyFunds(context.Background(), &withdraw.Request{ + Exchange: "Bybit", + Amount: 10, + Currency: currency.LTC, + Crypto: withdraw.CryptoRequest{ + Chain: currency.LTC.String(), + Address: "3CDJNfdWX8m2NwuGUV3nhXHXEeLygMXoAj", + AddressTag: "", + }}) + if err != nil && err.Error() != "Withdraw address chain or destination tag are not equal" { + t.Fatal(err) + } +} + +// test cases for USDCMarginedFutures + +func TestGetUSDCFuturesOrderbook(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCFuturesOrderbook(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCContracts(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCContracts(context.Background(), pair, "next", 1500) + if err != nil { + t.Error(err) + } + + _, err = b.GetUSDCContracts(context.Background(), currency.EMPTYPAIR, "", 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCSymbols(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCSymbols(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCKlines(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCKlines(context.Background(), pair, "5", time.Now().Add(-time.Hour), 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCMarkPriceKlines(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCMarkPriceKlines(context.Background(), pair, "5", time.Now().Add(-time.Hour), 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCIndexPriceKlines(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCIndexPriceKlines(context.Background(), pair, "5", time.Now().Add(-time.Hour), 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCPremiumIndexKlines(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCPremiumIndexKlines(context.Background(), pair, "5", time.Now().Add(-time.Hour), 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCOpenInterest(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCOpenInterest(context.Background(), pair, "1d", 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCLargeOrders(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCLargeOrders(context.Background(), pair, 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCAccountRatio(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCAccountRatio(context.Background(), pair, "1d", 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCLatestTrades(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCLatestTrades(context.Background(), pair, "PERPETUAL", 0) + if err != nil { + t.Error(err) + } +} + +func TestPlaceUSDCOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.PlaceUSDCOrder(context.Background(), pair, "Limit", "Order", "Buy", "", "", 10000, 1, 0, 0, 0, 0, 0, 0, false, false, false) + if err != nil { + t.Error(err) + } + + _, err = b.PlaceUSDCOrder(context.Background(), pair, "Market", "StopOrder", "Buy", "ImmediateOrCancel", "", 0, 64300, 0, 0, 0, 0, 1000, 0, false, false, false) + if err != nil { + t.Error(err) + } +} + +func TestModifyUSDCOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.ModifyUSDCOrder(context.Background(), pair, "Order", "", "orderLinkID", 0, 0, 0, 0, 0, 0, 0) + if err != nil { + t.Error(err) + } +} + +func TestCancelUSDCOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.CancelUSDCOrder(context.Background(), pair, "Order", "", "orderLinkID") + if err != nil { + t.Error(err) + } +} + +func TestCancelAllActiveUSDCOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + err = b.CancelAllActiveUSDCOrder(context.Background(), pair, "Order") + if err != nil { + t.Error(err) + } +} + +func TestGetActiveUSDCOrder(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetActiveUSDCOrder(context.Background(), pair, "PERPETUAL", "", "", "", "", "", 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCOrderHistory(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCOrderHistory(context.Background(), pair, "PERPETUAL", "", "", "", "", "", 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCTradeHistory(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCTradeHistory(context.Background(), pair, "PERPETUAL", "", "orderLinkID", "", "", 50, time.Now().Add(-time.Hour)) + if err == nil { // order with link ID "orderLinkID" not present + t.Error("GetUSDCTradeHistory() Expected error") + } +} + +func TestGetUSDCTransactionLog(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + _, err := b.GetUSDCTransactionLog(context.Background(), time.Time{}, time.Time{}, "TRADE", "", "", "", 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCWalletBalance(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + _, err := b.GetUSDCWalletBalance(context.Background()) + if err != nil && err.Error() != "System error. Please try again later." { + t.Error(err) + } +} + +func TestGetUSDCAssetInfo(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + _, err := b.GetUSDCAssetInfo(context.Background(), "") + if err != nil { + t.Error(err) + } + + _, err = b.GetUSDCAssetInfo(context.Background(), "BTC") + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCMarginInfo(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + _, err := b.GetUSDCMarginInfo(context.Background()) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCPositions(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCPosition(context.Background(), pair, "PERPETUAL", "", "", 0) + if err != nil { + t.Error(err) + } +} + +func TestSetUSDCLeverage(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetUSDCLeverage(context.Background(), pair, 2) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCSettlementHistory(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCSettlementHistory(context.Background(), pair, "", "", 0) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCRiskLimit(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCRiskLimit(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestSetUSDCRiskLimit(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() || !canManipulateRealOrders { + t.Skip("skipping test: api keys not set or canManipulateRealOrders set to false") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.SetUSDCRiskLimit(context.Background(), pair, 2) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCLastFundingRate(t *testing.T) { + t.Parallel() + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, err = b.GetUSDCLastFundingRate(context.Background(), pair) + if err != nil { + t.Error(err) + } +} + +func TestGetUSDCPredictedFundingRate(t *testing.T) { + t.Parallel() + if !areTestAPIKeysSet() { + t.Skip("skipping test: api keys not set") + } + + pair, err := currency.NewPairFromString("BTCPERP") + if err != nil { + t.Fatal(err) + } + + _, _, err = b.GetUSDCPredictedFundingRate(context.Background(), pair) + if err != nil { + t.Error(err) + } +} diff --git a/exchanges/bybit/bybit_types.go b/exchanges/bybit/bybit_types.go new file mode 100644 index 00000000..2ce6c2eb --- /dev/null +++ b/exchanges/bybit/bybit_types.go @@ -0,0 +1,917 @@ +package bybit + +import ( + "encoding/json" + "errors" + "strconv" + "time" + + "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" +) + +var ( + errTypeAssert = errors.New("type assertion failed") + errStrParsing = errors.New("parsing string failed") + errInvalidSide = errors.New("invalid side") + errInvalidInterval = errors.New("invalid interval") + errInvalidPeriod = errors.New("invalid period") + errInvalidStartTime = errors.New("startTime can't be zero or missing") + errInvalidQuantity = errors.New("quantity can't be zero or missing") + errInvalidBasePrice = errors.New("basePrice can't be empty or missing") + errInvalidStopPrice = errors.New("stopPrice can't be empty or missing") + errInvalidTimeInForce = errors.New("timeInForce can't be empty or missing") + errInvalidTakeProfitStopLoss = errors.New("takeProfitStopLoss can't be empty or missing") + errInvalidMargin = errors.New("margin can't be empty") + errInvalidLeverage = errors.New("leverage can't be zero or less then it") + errInvalidRiskID = errors.New("riskID can't be zero or lesser") + errInvalidPositionMode = errors.New("position mode is invalid") + errInvalidOrderType = errors.New("orderType can't be empty or missing") + errInvalidMode = errors.New("mode can't be empty or missing") + errInvalidBuyLeverage = errors.New("buyLeverage can't be zero or less then it") + errInvalidSellLeverage = errors.New("sellLeverage can't be zero or less then it") + errInvalidOrderRequest = errors.New("order request param can't be nil") + errInvalidOrderFilter = errors.New("orderFilter can't be empty or missing") + errInvalidCategory = errors.New("invalid category") + errInvalidCoin = errors.New("coin can't be empty") + + errStopOrderOrOrderLinkIDMissing = errors.New("atleast one should be present among stopOrderID and orderLinkID") + errOrderOrOrderLinkIDMissing = errors.New("atleast one should be present among orderID and orderLinkID") + + errSymbolMissing = errors.New("symbol missing") + errUnsupportedOrderType = errors.New("unsupported order type") + errEmptyOrderIDs = errors.New("orderIDs can't be empty") + errMissingPrice = errors.New("price should be present for Limit and LimitMaker orders") + errExpectedOneOrder = errors.New("expected one order") +) + +// bybitTimeSec provides an internal conversion helper +type bybitTimeSec time.Time + +// UnmarshalJSON is custom json unmarshaller for bybitTimeSec +func (b *bybitTimeSec) UnmarshalJSON(data []byte) error { + var timestamp int64 + err := json.Unmarshal(data, ×tamp) + if err != nil { + return err + } + *b = bybitTimeSec(time.Unix(timestamp, 0)) + return nil +} + +// Time returns a time.Time object +func (b bybitTimeSec) Time() time.Time { + return time.Time(b) +} + +// bybitTimeSecStr provides an internal conversion helper +type bybitTimeSecStr time.Time + +// UnmarshalJSON is custom json unmarshaller for bybitTimeSec +func (b *bybitTimeSecStr) UnmarshalJSON(data []byte) error { + var timestamp string + err := json.Unmarshal(data, ×tamp) + if err != nil { + return err + } + + t, err := strconv.ParseInt(timestamp, 10, 64) + if err != nil { + return err + } + *b = bybitTimeSecStr(time.Unix(t, 0)) + return nil +} + +// Time returns a time.Time object +func (b bybitTimeSecStr) Time() time.Time { + return time.Time(b) +} + +// bybitTimeMilliSec provides an internal conversion helper +type bybitTimeMilliSec time.Time + +// UnmarshalJSON is custom type json unmarshaller for bybitTimeMilliSec +func (b *bybitTimeMilliSec) UnmarshalJSON(data []byte) error { + var timestamp int64 + err := json.Unmarshal(data, ×tamp) + if err != nil { + return err + } + *b = bybitTimeMilliSec(time.UnixMilli(timestamp)) + return nil +} + +// Time returns a time.Time object +func (b bybitTimeMilliSec) Time() time.Time { + return time.Time(b) +} + +// bybitTimeMilliSecStr provides an internal conversion helper +type bybitTimeMilliSecStr time.Time + +// UnmarshalJSON is custom type json unmarshaller for bybitTimeMilliSec +func (b *bybitTimeMilliSecStr) UnmarshalJSON(data []byte) error { + var timestamp string + err := json.Unmarshal(data, ×tamp) + if err != nil { + return err + } + + t, err := strconv.ParseInt(timestamp, 10, 64) + if err != nil { + return err + } + *b = bybitTimeMilliSecStr(time.UnixMilli(t)) + return nil +} + +// Time returns a time.Time object +func (b bybitTimeMilliSecStr) Time() time.Time { + return time.Time(b) +} + +// bybitTimeNanoSec provides an internal conversion helper +type bybitTimeNanoSec time.Time + +// UnmarshalJSON is custom type json unmarshaller for bybitTimeNanoSec +func (b *bybitTimeNanoSec) UnmarshalJSON(data []byte) error { + var timestamp int64 + err := json.Unmarshal(data, ×tamp) + if err != nil { + return err + } + *b = bybitTimeNanoSec(time.Unix(0, timestamp)) + return nil +} + +// Time returns a time.Time object +func (b bybitTimeNanoSec) Time() time.Time { + return time.Time(b) +} + +// UnmarshalTo acts as interface to exchange API response +type UnmarshalTo interface { + GetError() error +} + +// PairData stores pair data +type PairData struct { + Name string `json:"name"` + Alias string `json:"alias"` + BaseCurrency string `json:"baseCurrency"` + QuoteCurrency string `json:"quoteCurrency"` + BasePrecision float64 `json:"basePrecision,string"` + QuotePrecision float64 `json:"quotePrecision,string"` + MinTradeQuantity float64 `json:"minTradeQuantity,string"` + MinTradeAmount float64 `json:"minTradeAmount,string"` + MinPricePrecision float64 `json:"minPricePrecision,string"` + MaxTradeQuantity float64 `json:"maxTradeQuantity,string"` + MaxTradeAmount float64 `json:"maxTradeAmount,string"` + Category int64 `json:"category"` + ShowStatus bool `json:"showStatus"` +} + +// Orderbook stores the orderbook data +type Orderbook struct { + Bids []orderbook.Item + Asks []orderbook.Item + Symbol string + Time time.Time +} + +// TradeItem stores a single trade +type TradeItem struct { + CurrencyPair string + Price float64 + Side string + Volume float64 + Time time.Time +} + +// KlineItem stores an individual kline data item +type KlineItem struct { + StartTime time.Time + EndTime time.Time + Open float64 + Close float64 + High float64 + Low float64 + Volume float64 + QuoteAssetVolume float64 + TakerBaseVolume float64 + TakerQuoteVolume float64 + TradesCount int64 +} + +// PriceChangeStats contains statistics for the last 24 hours trade +type PriceChangeStats struct { + Time time.Time + Symbol string + BestBidPrice float64 + BestAskPrice float64 + LastPrice float64 + OpenPrice float64 + HighPrice float64 + LowPrice float64 + Volume float64 + QuoteVolume float64 +} + +// LastTradePrice contains price for last trade +type LastTradePrice struct { + Symbol string `json:"symbol"` + Price float64 `json:"price,string"` +} + +// TickerData stores ticker data +type TickerData struct { + Symbol string + BidPrice float64 + BidQuantity float64 + AskPrice float64 + AskQuantity float64 + Time time.Time +} + +var ( + // BybitRequestParamsOrderLimit Limit order + BybitRequestParamsOrderLimit = "LIMIT" + + // BybitRequestParamsOrderMarket Market order + BybitRequestParamsOrderMarket = "MARKET" + + // BybitRequestParamsOrderLimitMaker Limit Maker + BybitRequestParamsOrderLimitMaker = "LIMIT_MAKER" +) + +var ( + // BybitRequestParamsTimeGTC Good Till Canceled + BybitRequestParamsTimeGTC = "GTC" + + // BybitRequestParamsTimeFOK Fill or Kill + BybitRequestParamsTimeFOK = "FOK" + + // BybitRequestParamsTimeIOC Immediate or Cancel + BybitRequestParamsTimeIOC = "IOC" +) + +// PlaceOrderRequest store new order request type +type PlaceOrderRequest struct { + Symbol string + Quantity float64 + Side string + TradeType string + TimeInForce string + Price float64 + OrderLinkID string +} + +// PlaceOrderResponse store new order response type +type PlaceOrderResponse struct { + OrderID string `json:"orderId"` + OrderLinkID string `json:"orderLinkId"` + Symbol string `json:"symbol"` + Time bybitTimeMilliSecStr `json:"transactTime"` + Price float64 `json:"price,string"` + Quantity float64 `json:"origQty,string"` + TradeType string `json:"type"` + Side string `json:"side"` + Status string `json:"status"` + TimeInForce string `json:"timeInForce"` + AccountID string `json:"accountId"` + SymbolName string `json:"symbolName"` + ExecutedQty float64 `json:"executedQty,string"` +} + +// QueryOrderResponse holds query order data +type QueryOrderResponse struct { + AccountID string `json:"accountId"` + ExchangeID string `json:"exchangeId"` + Symbol string `json:"symbol"` + SymbolName string `json:"symbolName"` + OrderLinkID string `json:"orderLinkId"` + OrderID string `json:"orderId"` + Price float64 `json:"price,string"` + Quantity float64 `json:"origQty,string"` + ExecutedQty float64 `json:"executedQty,string"` + CummulativeQuoteQty float64 `json:"cummulativeQuoteQty,string"` + AveragePrice float64 `json:"avgPrice,string"` + Status string `json:"status"` + TimeInForce string `json:"timeInForce"` + TradeType string `json:"type"` + Side string `json:"side"` + StopPrice float64 `json:"stopPrice,string"` + IcebergQty float64 `json:"icebergQty,string"` + Time bybitTimeMilliSecStr `json:"time"` + UpdateTime bybitTimeMilliSecStr `json:"updateTime"` + IsWorking bool `json:"isWorking"` +} + +// CancelOrderResponse is the return structured response from the exchange +type CancelOrderResponse struct { + OrderID string `json:"orderId"` + OrderLinkID string `json:"orderLinkId"` + Symbol string `json:"symbol"` + Status string `json:"status"` + AccountID string `json:"accountId"` + Time bybitTimeMilliSecStr `json:"transactTime"` + Price float64 `json:"price,string"` + Quantity float64 `json:"origQty,string"` + ExecutedQty float64 `json:"executedQty,string"` + TimeInForce string `json:"timeInForce"` + TradeType string `json:"type"` + Side string `json:"side"` +} + +// HistoricalTrade holds recent trade data +type HistoricalTrade struct { + Symbol string `json:"symbol"` + ID string `json:"id"` + OrderID string `json:"orderId"` + TicketID string `json:"ticketId"` + Price float64 `json:"price,string"` + Quantity float64 `json:"qty,string"` + Commission float64 `json:"commission,string"` + CommissionAsset float64 `json:"commissionAsset,string"` + Time bybitTimeMilliSecStr `json:"time"` + IsBuyer bool `json:"isBuyer"` + IsMaker bool `json:"isMaker"` + SymbolName string `json:"symbolName"` + MatchOrderID string `json:"matchOrderId"` + Fee FeeData `json:"fee"` + FeeTokenID string `json:"feeTokenId"` + FeeAmount float64 `json:"feeAmount,string"` + MakerRebate float64 `json:"makerRebate,string"` +} + +// FeeData store fees data +type FeeData struct { + FeeTokenID int64 `json:"feeTokenId"` + FeeTokenName string `json:"feeTokenName"` + Fee float64 `json:"fee,string"` +} + +// Balance holds wallet balance +type Balance struct { + Coin string `json:"coin"` + CoinID string `json:"coinId"` + CoinName string `json:"coinName"` + Total float64 `json:"total,string"` + Free float64 `json:"free,string"` + Locked float64 `json:"locked,string"` +} + +type orderbookResponse struct { + Data struct { + Asks [][2]string `json:"asks"` + Bids [][2]string `json:"bids"` + Time bybitTimeMilliSec `json:"time"` + } `json:"result"` + Error +} + +type DepositWalletInfo struct { + Coin string `json:"coin"` + Chains []ChainInfo `json:"chains"` +} + +type ChainInfo struct { + ChainType string `json:"chain_type"` + DepositAddress string `json:"address_deposit"` + DepositTag string `json:"tag_deposit"` + Chain string `json:"chain"` +} + +// Websocket Structures + +// Authenticate stores authentication variables required +type Authenticate struct { + Args []interface{} `json:"args"` + Operation string `json:"op"` +} + +// WsReq has the data used for ws request +type WsReq struct { + Topic string `json:"topic"` + Event string `json:"event"` + Parameters interface{} `json:"params"` +} + +// WsFuturesReq stores futures ws request +type WsFuturesReq struct { + Topic string `json:"op"` + Args []string `json:"args"` +} + +// WsParams store ws parameters +type WsParams struct { + Symbol string `json:"symbol"` + IsBinary bool `json:"binary,string"` + SymbolName string `json:"symbolName,omitempty"` + KlineType string `json:"klineType,omitempty"` // only present in kline ws stream +} + +// WsSpotTickerData stores ws ticker data +type WsSpotTickerData struct { + Symbol string `json:"symbol"` + Bid float64 `json:"bidPrice,string"` + Ask float64 `json:"askPrice,string"` + BidSize float64 `json:"bidQty,string"` + AskSize float64 `json:"askQty,string"` + Time bybitTimeMilliSec `json:"time"` +} + +// WsSpotTicker stores ws ticker data +type WsSpotTicker struct { + Topic string `json:"topic"` + Parameters WsParams `json:"params"` + Ticker WsSpotTickerData `json:"data"` +} + +// KlineStreamData stores ws kline stream data +type KlineStreamData struct { + StartTime bybitTimeMilliSec `json:"t"` + Symbol string `json:"s"` + ClosePrice float64 `json:"c,string"` + HighPrice float64 `json:"h,string"` + LowPrice float64 `json:"l,string"` + OpenPrice float64 `json:"o,string"` + Volume float64 `json:"v,string"` +} + +// KlineStream holds the kline stream data +type KlineStream struct { + Topic string `json:"topic"` + Parameters WsParams `json:"params"` + Kline KlineStreamData `json:"data"` +} + +// WsOrderbookData stores ws orderbook data +type WsOrderbookData struct { + Symbol string `json:"s"` + Time bybitTimeMilliSec `json:"t"` + Version string `json:"v"` + Bids [][2]string `json:"b"` + Asks [][2]string `json:"a"` +} + +// WsOrderbook stores ws orderbook data +type WsOrderbook struct { + Topic string `json:"topic"` + Parameters WsParams `json:"params"` + OBData WsOrderbookData `json:"data"` +} + +// WsTradeData stores ws trade data +type WsTradeData struct { + Time bybitTimeMilliSec `json:"t"` + ID string `json:"v"` + Price float64 `json:"p,string"` + Size float64 `json:"q,string"` + Side bool `json:"m"` +} + +// WsTrade stores ws trades data +type WsTrade struct { + Topic string `json:"topic"` + Parameters WsParams `json:"params"` + TradeData WsTradeData `json:"data"` +} + +// wsAccount defines websocket account info data +type wsAccount struct { + EventType string `json:"e"` + EventTime string `json:"E"` + CanTrade bool `json:"T"` + CanWithdraw bool `json:"W"` + CanDeposit bool `json:"D"` + Balance []Currencies `json:"B"` +} + +// Currencies stores currencies data +type Currencies struct { + Asset string `json:"a"` + Available float64 `json:"f,string"` + Locked float64 `json:"l,string"` +} + +// wsOrderUpdate defines websocket account order update data +type wsOrderUpdate struct { + EventType string `json:"e"` + EventTime string `json:"E"` + Symbol string `json:"s"` + ClientOrderID string `json:"c"` + Side string `json:"S"` + OrderType string `json:"o"` + TimeInForce string `json:"f"` + Quantity float64 `json:"q,string"` + Price float64 `json:"p,string"` + OrderStatus string `json:"X"` + OrderID string `json:"i"` + OpponentOrderID string `json:"M"` + LastExecutedQuantity float64 `json:"l,string"` + CumulativeFilledQuantity float64 `json:"z,string"` + LastExecutedPrice float64 `json:"L,string"` + Commission float64 `json:"n,string"` + CommissionAsset string `json:"N"` + IsNormal bool `json:"u"` + IsOnOrderBook bool `json:"w"` + IsLimitMaker bool `json:"m"` + OrderCreationTime bybitTimeMilliSecStr `json:"O"` + CumulativeQuoteTransactedQuantity float64 `json:"Z,string"` + AccountID string `json:"A"` + IsClose bool `json:"C"` + Leverage float64 `json:"v,string"` +} + +// wsOrderFilled defines websocket account order filled data +type wsOrderFilled struct { + EventType string `json:"e"` + EventTime string `json:"E"` + Symbol string `json:"s"` + Quantity float64 `json:"q,string"` + Timestamp bybitTimeMilliSecStr `json:"t"` + Price float64 `json:"p,string"` + TradeID string `json:"T"` + OrderID string `json:"o"` + UserGenOrderID string `json:"c"` + OpponentOrderID string `json:"O"` + AccountID string `json:"a"` + OpponentAccountID string `json:"A"` + IsMaker bool `json:"m"` + Side string `json:"S"` +} + +// WsFuturesOrderbookData stores ws futures orderbook data +type WsFuturesOrderbookData struct { + Price float64 `json:"price,string"` + Symbol string `json:"symbol"` + ID int64 `json:"id"` + Side string `json:"side"` + Size float64 `json:"size"` +} + +// WsFuturesOrderbook stores ws futures orderbook +type WsFuturesOrderbook struct { + Topic string `json:"topic"` + Type string `json:"string"` + OBData []WsFuturesOrderbookData `json:"data"` +} + +// WsUSDTOrderbook stores ws usdt orderbook +type WsUSDTOrderbook struct { + Topic string `json:"topic"` + Type string `json:"string"` + Data struct { + OBData []WsFuturesOrderbookData `json:"order_book"` + } `json:"data"` +} + +// WsCoinDeltaOrderbook stores ws coinmargined orderbook +type WsCoinDeltaOrderbook struct { + Topic string `json:"topic"` + Type string `json:"string"` + OBData struct { + Delete []WsFuturesOrderbookData `json:"delete"` + Update []WsFuturesOrderbookData `json:"update"` + Insert []WsFuturesOrderbookData `json:"insert"` + } `json:"data"` +} + +// WsFuturesTradeData stores ws future trade data +type WsFuturesTradeData struct { + Time time.Time `json:"timestamp"` + TimeInMilliseconds bybitTimeMilliSec `json:"trade_time_ms"` + Symbol string `json:"symbol"` + Side string `json:"side"` + Size float64 `json:"size"` + Price float64 `json:"price"` + Direction string `json:"tick_direction"` + ID string `json:"trade_id"` +} + +// WsFuturesTrade stores ws future trade +type WsFuturesTrade struct { + Topic string `json:"topic"` + TradeData []WsFuturesTradeData `json:"data"` +} + +// WsFuturesKlineData stores ws future kline data +type WsFuturesKlineData struct { + StartTime bybitTimeSec `json:"start"` + EndTime bybitTimeSec `json:"end"` + Close float64 `json:"close"` + Open float64 `json:"open"` + High float64 `json:"high"` + Low float64 `json:"low"` + Volume float64 `json:"volume"` + TurnOver float64 `json:"turnover"` + Confirm bool `json:"confirm"` + Timestamp bybitTimeMilliSec `json:"timestamp"` +} + +// WsFuturesKline stores ws future kline +type WsFuturesKline struct { + Topic string `json:"topic"` + KlineData []WsFuturesKlineData `json:"data"` +} + +// WsInsuranceData stores ws insurance data +type WsInsuranceData struct { + Currency string `json:"currency"` + Timestamp time.Time `json:"timestamp"` + WalletBalance float64 `json:"wallet_balance"` +} + +// WsInsurance stores ws insurance +type WsInsurance struct { + Topic string `json:"topic"` + Data []WsInsuranceData `json:"data"` +} + +// WsTickerData stores ws ticker data +type WsTickerData struct { + ID string `json:"id"` + Symbol string `json:"symbol"` + LastPrice float64 `json:"last_price,string"` + BidPrice float64 `json:"bid1_price"` + AskPrice float64 `json:"ask1_price"` + LastDirection string `json:"last_tick_direction"` + PrevPrice24h float64 `json:"prev_price_24h,string"` + Price24hPercentChange float64 `json:"price_24h_pcnt_e6"` + Price1hPercentChange float64 `json:"price_1h_pcnt_e6"` + HighPrice24h float64 `json:"high_price_24h,string"` + LowPrice24h float64 `json:"low_price_24h,string"` + PrevPrice1h float64 `json:"prev_price_1h,string"` + MarkPrice float64 `json:"mark_price,string"` + IndexPrice float64 `json:"index_price,string"` + OpenInterest float64 `json:"open_interest"` + OpenValue float64 `json:"open_value_e8"` + TotalTurnOver float64 `json:"total_turnover_e8"` + TurnOver24h float64 `json:"turnover_24h_e8"` + TotalVolume float64 `json:"total_volume"` + Volume24h float64 `json:"volume_24h"` + FundingRate int64 `json:"funding_rate_e6"` + PredictedFundingRate float64 `json:"predicted_funding_rate_e6"` + CreatedAt time.Time `json:"created_at"` + UpdateAt time.Time `json:"updated_at"` + NextFundingAt time.Time `json:"next_funding_time"` + CountDownHour int64 `json:"countdown_hour"` +} + +// WsTicker stores ws ticker +type WsTicker struct { + Topic string `json:"topic"` + Ticker WsTickerData `json:"data"` +} + +// WsDeltaTicker stores ws ticker +type WsDeltaTicker struct { + Topic string `json:"topic"` + Type string `json:"string"` + Data struct { + Delete []WsTickerData `json:"delete"` + Update []WsTickerData `json:"update"` + Insert []WsTickerData `json:"insert"` + } `json:"data"` +} + +// WsFuturesTickerData stores ws future ticker data +type WsFuturesTickerData struct { + ID string `json:"id"` + Symbol string `json:"symbol"` + SymbolName string `json:"symbol_name"` + SymbolYear int64 `json:"symbol_year"` + ContractType string `json:"contract_type"` + Coin string `json:"coin"` + QuoteSymbol string `json:"quote_symbol"` + Mode string `json:"mode"` + IsUpBorrowable int64 `json:"is_up_borrowable"` + ImportTime bybitTimeNanoSec `json:"import_time_e9"` + StartTradingTime bybitTimeNanoSec `json:"start_trading_time_e9"` + TimeToSettle bybitTimeNanoSec `json:"settle_time_e9"` + SettleFeeRate int64 `json:"settle_fee_rate_e8"` + ContractStatus string `json:"contract_status"` + SystemSubsidy int64 `json:"system_subsidy_e8"` + LastPrice float64 `json:"last_price,string"` + BidPrice float64 `json:"bid1_price"` + AskPrice float64 `json:"ask1_price"` + LastDirection string `json:"last_tick_direction"` + PrevPrice24h float64 `json:"prev_price_24h,string"` + Price24hPercentChange float64 `json:"price_24h_pcnt_e6"` + Price1hPercentChange float64 `json:"price_1h_pcnt_e6"` + HighPrice24h float64 `json:"high_price_24h,string"` + LowPrice24h float64 `json:"low_price_24h,string"` + PrevPrice1h float64 `json:"prev_price_1h,string"` + MarkPrice float64 `json:"mark_price,string"` + IndexPrice float64 `json:"index_price,string"` + OpenInterest float64 `json:"open_interest"` + OpenValue float64 `json:"open_value_e8"` + TotalTurnOver float64 `json:"total_turnover_e8"` + TurnOver24h float64 `json:"turnover_24h_e8"` + TotalVolume float64 `json:"total_volume"` + Volume24h float64 `json:"volume_24h"` + FairBasis float64 `json:"fair_basis_e8"` + FairBasisRate float64 `json:"fair_basis_rate_e8"` + BasisInYear float64 `json:"basis_in_year_e8"` + ExpectPrice float64 `json:"expect_price,string"` + CreatedAt time.Time `json:"created_at"` + UpdateAt time.Time `json:"updated_at"` +} + +// WsFuturesTicker stores ws future ticker +type WsFuturesTicker struct { + Topic string `json:"topic"` + Ticker WsFuturesTickerData `json:"data"` +} + +// WsDeltaFuturesTicker stores ws delta future ticker +type WsDeltaFuturesTicker struct { + Topic string `json:"topic"` + Type string `json:"string"` + Data struct { + Delete []WsFuturesTickerData `json:"delete"` + Update []WsFuturesTickerData `json:"update"` + Insert []WsFuturesTickerData `json:"insert"` + } `json:"data"` +} + +// WsLiquidationData stores ws liquidation data +type WsLiquidationData struct { + Symbol string `json:"symbol"` + Side string `json:"side"` + Price float64 `json:"price,string"` + Qty float64 `json:"qty"` + Timestamp bybitTimeMilliSec `json:"time"` +} + +// WsFuturesLiquidation stores ws future liquidation +type WsFuturesLiquidation struct { + Topic string `json:"topic"` + Data WsLiquidationData `json:"data"` +} + +// WsFuturesPositionData stores ws future position data +type WsFuturesPositionData struct { + UserID int64 `json:"user_id"` + Symbol string `json:"symbol"` + Side string `json:"side"` + Size float64 `json:"size"` + PositionID int64 `json:"position_idx"` // present in Futures position struct only + Mode int64 `json:"mode"` // present in Futures position struct only + Isolated bool `json:"isolated"` // present in Futures position struct only + PositionValue float64 `json:"position_value,string"` + EntryPrice float64 `json:"entry_price,string"` + LiquidPrice float64 `json:"liq_price,string"` + BustPrice float64 `json:"bust_price,string"` + Leverage float64 `json:"leverage,string"` + OrderMargin float64 `json:"order_margin,string"` + PositionMargin float64 `json:"position_margin,string"` + AvailableBalance float64 `json:"available_balance,string"` + TakeProfit float64 `json:"take_profit,string"` + TakeProfitTriggerBy string `json:"tp_trigger_by"` + StopLoss float64 `json:"stop_loss,string"` + StopLossTriggerBy string `json:"sl_trigger_by"` + RealisedPNL float64 `json:"realised_pnl,string"` + TrailingStop float64 `json:"trailing_stop,string"` + TrailingActive float64 `json:"trailing_active,string"` + WalletBalance float64 `json:"wallet_balance,string"` + RiskID int64 `json:"risk_id"` + ClosingFee float64 `json:"occ_closing_fee,string"` + FundingFee float64 `json:"occ_funding_fee,string"` + AutoAddMargin int64 `json:"auto_add_margin"` + TotalPNL float64 `json:"cum_realised_pnl,string"` + Status string `json:"position_status"` + Version int64 `json:"position_seq"` +} + +// WsFuturesPosition stores ws future position +type WsFuturesPosition struct { + Topic string `json:"topic"` + Action string `json:"action"` + Data []WsFuturesPositionData `json:"data"` +} + +// WsFuturesExecutionData stores ws future execution data +type WsFuturesExecutionData struct { + Symbol string `json:"symbol"` + Side string `json:"side"` + OrderID string `json:"order_id"` + ExecutionID string `json:"exec_id"` + OrderLinkID string `json:"order_link_id"` + Price float64 `json:"price,string"` + OrderQty float64 `json:"order_qty"` + ExecutionType string `json:"exec_type"` + ExecutionQty float64 `json:"exec_qty"` + ExecutionFee float64 `json:"exec_fee,string"` + LeavesQty float64 `json:"leaves_qty"` + IsMaker bool `json:"is_maker"` + Time time.Time `json:"trade_time"` +} + +// WsFuturesExecution stores ws future execution +type WsFuturesExecution struct { + Topic string `json:"topic"` + Data []WsFuturesExecutionData `json:"data"` +} + +// WsOrderData stores ws order data +type WsOrderData struct { + OrderID string `json:"order_id"` + OrderLinkID string `json:"order_link_id"` + Symbol string `json:"symbol"` + Side string `json:"side"` + OrderType string `json:"order_type"` + Price float64 `json:"price,string"` + OrderQty float64 `json:"qty"` + TimeInForce string `json:"time_in_force"` + CreateType string `json:"create_type"` + CancelType string `json:"cancel_type"` + OrderStatus string `json:"order_status"` + LeavesQty float64 `json:"leaves_qty"` + CummulativeExecQty float64 `json:"cum_exec_qty"` + CummulativeExecValue float64 `json:"cum_exec_value,string"` + CummulativeExecFee float64 `json:"cum_exec_fee,string"` + TakeProfit float64 `json:"take_profit,string"` + StopLoss float64 `json:"stop_loss,string"` + TrailingStop float64 `json:"trailing_stop,string"` + TrailingActive float64 `json:"trailing_active,string"` + LastExecPrice float64 `json:"last_exec_price,string"` + ReduceOnly bool `json:"reduce_only"` + CloseOnTrigger bool `json:"close_on_trigger"` + Time time.Time `json:"timestamp"` // present in CoinMarginedFutures and Futures only + CreateTime time.Time `json:"create_time"` // present in USDTMarginedFutures only + UpdateTime time.Time `json:"update_time"` // present in USDTMarginedFutures only +} + +// WsOrder stores ws order +type WsOrder struct { + Topic string `json:"topic"` + Data []WsOrderData `json:"data"` +} + +// WsStopOrderData stores ws stop order data +type WsStopOrderData struct { + OrderID string `json:"order_id"` + OrderLinkID string `json:"order_link_id"` + UserID int64 `json:"user_id"` + Symbol string `json:"symbol"` + Side string `json:"side"` + OrderType string `json:"order_type"` + Price float64 `json:"price,string"` + OrderQty float64 `json:"qty"` + TimeInForce string `json:"time_in_force"` + CreateType string `json:"create_type"` + CancelType string `json:"cancel_type"` + OrderStatus string `json:"order_status"` + StopOrderType string `json:"stop_order_type"` + TriggerBy string `json:"trigger_by"` + TriggerPrice float64 `json:"trigger_price,string"` + Time time.Time `json:"timestamp"` + CloseOnTrigger bool `json:"close_on_trigger"` +} + +// WsFuturesStopOrder stores ws future stop order +type WsFuturesStopOrder struct { + Topic string `json:"topic"` + Data []WsStopOrderData `json:"data"` +} + +// WsUSDTStopOrderData stores ws USDT stop order data +type WsUSDTStopOrderData struct { + OrderID string `json:"stop_order_id"` + OrderLinkID string `json:"order_link_id"` + UserID int64 `json:"user_id"` + Symbol string `json:"symbol"` + Side string `json:"side"` + OrderType string `json:"order_type"` + Price float64 `json:"price,string"` + OrderQty float64 `json:"qty"` + TimeInForce string `json:"time_in_force"` + OrderStatus string `json:"order_status"` + StopOrderType string `json:"stop_order_type"` + TriggerBy string `json:"trigger_by"` + TriggerPrice float64 `json:"trigger_price,string"` + ReduceOnly bool `json:"reduce_only"` + CloseOnTrigger bool `json:"close_on_trigger"` + CreateTime time.Time `json:"create_time"` + UpdateTime time.Time `json:"update_time"` +} + +// WsUSDTFuturesStopOrder stores ws USDT stop order +type WsUSDTFuturesStopOrder struct { + Topic string `json:"topic"` + Data []WsUSDTStopOrderData `json:"data"` +} + +// WsFuturesWalletData stores ws future wallet data +type WsFuturesWalletData struct { + WalletBalance float64 `json:"wallet_balance"` + AvailableBalance float64 `json:"available_balance"` +} + +// WsFuturesWallet stores ws future wallet +type WsFuturesWallet struct { + Topic string `json:"topic"` + Data []WsFuturesWalletData `json:"data"` +} diff --git a/exchanges/bybit/bybit_ufutures.go b/exchanges/bybit/bybit_ufutures.go new file mode 100644 index 00000000..1d6d5a14 --- /dev/null +++ b/exchanges/bybit/bybit_ufutures.go @@ -0,0 +1,1048 @@ +package bybit + +import ( + "context" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/currency" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" +) + +const ( + + // public endpoint + ufuturesKline = "/public/linear/kline" + ufuturesRecentTrades = "/public/linear/recent-trading-records" + ufuturesMarkPriceKline = "/public/linear/mark-price-kline" + ufuturesIndexKline = "/public/linear/index-price-kline" + ufuturesIndexPremiumKline = "/public/linear/premium-index-kline" + ufuturesGetLastFundingRate = "/public/linear/funding/prev-funding-rate" + ufuturesGetRiskLimit = "/public/linear/risk-limit" + + // auth endpoint + ufuturesCreateOrder = "/private/linear/order/create" + ufuturesGetActiveOrders = "/private/linear/order/list" + ufuturesCancelActiveOrder = "/private/linear/order/cancel" + ufuturesCancelAllActiveOrders = "/private/linear/order/cancel-all" + ufuturesReplaceActiveOrder = "/private/linear/order/replace" + ufuturesGetActiveRealtimeOrders = "/private/linear/order/search" + + ufuturesCreateConditionalOrder = "/private/linear/stop-order/create" + ufuturesGetConditionalOrders = "/private/linear/stop-order/list" + ufuturesCancelConditionalOrder = "/private/linear/stop-order/cancel" + ufuturesCancelAllConditionalOrders = "/private/linear/stop-order/cancel-all" + ufuturesReplaceConditionalOrder = "/private/linear/stop-order/replace" + ufuturesGetConditionalRealtimeOrders = "/private/linear/stop-order/search" + + ufuturesPosition = "/private/linear/position/list" + ufuturesSetAutoAddMargin = "/private/linear/position/set-auto-add-margin" + ufuturesSwitchMargin = "/private/linear/position/switch-isolated" + ufuturesSwitchPositionMode = "/private/linear/position/switch-mode" + ufuturesSwitchPosition = "/private/linear/tpsl/switch-mode" + ufuturesUpdateMargin = "/private/linear/position/add-margin" + ufuturesSetLeverage = "/private/linear/position/set-leverage" + ufuturesSetTradingStop = "/private/linear/position/trading-stop" + ufuturesGetTrades = "/private/linear/trade/execution/list" + ufuturesGetClosedTrades = "/private/linear/trade/closed-pnl/list" + + ufuturesSetRiskLimit = "/private/linear/position/set-risk" + ufuturesPredictFundingRate = "/private/linear/funding/predicted-funding" + ufuturesGetMyLastFundingFee = "/private/linear/funding/prev-funding" +) + +// GetUSDTFuturesKlineData gets futures kline data for USDTMarginedFutures. +func (by *Bybit) GetUSDTFuturesKlineData(ctx context.Context, symbol currency.Pair, interval string, limit int64, startTime time.Time) ([]FuturesCandleStick, error) { + resp := struct { + Data []FuturesCandleStick `json:"result"` + Error + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return resp.Data, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if !common.StringDataCompare(validFuturesIntervals, interval) { + return resp.Data, errInvalidInterval + } + params.Set("interval", interval) + params.Set("from", strconv.FormatInt(startTime.Unix(), 10)) + + path := common.EncodeURLValues(ufuturesKline, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDTMargined, path, publicFuturesRate, &resp) +} + +// GetUSDTPublicTrades gets past public trades for USDTMarginedFutures. +func (by *Bybit) GetUSDTPublicTrades(ctx context.Context, symbol currency.Pair, limit int64) ([]FuturesPublicTradesData, error) { + resp := struct { + Data []FuturesPublicTradesData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if limit > 0 && limit <= 1000 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + path := common.EncodeURLValues(ufuturesRecentTrades, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDTMargined, path, publicFuturesRate, &resp) +} + +// GetUSDTMarkPriceKline gets mark price kline data for USDTMarginedFutures. +func (by *Bybit) GetUSDTMarkPriceKline(ctx context.Context, symbol currency.Pair, interval string, limit int64, startTime time.Time) ([]MarkPriceKlineData, error) { + resp := struct { + Data []MarkPriceKlineData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if !common.StringDataCompare(validFuturesIntervals, interval) { + return resp.Data, errInvalidInterval + } + params.Set("interval", interval) + if startTime.IsZero() { + return resp.Data, errInvalidStartTime + } + params.Set("from", strconv.FormatInt(startTime.Unix(), 10)) + + path := common.EncodeURLValues(ufuturesMarkPriceKline, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDTMargined, path, publicFuturesRate, &resp) +} + +// GetUSDTIndexPriceKline gets index price kline data for USDTMarginedFutures. +func (by *Bybit) GetUSDTIndexPriceKline(ctx context.Context, symbol currency.Pair, interval string, limit int64, startTime time.Time) ([]IndexPriceKlineData, error) { + resp := struct { + Data []IndexPriceKlineData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if !common.StringDataCompare(validFuturesIntervals, interval) { + return resp.Data, errInvalidInterval + } + params.Set("interval", interval) + if startTime.IsZero() { + return resp.Data, errInvalidStartTime + } + params.Set("from", strconv.FormatInt(startTime.Unix(), 10)) + + path := common.EncodeURLValues(ufuturesIndexKline, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDTMargined, path, publicFuturesRate, &resp) +} + +// GetUSDTPremiumIndexPriceKline gets premium index price kline data for USDTMarginedFutures. +func (by *Bybit) GetUSDTPremiumIndexPriceKline(ctx context.Context, symbol currency.Pair, interval string, limit int64, startTime time.Time) ([]IndexPriceKlineData, error) { + resp := struct { + Data []IndexPriceKlineData `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if !common.StringDataCompare(validFuturesIntervals, interval) { + return resp.Data, errInvalidInterval + } + params.Set("interval", interval) + if startTime.IsZero() { + return resp.Data, errInvalidStartTime + } + params.Set("from", strconv.FormatInt(startTime.Unix(), 10)) + + path := common.EncodeURLValues(ufuturesIndexPremiumKline, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDTMargined, path, publicFuturesRate, &resp) +} + +// GetUSDTLastFundingRate returns latest generated funding fee +func (by *Bybit) GetUSDTLastFundingRate(ctx context.Context, symbol currency.Pair) (USDTFundingInfo, error) { + resp := struct { + Data USDTFundingInfo `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + path := common.EncodeURLValues(ufuturesGetLastFundingRate, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDTMargined, path, publicFuturesRate, &resp) +} + +// GetUSDTRiskLimit returns risk limit +func (by *Bybit) GetUSDTRiskLimit(ctx context.Context, symbol currency.Pair) ([]RiskInfo, error) { + resp := struct { + Data []RiskInfo `json:"result"` + Error + }{} + + params := url.Values{} + if !symbol.IsEmpty() { + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + } + + path := common.EncodeURLValues(ufuturesGetRiskLimit, params) + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDTMargined, path, publicFuturesRate, &resp) +} + +// CreateUSDTFuturesOrder sends a new USDT futures order to the exchange +func (by *Bybit) CreateUSDTFuturesOrder(ctx context.Context, symbol currency.Pair, side, orderType, timeInForce, + orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string, + quantity, price, takeProfit, stopLoss float64, closeOnTrigger, reduceOnly bool) (FuturesOrderDataResp, error) { + resp := struct { + Data FuturesOrderDataResp `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + if side == "" { + return resp.Data, errInvalidSide + } + params.Set("side", side) + + if orderType == "" { + return resp.Data, errInvalidOrderType + } + params.Set("order_type", orderType) + + if quantity <= 0 { + return resp.Data, errInvalidQuantity + } + params.Set("qty", strconv.FormatFloat(quantity, 'f', -1, 64)) + + if price != 0 { + params.Set("price", strconv.FormatFloat(price, 'f', -1, 64)) + } + if timeInForce == "" { + return resp.Data, errInvalidTimeInForce + } + params.Set("time_in_force", timeInForce) + + if closeOnTrigger { + params.Set("close_on_trigger", "true") + } else { + params.Set("close_on_trigger", "false") + } + + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if takeProfit != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64)) + } + if stopLoss != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + if reduceOnly { + params.Set("reduce_only", "true") + } else { + params.Set("reduce_only", "false") + } + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesCreateOrder, params, nil, &resp, uFuturesCreateOrderRate) +} + +// GetActiveUSDTFuturesOrders gets list of USDT futures active orders +func (by *Bybit) GetActiveUSDTFuturesOrders(ctx context.Context, symbol currency.Pair, orderStatus, direction, orderID, orderLinkID string, page, limit int64) ([]FuturesActiveOrderResp, error) { + resp := struct { + Result struct { + Data []FuturesActiveOrderResp `json:"data"` + CurrentPage int64 `json:"current_page"` + LastPage int64 `json:"last_page"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Result.Data, err + } + params.Set("symbol", symbolValue) + if orderStatus != "" { + params.Set("order_status", orderStatus) + } + if direction != "" { + params.Set("order", direction) + } + if page > 0 && page <= 50 { + params.Set("page", strconv.FormatInt(page, 10)) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if orderID != "" { + params.Set("order_id", orderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + + return resp.Result.Data, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesGetActiveOrders, params, nil, &resp, uFuturesGetActiveOrderRate) +} + +// CancelActiveUSDTFuturesOrders cancels USDT futures unfilled or partially filled orders +func (by *Bybit) CancelActiveUSDTFuturesOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID string) (string, error) { + resp := struct { + Data struct { + OrderID string `json:"order_id"` + } `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data.OrderID, err + } + params.Set("symbol", symbolValue) + if orderID == "" && orderLinkID == "" { + return resp.Data.OrderID, errOrderOrOrderLinkIDMissing + } + if orderID != "" { + params.Set("order_id", orderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + return resp.Data.OrderID, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesCancelActiveOrder, params, nil, &resp, uFuturesCancelOrderRate) +} + +// CancelAllActiveUSDTFuturesOrders cancels all USDT futures unfilled or partially filled orders +func (by *Bybit) CancelAllActiveUSDTFuturesOrders(ctx context.Context, symbol currency.Pair) ([]string, error) { + resp := struct { + Data []string `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesCancelAllActiveOrders, params, nil, &resp, uFuturesCancelAllOrderRate) +} + +// ReplaceActiveUSDTFuturesOrders modify unfilled or partially filled orders +func (by *Bybit) ReplaceActiveUSDTFuturesOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string, + updatedQty int64, updatedPrice, takeProfitPrice, stopLossPrice float64) (string, error) { + resp := struct { + Data struct { + OrderID string `json:"order_id"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return "", err + } + params.Set("symbol", symbolValue) + if orderID == "" && orderLinkID == "" { + return "", errOrderOrOrderLinkIDMissing + } + if orderID != "" { + params.Set("order_id", orderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if updatedQty != 0 { + params.Set("p_r_qty", strconv.FormatInt(updatedQty, 10)) + } + if updatedPrice != 0 { + params.Set("p_r_price", strconv.FormatFloat(updatedPrice, 'f', -1, 64)) + } + if takeProfitPrice != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfitPrice, 'f', -1, 64)) + } + if stopLossPrice != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLossPrice, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + return resp.Data.OrderID, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesReplaceActiveOrder, params, nil, &resp, uFuturesDefaultRate) +} + +// GetActiveUSDTRealtimeOrders query real time order data +func (by *Bybit) GetActiveUSDTRealtimeOrders(ctx context.Context, symbol currency.Pair, orderID, orderLinkID string) ([]FuturesActiveRealtimeOrder, error) { + var data []FuturesActiveRealtimeOrder + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return data, err + } + params.Set("symbol", symbolValue) + if orderID != "" { + params.Set("order_id", orderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + + if orderID == "" && orderLinkID == "" { + resp := struct { + Data []FuturesActiveRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesGetActiveRealtimeOrders, params, nil, &resp, uFuturesGetActiveRealtimeOrderRate) + if err != nil { + return data, err + } + data = append(data, resp.Data...) + } else { + resp := struct { + Data FuturesActiveRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesGetActiveRealtimeOrders, params, nil, &resp, uFuturesGetActiveRealtimeOrderRate) + if err != nil { + return data, err + } + data = append(data, resp.Data) + } + return data, nil +} + +// CreateConditionalUSDTFuturesOrder sends a new conditional USDT futures order to the exchange +func (by *Bybit) CreateConditionalUSDTFuturesOrder(ctx context.Context, symbol currency.Pair, side, orderType, timeInForce, + orderLinkID, takeProfitTriggerBy, stopLossTriggerBy, triggerBy string, + quantity, price, takeProfit, stopLoss, basePrice, stopPrice float64, closeOnTrigger, reduceOnly bool) (USDTFuturesConditionalOrderResp, error) { + resp := struct { + Data USDTFuturesConditionalOrderResp `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + params.Set("side", side) + params.Set("order_type", orderType) + if quantity <= 0 { + return resp.Data, errInvalidQuantity + } + params.Set("qty", strconv.FormatFloat(quantity, 'f', -1, 64)) + + if price != 0 { + params.Set("price", strconv.FormatFloat(price, 'f', -1, 64)) + } + if basePrice <= 0 { + return resp.Data, errInvalidBasePrice + } + params.Set("base_price", strconv.FormatFloat(basePrice, 'f', -1, 64)) + + if stopPrice <= 0 { + return resp.Data, errInvalidStopPrice + } + params.Set("stop_px", strconv.FormatFloat(stopPrice, 'f', -1, 64)) + + if timeInForce == "" { + return resp.Data, errInvalidTimeInForce + } + params.Set("time_in_force", timeInForce) + + if triggerBy != "" { + params.Set("trigger_by", triggerBy) + } + if closeOnTrigger { + params.Set("close_on_trigger", "true") + } else { + params.Set("close_on_trigger", "false") + } + if reduceOnly { + params.Set("reduce_only", "true") + } else { + params.Set("reduce_only", "false") + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if takeProfit != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64)) + } + if stopLoss != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesCreateConditionalOrder, params, nil, &resp, uFuturesCreateConditionalOrderRate) +} + +// GetConditionalUSDTFuturesOrders gets list of USDT futures conditional orders +func (by *Bybit) GetConditionalUSDTFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderStatus, direction, stopOrderID, orderLinkID string, limit, page int64) ([]USDTFuturesConditionalOrders, error) { + resp := struct { + Result struct { + Data []USDTFuturesConditionalOrders `json:"data"` + CurrentPage int64 `json:"current_page"` + LastPage int64 `json:"last_page"` + } `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Result.Data, err + } + params.Set("symbol", symbolValue) + if stopOrderStatus != "" { + params.Set("stop_order_status", stopOrderStatus) + } + if direction != "" { + params.Set("order", direction) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + if page != 0 { + params.Set("page", strconv.FormatInt(page, 10)) + } + if stopOrderID != "" { + params.Set("stop_order_id", stopOrderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + return resp.Result.Data, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesGetConditionalOrders, params, nil, &resp, uFuturesGetConditionalOrderRate) +} + +// CancelConditionalUSDTFuturesOrders cancels untriggered conditional orders +func (by *Bybit) CancelConditionalUSDTFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID string) (string, error) { + resp := struct { + Result struct { + StopOrderID string `json:"stop_order_id"` + } `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return "", err + } + params.Set("symbol", symbolValue) + if stopOrderID == "" && orderLinkID == "" { + return "", errStopOrderOrOrderLinkIDMissing + } + if stopOrderID != "" { + params.Set("stop_order_id", stopOrderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + return resp.Result.StopOrderID, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesCancelConditionalOrder, params, nil, &resp, uFuturesCancelConditionalOrderRate) +} + +// CancelAllConditionalUSDTFuturesOrders cancels all untriggered conditional orders +func (by *Bybit) CancelAllConditionalUSDTFuturesOrders(ctx context.Context, symbol currency.Pair) ([]string, error) { + resp := struct { + Data []string `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + return resp.Data, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesCancelAllConditionalOrders, params, nil, &resp, uFuturesCancelAllConditionalOrderRate) +} + +// ReplaceConditionalUSDTFuturesOrders modify unfilled or partially filled conditional orders +func (by *Bybit) ReplaceConditionalUSDTFuturesOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID, takeProfitTriggerBy, stopLossTriggerBy string, + updatedQty, updatedPrice, takeProfitPrice, stopLossPrice, orderTriggerPrice float64) (string, error) { + resp := struct { + Data struct { + OrderID string `json:"stop_order_id"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return "", err + } + params.Set("symbol", symbolValue) + if stopOrderID == "" && orderLinkID == "" { + return "", errStopOrderOrOrderLinkIDMissing + } + if stopOrderID != "" { + params.Set("stop_order_id", stopOrderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + if updatedQty != 0 { + params.Set("p_r_qty", strconv.FormatFloat(updatedQty, 'f', -1, 64)) + } + if updatedPrice != 0 { + params.Set("p_r_price", strconv.FormatFloat(updatedPrice, 'f', -1, 64)) + } + if orderTriggerPrice != 0 { + params.Set("p_r_trigger_price", strconv.FormatFloat(orderTriggerPrice, 'f', -1, 64)) + } + if takeProfitPrice != 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfitPrice, 'f', -1, 64)) + } + if stopLossPrice != 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLossPrice, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + return resp.Data.OrderID, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesReplaceConditionalOrder, params, nil, &resp, uFuturesDefaultRate) +} + +// GetConditionalUSDTRealtimeOrders query real time conditional order data +func (by *Bybit) GetConditionalUSDTRealtimeOrders(ctx context.Context, symbol currency.Pair, stopOrderID, orderLinkID string) ([]USDTFuturesConditionalRealtimeOrder, error) { + var data []USDTFuturesConditionalRealtimeOrder + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return data, err + } + params.Set("symbol", symbolValue) + if stopOrderID != "" { + params.Set("stop_order_id", stopOrderID) + } + if orderLinkID != "" { + params.Set("order_link_id", orderLinkID) + } + + if stopOrderID == "" && orderLinkID == "" { + resp := struct { + Result []USDTFuturesConditionalRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesGetConditionalRealtimeOrders, params, nil, &resp, uFuturesGetConditionalRealtimeOrderRate) + if err != nil { + return data, err + } + data = append(data, resp.Result...) + } else { + resp := struct { + Result USDTFuturesConditionalRealtimeOrder `json:"result"` + Error + }{} + err = by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesGetConditionalRealtimeOrders, params, nil, &resp, uFuturesGetConditionalRealtimeOrderRate) + if err != nil { + return data, err + } + data = append(data, resp.Result) + } + return data, nil +} + +// GetUSDTPositions returns list of user positions +func (by *Bybit) GetUSDTPositions(ctx context.Context, symbol currency.Pair) ([]USDTPositionResp, error) { + var data []USDTPositionResp + params := url.Values{} + + if !symbol.IsEmpty() { + resp := struct { + Result []USDTPositionResp `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return data, err + } + params.Set("symbol", symbolValue) + + err = by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesPosition, params, nil, &resp, uFuturesPositionRate) + if err != nil { + return data, err + } + data = resp.Result + } else { + resp := struct { + Result []struct { + IsValid bool `json:"is_valid"` + Data USDTPositionResp `json:"data"` + } `json:"result"` + Error + }{} + err := by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesPosition, params, nil, &resp, uFuturesPositionRate) + if err != nil { + return data, err + } + for x := range resp.Result { + data = append(data, resp.Result[x].Data) + } + } + return data, nil +} + +// SetAutoAddMargin sets auto add margin +func (by *Bybit) SetAutoAddMargin(ctx context.Context, symbol currency.Pair, autoAddMargin bool, side string) error { + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + params.Set("symbol", symbolValue) + if side == "" { + return errInvalidSide + } + params.Set("side", side) + + if autoAddMargin { + params.Set("take_profit", "true") + } else { + params.Set("take_profit", "false") + } + return by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesSetAutoAddMargin, params, nil, nil, uFuturesSetMarginRate) +} + +// ChangeUSDTMargin switches margin between cross or isolated +func (by *Bybit) ChangeUSDTMargin(ctx context.Context, symbol currency.Pair, buyLeverage, sellLeverage float64, isIsolated bool) error { + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + params.Set("symbol", symbolValue) + params.Set("buy_leverage", strconv.FormatFloat(buyLeverage, 'f', -1, 64)) + params.Set("sell_leverage", strconv.FormatFloat(sellLeverage, 'f', -1, 64)) + + if isIsolated { + params.Set("is_isolated", "true") + } else { + params.Set("is_isolated", "false") + } + + return by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesSwitchMargin, params, nil, nil, uFuturesSwitchMargin) +} + +// SwitchPositionMode switches mode between MergedSingle: One-Way Mode or BothSide: Hedge Mode +func (by *Bybit) SwitchPositionMode(ctx context.Context, symbol currency.Pair, mode string) error { + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + params.Set("symbol", symbolValue) + if mode == "" { + return errInvalidMode + } + params.Set("mode", mode) + + return by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesSwitchPositionMode, params, nil, nil, uFuturesSwitchPosition) +} + +// ChangeUSDTMode switches mode between full or partial position +func (by *Bybit) ChangeUSDTMode(ctx context.Context, symbol currency.Pair, takeProfitStopLoss string) (string, error) { + resp := struct { + Result struct { + Mode string `json:"tp_sl_mode"` + } `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Result.Mode, err + } + params.Set("symbol", symbolValue) + if takeProfitStopLoss == "" { + return resp.Result.Mode, errInvalidTakeProfitStopLoss + } + params.Set("tp_sl_mode", takeProfitStopLoss) + + return resp.Result.Mode, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesSwitchPosition, params, nil, &resp, uFuturesSwitchPosition) +} + +// SetUSDTMargin updates margin +func (by *Bybit) SetUSDTMargin(ctx context.Context, symbol currency.Pair, side, margin string) (UpdateMarginResp, error) { + resp := struct { + Result struct { + Data UpdateMarginResp + WalletBalance float64 + AvailableBalance float64 + } `json:"result"` + Error + }{} + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Result.Data, err + } + params.Set("symbol", symbolValue) + if side == "" { + return resp.Result.Data, errInvalidSide + } + params.Set("side", side) + + if margin == "" { + return resp.Result.Data, errInvalidMargin + } + params.Set("margin", margin) + + return resp.Result.Data, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesUpdateMargin, params, nil, &resp, uFuturesUpdateMarginRate) +} + +// SetUSDTLeverage sets leverage +func (by *Bybit) SetUSDTLeverage(ctx context.Context, symbol currency.Pair, buyLeverage, sellLeverage float64) error { + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + params.Set("symbol", symbolValue) + if buyLeverage <= 0 { + return errInvalidBuyLeverage + } + params.Set("buy_leverage", strconv.FormatFloat(buyLeverage, 'f', -1, 64)) + + if sellLeverage <= 0 { + return errInvalidSellLeverage + } + params.Set("sell_leverage", strconv.FormatFloat(sellLeverage, 'f', -1, 64)) + + return by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesSetLeverage, params, nil, nil, uFuturesSetLeverageRate) +} + +// SetUSDTTradingAndStop sets take profit, stop loss, and trailing stop for your open position +func (by *Bybit) SetUSDTTradingAndStop(ctx context.Context, symbol currency.Pair, takeProfit, stopLoss, trailingStop, stopLossQty, takeProfitQty float64, side, takeProfitTriggerBy, stopLossTriggerBy string) error { + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + params.Set("symbol", symbolValue) + if side == "" { + return errInvalidSide + } + params.Set("side", side) + + if takeProfit >= 0 { + params.Set("take_profit", strconv.FormatFloat(takeProfit, 'f', -1, 64)) + } + if stopLoss >= 0 { + params.Set("stop_loss", strconv.FormatFloat(stopLoss, 'f', -1, 64)) + } + if trailingStop >= 0 { + params.Set("trailing_stop", strconv.FormatFloat(trailingStop, 'f', -1, 64)) + } + if takeProfitQty != 0 { + params.Set("tp_size", strconv.FormatFloat(takeProfitQty, 'f', -1, 64)) + } + if stopLossQty != 0 { + params.Set("sl_size", strconv.FormatFloat(stopLossQty, 'f', -1, 64)) + } + if takeProfitTriggerBy != "" { + params.Set("tp_trigger_by", takeProfitTriggerBy) + } + if stopLossTriggerBy != "" { + params.Set("sl_trigger_by", stopLossTriggerBy) + } + + return by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesSetTradingStop, params, nil, nil, uFuturesSetTradingStopRate) +} + +// GetUSDTTradeRecords returns list of user trades +func (by *Bybit) GetUSDTTradeRecords(ctx context.Context, symbol currency.Pair, executionType string, startTime, endTime, page, limit int64) ([]TradeData, error) { + params := url.Values{} + resp := struct { + Data struct { + CurrentPage int64 `json:"current_page"` + Trades []TradeData `json:"data"` + } `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data.Trades, err + } + params.Set("symbol", symbolValue) + if executionType != "" { + params.Set("exec_type", executionType) + } + if startTime != 0 { + params.Set("start_time", strconv.FormatInt(startTime, 10)) + } + if endTime != 0 { + params.Set("end_time", strconv.FormatInt(endTime, 10)) + } + if page != 0 { + params.Set("page", strconv.FormatInt(page, 10)) + } + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + return resp.Data.Trades, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesGetTrades, params, nil, &resp, uFuturesGetTradesRate) +} + +// GetClosedUSDTTrades returns closed profit and loss records +func (by *Bybit) GetClosedUSDTTrades(ctx context.Context, symbol currency.Pair, executionType string, startTime, endTime time.Time, page, limit int64) ([]ClosedTrades, error) { + params := url.Values{} + + resp := struct { + Data struct { + CurrentPage int64 `json:"current_page"` + Trades []ClosedTrades `json:"data"` + } `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Data.Trades, err + } + params.Set("symbol", symbolValue) + if executionType != "" { + params.Set("execution_type", executionType) + } + if !startTime.IsZero() { + params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10)) + } + if !endTime.IsZero() { + params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10)) + } + if page > 0 && page <= 50 { + params.Set("page", strconv.FormatInt(page, 10)) + } + if limit > 0 && limit <= 50 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + return resp.Data.Trades, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesGetClosedTrades, params, nil, &resp, uFuturesGetClosedTradesRate) +} + +// SetUSDTRiskLimit sets risk limit +func (by *Bybit) SetUSDTRiskLimit(ctx context.Context, symbol currency.Pair, side string, riskID int64) (int64, error) { + resp := struct { + Result struct { + RiskID int64 `json:"risk_id"` + } `json:"result"` + Error + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Result.RiskID, err + } + params.Set("symbol", symbolValue) + if side == "" { + return 0, errInvalidSide + } + params.Set("side", side) + + if riskID <= 0 { + return resp.Result.RiskID, errInvalidRiskID + } + params.Set("risk_id", strconv.FormatInt(riskID, 10)) + + return resp.Result.RiskID, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodPost, ufuturesSetRiskLimit, params, nil, &resp, uFuturesDefaultRate) +} + +// GetPredictedUSDTFundingRate returns predicted funding rates and fees +func (by *Bybit) GetPredictedUSDTFundingRate(ctx context.Context, symbol currency.Pair) (fundingRate, fundingFee float64, err error) { + params := url.Values{} + resp := struct { + Result struct { + PredictedFundingRate float64 `json:"predicted_funding_rate"` + PredictedFundingFee float64 `json:"predicted_funding_fee"` + } `json:"result"` + Error + }{} + + var symbolValue string + symbolValue, err = by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Result.PredictedFundingRate, resp.Result.PredictedFundingFee, err + } + params.Set("symbol", symbolValue) + + err = by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesPredictFundingRate, params, nil, &resp, uFuturesPredictFundingRate) + fundingRate = resp.Result.PredictedFundingRate + fundingFee = resp.Result.PredictedFundingFee + return +} + +// GetLastUSDTFundingFee returns last funding fees +func (by *Bybit) GetLastUSDTFundingFee(ctx context.Context, symbol currency.Pair) (FundingFee, error) { + params := url.Values{} + resp := struct { + Result FundingFee `json:"result"` + Error + }{} + + symbolValue, err := by.FormatSymbol(symbol, asset.USDTMarginedFutures) + if err != nil { + return resp.Result, err + } + params.Set("symbol", symbolValue) + + return resp.Result, by.SendAuthHTTPRequest(ctx, exchange.RestUSDTMargined, http.MethodGet, ufuturesGetMyLastFundingFee, params, nil, &resp, uFuturesGetMyLastFundingFeeRate) +} diff --git a/exchanges/bybit/bybit_usdcfutures.go b/exchanges/bybit/bybit_usdcfutures.go new file mode 100644 index 00000000..97b3c643 --- /dev/null +++ b/exchanges/bybit/bybit_usdcfutures.go @@ -0,0 +1,1114 @@ +package bybit + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/crypto" + "github.com/thrasher-corp/gocryptotrader/currency" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/request" +) + +const ( + + // public endpoint + usdcfuturesGetOrderbook = "/perpetual/usdc/openapi/public/v1/order-book" + usdcfuturesGetContracts = "/perpetual/usdc/openapi/public/v1/symbols" + usdcfuturesGetSymbols = "/perpetual/usdc/openapi/public/v1/tick" + usdcfuturesGetKlines = "/perpetual/usdc/openapi/public/v1/kline/list" + usdcfuturesGetMarkPriceKlines = "/perpetual/usdc/openapi/public/v1/mark-price-kline" + usdcfuturesGetIndexPriceKlines = "/perpetual/usdc/openapi/public/v1/index-price-kline" + usdcfuturesGetPremiumIndexKlines = "/perpetual/usdc/openapi/public/v1/premium-index-kline" + usdcfuturesGetOpenInterest = "/perpetual/usdc/openapi/public/v1/open-interest" + usdcfuturesGetLargeOrders = "/perpetual/usdc/openapi/public/v1/big-deal" + usdcfuturesGetAccountRatio = "/perpetual/usdc/openapi/public/v1/account-ratio" + usdcfuturesGetLatestTrades = "/option/usdc/openapi/public/v1/query-trade-latest" + usdcfuturesGetLastFundingRate = "/perpetual/usdc/openapi/public/v1/prev-funding-rate" + usdcfuturesGetRiskLimit = "/perpetual/usdc/openapi/public/v1/risk-limit/list" + + // auth endpoint + usdcfuturesPlaceOrder = "/perpetual/usdc/openapi/private/v1/place-order" + usdcfuturesModifyOrder = "/perpetual/usdc/openapi/private/v1/replace-order" + usdcfuturesCancelOrder = "/perpetual/usdc/openapi/private/v1/cancel-order" + usdcfuturesCancelAllActiveOrder = "/perpetual/usdc/openapi/private/v1/cancel-all" + usdcfuturesGetActiveOrder = "/option/usdc/openapi/private/v1/query-active-orders" + usdcfuturesGetOrderHistory = "/option/usdc/openapi/private/v1/query-order-history" + usdcfuturesGetTradeHistory = "/option/usdc/openapi/private/v1/execution-list" + usdcfuturesGetTransactionLog = "/option/usdc/openapi/private/v1/query-transaction-log" + usdcfuturesGetWalletBalance = "/option/usdc/openapi/private/v1/query-wallet-balance" + usdcfuturesGetAssetInfo = "/option/usdc/openapi/private/v1/query-asset-info" + usdcfuturesGetMarginInfo = "/option/usdc/openapi/private/v1/query-margin-info" + usdcfuturesGetPosition = "/option/usdc/openapi/private/v1/query-position" + usdcfuturesSetLeverage = "/perpetual/usdc/openapi/private/v1/position/leverage/save" + usdcfuturesGetSettlementHistory = "/option/usdc/openapi/private/v1/session-settlement" + usdcfuturesSetRiskLimit = "/perpetual/usdc/openapi/private/v1/position/set-risk-limit" + usdcfuturesGetPredictedFundingRate = "/perpetual/usdc/openapi/private/v1/predicted-funding" +) + +// GetUSDCFuturesOrderbook gets orderbook data for USDCMarginedFutures. +func (by *Bybit) GetUSDCFuturesOrderbook(ctx context.Context, symbol currency.Pair) (*Orderbook, error) { + var resp Orderbook + data := struct { + Result []USDCOrderbookData `json:"result"` + USDCError + }{} + + params := url.Values{} + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return nil, err + } + params.Set("symbol", symbolValue) + + err = by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetOrderbook, params), usdcPublicRate, &data) + if err != nil { + return nil, err + } + + for x := range data.Result { + switch data.Result[x].Side { + case sideBuy: + resp.Bids = append(resp.Bids, orderbook.Item{ + Price: data.Result[x].Price, + Amount: data.Result[x].Size, + }) + case sideSell: + resp.Asks = append(resp.Asks, orderbook.Item{ + Price: data.Result[x].Price, + Amount: data.Result[x].Size, + }) + default: + return nil, errInvalidSide + } + } + return &resp, nil +} + +// GetUSDCContracts gets all contract information for USDCMarginedFutures. +func (by *Bybit) GetUSDCContracts(ctx context.Context, symbol currency.Pair, direction string, limit int64) ([]USDCContract, error) { + resp := struct { + Data []USDCContract `json:"result"` + USDCError + }{} + + params := url.Values{} + if !symbol.IsEmpty() { + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + } + + if direction != "" { + params.Set("direction", direction) + } + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetContracts, params), usdcPublicRate, &resp) +} + +// GetUSDCSymbols gets all symbol information for USDCMarginedFutures. +func (by *Bybit) GetUSDCSymbols(ctx context.Context, symbol currency.Pair) (USDCSymbol, error) { + resp := struct { + Data USDCSymbol `json:"result"` + USDCError + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return USDCSymbol{}, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetSymbols, params), usdcPublicRate, &resp) +} + +// GetUSDCKlines gets kline of symbol for USDCMarginedFutures. +func (by *Bybit) GetUSDCKlines(ctx context.Context, symbol currency.Pair, period string, startTime time.Time, limit int64) ([]USDCKline, error) { + resp := struct { + Data []USDCKline `json:"result"` + USDCError + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return nil, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + if !common.StringDataCompare(validFuturesIntervals, period) { + return resp.Data, errInvalidPeriod + } + params.Set("period", period) + + if startTime.IsZero() { + return nil, errInvalidStartTime + } + params.Set("startTime", strconv.FormatInt(startTime.Unix(), 10)) + + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetKlines, params), usdcPublicRate, &resp) +} + +// GetUSDCMarkPriceKlines gets mark price kline of symbol for USDCMarginedFutures. +func (by *Bybit) GetUSDCMarkPriceKlines(ctx context.Context, symbol currency.Pair, period string, startTime time.Time, limit int64) ([]USDCKlineBase, error) { + resp := struct { + Data []USDCKlineBase `json:"result"` + USDCError + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return nil, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + if !common.StringDataCompare(validFuturesIntervals, period) { + return resp.Data, errInvalidPeriod + } + params.Set("period", period) + + if startTime.IsZero() { + return nil, errInvalidStartTime + } + params.Set("startTime", strconv.FormatInt(startTime.Unix(), 10)) + + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetMarkPriceKlines, params), usdcPublicRate, &resp) +} + +// GetUSDCIndexPriceKlines gets index price kline of symbol for USDCMarginedFutures. +func (by *Bybit) GetUSDCIndexPriceKlines(ctx context.Context, symbol currency.Pair, period string, startTime time.Time, limit int64) ([]USDCKlineBase, error) { + resp := struct { + Data []USDCKlineBase `json:"result"` + USDCError + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return nil, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + if !common.StringDataCompare(validFuturesIntervals, period) { + return resp.Data, errInvalidPeriod + } + params.Set("period", period) + + if startTime.IsZero() { + return nil, errInvalidStartTime + } + params.Set("startTime", strconv.FormatInt(startTime.Unix(), 10)) + + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetIndexPriceKlines, params), usdcPublicRate, &resp) +} + +// GetUSDCPremiumIndexKlines gets premium index kline of symbol for USDCMarginedFutures. +func (by *Bybit) GetUSDCPremiumIndexKlines(ctx context.Context, symbol currency.Pair, period string, startTime time.Time, limit int64) ([]USDCKlineBase, error) { + resp := struct { + Data []USDCKlineBase `json:"result"` + USDCError + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return nil, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + if !common.StringDataCompare(validFuturesIntervals, period) { + return resp.Data, errInvalidPeriod + } + params.Set("period", period) + + if startTime.IsZero() { + return nil, errInvalidStartTime + } + params.Set("startTime", strconv.FormatInt(startTime.Unix(), 10)) + + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetPremiumIndexKlines, params), usdcPublicRate, &resp) +} + +// GetUSDCOpenInterest gets open interest of symbol for USDCMarginedFutures. +func (by *Bybit) GetUSDCOpenInterest(ctx context.Context, symbol currency.Pair, period string, limit int64) ([]USDCOpenInterest, error) { + resp := struct { + Data []USDCOpenInterest `json:"result"` + USDCError + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return nil, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + if !common.StringDataCompare(validFuturesPeriods, period) { + return resp.Data, errInvalidPeriod + } + params.Set("period", period) + + if limit > 0 && limit <= 200 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetOpenInterest, params), usdcPublicRate, &resp) +} + +// GetUSDCLargeOrders gets large order of symbol for USDCMarginedFutures. +func (by *Bybit) GetUSDCLargeOrders(ctx context.Context, symbol currency.Pair, limit int64) ([]USDCLargeOrder, error) { + resp := struct { + Data []USDCLargeOrder `json:"result"` + USDCError + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return nil, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + if limit > 0 && limit <= 100 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetLargeOrders, params), usdcPublicRate, &resp) +} + +// GetUSDCAccountRatio gets account long short ratio of symbol for USDCMarginedFutures. +func (by *Bybit) GetUSDCAccountRatio(ctx context.Context, symbol currency.Pair, period string, limit int64) ([]USDCAccountRatio, error) { + resp := struct { + Data []USDCAccountRatio `json:"result"` + USDCError + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return nil, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Data, err + } + params.Set("symbol", symbolValue) + + if !common.StringDataCompare(validFuturesPeriods, period) { + return resp.Data, errInvalidPeriod + } + params.Set("period", period) + + if limit > 0 && limit <= 500 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + return resp.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetAccountRatio, params), usdcPublicRate, &resp) +} + +// GetUSDCLatestTrades gets latest 500 trades for USDCMarginedFutures. +func (by *Bybit) GetUSDCLatestTrades(ctx context.Context, symbol currency.Pair, category string, limit int64) ([]USDCTrade, error) { + resp := struct { + Result struct { + ResultSize int64 `json:"resultTotalSize"` + Cursor string `json:"cursor"` + Data []USDCTrade `json:"dataList"` + } `json:"result"` + USDCError + }{} + + params := url.Values{} + if category == "" { + return nil, errInvalidCategory + } + params.Set("category", category) + + if !symbol.IsEmpty() { + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result.Data, err + } + params.Set("symbol", symbolValue) + } + + if limit > 0 && limit <= 500 { + params.Set("limit", strconv.FormatInt(limit, 10)) + } + return resp.Result.Data, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetLatestTrades, params), usdcPublicRate, &resp) +} + +// PlaceUSDCOrder create new USDC derivatives order. +func (by *Bybit) PlaceUSDCOrder(ctx context.Context, symbol currency.Pair, orderType, orderFilter, side, timeInForce, orderLinkID string, orderPrice, orderQty, takeProfit, stopLoss, tptriggerby, slTriggerBy, triggerPrice, triggerBy float64, reduceOnly, closeOnTrigger, mmp bool) (USDCCreateOrderResp, error) { + resp := struct { + Result USDCCreateOrderResp `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if symbol.IsEmpty() { + return USDCCreateOrderResp{}, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result, err + } + req["symbol"] = symbolValue + + if orderType == "" { + return USDCCreateOrderResp{}, errInvalidOrderType + } + req["orderType"] = orderType + + if orderFilter == "" { + return USDCCreateOrderResp{}, errInvalidOrderFilter + } + req["orderFilter"] = orderFilter + + if side == "" { + return USDCCreateOrderResp{}, errInvalidSide + } + req["side"] = side + + if orderQty == 0 { + return USDCCreateOrderResp{}, errInvalidQuantity + } + req["orderQty"] = strconv.FormatFloat(orderQty, 'f', -1, 64) + + if orderPrice != 0 { + req["orderPrice"] = strconv.FormatFloat(orderPrice, 'f', -1, 64) + } + + if timeInForce != "" { + req["timeInForce"] = timeInForce + } + + if orderLinkID != "" { + req["orderLinkId"] = orderLinkID + } + + if reduceOnly { + req["reduceOnly"] = true + } else { + req["reduceOnly"] = false + } + + if closeOnTrigger { + req["closeOnTrigger"] = true + } else { + req["closeOnTrigger"] = false + } + + if mmp { + req["mmp"] = true + } else { + req["mmp"] = false + } + + if takeProfit != 0 { + req["takeProfit"] = strconv.FormatFloat(takeProfit, 'f', -1, 64) + } + + if stopLoss != 0 { + req["stopLoss"] = strconv.FormatFloat(stopLoss, 'f', -1, 64) + } + + if tptriggerby != 0 { + req["tptriggerby"] = tptriggerby + } + + if slTriggerBy != 0 { + req["slTriggerBy"] = strconv.FormatFloat(slTriggerBy, 'f', -1, 64) + } + + if triggerPrice != 0 { + req["triggerPrice"] = strconv.FormatFloat(triggerPrice, 'f', -1, 64) + } + + if triggerBy != 0 { + req["triggerBy"] = triggerBy + } + return resp.Result, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesPlaceOrder, req, &resp, usdcPlaceOrderRate) +} + +// ModifyUSDCOrder modifies USDC derivatives order. +func (by *Bybit) ModifyUSDCOrder(ctx context.Context, symbol currency.Pair, orderFilter, orderID, orderLinkID string, orderPrice, orderQty, takeProfit, stopLoss, tptriggerby, slTriggerBy, triggerPrice float64) (string, error) { + resp := struct { + Result struct { + OrderID string `json:"orderId"` + OrderLinkedID string `json:"orderLinkId"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if symbol.IsEmpty() { + return resp.Result.OrderID, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result.OrderID, err + } + req["symbol"] = symbolValue + + if orderFilter == "" { + return resp.Result.OrderID, errInvalidOrderFilter + } + req["orderFilter"] = orderFilter + + if orderID == "" && orderLinkID == "" { + return resp.Result.OrderID, errOrderOrOrderLinkIDMissing + } + + if orderID != "" { + req["orderId"] = orderID + } + + if orderLinkID != "" { + req["orderLinkId"] = orderLinkID + } + + if orderPrice != 0 { + req["orderPrice"] = strconv.FormatFloat(orderPrice, 'f', -1, 64) + } + + if orderQty != 0 { + req["orderQty"] = strconv.FormatFloat(orderQty, 'f', -1, 64) + } + + if takeProfit != 0 { + req["takeProfit"] = strconv.FormatFloat(takeProfit, 'f', -1, 64) + } + + if stopLoss != 0 { + req["stopLoss"] = strconv.FormatFloat(stopLoss, 'f', -1, 64) + } + + if tptriggerby != 0 { + req["tptriggerby"] = strconv.FormatFloat(tptriggerby, 'f', -1, 64) + } + + if slTriggerBy != 0 { + req["slTriggerBy"] = strconv.FormatFloat(slTriggerBy, 'f', -1, 64) + } + + if triggerPrice != 0 { + req["triggerPrice"] = strconv.FormatFloat(triggerPrice, 'f', -1, 64) + } + return resp.Result.OrderID, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesModifyOrder, req, &resp, usdcModifyOrderRate) +} + +// CancelUSDCOrder cancels USDC derivatives order. +func (by *Bybit) CancelUSDCOrder(ctx context.Context, symbol currency.Pair, orderFilter, orderID, orderLinkID string) (string, error) { + resp := struct { + Result struct { + OrderID string `json:"orderId"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if symbol.IsEmpty() { + return resp.Result.OrderID, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result.OrderID, err + } + req["symbol"] = symbolValue + + if orderFilter == "" { + return resp.Result.OrderID, errInvalidOrderFilter + } + req["orderFilter"] = orderFilter + + if orderID == "" && orderLinkID == "" { + return resp.Result.OrderID, errOrderOrOrderLinkIDMissing + } + + if orderID != "" { + req["orderId"] = orderID + } + + if orderLinkID != "" { + req["orderLinkId"] = orderLinkID + } + return resp.Result.OrderID, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesCancelOrder, req, &resp, usdcCancelOrderRate) +} + +// CancelAllActiveUSDCOrder cancels all active USDC derivatives order. +func (by *Bybit) CancelAllActiveUSDCOrder(ctx context.Context, symbol currency.Pair, orderFilter string) error { + req := make(map[string]interface{}) + if symbol.IsEmpty() { + return errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return err + } + req["symbol"] = symbolValue + + if orderFilter == "" { + return errInvalidOrderFilter + } + req["orderFilter"] = orderFilter + return by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesCancelAllActiveOrder, req, nil, usdcCancelAllOrderRate) +} + +// GetActiveUSDCOrder gets all active USDC derivatives order. +func (by *Bybit) GetActiveUSDCOrder(ctx context.Context, symbol currency.Pair, category, orderID, orderLinkID, orderFilter, direction, cursor string, limit int64) ([]USDCOrder, error) { + resp := struct { + Result struct { + Cursor string `json:"cursor"` + ResultTotalSize int64 `json:"resultTotalSize"` + Data []USDCOrder `json:"dataList"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if !symbol.IsEmpty() { + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result.Data, err + } + req["symbol"] = symbolValue + } + + if category == "" { + return nil, errInvalidCategory + } + req["category"] = category + + if orderID != "" { + req["orderId"] = orderID + } + + if orderLinkID != "" { + req["orderLinkId"] = orderLinkID + } + + if orderFilter != "" { + req["orderFilter"] = orderFilter + } + + if direction != "" { + req["direction"] = direction + } + + if limit != 0 { + req["limit"] = strconv.FormatInt(limit, 10) + } + + if cursor != "" { + req["cursor"] = cursor + } + return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetActiveOrder, req, &resp, usdcGetOrderRate) +} + +// GetUSDCOrderHistory gets order history with support of last 30 days of USDC derivatives order. +func (by *Bybit) GetUSDCOrderHistory(ctx context.Context, symbol currency.Pair, category, orderID, orderLinkID, orderStatus, direction, cursor string, limit int64) ([]USDCOrderHistory, error) { + resp := struct { + Result struct { + Cursor string `json:"cursor"` + ResultTotalSize int64 `json:"resultTotalSize"` + Data []USDCOrderHistory `json:"dataList"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if !symbol.IsEmpty() { + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result.Data, err + } + req["symbol"] = symbolValue + } + + if category == "" { + return nil, errInvalidCategory + } + req["category"] = category + + if orderID != "" { + req["orderId"] = orderID + } + + if orderLinkID != "" { + req["orderLinkId"] = orderLinkID + } + + if orderStatus != "" { + req["orderStatus"] = orderStatus + } + + if direction != "" { + req["direction"] = direction + } + + if limit != 0 { + req["limit"] = strconv.FormatInt(limit, 10) + } + + if cursor != "" { + req["cursor"] = cursor + } + return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetOrderHistory, req, &resp, usdcGetOrderHistoryRate) +} + +// GetUSDCTradeHistory gets trade history with support of last 30 days of USDC derivatives trades. +func (by *Bybit) GetUSDCTradeHistory(ctx context.Context, symbol currency.Pair, category, orderID, orderLinkID, direction, cursor string, limit int64, startTime time.Time) ([]USDCTradeHistory, error) { + resp := struct { + Result struct { + Cursor string `json:"cursor"` + ResultTotalSize int64 `json:"resultTotalSize"` + Data []USDCTradeHistory `json:"dataList"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if !symbol.IsEmpty() { + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result.Data, err + } + req["symbol"] = symbolValue + } + + if category == "" { + return nil, errInvalidCategory + } + req["category"] = category + + if orderID == "" && orderLinkID == "" { + return nil, errOrderOrOrderLinkIDMissing + } + + if orderID != "" { + req["orderId"] = orderID + } + + if orderLinkID != "" { + req["orderLinkId"] = orderLinkID + } + + if startTime.IsZero() { + return nil, errInvalidStartTime + } + req["startTime"] = strconv.FormatInt(startTime.Unix(), 10) + + if direction != "" { + req["direction"] = direction + } + + if limit > 0 && limit <= 50 { + req["limit"] = strconv.FormatInt(limit, 10) + } + + if cursor != "" { + req["cursor"] = cursor + } + return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetTradeHistory, req, &resp, usdcGetTradeHistoryRate) +} + +// GetUSDCTransactionLog gets transaction logs with support of last 30 days of USDC derivatives trades. +func (by *Bybit) GetUSDCTransactionLog(ctx context.Context, startTime, endTime time.Time, txType, category, direction, cursor string, limit int64) ([]USDCTxLog, error) { + resp := struct { + Result struct { + Cursor string `json:"cursor"` + ResultTotalSize int64 `json:"resultTotalSize"` + Data []USDCTxLog `json:"dataList"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if !startTime.IsZero() { + req["startTime"] = strconv.FormatInt(startTime.Unix(), 10) + } + + if !endTime.IsZero() { + req["endTime"] = strconv.FormatInt(endTime.Unix(), 10) + } + + if txType == "" { + return nil, errors.New("type missing") + } + req["type"] = txType + + if category != "" { + req["category"] = category + } + + if direction != "" { + req["direction"] = direction + } + + if limit > 0 && limit <= 50 { + req["limit"] = strconv.FormatInt(limit, 10) + } + + if cursor != "" { + req["cursor"] = cursor + } + return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetTransactionLog, req, &resp, usdcGetTransactionRate) +} + +// GetUSDCWalletBalance gets USDC wallet balance. +func (by *Bybit) GetUSDCWalletBalance(ctx context.Context) (USDCWalletBalance, error) { + resp := struct { + Result USDCWalletBalance `json:"result"` + USDCError + }{} + + return resp.Result, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetWalletBalance, nil, &resp, usdcGetWalletRate) +} + +// GetUSDCAssetInfo gets USDC asset information. +func (by *Bybit) GetUSDCAssetInfo(ctx context.Context, baseCoin string) ([]USDCAssetInfo, error) { + resp := struct { + Result struct { + ResultTotalSize int64 `json:"resultTotalSize"` + Data []USDCAssetInfo `json:"dataList"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if baseCoin != "" { + req["baseCoin"] = baseCoin + } + + return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetAssetInfo, req, &resp, usdcGetAssetRate) +} + +// GetUSDCMarginInfo gets USDC account margin information. +func (by *Bybit) GetUSDCMarginInfo(ctx context.Context) (string, error) { + resp := struct { + Result struct { + MarginMode string `json:"marginMode"` + } `json:"result"` + USDCError + }{} + + return resp.Result.MarginMode, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetMarginInfo, nil, &resp, usdcGetMarginRate) +} + +// GetUSDCPosition gets USDC position information. +func (by *Bybit) GetUSDCPosition(ctx context.Context, symbol currency.Pair, category, direction, cursor string, limit int64) ([]USDCPosition, error) { + resp := struct { + Result struct { + Cursor string `json:"cursor"` + ResultTotalSize int64 `json:"resultTotalSize"` + Data []USDCPosition `json:"dataList"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if !symbol.IsEmpty() { + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result.Data, err + } + req["symbol"] = symbolValue + } + + if category == "" { + return nil, errInvalidCategory + } + req["category"] = category + + if cursor != "" { + req["cursor"] = cursor + } + + if direction != "" { + req["direction"] = direction + } + + if limit > 0 && limit <= 50 { + req["limit"] = strconv.FormatInt(limit, 10) + } + + return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetPosition, req, &resp, usdcGetPositionRate) +} + +// SetUSDCLeverage sets USDC leverage. +func (by *Bybit) SetUSDCLeverage(ctx context.Context, symbol currency.Pair, leverage float64) (float64, error) { + resp := struct { + Result struct { + Leverage float64 `json:"leverage,string"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if symbol.IsEmpty() { + return 0, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result.Leverage, err + } + req["symbol"] = symbolValue + + if leverage <= 0 { + return 0, errInvalidLeverage + } + req["leverage"] = strconv.FormatFloat(leverage, 'f', -1, 64) + + return resp.Result.Leverage, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesSetLeverage, req, &resp, usdcSetLeverageRate) +} + +// GetUSDCSettlementHistory gets USDC settlement history with support of last 30 days. +func (by *Bybit) GetUSDCSettlementHistory(ctx context.Context, symbol currency.Pair, direction, cursor string, limit int64) ([]USDCSettlementHistory, error) { + resp := struct { + Result struct { + Cursor string `json:"cursor"` + ResultTotalSize int64 `json:"resultTotalSize"` + Data []USDCSettlementHistory `json:"dataList"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if symbol.IsEmpty() { + return resp.Result.Data, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result.Data, err + } + req["symbol"] = symbolValue + + if cursor != "" { + req["cursor"] = cursor + } + + if direction != "" { + req["direction"] = direction + } + + if limit > 0 && limit <= 50 { + req["limit"] = strconv.FormatInt(limit, 10) + } + + return resp.Result.Data, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetSettlementHistory, req, &resp, usdcGetSettlementRate) +} + +// GetUSDCRiskLimit gets USDC risk limits data. +func (by *Bybit) GetUSDCRiskLimit(ctx context.Context, symbol currency.Pair) ([]USDCRiskLimit, error) { + resp := struct { + Result []USDCRiskLimit `json:"result"` + USDCError + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return nil, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result, err + } + params.Set("symbol", symbolValue) + + return resp.Result, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetRiskLimit, params), usdcPublicRate, &resp) +} + +// SetUSDCRiskLimit sets USDC risk limit. +func (by *Bybit) SetUSDCRiskLimit(ctx context.Context, symbol currency.Pair, riskID int64) (string, error) { + resp := struct { + Result struct { + RiskID string `json:"riskId"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + if symbol.IsEmpty() { + return resp.Result.RiskID, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result.RiskID, err + } + req["symbol"] = symbolValue + + if riskID <= 0 { + return resp.Result.RiskID, errInvalidRiskID + } + req["riskId"] = riskID + + return resp.Result.RiskID, by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesSetRiskLimit, req, &resp, usdcSetRiskRate) +} + +// GetUSDCLastFundingRate gets USDC last funding rates. +func (by *Bybit) GetUSDCLastFundingRate(ctx context.Context, symbol currency.Pair) (USDCFundingInfo, error) { + resp := struct { + Result USDCFundingInfo `json:"result"` + USDCError + }{} + + params := url.Values{} + if symbol.IsEmpty() { + return resp.Result, errSymbolMissing + } + symbolValue, err := by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result, err + } + params.Set("symbol", symbolValue) + + return resp.Result, by.SendHTTPRequest(ctx, exchange.RestUSDCMargined, common.EncodeURLValues(usdcfuturesGetLastFundingRate, params), usdcPublicRate, &resp) +} + +// GetUSDCPredictedFundingRate gets predicted funding rates and my predicted funding fee. +func (by *Bybit) GetUSDCPredictedFundingRate(ctx context.Context, symbol currency.Pair) (predictedFundingRate, predictedFundingFee float64, err error) { + resp := struct { + Result struct { + PredictedFundingRate float64 `json:"predictedFundingRate,string"` + PredictedFundingFee float64 `json:"predictedFundingFee,string"` + } `json:"result"` + USDCError + }{} + + req := make(map[string]interface{}) + var symbolValue string + if symbol.IsEmpty() { + return resp.Result.PredictedFundingRate, resp.Result.PredictedFundingFee, errSymbolMissing + } + symbolValue, err = by.FormatSymbol(symbol, asset.USDCMarginedFutures) + if err != nil { + return resp.Result.PredictedFundingRate, resp.Result.PredictedFundingFee, err + } + req["symbol"] = symbolValue + + err = by.SendUSDCAuthHTTPRequest(ctx, exchange.RestUSDCMargined, http.MethodPost, usdcfuturesGetPredictedFundingRate, req, &resp, usdcGetPredictedFundingRate) + predictedFundingRate = resp.Result.PredictedFundingRate + predictedFundingFee = resp.Result.PredictedFundingFee + return +} + +// SendUSDCAuthHTTPRequest sends an authenticated HTTP request +func (by *Bybit) SendUSDCAuthHTTPRequest(ctx context.Context, ePath exchange.URL, method, path string, data interface{}, result UnmarshalTo, f request.EndpointLimit) error { + creds, err := by.GetCredentials(ctx) + if err != nil { + return err + } + + if result == nil { + result = &USDCError{} + } + + endpointPath, err := by.API.Endpoints.GetURL(ePath) + if err != nil { + return err + } + + err = by.SendPayload(ctx, f, func() (*request.Item, error) { + nowTimeInMilli := strconv.FormatInt(time.Now().UnixMilli(), 10) + headers := make(map[string]string) + var payload, hmacSigned []byte + + if data != nil { + d, ok := data.(map[string]interface{}) + if !ok { + return nil, common.GetAssertError("map[string]interface{}", data) + } + payload, err = json.Marshal(d) + if err != nil { + return nil, err + } + } + + signInput := nowTimeInMilli + creds.Key + defaultRecvWindow + string(payload) + hmacSigned, err = crypto.GetHMAC(crypto.HashSHA256, []byte(signInput), []byte(creds.Secret)) + if err != nil { + return nil, err + } + + headers["Content-Type"] = "application/json" + headers["X-BAPI-API-KEY"] = creds.Key + headers["X-BAPI-SIGN"] = crypto.HexEncodeToString(hmacSigned) + headers["X-BAPI-SIGN-TYPE"] = "2" + headers["X-BAPI-TIMESTAMP"] = nowTimeInMilli + headers["X-BAPI-RECV-WINDOW"] = defaultRecvWindow + + return &request.Item{ + Method: method, + Path: endpointPath + path, + Headers: headers, + Body: bytes.NewBuffer(payload), + Result: &result, + AuthRequest: true, + Verbose: by.Verbose, + HTTPDebugging: by.HTTPDebugging, + HTTPRecording: by.HTTPRecording}, nil + }) + if err != nil { + return err + } + return result.GetError() +} + +// USDCError defines all error information for each USDC request +type USDCError struct { + ReturnCode int64 `json:"retCode"` + ReturnMsg string `json:"retMsg"` +} + +// GetError checks and returns an error if it is supplied. +func (e USDCError) GetError() error { + if e.ReturnCode != 0 && e.ReturnMsg != "" { + return errors.New(e.ReturnMsg) + } + return nil +} diff --git a/exchanges/bybit/bybit_websocket.go b/exchanges/bybit/bybit_websocket.go new file mode 100644 index 00000000..74e84e92 --- /dev/null +++ b/exchanges/bybit/bybit_websocket.go @@ -0,0 +1,555 @@ +package bybit + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/crypto" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" + "github.com/thrasher-corp/gocryptotrader/exchanges/trade" + "github.com/thrasher-corp/gocryptotrader/log" +) + +const ( + bybitWSBaseURL = "wss://stream.bybit.com/" + wsSpotPublicTopicV2 = "spot/quote/ws/v2" + wsSpotPrivate = "spot/ws" + bybitWebsocketTimer = 20 * time.Second + wsOrderbook = "depth" + wsTicker = "bookTicker" + wsTrades = "trade" + wsKlines = "kline" + + wsAccountInfo = "outboundAccountInfo" + wsOrderExecution = "executionReport" + wsTickerInfo = "ticketInfo" + + sub = "sub" // event for subscribe + cancel = "cancel" // event for unsubscribe +) + +var comms = make(chan stream.Response) + +// WsConnect connects to a websocket feed +func (by *Bybit) WsConnect() error { + if !by.Websocket.IsEnabled() || !by.IsEnabled() || !by.IsAssetWebsocketSupported(asset.Spot) { + return errors.New(stream.WebsocketNotEnabled) + } + var dialer websocket.Dialer + err := by.Websocket.Conn.Dial(&dialer, http.Header{}) + if err != nil { + return err + } + by.Websocket.Conn.SetupPingHandler(stream.PingHandler{ + MessageType: websocket.TextMessage, + Message: []byte(`{"op":"ping"}`), + Delay: bybitWebsocketTimer, + }) + + by.Websocket.Wg.Add(1) + go by.wsReadData(by.Websocket.Conn) + if by.IsWebsocketAuthenticationSupported() { + err = by.WsAuth(context.TODO()) + if err != nil { + by.Websocket.DataHandler <- err + by.Websocket.SetCanUseAuthenticatedEndpoints(false) + } + } + + by.Websocket.Wg.Add(1) + go by.WsDataHandler() + return nil +} + +// WsAuth sends an authentication message to receive auth data +func (by *Bybit) WsAuth(ctx context.Context) error { + var dialer websocket.Dialer + err := by.Websocket.AuthConn.Dial(&dialer, http.Header{}) + if err != nil { + return err + } + + by.Websocket.AuthConn.SetupPingHandler(stream.PingHandler{ + MessageType: websocket.TextMessage, + Message: []byte(`{"op":"ping"}`), + Delay: bybitWebsocketTimer, + }) + + by.Websocket.Wg.Add(1) + go by.wsReadData(by.Websocket.AuthConn) + + creds, err := by.GetCredentials(ctx) + if err != nil { + return err + } + intNonce := (time.Now().Unix() + 1) * 1000 + strNonce := strconv.FormatInt(intNonce, 10) + hmac, err := crypto.GetHMAC( + crypto.HashSHA256, + []byte("GET/realtime"+strNonce), + []byte(creds.Secret), + ) + if err != nil { + return err + } + sign := crypto.HexEncodeToString(hmac) + req := Authenticate{ + Operation: "auth", + Args: []interface{}{creds.Key, intNonce, sign}, + } + return by.Websocket.AuthConn.SendJSONMessage(req) +} + +// Subscribe sends a websocket message to receive data from the channel +func (by *Bybit) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToSubscribe { + var subReq WsReq + subReq.Topic = channelsToSubscribe[i].Channel + subReq.Event = sub + + formattedPair, err := by.FormatExchangeCurrency(channelsToSubscribe[i].Currency, asset.Spot) + if err != nil { + errs = append(errs, err) + continue + } + if channelsToSubscribe[i].Channel == wsKlines { + subReq.Parameters = WsParams{ + Symbol: formattedPair.String(), + IsBinary: true, + KlineType: "1m", + } + } else { + subReq.Parameters = WsParams{ + Symbol: formattedPair.String(), + IsBinary: true, + } + } + err = by.Websocket.Conn.SendJSONMessage(subReq) + if err != nil { + errs = append(errs, err) + continue + } + by.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) + } + if errs != nil { + return errs + } + return nil +} + +// Unsubscribe sends a websocket message to stop receiving data from the channel +func (by *Bybit) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + var errs common.Errors + + for i := range channelsToUnsubscribe { + var unSub WsReq + unSub.Event = cancel + unSub.Topic = channelsToUnsubscribe[i].Channel + + formattedPair, err := by.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, asset.Spot) + if err != nil { + errs = append(errs, err) + continue + } + unSub.Parameters = WsParams{ + Symbol: formattedPair.String(), + } + err = by.Websocket.Conn.SendJSONMessage(unSub) + if err != nil { + errs = append(errs, err) + continue + } + by.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) + } + if errs != nil { + return errs + } + return nil +} + +// wsReadData receives and passes on websocket messages for processing +func (by *Bybit) wsReadData(ws stream.Connection) { + defer by.Websocket.Wg.Done() + for { + resp := ws.ReadMessage() + if resp.Raw == nil { + return + } + comms <- resp + } +} + +// GenerateDefaultSubscriptions generates default subscription +func (by *Bybit) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + var subscriptions []stream.ChannelSubscription + var channels = []string{wsTicker, wsTrades, wsOrderbook, wsKlines} + pairs, err := by.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } + for z := range pairs { + for x := range channels { + subscriptions = append(subscriptions, + stream.ChannelSubscription{ + Channel: channels[x], + Currency: pairs[z], + Asset: asset.Spot, + }) + } + } + return subscriptions, nil +} + +func stringToOrderStatus(status string) (order.Status, error) { + switch status { + case "NEW": + return order.New, nil + case "CANCELED": + return order.Cancelled, nil + case "REJECTED": + return order.Rejected, nil + case "TRADE": + return order.PartiallyFilled, nil + case "EXPIRED": + return order.Expired, nil + default: + return order.UnknownStatus, errors.New(status + " not recognised as order status") + } +} + +// WsDataHandler handles data from wsReadData +func (by *Bybit) WsDataHandler() { + defer by.Websocket.Wg.Done() + for { + select { + case <-by.Websocket.ShutdownC: + return + case resp := <-comms: + err := by.wsHandleData(resp.Raw) + if err != nil { + by.Websocket.DataHandler <- err + } + } + } +} + +func (by *Bybit) wsHandleData(respRaw []byte) error { + var result interface{} + err := json.Unmarshal(respRaw, &result) + if err != nil { + return err + } + switch d := result.(type) { + case map[string]interface{}: + if method, ok := d["event"].(string); ok { + if strings.EqualFold(method, sub) { + return nil + } + if strings.EqualFold(method, cancel) { + return nil + } + } + + if t, ok := d["topic"].(string); ok { + switch t { + case wsOrderbook: + var data WsOrderbook + err := json.Unmarshal(respRaw, &data) + if err != nil { + return err + } + p, err := by.extractCurrencyPair(data.OBData.Symbol, asset.Spot) + if err != nil { + return err + } + + err = by.wsUpdateOrderbook(&data.OBData, p, asset.Spot) + if err != nil { + return err + } + return nil + case wsTrades: + if !by.IsSaveTradeDataEnabled() { + return nil + } + var data WsTrade + err := json.Unmarshal(respRaw, &data) + if err != nil { + return err + } + + p, err := by.extractCurrencyPair(data.Parameters.Symbol, asset.Spot) + if err != nil { + return err + } + + side := order.Sell + if data.TradeData.Side { + side = order.Buy + } + + return trade.AddTradesToBuffer(by.Name, trade.Data{ + Timestamp: data.TradeData.Time.Time(), + CurrencyPair: p, + AssetType: asset.Spot, + Exchange: by.Name, + Price: data.TradeData.Price, + Amount: data.TradeData.Size, + Side: side, + TID: data.TradeData.ID, + }) + case wsTicker: + var data WsSpotTicker + err := json.Unmarshal(respRaw, &data) + if err != nil { + return err + } + + p, err := by.extractCurrencyPair(data.Ticker.Symbol, asset.Spot) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Bid: data.Ticker.Bid, + Ask: data.Ticker.Ask, + LastUpdated: data.Ticker.Time.Time(), + AssetType: asset.Spot, + Pair: p, + } + return nil + case wsKlines: + var data KlineStream + err := json.Unmarshal(respRaw, &data) + if err != nil { + return err + } + + p, err := by.extractCurrencyPair(data.Kline.Symbol, asset.Spot) + if err != nil { + return err + } + + by.Websocket.DataHandler <- stream.KlineData{ + Pair: p, + AssetType: asset.Spot, + Exchange: by.Name, + StartTime: data.Kline.StartTime.Time(), + Interval: data.Parameters.KlineType, + OpenPrice: data.Kline.OpenPrice, + ClosePrice: data.Kline.ClosePrice, + HighPrice: data.Kline.HighPrice, + LowPrice: data.Kline.LowPrice, + Volume: data.Kline.Volume, + } + return nil + default: + by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + string(respRaw)} + } + } + + if m, ok := d["auth"]; ok { + log.Infof(log.WebsocketMgr, "%v received auth response: %v", by.Name, m) + return nil + } + + if m, ok := d["pong"]; ok { + log.Infof(log.WebsocketMgr, "%v received pong: %v", by.Name, m) + return nil + } + case []interface{}: + for i := range d { + obj, ok := d[i].(map[string]interface{}) + if !ok { + return common.GetAssertError("map[string]interface{}", d[i]) + } + e, ok := obj["e"].(string) + if !ok { + return common.GetAssertError("string", obj["e"]) + } + + switch e { + case wsAccountInfo: + var data []wsAccount + err := json.Unmarshal(respRaw, &data) + if err != nil { + return fmt.Errorf("%v - Could not convert to outboundAccountInfo structure %w", + by.Name, + err) + } + by.Websocket.DataHandler <- data + return nil + case wsOrderExecution: + var data []wsOrderUpdate + err := json.Unmarshal(respRaw, &data) + if err != nil { + return fmt.Errorf("%v - Could not convert to executionReport structure %w", + by.Name, + err) + } + + for j := range data { + oType, err := order.StringToOrderType(data[j].OrderType) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: data[j].OrderID, + Err: err, + } + } + var oSide order.Side + oSide, err = order.StringToOrderSide(data[j].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: data[j].OrderID, + Err: err, + } + } + var oStatus order.Status + oStatus, err = stringToOrderStatus(data[j].OrderStatus) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: data[j].OrderID, + Err: err, + } + } + + p, err := by.extractCurrencyPair(data[j].Symbol, asset.Spot) + if err != nil { + return err + } + + by.Websocket.DataHandler <- order.Detail{ + Price: data[j].Price, + Amount: data[j].Quantity, + ExecutedAmount: data[j].CumulativeFilledQuantity, + RemainingAmount: data[j].Quantity - data[j].CumulativeFilledQuantity, + Exchange: by.Name, + OrderID: data[j].OrderID, + Type: oType, + Side: oSide, + Status: oStatus, + AssetType: asset.Spot, + Date: data[j].OrderCreationTime.Time(), + Pair: p, + ClientOrderID: data[j].ClientOrderID, + Trades: []order.TradeHistory{ + { + Price: data[j].Price, + Amount: data[j].Quantity, + Exchange: by.Name, + Timestamp: data[j].OrderCreationTime.Time(), + }, + }, + } + } + return nil + case wsTickerInfo: + var data []wsOrderFilled + err := json.Unmarshal(respRaw, &data) + if err != nil { + return fmt.Errorf("%v - Could not convert to ticketInfo structure %w", + by.Name, + err) + } + + for j := range data { + var oSide order.Side + oSide, err = order.StringToOrderSide(data[j].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: data[j].OrderID, + Err: err, + } + } + + p, err := by.extractCurrencyPair(data[j].Symbol, asset.Spot) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &order.Detail{ + Exchange: by.Name, + OrderID: data[j].OrderID, + Side: oSide, + AssetType: asset.Spot, + Pair: p, + Price: data[j].Price, + Amount: data[j].Quantity, + Date: data[j].Timestamp.Time(), + Trades: []order.TradeHistory{ + { + Price: data[j].Price, + Amount: data[j].Quantity, + Exchange: by.Name, + Timestamp: data[j].Timestamp.Time(), + TID: data[j].TradeID, + IsMaker: data[j].IsMaker, + }, + }, + } + } + return nil + } + } + } + + return fmt.Errorf("unhandled stream data %s", string(respRaw)) +} + +func (by *Bybit) wsUpdateOrderbook(update *WsOrderbookData, p currency.Pair, assetType asset.Item) error { + if update == nil || (len(update.Asks) == 0 && len(update.Bids) == 0) { + return errors.New("no orderbook data") + } + asks := make([]orderbook.Item, len(update.Asks)) + for i := range update.Asks { + target, err := strconv.ParseFloat(update.Asks[i][0], 64) + if err != nil { + return err + } + amount, err := strconv.ParseFloat(update.Asks[i][1], 64) + if err != nil { + return err + } + asks[i] = orderbook.Item{Price: target, Amount: amount} + } + bids := make([]orderbook.Item, len(update.Bids)) + for i := range update.Bids { + target, err := strconv.ParseFloat(update.Bids[i][0], 64) + if err != nil { + return err + } + amount, err := strconv.ParseFloat(update.Bids[i][1], 64) + if err != nil { + return err + } + bids[i] = orderbook.Item{Price: target, Amount: amount} + } + return by.Websocket.Orderbook.LoadSnapshot(&orderbook.Base{ + Bids: bids, + Asks: asks, + Pair: p, + LastUpdated: update.Time.Time(), + Asset: assetType, + Exchange: by.Name, + VerifyOrderbook: by.CanVerifyOrderbook, + }) +} diff --git a/exchanges/bybit/bybit_wrapper.go b/exchanges/bybit/bybit_wrapper.go new file mode 100644 index 00000000..d8ff7201 --- /dev/null +++ b/exchanges/bybit/bybit_wrapper.go @@ -0,0 +1,2059 @@ +package bybit + +import ( + "context" + "fmt" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/config" + "github.com/thrasher-corp/gocryptotrader/currency" + exchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/account" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/deposit" + "github.com/thrasher-corp/gocryptotrader/exchanges/kline" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" + "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" + "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" + "github.com/thrasher-corp/gocryptotrader/exchanges/trade" + "github.com/thrasher-corp/gocryptotrader/log" + "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" +) + +// GetDefaultConfig returns a default exchange config +func (by *Bybit) GetDefaultConfig() (*config.Exchange, error) { + by.SetDefaults() + exchCfg := new(config.Exchange) + exchCfg.Name = by.Name + exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout + exchCfg.BaseCurrencies = by.BaseCurrencies + + err := by.SetupDefaults(exchCfg) + if err != nil { + return nil, err + } + + if by.Features.Supports.RESTCapabilities.AutoPairUpdates { + err := by.UpdateTradablePairs(context.TODO(), true) + if err != nil { + return nil, err + } + } + return exchCfg, nil +} + +// SetDefaults sets the basic defaults for Bybit +func (by *Bybit) SetDefaults() { + by.Name = "Bybit" + by.Enabled = true + by.Verbose = true + by.API.CredentialsValidator.RequiresKey = true + by.API.CredentialsValidator.RequiresSecret = true + + requestFmt := ¤cy.PairFormat{Uppercase: true} + + configFmt := ¤cy.PairFormat{Delimiter: currency.DashDelimiter, Uppercase: true} + err := by.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot, asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.Futures, asset.USDCMarginedFutures) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + + err = by.DisableAssetWebsocketSupport(asset.CoinMarginedFutures) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + + err = by.DisableAssetWebsocketSupport(asset.USDTMarginedFutures) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + + err = by.DisableAssetWebsocketSupport(asset.Futures) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + + err = by.DisableAssetWebsocketSupport(asset.USDCMarginedFutures) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + by.Features = exchange.Features{ + Supports: exchange.FeaturesSupported{ + REST: true, + Websocket: true, + RESTCapabilities: protocol.Features{ + TickerFetching: true, + TradeFetching: true, + KlineFetching: true, + OrderbookFetching: true, + AutoPairUpdates: true, + AccountInfo: true, + GetOrder: true, + GetOrders: true, + CancelOrders: true, + CancelOrder: true, + SubmitOrder: true, + DepositHistory: true, + WithdrawalHistory: true, + UserTradeHistory: true, + CryptoDeposit: true, + CryptoWithdrawal: true, + TradeFee: true, + FiatDepositFee: true, + FiatWithdrawalFee: true, + CryptoDepositFee: true, + ModifyOrder: true, + MultiChainDeposits: true, + MultiChainWithdrawals: true, + }, + WebsocketCapabilities: protocol.Features{ + TradeFetching: true, + TickerFetching: true, + KlineFetching: true, + OrderbookFetching: true, + AuthenticatedEndpoints: true, + AccountInfo: true, + GetOrders: true, + Subscribe: true, + Unsubscribe: true, + }, + WithdrawPermissions: exchange.AutoWithdrawCrypto | + exchange.AutoWithdrawFiat, + Kline: kline.ExchangeCapabilitiesSupported{ + Intervals: true, + }, + }, + Enabled: exchange.FeaturesEnabled{ + AutoPairUpdates: true, + Kline: kline.ExchangeCapabilitiesEnabled{ + Intervals: map[string]bool{ + kline.OneMin.Word(): true, + kline.ThreeMin.Word(): true, + kline.FiveMin.Word(): true, + kline.FifteenMin.Word(): true, + kline.ThirtyMin.Word(): true, + kline.OneHour.Word(): true, + kline.TwoHour.Word(): true, + kline.FourHour.Word(): true, + kline.SixHour.Word(): true, + kline.TwelveHour.Word(): true, + kline.OneDay.Word(): true, + kline.OneWeek.Word(): true, + kline.OneMonth.Word(): true, + }, + ResultLimit: 200, + }, + }, + } + + by.Requester, err = request.New(by.Name, + common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout), + request.WithLimiter(SetRateLimit())) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + by.API.Endpoints = by.NewEndpoints() + err = by.API.Endpoints.SetDefaultEndpoints(map[exchange.URL]string{ + exchange.RestSpot: bybitAPIURL, + exchange.RestCoinMargined: bybitAPIURL, + exchange.RestUSDTMargined: bybitAPIURL, + exchange.RestFutures: bybitAPIURL, + exchange.RestUSDCMargined: bybitAPIURL, + exchange.WebsocketSpot: bybitWSBaseURL + wsSpotPublicTopicV2, + }) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + + by.Websocket = stream.New() + by.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit + by.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout + by.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit +} + +// Setup takes in the supplied exchange configuration details and sets params +func (by *Bybit) Setup(exch *config.Exchange) error { + if !exch.Enabled { + by.SetEnabled(false) + return nil + } + + err := by.SetupDefaults(exch) + if err != nil { + return err + } + + wsRunningEndpoint, err := by.API.Endpoints.GetURL(exchange.WebsocketSpot) + if err != nil { + return err + } + + err = by.Websocket.Setup( + &stream.WebsocketSetup{ + ExchangeConfig: exch, + DefaultURL: bybitWSBaseURL + wsSpotPublicTopicV2, + RunningURL: wsRunningEndpoint, + RunningURLAuth: bybitWSBaseURL + wsSpotPrivate, + Connector: by.WsConnect, + Subscriber: by.Subscribe, + Unsubscriber: by.Unsubscribe, + GenerateSubscriptions: by.GenerateDefaultSubscriptions, + Features: &by.Features.Supports.WebsocketCapabilities, + OrderbookBufferConfig: buffer.Config{ + SortBuffer: true, + SortBufferByUpdateIDs: true, + }, + TradeFeed: by.Features.Enabled.TradeFeed, + }) + if err != nil { + return err + } + + err = by.Websocket.SetupNewConnection(stream.ConnectionSetup{ + URL: by.Websocket.GetWebsocketURL(), + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + }) + if err != nil { + return err + } + + return by.Websocket.SetupNewConnection(stream.ConnectionSetup{ + URL: bybitWSBaseURL + wsSpotPrivate, + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + Authenticated: true, + }) +} + +// AuthenticateWebsocket sends an authentication message to the websocket +func (by *Bybit) AuthenticateWebsocket(ctx context.Context) error { + return by.WsAuth(ctx) +} + +// Start starts the Bybit go routine +func (by *Bybit) Start(wg *sync.WaitGroup) error { + if wg == nil { + return fmt.Errorf("%T %w", wg, common.ErrNilPointer) + } + wg.Add(1) + go func() { + by.Run() + wg.Done() + }() + return nil +} + +// Run implements the Bybit wrapper +func (by *Bybit) Run() { + if by.Verbose { + log.Debugf(log.ExchangeSys, + "%s Websocket: %s.", + by.Name, + common.IsEnabled(by.Websocket.IsEnabled())) + by.PrintEnabledPairs() + } + + if !by.GetEnabledFeatures().AutoPairUpdates { + return + } + + err := by.UpdateTradablePairs(context.TODO(), false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + by.Name, + err) + } +} + +// FetchTradablePairs returns a list of the exchanges tradable pairs +func (by *Bybit) FetchTradablePairs(ctx context.Context, a asset.Item) ([]string, error) { + if !by.SupportsAsset(a) { + return nil, fmt.Errorf("asset type of %s is not supported by %s", a, by.Name) + } + + switch a { + case asset.Spot: + allPairs, err := by.GetAllSpotPairs(ctx) + if err != nil { + return nil, err + } + pairs := make([]string, len(allPairs)) + for x := range allPairs { + pairs[x] = allPairs[x].BaseCurrency + currency.DashDelimiter + allPairs[x].QuoteCurrency + } + return pairs, nil + case asset.CoinMarginedFutures: + allPairs, err := by.GetSymbolsInfo(ctx) + if err != nil { + return nil, err + } + pairs := make([]string, 0, len(allPairs)) + for x := range allPairs { + if allPairs[x].Status != "Trading" || allPairs[x].QuoteCurrency != "USD" { + continue + } + symbol := allPairs[x].BaseCurrency + currency.DashDelimiter + allPairs[x].QuoteCurrency + pairs = append(pairs, symbol) + } + return pairs, nil + case asset.USDTMarginedFutures: + allPairs, err := by.GetSymbolsInfo(ctx) + if err != nil { + return nil, err + } + pairs := make([]string, 0, len(allPairs)) + for x := range allPairs { + if allPairs[x].Status != "Trading" || allPairs[x].QuoteCurrency != "USDT" { + continue + } + + symbol := allPairs[x].BaseCurrency + + currency.DashDelimiter + + allPairs[x].QuoteCurrency + pairs = append(pairs, symbol) + } + return pairs, nil + case asset.Futures: + allPairs, err := by.GetSymbolsInfo(ctx) + if err != nil { + return nil, err + } + pairs := make([]string, 0, len(allPairs)) + for x := range allPairs { + if allPairs[x].Status != "Trading" { + continue + } + + symbol := allPairs[x].BaseCurrency + allPairs[x].QuoteCurrency + filter := strings.Split(allPairs[x].Name, symbol) + + if len(filter) != 2 || filter[1] == "" { + continue + } + pairs = append(pairs, symbol+currency.DashDelimiter+filter[1]) + } + return pairs, nil + case asset.USDCMarginedFutures: + allPairs, err := by.GetUSDCContracts(ctx, currency.EMPTYPAIR, "", 0) + if err != nil { + return nil, err + } + pairs := make([]string, len(allPairs)) + for x := range allPairs { + pairs[x] = allPairs[x].BaseCoin + currency.DashDelimiter + "PERP" + } + return pairs, nil + } + return nil, nil +} + +// UpdateTradablePairs updates the exchanges available pairs and stores +// them in the exchanges config +func (by *Bybit) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error { + assetTypes := by.GetAssetTypes(false) + for i := range assetTypes { + pairs, err := by.FetchTradablePairs(ctx, assetTypes[i]) + if err != nil { + return err + } + + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + + err = by.UpdatePairs(p, assetTypes[i], false, forceUpdate) + if err != nil { + return err + } + } + return nil +} + +// UpdateTickers updates the ticker for all currency pairs of a given asset type +func (by *Bybit) UpdateTickers(ctx context.Context, assetType asset.Item) error { + allPairs, err := by.GetEnabledPairs(assetType) + if err != nil { + return err + } + switch assetType { + case asset.Spot: + tick, err := by.Get24HrsChange(ctx, "") + if err != nil { + return err + } + for p := range allPairs { + formattedPair, err := by.FormatExchangeCurrency(allPairs[p], assetType) + if err != nil { + return err + } + + for y := range tick { + if tick[y].Symbol != formattedPair.String() { + continue + } + + cp, err := by.extractCurrencyPair(tick[y].Symbol, assetType) + if err != nil { + return err + } + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick[y].LastPrice, + High: tick[y].HighPrice, + Low: tick[y].LowPrice, + Bid: tick[y].BestBidPrice, + Ask: tick[y].BestAskPrice, + Volume: tick[y].Volume, + QuoteVolume: tick[y].QuoteVolume, + Open: tick[y].OpenPrice, + Pair: cp, + LastUpdated: tick[y].Time, + ExchangeName: by.Name, + AssetType: assetType}) + if err != nil { + return err + } + } + } + + case asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.Futures: + tick, err := by.GetFuturesSymbolPriceTicker(ctx, currency.Pair{}) + if err != nil { + return err + } + + for p := range allPairs { + formattedPair, err := by.FormatExchangeCurrency(allPairs[p], assetType) + if err != nil { + return err + } + + for y := range tick { + if tick[y].Symbol != formattedPair.String() { + continue + } + cp, err := by.extractCurrencyPair(tick[y].Symbol, assetType) + if err != nil { + return err + } + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick[y].LastPrice, + High: tick[y].HighPrice24h, + Low: tick[y].LowPrice24h, + Bid: tick[y].BidPrice, + Ask: tick[y].AskPrice, + Volume: tick[y].Volume24h, + Open: tick[y].OpenValue, + Pair: cp, + ExchangeName: by.Name, + AssetType: assetType}) + if err != nil { + return err + } + } + } + + case asset.USDCMarginedFutures: + for p := range allPairs { + formattedPair, err := by.FormatExchangeCurrency(allPairs[p], assetType) + if err != nil { + return err + } + + tick, err := by.GetUSDCSymbols(ctx, formattedPair) + if err != nil { + return err + } + + cp, err := by.extractCurrencyPair(tick.Symbol, assetType) + if err != nil { + return err + } + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick.LastPrice, + High: tick.High24h, + Low: tick.Low24h, + Bid: tick.Bid, + Ask: tick.Ask, + Volume: tick.Volume24h, + Pair: cp, + ExchangeName: by.Name, + AssetType: assetType}) + if err != nil { + return err + } + } + + default: + return fmt.Errorf("%s %w", assetType, asset.ErrNotSupported) + } + + return nil +} + +// UpdateTicker updates and returns the ticker for a currency pair +func (by *Bybit) UpdateTicker(ctx context.Context, p currency.Pair, assetType asset.Item) (*ticker.Price, error) { + formattedPair, err := by.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + + switch assetType { + case asset.Spot: + tick, err := by.Get24HrsChange(ctx, formattedPair.String()) + if err != nil { + return nil, err + } + + for y := range tick { + cp, err := by.extractCurrencyPair(tick[y].Symbol, assetType) + if err != nil { + return nil, err + } + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick[y].LastPrice, + High: tick[y].HighPrice, + Low: tick[y].LowPrice, + Bid: tick[y].BestBidPrice, + Ask: tick[y].BestAskPrice, + Volume: tick[y].Volume, + QuoteVolume: tick[y].QuoteVolume, + Open: tick[y].OpenPrice, + Pair: cp, + LastUpdated: tick[y].Time, + ExchangeName: by.Name, + AssetType: assetType}) + if err != nil { + return nil, err + } + } + + case asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.Futures: + tick, err := by.GetFuturesSymbolPriceTicker(ctx, formattedPair) + if err != nil { + return nil, err + } + + for y := range tick { + cp, err := by.extractCurrencyPair(tick[y].Symbol, assetType) + if err != nil { + return nil, err + } + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick[y].LastPrice, + High: tick[y].HighPrice24h, + Low: tick[y].LowPrice24h, + Bid: tick[y].BidPrice, + Ask: tick[y].AskPrice, + Volume: tick[y].Volume24h, + Open: tick[y].OpenValue, + Pair: cp, + ExchangeName: by.Name, + AssetType: assetType}) + if err != nil { + return nil, err + } + } + + case asset.USDCMarginedFutures: + tick, err := by.GetUSDCSymbols(ctx, formattedPair) + if err != nil { + return nil, err + } + + cp, err := by.extractCurrencyPair(tick.Symbol, assetType) + if err != nil { + return nil, err + } + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick.LastPrice, + High: tick.High24h, + Low: tick.Low24h, + Bid: tick.Bid, + Ask: tick.Ask, + Volume: tick.Volume24h, + Pair: cp, + ExchangeName: by.Name, + AssetType: assetType}) + if err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("%s %w", assetType, asset.ErrNotSupported) + } + + return ticker.GetTicker(by.Name, p, assetType) +} + +// FetchTicker returns the ticker for a currency pair +func (by *Bybit) FetchTicker(ctx context.Context, p currency.Pair, assetType asset.Item) (*ticker.Price, error) { + fPair, err := by.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + + tickerNew, err := ticker.GetTicker(by.Name, fPair, assetType) + if err != nil { + return by.UpdateTicker(ctx, p, assetType) + } + return tickerNew, nil +} + +// FetchOrderbook returns orderbook base on the currency pair +func (by *Bybit) FetchOrderbook(ctx context.Context, currency currency.Pair, assetType asset.Item) (*orderbook.Base, error) { + ob, err := orderbook.Get(by.Name, currency, assetType) + if err != nil { + return by.UpdateOrderbook(ctx, currency, assetType) + } + return ob, nil +} + +// UpdateOrderbook updates and returns the orderbook for a currency pair +func (by *Bybit) UpdateOrderbook(ctx context.Context, p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { + var orderbookNew *Orderbook + var err error + + formattedPair, err := by.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + + switch assetType { + case asset.Spot: + orderbookNew, err = by.GetOrderBook(ctx, formattedPair.String(), 0) + case asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.Futures: + orderbookNew, err = by.GetFuturesOrderbook(ctx, formattedPair) + case asset.USDCMarginedFutures: + orderbookNew, err = by.GetUSDCFuturesOrderbook(ctx, formattedPair) + default: + return nil, fmt.Errorf("%s %w", assetType, asset.ErrNotSupported) + } + if err != nil { + return nil, err + } + + book := &orderbook.Base{ + Exchange: by.Name, + Pair: formattedPair, + Asset: assetType, + VerifyOrderbook: by.CanVerifyOrderbook, + Bids: make([]orderbook.Item, len(orderbookNew.Bids)), + Asks: make([]orderbook.Item, len(orderbookNew.Asks)), + } + + for x := range orderbookNew.Bids { + book.Bids[x] = orderbook.Item{ + Amount: orderbookNew.Bids[x].Amount, + Price: orderbookNew.Bids[x].Price, + } + } + + for x := range orderbookNew.Asks { + book.Asks[x] = orderbook.Item{ + Amount: orderbookNew.Asks[x].Amount, + Price: orderbookNew.Asks[x].Price, + } + } + err = book.Process() + if err != nil { + return book, err + } + return orderbook.Get(by.Name, formattedPair, assetType) +} + +// UpdateAccountInfo retrieves balances for all enabled currencies +func (by *Bybit) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) { + var info account.Holdings + var acc account.SubAccount + info.Exchange = by.Name + switch assetType { + case asset.Spot: + balances, err := by.GetWalletBalance(ctx) + if err != nil { + return info, err + } + + currencyBalance := make([]account.Balance, len(balances)) + for i := range balances { + currencyBalance[i] = account.Balance{ + CurrencyName: currency.NewCode(balances[i].CoinName), + Total: balances[i].Total, + Hold: balances[i].Locked, + Free: balances[i].Total - balances[i].Locked, + } + } + + acc.Currencies = currencyBalance + + case asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.Futures: + balances, err := by.GetFutureWalletBalance(ctx, "") + if err != nil { + return info, err + } + + var i int + currencyBalance := make([]account.Balance, len(balances)) + for coinName, data := range balances { + currencyBalance[i] = account.Balance{ + CurrencyName: currency.NewCode(coinName), + Total: data.WalletBalance, + Hold: data.WalletBalance - data.AvailableBalance, + Free: data.AvailableBalance, + } + i++ + } + + acc.Currencies = currencyBalance + + case asset.USDCMarginedFutures: + balance, err := by.GetUSDCWalletBalance(ctx) + if err != nil { + return info, err + } + + acc.Currencies = []account.Balance{ + { + CurrencyName: currency.USD, + Total: balance.WalletBalance, + Hold: balance.WalletBalance - balance.AvailableBalance, + Free: balance.AvailableBalance, + }, + } + + default: + return info, fmt.Errorf("%s %w", assetType, asset.ErrNotSupported) + } + acc.AssetType = assetType + info.Accounts = append(info.Accounts, acc) + + creds, err := by.GetCredentials(ctx) + if err != nil { + return account.Holdings{}, err + } + if err := account.Process(&info, creds); err != nil { + return account.Holdings{}, err + } + return info, nil +} + +// FetchAccountInfo retrieves balances for all enabled currencies +func (by *Bybit) FetchAccountInfo(ctx context.Context, assetType asset.Item) (account.Holdings, error) { + creds, err := by.GetCredentials(ctx) + if err != nil { + return account.Holdings{}, err + } + acc, err := account.GetHoldings(by.Name, creds, assetType) + if err != nil { + return by.UpdateAccountInfo(ctx, assetType) + } + + return acc, nil +} + +// GetFundingHistory returns funding history, deposits and +// withdrawals +func (by *Bybit) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, error) { + return nil, common.ErrNotYetImplemented +} + +// GetWithdrawalsHistory returns previous withdrawals data +func (by *Bybit) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) ([]exchange.WithdrawalHistory, error) { + switch a { + case asset.CoinMarginedFutures: + w, err := by.GetWalletWithdrawalRecords(ctx, "", "", "", c, 0, 0) + if err != nil { + return nil, err + } + + withdrawHistory := make([]exchange.WithdrawalHistory, len(w)) + for i := range w { + withdrawHistory[i] = exchange.WithdrawalHistory{ + Status: w[i].Status, + TransferID: strconv.FormatInt(w[i].ID, 10), + Currency: w[i].Coin, + Amount: w[i].Amount, + Fee: w[i].Fee, + CryptoToAddress: w[i].Address, + CryptoTxID: w[i].TxID, + Timestamp: w[i].UpdatedAt, + } + } + return withdrawHistory, nil + default: + return nil, fmt.Errorf("%s %w", a, asset.ErrNotSupported) + } +} + +// GetRecentTrades returns the most recent trades for a currency and asset +func (by *Bybit) GetRecentTrades(ctx context.Context, p currency.Pair, assetType asset.Item) ([]trade.Data, error) { + var resp []trade.Data + + formattedPair, err := by.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + + switch assetType { + case asset.Spot: + tradeData, err := by.GetTrades(ctx, formattedPair.String(), 0) + if err != nil { + return nil, err + } + + for i := range tradeData { + resp = append(resp, trade.Data{ + Exchange: by.Name, + CurrencyPair: p, + AssetType: assetType, + Price: tradeData[i].Price, + Amount: tradeData[i].Volume, + Timestamp: tradeData[i].Time, + }) + } + + case asset.CoinMarginedFutures, asset.Futures: + tradeData, err := by.GetPublicTrades(ctx, formattedPair, 0) + if err != nil { + return nil, err + } + + for i := range tradeData { + resp = append(resp, trade.Data{ + Exchange: by.Name, + CurrencyPair: p, + AssetType: assetType, + Price: tradeData[i].Price, + Amount: tradeData[i].Qty, + Timestamp: tradeData[i].Time, + }) + } + + case asset.USDTMarginedFutures: + tradeData, err := by.GetUSDTPublicTrades(ctx, formattedPair, 0) + if err != nil { + return nil, err + } + + for i := range tradeData { + resp = append(resp, trade.Data{ + Exchange: by.Name, + CurrencyPair: p, + AssetType: assetType, + Price: tradeData[i].Price, + Amount: tradeData[i].Qty, + Timestamp: tradeData[i].Time, + }) + } + + case asset.USDCMarginedFutures: + tradeData, err := by.GetUSDCLatestTrades(ctx, formattedPair, "PERPETUAL", 0) + if err != nil { + return nil, err + } + + for i := range tradeData { + resp = append(resp, trade.Data{ + Exchange: by.Name, + CurrencyPair: p, + AssetType: assetType, + Price: tradeData[i].OrderPrice, + Amount: tradeData[i].OrderQty, + Timestamp: tradeData[i].Timestamp.Time(), + }) + } + + default: + return nil, fmt.Errorf("%s %w", assetType, asset.ErrNotSupported) + } + + if by.IsSaveTradeDataEnabled() { + err := trade.AddTradesToBuffer(by.Name, resp...) + if err != nil { + return nil, err + } + } + + sort.Sort(trade.ByDate(resp)) + return resp, nil +} + +// GetHistoricTrades returns historic trade data within the timeframe provided +func (by *Bybit) GetHistoricTrades(ctx context.Context, p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]trade.Data, error) { + return nil, common.ErrFunctionNotSupported +} + +// SubmitOrder submits a new order +func (by *Bybit) SubmitOrder(ctx context.Context, s *order.Submit) (*order.SubmitResponse, error) { + err := s.Validate() + if err != nil { + return nil, err + } + + formattedPair, err := by.FormatExchangeCurrency(s.Pair, s.AssetType) + if err != nil { + return nil, err + } + + var sideType string + switch s.Side { + case order.Buy: + sideType = sideBuy + case order.Sell: + sideType = sideSell + default: + return nil, errInvalidSide + } + + var orderID string + status := order.New + switch s.AssetType { + case asset.Spot: + timeInForce := BybitRequestParamsTimeGTC + var requestParamsOrderType string + switch s.Type { + case order.Market: + timeInForce = "" + requestParamsOrderType = BybitRequestParamsOrderMarket + case order.Limit: + requestParamsOrderType = BybitRequestParamsOrderLimit + default: + return nil, errUnsupportedOrderType + } + + var orderRequest = PlaceOrderRequest{ + Symbol: formattedPair.String(), + Side: sideType, + Price: s.Price, + Quantity: s.Amount, + TradeType: requestParamsOrderType, + TimeInForce: timeInForce, + OrderLinkID: s.ClientOrderID, + } + var response *PlaceOrderResponse + response, err = by.CreatePostOrder(ctx, &orderRequest) + if err != nil { + return nil, err + } + orderID = response.OrderID + if response.ExecutedQty == response.Quantity { + status = order.Filled + } + case asset.CoinMarginedFutures: + timeInForce := "GoodTillCancel" + var oType string + switch s.Type { + case order.Market: + timeInForce = "" + oType = "Market" + case order.Limit: + oType = "Limit" + default: + return nil, errUnsupportedOrderType + } + var o FuturesOrderDataResp + o, err = by.CreateCoinFuturesOrder(ctx, formattedPair, sideType, oType, timeInForce, + s.ClientOrderID, "", "", + s.Amount, s.Price, 0, 0, false, s.ReduceOnly) + if err != nil { + return nil, err + } + orderID = o.OrderID + case asset.USDTMarginedFutures: + timeInForce := "GoodTillCancel" + var oType string + switch s.Type { + case order.Market: + timeInForce = "" + oType = "Market" + case order.Limit: + oType = "Limit" + default: + return nil, errUnsupportedOrderType + } + var o FuturesOrderDataResp + o, err = by.CreateUSDTFuturesOrder(ctx, formattedPair, sideType, oType, timeInForce, + s.ClientOrderID, "", "", + s.Amount, s.Price, 0, 0, false, s.ReduceOnly) + if err != nil { + return nil, err + } + orderID = o.OrderID + case asset.Futures: + timeInForce := "GoodTillCancel" + var oType string + switch s.Type { + case order.Market: + timeInForce = "" + oType = "Market" + case order.Limit: + oType = "Limit" + default: + return nil, errUnsupportedOrderType + } + var o FuturesOrderDataResp + o, err = by.CreateFuturesOrder(ctx, 0, formattedPair, sideType, oType, timeInForce, + s.ClientOrderID, "", "", + s.Amount, s.Price, 0, 0, false, s.ReduceOnly) + if err != nil { + return nil, err + } + orderID = o.OrderID + case asset.USDCMarginedFutures: + timeInForce := "GoodTillCancel" + var oType string + switch s.Type { + case order.Market: + timeInForce = "" + oType = "Market" + case order.Limit: + oType = "Limit" + default: + return nil, errUnsupportedOrderType + } + var o USDCCreateOrderResp + o, err = by.PlaceUSDCOrder(ctx, formattedPair, oType, "Order", sideType, timeInForce, + s.ClientOrderID, s.Price, s.Amount, 0, 0, 0, 0, s.TriggerPrice, 0, s.ReduceOnly, false, false) + if err != nil { + return nil, err + } + orderID = o.ID + default: + return nil, fmt.Errorf("%s %w", s.AssetType, asset.ErrNotSupported) + } + + resp, err := s.DeriveSubmitResponse(orderID) + if err != nil { + return nil, err + } + resp.Status = status + return resp, nil +} + +// ModifyOrder will allow of changing orderbook placement and limit to +// market conversion +func (by *Bybit) ModifyOrder(ctx context.Context, action *order.Modify) (*order.ModifyResponse, error) { + if err := action.Validate(); err != nil { + return nil, err + } + + var ( + orderID string + err error + ) + switch action.AssetType { + case asset.CoinMarginedFutures: + orderID, err = by.ReplaceActiveCoinFuturesOrders(ctx, action.Pair, action.OrderID, action.ClientOrderID, "", "", int64(action.Amount), action.Price, 0, 0) + case asset.USDTMarginedFutures: + orderID, err = by.ReplaceActiveUSDTFuturesOrders(ctx, action.Pair, action.OrderID, action.ClientOrderID, "", "", int64(action.Amount), action.Price, 0, 0) + case asset.Futures: + orderID, err = by.ReplaceActiveFuturesOrders(ctx, action.Pair, action.OrderID, action.ClientOrderID, "", "", action.Amount, action.Price, 0, 0) + case asset.USDCMarginedFutures: + // TODO: take suggestion related to orderFilter. option accepted by bybit Order/StopOrder + orderID, err = by.ModifyUSDCOrder(ctx, action.Pair, "Order", action.OrderID, action.ClientOrderID, action.Price, action.Amount, 0, 0, 0, 0, 0) + default: + err = fmt.Errorf("%s %w", action.AssetType, asset.ErrNotSupported) + } + if err != nil { + return nil, err + } + + resp, err := action.DeriveModifyResponse() + if err != nil { + return nil, err + } + resp.OrderID = orderID + return resp, nil +} + +// CancelOrder cancels an order by its corresponding ID number +func (by *Bybit) CancelOrder(ctx context.Context, ord *order.Cancel) error { + if err := ord.Validate(ord.StandardCancel()); err != nil { + return err + } + + var err error + switch ord.AssetType { + case asset.Spot: + _, err = by.CancelExistingOrder(ctx, ord.OrderID, ord.ClientOrderID) + case asset.CoinMarginedFutures: + _, err = by.CancelActiveCoinFuturesOrders(ctx, ord.Pair, ord.OrderID, ord.ClientOrderID) + case asset.USDTMarginedFutures: + _, err = by.CancelActiveUSDTFuturesOrders(ctx, ord.Pair, ord.OrderID, ord.ClientOrderID) + case asset.Futures: + _, err = by.CancelActiveFuturesOrders(ctx, ord.Pair, ord.OrderID, ord.ClientOrderID) + case asset.USDCMarginedFutures: + _, err = by.CancelUSDCOrder(ctx, ord.Pair, "Order", ord.OrderID, ord.ClientOrderID) + default: + return fmt.Errorf("%s %w", ord.AssetType, asset.ErrNotSupported) + } + return err +} + +// CancelBatchOrders cancels orders by their corresponding ID numbers +func (by *Bybit) CancelBatchOrders(ctx context.Context, orders []order.Cancel) (order.CancelBatchResponse, error) { + return order.CancelBatchResponse{}, common.ErrNotYetImplemented +} + +// CancelAllOrders cancels all orders associated with a currency pair +func (by *Bybit) CancelAllOrders(ctx context.Context, orderCancellation *order.Cancel) (order.CancelAllResponse, error) { + if err := orderCancellation.Validate(); err != nil { + return order.CancelAllResponse{}, err + } + + status := "success" + var cancelAllOrdersResponse order.CancelAllResponse + cancelAllOrdersResponse.Status = make(map[string]string) + switch orderCancellation.AssetType { + case asset.Spot: + activeOrder, err := by.ListOpenOrders(ctx, orderCancellation.Pair.String(), "", 0) + if err != nil { + return cancelAllOrdersResponse, err + } + + if len(activeOrder) == 0 { // avoid further call if no active order present + break + } + var orderType, side string + if orderCancellation.Type != order.UnknownType { + orderType = orderCancellation.Type.String() + } + if orderCancellation.Side != order.UnknownSide { + side = orderCancellation.Side.Title() + } + + successful, err := by.BatchCancelOrder(ctx, orderCancellation.Pair.String(), side, orderType) + if !successful { + status = "failed" + } + if err != nil { + status = err.Error() + } + for i := range activeOrder { + cancelAllOrdersResponse.Status[activeOrder[i].OrderID] = status + } + + case asset.CoinMarginedFutures: + resp, err := by.CancelAllActiveCoinFuturesOrders(ctx, orderCancellation.Pair) + if err != nil { + status = err.Error() + } + for i := range resp { + cancelAllOrdersResponse.Status[resp[i].OrderID] = status + } + case asset.USDTMarginedFutures: + resp, err := by.CancelAllActiveUSDTFuturesOrders(ctx, orderCancellation.Pair) + if err != nil { + status = err.Error() + } + for i := range resp { + cancelAllOrdersResponse.Status[resp[i]] = status + } + case asset.Futures: + resp, err := by.CancelAllActiveFuturesOrders(ctx, orderCancellation.Pair) + if err != nil { + status = err.Error() + } + for i := range resp { + cancelAllOrdersResponse.Status[resp[i].CancelOrderID] = status + } + case asset.USDCMarginedFutures: + activeOrder, err := by.GetActiveUSDCOrder(ctx, orderCancellation.Pair, "PERPETUAL", "", "", "", "", "", 0) + if err != nil { + return cancelAllOrdersResponse, err + } + + if len(activeOrder) == 0 { // avoid further call if no active order present + break + } + err = by.CancelAllActiveUSDCOrder(ctx, orderCancellation.Pair, "Order") + if err != nil { + status = err.Error() + } + for i := range activeOrder { + cancelAllOrdersResponse.Status[activeOrder[i].ID] = status + } + default: + return cancelAllOrdersResponse, fmt.Errorf("%s %w", orderCancellation.AssetType, asset.ErrNotSupported) + } + return cancelAllOrdersResponse, nil +} + +// GetOrderInfo returns order information based on order ID +func (by *Bybit) GetOrderInfo(ctx context.Context, orderID string, pair currency.Pair, assetType asset.Item) (order.Detail, error) { + switch assetType { + case asset.Spot: + resp, err := by.QueryOrder(ctx, orderID, "") + if err != nil { + return order.Detail{}, err + } + + return order.Detail{ + Amount: resp.Quantity, + Exchange: by.Name, + OrderID: resp.OrderID, + ClientOrderID: resp.OrderLinkID, + Side: getSide(resp.Side), + Type: getTradeType(resp.TradeType), + Pair: pair, + Cost: resp.CummulativeQuoteQty, + AssetType: assetType, + Status: getOrderStatus(resp.Status), + Price: resp.Price, + ExecutedAmount: resp.ExecutedQty, + Date: resp.Time.Time(), + LastUpdated: resp.UpdateTime.Time(), + }, nil + + case asset.CoinMarginedFutures: + resp, err := by.GetActiveRealtimeCoinOrders(ctx, pair, orderID, "") + if err != nil { + return order.Detail{}, err + } + + if len(resp) != 1 { + return order.Detail{}, fmt.Errorf("%w, received %v orders", errExpectedOneOrder, len(resp)) + } + + return order.Detail{ + Amount: resp[0].Qty, + Exchange: by.Name, + OrderID: resp[0].OrderID, + ClientOrderID: resp[0].OrderLinkID, + Side: getSide(resp[0].Side), + Type: getTradeType(resp[0].OrderType), + Pair: pair, + Cost: resp[0].CumulativeQty, + AssetType: assetType, + Status: getOrderStatus(resp[0].OrderStatus), + Price: resp[0].Price, + ExecutedAmount: resp[0].Qty - resp[0].LeavesQty, + Date: resp[0].CreatedAt, + LastUpdated: resp[0].UpdatedAt, + }, nil + + case asset.USDTMarginedFutures: + resp, err := by.GetActiveUSDTRealtimeOrders(ctx, pair, orderID, "") + if err != nil { + return order.Detail{}, err + } + + if len(resp) != 1 { + return order.Detail{}, fmt.Errorf("%w, received %v orders", errExpectedOneOrder, len(resp)) + } + + return order.Detail{ + Amount: resp[0].Qty, + Exchange: by.Name, + OrderID: resp[0].OrderID, + ClientOrderID: resp[0].OrderLinkID, + Side: getSide(resp[0].Side), + Type: getTradeType(resp[0].OrderType), + Pair: pair, + Cost: resp[0].CumulativeQty, + AssetType: assetType, + Status: getOrderStatus(resp[0].OrderStatus), + Price: resp[0].Price, + ExecutedAmount: resp[0].Qty - resp[0].LeavesQty, + Date: resp[0].CreatedAt, + LastUpdated: resp[0].UpdatedAt, + }, nil + + case asset.Futures: + resp, err := by.GetActiveRealtimeOrders(ctx, pair, orderID, "") + if err != nil { + return order.Detail{}, err + } + + if len(resp) != 1 { + return order.Detail{}, fmt.Errorf("%w, received %v orders", errExpectedOneOrder, len(resp)) + } + + return order.Detail{ + Amount: resp[0].Qty, + Exchange: by.Name, + OrderID: resp[0].OrderID, + ClientOrderID: resp[0].OrderLinkID, + Side: getSide(resp[0].Side), + Type: getTradeType(resp[0].OrderType), + Pair: pair, + Cost: resp[0].CumulativeQty, + AssetType: assetType, + Status: getOrderStatus(resp[0].OrderStatus), + Price: resp[0].Price, + ExecutedAmount: resp[0].Qty - resp[0].LeavesQty, + Date: resp[0].CreatedAt, + LastUpdated: resp[0].UpdatedAt, + }, nil + + case asset.USDCMarginedFutures: + resp, err := by.GetActiveUSDCOrder(ctx, pair, "PERPETUAL", orderID, "", "", "", "", 0) + if err != nil { + return order.Detail{}, err + } + + if len(resp) != 1 { + return order.Detail{}, fmt.Errorf("%w, received %v orders", errExpectedOneOrder, len(resp)) + } + + return order.Detail{ + Amount: resp[0].Qty, + Exchange: by.Name, + OrderID: resp[0].ID, + ClientOrderID: resp[0].OrderLinkID, + Side: getSide(resp[0].Side), + Type: getTradeType(resp[0].OrderType), + Pair: pair, + Cost: resp[0].TotalOrderValue, + AssetType: assetType, + Status: getOrderStatus(resp[0].OrderStatus), + Price: resp[0].Price, + ExecutedAmount: resp[0].TotalFilledQty, + Date: resp[0].CreatedAt.Time(), + }, nil + + default: + return order.Detail{}, fmt.Errorf("%s %w", assetType, asset.ErrNotSupported) + } +} + +// GetDepositAddress returns a deposit address for a specified currency +func (by *Bybit) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, _, chain string) (*deposit.Address, error) { + dAddressInfo, err := by.GetDepositAddressForCurrency(ctx, cryptocurrency.String()) + if err != nil { + return nil, err + } + + for x := range dAddressInfo.Chains { + if dAddressInfo.Chains[x].Chain == chain || chain == "" { + return &deposit.Address{ + Address: dAddressInfo.Chains[x].DepositAddress, + Tag: dAddressInfo.Chains[x].DepositTag, + Chain: dAddressInfo.Chains[x].Chain, + }, nil + } + } + return nil, fmt.Errorf("deposit address not found for currency: %s chain: %s", cryptocurrency, chain) +} + +// GetAvailableTransferChains returns the available transfer blockchains for the specific +// cryptocurrency +func (by *Bybit) GetAvailableTransferChains(ctx context.Context, cryptocurrency currency.Code) ([]string, error) { + info, err := by.GetDepositAddressForCurrency(ctx, cryptocurrency.String()) + if err != nil { + return nil, err + } + + availableChains := make([]string, len(info.Chains)) + for x := range info.Chains { + availableChains[x] = info.Chains[x].Chain + } + return availableChains, nil +} + +// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is +// submitted +func (by *Bybit) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) { + if err := withdrawRequest.Validate(); err != nil { + return nil, err + } + amountStr := strconv.FormatFloat(withdrawRequest.Amount, 'f', -1, 64) + wID, err := by.WithdrawFund(ctx, + withdrawRequest.Currency.String(), + withdrawRequest.Crypto.Chain, + withdrawRequest.Crypto.Address, + withdrawRequest.Crypto.AddressTag, + amountStr) + if err != nil { + return nil, err + } + return &withdraw.ExchangeResponse{ + ID: wID, + }, nil +} + +// WithdrawFiatFunds returns a withdrawal ID when a withdrawal is +// submitted +func (by *Bybit) WithdrawFiatFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) { + return nil, common.ErrFunctionNotSupported +} + +// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a withdrawal is +// submitted +func (by *Bybit) WithdrawFiatFundsToInternationalBank(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) { + return nil, common.ErrFunctionNotSupported +} + +// GetActiveOrders retrieves any orders that are active/open +func (by *Bybit) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest) ([]order.Detail, error) { + if err := req.Validate(); err != nil { + return nil, err + } + + if len(req.Pairs) == 0 && req.AssetType != asset.Spot { + return nil, fmt.Errorf("GetActiveOrders: zero pairs found") + } + + if len(req.Pairs) == 0 { + // sending an empty currency pair retrieves data for all currencies + req.Pairs = append(req.Pairs, currency.Pair{}) + } + + var orders []order.Detail + switch req.AssetType { + case asset.Spot: + openOrders, err := by.ListOpenOrders(ctx, "", "", 0) + if err != nil { + return nil, err + } + + for x := range openOrders { + for i := range req.Pairs { + if req.Pairs[i].String() == openOrders[x].SymbolName { + orders = append(orders, order.Detail{ + Amount: openOrders[x].Quantity, + Date: openOrders[x].Time.Time(), + Exchange: by.Name, + OrderID: openOrders[x].OrderID, + ClientOrderID: openOrders[x].OrderLinkID, + Side: getSide(openOrders[x].Side), + Type: getTradeType(openOrders[x].TradeType), + Price: openOrders[x].Price, + Status: getOrderStatus(openOrders[x].Status), + Pair: req.Pairs[i], + AssetType: req.AssetType, + LastUpdated: openOrders[x].UpdateTime.Time(), + }) + } + } + } + case asset.CoinMarginedFutures: + for i := range req.Pairs { + openOrders, err := by.GetActiveCoinFuturesOrders(ctx, req.Pairs[i], "", "", "", 0) + if err != nil { + return nil, err + } + + for x := range openOrders { + orders = append(orders, order.Detail{ + Price: openOrders[x].Price, + Amount: openOrders[x].Qty, + ExecutedAmount: openOrders[x].Qty - openOrders[x].LeavesQty, + RemainingAmount: openOrders[x].LeavesQty, + Fee: openOrders[x].CumulativeFee, + Exchange: by.Name, + OrderID: openOrders[x].OrderID, + ClientOrderID: openOrders[x].OrderLinkID, + Type: getTradeType(openOrders[x].OrderType), + Side: getSide(openOrders[x].Side), + Status: getOrderStatus(openOrders[x].OrderStatus), + Pair: req.Pairs[i], + AssetType: req.AssetType, + Date: openOrders[x].CreatedAt, + }) + } + } + case asset.USDTMarginedFutures: + for i := range req.Pairs { + openOrders, err := by.GetActiveUSDTFuturesOrders(ctx, req.Pairs[i], "", "", "", "", 0, 0) + if err != nil { + return nil, err + } + + for x := range openOrders { + orders = append(orders, order.Detail{ + Price: openOrders[x].Price, + Amount: openOrders[x].Qty, + ExecutedAmount: openOrders[x].Qty - openOrders[x].LeavesQty, + RemainingAmount: openOrders[x].LeaveValue, + Fee: openOrders[x].CumulativeFee, + Exchange: by.Name, + OrderID: openOrders[x].OrderID, + ClientOrderID: openOrders[x].OrderLinkID, + Type: getTradeType(openOrders[x].OrderType), + Side: getSide(openOrders[x].Side), + Status: getOrderStatus(openOrders[x].OrderStatus), + Pair: req.Pairs[i], + AssetType: asset.USDTMarginedFutures, + Date: openOrders[x].CreatedAt, + }) + } + } + case asset.Futures: + for i := range req.Pairs { + openOrders, err := by.GetActiveFuturesOrders(ctx, req.Pairs[i], "", "", "", 0) + if err != nil { + return nil, err + } + + for x := range openOrders { + orders = append(orders, order.Detail{ + Price: openOrders[x].Price, + Amount: openOrders[x].Qty, + ExecutedAmount: openOrders[x].Qty - openOrders[x].LeavesQty, + RemainingAmount: openOrders[x].LeavesQty, + Fee: openOrders[x].CumulativeFee, + Exchange: by.Name, + OrderID: openOrders[x].OrderID, + ClientOrderID: openOrders[x].OrderLinkID, + Type: getTradeType(openOrders[x].OrderType), + Side: getSide(openOrders[x].Side), + Status: getOrderStatus(openOrders[x].OrderStatus), + Pair: req.Pairs[i], + AssetType: req.AssetType, + Date: openOrders[x].CreatedAt, + }) + } + } + case asset.USDCMarginedFutures: + openOrders, err := by.GetActiveUSDCOrder(ctx, currency.EMPTYPAIR, "PERPETUAL", "", "", "", "", "", 0) + if err != nil { + return nil, err + } + + for x := range openOrders { + for i := range req.Pairs { + if req.Pairs[i].String() == openOrders[x].Symbol { + orders = append(orders, order.Detail{ + Price: openOrders[x].Price, + Amount: openOrders[x].Qty, + ExecutedAmount: openOrders[x].TotalFilledQty, + RemainingAmount: openOrders[x].Qty - openOrders[x].TotalFilledQty, + Fee: openOrders[x].TotalFee, + Exchange: by.Name, + OrderID: openOrders[x].ID, + ClientOrderID: openOrders[x].OrderLinkID, + Type: getTradeType(openOrders[x].OrderType), + Side: getSide(openOrders[x].Side), + Status: getOrderStatus(openOrders[x].OrderStatus), + Pair: req.Pairs[i], + AssetType: req.AssetType, + Date: openOrders[x].CreatedAt.Time(), + }) + } + } + } + default: + return orders, fmt.Errorf("%s %w", req.AssetType, asset.ErrNotSupported) + } + order.FilterOrdersByPairs(&orders, req.Pairs) + order.FilterOrdersByType(&orders, req.Type) + order.FilterOrdersBySide(&orders, req.Side) + err := order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", by.Name, err) + } + return orders, nil +} + +// GetOrderHistory retrieves account order information +// Can Limit response to specific order status +func (by *Bybit) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest) ([]order.Detail, error) { + if err := req.Validate(); err != nil { + return nil, err + } + var orders []order.Detail + switch req.AssetType { + case asset.Spot: + resp, err := by.GetPastOrders(ctx, "", req.OrderID, 0, req.StartTime, req.EndTime) + if err != nil { + return nil, err + } + + for i := range resp { + // here, we are not using getSide because in sample response side's are in upper + var side order.Side + side, err = order.StringToOrderSide(resp[i].Side) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", by.Name, err) + } + + var pair currency.Pair + pair, err = currency.NewPairFromString(resp[i].Symbol) + if err != nil { + return nil, err + } + detail := order.Detail{ + Amount: resp[i].Quantity, + ExecutedAmount: resp[i].ExecutedQty, + RemainingAmount: resp[i].Quantity - resp[i].ExecutedQty, + Cost: resp[i].CummulativeQuoteQty, + Date: resp[i].Time.Time(), + LastUpdated: resp[i].UpdateTime.Time(), + Exchange: by.Name, + OrderID: resp[i].OrderID, + Side: side, + Type: getTradeType(resp[i].TradeType), + Price: resp[i].Price, + Pair: pair, + Status: getOrderStatus(resp[i].Status), + } + orders = append(orders, detail) + } + order.FilterOrdersByPairs(&orders, req.Pairs) + case asset.CoinMarginedFutures: + for i := range req.Pairs { + resp, err := by.GetClosedCoinTrades(ctx, req.Pairs[i], "", req.StartTime, req.EndTime, 0, 0) + if err != nil { + return nil, err + } + + for i := range resp { + var pair currency.Pair + pair, err = currency.NewPairFromString(resp[i].Symbol) + if err != nil { + return nil, err + } + detail := order.Detail{ + Amount: resp[i].Qty, + Date: resp[i].CreatedAt.Time(), + Exchange: by.Name, + OrderID: resp[i].OrderID, + Side: getSide(resp[i].OrderSide), + Type: getTradeType(resp[i].OrderType), + Price: resp[i].OrderPrice, + Pair: pair, + Leverage: resp[i].Leverage, + } + orders = append(orders, detail) + } + } + case asset.Futures: + for i := range req.Pairs { + resp, err := by.GetClosedTrades(ctx, req.Pairs[i], "", req.StartTime, req.EndTime, 0, 0) + if err != nil { + return nil, err + } + + for i := range resp { + var pair currency.Pair + pair, err = currency.NewPairFromString(resp[i].Symbol) + if err != nil { + return nil, err + } + detail := order.Detail{ + Amount: resp[i].Qty, + Date: resp[i].CreatedAt.Time(), + Exchange: by.Name, + OrderID: resp[i].OrderID, + Side: getSide(resp[i].OrderSide), + Type: getTradeType(resp[i].OrderType), + Price: resp[i].OrderPrice, + Pair: pair, + Leverage: resp[i].Leverage, + } + orders = append(orders, detail) + } + } + case asset.USDTMarginedFutures: + for i := range req.Pairs { + resp, err := by.GetClosedUSDTTrades(ctx, req.Pairs[i], "", req.StartTime, req.EndTime, 0, 0) + if err != nil { + return nil, err + } + + for i := range resp { + var pair currency.Pair + pair, err = currency.NewPairFromString(resp[i].Symbol) + if err != nil { + return nil, err + } + detail := order.Detail{ + Amount: resp[i].Qty, + Date: resp[i].CreatedAt.Time(), + Exchange: by.Name, + OrderID: resp[i].OrderID, + Side: getSide(resp[i].OrderSide), + Type: getTradeType(resp[i].OrderType), + Price: resp[i].OrderPrice, + Pair: pair, + Leverage: resp[i].Leverage, + } + orders = append(orders, detail) + } + } + case asset.USDCMarginedFutures: + resp, err := by.GetUSDCOrderHistory(ctx, currency.EMPTYPAIR, "PERPETUAL", req.OrderID, "", "", "", "", 0) + if err != nil { + return nil, err + } + + for i := range resp { + var orderType order.Type + orderType, err = order.StringToOrderType(resp[i].OrderType) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", by.Name, err) + } + orderStatus, err := order.StringToOrderStatus(resp[i].OrderStatus) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", by.Name, err) + } + + var pair currency.Pair + pair, err = currency.NewPairFromString(resp[i].Symbol) + if err != nil { + return nil, err + } + detail := order.Detail{ + Amount: resp[i].Qty, + ExecutedAmount: resp[i].TotalFilledQty, + RemainingAmount: resp[i].LeavesQty, + Date: resp[i].CreatedAt.Time(), + LastUpdated: resp[i].UpdatedAt.Time(), + Exchange: by.Name, + OrderID: resp[i].ID, + Side: getSide(resp[i].Side), + Type: orderType, + Price: resp[i].Price, + Pair: pair, + Status: orderStatus, + } + orders = append(orders, detail) + } + order.FilterOrdersByPairs(&orders, req.Pairs) + default: + return orders, fmt.Errorf("%s %w", req.AssetType, asset.ErrNotSupported) + } + + order.FilterOrdersByType(&orders, req.Type) + order.FilterOrdersBySide(&orders, req.Side) + err := order.FilterOrdersByTimeRange(&orders, req.StartTime, req.EndTime) + if err != nil { + log.Errorf(log.ExchangeSys, "%s %v", by.Name, err) + } + return orders, nil +} + +// GetFeeByType returns an estimate of fee based on the type of transaction +func (by *Bybit) GetFeeByType(ctx context.Context, feeBuilder *exchange.FeeBuilder) (float64, error) { + return 0, common.ErrNotYetImplemented +} + +// ValidateCredentials validates current credentials used for wrapper +func (by *Bybit) ValidateCredentials(ctx context.Context, assetType asset.Item) error { + _, err := by.UpdateAccountInfo(ctx, assetType) + return by.CheckTransientError(err) +} + +// FormatExchangeKlineInterval returns Interval to exchange formatted string +func (by *Bybit) FormatExchangeKlineInterval(ctx context.Context, interval kline.Interval) string { + switch interval { + case kline.OneMin: + return "1m" + case kline.ThreeMin: + return "3m" + case kline.FiveMin: + return "5m" + case kline.FifteenMin: + return "15m" + case kline.ThirtyMin: + return "30m" + case kline.OneHour: + return "1h" + case kline.TwoHour: + return "2h" + case kline.FourHour: + return "4h" + case kline.SixHour: + return "4h" + case kline.TwelveHour: + return "12h" + case kline.OneDay: + return "1d" + case kline.OneWeek: + return "1w" + case kline.OneMonth: + return "1M" + default: + return interval.Short() + } +} + +// FormatExchangeKlineIntervalFutures returns Interval to exchange formatted string for future assets +func (by *Bybit) FormatExchangeKlineIntervalFutures(ctx context.Context, interval kline.Interval) string { + switch interval { + case kline.OneMin: + return "1" + case kline.ThreeMin: + return "3" + case kline.FiveMin: + return "5" + case kline.FifteenMin: + return "15" + case kline.ThirtyMin: + return "30" + case kline.OneHour: + return "60" + case kline.TwoHour: + return "120" + case kline.FourHour: + return "240" + case kline.SixHour: + return "360" + case kline.TwelveHour: + return "720" + case kline.OneDay: + return "D" + case kline.OneWeek: + return "W" + case kline.OneMonth: + return "M" + default: + return interval.Short() + } +} + +// GetHistoricCandles returns candles between a time period for a set time interval +func (by *Bybit) GetHistoricCandles(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) { + if err := by.ValidateKline(pair, a, interval); err != nil { + return kline.Item{}, err + } + + klineItem := kline.Item{ + Exchange: by.Name, + Pair: pair, + Asset: a, + Interval: interval, + } + + formattedPair, err := by.FormatExchangeCurrency(pair, a) + if err != nil { + return klineItem, err + } + + switch a { + case asset.Spot: + candles, err := by.GetKlines(ctx, formattedPair.String(), by.FormatExchangeKlineInterval(ctx, interval), int64(by.Features.Enabled.Kline.ResultLimit), start, end) + if err != nil { + return klineItem, err + } + + for x := range candles { + klineItem.Candles = append(klineItem.Candles, kline.Candle{ + Time: candles[x].StartTime, + Open: candles[x].Open, + High: candles[x].High, + Low: candles[x].Low, + Close: candles[x].Close, + Volume: candles[x].Volume, + }) + } + case asset.CoinMarginedFutures, asset.Futures: + candles, err := by.GetFuturesKlineData(ctx, formattedPair, by.FormatExchangeKlineIntervalFutures(ctx, interval), int64(by.Features.Enabled.Kline.ResultLimit), start) + if err != nil { + return klineItem, err + } + + for x := range candles { + klineItem.Candles = append(klineItem.Candles, kline.Candle{ + Time: time.Unix(candles[x].OpenTime, 0), + Open: candles[x].Open, + High: candles[x].High, + Low: candles[x].Low, + Close: candles[x].Close, + Volume: candles[x].Volume, + }) + } + case asset.USDTMarginedFutures: + candles, err := by.GetUSDTFuturesKlineData(ctx, formattedPair, by.FormatExchangeKlineIntervalFutures(ctx, interval), int64(by.Features.Enabled.Kline.ResultLimit), start) + if err != nil { + return klineItem, err + } + + for x := range candles { + klineItem.Candles = append(klineItem.Candles, kline.Candle{ + Time: time.Unix(candles[x].OpenTime, 0), + Open: candles[x].Open, + High: candles[x].High, + Low: candles[x].Low, + Close: candles[x].Close, + Volume: candles[x].Volume, + }) + } + case asset.USDCMarginedFutures: + candles, err := by.GetUSDCKlines(ctx, formattedPair, by.FormatExchangeKlineIntervalFutures(ctx, interval), start, int64(by.Features.Enabled.Kline.ResultLimit)) + if err != nil { + return klineItem, err + } + + for x := range candles { + klineItem.Candles = append(klineItem.Candles, kline.Candle{ + Time: candles[x].OpenTime.Time(), + Open: candles[x].Open, + High: candles[x].High, + Low: candles[x].Low, + Close: candles[x].Close, + Volume: candles[x].Volume, + }) + } + default: + return klineItem, fmt.Errorf("%s %w", a, asset.ErrNotSupported) + } + + klineItem.RemoveOutsideRange(start, end) + klineItem.SortCandlesByTimestamp(false) + return klineItem, nil +} + +// GetHistoricCandlesExtended returns candles between a time period for a set time interval +func (by *Bybit) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) { + if err := by.ValidateKline(pair, a, interval); err != nil { + return kline.Item{}, err + } + + klineItem := kline.Item{ + Exchange: by.Name, + Pair: pair, + Asset: a, + Interval: interval, + } + + formattedPair, err := by.FormatExchangeCurrency(pair, a) + if err != nil { + return klineItem, err + } + + dates, err := kline.CalculateCandleDateRanges(start, end, interval, by.Features.Enabled.Kline.ResultLimit) + if err != nil { + return kline.Item{}, err + } + + for x := range dates.Ranges { + switch a { + case asset.Spot: + candles, err := by.GetKlines(ctx, formattedPair.String(), by.FormatExchangeKlineInterval(ctx, interval), int64(by.Features.Enabled.Kline.ResultLimit), dates.Ranges[x].Start.Time, dates.Ranges[x].End.Time) + if err != nil { + return kline.Item{}, err + } + + for i := range candles { + for j := range klineItem.Candles { + if klineItem.Candles[j].Time.Equal(candles[i].StartTime) { + continue + } + } + klineItem.Candles = append(klineItem.Candles, kline.Candle{ + Time: candles[i].StartTime, + Open: candles[i].Open, + High: candles[i].High, + Low: candles[i].Low, + Close: candles[i].Close, + Volume: candles[i].Volume, + }) + } + case asset.CoinMarginedFutures, asset.Futures: + candles, err := by.GetFuturesKlineData(ctx, formattedPair, by.FormatExchangeKlineIntervalFutures(ctx, interval), int64(by.Features.Enabled.Kline.ResultLimit), dates.Ranges[x].Start.Time) + if err != nil { + return kline.Item{}, err + } + + for i := range candles { + for j := range klineItem.Candles { + if klineItem.Candles[j].Time.Equal(time.Unix(candles[i].OpenTime, 0)) { + continue + } + } + klineItem.Candles = append(klineItem.Candles, kline.Candle{ + Time: time.Unix(candles[i].OpenTime, 0), + Open: candles[i].Open, + High: candles[i].High, + Low: candles[i].Low, + Close: candles[i].Close, + Volume: candles[i].Volume, + }) + } + case asset.USDTMarginedFutures: + candles, err := by.GetUSDTFuturesKlineData(ctx, formattedPair, by.FormatExchangeKlineIntervalFutures(ctx, interval), int64(by.Features.Enabled.Kline.ResultLimit), dates.Ranges[x].Start.Time) + if err != nil { + return kline.Item{}, err + } + + for i := range candles { + for j := range klineItem.Candles { + if klineItem.Candles[j].Time.Equal(time.Unix(candles[i].OpenTime, 0)) { + continue + } + } + klineItem.Candles = append(klineItem.Candles, kline.Candle{ + Time: time.Unix(candles[i].OpenTime, 0), + Open: candles[i].Open, + High: candles[i].High, + Low: candles[i].Low, + Close: candles[i].Close, + Volume: candles[i].Volume, + }) + } + case asset.USDCMarginedFutures: + candles, err := by.GetUSDCKlines(ctx, formattedPair, by.FormatExchangeKlineIntervalFutures(ctx, interval), dates.Ranges[x].Start.Time, int64(by.Features.Enabled.Kline.ResultLimit)) + if err != nil { + return klineItem, err + } + + for i := range candles { + for j := range klineItem.Candles { + if klineItem.Candles[j].Time.Equal(candles[i].OpenTime.Time()) { + continue + } + } + klineItem.Candles = append(klineItem.Candles, kline.Candle{ + Time: candles[x].OpenTime.Time(), + Open: candles[x].Open, + High: candles[x].High, + Low: candles[x].Low, + Close: candles[x].Close, + Volume: candles[x].Volume, + }) + } + default: + return kline.Item{}, fmt.Errorf("%s %w", a, asset.ErrNotSupported) + } + } + + dates.SetHasDataFromCandles(klineItem.Candles) + summary := dates.DataSummary(false) + if len(summary) > 0 { + log.Warnf(log.ExchangeSys, "%v - %v", by.Name, summary) + } + klineItem.RemoveDuplicates() + klineItem.RemoveOutsideRange(start, end) + klineItem.SortCandlesByTimestamp(false) + return klineItem, nil +} + +// GetServerTime returns the current exchange server time. +func (by *Bybit) GetServerTime(ctx context.Context, a asset.Item) (time.Time, error) { + switch a { + case asset.Spot: + info, err := by.GetSpotServerTime(ctx) + if err != nil { + return time.Time{}, err + } + return info, nil + case asset.CoinMarginedFutures, asset.USDTMarginedFutures, asset.Futures, asset.USDCMarginedFutures: + info, err := by.GetFuturesServerTime(ctx) + if err != nil { + return time.Time{}, err + } + return info, nil + } + return time.Time{}, fmt.Errorf("%s %w", a, asset.ErrNotSupported) +} + +func (by *Bybit) extractCurrencyPair(symbol string, item asset.Item) (currency.Pair, error) { + pairs, err := by.CurrencyPairs.GetPairs(item, true) + if err != nil { + return currency.Pair{}, err + } + return pairs.DeriveFrom(symbol) +} diff --git a/exchanges/bybit/bybit_ws_cfutures.go b/exchanges/bybit/bybit_ws_cfutures.go new file mode 100644 index 00000000..73a752a9 --- /dev/null +++ b/exchanges/bybit/bybit_ws_cfutures.go @@ -0,0 +1,789 @@ +package bybit + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/crypto" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" + "github.com/thrasher-corp/gocryptotrader/exchanges/trade" + "github.com/thrasher-corp/gocryptotrader/log" +) + +const ( + wsCoinMarginedPath = "realtime" + subscribe = "subscribe" + unsubscribe = "unsubscribe" + dot = "." + + // public endpoints + wsOrder25 = "orderBookL2_25" + wsOrder200 = "orderBook_200" + wsTrade = "trade" + wsInsurance = "insurance" + wsInstrument = "instrument_info" + wsCoinMarket = "klineV2" + wsLiquidation = "liquidation" + + wsOperationSnapshot = "snapshot" + wsOperationDelta = "delta" + wsOrderbookActionDelete = "delete" + wsOrderbookActionUpdate = "update" + wsOrderbookActionInsert = "insert" + wsKlineV2 = "klineV2" + + // private endpoints + wsPosition = "position" + wsExecution = "execution" + wsOrder = "order" + wsStopOrder = "stop_order" + wsWallet = "wallet" +) + +var pingRequest = WsFuturesReq{Topic: stream.Ping} + +// WsCoinConnect connects to a CMF websocket feed +func (by *Bybit) WsCoinConnect() error { + if !by.Websocket.IsEnabled() || !by.IsEnabled() { + return errors.New(stream.WebsocketNotEnabled) + } + var dialer websocket.Dialer + err := by.Websocket.Conn.Dial(&dialer, http.Header{}) + if err != nil { + return err + } + + pingMsg, err := json.Marshal(pingRequest) + if err != nil { + return err + } + by.Websocket.Conn.SetupPingHandler(stream.PingHandler{ + Message: pingMsg, + MessageType: websocket.PingMessage, + Delay: bybitWebsocketTimer, + }) + if by.Verbose { + log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", by.Name) + } + + by.Websocket.Wg.Add(1) + go by.wsCoinReadData() + if by.IsWebsocketAuthenticationSupported() { + err = by.WsCoinAuth(context.TODO()) + if err != nil { + by.Websocket.DataHandler <- err + by.Websocket.SetCanUseAuthenticatedEndpoints(false) + } + } + + by.Websocket.Wg.Add(1) + go by.WsDataHandler() + return nil +} + +// WsCoinAuth sends an authentication message to receive auth data +func (by *Bybit) WsCoinAuth(ctx context.Context) error { + creds, err := by.GetCredentials(ctx) + if err != nil { + return err + } + + intNonce := (time.Now().Unix() + 1) * 1000 + strNonce := strconv.FormatInt(intNonce, 10) + hmac, err := crypto.GetHMAC( + crypto.HashSHA256, + []byte("GET/realtime"+strNonce), + []byte(creds.Secret), + ) + if err != nil { + return err + } + sign := crypto.HexEncodeToString(hmac) + req := Authenticate{ + Operation: "auth", + Args: []interface{}{creds.Key, intNonce, sign}, + } + return by.Websocket.Conn.SendJSONMessage(req) +} + +// SubscribeCoin sends a websocket message to receive data from the channel +func (by *Bybit) SubscribeCoin(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToSubscribe { + var sub WsFuturesReq + sub.Topic = subscribe + + sub.Args = append(sub.Args, formatArgs(channelsToSubscribe[i].Channel, channelsToSubscribe[i].Params)) + err := by.Websocket.Conn.SendJSONMessage(sub) + if err != nil { + errs = append(errs, err) + continue + } + by.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) + } + if errs != nil { + return errs + } + return nil +} + +func formatArgs(channel string, params map[string]interface{}) string { + argStr := channel + for x := range params { + argStr += dot + fmt.Sprintf("%v", params[x]) + } + return argStr +} + +// UnsubscribeCoin sends a websocket message to stop receiving data from the channel +func (by *Bybit) UnsubscribeCoin(channelsToUnsubscribe []stream.ChannelSubscription) error { + var errs common.Errors + + for i := range channelsToUnsubscribe { + var unSub WsFuturesReq + unSub.Topic = unsubscribe + + formattedPair, err := by.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, asset.CoinMarginedFutures) + if err != nil { + errs = append(errs, err) + continue + } + unSub.Args = append(unSub.Args, channelsToUnsubscribe[i].Channel+dot+formattedPair.String()) + err = by.Websocket.Conn.SendJSONMessage(unSub) + if err != nil { + errs = append(errs, err) + continue + } + by.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) + } + if errs != nil { + return errs + } + return nil +} + +// wsCoinReadData gets and passes on websocket messages for processing +func (by *Bybit) wsCoinReadData() { + by.Websocket.Wg.Add(1) + defer by.Websocket.Wg.Done() + + for { + select { + case <-by.Websocket.ShutdownC: + return + default: + resp := by.Websocket.Conn.ReadMessage() + if resp.Raw == nil { + return + } + + err := by.wsCoinHandleData(resp.Raw) + if err != nil { + by.Websocket.DataHandler <- err + } + } + } +} + +func (by *Bybit) wsCoinHandleData(respRaw []byte) error { + var multiStreamData map[string]interface{} + err := json.Unmarshal(respRaw, &multiStreamData) + if err != nil { + return err + } + + t, ok := multiStreamData["topic"].(string) + if !ok { + log.Errorf(log.ExchangeSys, "%s Received unhandle message on websocket: %v\n", by.Name, multiStreamData) + return nil + } + + topics := strings.Split(t, dot) + if len(topics) < 1 { + return errors.New(by.Name + " - topic could not be extracted from response") + } + + if wsType, ok := multiStreamData["type"].(string); ok { + switch topics[0] { + case wsOrder25, wsOrder200: + switch wsType { + case wsOperationSnapshot: + var response WsFuturesOrderbook + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + var p currency.Pair + p, err = by.extractCurrencyPair(response.OBData[0].Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + + err = by.processOrderbook(response.OBData, + response.Type, + p, + asset.CoinMarginedFutures) + if err != nil { + return err + } + + case wsOperationDelta: + var response WsCoinDeltaOrderbook + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + if len(response.OBData.Delete) > 0 { + var p currency.Pair + p, err = by.extractCurrencyPair(response.OBData.Delete[0].Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + + err = by.processOrderbook(response.OBData.Delete, + wsOrderbookActionDelete, + p, + asset.CoinMarginedFutures) + if err != nil { + return err + } + } + + if len(response.OBData.Update) > 0 { + var p currency.Pair + p, err = by.extractCurrencyPair(response.OBData.Update[0].Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + + err = by.processOrderbook(response.OBData.Update, + wsOrderbookActionUpdate, + p, + asset.CoinMarginedFutures) + if err != nil { + return err + } + } + + if len(response.OBData.Insert) > 0 { + var p currency.Pair + p, err = by.extractCurrencyPair(response.OBData.Insert[0].Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + + err = by.processOrderbook(response.OBData.Insert, + wsOrderbookActionInsert, + p, + asset.CoinMarginedFutures) + if err != nil { + return err + } + } + default: + by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported orderbook operation"} + } + + case wsTrades: + if !by.IsSaveTradeDataEnabled() { + return nil + } + var response WsFuturesTrade + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + counter := 0 + trades := make([]trade.Data, len(response.TradeData)) + for i := range response.TradeData { + var p currency.Pair + p, err = by.extractCurrencyPair(response.TradeData[0].Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + + var oSide order.Side + oSide, err = order.StringToOrderSide(response.TradeData[i].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + Err: err, + } + } + + trades[counter] = trade.Data{ + TID: response.TradeData[i].ID, + Exchange: by.Name, + CurrencyPair: p, + AssetType: asset.CoinMarginedFutures, + Side: oSide, + Price: response.TradeData[i].Price, + Amount: response.TradeData[i].Size, + Timestamp: response.TradeData[i].Time, + } + counter++ + } + return by.AddTradesToBuffer(trades...) + + case wsKlineV2: + var response WsFuturesKline + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + var p currency.Pair + p, err = by.extractCurrencyPair(topics[len(topics)-1], asset.CoinMarginedFutures) + if err != nil { + return err + } + + for i := range response.KlineData { + by.Websocket.DataHandler <- stream.KlineData{ + Pair: p, + AssetType: asset.CoinMarginedFutures, + Exchange: by.Name, + OpenPrice: response.KlineData[i].Open, + HighPrice: response.KlineData[i].High, + LowPrice: response.KlineData[i].Low, + ClosePrice: response.KlineData[i].Close, + Volume: response.KlineData[i].Volume, + Timestamp: response.KlineData[i].Timestamp.Time(), + } + } + + case wsInsurance: + var response WsInsurance + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + by.Websocket.DataHandler <- response.Data + + case wsInstrument: + if wsType, ok := multiStreamData["type"].(string); ok { + switch wsType { + case wsOperationSnapshot: + var response WsTicker + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + var p currency.Pair + p, err = by.extractCurrencyPair(response.Ticker.Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Ticker.LastPrice, + High: response.Ticker.HighPrice24h, + Low: response.Ticker.LowPrice24h, + Bid: response.Ticker.BidPrice, + Ask: response.Ticker.AskPrice, + Volume: response.Ticker.Volume24h, + Close: response.Ticker.PrevPrice24h, + LastUpdated: response.Ticker.UpdateAt, + AssetType: asset.CoinMarginedFutures, + Pair: p, + } + + case wsOperationDelta: + var response WsDeltaTicker + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + if len(response.Data.Delete) > 0 { + for x := range response.Data.Delete { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data.Delete[x].Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Data.Delete[x].LastPrice, + High: response.Data.Delete[x].HighPrice24h, + Low: response.Data.Delete[x].LowPrice24h, + Bid: response.Data.Delete[x].BidPrice, + Ask: response.Data.Delete[x].AskPrice, + Volume: response.Data.Delete[x].Volume24h, + Close: response.Data.Delete[x].PrevPrice24h, + LastUpdated: response.Data.Delete[x].UpdateAt, + AssetType: asset.CoinMarginedFutures, + Pair: p, + } + } + } + + if len(response.Data.Update) > 0 { + for x := range response.Data.Update { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data.Update[x].Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Data.Update[x].LastPrice, + High: response.Data.Update[x].HighPrice24h, + Low: response.Data.Update[x].LowPrice24h, + Bid: response.Data.Update[x].BidPrice, + Ask: response.Data.Update[x].AskPrice, + Volume: response.Data.Update[x].Volume24h, + Close: response.Data.Update[x].PrevPrice24h, + LastUpdated: response.Data.Update[x].UpdateAt, + AssetType: asset.CoinMarginedFutures, + Pair: p, + } + } + } + + if len(response.Data.Insert) > 0 { + for x := range response.Data.Insert { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data.Insert[x].Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Data.Insert[x].LastPrice, + High: response.Data.Insert[x].HighPrice24h, + Low: response.Data.Insert[x].LowPrice24h, + Bid: response.Data.Insert[x].BidPrice, + Ask: response.Data.Insert[x].AskPrice, + Volume: response.Data.Insert[x].Volume24h, + Close: response.Data.Insert[x].PrevPrice24h, + LastUpdated: response.Data.Insert[x].UpdateAt, + AssetType: asset.CoinMarginedFutures, + Pair: p, + } + } + } + + default: + by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported ticker operation"} + } + } + + case wsLiquidation: + var response WsFuturesLiquidation + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + by.Websocket.DataHandler <- response.Data + + case wsPosition: + var response WsFuturesPosition + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + by.Websocket.DataHandler <- response.Data + + case wsExecution: + var response WsFuturesExecution + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + for i := range response.Data { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data[i].Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + + var oSide order.Side + oSide, err = order.StringToOrderSide(response.Data[i].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[i].OrderID, + Err: err, + } + } + + var oStatus order.Status + oStatus, err = order.StringToOrderStatus(response.Data[i].ExecutionType) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[i].OrderID, + Err: err, + } + } + + by.Websocket.DataHandler <- &order.Detail{ + Exchange: by.Name, + OrderID: response.Data[i].OrderID, + AssetType: asset.CoinMarginedFutures, + Pair: p, + Price: response.Data[i].Price, + Amount: response.Data[i].OrderQty, + Side: oSide, + Status: oStatus, + Trades: []order.TradeHistory{ + { + Price: response.Data[i].Price, + Amount: response.Data[i].OrderQty, + Exchange: by.Name, + Side: oSide, + Timestamp: response.Data[i].Time, + TID: response.Data[i].ExecutionID, + IsMaker: response.Data[i].IsMaker, + }, + }, + } + } + + case wsOrder: + var response WsOrder + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + for x := range response.Data { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + var oSide order.Side + oSide, err = order.StringToOrderSide(response.Data[x].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oType order.Type + oType, err = order.StringToOrderType(response.Data[x].OrderType) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oStatus order.Status + oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + by.Websocket.DataHandler <- &order.Detail{ + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Type: oType, + Side: oSide, + Status: oStatus, + AssetType: asset.CoinMarginedFutures, + Date: response.Data[x].Time, + Pair: p, + Trades: []order.TradeHistory{ + { + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + Side: oSide, + Timestamp: response.Data[x].Time, + }, + }, + } + } + + case wsStopOrder: + var response WsFuturesStopOrder + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + for x := range response.Data { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.CoinMarginedFutures) + if err != nil { + return err + } + var oSide order.Side + oSide, err = order.StringToOrderSide(response.Data[x].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oType order.Type + oType, err = order.StringToOrderType(response.Data[x].OrderType) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oStatus order.Status + oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + by.Websocket.DataHandler <- &order.Detail{ + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + AccountID: strconv.FormatInt(response.Data[x].UserID, 10), + Type: oType, + Side: oSide, + Status: oStatus, + AssetType: asset.CoinMarginedFutures, + Date: response.Data[x].Time, + Pair: p, + Trades: []order.TradeHistory{ + { + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + Side: oSide, + Timestamp: response.Data[x].Time, + }, + }, + } + } + + case wsWallet: + var response WsFuturesWallet + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + by.Websocket.DataHandler <- response.Data + + default: + by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + string(respRaw)} + } + } + return nil +} + +// processOrderbook processes orderbook updates +func (by *Bybit) processOrderbook(data []WsFuturesOrderbookData, action string, p currency.Pair, a asset.Item) error { + if len(data) < 1 { + return errors.New("no orderbook data") + } + + switch action { + case wsOperationSnapshot: + var book orderbook.Base + for i := range data { + item := orderbook.Item{ + Price: data[i].Price, + Amount: data[i].Size, + ID: data[i].ID, + } + switch { + case strings.EqualFold(data[i].Side, sideSell): + book.Asks = append(book.Asks, item) + case strings.EqualFold(data[i].Side, sideBuy): + book.Bids = append(book.Bids, item) + default: + return fmt.Errorf("could not process websocket orderbook update, order side could not be matched for %s", + data[i].Side) + } + } + book.Asset = a + book.Pair = p + book.Exchange = by.Name + book.VerifyOrderbook = by.CanVerifyOrderbook + + err := by.Websocket.Orderbook.LoadSnapshot(&book) + if err != nil { + return fmt.Errorf("process orderbook error - %s", err) + } + default: + updateAction, err := by.GetActionFromString(action) + if err != nil { + return err + } + + var asks, bids []orderbook.Item + for i := range data { + item := orderbook.Item{ + Price: data[i].Price, + Amount: data[i].Size, + ID: data[i].ID, + } + + switch { + case strings.EqualFold(data[i].Side, sideSell): + asks = append(asks, item) + case strings.EqualFold(data[i].Side, sideBuy): + bids = append(bids, item) + default: + return fmt.Errorf("could not process websocket orderbook update, order side could not be matched for %s", + data[i].Side) + } + } + + err = by.Websocket.Orderbook.Update(&orderbook.Update{ + Bids: bids, + Asks: asks, + Pair: p, + Asset: a, + Action: updateAction, + }) + if err != nil { + return err + } + } + return nil +} + +// GetActionFromString matches a string action to an internal action. +func (by *Bybit) GetActionFromString(s string) (orderbook.Action, error) { + switch s { + case wsOrderbookActionUpdate: + return orderbook.Amend, nil + case wsOrderbookActionDelete: + return orderbook.Delete, nil + case wsOrderbookActionInsert: + return orderbook.Insert, nil + } + return 0, fmt.Errorf("%s %w", s, orderbook.ErrInvalidAction) +} diff --git a/exchanges/bybit/bybit_ws_futures.go b/exchanges/bybit/bybit_ws_futures.go new file mode 100644 index 00000000..30b59ec5 --- /dev/null +++ b/exchanges/bybit/bybit_ws_futures.go @@ -0,0 +1,645 @@ +package bybit + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/crypto" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" + "github.com/thrasher-corp/gocryptotrader/exchanges/trade" + "github.com/thrasher-corp/gocryptotrader/log" +) + +const ( + wsFuturesPath = "realtime" +) + +// WsFuturesConnect connects to a Futures websocket feed +func (by *Bybit) WsFuturesConnect() error { + if !by.Websocket.IsEnabled() || !by.IsEnabled() { + return errors.New(stream.WebsocketNotEnabled) + } + var dialer websocket.Dialer + err := by.Websocket.Conn.Dial(&dialer, http.Header{}) + if err != nil { + return err + } + + pingMsg, err := json.Marshal(pingRequest) + if err != nil { + return err + } + by.Websocket.Conn.SetupPingHandler(stream.PingHandler{ + Message: pingMsg, + MessageType: websocket.PingMessage, + Delay: bybitWebsocketTimer, + }) + if by.Verbose { + log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", by.Name) + } + + go by.wsFuturesReadData() + if by.IsWebsocketAuthenticationSupported() { + err = by.WsFuturesAuth(context.TODO()) + if err != nil { + by.Websocket.DataHandler <- err + by.Websocket.SetCanUseAuthenticatedEndpoints(false) + } + } + + return nil +} + +// WsFuturesAuth sends an authentication message to receive auth data +func (by *Bybit) WsFuturesAuth(ctx context.Context) error { + creds, err := by.GetCredentials(ctx) + if err != nil { + return err + } + + intNonce := (time.Now().Unix() + 1) * 1000 + strNonce := strconv.FormatInt(intNonce, 10) + hmac, err := crypto.GetHMAC( + crypto.HashSHA256, + []byte("GET/realtime"+strNonce), + []byte(creds.Secret), + ) + if err != nil { + return err + } + sign := crypto.HexEncodeToString(hmac) + req := Authenticate{ + Operation: "auth", + Args: []interface{}{creds.Key, intNonce, sign}, + } + return by.Websocket.Conn.SendJSONMessage(req) +} + +// SubscribeFutures sends a websocket message to receive data from the channel +func (by *Bybit) SubscribeFutures(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToSubscribe { + var sub WsFuturesReq + sub.Topic = subscribe + + sub.Args = append(sub.Args, formatArgs(channelsToSubscribe[i].Channel, channelsToSubscribe[i].Params)) + err := by.Websocket.Conn.SendJSONMessage(sub) + if err != nil { + errs = append(errs, err) + continue + } + by.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) + } + if errs != nil { + return errs + } + return nil +} + +// UnsubscribeFutures sends a websocket message to stop receiving data from the channel +func (by *Bybit) UnsubscribeFutures(channelsToUnsubscribe []stream.ChannelSubscription) error { + var errs common.Errors + + for i := range channelsToUnsubscribe { + var unSub WsFuturesReq + unSub.Topic = unsubscribe + + formattedPair, err := by.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, asset.Futures) + if err != nil { + errs = append(errs, err) + continue + } + unSub.Args = append(unSub.Args, channelsToUnsubscribe[i].Channel+dot+formattedPair.String()) + err = by.Websocket.Conn.SendJSONMessage(unSub) + if err != nil { + errs = append(errs, err) + continue + } + by.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) + } + if errs != nil { + return errs + } + return nil +} + +// wsFuturesReadData gets and passes on websocket messages for processing +func (by *Bybit) wsFuturesReadData() { + by.Websocket.Wg.Add(1) + defer by.Websocket.Wg.Done() + + for { + select { + case <-by.Websocket.ShutdownC: + return + default: + resp := by.Websocket.Conn.ReadMessage() + if resp.Raw == nil { + return + } + + err := by.wsFuturesHandleData(resp.Raw) + if err != nil { + by.Websocket.DataHandler <- err + } + } + } +} + +func (by *Bybit) wsFuturesHandleData(respRaw []byte) error { + var multiStreamData map[string]interface{} + err := json.Unmarshal(respRaw, &multiStreamData) + if err != nil { + return err + } + + t, ok := multiStreamData["topic"].(string) + if !ok { + log.Errorf(log.ExchangeSys, "%s Received unhandle message on websocket: %v\n", by.Name, multiStreamData) + return nil + } + + topics := strings.Split(t, dot) + if len(topics) < 1 { + return errors.New(by.Name + " - topic could not be extracted from response") + } + + switch topics[0] { + case wsOrder25, wsOrder200: + if wsType, ok := multiStreamData["type"].(string); ok { + switch wsType { + case wsOperationSnapshot: + var response WsFuturesOrderbook + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + var p currency.Pair + p, err = by.extractCurrencyPair(response.OBData[0].Symbol, asset.Futures) + if err != nil { + return err + } + + err = by.processOrderbook(response.OBData, + response.Type, + p, + asset.Futures) + if err != nil { + return err + } + + case wsOperationDelta: + var response WsCoinDeltaOrderbook + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + if len(response.OBData.Delete) > 0 { + var p currency.Pair + p, err = by.extractCurrencyPair(response.OBData.Delete[0].Symbol, asset.Futures) + if err != nil { + return err + } + err = by.processOrderbook(response.OBData.Delete, + wsOrderbookActionDelete, + p, + asset.Futures) + if err != nil { + return err + } + } + + if len(response.OBData.Update) > 0 { + var p currency.Pair + p, err = by.extractCurrencyPair(response.OBData.Update[0].Symbol, asset.Futures) + if err != nil { + return err + } + + err = by.processOrderbook(response.OBData.Update, + wsOrderbookActionUpdate, + p, + asset.Futures) + if err != nil { + return err + } + } + + if len(response.OBData.Insert) > 0 { + var p currency.Pair + p, err = by.extractCurrencyPair(response.OBData.Insert[0].Symbol, asset.Futures) + if err != nil { + return err + } + + err = by.processOrderbook(response.OBData.Insert, + wsOrderbookActionInsert, + p, + asset.Futures) + if err != nil { + return err + } + } + + default: + by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported orderbook operation"} + } + } + + case wsTrades: + if !by.IsSaveTradeDataEnabled() { + return nil + } + var response WsFuturesTrade + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + counter := 0 + trades := make([]trade.Data, len(response.TradeData)) + for i := range response.TradeData { + var p currency.Pair + p, err = by.extractCurrencyPair(response.TradeData[0].Symbol, asset.Futures) + if err != nil { + return err + } + + var oSide order.Side + oSide, err = order.StringToOrderSide(response.TradeData[i].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + Err: err, + } + } + + trades[counter] = trade.Data{ + TID: response.TradeData[i].ID, + Exchange: by.Name, + CurrencyPair: p, + AssetType: asset.Futures, + Side: oSide, + Price: response.TradeData[i].Price, + Amount: response.TradeData[i].Size, + Timestamp: response.TradeData[i].Time, + } + counter++ + } + return by.AddTradesToBuffer(trades...) + + case wsKlineV2: + var response WsFuturesKline + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + var p currency.Pair + p, err = by.extractCurrencyPair(topics[len(topics)-1], asset.Futures) + if err != nil { + return err + } + + for i := range response.KlineData { + by.Websocket.DataHandler <- stream.KlineData{ + Pair: p, + AssetType: asset.Futures, + Exchange: by.Name, + OpenPrice: response.KlineData[i].Open, + HighPrice: response.KlineData[i].High, + LowPrice: response.KlineData[i].Low, + ClosePrice: response.KlineData[i].Close, + Volume: response.KlineData[i].Volume, + Timestamp: response.KlineData[i].Timestamp.Time(), + } + } + + case wsInstrument: + if wsType, ok := multiStreamData["type"].(string); ok { + switch wsType { + case wsOperationSnapshot: + var response WsFuturesTicker + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + var p currency.Pair + p, err = by.extractCurrencyPair(response.Ticker.Symbol, asset.Futures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Ticker.LastPrice, + High: response.Ticker.HighPrice24h, + Low: response.Ticker.LowPrice24h, + Bid: response.Ticker.BidPrice, + Ask: response.Ticker.AskPrice, + Volume: response.Ticker.Volume24h, + Close: response.Ticker.PrevPrice24h, + LastUpdated: response.Ticker.UpdateAt, + AssetType: asset.Futures, + Pair: p, + } + + case wsOperationDelta: + var response WsDeltaFuturesTicker + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + if len(response.Data.Delete) > 0 { + for x := range response.Data.Delete { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data.Delete[x].Symbol, asset.Futures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Data.Delete[x].LastPrice, + High: response.Data.Delete[x].HighPrice24h, + Low: response.Data.Delete[x].LowPrice24h, + Bid: response.Data.Delete[x].BidPrice, + Ask: response.Data.Delete[x].AskPrice, + Volume: response.Data.Delete[x].Volume24h, + Close: response.Data.Delete[x].PrevPrice24h, + LastUpdated: response.Data.Delete[x].UpdateAt, + AssetType: asset.Futures, + Pair: p, + } + } + } + + if len(response.Data.Update) > 0 { + for x := range response.Data.Update { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data.Update[x].Symbol, asset.Futures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Data.Update[x].LastPrice, + High: response.Data.Update[x].HighPrice24h, + Low: response.Data.Update[x].LowPrice24h, + Bid: response.Data.Update[x].BidPrice, + Ask: response.Data.Update[x].AskPrice, + Volume: response.Data.Update[x].Volume24h, + Close: response.Data.Update[x].PrevPrice24h, + LastUpdated: response.Data.Update[x].UpdateAt, + AssetType: asset.Futures, + Pair: p, + } + } + } + + if len(response.Data.Insert) > 0 { + for x := range response.Data.Insert { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data.Insert[x].Symbol, asset.Futures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Data.Insert[x].LastPrice, + High: response.Data.Insert[x].HighPrice24h, + Low: response.Data.Insert[x].LowPrice24h, + Bid: response.Data.Insert[x].BidPrice, + Ask: response.Data.Insert[x].AskPrice, + Volume: response.Data.Insert[x].Volume24h, + Close: response.Data.Insert[x].PrevPrice24h, + LastUpdated: response.Data.Insert[x].UpdateAt, + AssetType: asset.Futures, + Pair: p, + } + } + } + + default: + by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported ticker operation"} + } + } + + case wsInsurance: + var response WsInsurance + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + by.Websocket.DataHandler <- response.Data + + case wsPosition: + var response WsFuturesPosition + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + by.Websocket.DataHandler <- response.Data + + case wsExecution: + var response WsFuturesExecution + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + for i := range response.Data { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data[i].Symbol, asset.Futures) + if err != nil { + return err + } + + var oSide order.Side + oSide, err = order.StringToOrderSide(response.Data[i].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[i].OrderID, + Err: err, + } + } + + var oStatus order.Status + oStatus, err = order.StringToOrderStatus(response.Data[i].ExecutionType) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[i].OrderID, + Err: err, + } + } + + by.Websocket.DataHandler <- &order.Detail{ + Exchange: by.Name, + OrderID: response.Data[i].OrderID, + AssetType: asset.Futures, + Pair: p, + Price: response.Data[i].Price, + Amount: response.Data[i].OrderQty, + Side: oSide, + Status: oStatus, + Trades: []order.TradeHistory{ + { + Price: response.Data[i].Price, + Amount: response.Data[i].OrderQty, + Exchange: by.Name, + Side: oSide, + Timestamp: response.Data[i].Time, + }, + }, + } + } + + case wsOrder: + var response WsOrder + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + for x := range response.Data { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.Futures) + if err != nil { + return err + } + var oSide order.Side + oSide, err = order.StringToOrderSide(response.Data[x].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oType order.Type + oType, err = order.StringToOrderType(response.Data[x].OrderType) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oStatus order.Status + oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + by.Websocket.DataHandler <- &order.Detail{ + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Type: oType, + Side: oSide, + Status: oStatus, + AssetType: asset.Futures, + Date: response.Data[x].Time, + Pair: p, + Trades: []order.TradeHistory{ + { + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + Side: oSide, + Timestamp: response.Data[x].Time, + }, + }, + } + } + + case wsStopOrder: + var response WsFuturesStopOrder + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + for x := range response.Data { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.Futures) + if err != nil { + return err + } + var oSide order.Side + oSide, err = order.StringToOrderSide(response.Data[x].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oType order.Type + oType, err = order.StringToOrderType(response.Data[x].OrderType) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oStatus order.Status + oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + by.Websocket.DataHandler <- &order.Detail{ + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + AccountID: strconv.FormatInt(response.Data[x].UserID, 10), + Type: oType, + Side: oSide, + Status: oStatus, + AssetType: asset.Futures, + Date: response.Data[x].Time, + Pair: p, + Trades: []order.TradeHistory{ + { + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + Side: oSide, + Timestamp: response.Data[x].Time, + }, + }, + } + } + + default: + by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + string(respRaw)} + } + + return nil +} diff --git a/exchanges/bybit/bybit_ws_ufutures.go b/exchanges/bybit/bybit_ws_ufutures.go new file mode 100644 index 00000000..598c0efc --- /dev/null +++ b/exchanges/bybit/bybit_ws_ufutures.go @@ -0,0 +1,654 @@ +package bybit + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/crypto" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" + "github.com/thrasher-corp/gocryptotrader/exchanges/trade" + "github.com/thrasher-corp/gocryptotrader/log" +) + +const ( + wsUSDTMarginedPathPublic = "realtime_public" + wsUSDTMarginedPathPrivate = "realtime_private" + + wsUSDTKline = "candle" +) + +// WsUSDTConnect connects to a USDT websocket feed +func (by *Bybit) WsUSDTConnect() error { + if !by.Websocket.IsEnabled() || !by.IsEnabled() { + return errors.New(stream.WebsocketNotEnabled) + } + var dialer websocket.Dialer + err := by.Websocket.Conn.Dial(&dialer, http.Header{}) + if err != nil { + return err + } + + pingMsg, err := json.Marshal(pingRequest) + if err != nil { + return err + } + by.Websocket.Conn.SetupPingHandler(stream.PingHandler{ + Message: pingMsg, + MessageType: websocket.PingMessage, + Delay: bybitWebsocketTimer, + }) + if by.Verbose { + log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", by.Name) + } + + go by.wsUSDTReadData() + if by.IsWebsocketAuthenticationSupported() { + err = by.WsUSDTAuth(context.TODO()) + if err != nil { + by.Websocket.DataHandler <- err + by.Websocket.SetCanUseAuthenticatedEndpoints(false) + } + } + + return nil +} + +// WsUSDTAuth sends an authentication message to receive auth data +func (by *Bybit) WsUSDTAuth(ctx context.Context) error { + creds, err := by.GetCredentials(ctx) + if err != nil { + return err + } + + intNonce := (time.Now().Unix() + 1) * 1000 + strNonce := strconv.FormatInt(intNonce, 10) + hmac, err := crypto.GetHMAC( + crypto.HashSHA256, + []byte("GET/realtime"+strNonce), + []byte(creds.Secret), + ) + if err != nil { + return err + } + sign := crypto.HexEncodeToString(hmac) + req := Authenticate{ + Operation: "auth", + Args: []interface{}{creds.Key, intNonce, sign}, + } + return by.Websocket.Conn.SendJSONMessage(req) +} + +// SubscribeUSDT sends a websocket message to receive data from the channel +func (by *Bybit) SubscribeUSDT(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToSubscribe { + var sub WsFuturesReq + sub.Topic = subscribe + + sub.Args = append(sub.Args, formatArgs(channelsToSubscribe[i].Channel, channelsToSubscribe[i].Params)) + err := by.Websocket.Conn.SendJSONMessage(sub) + if err != nil { + errs = append(errs, err) + continue + } + by.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) + } + if errs != nil { + return errs + } + return nil +} + +// UnsubscribeUSDT sends a websocket message to stop receiving data from the channel +func (by *Bybit) UnsubscribeUSDT(channelsToUnsubscribe []stream.ChannelSubscription) error { + var errs common.Errors + + for i := range channelsToUnsubscribe { + var unSub WsFuturesReq + unSub.Topic = unsubscribe + + formattedPair, err := by.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, asset.USDTMarginedFutures) + if err != nil { + errs = append(errs, err) + continue + } + unSub.Args = append(unSub.Args, channelsToUnsubscribe[i].Channel+dot+formattedPair.String()) + err = by.Websocket.Conn.SendJSONMessage(unSub) + if err != nil { + errs = append(errs, err) + continue + } + by.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) + } + if errs != nil { + return errs + } + return nil +} + +// wsUSDTReadData gets and passes on websocket messages for processing +func (by *Bybit) wsUSDTReadData() { + by.Websocket.Wg.Add(1) + defer by.Websocket.Wg.Done() + + for { + select { + case <-by.Websocket.ShutdownC: + return + default: + resp := by.Websocket.Conn.ReadMessage() + if resp.Raw == nil { + return + } + + err := by.wsUSDTHandleData(resp.Raw) + if err != nil { + by.Websocket.DataHandler <- err + } + } + } +} + +func (by *Bybit) wsUSDTHandleData(respRaw []byte) error { + var multiStreamData map[string]interface{} + err := json.Unmarshal(respRaw, &multiStreamData) + if err != nil { + return err + } + + t, ok := multiStreamData["topic"].(string) + if !ok { + log.Errorf(log.ExchangeSys, "%s Received unhandle message on websocket: %v\n", by.Name, multiStreamData) + return nil + } + + topics := strings.Split(t, dot) + if len(topics) < 1 { + return errors.New(by.Name + " - topic could not be extracted from response") + } + + switch topics[0] { + case wsOrder25, wsOrder200: + if wsType, ok := multiStreamData["type"].(string); ok { + switch wsType { + case wsOperationSnapshot: + var response WsUSDTOrderbook + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data.OBData[0].Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + + err = by.processOrderbook(response.Data.OBData, + response.Type, + p, + asset.USDTMarginedFutures) + if err != nil { + return err + } + + case wsOperationDelta: + var response WsCoinDeltaOrderbook + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + if len(response.OBData.Delete) > 0 { + var p currency.Pair + p, err = by.extractCurrencyPair(response.OBData.Delete[0].Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + + err = by.processOrderbook(response.OBData.Delete, + wsOrderbookActionDelete, + p, + asset.USDTMarginedFutures) + if err != nil { + return err + } + } + + if len(response.OBData.Update) > 0 { + var p currency.Pair + p, err = by.extractCurrencyPair(response.OBData.Update[0].Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + + err = by.processOrderbook(response.OBData.Update, + wsOrderbookActionUpdate, + p, + asset.USDTMarginedFutures) + if err != nil { + return err + } + } + + if len(response.OBData.Insert) > 0 { + var p currency.Pair + p, err = by.extractCurrencyPair(response.OBData.Insert[0].Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + + err = by.processOrderbook(response.OBData.Insert, + wsOrderbookActionInsert, + p, + asset.USDTMarginedFutures) + if err != nil { + return err + } + } + default: + by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported orderbook operation"} + } + } + + case wsTrades: + if !by.IsSaveTradeDataEnabled() { + return nil + } + var response WsFuturesTrade + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + trades := make([]trade.Data, len(response.TradeData)) + for i := range response.TradeData { + var p currency.Pair + p, err = by.extractCurrencyPair(response.TradeData[0].Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + + var oSide order.Side + oSide, err = order.StringToOrderSide(response.TradeData[i].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + Err: err, + } + } + + trades[i] = trade.Data{ + TID: response.TradeData[i].ID, + Exchange: by.Name, + CurrencyPair: p, + AssetType: asset.USDTMarginedFutures, + Side: oSide, + Price: response.TradeData[i].Price, + Amount: response.TradeData[i].Size, + Timestamp: response.TradeData[i].Time, + } + } + return by.AddTradesToBuffer(trades...) + + case wsUSDTKline: + var response WsFuturesKline + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + var p currency.Pair + p, err = by.extractCurrencyPair(topics[len(topics)-1], asset.USDTMarginedFutures) + if err != nil { + return err + } + + for i := range response.KlineData { + by.Websocket.DataHandler <- stream.KlineData{ + Pair: p, + AssetType: asset.USDTMarginedFutures, + Exchange: by.Name, + OpenPrice: response.KlineData[i].Open, + HighPrice: response.KlineData[i].High, + LowPrice: response.KlineData[i].Low, + ClosePrice: response.KlineData[i].Close, + Volume: response.KlineData[i].Volume, + Timestamp: response.KlineData[i].Timestamp.Time(), + } + } + + case wsInstrument: + if wsType, ok := multiStreamData["type"].(string); ok { + switch wsType { + case wsOperationSnapshot: + var response WsTicker + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + var p currency.Pair + p, err = by.extractCurrencyPair(response.Ticker.Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Ticker.LastPrice, + High: response.Ticker.HighPrice24h, + Low: response.Ticker.LowPrice24h, + Bid: response.Ticker.BidPrice, + Ask: response.Ticker.AskPrice, + Volume: response.Ticker.Volume24h, + Close: response.Ticker.PrevPrice24h, + LastUpdated: response.Ticker.UpdateAt, + AssetType: asset.USDTMarginedFutures, + Pair: p, + } + + case wsOperationDelta: + var response WsDeltaTicker + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + if len(response.Data.Delete) > 0 { + for x := range response.Data.Delete { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data.Delete[x].Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Data.Delete[x].LastPrice, + High: response.Data.Delete[x].HighPrice24h, + Low: response.Data.Delete[x].LowPrice24h, + Bid: response.Data.Delete[x].BidPrice, + Ask: response.Data.Delete[x].AskPrice, + Volume: response.Data.Delete[x].Volume24h, + Close: response.Data.Delete[x].PrevPrice24h, + LastUpdated: response.Data.Delete[x].UpdateAt, + AssetType: asset.USDTMarginedFutures, + Pair: p, + } + } + } + + if len(response.Data.Update) > 0 { + for x := range response.Data.Update { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data.Update[x].Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Data.Update[x].LastPrice, + High: response.Data.Update[x].HighPrice24h, + Low: response.Data.Update[x].LowPrice24h, + Bid: response.Data.Update[x].BidPrice, + Ask: response.Data.Update[x].AskPrice, + Volume: response.Data.Update[x].Volume24h, + Close: response.Data.Update[x].PrevPrice24h, + LastUpdated: response.Data.Update[x].UpdateAt, + AssetType: asset.USDTMarginedFutures, + Pair: p, + } + } + } + + if len(response.Data.Insert) > 0 { + for x := range response.Data.Insert { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data.Insert[x].Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + + by.Websocket.DataHandler <- &ticker.Price{ + ExchangeName: by.Name, + Last: response.Data.Insert[x].LastPrice, + High: response.Data.Insert[x].HighPrice24h, + Low: response.Data.Insert[x].LowPrice24h, + Bid: response.Data.Insert[x].BidPrice, + Ask: response.Data.Insert[x].AskPrice, + Volume: response.Data.Insert[x].Volume24h, + Close: response.Data.Insert[x].PrevPrice24h, + LastUpdated: response.Data.Insert[x].UpdateAt, + AssetType: asset.USDTMarginedFutures, + Pair: p, + } + } + } + + default: + by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + "unsupported ticker operation"} + } + } + + case wsLiquidation: + var response WsFuturesLiquidation + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + by.Websocket.DataHandler <- response.Data + + case wsPosition: + var response WsFuturesPosition + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + by.Websocket.DataHandler <- response.Data + + case wsExecution: + var response WsFuturesExecution + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + + for i := range response.Data { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data[i].Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + + var oSide order.Side + oSide, err = order.StringToOrderSide(response.Data[i].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[i].OrderID, + Err: err, + } + } + + var oStatus order.Status + oStatus, err = order.StringToOrderStatus(response.Data[i].ExecutionType) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[i].OrderID, + Err: err, + } + } + + by.Websocket.DataHandler <- &order.Detail{ + Exchange: by.Name, + OrderID: response.Data[i].OrderID, + AssetType: asset.USDTMarginedFutures, + Pair: p, + Side: oSide, + Status: oStatus, + Price: response.Data[i].Price, + Amount: response.Data[i].OrderQty, + Trades: []order.TradeHistory{ + { + Price: response.Data[i].Price, + Amount: response.Data[i].OrderQty, + Exchange: by.Name, + Side: oSide, + Timestamp: response.Data[i].Time, + }, + }, + } + } + + case wsOrder: + var response WsOrder + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + for x := range response.Data { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + var oSide order.Side + oSide, err = order.StringToOrderSide(response.Data[x].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oType order.Type + oType, err = order.StringToOrderType(response.Data[x].OrderType) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oStatus order.Status + oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + by.Websocket.DataHandler <- &order.Detail{ + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Type: oType, + Side: oSide, + Status: oStatus, + AssetType: asset.USDTMarginedFutures, + Date: response.Data[x].CreateTime, + Pair: p, + Trades: []order.TradeHistory{ + { + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + Side: oSide, + Timestamp: response.Data[x].Time, + }, + }, + } + } + + case wsStopOrder: + var response WsUSDTFuturesStopOrder + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + for x := range response.Data { + var p currency.Pair + p, err = by.extractCurrencyPair(response.Data[x].Symbol, asset.USDTMarginedFutures) + if err != nil { + return err + } + var oSide order.Side + oSide, err = order.StringToOrderSide(response.Data[x].Side) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oType order.Type + oType, err = order.StringToOrderType(response.Data[x].OrderType) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + var oStatus order.Status + oStatus, err = order.StringToOrderStatus(response.Data[x].OrderStatus) + if err != nil { + by.Websocket.DataHandler <- order.ClassificationError{ + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + Err: err, + } + } + by.Websocket.DataHandler <- &order.Detail{ + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + OrderID: response.Data[x].OrderID, + AccountID: strconv.FormatInt(response.Data[x].UserID, 10), + Type: oType, + Side: oSide, + Status: oStatus, + AssetType: asset.USDTMarginedFutures, + Date: response.Data[x].CreateTime, + Pair: p, + Trades: []order.TradeHistory{ + { + Price: response.Data[x].Price, + Amount: response.Data[x].OrderQty, + Exchange: by.Name, + Side: oSide, + Timestamp: response.Data[x].CreateTime, + }, + }, + } + } + + case wsWallet: + var response WsFuturesWallet + err = json.Unmarshal(respRaw, &response) + if err != nil { + return err + } + by.Websocket.DataHandler <- response.Data + + default: + by.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: by.Name + stream.UnhandledMessage + string(respRaw)} + } + + return nil +} diff --git a/exchanges/bybit/futures_type.go b/exchanges/bybit/futures_type.go new file mode 100644 index 00000000..3f3292b7 --- /dev/null +++ b/exchanges/bybit/futures_type.go @@ -0,0 +1,965 @@ +package bybit + +import "time" + +var ( + validFuturesIntervals = []string{ + "1", "3", "5", "15", "30", "60", "120", "240", "360", "720", + "D", "M", "W", "d", "m", "w", + } + + validFuturesPeriods = []string{ + "5min", "15min", "30min", "1h", "4h", "1d", + } +) + +// OrderbookData stores ob data for cmargined futures +type OrderbookData struct { + Symbol string `json:"symbol"` + Price float64 `json:"price,string"` + Size float64 `json:"size"` + Side string `json:"side"` +} + +// FuturesCandleStick holds kline data +type FuturesCandleStick struct { + ID int64 `json:"id"` + Symbol string `json:"symbol"` + Interval string `json:"interval"` + OpenTime int64 `json:"open_time"` + Open float64 `json:"open"` + High float64 `json:"high"` + Low float64 `json:"low"` + Close float64 `json:"close"` + Volume float64 `json:"volume"` + TurnOver float64 `json:"turnover"` +} + +// FuturesCandleStickWithStringParam holds kline data +type FuturesCandleStickWithStringParam struct { + ID int64 `json:"id"` + Symbol string `json:"symbol"` + Interval string `json:"interval"` + OpenTime int64 `json:"open_time"` + Open float64 `json:"open,string"` + High float64 `json:"high,string"` + Low float64 `json:"low,string"` + Close float64 `json:"close,string"` + Volume float64 `json:"volume,string"` + TurnOver float64 `json:"turnover,string"` +} + +// SymbolPriceTicker stores ticker price stats +type SymbolPriceTicker struct { + Symbol string `json:"symbol"` + BidPrice float64 `json:"bid_price,string"` + AskPrice float64 `json:"ask_price,string"` + LastPrice float64 `json:"last_price,string"` + LastTickDirection string `json:"last_tick_direction"` + Price24hAgo float64 `json:"prev_price_24h,string"` + PricePcntChange24h float64 `json:"price_24h_pcnt,string"` + HighPrice24h float64 `json:"high_price_24h,string"` + LowPrice24h float64 `json:"low_price_24h,string"` + Price1hAgo float64 `json:"prev_price_1h,string"` + PricePcntChange1h float64 `json:"price_1h_pcnt,string"` + MarkPrice float64 `json:"mark_price,string"` + IndexPrice float64 `json:"index_price,string"` + OpenInterest float64 `json:"open_interest"` + OpenValue float64 `json:"open_value,string"` + TotalTurnover float64 `json:"total_turnover,string"` + Turnover24h float64 `json:"turnover_24h,string"` + TotalVolume float64 `json:"total_volume"` + Volume24h float64 `json:"volume_24h"` + FundingRate float64 `json:"funding_rate,string"` + PredictedFundingRate float64 `json:"predicted_funding_rate,string"` + NextFundingTime string `json:"next_funding_time"` + CountdownHour int64 `json:"countdown_hour"` + DeliveryFeeRate string `json:"delivery_fee_rate"` // type is string because it comes as empty string in API response sometime + PredictedDeliveryPrice string `json:"predicted_delivery_price"` // type is string because it comes as empty string in API response sometime + DeliveryTime string `json:"delivery_time"` +} + +// FuturesPublicTradesData stores recent public trades for futures +type FuturesPublicTradesData struct { + Symbol string `json:"symbol"` + Price float64 `json:"price"` + Qty float64 `json:"qty"` + Time time.Time `json:"time"` + Side string `json:"side"` + TimeInMilliSec int64 `json:"trade_time_ms"` +} + +// SymbolInfo stores symbol information for futures pair +type SymbolInfo struct { + Name string `json:"name"` + Alias string `json:"alias"` + Status string `json:"status"` + BaseCurrency string `json:"base_currency"` + QuoteCurrency string `json:"quote_currency"` + PriceScale float64 `json:"price_scale"` + TakerFee string `json:"taker_fee"` + MakerFee string `json:"maker_fee"` + FundingFeeInterval int64 `json:"funding_interval"` + LeverageFilter struct { + MinLeverage float64 `json:"min_leverage"` + MaxLeverage float64 `json:"max_leverage"` + LeverageStep float64 `json:"leverage_step,string"` + } `json:"leverage_filter"` + PriceFilter struct { + MinPrice float64 `json:"min_price,string"` + MaxPrice float64 `json:"max_price,string"` + TickSize float64 `json:"tick_size,string"` + } `json:"price_filter"` + LotSizeFilter struct { + MinTradeQty float64 `json:"min_trading_qty"` + MaxTradeQty float64 `json:"max_trading_qty"` + QtyStep float64 `json:"qty_step"` + } `json:"lot_size_filter"` +} + +// MarkPriceKlineData stores mark price kline data +type MarkPriceKlineData struct { + ID int64 `json:"id"` + Symbol string `json:"symbol"` + Interval string `json:"period"` + StartAt int64 `json:"start_at"` + Open float64 `json:"open"` + High float64 `json:"high"` + Low float64 `json:"low"` + Close float64 `json:"close"` +} + +// IndexPriceKlineData stores index price kline data +type IndexPriceKlineData struct { + Symbol string `json:"symbol"` + Interval string `json:"period"` + StartAt int64 `json:"open_time"` + Open float64 `json:"open,string"` + High float64 `json:"high,string"` + Low float64 `json:"low,string"` + Close float64 `json:"close,string"` +} + +// OpenInterestData stores open interest data +type OpenInterestData struct { + OpenInterest float64 `json:"open_interest"` + Symbol string `json:"symbol"` + Time int64 `json:"time"` +} + +// BigDealData stores big deal data +type BigDealData struct { + Symbol string `json:"symbol"` + Side string `json:"side"` + Time int64 `json:"timestamp"` + Value int64 `json:"value"` +} + +// AccountRatioData stores user accounts long short ratio +type AccountRatioData struct { + Symbol string `json:"symbol"` + BuyRatio float64 `json:"buy_ratio"` + SellRatio float64 `json:"sell_ratio"` + Time int64 `json:"timestamp"` +} + +// BaseFuturesOrder is base future order structure +type BaseFuturesOrder struct { + UserID int64 `json:"user_id"` + Symbol string `json:"symbol"` + Side string `json:"side"` + OrderType string `json:"order_type"` + Price float64 `json:"price"` + Qty float64 `json:"qty"` + TimeInForce string `json:"time_in_force"` +} + +// FuturesOrderData stores futures order data +type FuturesOrderData struct { + BaseFuturesOrder + OrderStatus string `json:"order_status"` + OrderLinkID string `json:"order_link_id"` + OrderID string `json:"order_id"` + LeavesQty float64 `json:"leaves_qty"` + CumulativeQty float64 `json:"cum_exec_qty"` + CumulativeValue float64 `json:"cum_exec_value"` + CumulativeFee float64 `json:"cum_exec_fee"` + RejectReason string `json:"reject_reason"` + CreatedAt time.Time `json:"create_at"` +} + +// FuturesOrderCancelResp stores future order cancel response +type FuturesOrderCancelResp struct { + FuturesOrderData + LastExecutionTime string `json:"last_exec_time"` + LastExecutionPrice float64 `json:"last_exec_price"` + UpdateAt string `json:"updated_at"` +} + +// FuturesOrderDataResp stores future order response +type FuturesOrderDataResp struct { + FuturesOrderCancelResp + TakeProfit float64 `json:"take_profit"` + StopLoss float64 `json:"stop_loss"` + TakeProfitTriggerBy string `json:"tp_trigger_by"` + StopLossTriggerBy string `json:"sl_trigger_by"` +} + +// FuturesActiveOrderData stores future active order data +type FuturesActiveOrderData struct { + FuturesOrderData + LeaveValue float64 `json:"leaves_value"` +} + +// FuturesActiveOrderResp stores future active order response +type FuturesActiveOrderResp struct { + FuturesActiveOrderData + TakeProfit float64 `json:"take_profit"` + StopLoss float64 `json:"stop_loss"` + TakeProfitTriggerBy string `json:"tp_trigger_by"` + StopLossTriggerBy string `json:"sl_trigger_by"` +} + +// FuturesActiveOrder stores future active order +type FuturesActiveOrder struct { + FuturesActiveOrderData + PositionID int64 `json:"position_idx"` + UpdatedAt string `json:"updated_at"` +} + +// FuturesRealtimeOrderData stores futures realtime order data +type FuturesRealtimeOrderData struct { + BaseFuturesOrder + OrderStatus string `json:"order_status"` + OrderLinkID string `json:"order_link_id"` + TakeProfit float64 `json:"take_profit"` + StopLoss float64 `json:"stop_loss"` + TakeProfitTriggerBy string `json:"tp_trigger_by"` + StopLossTriggerBy string `json:"sl_trigger_by"` +} + +// FuturesActiveRealtimeOrder stores future active realtime order +type FuturesActiveRealtimeOrder struct { + FuturesRealtimeOrderData + ExtensionField map[string]interface{} `json:"ext_fields"` + LastExecutionTime string `json:"last_exec_time"` + LastExecutionPrice float64 `json:"last_exec_price"` + LeavesQty float64 `json:"leaves_qty"` + LeaveValue float64 `json:"leaves_value,string"` + CumulativeQty float64 `json:"cum_exec_qty,string"` + CumulativeValue float64 `json:"cum_exec_value,string"` + CumulativeFee float64 `json:"cum_exec_fee,string"` + RejectReason string `json:"reject_reason"` + CancelType string `json:"cancel_type"` + CreatedAt time.Time `json:"create_at"` + UpdatedAt time.Time `json:"updated_at"` + OrderID string `json:"order_id"` +} + +// CoinFuturesConditionalRealtimeOrder stores CMF future coinditional realtime order +type CoinFuturesConditionalRealtimeOrder struct { + FuturesRealtimeOrderData + ExtensionField map[string]interface{} `json:"ext_fields"` + LeavesQty float64 `json:"leaves_qty"` + LeaveValue float64 `json:"leaves_value,string"` + CumulativeQty float64 `json:"cum_exec_qty,string"` + CumulativeValue float64 `json:"cum_exec_value,string"` + CumulativeFee float64 `json:"cum_exec_fee,string"` + RejectReason string `json:"reject_reason"` + CancelType string `json:"cancel_type"` + CreatedAt string `json:"create_at"` + UpdatedAt string `json:"updated_at"` + OrderID string `json:"order_id"` +} + +// FuturesConditionalRealtimeOrder stores future conditional realtime order +type FuturesConditionalRealtimeOrder struct { + CoinFuturesConditionalRealtimeOrder + PositionID int64 `json:"position_idx"` +} + +// USDTFuturesConditionalRealtimeOrder stores USDT future conditional realtime order +type USDTFuturesConditionalRealtimeOrder struct { + FuturesRealtimeOrderData + StopOrderID string `json:"stop_order_id"` + OrderStatus string `json:"order_status"` + TriggerPrice float64 `json:"trigger_price"` + CreatedAt string `json:"created_time"` + UpdatedAt string `json:"updated_time"` + BasePrice float64 `json:"base_price"` + TriggerBy string `json:"trigger_by"` + ReduceOnly bool `json:"reduce_only"` + CloseOnTrigger bool `json:"close_on_trigger"` +} + +// FuturesConditionalOrderData stores futures conditional order data +type FuturesConditionalOrderData struct { + BaseFuturesOrder + TriggerBy string `json:"trigger_by"` + BasePrice float64 `json:"base_price"` + StopOrderID string `json:"stop_order_id"` + OrderLinkID string `json:"order_link_id"` + TakeProfitTriggerBy string `json:"tp_trigger_by"` + StopLossTriggerBy string `json:"sl_trigger_by"` +} + +// FuturesConditionalOrderResp stores futures conditional order response +type FuturesConditionalOrderResp struct { + FuturesConditionalOrderData + Remark string `json:"remark"` + RejectReason string `json:"reject_reason"` + StopPrice float64 `json:"stop_px"` + TakeProfit float64 `json:"take_profit"` + StopLoss float64 `json:"stop_loss"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` +} + +// USDTFuturesConditionalOrderResp stores USDT futures conditional order response +type USDTFuturesConditionalOrderResp struct { + FuturesConditionalOrderData + OrderStatus string `json:"order_status"` + TriggerPrice float64 `json:"trigger_price"` + ReduceOnly bool `json:"reduce_only"` + CloseOnTrigger bool `json:"close_on_trigger"` + CreatedAt string `json:"created_time"` + UpdatedAt string `json:"updated_time"` +} + +// CoinFuturesConditionalOrders stores CMF future conditional order +type CoinFuturesConditionalOrders struct { + FuturesConditionalOrderData + StopOrderStatus string `json:"stop_order_status"` + StopOrderType string `json:"stop_order_type"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + StopPrice float64 `json:"stop_px"` + StopOrderID string `json:"stop_order_id"` + TakeProfit float64 `json:"take_profit"` + StopLoss float64 `json:"stop_loss"` +} + +// FuturesConditionalOrders stores future conditional order +type FuturesConditionalOrders struct { + CoinFuturesConditionalOrders + PositionID int64 `json:"position_idx"` +} + +// USDTFuturesConditionalOrders stores USDT futures conditional order +type USDTFuturesConditionalOrders struct { + FuturesConditionalOrderData + OrderStatus string `json:"order_status"` + TriggerPrice float64 `json:"trigger_price"` + CreatedAt string `json:"created_time"` + UpdatedAt string `json:"updated_time"` + TakeProfit float64 `json:"take_profit"` + StopLoss float64 `json:"stop_loss"` +} + +// FuturesCancelOrderData stores future cancel order data +type FuturesCancelOrderData struct { + CancelOrderID string `json:"clOrdID"` + BaseFuturesOrder + CreateType string `json:"create_type"` + CancelType string `json:"cancel_type"` + OrderStatus string `json:"order_status"` + LeavesQty float64 `json:"leaves_qty"` + LeavesValue float64 `json:"leaves_value"` + CreatedAt string `json:"create_at"` + UpdateAt string `json:"updated_at"` + CrossStatus string `json:"cross_status"` + CrossSeq int64 `json:"cross_seq"` +} + +// FuturesCancelOrderResp stores future cancel order response +type FuturesCancelOrderResp struct { + FuturesCancelOrderData + StopOrderType string `json:"stop_order_type"` + TriggerBy string `json:"trigger_by"` + BasePrice float64 `json:"base_price,string"` + ExpectedDirection string `json:"expected_direction"` +} + +// RiskInfo stores risk information +type RiskInfo struct { + ID int64 `json:"id"` + Symbol string `json:"symbol"` + Limit int64 `json:"limit"` + MaintainMargin float64 `json:"maintain_margin"` + StartingMargin float64 `json:"starting_margin"` + Section []string `json:"section"` + IsLowestRisk int64 `json:"is_lowest_risk"` + CreatedAt string `json:"create_at"` + UpdateAt string `json:"updated_at"` + MaxLeverage float64 `json:"max_leverage"` +} + +// RiskInfoWithStringParam stores risk information where string params +type RiskInfoWithStringParam struct { + ID int64 `json:"id"` + Symbol string `json:"symbol"` + Limit int64 `json:"limit"` + MaintainMargin float64 `json:"maintain_margin,string"` + StartingMargin float64 `json:"starting_margin,string"` + Section []string `json:"section"` + IsLowestRisk int64 `json:"is_lowest_risk"` + CreatedAt string `json:"create_at"` + UpdateAt string `json:"updated_at"` + MaxLeverage float64 `json:"max_leverage,string"` +} + +// FundingInfo stores funding information +type FundingInfo struct { + Symbol string `json:"symbol"` + FundingRate float64 `json:"funding_rate,string"` + FundingRateTimestamp int64 `json:"funding_rate_timestamp"` +} + +// USDTFundingInfo stores USDT funding information +type USDTFundingInfo struct { + Symbol string `json:"symbol"` + FundingRate float64 `json:"funding_rate"` + FundingRateTimestamp string `json:"funding_rate_timestamp"` +} + +// AnnouncementInfo stores announcement information +type AnnouncementInfo struct { + ID int64 `json:"id"` + Title string `json:"title"` + Link string `json:"link"` + Summary string `json:"summary"` + CreatedAt string `json:"created_at"` +} + +// Position stores position +type Position struct { + UserID int64 `json:"user_id"` + Symbol string `json:"symbol"` + Side string `json:"side"` + Size float64 `json:"size"` + PositionValue float64 `json:"position_value"` + EntryPrice float64 `json:"entry_price"` + LiquidationPrice float64 `json:"liq_price"` + BankruptcyPrice float64 `json:"bust_price"` + Leverage float64 `json:"leverage"` + PositionMargin float64 `json:"position_margin"` + OccupiedClosingFee float64 `json:"occ_closing_fee"` + RealisedPNL float64 `json:"realised_pnl"` + AccumulatedRealisedPNL float64 `json:"cum_realised_pnl"` +} + +// PositionWithStringParam stores position with string params +type PositionWithStringParam struct { + UserID int64 `json:"user_id"` + Symbol string `json:"symbol"` + Side string `json:"side"` + Size float64 `json:"size"` + PositionValue float64 `json:"position_value,string"` + EntryPrice float64 `json:"entry_price,string"` + LiquidationPrice float64 `json:"liq_price,string"` + BankruptcyPrice float64 `json:"bust_price,string"` + Leverage float64 `json:"leverage,string"` + PositionMargin float64 `json:"position_margin,string"` + OccupiedClosingFee float64 `json:"occ_closing_fee,string"` + RealisedPNL float64 `json:"realised_pnl,string"` + AccumulatedRealisedPNL float64 `json:"cum_realised_pnl,string"` +} + +// PositionData stores position data +type PositionData struct { + Position + IsIsolated bool `json:"is_isolated"` + AutoAddMargin int64 `json:"auto_add_margin"` + UnrealisedPNL float64 `json:"unrealised_pnl"` + DeleverageIndicator int64 `json:"deleverage_indicator"` + RiskID int64 `json:"risk_id"` + TakeProfit float64 `json:"take_profit"` + StopLoss float64 `json:"stop_loss"` + TrailingStop float64 `json:"trailing_stop"` +} + +// PositionDataWithStringParam stores position data with string params +type PositionDataWithStringParam struct { + PositionWithStringParam + IsIsolated bool `json:"is_isolated"` + AutoAddMargin int64 `json:"auto_add_margin"` + UnrealisedPNL float64 `json:"unrealised_pnl"` + DeleverageIndicator int64 `json:"deleverage_indicator"` + RiskID int64 `json:"risk_id"` + TakeProfit float64 `json:"take_profit,string"` + StopLoss float64 `json:"stop_loss,string"` + TrailingStop float64 `json:"trailing_stop,string"` +} + +// PositionResp stores position response +type PositionResp struct { + PositionDataWithStringParam + PositionID int64 `json:"position_idx"` + Mode int64 `json:"mode"` + ID int64 `json:"id"` + EffectiveLeverage float64 `json:"effective_leverage,string"` + OccupiedFundingFee float64 `json:"occ_funding_fee,string"` + PositionStatus string `json:"position_status"` + CalculatedData string `json:"oc_calc_data"` + OrderMargin float64 `json:"order_margin,string"` + WalletBalance float64 `json:"wallet_balance,string"` + CrossSequence int64 `json:"cross_seq"` + PositionSequence int64 `json:"position_seq"` + TakeProfitStopLossMode string `json:"tp_sl_mode"` + CreatedAt string `json:"created_at"` + UpdateAt string `json:"updated_at"` +} + +// SetTradingAndStopResp stores set trading and stop response +type SetTradingAndStopResp struct { + PositionData + ID int64 `json:"id"` + RiskID int64 `json:"risk_id"` + AutoAddMargin int64 `json:"auto_add_margin"` + OccupiedFundingFee float64 `json:"occ_funding_fee,string"` + TakeProfit float64 `json:"take_profit,string"` + StopLoss float64 `json:"stop_loss,string"` + PositionStatus string `json:"position_status"` + DeleverageIndicator int64 `json:"deleverage_indicator"` + CalculatedData string `json:"oc_calc_data"` + OrderMargin float64 `json:"order_margin,string"` + WalletBalance float64 `json:"wallet_balance,string"` + CrossSequence int64 `json:"cross_seq"` + PositionSequence int64 `json:"position_seq"` + CreatedAt string `json:"created_at"` + UpdateAt string `json:"updated_at"` + ExtensionField map[string]interface{} `json:"ext_fields"` +} + +// USDTPositionResp stores USDT position response +type USDTPositionResp struct { + PositionData + FreeQty float64 `json:"free_qty"` + TakeProfitStopLossMode string `json:"tp_sl_mode"` +} + +// UpdateMarginResp stores update margin response +type UpdateMarginResp struct { + Position + FreeQty float64 `json:"free_qty"` +} + +// TradeData stores trade data +type TradeData struct { + OrderID string `json:"order_id"` + OrderLinkedID string `json:"order_link_id"` + OrderSide string `json:"side"` + Symbol string `json:"symbol"` + ExecutionID string `json:"exec_id"` + OrderPrice float64 `json:"order_price"` + OrderQty float64 `json:"order_qty"` + OrderType string `json:"order_type"` + FeeRate float64 `json:"fee_rate"` + ExecutionFee float64 `json:"exec_fee,string"` + ExecutionPrice float64 `json:"exec_price,string"` + ExecutionQty float64 `json:"exec_qty"` + ExecutionType string `json:"exec_type"` + ExecutionValue float64 `json:"exec_value,string"` + LeavesQty float64 `json:"leaves_qty"` + ClosedSize float64 `json:"closed_size"` + LastLiquidity string `json:"last_liquidity_ind"` + TradeTimeMs int64 `json:"trade_time_ms"` +} + +// TradeResp stores trade response +type TradeResp struct { + TradeData + CrossSequence int64 `json:"cross_seq"` + NthFill int64 `json:"nth_fill"` + UserID int64 `json:"user_id"` +} + +// ClosedTrades stores closed trades +type ClosedTrades struct { + ID int64 `json:"id"` + UserID int64 `json:"user_id"` + Symbol string `json:"symbol"` + OrderID string `json:"order_id"` + OrderSide string `json:"side"` + Qty float64 `json:"qty"` + OrderPrice float64 `json:"order_price"` + OrderType string `json:"order_type"` + ExecutionType string `json:"exec_type"` + ClosedSize float64 `json:"closed_size"` + CumulativeEntryValue float64 `json:"cum_entry_value"` + AvgEntryPrice float64 `json:"avg_entry_price"` + CumulativeExitValue float64 `json:"cum_exit_value"` + AvgEntryValue float64 `json:"avg_exit_price"` + ClosedProfitLoss float64 `json:"closed_pnl"` + FillCount int64 `json:"fill_count"` + Leverage float64 `json:"leverage"` + CreatedAt bybitTimeSec `json:"created_at"` +} + +// FundingFee stores funding fee +type FundingFee struct { + Symbol string `json:"symbol"` + Side string `json:"side"` + Size float64 `json:"size"` + FundingRate float64 `json:"funding_rate"` + ExecutionFee float64 `json:"exec_fee"` + ExecutionTime int64 `json:"exec_timestamp"` +} + +// APIKeyData stores API key data +type APIKeyData struct { + APIKey string `json:"api_key"` + Type string `json:"type"` + UserID int64 `json:"user_id"` + InviterID int64 `json:"inviter_id"` + IPs []string `json:"ips"` + Note string `json:"note"` + Permission []string `json:"permissions"` + CreatedAt string `json:"created_at"` + ExpiredAt string `json:"expired_at"` + ReadOnly bool `json:"read_only"` + VIPLevel string `json:"vip_level"` + MarketMakerLevel string `json:"mkt_maker_level"` +} + +// LCPData stores LiquidityContributionPointsData data +type LCPData struct { + Date string `json:"date"` + SelfRatio float64 `json:"self_ratio"` + PlatformRatio float64 `json:"platform_ratio"` + Score float64 `json:"score"` +} + +// WalletData stores wallet data +type WalletData struct { + Equity float64 `json:"equity"` // equity = wallet_balance + unrealised_pnl + AvailableBalance float64 `json:"available_balance"` + UserMargin float64 `json:"used_margin"` + OrderMargin float64 `json:"order_margin"` + PositionMargin float64 `json:"position_margin"` + PositionClosingFee float64 `json:"occ_closing_fee"` + PositionFundingFee float64 `json:"occ_funding_fee"` + WalletBalance float64 `json:"wallet_balance"` + RealisedPNL float64 `json:"realised_pnl"` + UnrealisedPNL float64 `json:"unrealised_pnl"` + CumulativeRealisedPNL float64 `json:"cum_realised_pnl"` + GivenCash float64 `json:"given_cash"` + ServiceCash float64 `json:"service_cash"` +} + +// FundRecord stores funding records +type FundRecord struct { + ID int64 `json:"id"` + UserID int64 `json:"user_id"` + Coin string `json:"coin"` + Type string `json:"type"` + Amount float64 `json:"amount,string"` + TxID string `json:"tx_id"` + Address string `json:"address"` + WalletBalance float64 `json:"wallet_balance,string"` + ExecutionTime string `json:"exec_time"` + CrossSequence int64 `json:"cross_seq"` +} + +// FundWithdrawalRecord stores funding withdrawal records +type FundWithdrawalRecord struct { + ID int64 `json:"id"` + UserID int64 `json:"user_id"` + Coin string `json:"coin"` + Status string `json:"status"` + Amount float64 `json:"amount,string"` + Fee float64 `json:"fee"` + Address string `json:"address"` + TxID string `json:"tx_id"` + SubmittedAt time.Time `json:"submited_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// AssetExchangeRecord stores asset exchange records +type AssetExchangeRecord struct { + ID int64 `json:"id"` + FromCoin string `json:"from_coin"` + FromAmount float64 `json:"from_amount"` + ToCoin string `json:"to_coin"` + ToAmount float64 `json:"to_amount"` + ExchangeRate float64 `json:"exchange_rate"` + FromFee float64 `json:"from_fee"` + CreatedAt string `json:"created_at"` +} + +// USDCOrderbookData stores orderbook data for USDCMarginedFutures +type USDCOrderbookData struct { + Price float64 `json:"price,string"` + Size float64 `json:"size,string"` + Side string `json:"side"` +} + +// USDCContract stores contract data +type USDCContract struct { + Symbol string `json:"symbol"` + Status string `json:"status"` + BaseCoin string `json:"baseCoin"` + QuoteCoin string `json:"quoteCoin"` + TakerFeeRate float64 `json:"takerFeeRate,string"` + MakerFeeRate float64 `json:"makerFeeRate,string"` + MinLeverage float64 `json:"minLeverage,string"` + MaxLeverage float64 `json:"maxLeverage,string"` + LeverageStep float64 `json:"leverageStep,string"` + MinPrice float64 `json:"minPrice,string"` + MaxPrice float64 `json:"maxPrice,string"` + TickSize float64 `json:"tickSize,string"` + MaxTradingQty float64 `json:"maxTradingQty,string"` + MinTradingQty float64 `json:"minTradingQty,string"` + QtyStep float64 `json:"qtyStep,string"` + DeliveryTime bybitTimeMilliSecStr `json:"deliveryTime"` +} + +// USDCSymbol stores symbol data +type USDCSymbol struct { + Symbol string `json:"symbol"` + NextFundingTime string `json:"nextFundingTime"` + Bid float64 `json:"bid,string"` + BidSize float64 `json:"bidSize,string"` + Ask float64 `json:"ask,string"` + AskSize float64 `json:"askSize,string"` + LastPrice float64 `json:"lastPrice,string"` + OpenInterest float64 `json:"openInterest,string"` + IndexPrice float64 `json:"indexPrice,string"` + MarkPrice float64 `json:"markPrice,string"` + Change24h float64 `json:"change24h,string"` + High24h float64 `json:"high24h,string"` + Low24h float64 `json:"low24h,string"` + Volume24h float64 `json:"volume24h,string"` + Turnover24h float64 `json:"turnover24h,string"` + TotalVolume float64 `json:"totalVolume,string"` + TotalTurnover float64 `json:"totalTurnover,string"` + FundingRate float64 `json:"fundingRate,string"` + PredictedFundingRate float64 `json:"predictedFundingRate,string"` + CountdownHour float64 `json:"countdownHour,string"` + UnderlyingPrice string `json:"underlyingPrice"` +} + +// USDCKlineBase stores Kline Base +type USDCKlineBase struct { + Symbol string `json:"symbol"` + Period string `json:"period"` + OpenTime bybitTimeSecStr `json:"openTime"` + Open float64 `json:"open,string"` + High float64 `json:"high,string"` + Low float64 `json:"low,string"` + Close float64 `json:"close,string"` +} + +// USDCKline stores kline data +type USDCKline struct { + USDCKlineBase + Volume float64 `json:"volume,string"` + Turnover float64 `json:"turnover,string"` +} + +// USDCOpenInterest stores open interest data +type USDCOpenInterest struct { + Symbol string `json:"symbol"` + Timestamp bybitTimeMilliSecStr `json:"timestamp"` + OpenInterest float64 `json:"openInterest,string"` +} + +// USDCLargeOrder stores large order data +type USDCLargeOrder struct { + Symbol string `json:"symbol"` + Side string `json:"side"` + Timestamp bybitTimeMilliSecStr `json:"timestamp"` + Value float64 `json:"value"` +} + +// USDCAccountRatio stores long-short ratio data +type USDCAccountRatio struct { + Symbol string `json:"symbol"` + BuyRatio float64 `json:"buyRatio"` + SellRatio float64 `json:"sellRatio"` + Timestamp bybitTimeMilliSecStr `json:"timestamp"` +} + +// USDCTrade stores trade data +type USDCTrade struct { + ID string `json:"id"` + Symbol string `json:"symbol"` + OrderPrice float64 `json:"orderPrice,string"` + OrderQty float64 `json:"orderQty,string"` + Side string `json:"side"` + Timestamp bybitTimeMilliSecStr `json:"time"` +} + +// USDCCreateOrderResp stores create order response +type USDCCreateOrderResp struct { + ID string `json:"orderId"` + OrderLinkID string `json:"orderLinkId"` + Symbol string `json:"symbol"` + OrderPrice float64 `json:"orderPrice,string"` + OrderQty float64 `json:"orderQty,string"` + OrderType string `json:"orderType"` + Side string `json:"side"` +} + +// USDCOrder store order data +type USDCOrder struct { + ID string `json:"orderId"` + OrderLinkID string `json:"orderLinkId"` + Symbol string `json:"symbol"` + OrderType string `json:"orderType"` + Side string `json:"side"` + Qty float64 `json:"qty,string"` + Price float64 `json:"price,string"` + TimeInForce string `json:"timeInForce"` + TotalOrderValue float64 `json:"cumExecValue,string"` + TotalFilledQty float64 `json:"cumExecQty,string"` + TotalFee float64 `json:"cumExecFee,string"` + InitialMargin string `json:"orderIM"` + OrderStatus string `json:"orderStatus"` + TakeProfit float64 `json:"takeProfit,string"` + StopLoss float64 `json:"stopLoss,string"` + TPTriggerBy string `json:"tpTriggerBy"` + SLTriggerBy string `json:"slTriggerBy"` + LastExecPrice float64 `json:"lastExecPrice"` + BasePrice string `json:"basePrice"` + TriggerPrice float64 `json:"triggerPrice,string"` + TriggerBy string `json:"triggerBy"` + ReduceOnly bool `json:"reduceOnly"` + StopOrderType string `json:"stopOrderType"` + CloseOnTrigger string `json:"closeOnTrigger"` + CreatedAt bybitTimeMilliSecStr `json:"createdAt"` +} + +// USDCOrderHistory stores order history +type USDCOrderHistory struct { + USDCOrder + LeavesQty float64 `json:"leavesQty,string"` // Est. unfilled order qty + CashFlow string `json:"cashFlow"` + RealisedPnl float64 `json:"realisedPnl,string"` + UpdatedAt bybitTimeMilliSecStr `json:"updatedAt"` +} + +// USDCTradeHistory stores trade history +type USDCTradeHistory struct { + ID string `json:"orderId"` + OrderLinkID string `json:"orderLinkId"` + Symbol string `json:"symbol"` + Side string `json:"side"` + TradeID string `json:"tradeId"` + ExecPrice float64 `json:"execPrice,string"` + ExecQty float64 `json:"execQty,string"` + ExecFee float64 `json:"execFee,string"` + FeeRate float64 `json:"feeRate,string"` + ExecType string `json:"execType"` + ExecValue float64 `json:"execValue,string"` + TradeTime bybitTimeMilliSecStr `json:"tradeTime"` + LastLiquidityInd string `json:"lastLiquidityInd"` +} + +// USDCTxLog stores transaction log data +type USDCTxLog struct { + TxTime bybitTimeMilliSecStr `json:"transactionTime"` + Symbol string `json:"symbol"` + Type string `json:"type"` + Side string `json:"side"` + Quantity float64 `json:"qty,string"` + Size float64 `json:"size,string"` + TradePrice float64 `json:"tradePrice,string"` + Funding float64 `json:"funding,string"` + Fee float64 `json:"fee,string"` + CashFlow string `json:"cashFlow"` + Change float64 `json:"change,string"` + WalletBalance float64 `json:"walletBalance,string"` + FeeRate float64 `json:"feeRate,string"` + TradeID string `json:"tradeId"` + OrderID string `json:"orderId"` + OrderLinkID string `json:"orderLinkId"` + Info string `json:"info"` +} + +// USDCWalletBalance store USDC wallet balance +type USDCWalletBalance struct { + Equity float64 `json:"equity,string"` + WalletBalance float64 `json:"walletBalance,string"` + AvailableBalance float64 `json:"availableBalance,string"` + AccountIM float64 `json:"accountIM,string"` + AccountMM float64 `json:"accountMM,string"` + TotalRPL float64 `json:"totalRPL,string"` + TotalSessionUPL float64 `json:"totalSessionUPL,string"` + TotalSessionRPL float64 `json:"totalSessionRPL,string"` +} + +// USDCAssetInfo stores USDC asset data +type USDCAssetInfo struct { + BaseCoin string `json:"baseCoin"` + TotalDelta float64 `json:"totalDelta,string"` + TotalGamma float64 `json:"totalGamma,string"` + TotalVega float64 `json:"totalVega,string"` + TotalTheta float64 `json:"totalTheta,string"` + TotalRPL float64 `json:"totalRPL,string"` + SessionUPL float64 `json:"sessionUPL,string"` + SessionRPL float64 `json:"sessionRPL,string"` + IM float64 `json:"im,string"` + MM float64 `json:"mm,string"` +} + +// USDCPosition store USDC position data +type USDCPosition struct { + Symbol string `json:"symbol"` + Leverage float64 `json:"leverage,string"` + ClosingFee float64 `json:"occClosingFee,string"` + LiquidPrice string `json:"liqPrice"` + Position float64 `json:"positionValue"` + TakeProfit float64 `json:"takeProfit,string"` + RiskID string `json:"riskId"` + TrailingStop float64 `json:"trailingStop,string"` + UnrealisedPnl float64 `json:"unrealisedPnl,string"` + MarkPrice float64 `json:"markPrice,string"` + CumRealisedPnl float64 `json:"cumRealisedPnl,string"` + PositionMM float64 `json:"positionMM,string"` + PositionIM float64 `json:"positionIM,string"` + EntryPrice float64 `json:"entryPrice,string"` + Size float64 `json:"size,string"` + SessionRPL float64 `json:"sessionRPL,string"` + SessionUPL float64 `json:"sessionUPL,string"` + StopLoss float64 `json:"stopLoss,string"` + OrderMargin float64 `json:"orderMargin,string"` + SessionAvgPrice float64 `json:"sessionAvgPrice,string"` + CreatedAt bybitTimeMilliSecStr `json:"createdAt"` + UpdatedAt bybitTimeMilliSecStr `json:"updatedAt"` + TpSLMode string `json:"tpSLMode"` + Side string `json:"side"` + BustPrice string `json:"bustPrice"` + PositionStatus string `json:"positionStatus"` + DeleverageIndicator int64 `json:"deleverageIndicator"` +} + +// USDCSettlementHistory store USDC settlement history data +type USDCSettlementHistory struct { + Symbol string `json:"symbol"` + Side string `json:"side"` + Time bybitTimeMilliSecStr `json:"time"` + Size float64 `json:"size,string"` + SessionAvgPrice float64 `json:"sessionAvgPrice,string"` + MarkPrice float64 `json:"markPrice,string"` + SessionRpl float64 `json:"sessionRpl,string"` +} + +// USDCRiskLimit store USDC risk limit data +type USDCRiskLimit struct { + RiskID string `json:"riskId"` + Symbol string `json:"symbol"` + Limit string `json:"limit"` + Section []string `json:"section"` + StartingMargin float64 `json:"startingMargin,string"` + MaintainMargin float64 `json:"maintainMargin,string"` + IsLowestRisk bool `json:"isLowestRisk"` + MaxLeverage float64 `json:"maxLeverage,string"` +} + +// USDCFundingInfo store USDC funding data +type USDCFundingInfo struct { + Symbol string `json:"symbol"` + Time bybitTimeMilliSecStr `json:"fundingRateTimestamp"` + Rate float64 `json:"fundingRate,string"` +} diff --git a/exchanges/bybit/ratelimit.go b/exchanges/bybit/ratelimit.go new file mode 100644 index 00000000..05cc7f51 --- /dev/null +++ b/exchanges/bybit/ratelimit.go @@ -0,0 +1,403 @@ +package bybit + +import ( + "context" + "fmt" + "time" + + "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "golang.org/x/time/rate" +) + +const ( + spotInterval = time.Second + spotRequestRate = 70 + futuresPublicInterval = time.Second + futuresRequestRate = 50 + + spotPrivateRequestRate = 20 + futuresInterval = time.Minute + futuresDefaultRateCount = 100 + futuresOrderRate = 100 + futuresOrderListRate = 600 + futuresExecutionRate = 120 + futuresPositionRateCount = 75 + futuresPositionListRate = 120 + futuresFundingRate = 120 + futuresWalletRate = 120 + futuresAccountRate = 600 + + usdcPerpetualPublicRate = 50 + usdcPerpetualCancelAllRate = 1 + usdcPerpetualPrivateRate = 5 + usdcPerpetualInterval = time.Second +) + +const ( + publicSpotRate request.EndpointLimit = iota + publicFuturesRate + privateSpotRate + + cFuturesDefaultRate + + cFuturesCancelActiveOrderRate + cFuturesCancelAllActiveOrderRate + cFuturesCreateConditionalOrderRate + cFuturesCancelConditionalOrderRate + cFuturesReplaceActiveOrderRate + cFuturesReplaceConditionalOrderRate + cFuturesCreateOrderRate + cFuturesCancelAllConditionalOrderRate + + cFuturesGetActiveOrderRate + cFuturesGetConditionalOrderRate + cFuturesGetRealtimeOrderRate + + cFuturesTradeRate + + cFuturesSetLeverageRate + cFuturesUpdateMarginRate + cFuturesSetTradingRate + cFuturesSwitchPositionRate + cFuturesGetTradingFeeRate + + cFuturesPositionRate + cFuturesWalletBalanceRate + + cFuturesLastFundingFeeRate + cFuturesPredictFundingRate + + cFuturesWalletFundRecordRate + cFuturesWalletWithdrawalRate + + cFuturesAPIKeyInfoRate + + uFuturesDefaultRate + + uFuturesCreateOrderRate + uFuturesCancelOrderRate + uFuturesCancelAllOrderRate + uFuturesCreateConditionalOrderRate + uFuturesCancelConditionalOrderRate + uFuturesCancelAllConditionalOrderRate + + uFuturesSetLeverageRate + uFuturesSwitchMargin + uFuturesSwitchPosition + uFuturesSetMarginRate + uFuturesSetTradingStopRate + uFuturesUpdateMarginRate + + uFuturesPositionRate + uFuturesGetClosedTradesRate + uFuturesGetTradesRate + + uFuturesGetActiveOrderRate + uFuturesGetActiveRealtimeOrderRate + uFuturesGetConditionalOrderRate + uFuturesGetConditionalRealtimeOrderRate + + uFuturesGetMyLastFundingFeeRate + uFuturesPredictFundingRate + + futuresDefaultRate + + futuresCancelOrderRate + futuresCreateOrderRate + futuresReplaceOrderRate + futuresCancelAllOrderRate + futuresCancelAllConditionalOrderRate + futuresReplaceConditionalOrderRate + futuresCancelConditionalOrderRate + futuresCreateConditionalOrderRate + + futuresGetActiveOrderRate + futuresGetConditionalOrderRate + futuresGetActiveRealtimeOrderRate + futuresGetConditionalRealtimeOrderRate + + futuresGetTradeRate + + futuresSetLeverageRate + futuresUpdateMarginRate + futuresSetTradingStopRate + futuresSwitchPositionModeRate + futuresSwitchMarginRate + futuresSwitchPositionRate + + futuresPositionRate + + usdcPublicRate + + usdcCancelAllOrderRate + + usdcPlaceOrderRate + usdcModifyOrderRate + usdcCancelOrderRate + usdcGetOrderRate + usdcGetOrderHistoryRate + usdcGetTradeHistoryRate + usdcGetTransactionRate + usdcGetWalletRate + usdcGetAssetRate + usdcGetMarginRate + usdcGetPositionRate + usdcSetLeverageRate + usdcGetSettlementRate + usdcSetRiskRate + usdcGetPredictedFundingRate +) + +// RateLimit implements the request.Limiter interface +type RateLimit struct { + SpotRate *rate.Limiter + FuturesRate *rate.Limiter + PrivateSpotRate *rate.Limiter + CMFuturesDefaultRate *rate.Limiter + CMFuturesOrderRate *rate.Limiter + CMFuturesOrderListRate *rate.Limiter + CMFuturesExecutionRate *rate.Limiter + CMFuturesPositionRate *rate.Limiter + CMFuturesPositionListRate *rate.Limiter + CMFuturesFundingRate *rate.Limiter + CMFuturesWalletRate *rate.Limiter + CMFuturesAccountRate *rate.Limiter + UFuturesDefaultRate *rate.Limiter + UFuturesOrderRate *rate.Limiter + UFuturesPositionRate *rate.Limiter + UFuturesPositionListRate *rate.Limiter + UFuturesOrderListRate *rate.Limiter + UFuturesFundingRate *rate.Limiter + FuturesDefaultRate *rate.Limiter + FuturesOrderRate *rate.Limiter + FuturesOrderListRate *rate.Limiter + FuturesExecutionRate *rate.Limiter + FuturesPositionRate *rate.Limiter + FuturesPositionListRate *rate.Limiter + USDCPublic *rate.Limiter + USDCPlaceOrderRate *rate.Limiter + USDCModifyOrderRate *rate.Limiter + USDCCancelOrderRate *rate.Limiter + USDCCancelAllOrderRate *rate.Limiter + USDCGetOrderRate *rate.Limiter + USDCGetOrderHistoryRate *rate.Limiter + USDCGetTradeHistoryRate *rate.Limiter + USDCGetTransactionRate *rate.Limiter + USDCGetWalletRate *rate.Limiter + USDCGetAssetRate *rate.Limiter + USDCGetMarginRate *rate.Limiter + USDCGetPositionRate *rate.Limiter + USDCSetLeverageRate *rate.Limiter + USDCGetSettlementRate *rate.Limiter + USDCSetRiskRate *rate.Limiter + USDCGetPredictedFundingRate *rate.Limiter +} + +// Limit executes rate limiting functionality for Binance +func (r *RateLimit) Limit(ctx context.Context, f request.EndpointLimit) error { + var limiter *rate.Limiter + var tokens int + switch f { + case publicSpotRate: + limiter, tokens = r.SpotRate, 1 + case privateSpotRate: + limiter, tokens = r.PrivateSpotRate, 1 + + case cFuturesDefaultRate: + limiter, tokens = r.CMFuturesDefaultRate, 1 + + case cFuturesCancelActiveOrderRate, cFuturesCreateConditionalOrderRate, cFuturesCancelConditionalOrderRate, cFuturesReplaceActiveOrderRate, + cFuturesReplaceConditionalOrderRate, cFuturesCreateOrderRate: + limiter, tokens = r.CMFuturesOrderRate, 1 + case cFuturesCancelAllActiveOrderRate, cFuturesCancelAllConditionalOrderRate: + limiter, tokens = r.CMFuturesOrderRate, 10 + + case cFuturesGetActiveOrderRate, cFuturesGetConditionalOrderRate, cFuturesGetRealtimeOrderRate: + limiter, tokens = r.CMFuturesOrderListRate, 1 + + case cFuturesTradeRate: + limiter, tokens = r.CMFuturesExecutionRate, 1 + + case cFuturesSetLeverageRate, cFuturesUpdateMarginRate, cFuturesSetTradingRate, cFuturesSwitchPositionRate, cFuturesGetTradingFeeRate: + limiter, tokens = r.CMFuturesPositionRate, 1 + + case cFuturesPositionRate, cFuturesWalletBalanceRate: + limiter, tokens = r.CMFuturesPositionListRate, 1 + + case cFuturesLastFundingFeeRate, cFuturesPredictFundingRate: + limiter, tokens = r.CMFuturesFundingRate, 1 + + case cFuturesWalletFundRecordRate, cFuturesWalletWithdrawalRate: + limiter, tokens = r.CMFuturesWalletRate, 1 + + case cFuturesAPIKeyInfoRate: + limiter, tokens = r.CMFuturesAccountRate, 1 + + case uFuturesDefaultRate: + limiter, tokens = r.UFuturesDefaultRate, 1 + + case uFuturesCreateOrderRate, uFuturesCancelOrderRate, uFuturesCreateConditionalOrderRate, uFuturesCancelConditionalOrderRate: + limiter, tokens = r.UFuturesOrderRate, 1 + + case uFuturesCancelAllOrderRate, uFuturesCancelAllConditionalOrderRate: + limiter, tokens = r.UFuturesOrderRate, 10 + + case uFuturesSetLeverageRate, uFuturesSwitchMargin, uFuturesSwitchPosition, uFuturesSetMarginRate, uFuturesSetTradingStopRate, uFuturesUpdateMarginRate: + limiter, tokens = r.UFuturesPositionRate, 1 + + case uFuturesPositionRate, uFuturesGetClosedTradesRate, uFuturesGetTradesRate: + limiter, tokens = r.UFuturesPositionListRate, 1 + + case uFuturesGetActiveOrderRate, uFuturesGetActiveRealtimeOrderRate, uFuturesGetConditionalOrderRate, uFuturesGetConditionalRealtimeOrderRate: + limiter, tokens = r.UFuturesOrderListRate, 1 + + case uFuturesGetMyLastFundingFeeRate, uFuturesPredictFundingRate: + limiter, tokens = r.UFuturesFundingRate, 1 + + case futuresDefaultRate: + limiter, tokens = r.FuturesDefaultRate, 1 + + case futuresCancelOrderRate, futuresCreateOrderRate, futuresReplaceOrderRate, futuresReplaceConditionalOrderRate, futuresCancelConditionalOrderRate, + futuresCreateConditionalOrderRate: + limiter, tokens = r.FuturesOrderRate, 1 + + case futuresCancelAllOrderRate, futuresCancelAllConditionalOrderRate: + limiter, tokens = r.FuturesOrderRate, 10 + + case futuresGetActiveOrderRate, futuresGetConditionalOrderRate, futuresGetActiveRealtimeOrderRate, futuresGetConditionalRealtimeOrderRate: + limiter, tokens = r.FuturesOrderListRate, 1 + + case futuresGetTradeRate: + limiter, tokens = r.FuturesExecutionRate, 1 + + case futuresSetLeverageRate, futuresUpdateMarginRate, futuresSetTradingStopRate, futuresSwitchPositionModeRate, futuresSwitchMarginRate, futuresSwitchPositionRate: + limiter, tokens = r.FuturesPositionRate, 1 + + case futuresPositionRate: + limiter, tokens = r.FuturesPositionListRate, 1 + + case usdcPublicRate: + limiter, tokens = r.USDCPublic, 1 + + case usdcCancelAllOrderRate: + limiter, tokens = r.USDCCancelAllOrderRate, 1 + + case usdcPlaceOrderRate: + limiter, tokens = r.USDCPlaceOrderRate, 1 + + case usdcModifyOrderRate: + limiter, tokens = r.USDCModifyOrderRate, 1 + + case usdcCancelOrderRate: + limiter, tokens = r.USDCCancelOrderRate, 1 + + case usdcGetOrderRate: + limiter, tokens = r.USDCGetOrderRate, 1 + + case usdcGetOrderHistoryRate: + limiter, tokens = r.USDCGetOrderHistoryRate, 1 + + case usdcGetTradeHistoryRate: + limiter, tokens = r.USDCGetTradeHistoryRate, 1 + + case usdcGetTransactionRate: + limiter, tokens = r.USDCGetTransactionRate, 1 + + case usdcGetWalletRate: + limiter, tokens = r.USDCGetWalletRate, 1 + + case usdcGetAssetRate: + limiter, tokens = r.USDCGetAssetRate, 1 + + case usdcGetMarginRate: + limiter, tokens = r.USDCGetMarginRate, 1 + + case usdcGetPositionRate: + limiter, tokens = r.USDCGetPositionRate, 1 + + case usdcSetLeverageRate: + limiter, tokens = r.USDCSetLeverageRate, 1 + + case usdcGetSettlementRate: + limiter, tokens = r.USDCGetSettlementRate, 1 + + case usdcSetRiskRate: + limiter, tokens = r.USDCSetRiskRate, 1 + + case usdcGetPredictedFundingRate: + limiter, tokens = r.USDCGetPredictedFundingRate, 1 + + default: + limiter, tokens = r.SpotRate, 1 + } + + var finalDelay time.Duration + var reserves = make([]*rate.Reservation, tokens) + for i := 0; i < tokens; i++ { + // Consume tokens 1 at a time as this avoids needing burst capacity in the limiter, + // which would otherwise allow the rate limit to be exceeded over short periods + reserves[i] = limiter.Reserve() + finalDelay = limiter.Reserve().Delay() + } + + if dl, ok := ctx.Deadline(); ok && dl.Before(time.Now().Add(finalDelay)) { + // Cancel all potential reservations to free up rate limiter if deadline + // is exceeded. + for x := range reserves { + reserves[x].Cancel() + } + return fmt.Errorf("rate limit delay of %s will exceed deadline: %w", + finalDelay, + context.DeadlineExceeded) + } + + time.Sleep(finalDelay) + return nil +} + +// SetRateLimit returns the rate limit for the exchange +func SetRateLimit() *RateLimit { + return &RateLimit{ + SpotRate: request.NewRateLimit(spotInterval, spotRequestRate), + FuturesRate: request.NewRateLimit(futuresPublicInterval, futuresRequestRate), + PrivateSpotRate: request.NewRateLimit(spotInterval, spotPrivateRequestRate), + CMFuturesDefaultRate: request.NewRateLimit(futuresInterval, futuresDefaultRateCount), + CMFuturesOrderRate: request.NewRateLimit(futuresInterval, futuresOrderRate), + CMFuturesOrderListRate: request.NewRateLimit(futuresInterval, futuresOrderListRate), + CMFuturesExecutionRate: request.NewRateLimit(futuresInterval, futuresExecutionRate), + CMFuturesPositionRate: request.NewRateLimit(futuresInterval, futuresPositionRateCount), + CMFuturesPositionListRate: request.NewRateLimit(futuresInterval, futuresPositionListRate), + CMFuturesFundingRate: request.NewRateLimit(futuresInterval, futuresFundingRate), + CMFuturesWalletRate: request.NewRateLimit(futuresInterval, futuresWalletRate), + CMFuturesAccountRate: request.NewRateLimit(futuresInterval, futuresAccountRate), + UFuturesDefaultRate: request.NewRateLimit(futuresInterval, futuresDefaultRateCount), + UFuturesOrderRate: request.NewRateLimit(futuresInterval, futuresOrderRate), + UFuturesPositionRate: request.NewRateLimit(futuresInterval, futuresPositionRateCount), + UFuturesPositionListRate: request.NewRateLimit(futuresInterval, futuresPositionListRate), + UFuturesOrderListRate: request.NewRateLimit(futuresInterval, futuresOrderListRate), + UFuturesFundingRate: request.NewRateLimit(futuresInterval, futuresFundingRate), + FuturesDefaultRate: request.NewRateLimit(futuresInterval, futuresDefaultRateCount), + FuturesOrderRate: request.NewRateLimit(futuresInterval, futuresOrderRate), + FuturesOrderListRate: request.NewRateLimit(futuresInterval, futuresOrderListRate), + FuturesExecutionRate: request.NewRateLimit(futuresInterval, futuresExecutionRate), + FuturesPositionRate: request.NewRateLimit(futuresInterval, futuresPositionRateCount), + FuturesPositionListRate: request.NewRateLimit(futuresInterval, futuresPositionListRate), + USDCPublic: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPublicRate), + USDCPlaceOrderRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCModifyOrderRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCCancelOrderRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCCancelAllOrderRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualCancelAllRate), + USDCGetOrderRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCGetOrderHistoryRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCGetTradeHistoryRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCGetTransactionRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCGetWalletRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCGetAssetRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCGetMarginRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCGetPositionRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCSetLeverageRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCGetSettlementRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCSetRiskRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + USDCGetPredictedFundingRate: request.NewRateLimit(usdcPerpetualInterval, usdcPerpetualPrivateRate), + } +} diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index 4f0c46f5..87ee52a9 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -483,7 +483,7 @@ func (c *CoinbasePro) GetFundingHistory(_ context.Context) ([]exchange.FundHisto } // GetWithdrawalsHistory returns previous withdrawals data -func (c *CoinbasePro) GetWithdrawalsHistory(_ context.Context, _ currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (c *CoinbasePro) GetWithdrawalsHistory(_ context.Context, _ currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index 29153421..95d78f54 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -540,7 +540,7 @@ func (c *COINUT) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, } // GetWithdrawalsHistory returns previous withdrawals data -func (c *COINUT) GetWithdrawalsHistory(_ context.Context, _ currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (c *COINUT) GetWithdrawalsHistory(_ context.Context, _ currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index e734456e..ef635508 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -1175,6 +1175,8 @@ func (u URL) String() string { return restCoinMarginedFuturesURL case RestFutures: return restFuturesURL + case RestUSDCMargined: + return restUSDCMarginedFuturesURL case RestSandbox: return restSandboxURL case RestSwap: @@ -1209,6 +1211,8 @@ func getURLTypeFromString(ep string) (URL, error) { return RestCoinMargined, nil case restFuturesURL: return RestFutures, nil + case restUSDCMarginedFuturesURL: + return RestUSDCMargined, nil case restSandboxURL: return RestSandbox, nil case restSwapURL: diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index 6c2ad739..09b4e3e4 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -2008,6 +2008,9 @@ func TestString(t *testing.T) { if RestFutures.String() != "RestFuturesURL" { t.Errorf("invalid string conversion") } + if RestUSDCMargined.String() != "RestUSDCMarginedFuturesURL" { + t.Errorf("invalid string conversion") + } if RestSandbox.String() != "RestSandboxURL" { t.Errorf("invalid string conversion") } @@ -2202,6 +2205,7 @@ func TestGetGetURLTypeFromString(t *testing.T) { {Endpoint: "RestUSDTMarginedFuturesURL", Expected: RestUSDTMargined}, {Endpoint: "RestCoinMarginedFuturesURL", Expected: RestCoinMargined}, {Endpoint: "RestFuturesURL", Expected: RestFutures}, + {Endpoint: "RestUSDCMarginedFuturesURL", Expected: RestUSDCMargined}, {Endpoint: "RestSandboxURL", Expected: RestSandbox}, {Endpoint: "RestSwapURL", Expected: RestSwap}, {Endpoint: "WebsocketSpotURL", Expected: WebsocketSpot}, diff --git a/exchanges/exchange_types.go b/exchanges/exchange_types.go index 2488e153..dafeb05a 100644 --- a/exchanges/exchange_types.go +++ b/exchanges/exchange_types.go @@ -241,6 +241,7 @@ const ( RestUSDTMargined RestCoinMargined RestFutures + RestUSDCMargined RestSwap RestSandbox WebsocketSpot @@ -254,6 +255,7 @@ const ( restSpotSupplementaryURL = "RestSpotSupplementaryURL" restUSDTMarginedFuturesURL = "RestUSDTMarginedFuturesURL" restCoinMarginedFuturesURL = "RestCoinMarginedFuturesURL" + restUSDCMarginedFuturesURL = "RestUSDCMarginedFuturesURL" restFuturesURL = "RestFuturesURL" restSandboxURL = "RestSandboxURL" restSwapURL = "RestSwapURL" @@ -270,6 +272,7 @@ var keyURLs = []URL{RestSpot, RestUSDTMargined, RestCoinMargined, RestFutures, + RestUSDCMargined, RestSwap, RestSandbox, WebsocketSpot, diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index 2bcdf7ff..47e660cc 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -415,7 +415,7 @@ func (e *EXMO) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, e } // GetWithdrawalsHistory returns previous withdrawals data -func (e *EXMO) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (e *EXMO) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index d189b6fe..324e6910 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -571,7 +571,7 @@ func (f *FTX) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, er } // GetWithdrawalsHistory returns previous withdrawals data -func (f *FTX) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (f *FTX) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index 79c9fa89..f633bbf5 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -458,7 +458,7 @@ func (g *Gateio) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, } // GetWithdrawalsHistory returns previous withdrawals data -func (g *Gateio) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (g *Gateio) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index 516038c0..b9fa4367 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -467,7 +467,7 @@ func (g *Gemini) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, } // GetWithdrawalsHistory returns previous withdrawals data -func (g *Gemini) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (g *Gemini) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index e998ebe6..a8d85700 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -486,7 +486,7 @@ func (h *HitBTC) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, } // GetWithdrawalsHistory returns previous withdrawals data -func (h *HitBTC) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (h *HitBTC) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index b3bcfa5d..8ffeb51e 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -838,7 +838,7 @@ func (h *HUOBI) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, } // GetWithdrawalsHistory returns previous withdrawals data -func (h *HUOBI) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (h *HUOBI) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index 8d7cf589..291e5a4f 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -52,7 +52,7 @@ type IBotExchange interface { GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, accountID, chain string) (*deposit.Address, error) GetAvailableTransferChains(ctx context.Context, cryptocurrency currency.Code) ([]string, error) - GetWithdrawalsHistory(ctx context.Context, code currency.Code) ([]WithdrawalHistory, error) + GetWithdrawalsHistory(ctx context.Context, code currency.Code, a asset.Item) ([]WithdrawalHistory, error) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) WithdrawFiatFunds(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) WithdrawFiatFundsToInternationalBank(ctx context.Context, withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index bc636254..91f6abc9 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -334,7 +334,7 @@ func (i *ItBit) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, } // GetWithdrawalsHistory returns previous withdrawals data -func (i *ItBit) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (i *ItBit) GetWithdrawalsHistory(ctx context.Context, c currency.Code, _ asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 67dfa881..73f25708 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -660,7 +660,7 @@ func (k *Kraken) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, } // GetWithdrawalsHistory returns previous withdrawals data -func (k *Kraken) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (k *Kraken) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { withdrawals, err := k.WithdrawStatus(ctx, c, "") for i := range withdrawals { resp = append(resp, exchange.WithdrawalHistory{ diff --git a/exchanges/lbank/lbank_wrapper.go b/exchanges/lbank/lbank_wrapper.go index 659da3c1..c2c9410d 100644 --- a/exchanges/lbank/lbank_wrapper.go +++ b/exchanges/lbank/lbank_wrapper.go @@ -384,7 +384,7 @@ func (l *Lbank) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, } // GetWithdrawalsHistory returns previous withdrawals data -func (l *Lbank) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (l *Lbank) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index 13147c48..a312581e 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -332,7 +332,7 @@ func (l *LocalBitcoins) GetFundingHistory(ctx context.Context) ([]exchange.FundH } // GetWithdrawalsHistory returns previous withdrawals data -func (l *LocalBitcoins) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (l *LocalBitcoins) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go index 795c0430..bdea482f 100644 --- a/exchanges/okgroup/okgroup_wrapper.go +++ b/exchanges/okgroup/okgroup_wrapper.go @@ -500,7 +500,7 @@ func (o *OKGroup) WithdrawFiatFundsToInternationalBank(_ context.Context, _ *wit } // GetWithdrawalsHistory returns previous withdrawals data -func (o *OKGroup) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (o *OKGroup) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index fda750f8..a86e2b2e 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -459,7 +459,7 @@ func (p *Poloniex) GetFundingHistory(ctx context.Context) ([]exchange.FundHistor } // GetWithdrawalsHistory returns previous withdrawals data -func (p *Poloniex) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (p *Poloniex) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/sharedtestvalues/customex.go b/exchanges/sharedtestvalues/customex.go index 438fbf39..017bf6a3 100644 --- a/exchanges/sharedtestvalues/customex.go +++ b/exchanges/sharedtestvalues/customex.go @@ -174,7 +174,7 @@ func (c *CustomEx) GetOrderHistory(ctx context.Context, getOrdersRequest *order. return nil, nil } -func (c *CustomEx) GetWithdrawalsHistory(ctx context.Context, code currency.Code) ([]exchange.WithdrawalHistory, error) { +func (c *CustomEx) GetWithdrawalsHistory(ctx context.Context, code currency.Code, a asset.Item) ([]exchange.WithdrawalHistory, error) { return []exchange.WithdrawalHistory{}, nil } diff --git a/exchanges/support.go b/exchanges/support.go index c5f9abb7..6f30550d 100644 --- a/exchanges/support.go +++ b/exchanges/support.go @@ -23,6 +23,7 @@ var Exchanges = []string{ "bittrex", "btc markets", "btse", + "bybit", "coinbasepro", "coinut", "exmo", diff --git a/exchanges/trade/README.md b/exchanges/trade/README.md index 9f5ccec0..6a359d25 100644 --- a/exchanges/trade/README.md +++ b/exchanges/trade/README.md @@ -69,6 +69,7 @@ _b in this context is an `IBotExchange` implemented struct_ | Bittrex | Yes | Yes | No | | BTCMarkets | Yes | Yes | No | | BTSE | Yes | Yes | No | +| Bybit | Yes | Yes | Yes | | CoinbasePro | Yes | Yes | No| | COINUT | Yes | Yes | No | | Exmo | Yes | NA | No | diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index dd503189..1d3df1d0 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -360,7 +360,7 @@ func (y *Yobit) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, } // GetWithdrawalsHistory returns previous withdrawals data -func (y *Yobit) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (y *Yobit) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index b2c55205..7eeecab5 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -428,7 +428,7 @@ func (z *ZB) GetFundingHistory(ctx context.Context) ([]exchange.FundHistory, err } // GetWithdrawalsHistory returns previous withdrawals data -func (z *ZB) GetWithdrawalsHistory(ctx context.Context, c currency.Code) (resp []exchange.WithdrawalHistory, err error) { +func (z *ZB) GetWithdrawalsHistory(ctx context.Context, c currency.Code, a asset.Item) (resp []exchange.WithdrawalHistory, err error) { return nil, common.ErrNotYetImplemented } diff --git a/gctrpc/buf.lock b/gctrpc/buf.lock index 87b5db40..80ce3428 100644 --- a/gctrpc/buf.lock +++ b/gctrpc/buf.lock @@ -4,8 +4,8 @@ deps: - remote: buf.build owner: googleapis repository: googleapis - commit: 8ab0a452adb64b36ac7a40ae95bd59b2 + commit: 2646de1347094058879360e68cb2ccc6 - remote: buf.build owner: grpc-ecosystem repository: grpc-gateway - commit: febd9e8be39b4f4b878b9c64bcc203fd + commit: 00116f302b12478b85deb33b734e026c diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index a7ef9ec2..244cd43b 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -5690,10 +5690,11 @@ type WithdrawalEventsByExchangeRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Limit int32 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"` - Currency string `protobuf:"bytes,4,opt,name=currency,proto3" json:"currency,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Limit int32 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"` + Currency string `protobuf:"bytes,4,opt,name=currency,proto3" json:"currency,omitempty"` + AssetType string `protobuf:"bytes,5,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` } func (x *WithdrawalEventsByExchangeRequest) Reset() { @@ -5756,6 +5757,13 @@ func (x *WithdrawalEventsByExchangeRequest) GetCurrency() string { return "" } +func (x *WithdrawalEventsByExchangeRequest) GetAssetType() string { + if x != nil { + return x.AssetType + } + return "" +} + type WithdrawalEventsByDateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -13259,7 +13267,7 @@ var file_rpc_proto_rawDesc = []byte{ 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x22, 0x81, 0x01, 0x0a, 0x21, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, + 0x6e, 0x74, 0x22, 0xa0, 0x01, 0x0a, 0x21, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, @@ -13267,1749 +13275,1751 @@ var file_rpc_proto_rawDesc = []byte{ 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x79, 0x0a, 0x1d, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x22, 0x5b, 0x0a, 0x22, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x95, - 0x02, 0x0a, 0x17, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x6c, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x54, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xea, 0x01, 0x0a, - 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x66, 0x69, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x61, 0x74, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x04, 0x66, 0x69, - 0x61, 0x74, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x22, 0xb8, 0x01, 0x0a, 0x13, 0x46, 0x69, - 0x61, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, - 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x73, 0x62, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x62, 0x73, 0x62, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x77, - 0x69, 0x66, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x77, 0x69, 0x66, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x69, 0x62, 0x61, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x69, 0x62, 0x61, 0x6e, 0x22, 0x79, 0x0a, 0x15, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x61, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x74, 0x78, - 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x22, - 0x31, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, - 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x67, - 0x65, 0x72, 0x22, 0x6e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x69, 0x6e, - 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x72, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x12, 0x14, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x22, 0x47, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, - 0x06, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, - 0x6f, 0x67, 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x4b, 0x0a, 0x17, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x79, 0x0a, 0x1d, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0xd8, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x35, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, - 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x1a, 0x5a, 0x0a, 0x14, 0x53, 0x75, 0x70, 0x70, 0x6f, - 0x72, 0x74, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x53, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x97, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x70, 0x61, 0x69, - 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, - 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x80, 0x01, - 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, - 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x22, 0x3f, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, + 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x22, 0x5b, 0x0a, 0x22, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x95, 0x02, + 0x0a, 0x17, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x6c, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x54, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xea, 0x01, 0x0a, 0x16, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x2f, 0x0a, 0x04, 0x66, 0x69, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x61, 0x74, 0x57, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x04, 0x66, 0x69, 0x61, + 0x74, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x52, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x22, 0xb8, 0x01, 0x0a, 0x13, 0x46, 0x69, 0x61, + 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, + 0x0c, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x73, 0x62, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x62, 0x73, 0x62, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x77, 0x69, + 0x66, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x77, 0x69, 0x66, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x69, 0x62, 0x61, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, + 0x62, 0x61, 0x6e, 0x22, 0x79, 0x0a, 0x15, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x54, 0x61, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x74, 0x78, 0x5f, + 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x22, 0x31, + 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x67, + 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x67, 0x65, + 0x72, 0x22, 0x6e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x69, 0x6e, 0x66, + 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x22, 0x47, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, + 0x67, 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x4b, 0x0a, 0x17, 0x47, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x22, 0x7d, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, - 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x22, 0x3c, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x99, - 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, - 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x43, 0x0a, 0x15, 0x47, 0x65, - 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x75, 0x64, - 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, - 0xa4, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, - 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x88, 0x01, 0x0a, 0x0b, 0x53, 0x61, 0x76, 0x65, 0x64, - 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x64, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x64, 0x65, 0x49, - 0x64, 0x22, 0xa7, 0x01, 0x0a, 0x13, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x2b, - 0x0a, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, - 0x64, 0x65, 0x73, 0x52, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x22, 0xfb, 0x01, 0x0a, 0x1d, - 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0xd8, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x1a, 0x5a, 0x0a, 0x14, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x53, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0x97, 0x01, 0x0a, 0x16, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, - 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, - 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x69, - 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, - 0x12, 0x0a, 0x04, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, - 0x79, 0x6e, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0xe6, 0x02, 0x0a, 0x19, 0x47, 0x65, - 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, - 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x65, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x69, 0x6d, - 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x5f, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, - 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x79, 0x6e, 0x63, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x15, 0x0a, 0x06, - 0x75, 0x73, 0x65, 0x5f, 0x64, 0x62, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x75, 0x73, - 0x65, 0x44, 0x62, 0x12, 0x37, 0x0a, 0x18, 0x66, 0x69, 0x6c, 0x6c, 0x5f, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6e, 0x67, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x66, 0x69, 0x6c, 0x6c, 0x4d, 0x69, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x57, 0x69, 0x74, 0x68, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, - 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, - 0x63, 0x65, 0x22, 0xce, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, - 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, - 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, - 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, - 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x06, 0x63, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x06, 0x63, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x22, 0x84, 0x01, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, - 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x03, 0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x67, 0x68, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x04, 0x68, 0x69, 0x67, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6f, 0x70, 0x65, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x6f, 0x70, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, - 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x63, 0x6c, 0x6f, - 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x22, 0x78, 0x0a, 0x0a, 0x41, 0x75, - 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x22, 0x62, 0x0a, 0x09, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, - 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, - 0x08, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, 0x6e, 0x22, 0x44, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x41, - 0x0a, 0x14, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, - 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x18, 0x0a, 0x16, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0xa8, 0x01, 0x0a, 0x16, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, - 0x0b, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x12, 0x1c, - 0x0a, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x47, 0x0a, 0x1a, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x42, 0x0a, 0x15, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, - 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x4a, 0x0a, 0x18, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x5e, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x07, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x73, 0x22, 0x6f, 0x0a, 0x16, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x3d, 0x0a, 0x0f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x63, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x50, 0x0a, 0x1a, 0x53, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, + 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x70, 0x61, 0x69, 0x72, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x70, + 0x61, 0x69, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x80, 0x01, 0x0a, + 0x19, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, + 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, + 0x3f, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x22, 0x7d, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, + 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, + 0x3c, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, + 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x99, 0x01, + 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, + 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, + 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x43, 0x0a, 0x15, 0x47, 0x65, 0x74, + 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x75, 0x64, 0x69, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xa4, + 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x41, 0x0a, 0x23, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, - 0x36, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x33, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x22, 0x35, 0x0a, 0x17, - 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x22, 0x93, 0x02, 0x0a, 0x18, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, - 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, + 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0x88, 0x01, 0x0a, 0x0b, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, + 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x72, 0x61, 0x64, 0x65, 0x49, 0x64, + 0x22, 0xa7, 0x01, 0x0a, 0x13, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x2b, 0x0a, + 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, + 0x65, 0x73, 0x52, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x22, 0xfb, 0x01, 0x0a, 0x1d, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, + 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x12, + 0x0a, 0x04, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x79, + 0x6e, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0xe6, 0x02, 0x0a, 0x19, 0x47, 0x65, 0x74, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x65, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x69, 0x6d, 0x65, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x5f, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x78, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x79, 0x6e, 0x63, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x15, 0x0a, 0x06, 0x75, + 0x73, 0x65, 0x5f, 0x64, 0x62, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x75, 0x73, 0x65, + 0x44, 0x62, 0x12, 0x37, 0x0a, 0x18, 0x66, 0x69, 0x6c, 0x6c, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6e, 0x67, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x66, 0x69, 0x6c, 0x6c, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, + 0x67, 0x57, 0x69, 0x74, 0x68, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, + 0x65, 0x22, 0xce, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, + 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x17, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, - 0x61, 0x74, 0x65, 0x64, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x24, 0x0a, - 0x0d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x75, - 0x72, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, - 0x67, 0x55, 0x72, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, - 0x78, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x50, 0x0a, 0x1a, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, + 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, + 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x61, + 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x22, 0x84, 0x01, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, 0x6d, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, + 0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x67, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x04, 0x68, 0x69, 0x67, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6f, 0x70, 0x65, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x6f, 0x70, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, + 0x6c, 0x6f, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x73, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x22, 0x78, 0x0a, 0x0a, 0x41, 0x75, 0x64, + 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x22, 0x62, 0x0a, 0x09, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x75, 0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, + 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, 0x6e, 0x22, 0x44, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x41, 0x0a, + 0x14, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x22, 0x19, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, + 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x18, 0x0a, 0x16, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0xa8, 0x01, 0x0a, 0x16, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x12, 0x1c, 0x0a, + 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x47, 0x0a, 0x1a, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x22, 0x42, 0x0a, 0x15, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, + 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x4a, 0x0a, 0x18, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x22, 0x5e, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x07, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x73, 0x22, 0x6f, 0x0a, 0x16, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x3d, 0x0a, 0x0f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x22, 0x63, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x50, 0x0a, 0x1a, 0x53, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x3e, 0x0a, 0x20, 0x57, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x41, 0x0a, 0x23, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x36, + 0x0a, 0x18, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x33, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x22, 0x35, 0x0a, 0x17, 0x57, + 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x22, 0x93, 0x02, 0x0a, 0x18, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x7b, 0x0a, 0x15, 0x57, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, - 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x21, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, + 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x17, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x64, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, + 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x72, + 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, + 0x55, 0x72, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x50, 0x0a, 0x1a, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x3e, 0x0a, 0x20, 0x57, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x73, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, - 0x4c, 0x0a, 0x18, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, - 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x22, 0x46, 0x0a, - 0x16, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0xd3, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, - 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, - 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, - 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, - 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0xb6, 0x01, 0x0a, 0x1e, - 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, - 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, - 0x0a, 0x0d, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x65, 0x6e, 0x64, 0x22, 0xcd, 0x01, 0x0a, 0x1c, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, - 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, - 0x61, 0x69, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70, - 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x22, 0x57, 0x0a, 0x21, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xa3, 0x06, - 0x0a, 0x1b, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, - 0x61, 0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, - 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, - 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, - 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2c, 0x0a, 0x12, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x53, 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, - 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x61, - 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, + 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x7b, 0x0a, 0x15, 0x57, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x21, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x73, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4c, + 0x0a, 0x18, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, + 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x22, 0x46, 0x0a, 0x16, + 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x75, 0x72, 0x6c, 0x22, 0xd3, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, + 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, + 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x22, 0xb6, 0x01, 0x0a, 0x1e, 0x46, + 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, + 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, + 0x0d, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x65, 0x6e, 0x64, 0x22, 0xcd, 0x01, 0x0a, 0x1c, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, + 0x69, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, + 0x72, 0x69, 0x6f, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0x57, 0x0a, 0x21, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xa3, 0x06, 0x0a, + 0x1b, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, + 0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, + 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, + 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, + 0x69, 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x64, 0x61, 0x74, + 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, + 0x72, 0x79, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x65, 0x6d, + 0x70, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69, + 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x5f, 0x6f, 0x6e, 0x6c, + 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x4f, + 0x6e, 0x6c, 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x3a, 0x0a, 0x19, + 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x6a, 0x6f, 0x62, + 0x5f, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x17, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x4a, 0x6f, 0x62, + 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, 0x63, 0x69, + 0x6d, 0x61, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, + 0x69, 0x73, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x64, 0x65, 0x63, 0x69, + 0x6d, 0x61, 0x6c, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, + 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x11, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x15, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x1a, 0x69, 0x73, + 0x73, 0x75, 0x65, 0x5f, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x70, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, 0x18, + 0x69, 0x73, 0x73, 0x75, 0x65, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x50, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x73, 0x75, 0x65, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x4f, 0x6e, 0x49, 0x73, 0x73, + 0x75, 0x65, 0x22, 0x56, 0x0a, 0x1b, 0x49, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x71, 0x75, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x37, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x58, 0x0a, 0x1c, 0x49, 0x6e, + 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a, 0x6f, + 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x6a, 0x6f, + 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x04, + 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x4f, 0x0a, 0x1c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x15, + 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x22, 0x70, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x64, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x66, 0x75, 0x6c, 0x6c, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x87, 0x07, 0x0a, 0x0e, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, + 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, + 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, + 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x69, 0x7a, + 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x53, - 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x5f, 0x6f, 0x6e, - 0x6c, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, - 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, - 0x74, 0x65, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, - 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x3a, 0x0a, - 0x19, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x6a, 0x6f, - 0x62, 0x5f, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x17, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x4a, 0x6f, - 0x62, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x64, 0x65, 0x63, - 0x69, 0x6d, 0x61, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x64, 0x65, 0x63, - 0x69, 0x6d, 0x61, 0x6c, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, - 0x73, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, - 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x11, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x1a, 0x69, - 0x73, 0x73, 0x75, 0x65, 0x5f, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x70, - 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x18, 0x69, 0x73, 0x73, 0x75, 0x65, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x50, - 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, 0x70, - 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x73, 0x75, 0x65, 0x18, 0x13, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x4f, 0x6e, 0x49, 0x73, - 0x73, 0x75, 0x65, 0x22, 0x56, 0x0a, 0x1b, 0x49, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x71, - 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a, 0x6f, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x37, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, - 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x58, 0x0a, 0x1c, 0x49, - 0x6e, 0x73, 0x65, 0x72, 0x74, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4a, - 0x6f, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x6a, - 0x6f, 0x62, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, - 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x4f, 0x0a, 0x1c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x22, 0x70, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, + 0x61, 0x74, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x64, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, + 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x76, 0x65, + 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x6f, 0x76, 0x65, 0x72, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x3a, 0x0a, 0x19, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, + 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, + 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, + 0x18, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x16, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x43, 0x6f, 0x6d, + 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x61, 0x72, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x3c, 0x0a, 0x1a, 0x69, 0x73, 0x73, 0x75, 0x65, 0x5f, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, 0x6e, + 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x13, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x18, 0x69, 0x73, 0x73, 0x75, 0x65, 0x54, 0x6f, 0x6c, 0x65, 0x72, 0x61, + 0x6e, 0x63, 0x65, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, + 0x10, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x73, 0x75, + 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x4f, 0x6e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x6a, 0x6f, 0x62, 0x5f, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0a, 0x6a, 0x6f, 0x62, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x69, 0x65, + 0x73, 0x22, 0xa0, 0x01, 0x0a, 0x14, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, + 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, + 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x75, 0x6e, + 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6e, + 0x44, 0x61, 0x74, 0x65, 0x22, 0x43, 0x0a, 0x0f, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, + 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x5c, 0x0a, 0x20, 0x47, 0x65, 0x74, + 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, + 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x22, 0x64, 0x0a, 0x1e, 0x53, 0x65, 0x74, 0x44, 0x61, + 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, - 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x75, 0x6c, 0x6c, 0x5f, 0x64, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x66, 0x75, 0x6c, - 0x6c, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x87, 0x07, 0x0a, 0x0e, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, - 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, - 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, - 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, - 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, - 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x69, - 0x7a, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x72, - 0x65, 0x74, 0x72, 0x79, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, - 0x65, 0x6d, 0x70, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, - 0x53, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, - 0x64, 0x61, 0x74, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x64, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x76, - 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, - 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x6f, 0x76, 0x65, - 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x61, - 0x74, 0x61, 0x12, 0x3a, 0x0a, 0x19, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, - 0x74, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, - 0x69, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, - 0x0a, 0x18, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x16, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x43, 0x6f, - 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x73, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x61, 0x72, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x3c, 0x0a, 0x1a, 0x69, 0x73, 0x73, 0x75, 0x65, 0x5f, 0x74, 0x6f, 0x6c, 0x65, 0x72, 0x61, - 0x6e, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x13, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x18, 0x69, 0x73, 0x73, 0x75, 0x65, 0x54, 0x6f, 0x6c, 0x65, 0x72, - 0x61, 0x6e, 0x63, 0x65, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x28, - 0x0a, 0x10, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x6e, 0x5f, 0x69, 0x73, 0x73, - 0x75, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, - 0x65, 0x4f, 0x6e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x6a, 0x6f, 0x62, 0x5f, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0a, 0x6a, 0x6f, 0x62, - 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x69, - 0x65, 0x73, 0x22, 0xa0, 0x01, 0x0a, 0x14, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, - 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, - 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x44, 0x61, 0x74, 0x61, - 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x75, - 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, - 0x6e, 0x44, 0x61, 0x74, 0x65, 0x22, 0x43, 0x0a, 0x0f, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, - 0x62, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x5c, 0x0a, 0x20, 0x47, 0x65, - 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, - 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, - 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, - 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x22, 0x64, 0x0a, 0x1e, 0x53, 0x65, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, - 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, - 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x81, - 0x01, 0x0a, 0x27, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, - 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, - 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, - 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x69, 0x63, 0x6b, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x70, 0x72, 0x65, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x12, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, - 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, - 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x22, 0x41, - 0x0a, 0x13, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, - 0x64, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x49, - 0x64, 0x22, 0x38, 0x0a, 0x1a, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x63, 0x0a, 0x1b, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, - 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x22, 0x67, 0x0a, 0x1f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, - 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x64, 0x0a, 0x1c, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, + 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x81, 0x01, + 0x0a, 0x27, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, + 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, + 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x19, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x70, 0x72, 0x65, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, + 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x12, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, + 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x22, 0x41, 0x0a, + 0x13, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, + 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, + 0x22, 0x38, 0x0a, 0x1a, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x63, 0x0a, 0x1b, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, - 0x63, 0x0a, 0x1b, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, - 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x22, 0x57, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, - 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0xbe, 0x01, - 0x0a, 0x0d, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x12, 0x29, 0x0a, 0x10, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x77, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, - 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, - 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xa9, - 0x02, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, - 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, - 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, - 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, - 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, - 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x94, 0x02, 0x0a, 0x1b, 0x47, - 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, - 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, - 0x0a, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, - 0x5f, 0x70, 0x6e, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x6e, 0x6c, 0x12, 0x30, 0x0a, 0x14, - 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, - 0x5f, 0x70, 0x6e, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x55, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x6e, 0x6c, 0x12, 0x1b, - 0x0a, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x6e, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x6e, 0x6c, 0x12, 0x34, 0x0a, 0x09, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x93, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x11, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, - 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x6e, 0x72, - 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x5f, 0x70, 0x6e, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x6e, 0x6c, - 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x5f, 0x70, 0x6e, 0x6c, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, - 0x50, 0x6e, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x64, - 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, - 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, - 0x67, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, - 0x6f, 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, - 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xd2, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, - 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x62, 0x72, - 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, - 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, - 0x2b, 0x0a, 0x11, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x66, 0x66, - 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2e, 0x0a, 0x13, - 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x5a, 0x65, 0x72, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0xbe, 0x05, 0x0a, - 0x15, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6c, 0x6c, 0x61, - 0x74, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x4f, 0x0a, 0x25, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x76, 0x65, 0x5f, 0x73, 0x70, 0x6f, 0x74, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x4f, 0x66, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x53, 0x70, 0x6f, - 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x65, 0x0a, 0x30, 0x63, 0x6f, 0x6c, - 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, - 0x73, 0x70, 0x6f, 0x74, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x2b, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x43, - 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x42, 0x79, 0x50, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x76, 0x65, 0x53, 0x70, 0x6f, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, - 0x12, 0x27, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, - 0x72, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x64, 0x43, - 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x46, 0x0a, 0x0e, 0x75, 0x73, 0x65, - 0x64, 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, - 0x74, 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, - 0x77, 0x6e, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, - 0x6e, 0x12, 0x31, 0x0a, 0x14, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, - 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x13, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, - 0x65, 0x72, 0x61, 0x6c, 0x12, 0x35, 0x0a, 0x16, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, - 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, - 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x75, - 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x5f, 0x70, 0x6e, 0x6c, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, - 0x6e, 0x6c, 0x12, 0x4c, 0x0a, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x62, - 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, - 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, - 0x12, 0x4b, 0x0a, 0x12, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x72, 0x65, - 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, - 0x42, 0x79, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0xf7, 0x04, - 0x0a, 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, - 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x46, - 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1f, 0x0a, - 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x44, - 0x0a, 0x1f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x5f, - 0x75, 0x73, 0x65, 0x5f, 0x61, 0x73, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, - 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x55, 0x73, 0x65, 0x41, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, - 0x65, 0x72, 0x61, 0x6c, 0x12, 0x37, 0x0a, 0x18, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x5f, 0x66, - 0x61, 0x69, 0x72, 0x5f, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x46, 0x61, - 0x69, 0x72, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, - 0x09, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x37, 0x0a, 0x17, 0x63, - 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, - 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x63, 0x6f, - 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x74, - 0x6f, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, - 0x5f, 0x70, 0x6e, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, - 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x6e, 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x75, 0x6e, - 0x64, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x49, 0x6e, 0x55, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x1a, 0x61, - 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, - 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x18, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, - 0x74, 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x0e, 0x75, 0x73, 0x65, - 0x64, 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, - 0x74, 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, - 0x77, 0x6e, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, - 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x8f, 0x02, 0x0a, 0x14, 0x43, 0x6f, 0x6c, 0x6c, - 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, 0x79, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, - 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, - 0x12, 0x26, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x70, 0x65, 0x6e, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, - 0x0a, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, - 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4d, - 0x61, 0x72, 0x67, 0x69, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, - 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, - 0x74, 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, 0x22, 0xae, 0x03, 0x0a, 0x17, 0x43, 0x6f, - 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, 0x42, 0x72, 0x65, 0x61, - 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, - 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x73, 0x12, - 0x2b, 0x0a, 0x12, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x6e, 0x66, 0x74, - 0x5f, 0x62, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x6f, 0x63, - 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x4e, 0x66, 0x74, 0x42, 0x69, 0x64, 0x73, 0x12, 0x31, 0x0a, 0x15, - 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x76, 0x6f, - 0x75, 0x63, 0x68, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6c, 0x6f, 0x63, - 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x46, 0x65, 0x65, 0x56, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x12, - 0x4d, 0x0a, 0x24, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x6f, - 0x74, 0x5f, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1f, 0x6c, - 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x53, 0x70, 0x6f, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, - 0x6e, 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x73, 0x12, 0x31, - 0x0a, 0x15, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x6f, 0x74, - 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6c, - 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x53, 0x70, 0x6f, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x61, 0x73, 0x5f, 0x63, - 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x12, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, - 0x72, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x66, - 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x73, - 0x65, 0x64, 0x49, 0x6e, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x75, - 0x73, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x6f, 0x74, 0x5f, 0x6d, 0x61, 0x72, 0x67, - 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x75, 0x73, 0x65, 0x64, 0x49, 0x6e, - 0x53, 0x70, 0x6f, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x68, - 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, - 0x10, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0xa1, 0x05, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, - 0x61, 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, - 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, - 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, - 0x74, 0x68, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, - 0x6e, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x65, 0x72, - 0x69, 0x6f, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, - 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x66, 0x61, 0x73, 0x74, 0x50, 0x65, 0x72, 0x69, - 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, - 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x6c, 0x6f, 0x77, 0x50, 0x65, 0x72, - 0x69, 0x6f, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, - 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x70, 0x18, 0x0b, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x13, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x44, 0x65, 0x76, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x74, 0x61, 0x6e, 0x64, - 0x61, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x6f, - 0x77, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, - 0x72, 0x64, 0x44, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x12, - 0x2e, 0x0a, 0x13, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, - 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6d, 0x6f, - 0x76, 0x69, 0x6e, 0x67, 0x41, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x25, 0x0a, 0x0e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x33, 0x0a, 0x0a, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, - 0x70, 0x61, 0x69, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, - 0x52, 0x09, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x50, 0x61, 0x69, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6f, - 0x74, 0x68, 0x65, 0x72, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x29, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x66, 0x53, - 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x01, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, - 0x22, 0xbe, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, - 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x4b, 0x0a, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, - 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x1a, 0x51, - 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x66, 0x53, - 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x83, 0x04, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, - 0x61, 0x74, 0x65, 0x73, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x67, 0x0a, 0x1f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, - 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x65, - 0x74, 0x5f, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x61, 0x74, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x67, 0x65, 0x74, 0x50, 0x72, 0x65, 0x64, 0x69, - 0x63, 0x74, 0x65, 0x64, 0x52, 0x61, 0x74, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x67, 0x65, 0x74, 0x5f, - 0x6c, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x67, 0x65, 0x74, 0x4c, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x65, - 0x74, 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x73, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x67, 0x65, 0x74, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x52, - 0x61, 0x74, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x6f, 0x72, 0x72, - 0x6f, 0x77, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, - 0x67, 0x65, 0x74, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x43, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x2a, - 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x72, 0x61, - 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x41, 0x6c, 0x6c, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x61, - 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, - 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x61, 0x6b, 0x65, 0x72, - 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x74, 0x61, 0x6b, 0x65, 0x72, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, - 0x05, 0x72, 0x61, 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x72, 0x61, 0x74, 0x65, 0x73, 0x22, 0x3e, 0x0a, 0x0e, 0x4c, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, - 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x34, 0x0a, 0x0a, 0x42, 0x6f, 0x72, 0x72, 0x6f, - 0x77, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xe2, 0x02, - 0x0a, 0x0a, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, - 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, - 0x77, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x61, - 0x72, 0x6b, 0x65, 0x74, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1f, - 0x0a, 0x0b, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x52, 0x61, 0x74, 0x65, 0x12, - 0x1f, 0x0a, 0x0b, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x52, 0x61, 0x74, 0x65, - 0x12, 0x2c, 0x0a, 0x12, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, - 0x77, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x68, 0x6f, - 0x75, 0x72, 0x6c, 0x79, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x52, 0x61, 0x74, 0x65, 0x12, 0x2c, - 0x0a, 0x12, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x5f, - 0x72, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x79, 0x65, 0x61, 0x72, - 0x6c, 0x79, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x52, 0x61, 0x74, 0x65, 0x12, 0x3f, 0x0a, 0x0f, - 0x6c, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x6c, - 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x33, 0x0a, - 0x0b, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6f, 0x72, 0x72, - 0x6f, 0x77, 0x43, 0x6f, 0x73, 0x74, 0x52, 0x0a, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x43, 0x6f, - 0x73, 0x74, 0x22, 0xae, 0x03, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, - 0x52, 0x61, 0x74, 0x65, 0x73, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x72, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x72, - 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x52, 0x05, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1f, - 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, - 0x28, 0x0a, 0x10, 0x73, 0x75, 0x6d, 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x5f, 0x63, 0x6f, - 0x73, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x75, 0x6d, 0x42, 0x6f, - 0x72, 0x72, 0x6f, 0x77, 0x43, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x76, 0x67, - 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x61, 0x76, 0x67, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x53, 0x69, 0x7a, - 0x65, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x75, 0x6d, 0x5f, 0x6c, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x12, 0x73, 0x75, 0x6d, 0x4c, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x76, 0x67, 0x5f, 0x6c, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, - 0x76, 0x67, 0x4c, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x33, 0x0a, - 0x0b, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, + 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x64, 0x0a, 0x1c, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0x63, + 0x0a, 0x1b, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x22, 0x57, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0f, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0xbe, 0x01, 0x0a, + 0x0d, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x12, 0x29, 0x0a, 0x10, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x5f, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x77, 0x69, 0x74, 0x68, + 0x64, 0x72, 0x61, 0x77, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x64, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x45, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x74, + 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xa9, 0x02, + 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x28, + 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, + 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, + 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, + 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6f, + 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x94, 0x02, 0x0a, 0x1b, 0x47, 0x65, + 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x73, 0x75, 0x62, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, + 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x5f, + 0x70, 0x6e, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x52, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x6e, 0x6c, 0x12, 0x30, 0x0a, 0x14, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x5f, + 0x70, 0x6e, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x55, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x6e, 0x6c, 0x12, 0x1b, 0x0a, + 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x6e, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x6e, 0x6c, 0x12, 0x34, 0x0a, 0x09, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x22, 0x93, 0x02, 0x0a, 0x0e, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x6e, 0x72, 0x65, + 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x5f, 0x70, 0x6e, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x6e, 0x6c, 0x12, + 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x5f, 0x70, 0x6e, 0x6c, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, + 0x6e, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, + 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x69, 0x6e, + 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x69, 0x6e, 0x67, + 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x6f, + 0x73, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x06, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xd2, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x6f, + 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x62, 0x72, 0x65, + 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x2b, + 0x0a, 0x11, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x66, 0x66, 0x6c, + 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, + 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x5a, 0x65, 0x72, 0x6f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0xbe, 0x05, 0x0a, 0x15, + 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x5f, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, + 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x12, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x43, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x4f, 0x0a, 0x25, 0x74, 0x6f, 0x74, 0x61, 0x6c, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x76, 0x65, 0x5f, 0x73, 0x70, 0x6f, 0x74, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x4f, 0x66, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x53, 0x70, 0x6f, 0x74, + 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x65, 0x0a, 0x30, 0x63, 0x6f, 0x6c, 0x6c, + 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x73, + 0x70, 0x6f, 0x74, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x2b, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x43, 0x6f, + 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64, 0x42, 0x79, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x76, 0x65, 0x53, 0x70, 0x6f, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, + 0x27, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, + 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x64, 0x43, 0x6f, + 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x46, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x64, + 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, + 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, + 0x6e, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, + 0x12, 0x31, 0x0a, 0x14, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x6f, + 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, + 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, + 0x72, 0x61, 0x6c, 0x12, 0x35, 0x0a, 0x16, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, + 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x15, 0x6d, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, + 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x6e, + 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x5f, 0x70, 0x6e, 0x6c, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x6e, + 0x6c, 0x12, 0x4c, 0x0a, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x62, 0x72, + 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, + 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x11, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, + 0x4b, 0x0a, 0x12, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x72, 0x65, 0x61, + 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, + 0x79, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0xf7, 0x04, 0x0a, + 0x15, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6f, 0x72, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x66, + 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x46, 0x72, + 0x6f, 0x6d, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x44, 0x0a, + 0x1f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x75, + 0x73, 0x65, 0x5f, 0x61, 0x73, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, + 0x65, 0x46, 0x6f, 0x72, 0x55, 0x73, 0x65, 0x41, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, + 0x72, 0x61, 0x6c, 0x12, 0x37, 0x0a, 0x18, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x5f, 0x66, 0x61, + 0x69, 0x72, 0x5f, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x46, 0x61, 0x69, + 0x72, 0x4d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x37, 0x0a, 0x17, 0x63, 0x6f, + 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x63, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x5f, 0x74, 0x6f, + 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x54, 0x6f, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x5f, + 0x70, 0x6e, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x6e, 0x72, 0x65, 0x61, + 0x6c, 0x69, 0x73, 0x65, 0x64, 0x50, 0x6e, 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x75, 0x6e, 0x64, + 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x66, 0x75, 0x6e, 0x64, 0x73, 0x49, 0x6e, 0x55, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x1a, 0x61, 0x64, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, + 0x72, 0x61, 0x6c, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, + 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, + 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, 0x12, 0x46, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x64, + 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, + 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, + 0x6e, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x64, 0x6f, 0x77, 0x6e, + 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x8f, 0x02, 0x0a, 0x14, 0x43, 0x6f, 0x6c, 0x6c, 0x61, + 0x74, 0x65, 0x72, 0x61, 0x6c, 0x42, 0x79, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, + 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, + 0x26, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x69, + 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x70, 0x65, 0x6e, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x4d, 0x61, + 0x72, 0x67, 0x69, 0x6e, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, + 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, + 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, 0x22, 0xae, 0x03, 0x0a, 0x17, 0x43, 0x6f, 0x6c, + 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x64, 0x42, 0x72, 0x65, 0x61, 0x6b, + 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, + 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x53, 0x74, 0x61, 0x6b, 0x65, 0x73, 0x12, 0x2b, + 0x0a, 0x12, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x6e, 0x66, 0x74, 0x5f, + 0x62, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x6f, 0x63, 0x6b, + 0x65, 0x64, 0x49, 0x6e, 0x4e, 0x66, 0x74, 0x42, 0x69, 0x64, 0x73, 0x12, 0x31, 0x0a, 0x15, 0x6c, + 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x76, 0x6f, 0x75, + 0x63, 0x68, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6c, 0x6f, 0x63, 0x6b, + 0x65, 0x64, 0x49, 0x6e, 0x46, 0x65, 0x65, 0x56, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x12, 0x4d, + 0x0a, 0x24, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x6f, 0x74, + 0x5f, 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x5f, 0x66, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, + 0x6f, 0x66, 0x66, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1f, 0x6c, 0x6f, + 0x63, 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x53, 0x70, 0x6f, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, + 0x46, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x73, 0x12, 0x31, 0x0a, + 0x15, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x6f, 0x74, 0x5f, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6c, 0x6f, + 0x63, 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x53, 0x70, 0x6f, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x61, 0x73, 0x5f, 0x63, 0x6f, + 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, + 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x41, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, + 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x66, 0x75, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x75, 0x73, 0x65, + 0x64, 0x49, 0x6e, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x75, 0x73, + 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x70, 0x6f, 0x74, 0x5f, 0x6d, 0x61, 0x72, 0x67, 0x69, + 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x75, 0x73, 0x65, 0x64, 0x49, 0x6e, 0x53, + 0x70, 0x6f, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x68, 0x75, + 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, 0x10, + 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0xa1, 0x05, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, + 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, + 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, + 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, + 0x68, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, + 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, + 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x65, 0x72, 0x69, + 0x6f, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x66, 0x61, 0x73, 0x74, 0x50, 0x65, 0x72, 0x69, 0x6f, + 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x6c, 0x6f, 0x77, 0x50, 0x65, 0x72, 0x69, + 0x6f, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x64, + 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x13, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x44, 0x65, 0x76, 0x69, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, + 0x72, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x6f, 0x77, + 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, + 0x64, 0x44, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x2e, + 0x0a, 0x13, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6d, 0x6f, 0x76, + 0x69, 0x6e, 0x67, 0x41, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x25, + 0x0a, 0x0e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x33, 0x0a, 0x0a, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x70, + 0x61, 0x69, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, + 0x09, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x50, 0x61, 0x69, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x74, + 0x68, 0x65, 0x72, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x22, 0x29, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x66, 0x53, 0x69, + 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x01, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x22, + 0xbe, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, 0x6c, + 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x4b, 0x0a, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, + 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x1a, 0x51, 0x0a, + 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x66, 0x53, 0x69, + 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x83, 0x04, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, + 0x74, 0x65, 0x73, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x65, 0x74, + 0x5f, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x67, 0x65, 0x74, 0x50, 0x72, 0x65, 0x64, 0x69, 0x63, + 0x74, 0x65, 0x64, 0x52, 0x61, 0x74, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x67, 0x65, 0x74, 0x5f, 0x6c, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x67, 0x65, 0x74, 0x4c, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x65, 0x74, + 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0e, 0x67, 0x65, 0x74, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x52, 0x61, + 0x74, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, + 0x77, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x67, + 0x65, 0x74, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x43, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x2a, 0x0a, + 0x11, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x72, 0x61, 0x74, + 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x41, 0x6c, 0x6c, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x61, 0x6c, + 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, + 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x74, 0x61, 0x6b, 0x65, 0x72, 0x5f, + 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x74, 0x61, 0x6b, 0x65, 0x72, 0x46, 0x65, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x05, + 0x72, 0x61, 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x52, + 0x05, 0x72, 0x61, 0x74, 0x65, 0x73, 0x22, 0x3e, 0x0a, 0x0e, 0x4c, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x34, 0x0a, 0x0a, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, + 0x43, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0xe2, 0x02, 0x0a, + 0x0a, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, + 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6d, 0x61, 0x72, + 0x6b, 0x65, 0x74, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1f, 0x0a, + 0x0b, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x52, 0x61, 0x74, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x52, 0x61, 0x74, 0x65, 0x12, + 0x2c, 0x0a, 0x12, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, + 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x68, 0x6f, 0x75, + 0x72, 0x6c, 0x79, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x52, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, + 0x12, 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x5f, 0x72, + 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x79, 0x65, 0x61, 0x72, 0x6c, + 0x79, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x52, 0x61, 0x74, 0x65, 0x12, 0x3f, 0x0a, 0x0f, 0x6c, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x6c, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x0b, + 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6f, 0x72, 0x72, 0x6f, + 0x77, 0x43, 0x6f, 0x73, 0x74, 0x52, 0x0a, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x43, 0x6f, 0x73, + 0x74, 0x22, 0xae, 0x03, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, + 0x61, 0x74, 0x65, 0x73, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x72, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x72, 0x67, - 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x61, - 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, - 0x72, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x52, 0x0d, - 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x65, 0x64, 0x52, 0x61, 0x74, 0x65, 0x12, 0x24, 0x0a, - 0x0e, 0x74, 0x61, 0x6b, 0x65, 0x72, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x61, 0x6b, 0x65, 0x72, 0x46, 0x65, 0x65, 0x52, - 0x61, 0x74, 0x65, 0x32, 0xfc, 0x5b, 0x0a, 0x15, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4f, 0x0a, - 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, - 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, - 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, - 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, - 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, - 0x6d, 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, - 0x0f, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, - 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, - 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, - 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, - 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, - 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, - 0x79, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, - 0x0a, 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, - 0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, - 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, + 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x52, 0x05, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1f, 0x0a, + 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x28, + 0x0a, 0x10, 0x73, 0x75, 0x6d, 0x5f, 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x5f, 0x63, 0x6f, 0x73, + 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x75, 0x6d, 0x42, 0x6f, 0x72, + 0x72, 0x6f, 0x77, 0x43, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x76, 0x67, 0x5f, + 0x62, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x61, 0x76, 0x67, 0x42, 0x6f, 0x72, 0x72, 0x6f, 0x77, 0x53, 0x69, 0x7a, 0x65, + 0x12, 0x30, 0x0a, 0x14, 0x73, 0x75, 0x6d, 0x5f, 0x6c, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, + 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, + 0x73, 0x75, 0x6d, 0x4c, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x76, 0x67, 0x5f, 0x6c, 0x65, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x76, + 0x67, 0x4c, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x33, 0x0a, 0x0b, + 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x72, 0x67, 0x69, + 0x6e, 0x52, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x61, 0x74, + 0x65, 0x12, 0x39, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, + 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x70, + 0x72, 0x65, 0x64, 0x69, 0x63, 0x74, 0x65, 0x64, 0x52, 0x61, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0e, + 0x74, 0x61, 0x6b, 0x65, 0x72, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x61, 0x6b, 0x65, 0x72, 0x46, 0x65, 0x65, 0x52, 0x61, + 0x74, 0x65, 0x32, 0xfc, 0x5b, 0x0a, 0x15, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, + 0x72, 0x61, 0x64, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4f, 0x0a, 0x07, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, + 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, + 0x0d, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, + 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x68, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, + 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x12, 0x6a, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, + 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, + 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, + 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, + 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, + 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x6e, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, - 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, - 0x63, 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, - 0x12, 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, - 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, - 0x01, 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, - 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, - 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, - 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, - 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, - 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, - 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, - 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, - 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, + 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, + 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, + 0x6f, 0x64, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x6f, 0x74, 0x70, 0x12, 0x73, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x54, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, - 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, - 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, - 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, - 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, + 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, + 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, + 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, + 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x71, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, 0x0a, 0x14, 0x47, 0x65, + 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, + 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, + 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x1b, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x73, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x12, 0x76, 0x0a, 0x13, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, + 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, + 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x7f, 0x0a, 0x16, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1a, + 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, + 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, + 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, + 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, 0x12, + 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, 0x47, + 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, + 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, + 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, 0x75, + 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, - 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, - 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, - 0x12, 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, - 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, - 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, - 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, - 0x12, 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, - 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, - 0x12, 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, - 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, + 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, 0x12, + 0x5e, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, + 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, 0x74, + 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, + 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, + 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, + 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, - 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, - 0x12, 0x7a, 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x62, 0x61, - 0x74, 0x63, 0x68, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, - 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, - 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, - 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, - 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, - 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, - 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, - 0x2a, 0x12, 0x5e, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, - 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, - 0x2a, 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, + 0x12, 0xb2, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, - 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, + 0x01, 0x2a, 0x12, 0x9e, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x23, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, - 0x46, 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, - 0x01, 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, - 0x64, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, - 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, - 0x6e, 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, - 0x12, 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, - 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, - 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x73, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, + 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, + 0x3a, 0x01, 0x2a, 0x12, 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, + 0x69, 0x61, 0x74, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, + 0x2a, 0x12, 0x8b, 0x01, 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, + 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, + 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, + 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, + 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x2d, 0x22, 0x28, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, + 0x64, 0x73, 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, + 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, - 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, - 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, + 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, + 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, + 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x22, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, + 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, + 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, + 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, + 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, + 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, + 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, - 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, - 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, - 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, - 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, + 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, + 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, + 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, + 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, + 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, + 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, - 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, - 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, - 0x72, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, - 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, - 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, + 0x0f, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, + 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x18, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, - 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, + 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, - 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, + 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x68, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, + 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, - 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, - 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, 0x0d, 0x47, + 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, 0x74, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x6b, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x65, 0x12, 0x6b, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x78, + 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, + 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, + 0x6e, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, + 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x61, 0x6c, 0x6c, 0x3a, 0x01, 0x2a, 0x12, + 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, + 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, + 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, + 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x6c, 0x69, 0x73, + 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x12, + 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, + 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, 0x65, + 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, + 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, 0x6c, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x1c, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, 0x2f, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x75, + 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, 0x11, + 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, + 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, 0x65, + 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, - 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, 0x0a, 0x0e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x1d, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, - 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x65, 0x0a, 0x0d, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, - 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, - 0x12, 0x6e, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, - 0x70, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x61, 0x6c, 0x6c, 0x3a, 0x01, 0x2a, - 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, - 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x6c, 0x69, - 0x73, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, - 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, - 0x0a, 0x12, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x69, 0x63, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x6a, 0x0a, 0x10, 0x53, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x73, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x41, 0x6c, - 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x22, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x41, 0x6c, 0x6c, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x61, 0x6c, 0x6c, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x8e, 0x01, 0x0a, - 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, 0x2b, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x61, - 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, 0x31, - 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x70, 0x61, 0x69, 0x72, 0x73, 0x12, 0x77, 0x0a, - 0x11, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, - 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x10, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x13, 0x57, - 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x12, 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, - 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, + 0x97, 0x01, 0x0a, 0x19, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x28, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, + 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, 0x62, + 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, - 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x47, 0x65, 0x74, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, - 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x6d, 0x0a, 0x11, 0x57, 0x65, - 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, - 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, - 0x74, 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, - 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, - 0x65, 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, - 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, - 0x72, 0x6c, 0x12, 0x6a, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, - 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, - 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x65, 0x74, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x70, - 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, 0x61, - 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, - 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x30, 0x01, - 0x12, 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, + 0x74, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, + 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x73, 0x65, 0x74, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x67, 0x0a, 0x0f, 0x57, 0x65, 0x62, 0x73, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x55, 0x52, 0x4c, 0x12, 0x1e, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, + 0x74, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, + 0x31, 0x2f, 0x77, 0x65, 0x62, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x65, 0x74, 0x75, 0x72, + 0x6c, 0x12, 0x6a, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x54, 0x72, + 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, + 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, + 0x74, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x70, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, - 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, - 0x61, 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x16, 0x43, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, - 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, - 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, - 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, - 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, - 0x23, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x73, 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, - 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, - 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, - 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x73, 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, - 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, - 0x31, 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x72, 0x61, - 0x64, 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, 0x01, 0x0a, - 0x14, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, - 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, - 0x73, 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, - 0x6f, 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, - 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, - 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, - 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, - 0x6f, 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, - 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x61, 0x74, - 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, 0x01, 0x0a, - 0x19, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, - 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, - 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x25, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, - 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, 0x65, 0x74, - 0x77, 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, + 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x30, 0x01, 0x12, + 0x68, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x61, + 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, + 0x72, 0x61, 0x64, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x61, + 0x76, 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x16, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x74, 0x54, 0x72, 0x61, 0x64, 0x65, 0x73, 0x54, 0x6f, 0x43, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, + 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x12, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x74, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x74, 0x6f, 0x63, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x1f, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x43, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, + 0x73, 0x73, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, + 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, + 0x61, 0x76, 0x65, 0x64, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x73, 0x12, 0x9a, 0x01, 0x0a, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x53, 0x61, 0x76, 0x65, 0x64, 0x54, 0x72, 0x61, 0x64, 0x65, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4d, 0x69, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x76, + 0x31, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x73, 0x61, 0x76, + 0x65, 0x64, 0x74, 0x72, 0x61, 0x64, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x73, + 0x12, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, + 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x54, 0x72, 0x61, 0x64, 0x65, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, + 0x2f, 0x73, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, 0x72, 0x61, 0x64, + 0x65, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x86, 0x01, 0x0a, 0x14, + 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, + 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, + 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x73, + 0x65, 0x72, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, + 0x62, 0x3a, 0x01, 0x2a, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, - 0x62, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, + 0x62, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x71, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, + 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x61, 0x74, 0x61, + 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x85, 0x01, 0x0a, 0x19, + 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x12, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x42, 0x65, 0x74, 0x77, 0x65, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x73, 0x22, 0x25, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x64, 0x61, 0x74, + 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x62, 0x65, 0x74, 0x77, + 0x65, 0x65, 0x6e, 0x12, 0x81, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, + 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, + 0x62, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, + 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, - 0x74, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, - 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, - 0x0a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, - 0x74, 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, - 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x70, 0x72, - 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x68, 0x0a, - 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, - 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, - 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, - 0x69, 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, - 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, 0x65, 0x74, - 0x61, 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, 0x14, 0x43, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, 0x1b, 0x2f, 0x76, + 0x31, 0x2f, 0x73, 0x65, 0x74, 0x64, 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x6a, 0x6f, 0x62, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x9d, 0x01, 0x0a, + 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, + 0x65, 0x12, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x44, 0x61, 0x74, 0x61, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4a, 0x6f, 0x62, 0x50, + 0x72, 0x65, 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x82, - 0x01, 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, 0x67, 0x63, + 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x29, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x61, 0x74, 0x61, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x6a, 0x6f, 0x62, 0x70, 0x72, 0x65, + 0x72, 0x65, 0x71, 0x75, 0x69, 0x73, 0x69, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x68, 0x0a, 0x10, + 0x47, 0x65, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, + 0x6f, 0x64, 0x69, 0x66, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x64, 0x69, 0x66, + 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x69, + 0x66, 0x79, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x79, 0x0a, 0x13, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x22, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x67, 0x65, 0x74, 0x61, + 0x6c, 0x6c, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, + 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x76, 0x0a, 0x14, 0x43, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x64, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x12, 0x79, 0x0a, 0x15, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, - 0x61, 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, - 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, - 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x53, 0x0a, - 0x08, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x68, 0x75, 0x74, - 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x0e, 0x12, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, - 0x77, 0x6e, 0x12, 0x83, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, - 0x63, 0x61, 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x23, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, - 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, - 0x68, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, 0x6c, - 0x61, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, - 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x73, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4d, + 0x74, 0x65, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x12, 0x82, 0x01, + 0x0a, 0x18, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, + 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x54, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x63, 0x79, 0x73, 0x74, 0x61, 0x74, 0x65, 0x74, 0x72, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x70, 0x61, + 0x69, 0x72, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x50, 0x6f, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, + 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x75, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x65, 0x74, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, + 0x65, 0x72, 0x61, 0x6c, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, + 0x65, 0x74, 0x63, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x53, 0x0a, 0x08, + 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, + 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x0e, 0x12, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, + 0x6e, 0x12, 0x83, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, + 0x61, 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, 0x6c, + 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, + 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, + 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x61, + 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x73, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x73, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x6d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x72, 0x61, 0x74, 0x65, 0x73, 0x68, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, - 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x79, 0x12, 0x24, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, + 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x73, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x73, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6d, + 0x61, 0x72, 0x67, 0x69, 0x6e, 0x72, 0x61, 0x74, 0x65, 0x73, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, + 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index b7c5bfc9..22d8aa6e 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -535,6 +535,7 @@ message WithdrawalEventsByExchangeRequest { string id = 2; int32 limit = 3; string currency = 4; + string asset_type = 5; } message WithdrawalEventsByDateRequest { diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index ebd888ae..9df5f790 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -6241,6 +6241,9 @@ }, "currency": { "type": "string" + }, + "assetType": { + "type": "string" } } }, diff --git a/testdata/configtest.json b/testdata/configtest.json index 42996a2e..f3cd307b 100644 --- a/testdata/configtest.json +++ b/testdata/configtest.json @@ -638,6 +638,112 @@ } ] }, + { + "name": "Bybit", + "enabled": true, + "verbose": false, + "httpTimeout": 15000000000, + "websocketResponseCheckTimeout": 30000000, + "websocketResponseMaxLimit": 7000000000, + "websocketTrafficTimeout": 30000000000, + "baseCurrencies": "", + "currencyPairs": { + "bypassConfigFormatUpgrades": false, + "requestFormat": { + "uppercase": true + }, + "configFormat": { + "uppercase": true, + "delimiter": "-" + }, + "useGlobalFormat": true, + "pairs": { + "coinmarginedfutures": { + "assetEnabled": true, + "enabled": "BTC-USD", + "available": "BTC-USD,XRP-USD,SOL-USD,ADA-USD,MANA-USD,LTC-USD,ETH-USD,EOS-USD,DOT-USD,BIT-USD" + }, + "futures": { + "assetEnabled": true, + "enabled": "BTCUSD-Z22", + "available": "BTCUSD-U22,BTCUSD-M22,BTCUSD-Z22,ETHUSD-U22,ETHUSD-M22" + }, + "spot": { + "assetEnabled": true, + "enabled": "BTC-USDT,ETH-USDT", + "available": "BTC-USDT,ETH-USDT,XRP-USDT,EOS-USDT,ETH-BTC,XRP-BTC,DOT-USDT,XLM-USDT,LTC-USDT,DOGE-USDT,BIT-USDT,CHZ-USDT,AXS-USDT,MANA-USDT,DYDX-USDT,MKR-USDT,COMP-USDT,AAVE-USDT,YFI-USDT,LINK-USDT,SUSHI-USDT,UNI-USDT,KSM-USDT,ICP-USDT,ADA-USDT,KLAY-USDT,XTZ-USDT,BCH-USDT,SRM-USDT,QNT-USDT,USDC-USDT,GRT-USDT,SOL-USDT,FIL-USDT,OMG-USDT,TRIBE-USDT,BAT-USDT,ZRX-USDT,CRV-USDT,AGLD-USDT,ANKR-USDT,PERP-USDT,MATIC-USDT,WAVES-USDT,LUNC-USDT,SPELL-USDT,SHIB-USDT,FTM-USDT,ATOM-USDT,ALGO-USDT,ENJ-USDT,CBX-USDT,SAND-USDT,AVAX-USDT,WOO-USDT,FTT-USDT,GODS-USDT,IMX-USDT,ENS-USDT,GM-USDT,CWAR-USDT,CAKE-USDT,STETH-USDT,GALFT-USDT,LFW-USDT,SLP-USDT,C98-USDT,PSP-USDT,GENE-USDT,AVA-USDT,ONE-USDT,PTU-USDT,SHILL-USDT,XYM-USDT,BOBA-USDT,INSUR-USDT,JASMY-USDT,GALA-USDT,RNDR-USDT,TRVL-USDT,WEMIX-USDT,XEM-USDT,KMA-USDT,BICO-USDT,CEL-USDT,UMA-USDT,HOT-USDT,NEXO-USDT,AMP-USDT,BNT-USDT,SNX-USDT,REN-USDT,1INCH-USDT,TEL-USDT,SIS-USDT,LRC-USDT,LDO-USDT,REAL-USDT,KRL-USDT,DEVT-USDT,CRAFT-USDT,BIT-BTC,BIT-USDC,ETH-USDC,BTC-USDC,1SOL-USDT,PLT-USDT,IZI-USDT,QTUM-USDT,DCR-USDT,ZEN-USDT,THETA-USDT,MX-USDT,DGB-USDT,RVN-USDT,EGLD-USDT,RUNE-USDT,XLM-BTC,XLM-USDC,SOL-USDC,XRP-USDC,ALGO-BTC,SOL-BTC,DFL-USDT,RAIN-USDT,RUN-USDT,XEC-USDT,ICX-USDT,XDC-USDT,HNT-USDT,BTG-USDT,ZIL-USDT,HBAR-USDT,FLOW-USDT,SOS-USDT,KASTA-USDT,GAS-USDT,STX-USDT,SIDUS-USDT,VPAD-USDT,GGM-USDT,LOOKS-USDT,MBS-USDT,DAI-USDT,BUSD-USDT,ACA-USDT,MV-USDT,MIX-USDT,LTC-USDC,MANA-BTC,MATIC-BTC,LTC-BTC,DOT-BTC,SAND-BTC,MANA-USDC,MATIC-USDC,SAND-USDC,DOT-USDC,LUNC-USDC,RSS3-USDT,SYNR-USDT,TAP-USDT,ERTHA-USDT,GMX-USDT,POSI-USDT,T-USDT,ACH-USDT,JST-USDT,SUN-USDT,BTT-USDT,TRX-USDT,NFT-USDT,POKT-USDT,SCRT-USDT,PSTAKE-USDT,SON-USDT,HERO-USDT,DOME-USDT,ZBC-USDT,USTC-USDT,BNB-USDT,NEAR-USDT,PAXG-USDT,SD-USDT,APE-USDT,BTC3S-USDT,BTC3L-USDT,FIDA-USDT,MINA-USDT,SC-USDT,RACA-USDT,IME-USDT,CAPS-USDT,STG-USDT,LMR-USDT,GLMR-USDT,MOVR-USDT,ZAM-USDT,ETH-DAI,BTC-DAI,WBTC-USDT,XAVA-USDT,MELOS-USDT,GMT-USDT,GST-USDT,CELO-USDT,SFUND-USDT,ELT-USDT,LGX-USDT,BIT-DAI,APEX-USDT,CTC-USDT,COT-USDT,KMON-USDT,PLY-USDT,XWG-USDT,FITFI-USDT,STRM-USDT,GAL-USDT,FCD-USDT,ETH3S-USDT,ETH3L-USDT,KOK-USDT,FAME-USDT,XRP3S-USDT,XRP3L-USDT,USDD-USDT,LUNA-USDT" + }, + "usdcmarginedfutures": { + "assetEnabled": true, + "enabled": "BTC-PERP", + "available": "BTC-PERP" + }, + "usdtmarginedfutures": { + "assetEnabled": true, + "enabled": "BTC-USDT", + "available": "BTC-USDT,AGLD-USDT,RAY-USDT,ASTR-USDT,XTZ-USDT,SFP-USDT,REEF-USDT,XEM-USDT,IOST-USDT,SLP-USDT,JASMY-USDT,ZEC-USDT,SC-USDT,MINA-USDT,HNT-USDT,XRP-USDT,MATIC-USDT,BNB-USDT,CHZ-USDT,OMG-USDT,ZEN-USDT,PEOPLE-USDT,DAR-USDT,SUN-USDT,DODO-USDT,AKRO-USDT,RUNE-USDT,YGG-USDT,XCN-USDT,FITFI-USDT,BIT-USDT,PAXG-USDT,KDA-USDT,AVAX-USDT,GALA-USDT,RVN-USDT,MTL-USDT,GAL-USDT,SXP-USDT,KAVA-USDT,CREAM-USDT,CKB-USDT,BSW-USDT,EOS-USDT,UNI-USDT,SUSHI-USDT,ATOM-USDT,LRC-USDT,SAND-USDT,GRT-USDT,CVC-USDT,1000BTT-USDT,FLM-USDT,VET-USDT,CELO-USDT,OGN-USDT,BCH-USDT,DOGE-USDT,CRV-USDT,LPT-USDT,DGB-USDT,TOMO-USDT,ETC-USDT,FLOW-USDT,STORJ-USDT,KLAY-USDT,SPELL-USDT,10000NFT-USDT,STMX-USDT,ALGO-USDT,DASH-USDT,API3-USDT,GLMR-USDT,ZRX-USDT,IOTX-USDT,QTUM-USDT,SKL-USDT,IOTA-USDT,KNC-USDT,ROSE-USDT,ARPA-USDT,SOL-USDT,BAT-USDT,ICX-USDT,ACH-USDT,XLM-USDT,SHIB1000-USDT,YFI-USDT,EGLD-USDT,REQ-USDT,APE-USDT,BOBA-USDT,TRX-USDT,COMP-USDT,CRO-USDT,ALPHA-USDT,LTC-USDT,AAVE-USDT,NEAR-USDT,ENS-USDT,RSR-USDT,STX-USDT,XMR-USDT,BNX-USDT,CVX-USDT,GST-USDT,ADA-USDT,SRM-USDT,CELR-USDT,C98-USDT,GTC-USDT,AR-USDT,SCRT-USDT,LINK-USDT,DOT-USDT,DYDX-USDT,ANKR-USDT,AUDIO-USDT,KSM-USDT,REN-USDT,IMX-USDT,MASK-USDT,WAVES-USDT,CTSI-USDT,LINA-USDT,MKR-USDT,ANT-USDT,DUSK-USDT,HOT-USDT,BSV-USDT,ALICE-USDT,GMT-USDT,BAKE-USDT,SNX-USDT,BICO-USDT,ZIL-USDT,FIL-USDT,ENJ-USDT,ICP-USDT,FTT-USDT,ILV-USDT,CTK-USDT,LOOKS-USDT,THETA-USDT,ONE-USDT,TLM-USDT,RNDR-USDT,DENT-USDT,NEO-USDT,OCEAN-USDT,ETH-USDT,AXS-USDT,COTI-USDT,JST-USDT,FXS-USDT,HBAR-USDT,CHR-USDT,1INCH-USDT,RSS3-USDT,1000XEC-USDT,BAL-USDT,CTC-USDT,MANA-USDT,FTM-USDT,WOO-USDT,LIT-USDT,BAND-USDT" + } + } + }, + "api": { + "authenticatedSupport": true, + "authenticatedWebsocketApiSupport": false, + "credentials": { + "key": "Key", + "secret": "Secret" + }, + "credentialsValidator": { + "requiresKey": true, + "requiresSecret": true + }, + "urlEndpoints": { + "RestCoinMarginedFuturesURL": "https://api.bybit.com", + "RestFuturesURL": "https://api.bybit.com", + "RestSpotURL": "https://api.bybit.com", + "RestUSDTMarginedFuturesURL": "https://api.bybit.com", + "WebsocketSpotURL": "wss://stream.bybit.com/spot/quote/ws/v2", + "RestUSDCMarginedFuturesURL": "https://api.bybit.com" + } + }, + "features": { + "supports": { + "restAPI": true, + "restCapabilities": { + "autoPairUpdates": false + }, + "websocketAPI": true, + "websocketCapabilities": {} + }, + "enabled": { + "autoPairUpdates": false, + "websocketAPI": true, + "saveTradeData": false, + "tradeFeed": false, + "fillsFeed": false + } + }, + "bankAccounts": [ + { + "enabled": false, + "bankName": "", + "bankAddress": "", + "bankPostalCode": "", + "bankPostalCity": "", + "bankCountry": "", + "accountName": "", + "accountNumber": "", + "swiftCode": "", + "iban": "", + "supportedCurrencies": "" + } + ], + "orderbook": { + "verificationBypass": false, + "websocketBufferLimit": 5, + "websocketBufferEnabled": false, + "publishPeriod": 10000000000 + } + }, { "name": "LBank", "enabled": true,