From d8afba379598dcb48fff43dace9994d785f22c2f Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Thu, 28 Jul 2022 13:21:29 +0100 Subject: [PATCH 01/18] chore: Config update, small cleanup --- bot/normal/normal.go | 7 ++--- config/config.go | 8 +++--- config/config.yaml | 5 ++-- ...config_docker.yaml => config_capsule.yaml} | 5 ++-- node/datanode.go | 27 ++----------------- token/service.go | 4 +-- 6 files changed, 15 insertions(+), 41 deletions(-) rename config/{config_docker.yaml => config_capsule.yaml} (94%) diff --git a/bot/normal/normal.go b/bot/normal/normal.go index 5bbbe71..48c85f1 100644 --- a/bot/normal/normal.go +++ b/bot/normal/normal.go @@ -6,7 +6,6 @@ import ( "fmt" "strings" "sync" - "time" dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" "code.vegaprotocol.io/vegawallet/wallets" @@ -69,11 +68,9 @@ func New(botConf config.BotConfig, locations []string, seedConf *config.TokenCon // Start starts the liquidity bot goroutine(s). func (b *bot) Start() error { - callTimeout := time.Duration(b.config.CallTimeout) * time.Millisecond - dataNode := node.NewDataNode( b.locations, - callTimeout, + b.config.CallTimeoutMills, ) dataNode.DialConnection(context.Background()) // blocking @@ -91,7 +88,7 @@ func (b *bot) Start() error { b.marketStream = data.NewMarketStream(dataNode, b.walletPubKey, pauseCh) - b.tokens, err = token.NewService(b.tokenConfig, b.walletPubKey) + b.tokens, err = token.NewService(b.tokenConfig, b.config.QuoteTokenAddress, b.walletPubKey) if err != nil { return fmt.Errorf("failed to create token service: %w", err) } diff --git a/config/config.go b/config/config.go index 5a854f3..b54be14 100644 --- a/config/config.go +++ b/config/config.go @@ -119,8 +119,8 @@ type BotConfig struct { // It is *not* a public key seen by Vega. Name string `yaml:"name"` - // CallTimeout is the per-call timeout (in milliseconds) for communicating with the Vega node gRPC endpoint. - CallTimeout int `yaml:"callTimeout"` + // CallTimeoutMills is the per-call timeout (in milliseconds) for communicating with the Vega node gRPC endpoint. + CallTimeoutMills int `yaml:"callTimeoutMills"` // InstrumentBase is the base asset of the instrument. InstrumentBase string `yaml:"instrumentBase"` @@ -128,6 +128,9 @@ type BotConfig struct { // InstrumentQuote is the quote asset of the instrument. InstrumentQuote string `yaml:"instrumentQuote"` + // QuoteTokenAddress is the address of the base token. + QuoteTokenAddress string `yaml:"quoteTokenAddress"` + // Strategy specifies which algorithm the bot is to use. Strategy string `yaml:"strategy"` @@ -147,7 +150,6 @@ type TokenConfig struct { EthereumAPIAddress string `yaml:"ethereumAPIAddress"` Erc20BridgeAddress string `yaml:"erc20BridgeAddress"` StakingBridgeAddress string `yaml:"stakingBridgeAddress"` - ERC20TokenAddress string `yaml:"erc20TokenAddress"` VegaTokenAddress string `yaml:"vegaTokenAddress"` ContractOwnerAddress string `yaml:"contractOwnerAddress"` ContractOwnerPrivateKey string `yaml:"contractOwnerPrivateKey"` diff --git a/config/config.yaml b/config/config.yaml index 41ca1a5..c777ab4 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -16,7 +16,6 @@ token: ethereumAPIAddress: https://127.0.0.1:8545 erc20BridgeAddress: 0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54 stakingBridgeAddress: 0x9135f5afd6F055e731bca2348429482eE614CFfA - erc20TokenAddress: 0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0 vegaTokenAddress: 0x67175Da1D5e966e40D11c4B2519392B2058373de contractOwnerAddress: 0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F contractOwnerPrivateKey: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 @@ -26,10 +25,10 @@ locations: - localhost:3027 bots: - name: w00 - connectTimeout: 30000 # milliseconds - callTimeout: 10000 # milliseconds + callTimeoutMills: 10000 instrumentBase: BTC instrumentQuote: USD + quoteTokenAddress: 0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0 strategy: normal settlementAsset: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede strategyDetails: diff --git a/config/config_docker.yaml b/config/config_capsule.yaml similarity index 94% rename from config/config_docker.yaml rename to config/config_capsule.yaml index 2e20d13..63de503 100644 --- a/config/config_docker.yaml +++ b/config/config_capsule.yaml @@ -16,7 +16,6 @@ token: ethereumAPIAddress: https://host.docker.internal:8545 erc20BridgeAddress: 0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54 stakingBridgeAddress: 0x9135f5afd6F055e731bca2348429482eE614CFfA - erc20TokenAddress: 0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0 vegaTokenAddress: 0x67175Da1D5e966e40D11c4B2519392B2058373de contractOwnerAddress: 0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F contractOwnerPrivateKey: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 @@ -26,10 +25,10 @@ locations: - https://host.docker.internal:3007 bots: - name: w00 - connectTimeout: 30000 # milliseconds - callTimeout: 10000 # milliseconds + callTimeoutMills: 10000 instrumentBase: BTC instrumentQuote: USD + quoteTokenAddress: 0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0 strategy: normal settlementAsset: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede strategyDetails: diff --git a/node/datanode.go b/node/datanode.go index cf6b3b4..3b38db8 100644 --- a/node/datanode.go +++ b/node/datanode.go @@ -27,10 +27,10 @@ type DataNode struct { } // NewDataNode returns a new node. -func NewDataNode(hosts []string, callTimeout time.Duration) *DataNode { +func NewDataNode(hosts []string, callTimeoutMil int) *DataNode { return &DataNode{ hosts: hosts, - callTimeout: callTimeout, + callTimeout: time.Duration(callTimeoutMil) * time.Millisecond, log: log.WithFields(log.Fields{"service": "DataNode"}), } } @@ -281,29 +281,6 @@ func (n *DataNode) PositionsByParty(req *dataapipb.PositionsByPartyRequest) (res return } -// PositionsSubscribe opens a stream. -func (n *DataNode) PositionsSubscribe(req *dataapipb.PositionsSubscribeRequest) (client dataapipb.TradingDataService_PositionsSubscribeClient, err error) { - msg := "gRPC call failed: PositionsSubscribe: %w" - if n == nil { - err = fmt.Errorf(msg, e.ErrNil) - return - } - - if n.conn.GetState() != connectivity.Ready { - err = fmt.Errorf(msg, e.ErrConnectionNotReady) - return - } - - c := dataapipb.NewTradingDataServiceClient(n.conn) - // no timeout on streams - client, err = c.PositionsSubscribe(context.Background(), req) - if err != nil { - err = fmt.Errorf(msg, e.ErrorDetail(err)) - return - } - return -} - // AssetByID returns the specified asset. func (n *DataNode) AssetByID(req *dataapipb.AssetByIDRequest) (response *dataapipb.AssetByIDResponse, err error) { msg := "gRPC call failed (data-node): AssetByID: %w" diff --git a/token/service.go b/token/service.go index 5a1a1db..92cae15 100644 --- a/token/service.go +++ b/token/service.go @@ -27,7 +27,7 @@ type Service struct { log *log.Entry } -func NewService(conf *config.TokenConfig, vegaPubKey string) (*Service, error) { +func NewService(conf *config.TokenConfig, baseTokenAddress, vegaPubKey string) (*Service, error) { ctx := context.Background() client, err := vgethereum.NewClient(ctx, conf.EthereumAPIAddress, 1440) @@ -40,7 +40,7 @@ func NewService(conf *config.TokenConfig, vegaPubKey string) (*Service, error) { vegaPubKey: vegaPubKey, erc20BridgeAddress: common.HexToAddress(conf.Erc20BridgeAddress), stakingBridgeAddress: common.HexToAddress(conf.StakingBridgeAddress), - erc20TokenAddress: common.HexToAddress(conf.ERC20TokenAddress), + erc20TokenAddress: common.HexToAddress(baseTokenAddress), vegaTokenAddress: common.HexToAddress(conf.VegaTokenAddress), contractOwnerAddress: common.HexToAddress(conf.ContractOwnerAddress), contractOwnerPrivateKey: conf.ContractOwnerPrivateKey, From f4d74917419e8ae68ccb52e089b340d60bb87c0b Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Wed, 3 Aug 2022 15:45:07 +0100 Subject: [PATCH 02/18] chore: [WIP] Improve logging, overall fixes and improvements --- bot/normal/interfaces.go | 1 + bot/normal/market.go | 16 +- bot/normal/normal.go | 59 ++++++- bot/normal/position_management.go | 210 ++++++++++++++++--------- bot/normal/price_steering.go | 31 ++-- config/config.go | 10 +- config/config.yaml | 6 +- data/datarequests.go | 67 ++++---- data/event_process.go | 10 +- data/interfaces.go | 3 +- data/store.go | 17 +- data/streamingdata.go | 20 +-- node/datanode.go | 79 ++++------ token/{service.go => erc20_service.go} | 25 ++- types/num/int.go | 15 +- types/num/uint.go | 6 + 16 files changed, 330 insertions(+), 245 deletions(-) rename token/{service.go => erc20_service.go} (91%) diff --git a/bot/normal/interfaces.go b/bot/normal/interfaces.go index d4a5489..fe2e2d0 100644 --- a/bot/normal/interfaces.go +++ b/bot/normal/interfaces.go @@ -44,6 +44,7 @@ type dataStore interface { MarkPrice() *num.Uint OpenVolume() int64 } + type marketStream interface { WaitForStakeLinking() error WaitForProposalID() (string, error) diff --git a/bot/normal/market.go b/bot/normal/market.go index bbba201..9a29d8d 100644 --- a/bot/normal/market.go +++ b/bot/normal/market.go @@ -196,7 +196,8 @@ func (b *bot) submitOrder( side vega.Side, tif vega.Order_TimeInForce, orderType vega.Order_Type, - reference string, + reference, + from string, secondsFromNow int64, ) error { cmd := &walletpb.SubmitTransactionRequest_OrderSubmission{ @@ -217,9 +218,9 @@ func (b *bot) submitOrder( "reference": reference, "size": size, "side": side, - "price": price, + "price": price.String(), "tif": tif.String(), - }).Debug("Submitting order") + }).Debugf("%s: Submitting order", from) if tif == vega.Order_TIME_IN_FORCE_GTT { cmd.OrderSubmission.ExpiresAt = time.Now().UnixNano() + (secondsFromNow * 1000000000) @@ -241,8 +242,8 @@ func (b *bot) submitOrder( return nil } -func (b *bot) seedOrders(ctx context.Context) error { - b.log.Debug("Seeding orders") +func (b *bot) seedOrders(ctx context.Context, from string) error { + b.log.Debugf("%s: Seeding orders", from) externalPrice, err := b.getExternalPrice() if err != nil { @@ -273,6 +274,7 @@ func (b *bot) seedOrders(ctx context.Context) error { tif, vega.Order_TYPE_LIMIT, "MarketCreation", + from, int64(b.config.StrategyDetails.PosManagementFraction), ); err != nil { return fmt.Errorf("failed to create seed order: %w", err) @@ -285,7 +287,7 @@ func (b *bot) seedOrders(ctx context.Context) error { } } - b.log.Debug("Seeding orders finished") + b.log.Debugf("%s: Seeding orders finished", from) return nil } @@ -342,7 +344,7 @@ func (b *bot) getExampleMarket() *vega.NewMarket { func (b *bot) getExampleProduct() *vega.InstrumentConfiguration_Future { return &vega.InstrumentConfiguration_Future{ Future: &vega.FutureProduct{ - SettlementAsset: b.config.SettlementAsset, + SettlementAsset: b.config.SettlementAssetID, QuoteName: fmt.Sprintf("%s%s", b.config.InstrumentBase, b.config.InstrumentQuote), OracleSpecForSettlementPrice: &oraclesv1.OracleSpecConfiguration{ PubKeys: []string{"0xDEADBEEF"}, diff --git a/bot/normal/normal.go b/bot/normal/normal.go index 48c85f1..4070607 100644 --- a/bot/normal/normal.go +++ b/bot/normal/normal.go @@ -4,6 +4,8 @@ import ( "context" "encoding/json" "fmt" + "path" + "runtime" "strings" "sync" @@ -68,16 +70,23 @@ func New(botConf config.BotConfig, locations []string, seedConf *config.TokenCon // Start starts the liquidity bot goroutine(s). func (b *bot) Start() error { + setupLogger(false) // TODO: pretty from config? + dataNode := node.NewDataNode( b.locations, b.config.CallTimeoutMills, ) - dataNode.DialConnection(context.Background()) // blocking + b.log.WithFields( + log.Fields{ + "hosts": b.locations, + }).Debug("Attempting to connect to Vega gRPC node...") + + dataNode.MustDialConnection(context.Background()) // blocking b.node = dataNode b.log = b.log.WithFields(log.Fields{"node": b.node.Target()}) - b.log.Debug("Connected to Vega gRPC node") + b.log.Info("Connected to Vega gRPC node") err := b.setupWallet() if err != nil { @@ -88,7 +97,22 @@ func (b *bot) Start() error { b.marketStream = data.NewMarketStream(dataNode, b.walletPubKey, pauseCh) - b.tokens, err = token.NewService(b.tokenConfig, b.config.QuoteTokenAddress, b.walletPubKey) + assetResponse, err := b.node.AssetByID(&dataapipb.AssetByIDRequest{ + Id: b.config.QuoteTokenID, + }) + if err != nil { + return fmt.Errorf("failed to get asset info: %w", err) + } + + erc20 := assetResponse.Asset.Details.GetErc20() + + quoteTokenAddress := b.config.QuoteTokenID + + if erc20 != nil { + quoteTokenAddress = erc20.ContractAddress + } + + b.tokens, err = token.NewService(b.tokenConfig, quoteTokenAddress, b.walletPubKey) if err != nil { return fmt.Errorf("failed to create token service: %w", err) } @@ -105,14 +129,15 @@ func (b *bot) Start() error { }).Info("Fetched market info") // Use the settlementAssetID to lookup the settlement ethereum address - assetResponse, err := b.node.AssetByID(&dataapipb.AssetByIDRequest{Id: b.settlementAssetID}) + assetResponse, err = b.node.AssetByID(&dataapipb.AssetByIDRequest{Id: b.settlementAssetID}) if err != nil { return fmt.Errorf("unable to look up asset details for %s: %w", b.settlementAssetID, err) } - erc20 := assetResponse.Asset.Details.GetErc20() - if erc20 != nil { + if erc20 := assetResponse.Asset.Details.GetErc20(); erc20 != nil { b.settlementAssetAddress = erc20.ContractAddress + } else { + b.settlementAssetAddress = b.settlementAssetID } store := data.NewStore() @@ -138,6 +163,24 @@ func (b *bot) Start() error { return nil } +func setupLogger(pretty bool) { + log.SetReportCaller(true) + log.SetFormatter(&log.JSONFormatter{ + CallerPrettyfier: func(f *runtime.Frame) (string, string) { + filename := path.Base(f.File) + function := strings.ReplaceAll(f.Function, "code.vegaprotocol.io/", "") + idx := strings.Index(function, ".") + function = fmt.Sprintf("%s/%s/%s():%d", function[:idx], filename, function[idx+1:], f.Line) + return function, "" + }, + PrettyPrint: pretty, + DataKey: "_vals", + FieldMap: log.FieldMap{ + log.FieldKeyMsg: "_msg", + }, + }) +} + func (b *bot) pauseChannel() chan types.PauseSignal { in := make(chan types.PauseSignal) go func() { @@ -212,13 +255,13 @@ func (b *bot) setupWallet() error { if err = b.walletClient.CreateWallet(ctx, b.config.Name, walletPassphrase); err != nil { return fmt.Errorf("failed to create wallet: %w", err) } - b.log.Debug("Created and logged into wallet") + b.log.Info("Created and logged into wallet") } else { return fmt.Errorf("failed to log into wallet: %w", err) } } - b.log.Debug("Logged into wallet") + b.log.Info("Logged into wallet") if b.walletPubKey == "" { publicKeys, err := b.walletClient.ListPublicKeys(ctx) diff --git a/bot/normal/position_management.go b/bot/normal/position_management.go index 7f8ee22..d6b9447 100644 --- a/bot/normal/position_management.go +++ b/bot/normal/position_management.go @@ -14,13 +14,15 @@ import ( log "github.com/sirupsen/logrus" "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/liqbot/util" ) +// TODO: maybe after staking, deposit back the same amount as staked. func (b *bot) runPositionManagement(ctx context.Context) { - defer b.log.Warning("Position management stopped") + defer b.log.Warning("PositionManagement: Stopped") if err := b.prePositionManagement(ctx); err != nil { - b.log.WithFields(log.Fields{"error": err.Error()}).Warning("Failed to init position management") + b.log.WithFields(log.Fields{"error": err.Error()}).Error("PositionManagement: Failed to initialize") return } @@ -30,15 +32,15 @@ func (b *bot) runPositionManagement(ctx context.Context) { for { select { case <-b.pausePosMgmt: - b.log.Warning("Position management paused") + b.log.Warning("PositionManagement: Paused") <-b.pausePosMgmt - b.log.Info("Position management resumed") + b.log.Info("PositionManagement: Resumed") case <-b.stopPosMgmt: return case <-ctx.Done(): b.log.WithFields(log.Fields{ "error": ctx.Err(), - }).Warning("Stopped by context") + }).Warning("PositionManagement: Stopped by context") return default: err := doze(sleepTime, b.stopPosMgmt) @@ -49,89 +51,119 @@ func (b *bot) runPositionManagement(ctx context.Context) { // Only update liquidity and position if we are not in auction if !b.canPlaceOrders() { if err := b.ensureCommitmentAmount(ctx); err != nil { - b.log.WithFields(log.Fields{"error": err.Error()}).Warning("Failed to update commitment amount") + b.log.WithFields(log.Fields{"error": err.Error()}).Warning("PositionManagement: Failed to update commitment amount") } if err = b.placeAuctionOrders(ctx); err != nil { - b.log.WithFields(log.Fields{"error": err.Error()}).Warning("Failed to place auction orders") + b.log.WithFields(log.Fields{"error": err.Error()}).Warning("PositionManagement: Failed to place auction orders") } continue } previousOpenVolume, err = b.manageDirection(ctx, previousOpenVolume) if err != nil { - b.log.WithFields(log.Fields{"error": err.Error()}).Warning("Failed to change LP direction") + b.log.WithFields(log.Fields{"error": err.Error()}).Warning("PositionManagement: Failed to change LP direction") } if err = b.managePosition(ctx); err != nil { - b.log.WithFields(log.Fields{"error": err.Error()}).Warning("Failed to manage position") + b.log.WithFields(log.Fields{"error": err.Error()}).Warning("PositionManagement: Failed to manage position") } } } } func (b *bot) ensureCommitmentAmount(ctx context.Context) error { - suppliedStake := b.data.SuppliedStake() - targetStake := b.data.TargetStake() - - if suppliedStake.IsZero() { - return fmt.Errorf("no stake supplied") + requiredCommitment, err := b.getRequiredCommitment() + if err != nil { + return fmt.Errorf("failed to get new commitment amount: %w", err) } - if suppliedStake.GT(targetStake) { + if requiredCommitment.IsZero() { return nil } - newCommitmentAmount := num.Zero().Mul(targetStake, num.NewUint(2)) - balanceTotal := b.data.Balance().Total() - b.log.WithFields( log.Fields{ - "suppliedStake": suppliedStake, - "targetStake": targetStake, - "balanceTotal": balanceTotal, - "newCommitment": newCommitmentAmount, + "newCommitment": requiredCommitment.String(), }, - ).Debug("Supplied stake is less than target stake, increasing commitment amount...") - - if dx := num.Zero().Sub(newCommitmentAmount, balanceTotal); dx.GT(num.Zero()) { - depositAmount := num.Zero().Mul(dx, num.NewUint(2)) - - b.log.WithFields( - log.Fields{ - "balanceTotal": balanceTotal, - "newCommitment": newCommitmentAmount, - "delta": dx, - "depositAmount": depositAmount, - }).Debug("Account balance is less than new commitment amount, depositing...") - - if err := b.tokens.Deposit(ctx, depositAmount); err != nil { - // "VM Exception while processing transaction: revert" could mean amount is too high - return fmt.Errorf("failed to deposit tokens: %w", err) - } + ).Debug("PositionManagement: Supplied stake is less than target stake, increasing commitment amount...") - b.log.Debug("Waiting for deposit to be finalized...") - - if err := b.dataStream.WaitForDepositFinalize(depositAmount); err != nil { - return fmt.Errorf("failed to finalize deposit: %w", err) - } + if err = b.ensureBalance(ctx, requiredCommitment); err != nil { + return fmt.Errorf("failed to ensure balance: %w", err) } buys, sells, _ := b.getShape() b.log.WithFields( log.Fields{ - "newCommitment": newCommitmentAmount, + "newCommitment": requiredCommitment.String(), }, - ).Debug("Sending new commitment amount...") + ).Debug("PositionManagement: Sending new commitment amount...") - if err := b.sendLiquidityProvisionAmendment(ctx, newCommitmentAmount, buys, sells); err != nil { + if err = b.sendLiquidityProvisionAmendment(ctx, requiredCommitment, buys, sells); err != nil { return fmt.Errorf("failed to update commitment amount: %w", err) } return nil } +func (b *bot) getRequiredCommitment() (*num.Uint, error) { + suppliedStake := b.data.SuppliedStake().Clone() + targetStake := b.data.TargetStake().Clone() + + b.log.WithFields(log.Fields{ + "suppliedStake": suppliedStake.String(), + "targetStake": targetStake.String(), + }).Debug("PositionManagement: Checking for required commitment") + + if targetStake.IsZero() { + var err error + targetStake, err = util.ConvertUint256(b.config.StrategyDetails.CommitmentAmount) + if err != nil { + return nil, fmt.Errorf("failed to convert commitment amount: %w", err) + } + } + + dx := suppliedStake.Int().Sub(targetStake.Int()) + + if dx.IsPositive() { + return num.Zero(), nil + } + + return num.Zero().Add(targetStake, dx.Uint()), nil +} + +func (b *bot) ensureBalance(ctx context.Context, targetAmount *num.Uint) error { + balanceTotal := b.data.Balance().Total() // TODO: should it be total balance? + dx := balanceTotal.Int().Sub(targetAmount.Int()) + + if dx.IsPositive() { + return nil + } + + depositAmount := num.Zero().Add(targetAmount, dx.Uint()) + + b.log.WithFields( + log.Fields{ + "balanceTotal": balanceTotal.String(), + "targetAmount": targetAmount.String(), + "delta": dx.String(), + "depositAmount": depositAmount.String(), + }).Debug("PositionManagement: Account balance is less than target amount, depositing...") + + if err := b.tokens.Deposit(ctx, depositAmount); err != nil { + // "VM Exception while processing transaction: revert" could mean amount is too high + return fmt.Errorf("failed to deposit tokens: %w", err) + } + + b.log.Debug("PositionManagement: Waiting for deposit to be finalized...") + + if err := b.dataStream.WaitForDepositFinalize(depositAmount); err != nil { + return fmt.Errorf("failed to finalize deposit: %w", err) + } + return nil +} + func (b *bot) manageDirection(ctx context.Context, previousOpenVolume int64) (int64, error) { openVolume := b.data.OpenVolume() buyShape, sellShape, shape := b.getShape() @@ -140,18 +172,25 @@ func (b *bot) manageDirection(ctx context.Context, previousOpenVolume int64) (in "openVolume": b.data.OpenVolume(), "previousOpenVolume": previousOpenVolume, "shape": shape, - }).Debug("Checking for direction change") + }).Debug("PositionManagement: Checking for direction change") // If we flipped then send the new LP order if !b.shouldAmend(openVolume, previousOpenVolume) { return openVolume, nil } - b.log.WithFields(log.Fields{"shape": shape}).Debug("Flipping LP direction") + b.log.WithFields(log.Fields{"shape": shape}).Debug("PositionManagement: Flipping LP direction") - commitment := b.data.Balance().Total() + commitment, err := b.getRequiredCommitment() + if err != nil { + return openVolume, fmt.Errorf("failed to get required commitment amount: %w", err) + } + + if err = b.ensureBalance(ctx, commitment); err != nil { + return openVolume, fmt.Errorf("failed to ensure balance: %w", err) + } - if err := b.sendLiquidityProvisionAmendment(ctx, commitment, buyShape, sellShape); err != nil { + if err = b.sendLiquidityProvisionAmendment(ctx, commitment, buyShape, sellShape); err != nil { return openVolume, fmt.Errorf("failed to send liquidity provision amendment: %w", err) } @@ -164,17 +203,20 @@ func (b *bot) shouldAmend(openVolume, previousOpenVolume int64) bool { func (b *bot) managePosition(ctx context.Context) error { size, side, shouldPlace := b.checkPosition() + b.log.WithFields(log.Fields{ + "currentPrice": b.data.MarkPrice().String(), + "balanceGeneral": b.data.Balance().General.String(), + "balanceMargin": b.data.Balance().Margin.String(), + "openVolume": b.data.OpenVolume(), + "size": size, + "side": side, + "shouldPlace": shouldPlace, + }).Debug("PositionManagement: Checking for position management") + if !shouldPlace { return nil } - b.log.WithFields(log.Fields{ - "currentPrice": b.data.MarkPrice(), - "balanceGeneral": b.data.Balance().General, - "balanceMargin": b.data.Balance().Margin, - "openVolume": b.data.OpenVolume(), - }).Debug("Position management info") - if err := b.submitOrder( ctx, size, @@ -183,6 +225,7 @@ func (b *bot) managePosition(ctx context.Context) error { vega.Order_TIME_IN_FORCE_IOC, vega.Order_TYPE_MARKET, "PosManagement", + "PositionManagement", 0, ); err != nil { return fmt.Errorf("failed to place order: %w", err) @@ -205,14 +248,19 @@ func (b *bot) sendLiquidityProvision(ctx context.Context, commitment *num.Uint, }, } + b.log.WithFields(log.Fields{ + "commitment": commitment.String(), + "balanceTotal": b.data.Balance().Total().String(), + }).Debug("PositionManagement: Submitting LiquidityProvisionSubmission...") + if err := b.walletClient.SignTx(ctx, submitTxReq); err != nil { return fmt.Errorf("failed to submit LiquidityProvisionSubmission: %w", err) } b.log.WithFields(log.Fields{ - "commitment": commitment, - "balanceTotal": b.data.Balance().Total(), - }).Debug("Submitted LiquidityProvisionSubmission") + "commitment": commitment.String(), + "balanceTotal": b.data.Balance().Total().String(), + }).Debug("PositionManagement: Submitted LiquidityProvisionSubmission") return nil } @@ -240,8 +288,8 @@ func (b *bot) sendLiquidityProvisionAmendment(ctx context.Context, commitment *n } b.log.WithFields(log.Fields{ - "commitment": commitment, - }).Debug("Submitted LiquidityProvisionAmendment") + "commitment": commitment.String(), + }).Debug("PositionManagement: Submitted LiquidityProvisionAmendment") return nil } @@ -261,8 +309,8 @@ func (b *bot) sendLiquidityProvisionCancellation(ctx context.Context) error { b.log.WithFields(log.Fields{ "commitment": "0", - "balanceTotal": b.data.Balance().Total(), - }).Debug("Submitted LiquidityProvisionAmendment") + "balanceTotal": b.data.Balance().Total().String(), + }).Debug("PositionManagement: Submitted LiquidityProvisionAmendment") return nil } @@ -295,10 +343,22 @@ func (b *bot) prePositionManagement(ctx context.Context) error { return fmt.Errorf("failed initial margin check: %w", err) } - commitment := b.data.Balance().Total() + commitment, err := b.getRequiredCommitment() + if err != nil { + return fmt.Errorf("failed to get required commitment: %w", err) + } + + // TODO: is this ok? + if commitment.IsZero() { + return nil + } + + if err = b.ensureBalance(ctx, commitment); err != nil { + return fmt.Errorf("failed to ensure balance: %w", err) + } // Submit LP order to market. - if err := b.sendLiquidityProvision(ctx, commitment, buyShape, sellShape); err != nil { + if err = b.sendLiquidityProvision(ctx, commitment, buyShape, sellShape); err != nil { return fmt.Errorf("failed to send liquidity provision order: %w", err) } @@ -347,11 +407,11 @@ func (b *bot) checkInitialMargin(buyShape, sellShape []*vega.LiquidityOrder) err } b.log.WithFields(log.Fields{ - "available": avail, - "cost": shapeMarginCost, - "missing": num.Zero().Sub(avail, shapeMarginCost), + "available": avail.String(), + "cost": shapeMarginCost.String(), + "missing": num.Zero().Sub(avail, shapeMarginCost).String(), "missingPercent": missingPercent, - }).Error("Not enough collateral to safely keep orders up given current price, risk parameters and supplied default shapes.") + }).Error("PositionManagement: Not enough collateral to safely keep orders up given current price, risk parameters and supplied default shapes.") return errors.New("not enough collateral") } @@ -361,11 +421,11 @@ func (b *bot) checkInitialMargin(buyShape, sellShape []*vega.LiquidityOrder) err func (b *bot) placeAuctionOrders(ctx context.Context) error { // Check if we have a currentPrice we can use if b.data.MarkPrice().IsZero() { - b.log.Debug("No current price to place auction orders") + b.log.Debug("PositionManagement: No current price to place auction orders") return nil } - b.log.WithFields(log.Fields{"currentPrice": b.data.MarkPrice()}).Debug("Placing auction orders") + b.log.WithFields(log.Fields{"currentPrice": b.data.MarkPrice().String()}).Debug("PositionManagement: Placing auction orders") // Place the random orders split into totalVolume := num.Zero() @@ -389,7 +449,7 @@ func (b *bot) placeAuctionOrders(ctx context.Context) error { orderType := vega.Order_TYPE_LIMIT ref := "AuctionOrder" - if err := b.submitOrder(ctx, size.Uint64(), price, side, tif, orderType, ref, 330); err != nil { + if err := b.submitOrder(ctx, size.Uint64(), price, side, tif, orderType, ref, "PositionManagement", 330); err != nil { // We failed to send an order so stop trying to send anymore return fmt.Errorf("failed to send auction order: %w", err) } @@ -397,7 +457,7 @@ func (b *bot) placeAuctionOrders(ctx context.Context) error { totalVolume = num.Zero().Add(totalVolume, size) } - b.log.WithFields(log.Fields{"totalVolume": totalVolume}).Debug("Placed auction orders") + b.log.WithFields(log.Fields{"totalVolume": totalVolume.String()}).Debug("PositionManagement: Placed auction orders") return nil } diff --git a/bot/normal/price_steering.go b/bot/normal/price_steering.go index 047eb76..80cdbb6 100644 --- a/bot/normal/price_steering.go +++ b/bot/normal/price_steering.go @@ -15,15 +15,15 @@ import ( ) func (b *bot) runPriceSteering(ctx context.Context) { - defer b.log.Warning("Price steering stopped") + defer b.log.Warning("PriceSteering: Stopped") if !b.canPlaceOrders() { b.log.WithFields(log.Fields{ "PriceSteerOrderScale": b.config.StrategyDetails.PriceSteerOrderScale, - }).Debug("Price steering: Cannot place orders") + }).Debug("PriceSteering: Cannot place orders") - if err := b.seedOrders(ctx); err != nil { - b.log.WithFields(log.Fields{"error": err.Error()}).Error("failed to seed orders") + if err := b.seedOrders(ctx, "PriceSteering"); err != nil { + b.log.WithFields(log.Fields{"error": err.Error()}).Error("PriceSteering: Failed to seed orders") return } } @@ -33,7 +33,7 @@ func (b *bot) runPriceSteering(ctx context.Context) { for { select { case <-b.pausePriceSteer: - b.log.Warning("Price steering paused") + b.log.Warning("PriceSteering: Paused") <-b.pausePriceSteer b.log.Info("Price steering resumed") case <-b.stopPriceSteer: @@ -41,7 +41,7 @@ func (b *bot) runPriceSteering(ctx context.Context) { case <-ctx.Done(): b.log.WithFields(log.Fields{ "error": ctx.Err(), - }).Warning("Stopped by context") + }).Warning("PriceSteering: Stopped by context") return default: if err := doze(time.Duration(sleepTime)*time.Millisecond, b.stopPriceSteer); err != nil { @@ -53,7 +53,7 @@ func (b *bot) runPriceSteering(ctx context.Context) { b.log.WithFields(log.Fields{ "error": err.Error(), "sleepTime": sleepTime, - }).Warning("Error during price steering") + }).Warning("PriceSteering: Error during price steering") } sleepTime = b.moveSteerSleepTime(sleepTime, err != nil) @@ -78,23 +78,23 @@ func (b *bot) steerPrice(ctx context.Context) error { } b.log.WithFields(log.Fields{ - "currentPrice": staticMidPrice, - "externalPrice": externalPrice, - "diff": currentDiff, - "currentDiffFraction": currentDiffFraction, - "minPriceSteerFraction": minPriceSteerFraction, + "currentPrice": staticMidPrice.String(), + "externalPrice": externalPrice.String(), + "diff": currentDiff.String(), + "currentDiffFraction": currentDiffFraction.String(), + "minPriceSteerFraction": minPriceSteerFraction.String(), "shouldMove": map[vega.Side]string{vega.Side_SIDE_BUY: "UP", vega.Side_SIDE_SELL: "DN"}[side], - }).Debug("Steering info") + }).Debug("PriceSteering: Steering info") if !currentDiffFraction.GreaterThan(minPriceSteerFraction) { - b.log.Debug("Current difference is not higher than minimum price steering fraction") + b.log.Debug("PriceSteering: Current difference is not higher than minimum price steering fraction") return nil } // find out what price and size of the order we should place price, size, err := b.getRealisticOrderDetails(externalPrice) if err != nil { - b.log.WithFields(log.Fields{"error": err.Error()}).Fatal("Unable to get realistic order details for price steering") + b.log.WithFields(log.Fields{"error": err.Error()}).Fatal("PriceSteering: Unable to get realistic order details for price steering") } if err = b.submitOrder( @@ -105,6 +105,7 @@ func (b *bot) steerPrice(ctx context.Context) error { vega.Order_TIME_IN_FORCE_GTT, vega.Order_TYPE_LIMIT, "PriceSteeringOrder", + "PriceSteering", int64(b.config.StrategyDetails.LimitOrderDistributionParams.GttLength)); err != nil { return fmt.Errorf("failed to submit order: %w", err) } diff --git a/config/config.go b/config/config.go index b54be14..81a3bd3 100644 --- a/config/config.go +++ b/config/config.go @@ -128,14 +128,14 @@ type BotConfig struct { // InstrumentQuote is the quote asset of the instrument. InstrumentQuote string `yaml:"instrumentQuote"` - // QuoteTokenAddress is the address of the base token. - QuoteTokenAddress string `yaml:"quoteTokenAddress"` + // QuoteTokenID is the id of the base token. + QuoteTokenID string `yaml:"quoteTokenID"` // Strategy specifies which algorithm the bot is to use. Strategy string `yaml:"strategy"` - // SettlementAsset is the asset used for settlement. - SettlementAsset string `yaml:"settlementAsset"` + // SettlementAssetID is the asset used for settlement. + SettlementAssetID string `yaml:"settlementAssetID"` // StrategyDetails contains the parameters needed by the strategy algorithm. StrategyDetails Strategy `yaml:"strategyDetails"` @@ -153,4 +153,6 @@ type TokenConfig struct { VegaTokenAddress string `yaml:"vegaTokenAddress"` ContractOwnerAddress string `yaml:"contractOwnerAddress"` ContractOwnerPrivateKey string `yaml:"contractOwnerPrivateKey"` + ChainID int64 `yaml:"chainID"` + SyncTimeoutSec int `yaml:"syncTimeoutSec"` } diff --git a/config/config.yaml b/config/config.yaml index c777ab4..08af52a 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -19,6 +19,8 @@ token: vegaTokenAddress: 0x67175Da1D5e966e40D11c4B2519392B2058373de contractOwnerAddress: 0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F contractOwnerPrivateKey: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 + chainID: 1440 + syncTimeoutSec: 5 locations: - localhost:3007 - localhost:3017 @@ -28,9 +30,9 @@ bots: callTimeoutMills: 10000 instrumentBase: BTC instrumentQuote: USD - quoteTokenAddress: 0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0 + quoteTokenID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede # 0x3773A5c7aFF77e014cBF067dd31801b4C6dc4136 + settlementAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede strategy: normal - settlementAsset: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede strategyDetails: expectedMarkPrice: 0 auctionVolume: 100 diff --git a/data/datarequests.go b/data/datarequests.go index 685e801..9f704d3 100644 --- a/data/datarequests.go +++ b/data/datarequests.go @@ -4,16 +4,16 @@ import ( "errors" "fmt" - "code.vegaprotocol.io/liqbot/types/num" - "code.vegaprotocol.io/liqbot/util" - dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" "code.vegaprotocol.io/protos/vega" log "github.com/sirupsen/logrus" + + "code.vegaprotocol.io/liqbot/types" + "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/liqbot/util" ) func (d *data) getOpenVolume() (int64, error) { - // Position positions, err := d.getPositions() if err != nil { return 0, fmt.Errorf("failed to get position details: %w", err) @@ -31,56 +31,43 @@ func (d *data) getOpenVolume() (int64, error) { return openVolume, nil } -func (d *data) getAccount(typ vega.AccountType) (*num.Uint, error) { +func (d *data) getAccount() (*types.Balance, error) { response, err := d.node.PartyAccounts(&dataapipb.PartyAccountsRequest{ PartyId: d.walletPubKey, Asset: d.settlementAssetID, - Type: typ, }) if err != nil { return nil, err } - if len(response.Accounts) == 0 { - d.log.WithFields(log.Fields{ - "type": typ, - }).Debug("zero accounts for party") - return num.Zero(), nil - } - if len(response.Accounts) > 1 { - return nil, fmt.Errorf("too many accounts for party: %d", len(response.Accounts)) - } - return util.ConvertUint256(response.Accounts[0].Balance) -} - -// getAccountGeneral get this bot's general account balance. -func (d *data) getAccountGeneral() (*num.Uint, error) { - balance, err := d.getAccount(vega.AccountType_ACCOUNT_TYPE_GENERAL) - if err != nil { - return nil, fmt.Errorf("failed to get general account balance: %w", err) + balance := &types.Balance{ + General: &num.Uint{}, + Margin: &num.Uint{}, + Bond: &num.Uint{}, } - return balance, nil -} - -// getAccountMargin get this bot's margin account balance. -func (d *data) getAccountMargin() (*num.Uint, error) { - balanceMargin, err := d.getAccount(vega.AccountType_ACCOUNT_TYPE_MARGIN) - if err != nil { - return nil, fmt.Errorf("failed to get margin account balance: %w", err) + if len(response.Accounts) == 0 { + d.log.WithFields(log.Fields{ + "party": d.walletPubKey, + }).Warning("Party has no accounts") + return balance, nil } - return balanceMargin, nil -} - -// getAccountBond get this bot's bond account balance. -func (d *data) getAccountBond() (*num.Uint, error) { - balanceBond, err := d.getAccount(vega.AccountType_ACCOUNT_TYPE_BOND) - if err != nil { - return nil, fmt.Errorf("failed to get bond account balance: %w", err) + for _, account := range response.Accounts { + switch account.Type { + case vega.AccountType_ACCOUNT_TYPE_GENERAL: + balance.General, err = util.ConvertUint256(account.Balance) + case vega.AccountType_ACCOUNT_TYPE_MARGIN: + balance.Margin, err = util.ConvertUint256(account.Balance) + case vega.AccountType_ACCOUNT_TYPE_BOND: + balance.Bond, err = util.ConvertUint256(account.Balance) + } + if err != nil { + return nil, fmt.Errorf("failed to convert account balance: %w", err) + } } - return balanceBond, nil + return balance, nil } // getPositions get this bot's positions. diff --git a/data/event_process.go b/data/event_process.go index d0978db..1d23125 100644 --- a/data/event_process.go +++ b/data/event_process.go @@ -55,7 +55,7 @@ func (b *busEventProcessor) processEvents( b.log.WithFields( log.Fields{ - "error": err, + "error": err.Error(), "name": name, }, ).Warningf("Stream closed, resubscribing...") @@ -69,7 +69,7 @@ func (b *busEventProcessor) processEvents( stop, err = process(rsp) if err != nil { b.log.WithFields(log.Fields{ - "error": err, + "error": err.Error(), "name": name, }).Warning("Unable to process event") } @@ -94,11 +94,11 @@ func (b *busEventProcessor) mustGetStream( if errors.Unwrap(err).Error() == e.ErrConnectionNotReady.Error() { b.log.WithFields(log.Fields{ "name": name, - "error": err, + "error": err.Error(), "attempt": attempt, }).Warning("Node is not ready, reconnecting") - <-b.node.DialConnection(ctx) + b.node.MustDialConnection(ctx) b.log.WithFields(log.Fields{ "name": name, @@ -115,7 +115,7 @@ func (b *busEventProcessor) mustGetStream( b.log.WithFields(log.Fields{ "name": name, - "error": err, + "error": err.Error(), "attempt": attempt, }).Errorf("Failed to subscribe to stream, retrying in %s...", sleepTime) diff --git a/data/interfaces.go b/data/interfaces.go index 780942c..953180f 100644 --- a/data/interfaces.go +++ b/data/interfaces.go @@ -19,11 +19,12 @@ type DataNode interface { PartyAccounts(req *dataapipb.PartyAccountsRequest) (response *dataapipb.PartyAccountsResponse, err error) MarketDataByID(req *dataapipb.MarketDataByIDRequest) (response *dataapipb.MarketDataByIDResponse, err error) PositionsByParty(req *dataapipb.PositionsByPartyRequest) (response *dataapipb.PositionsByPartyResponse, err error) - DialConnection(ctx context.Context) chan struct{} + MustDialConnection(ctx context.Context) } type dataStore interface { balanceSet(typ vega.AccountType, bal *num.Uint) + balanceInit(balance *types.Balance) marketDataSet(marketData *types.MarketData) openVolumeSet(openVolume int64) cache() diff --git a/data/store.go b/data/store.go index 9d033e4..cc2baa2 100644 --- a/data/store.go +++ b/data/store.go @@ -8,8 +8,9 @@ import ( ) type store struct { - balanceGetCh chan balanceGetReq - balanceSetCh chan balanceSetReq + balanceGetCh chan balanceGetReq + balanceInitCh chan balanceInitReq + balanceSetCh chan balanceSetReq marketDataGetCh chan marketDataGetReq marketDataSetCh chan marketDataSetReq @@ -21,6 +22,7 @@ type store struct { func NewStore() *store { return &store{ balanceGetCh: make(chan balanceGetReq), + balanceInitCh: make(chan balanceInitReq), balanceSetCh: make(chan balanceSetReq), marketDataGetCh: make(chan marketDataGetReq), marketDataSetCh: make(chan marketDataSetReq), @@ -33,6 +35,10 @@ type balanceGetReq struct { resp chan *types.Balance } +type balanceInitReq struct { + balance *types.Balance +} + type balanceSetReq struct { typ vega.AccountType balance *num.Uint @@ -110,6 +116,10 @@ func (s *store) OpenVolume() int64 { return <-resp } +func (s *store) balanceInit(balance *types.Balance) { + s.balanceInitCh <- balanceInitReq{balance: balance} +} + func (s *store) balanceSet(typ vega.AccountType, bal *num.Uint) { s.balanceSetCh <- balanceSetReq{typ: typ, balance: bal.Clone()} } @@ -152,6 +162,8 @@ func (s *store) cache() { case vega.AccountType_ACCOUNT_TYPE_BOND: d.balance.Bond = req.balance } + case req := <-s.balanceInitCh: + d.balance = req.balance case req := <-s.marketDataGetCh: if d.marketData != nil { mdVal := *d.marketData @@ -164,7 +176,6 @@ func (s *store) cache() { case req := <-s.openVolumeSetCh: d.openVolume = req.openVolume default: - // Do nothing } } } diff --git a/data/streamingdata.go b/data/streamingdata.go index 209d4b3..e05fb51 100644 --- a/data/streamingdata.go +++ b/data/streamingdata.go @@ -89,26 +89,12 @@ func (d *data) WaitForDepositFinalize(amount *num.Uint) error { func (d *data) setInitialData() error { // Collateral - balanceGeneral, err := d.getAccountGeneral() + balance, err := d.getAccount() if err != nil { return fmt.Errorf("failed to get account general: %w", err) } - d.store.balanceSet(vega.AccountType_ACCOUNT_TYPE_GENERAL, balanceGeneral) - - balanceMargin, err := d.getAccountMargin() - if err != nil { - return fmt.Errorf("failed to get account margin: %w", err) - } - - d.store.balanceSet(vega.AccountType_ACCOUNT_TYPE_MARGIN, balanceMargin) - - balanceBond, err := d.getAccountBond() - if err != nil { - return fmt.Errorf("failed to get account bond: %w", err) - } - - d.store.balanceSet(vega.AccountType_ACCOUNT_TYPE_BOND, balanceBond) + d.store.balanceInit(balance) marketData, err := d.getMarketData() if err != nil { @@ -226,7 +212,7 @@ func (d *data) subscribeToAccountEvents() { bal, err := util.ConvertUint256(acct.Balance) if err != nil { d.log.WithFields(log.Fields{ - "error": err, + "error": err.Error(), "AccountBalance": acct.Balance, }).Warning("processEventBusData: failed to unmarshal uint256: error or overflow") continue diff --git a/node/datanode.go b/node/datanode.go index 3b38db8..761399e 100644 --- a/node/datanode.go +++ b/node/datanode.go @@ -3,12 +3,12 @@ package node import ( "context" "fmt" + "log" "sync" "time" dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" vegaapipb "code.vegaprotocol.io/protos/vega/api/v1" - log "github.com/sirupsen/logrus" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials/insecure" @@ -21,9 +21,9 @@ type DataNode struct { hosts []string // format: host:port callTimeout time.Duration conn *grpc.ClientConn - connecting bool mu sync.RWMutex - log *log.Entry + wg sync.WaitGroup + once sync.Once } // NewDataNode returns a new node. @@ -31,48 +31,36 @@ func NewDataNode(hosts []string, callTimeoutMil int) *DataNode { return &DataNode{ hosts: hosts, callTimeout: time.Duration(callTimeoutMil) * time.Millisecond, - log: log.WithFields(log.Fields{"service": "DataNode"}), } } -func (n *DataNode) DialConnection(ctx context.Context) chan struct{} { +// MustDialConnection tries to establish a connection to one of the nodes from a list of locations. +// It is idempotent, while it each call will block the caller until a connection is established. +func (n *DataNode) MustDialConnection(ctx context.Context) { ctx, cancel := context.WithCancel(ctx) defer cancel() - wg := &sync.WaitGroup{} - wg.Add(len(n.hosts)) - waitCh := make(chan struct{}) - - n.mu.Lock() - if n.connecting { - n.mu.Unlock() - return waitCh - } - - n.log.WithFields( - log.Fields{ - "hosts": n.hosts, - }).Debug("Attempting to connect to DataNode...") + n.once.Do(func() { + n.wg.Add(len(n.hosts)) + + for _, h := range n.hosts { + go func(host string) { + defer func() { + cancel() + n.wg.Done() + }() + n.dialNode(ctx, host) + }(h) + } + }) - n.connecting = true - n.mu.Unlock() + n.wg.Wait() - for _, h := range n.hosts { - go func(host string) { - defer func() { - cancel() - wg.Done() - }() - n.dialNode(ctx, host) - }(h) + n.mu.RLock() + defer n.mu.RUnlock() + if n.conn == nil { + log.Fatalf("Failed to connect to DataNode") } - - wg.Wait() - n.mu.Lock() - n.connecting = false - n.mu.Unlock() - close(waitCh) - return waitCh } func (n *DataNode) dialNode(ctx context.Context, host string) { @@ -83,28 +71,15 @@ func (n *DataNode) dialNode(ctx context.Context, host string) { grpc.WithBlock(), ) if err != nil { - n.mu.RLock() - defer n.mu.RUnlock() - // another goroutine has already established a connection - if n.conn == nil { - n.log.WithFields( - log.Fields{ - "error": err, - "host": host, - }).Error("Failed to connect to data node") + if err != context.Canceled { + log.Printf("Failed to dial node '%s': %s\n", host, err) } return } n.mu.Lock() - defer n.mu.Unlock() n.conn = conn - - n.log.WithFields( - log.Fields{ - "host": host, - "state": n.conn.GetState(), - }).Info("Connected to data node") + n.mu.Unlock() return } diff --git a/token/service.go b/token/erc20_service.go similarity index 91% rename from token/service.go rename to token/erc20_service.go index 92cae15..7b1de4d 100644 --- a/token/service.go +++ b/token/erc20_service.go @@ -4,10 +4,12 @@ import ( "context" "fmt" "math/big" + "time" - vgethereum "code.vegaprotocol.io/shared/libs/ethereum" log "github.com/sirupsen/logrus" + vgethereum "code.vegaprotocol.io/shared/libs/ethereum" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -24,13 +26,19 @@ type Service struct { vegaTokenAddress common.Address contractOwnerAddress common.Address contractOwnerPrivateKey string + syncTimeout *time.Duration log *log.Entry } func NewService(conf *config.TokenConfig, baseTokenAddress, vegaPubKey string) (*Service, error) { ctx := context.Background() - client, err := vgethereum.NewClient(ctx, conf.EthereumAPIAddress, 1440) + var syncTimeout time.Duration + if conf.SyncTimeoutSec != 0 { + syncTimeout = time.Duration(conf.SyncTimeoutSec) * time.Second + } + + client, err := vgethereum.NewClient(ctx, conf.EthereumAPIAddress, conf.ChainID) if err != nil { return nil, fmt.Errorf("failed to create Ethereum client: %w", err) } @@ -44,17 +52,18 @@ func NewService(conf *config.TokenConfig, baseTokenAddress, vegaPubKey string) ( vegaTokenAddress: common.HexToAddress(conf.VegaTokenAddress), contractOwnerAddress: common.HexToAddress(conf.ContractOwnerAddress), contractOwnerPrivateKey: conf.ContractOwnerPrivateKey, - log: log.WithFields(log.Fields{"service": "token"}), + syncTimeout: &syncTimeout, + log: log.WithFields(log.Fields{"service": "Token"}), }, nil } func (s *Service) Stake(ctx context.Context, amount *num.Uint) error { - stakingBridge, err := s.client.NewStakingBridgeSession(ctx, s.contractOwnerPrivateKey, s.stakingBridgeAddress, nil) + stakingBridge, err := s.client.NewStakingBridgeSession(ctx, s.contractOwnerPrivateKey, s.stakingBridgeAddress, s.syncTimeout) if err != nil { return fmt.Errorf("failed to create staking bridge: %w", err) } - vegaToken, err := s.client.NewBaseTokenSession(ctx, s.contractOwnerPrivateKey, s.vegaTokenAddress, nil) + vegaToken, err := s.client.NewBaseTokenSession(ctx, s.contractOwnerPrivateKey, s.vegaTokenAddress, s.syncTimeout) if err != nil { return fmt.Errorf("failed to create vega token: %w", err) } @@ -73,12 +82,12 @@ func (s *Service) Stake(ctx context.Context, amount *num.Uint) error { } func (s *Service) Deposit(ctx context.Context, amount *num.Uint) error { - erc20Token, err := s.client.NewBaseTokenSession(ctx, s.contractOwnerPrivateKey, s.erc20TokenAddress, nil) + erc20Token, err := s.client.NewBaseTokenSession(ctx, s.contractOwnerPrivateKey, s.erc20TokenAddress, s.syncTimeout) if err != nil { return fmt.Errorf("failed to create ERC20 token: %w", err) } - erc20bridge, err := s.client.NewERC20BridgeSession(ctx, s.contractOwnerPrivateKey, s.erc20BridgeAddress, nil) + erc20bridge, err := s.client.NewERC20BridgeSession(ctx, s.contractOwnerPrivateKey, s.erc20BridgeAddress, s.syncTimeout) if err != nil { return fmt.Errorf("failed to create staking bridge: %w", err) } @@ -116,7 +125,7 @@ func (s *Service) mintToken(token token, address common.Address, amount *big.Int "address": address, }).Debug("Minting new token") - if _, err := token.MintSync(address, amount); err != nil { + if _, err = token.MintSync(address, amount); err != nil { return fmt.Errorf("failed to call Mint contract: %w", err) } diff --git a/types/num/int.go b/types/num/int.go index 4de5a08..4b7fb58 100644 --- a/types/num/int.go +++ b/types/num/int.go @@ -10,11 +10,16 @@ type Int struct { // IntFromUint ... func IntFromUint(u *Uint, s bool) *Int { - copy := &Int{ + cp := &Int{ s: s, U: u.Clone(), } - return copy + return cp +} + +// Uint returns a copy of the underlying Uint. +func (i *Int) Uint() *Uint { + return i.U.Clone() } // IsNegative tests if the stored value is negative @@ -190,9 +195,3 @@ func NewInt(val int64) *Int { s: true, } } - -// NewIntFromUint creates a new Int with the value of the -// uint passed as a parameter. -func NewIntFromUint(val *Uint) *Int { - return &Int{U: val, s: true} -} diff --git a/types/num/uint.go b/types/num/uint.go index 2520be4..364ef13 100644 --- a/types/num/uint.go +++ b/types/num/uint.go @@ -1,3 +1,4 @@ +// TODO: shared libs? package num import ( @@ -132,6 +133,11 @@ func (u Uint) BigInt() *big.Int { return u.u.ToBig() } +// Int ... +func (u Uint) Int() *Int { + return IntFromUint(&u, true) +} + // Float64 ... func (u Uint) Float64() float64 { d := DecimalFromUint(&u) From f47dffa0dede7b0eb1ff0850511bea5994880f43 Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Mon, 15 Aug 2022 02:03:16 +0100 Subject: [PATCH 03/18] chore: Improve cache, improve minting fix bugs, add whale service, more refactoring. --- bot/bot.go | 64 +++++--- bot/normal/interfaces.go | 18 +-- bot/normal/market.go | 12 +- bot/normal/normal.go | 193 +++++++++++------------ bot/normal/position_management.go | 79 +++------- bot/normal/price_steering.go | 2 +- config/config.go | 43 ++++-- config/config.yaml | 19 ++- config/config_capsule.yaml | 19 ++- config/config_test.go | 4 + data/datarequests.go | 81 +++++----- data/event_process.go | 7 +- data/interfaces.go | 22 +-- data/store.go | 181 ---------------------- data/streamingdata.go | 168 +++++--------------- data/streamingdeposit.go | 59 +++++++ data/streamingmarket.go | 13 +- go.mod | 8 +- go.sum | 15 +- node/datanode.go | 20 +-- service/service.go | 53 ++++++- token/erc20_service.go | 131 +++++++++------- token/erc20_service_test.go | 45 ++++++ token/faucet_service.go | 61 ++++++++ types/interfaces.go | 30 ++++ types/num/uint.go | 3 + types/store.go | 69 +++++++++ types/store_test.go | 73 +++++++++ types/types.go | 167 ++++++++++++++++++-- wallet/client.go | 12 +- whale/interfaces.go | 35 +++++ whale/service.go | 247 ++++++++++++++++++++++++++++++ 32 files changed, 1278 insertions(+), 675 deletions(-) delete mode 100644 data/store.go create mode 100644 data/streamingdeposit.go create mode 100644 token/erc20_service_test.go create mode 100644 token/faucet_service.go create mode 100644 types/interfaces.go create mode 100644 types/store.go create mode 100644 types/store_test.go create mode 100644 whale/interfaces.go create mode 100644 whale/service.go diff --git a/bot/bot.go b/bot/bot.go index a53fae9..e6a811a 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -2,36 +2,58 @@ package bot import ( "errors" - - ppconfig "code.vegaprotocol.io/priceproxy/config" - ppservice "code.vegaprotocol.io/priceproxy/service" + "fmt" "code.vegaprotocol.io/liqbot/bot/normal" "code.vegaprotocol.io/liqbot/config" + "code.vegaprotocol.io/liqbot/data" + "code.vegaprotocol.io/liqbot/node" + "code.vegaprotocol.io/liqbot/types" + "code.vegaprotocol.io/liqbot/wallet" ) -// Bot is the generic bot interface. -// -//go:generate go run github.com/golang/mock/mockgen -destination mocks/bot_mock.go -package mocks code.vegaprotocol.io/liqbot/bot Bot -type Bot interface { - Start() error - Stop() - GetTraderDetails() string -} - -// PricingEngine is the source of price information from the price proxy. -// -//go:generate go run github.com/golang/mock/mockgen -destination mocks/pricingengine_mock.go -package mocks code.vegaprotocol.io/liqbot/bot PricingEngine -type PricingEngine interface { - GetPrice(pricecfg ppconfig.PriceConfig) (pi ppservice.PriceResponse, err error) -} - // New returns a new Bot instance. -func New(botConf config.BotConfig, locations []string, seedConf *config.TokenConfig, pe PricingEngine, wc normal.WalletClient) (Bot, error) { +func New( + botConf config.BotConfig, + conf config.Config, + pricing types.PricingEngine, + whale types.WhaleService, +) (types.Bot, error) { switch botConf.Strategy { case config.BotStrategyNormal: - return normal.New(botConf, locations, seedConf, pe, wc), nil + bot, err := newNormalBot(botConf, conf, pricing, whale) + if err != nil { + return nil, fmt.Errorf("failed to create normal bot '%s': %w", botConf.Name, err) + } + return bot, nil default: return nil, errors.New("unrecognised bot strategy") } } + +func newNormalBot( + botConf config.BotConfig, + conf config.Config, + pricing types.PricingEngine, + whale types.WhaleService, +) (types.Bot, error) { + dataNode := node.NewDataNode( + conf.Locations, + conf.CallTimeoutMills, + ) + + marketStream := data.NewMarketStream(dataNode) + streamData := data.NewStreamData(dataNode) + botWallet := wallet.NewClient(conf.Wallet.URL) + + return normal.New( + botConf, + conf.VegaAssetID, + dataNode, + marketStream, + streamData, + pricing, + botWallet, + whale, + ), nil +} diff --git a/bot/normal/interfaces.go b/bot/normal/interfaces.go index fe2e2d0..201a84f 100644 --- a/bot/normal/interfaces.go +++ b/bot/normal/interfaces.go @@ -6,15 +6,16 @@ import ( ppconfig "code.vegaprotocol.io/priceproxy/config" ppservice "code.vegaprotocol.io/priceproxy/service" dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" - "code.vegaprotocol.io/protos/vega" "code.vegaprotocol.io/protos/vega/wallet/v1" + "code.vegaprotocol.io/liqbot/data" "code.vegaprotocol.io/liqbot/types" "code.vegaprotocol.io/liqbot/types/num" ) // TradingDataService implements the gRPC service of the same name. type tradingDataService interface { + MustDialConnection(ctx context.Context) Target() string Markets(req *dataapipb.MarketsRequest) (*dataapipb.MarketsResponse, error) // BOT AssetByID(req *dataapipb.AssetByIDRequest) (*dataapipb.AssetByIDResponse, error) // BOT @@ -37,25 +38,20 @@ type WalletClient interface { type dataStore interface { Balance() types.Balance - TradingMode() vega.Market_TradingMode - StaticMidPrice() *num.Uint - TargetStake() *num.Uint - SuppliedStake() *num.Uint - MarkPrice() *num.Uint - OpenVolume() int64 + Market() types.MarketData } type marketStream interface { + Setup(walletPubKey string, pauseCh chan types.PauseSignal) WaitForStakeLinking() error WaitForProposalID() (string, error) WaitForProposalEnacted(pID string) error } type dataStream interface { - WaitForDepositFinalize(amount *num.Uint) error + InitData(pubKey, marketID, settlementAssetID string, pauseCh chan types.PauseSignal) (data.GetDataStore, error) } -type tokenService interface { - Stake(ctx context.Context, amount *num.Uint) error - Deposit(ctx context.Context, amount *num.Uint) error +type whaleService interface { + TopUp(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error } diff --git a/bot/normal/market.go b/bot/normal/market.go index 9a29d8d..22f4d44 100644 --- a/bot/normal/market.go +++ b/bot/normal/market.go @@ -90,11 +90,12 @@ func (b *bot) createMarket(ctx context.Context) (*vega.Market, error) { amount := b.config.StrategyDetails.SeedAmount.Get() - if err := b.tokens.Deposit(ctx, amount); err != nil { + // TODO: is it b.settlementAssetID? + if err := b.whale.TopUp(ctx, b.config.Name, b.walletPubKey, b.settlementAssetID, amount); err != nil { return nil, fmt.Errorf("failed to seed deposit tokens: %w", err) } - if err := b.tokens.Stake(ctx, amount); err != nil { + if err := b.whale.TopUp(ctx, b.config.Name, b.walletPubKey, b.vegaAssetID, amount); err != nil { return nil, fmt.Errorf("failed to seed stake tokens: %w", err) } @@ -200,6 +201,11 @@ func (b *bot) submitOrder( from string, secondsFromNow int64, ) error { + // TODO: is it ok to ensure balance here? + if err := b.ensureBalance(ctx, price, from); err != nil { + return fmt.Errorf("failed to ensure balance: %w", err) + } + cmd := &walletpb.SubmitTransactionRequest_OrderSubmission{ OrderSubmission: &commandspb.OrderSubmission{ MarketId: b.marketID, @@ -292,7 +298,7 @@ func (b *bot) seedOrders(ctx context.Context, from string) error { } func (b *bot) canPlaceOrders() bool { - return b.data.TradingMode() == vega.Market_TRADING_MODE_CONTINUOUS + return b.Market().TradingMode() == vega.Market_TRADING_MODE_CONTINUOUS } func (b *bot) getExampleMarketProposal() *v1.ProposalSubmission { diff --git a/bot/normal/normal.go b/bot/normal/normal.go index 4070607..0cab99a 100644 --- a/bot/normal/normal.go +++ b/bot/normal/normal.go @@ -14,53 +14,62 @@ import ( log "github.com/sirupsen/logrus" "code.vegaprotocol.io/liqbot/config" - "code.vegaprotocol.io/liqbot/data" - "code.vegaprotocol.io/liqbot/node" - "code.vegaprotocol.io/liqbot/token" "code.vegaprotocol.io/liqbot/types" + "code.vegaprotocol.io/liqbot/types/num" ) // bot represents one Normal liquidity bot. type bot struct { pricingEngine PricingEngine node tradingDataService - locations []string - data dataStore - marketStream marketStream - dataStream dataStream - walletClient WalletClient - tokens tokenService + dataStore + marketStream marketStream + dataStream dataStream + walletClient WalletClient + whale whaleService - config config.BotConfig - tokenConfig *config.TokenConfig - log *log.Entry + config config.BotConfig + log *log.Entry stopPosMgmt chan bool stopPriceSteer chan bool pausePosMgmt chan struct{} pausePriceSteer chan struct{} - walletPubKey string // "58595a" ... + walletPubKey string marketID string decimalPlaces int settlementAssetID string + vegaAssetID string settlementAssetAddress string botPaused bool mu sync.Mutex } // New returns a new instance of bot. -func New(botConf config.BotConfig, locations []string, seedConf *config.TokenConfig, pe PricingEngine, wc WalletClient) *bot { +func New( + botConf config.BotConfig, + vegaAssetID string, + dataNode tradingDataService, + marketStream marketStream, + dataStream dataStream, + pe PricingEngine, + wc WalletClient, + whale whaleService, +) *bot { return &bot{ - config: botConf, - tokenConfig: seedConf, + config: botConf, + vegaAssetID: vegaAssetID, + node: dataNode, + marketStream: marketStream, + dataStream: dataStream, log: log.WithFields(log.Fields{ - "bot": botConf.Name, - "node": locations, + "bot": botConf.Name, }), - locations: locations, - pricingEngine: pe, - walletClient: wc, + pricingEngine: pe, + walletClient: wc, + whale: whale, + stopPosMgmt: make(chan bool), stopPriceSteer: make(chan bool), pausePosMgmt: make(chan struct{}), @@ -72,50 +81,22 @@ func New(botConf config.BotConfig, locations []string, seedConf *config.TokenCon func (b *bot) Start() error { setupLogger(false) // TODO: pretty from config? - dataNode := node.NewDataNode( - b.locations, - b.config.CallTimeoutMills, - ) - - b.log.WithFields( - log.Fields{ - "hosts": b.locations, - }).Debug("Attempting to connect to Vega gRPC node...") + b.log.Debug("Attempting to connect to Vega gRPC node...") + b.node.MustDialConnection(context.Background()) // blocking - dataNode.MustDialConnection(context.Background()) // blocking - - b.node = dataNode b.log = b.log.WithFields(log.Fields{"node": b.node.Target()}) b.log.Info("Connected to Vega gRPC node") - err := b.setupWallet() + ctx := context.Background() + + err := b.setupWallet(ctx) if err != nil { return fmt.Errorf("failed to setup wallet: %w", err) } pauseCh := b.pauseChannel() - b.marketStream = data.NewMarketStream(dataNode, b.walletPubKey, pauseCh) - - assetResponse, err := b.node.AssetByID(&dataapipb.AssetByIDRequest{ - Id: b.config.QuoteTokenID, - }) - if err != nil { - return fmt.Errorf("failed to get asset info: %w", err) - } - - erc20 := assetResponse.Asset.Details.GetErc20() - - quoteTokenAddress := b.config.QuoteTokenID - - if erc20 != nil { - quoteTokenAddress = erc20.ContractAddress - } - - b.tokens, err = token.NewService(b.tokenConfig, quoteTokenAddress, b.walletPubKey) - if err != nil { - return fmt.Errorf("failed to create token service: %w", err) - } + b.marketStream.Setup(b.walletPubKey, pauseCh) if err = b.setupMarket(); err != nil { return fmt.Errorf("failed to setup market: %w", err) @@ -129,26 +110,22 @@ func (b *bot) Start() error { }).Info("Fetched market info") // Use the settlementAssetID to lookup the settlement ethereum address - assetResponse, err = b.node.AssetByID(&dataapipb.AssetByIDRequest{Id: b.settlementAssetID}) + assetResponse, err := b.node.AssetByID(&dataapipb.AssetByIDRequest{Id: b.settlementAssetID}) if err != nil { return fmt.Errorf("unable to look up asset details for %s: %w", b.settlementAssetID, err) } + b.settlementAssetAddress = b.settlementAssetID if erc20 := assetResponse.Asset.Details.GetErc20(); erc20 != nil { b.settlementAssetAddress = erc20.ContractAddress - } else { - b.settlementAssetAddress = b.settlementAssetID } - store := data.NewStore() - b.data = store - - b.dataStream, err = data.NewDataStream(b.marketID, b.walletPubKey, b.settlementAssetID, dataNode, store, pauseCh) + b.dataStore, err = b.dataStream.InitData(b.walletPubKey, b.marketID, b.settlementAssetID, pauseCh) if err != nil { return fmt.Errorf("failed to create data stream: %w", err) } - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(ctx) go func() { defer cancel() @@ -187,24 +164,15 @@ func (b *bot) pauseChannel() chan types.PauseSignal { for { select { case p := <-in: - if !b.togglePause(p) { - return - } - select { - default: - b.pausePosMgmt <- struct{}{} - } - select { - default: - b.pausePriceSteer <- struct{}{} - } + b.Pause(p) } } }() return in } -func (b *bot) togglePause(p types.PauseSignal) bool { +// Pause pauses the liquidity bot goroutine(s). +func (b *bot) Pause(p types.PauseSignal) { b.mu.Lock() defer b.mu.Unlock() @@ -215,9 +183,18 @@ func (b *bot) togglePause(p types.PauseSignal) bool { b.log.WithFields(log.Fields{"From": p.From}).Info("Resuming bot") b.botPaused = false } else { - return false + return + } + + select { + case b.pausePosMgmt <- struct{}{}: + default: + } + select { + case b.pausePriceSteer <- struct{}{}: + default: } - return true + fmt.Println("bot paused") } // Stop stops the liquidity bot goroutine(s). @@ -246,8 +223,7 @@ func (b *bot) GetTraderDetails() string { return string(jsn) } -func (b *bot) setupWallet() error { - ctx := context.Background() +func (b *bot) setupWallet(ctx context.Context) error { walletPassphrase := "123" if err := b.walletClient.LoginWallet(ctx, b.config.Name, walletPassphrase); err != nil { @@ -263,26 +239,55 @@ func (b *bot) setupWallet() error { b.log.Info("Logged into wallet") - if b.walletPubKey == "" { - publicKeys, err := b.walletClient.ListPublicKeys(ctx) - if err != nil { - return fmt.Errorf("failed to list public keys: %w", err) - } + publicKeys, err := b.walletClient.ListPublicKeys(ctx) + if err != nil { + return fmt.Errorf("failed to list public keys: %w", err) + } - if len(publicKeys) == 0 { - key, err := b.walletClient.GenerateKeyPair(ctx, walletPassphrase, []types.Meta{}) - if err != nil { - return fmt.Errorf("failed to generate keypair: %w", err) - } - b.walletPubKey = key.Pub - b.log.WithFields(log.Fields{"pubKey": b.walletPubKey}).Debug("Created keypair") - } else { - b.walletPubKey = publicKeys[0] - b.log.WithFields(log.Fields{"pubKey": b.walletPubKey}).Debug("Using existing keypair") + if len(publicKeys) == 0 { + key, err := b.walletClient.GenerateKeyPair(ctx, walletPassphrase, []types.Meta{}) + if err != nil { + return fmt.Errorf("failed to generate keypair: %w", err) } + b.walletPubKey = key.Pub + b.log.WithFields(log.Fields{"pubKey": b.walletPubKey}).Debug("Created keypair") + } else { + b.walletPubKey = publicKeys[0] + b.log.WithFields(log.Fields{"pubKey": b.walletPubKey}).Debug("Using existing keypair") } b.log = b.log.WithFields(log.Fields{"pubkey": b.walletPubKey}) return nil } + +func (b *bot) ensureBalance(ctx context.Context, targetAmount *num.Uint, from string) error { + balanceTotal := b.Balance().Total() // TODO: should it be total balance? + + b.log.WithFields( + log.Fields{ + "balanceTotal": balanceTotal.String(), + }).Debugf("%s: Total account balance", from) + + if balanceTotal.GT(targetAmount) { + return nil + } + + b.log.WithFields( + log.Fields{ + "balanceTotal": balanceTotal.String(), + "targetAmount": targetAmount.String(), + }).Debugf("%s: Account balance is less than target amount, depositing...", from) + + b.Pause(types.PauseSignal{From: from, Pause: true}) + + b.log.Debugf("%s: Waiting for deposit to be finalized...", from) + + if err := b.whale.TopUp(ctx, b.config.Name, b.walletPubKey, b.settlementAssetID, targetAmount); err != nil { + return fmt.Errorf("failed to top-up tokens: %w", err) + } + + b.Pause(types.PauseSignal{From: from, Pause: true}) + + return nil +} diff --git a/bot/normal/position_management.go b/bot/normal/position_management.go index d6b9447..4a365a4 100644 --- a/bot/normal/position_management.go +++ b/bot/normal/position_management.go @@ -88,7 +88,7 @@ func (b *bot) ensureCommitmentAmount(ctx context.Context) error { }, ).Debug("PositionManagement: Supplied stake is less than target stake, increasing commitment amount...") - if err = b.ensureBalance(ctx, requiredCommitment); err != nil { + if err = b.ensureBalance(ctx, requiredCommitment, "PositionManagement"); err != nil { return fmt.Errorf("failed to ensure balance: %w", err) } @@ -108,8 +108,8 @@ func (b *bot) ensureCommitmentAmount(ctx context.Context) error { } func (b *bot) getRequiredCommitment() (*num.Uint, error) { - suppliedStake := b.data.SuppliedStake().Clone() - targetStake := b.data.TargetStake().Clone() + suppliedStake := b.Market().SuppliedStake().Clone() + targetStake := b.Market().TargetStake().Clone() b.log.WithFields(log.Fields{ "suppliedStake": suppliedStake.String(), @@ -133,43 +133,12 @@ func (b *bot) getRequiredCommitment() (*num.Uint, error) { return num.Zero().Add(targetStake, dx.Uint()), nil } -func (b *bot) ensureBalance(ctx context.Context, targetAmount *num.Uint) error { - balanceTotal := b.data.Balance().Total() // TODO: should it be total balance? - dx := balanceTotal.Int().Sub(targetAmount.Int()) - - if dx.IsPositive() { - return nil - } - - depositAmount := num.Zero().Add(targetAmount, dx.Uint()) - - b.log.WithFields( - log.Fields{ - "balanceTotal": balanceTotal.String(), - "targetAmount": targetAmount.String(), - "delta": dx.String(), - "depositAmount": depositAmount.String(), - }).Debug("PositionManagement: Account balance is less than target amount, depositing...") - - if err := b.tokens.Deposit(ctx, depositAmount); err != nil { - // "VM Exception while processing transaction: revert" could mean amount is too high - return fmt.Errorf("failed to deposit tokens: %w", err) - } - - b.log.Debug("PositionManagement: Waiting for deposit to be finalized...") - - if err := b.dataStream.WaitForDepositFinalize(depositAmount); err != nil { - return fmt.Errorf("failed to finalize deposit: %w", err) - } - return nil -} - func (b *bot) manageDirection(ctx context.Context, previousOpenVolume int64) (int64, error) { - openVolume := b.data.OpenVolume() + openVolume := b.Market().OpenVolume() buyShape, sellShape, shape := b.getShape() b.log.WithFields(log.Fields{ - "openVolume": b.data.OpenVolume(), + "openVolume": b.Market().OpenVolume(), "previousOpenVolume": previousOpenVolume, "shape": shape, }).Debug("PositionManagement: Checking for direction change") @@ -186,7 +155,7 @@ func (b *bot) manageDirection(ctx context.Context, previousOpenVolume int64) (in return openVolume, fmt.Errorf("failed to get required commitment amount: %w", err) } - if err = b.ensureBalance(ctx, commitment); err != nil { + if err = b.ensureBalance(ctx, commitment, "PositionManagement"); err != nil { return openVolume, fmt.Errorf("failed to ensure balance: %w", err) } @@ -198,16 +167,16 @@ func (b *bot) manageDirection(ctx context.Context, previousOpenVolume int64) (in } func (b *bot) shouldAmend(openVolume, previousOpenVolume int64) bool { - return (openVolume > 0 && previousOpenVolume <= 0) || (b.data.OpenVolume() < 0 && previousOpenVolume >= 0) + return (openVolume > 0 && previousOpenVolume <= 0) || (b.Market().OpenVolume() < 0 && previousOpenVolume >= 0) } func (b *bot) managePosition(ctx context.Context) error { size, side, shouldPlace := b.checkPosition() b.log.WithFields(log.Fields{ - "currentPrice": b.data.MarkPrice().String(), - "balanceGeneral": b.data.Balance().General.String(), - "balanceMargin": b.data.Balance().Margin.String(), - "openVolume": b.data.OpenVolume(), + "currentPrice": b.Market().MarkPrice().String(), + "balanceGeneral": b.Balance().General().String(), + "balanceMargin": b.Balance().Margin().String(), + "openVolume": b.Market().OpenVolume(), "size": size, "side": side, "shouldPlace": shouldPlace, @@ -250,7 +219,7 @@ func (b *bot) sendLiquidityProvision(ctx context.Context, commitment *num.Uint, b.log.WithFields(log.Fields{ "commitment": commitment.String(), - "balanceTotal": b.data.Balance().Total().String(), + "balanceTotal": b.Balance().Total().String(), }).Debug("PositionManagement: Submitting LiquidityProvisionSubmission...") if err := b.walletClient.SignTx(ctx, submitTxReq); err != nil { @@ -259,7 +228,7 @@ func (b *bot) sendLiquidityProvision(ctx context.Context, commitment *num.Uint, b.log.WithFields(log.Fields{ "commitment": commitment.String(), - "balanceTotal": b.data.Balance().Total().String(), + "balanceTotal": b.Balance().Total().String(), }).Debug("PositionManagement: Submitted LiquidityProvisionSubmission") return nil } @@ -309,7 +278,7 @@ func (b *bot) sendLiquidityProvisionCancellation(ctx context.Context) error { b.log.WithFields(log.Fields{ "commitment": "0", - "balanceTotal": b.data.Balance().Total().String(), + "balanceTotal": b.Balance().Total().String(), }).Debug("PositionManagement: Submitted LiquidityProvisionAmendment") return nil @@ -319,7 +288,7 @@ func (b *bot) checkPosition() (uint64, vega.Side, bool) { size := uint64(0) side := vega.Side_SIDE_UNSPECIFIED shouldPlace := true - openVolume := b.data.OpenVolume() + openVolume := b.Market().OpenVolume() if openVolume >= 0 && num.NewUint(uint64(openVolume)).GT(b.config.StrategyDetails.MaxLong.Get()) { size = mulFrac(num.NewUint(uint64(openVolume)), b.config.StrategyDetails.PosManagementFraction, 15).Uint64() @@ -353,7 +322,7 @@ func (b *bot) prePositionManagement(ctx context.Context) error { return nil } - if err = b.ensureBalance(ctx, commitment); err != nil { + if err = b.ensureBalance(ctx, commitment, "PositionManagement"); err != nil { return fmt.Errorf("failed to ensure balance: %w", err) } @@ -371,7 +340,7 @@ func (b *bot) getShape() ([]*vega.LiquidityOrder, []*vega.LiquidityOrder, string buyShape := b.config.StrategyDetails.LongeningShape.Buys.ToVegaLiquidityOrders() sellShape := b.config.StrategyDetails.LongeningShape.Sells.ToVegaLiquidityOrders() - if b.data.OpenVolume() > 0 { + if b.Market().OpenVolume() > 0 { shape = "shortening" buyShape = b.config.StrategyDetails.ShorteningShape.Buys.ToVegaLiquidityOrders() sellShape = b.config.StrategyDetails.ShorteningShape.Sells.ToVegaLiquidityOrders() @@ -382,7 +351,7 @@ func (b *bot) getShape() ([]*vega.LiquidityOrder, []*vega.LiquidityOrder, string func (b *bot) checkInitialMargin(buyShape, sellShape []*vega.LiquidityOrder) error { // Turn the shapes into a set of orders scaled by commitment - obligation := b.data.Balance().Total() + obligation := b.Balance().Total() buyOrders := b.calculateOrderSizes(obligation, buyShape) sellOrders := b.calculateOrderSizes(obligation, sellShape) @@ -393,7 +362,7 @@ func (b *bot) checkInitialMargin(buyShape, sellShape []*vega.LiquidityOrder) err sellCost := b.calculateMarginCost(sellRisk, sellOrders) shapeMarginCost := num.Max(buyCost, sellCost) - avail := mulFrac(b.data.Balance().General, b.config.StrategyDetails.OrdersFraction, 15) + avail := mulFrac(b.Balance().General(), b.config.StrategyDetails.OrdersFraction, 15) if !avail.LT(shapeMarginCost) { return nil @@ -420,12 +389,12 @@ func (b *bot) checkInitialMargin(buyShape, sellShape []*vega.LiquidityOrder) err // around the current price at upto 50+/- from it. func (b *bot) placeAuctionOrders(ctx context.Context) error { // Check if we have a currentPrice we can use - if b.data.MarkPrice().IsZero() { + if b.Market().MarkPrice().IsZero() { b.log.Debug("PositionManagement: No current price to place auction orders") return nil } - b.log.WithFields(log.Fields{"currentPrice": b.data.MarkPrice().String()}).Debug("PositionManagement: Placing auction orders") + b.log.WithFields(log.Fields{"currentPrice": b.Market().MarkPrice().String()}).Debug("PositionManagement: Placing auction orders") // Place the random orders split into totalVolume := num.Zero() @@ -438,7 +407,7 @@ func (b *bot) placeAuctionOrders(ctx context.Context) error { remaining := num.Zero().Sub(b.config.StrategyDetails.AuctionVolume.Get(), totalVolume) size := num.Min(num.UintChain(b.config.StrategyDetails.AuctionVolume.Get()).Div(num.NewUint(10)).Add(num.NewUint(1)).Get(), remaining) // #nosec G404 - price := num.Zero().Add(b.data.MarkPrice(), num.NewUint(uint64(rand.Int63n(100)-50))) + price := num.Zero().Add(b.Market().MarkPrice(), num.NewUint(uint64(rand.Int63n(100)-50))) side := vega.Side_SIDE_BUY // #nosec G404 if rand.Intn(2) == 0 { @@ -477,7 +446,7 @@ func (b *bot) calculateOrderSizes(obligation *num.Uint, liquidityOrders []*vega. size := num.UintChain(obligation.Clone()). Mul(num.NewUint(uint64(lo.Proportion))). Mul(num.NewUint(10)). - Div(totalProportion).Div(b.data.MarkPrice()).Get() + Div(totalProportion).Div(b.Market().MarkPrice()).Get() peggedOrder := vega.PeggedOrder{ Reference: lo.Reference, Offset: lo.Offset, @@ -511,7 +480,7 @@ func (b *bot) calculateMarginCost(risk float64, orders []*vega.Order) *num.Uint } } - totalMargin := num.UintChain(num.Zero()).Add(margins...).Mul(b.data.MarkPrice()).Get() + totalMargin := num.UintChain(num.Zero()).Add(margins...).Mul(b.Market().MarkPrice()).Get() return mulFrac(totalMargin, risk, 15) } diff --git a/bot/normal/price_steering.go b/bot/normal/price_steering.go index 80cdbb6..8987bfc 100644 --- a/bot/normal/price_steering.go +++ b/bot/normal/price_steering.go @@ -67,7 +67,7 @@ func (b *bot) steerPrice(ctx context.Context) error { return fmt.Errorf("failed to get external price: %w", err) } - staticMidPrice := b.data.StaticMidPrice() + staticMidPrice := b.Market().StaticMidPrice() currentDiff, statIsGt := num.Zero().Delta(externalPrice, staticMidPrice) currentDiffFraction := num.DecimalFromUint(currentDiff).Div(num.DecimalFromUint(externalPrice)) minPriceSteerFraction := num.DecimalFromInt64(int64(100)).Mul(num.DecimalFromFloat(b.config.StrategyDetails.MinPriceSteerFraction)) diff --git a/config/config.go b/config/config.go index 81a3bd3..fd2521e 100644 --- a/config/config.go +++ b/config/config.go @@ -16,10 +16,13 @@ import ( type Config struct { Server *ServerConfig `yaml:"server"` - Pricing *PricingConfig `yaml:"pricing"` - Wallet *WalletConfig `yaml:"wallet"` - Token *TokenConfig `yaml:"token"` - Locations []string `yaml:"locations"` + CallTimeoutMills int `yaml:"callTimeoutMills"` + VegaAssetID string `yaml:"vegaAssetID"` + Pricing *PricingConfig `yaml:"pricing"` + Wallet *WalletConfig `yaml:"wallet"` + Whale *WhaleConfig `yaml:"whale"` + Token *TokenConfig `yaml:"token"` + Locations []string `yaml:"locations"` Bots []BotConfig `yaml:"bots"` } @@ -38,6 +41,10 @@ func (cfg *Config) CheckConfig() error { return fmt.Errorf("%s: %s", errors.ErrMissingEmptyConfigSection.Error(), "wallet") } + if cfg.Whale == nil { + return fmt.Errorf("%s: %s", errors.ErrMissingEmptyConfigSection.Error(), "whale") + } + if cfg.Token == nil { return fmt.Errorf("%s: %s", errors.ErrMissingEmptyConfigSection.Error(), "token") } @@ -112,6 +119,15 @@ type PricingConfig struct { Address *url.URL `yaml:"address"` } +type WhaleConfig struct { + WalletPubKey string `yaml:"walletPubKey"` + WalletName string `yaml:"walletName"` + WalletPassphrase string `yaml:"walletPassphrase"` + OwnerPrivateKeys map[string]string `yaml:"ownerPrivateKeys"` + FaucetURL string `yaml:"faucetURL"` + SyncTimeoutSec int `yaml:"syncTimeoutSec"` +} + // BotConfig specifies the configuration parameters for one bot, which talks to one market on one // Vega node. type BotConfig struct { @@ -119,17 +135,14 @@ type BotConfig struct { // It is *not* a public key seen by Vega. Name string `yaml:"name"` - // CallTimeoutMills is the per-call timeout (in milliseconds) for communicating with the Vega node gRPC endpoint. - CallTimeoutMills int `yaml:"callTimeoutMills"` - // InstrumentBase is the base asset of the instrument. InstrumentBase string `yaml:"instrumentBase"` // InstrumentQuote is the quote asset of the instrument. InstrumentQuote string `yaml:"instrumentQuote"` - // QuoteTokenID is the id of the base token. - QuoteTokenID string `yaml:"quoteTokenID"` + // QuoteAssetID is the id of the quote asset. + QuoteAssetID string `yaml:"quoteAssetID"` // Strategy specifies which algorithm the bot is to use. Strategy string `yaml:"strategy"` @@ -147,12 +160,8 @@ type WalletConfig struct { } type TokenConfig struct { - EthereumAPIAddress string `yaml:"ethereumAPIAddress"` - Erc20BridgeAddress string `yaml:"erc20BridgeAddress"` - StakingBridgeAddress string `yaml:"stakingBridgeAddress"` - VegaTokenAddress string `yaml:"vegaTokenAddress"` - ContractOwnerAddress string `yaml:"contractOwnerAddress"` - ContractOwnerPrivateKey string `yaml:"contractOwnerPrivateKey"` - ChainID int64 `yaml:"chainID"` - SyncTimeoutSec int `yaml:"syncTimeoutSec"` + EthereumAPIAddress string `yaml:"ethereumAPIAddress"` + Erc20BridgeAddress string `yaml:"erc20BridgeAddress"` + StakingBridgeAddress string `yaml:"stakingBridgeAddress"` + SyncTimeoutSec int `yaml:"syncTimeoutSec"` } diff --git a/config/config.yaml b/config/config.yaml index 08af52a..e09d0ea 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -5,6 +5,10 @@ server: listen: ":7800" logformat: text # json, text loglevel: debug # debug, info, warning, error + +callTimeoutMills: 10000 +vegaAssetID: b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b + pricing: address: scheme: https @@ -13,14 +17,17 @@ pricing: wallet: url: http://127.0.0.1:1789 token: - ethereumAPIAddress: https://127.0.0.1:8545 + ethereumAPIAddress: ws://127.0.0.1:8545 erc20BridgeAddress: 0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54 stakingBridgeAddress: 0x9135f5afd6F055e731bca2348429482eE614CFfA - vegaTokenAddress: 0x67175Da1D5e966e40D11c4B2519392B2058373de - contractOwnerAddress: 0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F - contractOwnerPrivateKey: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 - chainID: 1440 syncTimeoutSec: 5 +whale: + walletPubKey: + walletName: w00 + walletPassphrase: 123 + ownerPrivateKeys: + 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 + faucetURL: locations: - localhost:3007 - localhost:3017 @@ -30,7 +37,7 @@ bots: callTimeoutMills: 10000 instrumentBase: BTC instrumentQuote: USD - quoteTokenID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede # 0x3773A5c7aFF77e014cBF067dd31801b4C6dc4136 + quoteAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede # 0x3773A5c7aFF77e014cBF067dd31801b4C6dc4136 settlementAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede strategy: normal strategyDetails: diff --git a/config/config_capsule.yaml b/config/config_capsule.yaml index 63de503..801d74f 100644 --- a/config/config_capsule.yaml +++ b/config/config_capsule.yaml @@ -5,6 +5,10 @@ server: listen: ":7800" logformat: text # json, text loglevel: debug # debug, info, warning, error + +callTimeoutMills: 10000 +vegaAssetID: b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b + pricing: address: scheme: https @@ -13,12 +17,17 @@ pricing: wallet: url: http://host.docker.internal:1789 token: - ethereumAPIAddress: https://host.docker.internal:8545 + ethereumAPIAddress: ws://host.docker.internal:8545 erc20BridgeAddress: 0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54 stakingBridgeAddress: 0x9135f5afd6F055e731bca2348429482eE614CFfA - vegaTokenAddress: 0x67175Da1D5e966e40D11c4B2519392B2058373de - contractOwnerAddress: 0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F - contractOwnerPrivateKey: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 + syncTimeoutSec: 5 +whale: + walletPubKey: + walletName: w00 + walletPassphrase: 123 + ownerPrivateKeys: + 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 + faucetURL: locations: - https://host.docker.internal:3027 - https://host.docker.internal:3017 @@ -28,7 +37,7 @@ bots: callTimeoutMills: 10000 instrumentBase: BTC instrumentQuote: USD - quoteTokenAddress: 0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0 + quoteAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede # 0x3773A5c7aFF77e014cBF067dd31801b4C6dc4136 strategy: normal settlementAsset: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede strategyDetails: diff --git a/config/config_test.go b/config/config_test.go index d2df2ad..abc524b 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -28,6 +28,10 @@ func TestCheckConfig(t *testing.T) { err = cfg.CheckConfig() assert.True(t, strings.HasPrefix(err.Error(), errors.ErrMissingEmptyConfigSection.Error())) + cfg.Whale = &config.WhaleConfig{} + err = cfg.CheckConfig() + assert.True(t, strings.HasPrefix(err.Error(), errors.ErrMissingEmptyConfigSection.Error())) + cfg.Token = &config.TokenConfig{} err = cfg.CheckConfig() assert.True(t, strings.HasPrefix(err.Error(), errors.ErrMissingEmptyConfigSection.Error())) diff --git a/data/datarequests.go b/data/datarequests.go index 9f704d3..a9791b6 100644 --- a/data/datarequests.go +++ b/data/datarequests.go @@ -9,86 +9,93 @@ import ( log "github.com/sirupsen/logrus" "code.vegaprotocol.io/liqbot/types" - "code.vegaprotocol.io/liqbot/types/num" "code.vegaprotocol.io/liqbot/util" ) -func (d *data) getOpenVolume() (int64, error) { +func (d *data) initOpenVolume() error { positions, err := d.getPositions() if err != nil { - return 0, fmt.Errorf("failed to get position details: %w", err) + return fmt.Errorf("failed to get position details: %w", err) } var openVolume int64 // If we have not traded yet, then we won't have a position if positions != nil { if len(positions) != 1 { - return 0, errors.New("one position item required") + return errors.New("one position item required") } openVolume = positions[0].OpenVolume } - return openVolume, nil + d.store.MarketSet(types.SetOpenVolume(openVolume)) + return nil } -func (d *data) getAccount() (*types.Balance, error) { - response, err := d.node.PartyAccounts(&dataapipb.PartyAccountsRequest{ - PartyId: d.walletPubKey, - Asset: d.settlementAssetID, +// getPositions get this bot's positions. +func (d *data) getPositions() ([]*vega.Position, error) { + response, err := d.node.PositionsByParty(&dataapipb.PositionsByPartyRequest{ + PartyId: d.walletPubKey, + MarketId: d.marketID, }) if err != nil { return nil, err } - balance := &types.Balance{ - General: &num.Uint{}, - Margin: &num.Uint{}, - Bond: &num.Uint{}, + return response.Positions, nil +} + +func (d *data) initBalance() error { + response, err := d.node.PartyAccounts(&dataapipb.PartyAccountsRequest{ + PartyId: d.walletPubKey, + Asset: d.settlementAssetID, + }) + if err != nil { + return err } if len(response.Accounts) == 0 { d.log.WithFields(log.Fields{ "party": d.walletPubKey, }).Warning("Party has no accounts") - return balance, nil + return nil } for _, account := range response.Accounts { - switch account.Type { - case vega.AccountType_ACCOUNT_TYPE_GENERAL: - balance.General, err = util.ConvertUint256(account.Balance) - case vega.AccountType_ACCOUNT_TYPE_MARGIN: - balance.Margin, err = util.ConvertUint256(account.Balance) - case vega.AccountType_ACCOUNT_TYPE_BOND: - balance.Bond, err = util.ConvertUint256(account.Balance) - } - if err != nil { - return nil, fmt.Errorf("failed to convert account balance: %w", err) + if err = d.setBalanceByType(account); err != nil { + d.log.WithFields( + log.Fields{ + "error": err.Error(), + "accountType": account.Type.String(), + }, + ).Error("failed to set account balance") } } - return balance, nil + return nil } -// getPositions get this bot's positions. -func (d *data) getPositions() ([]*vega.Position, error) { - response, err := d.node.PositionsByParty(&dataapipb.PositionsByPartyRequest{ - PartyId: d.walletPubKey, - MarketId: d.marketID, - }) +func (d *data) setBalanceByType(account *vega.Account) error { + balance, err := util.ConvertUint256(account.Balance) if err != nil { - return nil, err + return fmt.Errorf("failed to convert account balance: %w", err) } - return response.Positions, nil + d.store.BalanceSet(types.SetBalanceByType(account.Type, balance)) + return nil } -// getMarketData gets the latest info about the market. -func (d *data) getMarketData() (*vega.MarketData, error) { +// initMarketData gets the latest info about the market. +func (d *data) initMarketData() error { response, err := d.node.MarketDataByID(&dataapipb.MarketDataByIDRequest{MarketId: d.marketID}) if err != nil { - return nil, fmt.Errorf("failed to get market data (ID:%s): %w", d.marketID, err) + return fmt.Errorf("failed to get market data (ID:%s): %w", d.marketID, err) + } + + md, err := types.FromVegaMD(response.MarketData) + if err != nil { + return fmt.Errorf("failed to convert market data: %w", err) } - return response.MarketData, nil + d.store.MarketSet(types.SetMarketData(md)) + return nil } diff --git a/data/event_process.go b/data/event_process.go index 1d23125..a920ae5 100644 --- a/data/event_process.go +++ b/data/event_process.go @@ -14,12 +14,12 @@ import ( ) type busEventProcessor struct { - node DataNode + node busStreamer log *log.Entry pauseCh chan types.PauseSignal } -func newBusEventProcessor(node DataNode, pauseCh chan types.PauseSignal) *busEventProcessor { +func newBusEventProcessor(node busStreamer, pauseCh chan types.PauseSignal) *busEventProcessor { return &busEventProcessor{ node: node, log: log.WithFields(log.Fields{"module": "EventProcessor", "event": "EventBus"}), @@ -144,6 +144,9 @@ func (b *busEventProcessor) getStream(ctx context.Context, req *coreapipb.Observ } func (b *busEventProcessor) pause(p bool, name string) { + if b.pauseCh == nil { + return + } select { case b.pauseCh <- types.PauseSignal{From: name, Pause: p}: default: diff --git a/data/interfaces.go b/data/interfaces.go index 953180f..ca86160 100644 --- a/data/interfaces.go +++ b/data/interfaces.go @@ -4,31 +4,35 @@ import ( "context" dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" - "code.vegaprotocol.io/protos/vega" vegaapipb "code.vegaprotocol.io/protos/vega/api/v1" "code.vegaprotocol.io/liqbot/types" - "code.vegaprotocol.io/liqbot/types/num" ) // DataNode is a Vega Data node // //go:generate go run github.com/golang/mock/mockgen -destination mocks/datanode_mock.go -package mocks code.vegaprotocol.io/liqbot/data DataNode type DataNode interface { - ObserveEventBus(ctx context.Context) (client vegaapipb.CoreService_ObserveEventBusClient, err error) + busStreamer PartyAccounts(req *dataapipb.PartyAccountsRequest) (response *dataapipb.PartyAccountsResponse, err error) MarketDataByID(req *dataapipb.MarketDataByIDRequest) (response *dataapipb.MarketDataByIDResponse, err error) PositionsByParty(req *dataapipb.PositionsByPartyRequest) (response *dataapipb.PositionsByPartyResponse, err error) +} + +type busStreamer interface { MustDialConnection(ctx context.Context) + ObserveEventBus(ctx context.Context) (client vegaapipb.CoreService_ObserveEventBusClient, err error) +} + +type GetDataStore interface { + Balance() types.Balance + Market() types.MarketData } -type dataStore interface { - balanceSet(typ vega.AccountType, bal *num.Uint) - balanceInit(balance *types.Balance) - marketDataSet(marketData *types.MarketData) - openVolumeSet(openVolume int64) - cache() +type setDataStore interface { + BalanceSet(sets ...func(*types.Balance)) OpenVolume() int64 + MarketSet(sets ...func(*types.MarketData)) } type busEventer interface { diff --git a/data/store.go b/data/store.go deleted file mode 100644 index cc2baa2..0000000 --- a/data/store.go +++ /dev/null @@ -1,181 +0,0 @@ -package data - -import ( - "code.vegaprotocol.io/protos/vega" - - "code.vegaprotocol.io/liqbot/types" - "code.vegaprotocol.io/liqbot/types/num" -) - -type store struct { - balanceGetCh chan balanceGetReq - balanceInitCh chan balanceInitReq - balanceSetCh chan balanceSetReq - - marketDataGetCh chan marketDataGetReq - marketDataSetCh chan marketDataSetReq - - openVolumeGetCh chan openVolumeGetReq - openVolumeSetCh chan openVolumeSetReq -} - -func NewStore() *store { - return &store{ - balanceGetCh: make(chan balanceGetReq), - balanceInitCh: make(chan balanceInitReq), - balanceSetCh: make(chan balanceSetReq), - marketDataGetCh: make(chan marketDataGetReq), - marketDataSetCh: make(chan marketDataSetReq), - openVolumeGetCh: make(chan openVolumeGetReq), - openVolumeSetCh: make(chan openVolumeSetReq), - } -} - -type balanceGetReq struct { - resp chan *types.Balance -} - -type balanceInitReq struct { - balance *types.Balance -} - -type balanceSetReq struct { - typ vega.AccountType - balance *num.Uint -} - -type marketDataGetReq struct { - resp chan *types.MarketData -} - -type marketDataSetReq struct { - marketData *types.MarketData -} - -type openVolumeGetReq struct { - resp chan int64 -} - -type openVolumeSetReq struct { - openVolume int64 -} - -func (s *store) Balance() types.Balance { - resp := make(chan *types.Balance) - s.balanceGetCh <- balanceGetReq{resp} - balance := <-resp - if balance == nil { - return types.Balance{} - } - return *balance -} - -func (s *store) TradingMode() vega.Market_TradingMode { - md := s.marketDataGet() - if md == nil { - return vega.Market_TRADING_MODE_UNSPECIFIED - } - return md.TradingMode -} - -func (s *store) StaticMidPrice() *num.Uint { - md := s.marketDataGet() - if md == nil { - return num.Zero() - } - return md.StaticMidPrice -} - -func (s *store) MarkPrice() *num.Uint { - md := s.marketDataGet() - if md == nil { - return num.Zero() - } - return md.MarkPrice -} - -func (s *store) TargetStake() *num.Uint { - md := s.marketDataGet() - if md == nil { - return num.Zero() - } - return md.TargetStake -} - -func (s *store) SuppliedStake() *num.Uint { - md := s.marketDataGet() - if md == nil { - return num.Zero() - } - return md.SuppliedStake -} - -func (s *store) OpenVolume() int64 { - resp := make(chan int64) - s.openVolumeGetCh <- openVolumeGetReq{resp} - return <-resp -} - -func (s *store) balanceInit(balance *types.Balance) { - s.balanceInitCh <- balanceInitReq{balance: balance} -} - -func (s *store) balanceSet(typ vega.AccountType, bal *num.Uint) { - s.balanceSetCh <- balanceSetReq{typ: typ, balance: bal.Clone()} -} - -func (s *store) marketDataGet() *types.MarketData { - resp := make(chan *types.MarketData) - s.marketDataGetCh <- marketDataGetReq{resp} - return <-resp -} - -func (s *store) marketDataSet(marketData *types.MarketData) { - s.marketDataSetCh <- marketDataSetReq{marketData} -} - -func (s *store) openVolumeSet(openVolume int64) { - s.openVolumeSetCh <- openVolumeSetReq{openVolume} -} - -func (s *store) cache() { - d := struct { - balance *types.Balance - marketData *types.MarketData - tradingMode *vega.Market_TradingMode - openVolume int64 - }{ - balance: new(types.Balance), - marketData: new(types.MarketData), - } - - for { - select { - case req := <-s.balanceGetCh: - req.resp <- d.balance - case req := <-s.balanceSetCh: - switch req.typ { - case vega.AccountType_ACCOUNT_TYPE_GENERAL: - d.balance.General = req.balance - case vega.AccountType_ACCOUNT_TYPE_MARGIN: - d.balance.Margin = req.balance - case vega.AccountType_ACCOUNT_TYPE_BOND: - d.balance.Bond = req.balance - } - case req := <-s.balanceInitCh: - d.balance = req.balance - case req := <-s.marketDataGetCh: - if d.marketData != nil { - mdVal := *d.marketData - req.resp <- &mdVal - } - case req := <-s.marketDataSetCh: - d.marketData = req.marketData - case req := <-s.openVolumeGetCh: - req.resp <- d.openVolume - case req := <-s.openVolumeSetCh: - d.openVolume = req.openVolume - default: - } - } -} diff --git a/data/streamingdata.go b/data/streamingdata.go index e05fb51..6da52d7 100644 --- a/data/streamingdata.go +++ b/data/streamingdata.go @@ -3,18 +3,14 @@ package data import ( "context" "fmt" - "time" "code.vegaprotocol.io/vega/events" - "code.vegaprotocol.io/liqbot/types" - "code.vegaprotocol.io/liqbot/types/num" - "code.vegaprotocol.io/liqbot/util" - - "code.vegaprotocol.io/protos/vega" coreapipb "code.vegaprotocol.io/protos/vega/api/v1" eventspb "code.vegaprotocol.io/protos/vega/events/v1" log "github.com/sirupsen/logrus" + + "code.vegaprotocol.io/liqbot/types" ) type data struct { @@ -23,118 +19,57 @@ type data struct { walletPubKey string marketID string settlementAssetID string - store dataStore + store setDataStore busEvProc busEventer } -func NewDataStream(marketID, pubKey, settlementAssetID string, node DataNode, store dataStore, pauseCh chan types.PauseSignal) (*data, error) { - d := &data{ - log: log.WithField("module", "DataStreamer"), - node: node, - store: store, - busEvProc: newBusEventProcessor(node, pauseCh), +func NewStreamData( + node DataNode, +) *data { + return &data{ + log: log.WithField("module", "DataStreamer"), + node: node, } +} + +func (d *data) InitData( + pubKey, + marketID, + settlementAssetID string, + pauseCh chan types.PauseSignal, +) (GetDataStore, error) { + store := types.NewStore() d.walletPubKey = pubKey d.marketID = marketID d.settlementAssetID = settlementAssetID - - go d.store.cache() + d.store = store + d.busEvProc = newBusEventProcessor(d.node, pauseCh) if err := d.setInitialData(); err != nil { return nil, fmt.Errorf("failed to set initial data: %w", err) } - d.subscribe() - return d, nil -} - -func (d *data) subscribe() { go d.subscribeToMarketEvents() go d.subscribeToAccountEvents() go d.subscribePositions() -} - -// WaitForDepositFinalize is a blocking call that waits for the deposit finalize event to be received. -func (d *data) WaitForDepositFinalize(amount *num.Uint) error { - req := &coreapipb.ObserveEventBusRequest{ - Type: []eventspb.BusEventType{ - eventspb.BusEventType_BUS_EVENT_TYPE_DEPOSIT, - }, - PartyId: d.walletPubKey, - } - - proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { - for _, event := range rsp.Events { - dep := event.GetDeposit() - // filter out any that are for different assets, or not finalized - if dep.Asset != d.settlementAssetID || dep.Status != vega.Deposit_STATUS_FINALIZED { - continue - } - - if dep.Amount == amount.String() { - d.log.WithFields(log.Fields{"amount": amount}).Info("Deposit finalized") - return true, nil - } - } - return false, nil - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*40) - defer cancel() - d.busEvProc.processEvents(ctx, "DepositData", req, proc) - return ctx.Err() + return store, nil } func (d *data) setInitialData() error { - // Collateral - balance, err := d.getAccount() - if err != nil { - return fmt.Errorf("failed to get account general: %w", err) + if err := d.initBalance(); err != nil { + return fmt.Errorf("failed to set account balance: %w", err) } - d.store.balanceInit(balance) - - marketData, err := d.getMarketData() - if err != nil { + if err := d.initMarketData(); err != nil { return fmt.Errorf("failed to get market data: %w", err) } - currentPrice, err := util.ConvertUint256(marketData.StaticMidPrice) - if err != nil { - return fmt.Errorf("failed to convert current price: %w", err) - } - - targetStake, err := util.ConvertUint256(marketData.TargetStake) - if err != nil { - d.log.WithFields(log.Fields{ - "targetStake": marketData.TargetStake, - }).Warning("processEventBusData: failed to unmarshal uint256: error or overflow") - } - - suppliedStake, err := util.ConvertUint256(marketData.SuppliedStake) - if err != nil { - d.log.WithFields(log.Fields{ - "suppliedStake": marketData.SuppliedStake, - }).Warning("processEventBusData: failed to unmarshal uint256: error or overflow") - } - - d.store.marketDataSet(&types.MarketData{ - StaticMidPrice: currentPrice, - MarkPrice: &num.Uint{}, - TargetStake: targetStake, - SuppliedStake: suppliedStake, - TradingMode: marketData.MarketTradingMode, - }) - - openVolume, err := d.getOpenVolume() - if err != nil { + if err := d.initOpenVolume(); err != nil { return fmt.Errorf("failed to get open volume: %w", err) } - d.store.openVolumeSet(openVolume) - return nil } @@ -150,41 +85,12 @@ func (d *data) subscribeToMarketEvents() { for _, event := range rsp.Events { marketData := event.GetMarketData() - markPrice, err := util.ConvertUint256(marketData.MarkPrice) + md, err := types.FromVegaMD(marketData) if err != nil { - d.log.WithFields(log.Fields{ - "staticMidPrice": marketData.StaticMidPrice, - }).Warning("processEventBusData: failed to unmarshal uint256: error or overflow") + return false, fmt.Errorf("failed to convert market data: %w", err) } - staticMidPrice, err := util.ConvertUint256(marketData.StaticMidPrice) - if err != nil { - d.log.WithFields(log.Fields{ - "staticMidPrice": marketData.StaticMidPrice, - }).Warning("processEventBusData: failed to unmarshal uint256: error or overflow") - } - - targetStake, err := util.ConvertUint256(marketData.TargetStake) - if err != nil { - d.log.WithFields(log.Fields{ - "targetStake": marketData.TargetStake, - }).Warning("processEventBusData: failed to unmarshal uint256: error or overflow") - } - - suppliedStake, err := util.ConvertUint256(marketData.SuppliedStake) - if err != nil { - d.log.WithFields(log.Fields{ - "suppliedStake": marketData.SuppliedStake, - }).Warning("processEventBusData: failed to unmarshal uint256: error or overflow") - } - - d.store.marketDataSet(&types.MarketData{ - StaticMidPrice: staticMidPrice, - MarkPrice: markPrice, - TargetStake: targetStake, - SuppliedStake: suppliedStake, - TradingMode: marketData.MarketTradingMode, - }) + d.store.MarketSet(types.SetMarketData(md)) } return false, nil } @@ -209,16 +115,14 @@ func (d *data) subscribeToAccountEvents() { continue } - bal, err := util.ConvertUint256(acct.Balance) - if err != nil { - d.log.WithFields(log.Fields{ - "error": err.Error(), - "AccountBalance": acct.Balance, - }).Warning("processEventBusData: failed to unmarshal uint256: error or overflow") - continue + if err := d.setBalanceByType(acct); err != nil { + d.log.WithFields( + log.Fields{ + "error": err.Error(), + "accountType": acct.Type.String(), + }, + ).Error("failed to set account balance") } - - d.store.balanceSet(acct.Type, bal) } return false, nil } @@ -247,7 +151,7 @@ func (d *data) subscribePositions() { } } - d.store.openVolumeSet(openVolume) + d.store.MarketSet(types.SetOpenVolume(openVolume)) return false, nil } diff --git a/data/streamingdeposit.go b/data/streamingdeposit.go new file mode 100644 index 0000000..2a36907 --- /dev/null +++ b/data/streamingdeposit.go @@ -0,0 +1,59 @@ +package data + +import ( + "context" + "time" + + "code.vegaprotocol.io/protos/vega" + coreapipb "code.vegaprotocol.io/protos/vega/api/v1" + eventspb "code.vegaprotocol.io/protos/vega/events/v1" + log "github.com/sirupsen/logrus" + + "code.vegaprotocol.io/liqbot/types/num" +) + +type deposit struct { + log *log.Entry + busEvProc busEventer + walletPubKey string +} + +func NewDepositStream(node busStreamer, pubKey string) *deposit { + return &deposit{ + log: log.WithField("module", "DepositStreamer"), + busEvProc: newBusEventProcessor(node, nil), + walletPubKey: pubKey, + } +} + +// WaitForDepositFinalize is a blocking call that waits for the deposit finalize event to be received. +func (d *deposit) WaitForDepositFinalize(ctx context.Context, settlementAssetID string, amount *num.Uint, timeout time.Duration) error { + req := &coreapipb.ObserveEventBusRequest{ + Type: []eventspb.BusEventType{ + eventspb.BusEventType_BUS_EVENT_TYPE_DEPOSIT, + }, + PartyId: d.walletPubKey, + } + + proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { + for _, event := range rsp.Events { + dep := event.GetDeposit() + // filter out any that are for different assets, or not finalized + if dep.Asset != settlementAssetID || dep.Status != vega.Deposit_STATUS_FINALIZED { + continue + } + + if dep.Amount == amount.String() { + d.log.WithFields(log.Fields{"amount": amount}).Info("Deposit finalized") + return true, nil + } + } + return false, nil + } + + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + d.busEvProc.processEvents(ctx, "DepositData", req, proc) + return ctx.Err() +} diff --git a/data/streamingmarket.go b/data/streamingmarket.go index 370137c..1ed5a22 100644 --- a/data/streamingmarket.go +++ b/data/streamingmarket.go @@ -20,15 +20,18 @@ type market struct { busEvProc busEventer } -func NewMarketStream(node DataNode, walletPubKey string, pauseCh chan types.PauseSignal) *market { +func NewMarketStream(node DataNode) *market { return &market{ - node: node, - log: log.WithField("module", "MarketStreamer"), - busEvProc: newBusEventProcessor(node, pauseCh), - walletPubKey: walletPubKey, + node: node, + log: log.WithField("module", "MarketStreamer"), } } +func (m *market) Setup(walletPubKey string, pauseCh chan types.PauseSignal) { + m.walletPubKey = walletPubKey + m.busEvProc = newBusEventProcessor(m.node, pauseCh) +} + func (m *market) WaitForStakeLinking() error { req := &coreapipb.ObserveEventBusRequest{ Type: []eventspb.BusEventType{eventspb.BusEventType_BUS_EVENT_TYPE_STAKE_LINKING}, diff --git a/go.mod b/go.mod index 2bd5626..cd6ba92 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,10 @@ go 1.18 require ( code.vegaprotocol.io/priceproxy v0.1.0 code.vegaprotocol.io/protos v0.53.0 - code.vegaprotocol.io/shared v0.0.0-20220704150014-7c22d12ccb72 + code.vegaprotocol.io/shared v0.0.0-20220813000126-79cdcc23dfd0 code.vegaprotocol.io/vega v0.53.0 code.vegaprotocol.io/vegawallet v0.16.1 - github.com/ethereum/go-ethereum v1.10.20 + github.com/ethereum/go-ethereum v1.10.21 github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.2 github.com/hashicorp/go-multierror v1.1.1 @@ -37,7 +37,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set v1.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect - github.com/dgraph-io/badger/v2 v2.2007.2 // indirect + github.com/dgraph-io/badger/v2 v2.2007.3 // indirect github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de // indirect github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect github.com/dustin/go-humanize v1.0.0 // indirect @@ -75,7 +75,7 @@ require ( golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect - golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da // indirect + golang.org/x/tools v0.1.9 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index f925b9e..dd2bb59 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ code.vegaprotocol.io/priceproxy v0.1.0 h1:PibvUIcTXS/MOIJKJwGXhJbDwg9ZPLItcIlfMv code.vegaprotocol.io/priceproxy v0.1.0/go.mod h1:W/CpimpwdplSLOnO1gEz2+VLQIV5I9FsDOST+1NDJ5A= code.vegaprotocol.io/protos v0.53.0 h1:3Xl/2o+IWd3dxe1kHnLCbF5DqKq5AoiaI8rVIdpjcng= code.vegaprotocol.io/protos v0.53.0/go.mod h1:4BqwDw6jhc/mnwbXq8ZFUtYBFCnk8tBW6zuPsBt8OrQ= -code.vegaprotocol.io/shared v0.0.0-20220704150014-7c22d12ccb72 h1:BJwmbEbC+ujqLSA1dSyUwDOc4+aRoZcndj/RM210CtE= -code.vegaprotocol.io/shared v0.0.0-20220704150014-7c22d12ccb72/go.mod h1:P9MfU2GyzI4Vc7OrKx9+qN9JV0bNGFB/aYe0++e7158= +code.vegaprotocol.io/shared v0.0.0-20220813000126-79cdcc23dfd0 h1:6az/9MVVpbKSkFR8mFUy6+oWWI6HfNT769fcu9WLETg= +code.vegaprotocol.io/shared v0.0.0-20220813000126-79cdcc23dfd0/go.mod h1:XzX67GsyOHzvytMr0QOHX4CCTdCZDYKUUi88rx40Nt0= code.vegaprotocol.io/vega v0.53.0 h1:GaJQxiQYbiyJZ8FZc9zV5PdzN4LR4HhgZPCN2RgVB2I= code.vegaprotocol.io/vega v0.53.0/go.mod h1:pBUCAGr1Sv1TbpX2QlXwor21k7QhEn9kQuZ6wXGhN1I= code.vegaprotocol.io/vegawallet v0.16.1 h1:BRRxCKMa6vaIVHBBzwlG8Sbm1+m2xh9h6QyRt2JwVJQ= @@ -209,8 +209,9 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/dgraph-io/badger/v2 v2.2007.1/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= -github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= +github.com/dgraph-io/badger/v2 v2.2007.3 h1:Sl9tQWz92WCbVSe8pj04Tkqlm2boW+KAxd+XSs58SQI= +github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -238,8 +239,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.10.20 h1:75IW830ClSS40yrQC1ZCMZCt5I+zU16oqId2SiQwdQ4= -github.com/ethereum/go-ethereum v1.10.20/go.mod h1:LWUN82TCHGpxB3En5HVmLLzPD7YSrEUFmFfN1nKkVN0= +github.com/ethereum/go-ethereum v1.10.21 h1:5lqsEx92ZaZzRyOqBEXux4/UR06m296RGzN3ol3teJY= +github.com/ethereum/go-ethereum v1.10.21/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= @@ -1227,8 +1228,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da h1:Tno72dYE94v/7SyyIj9iBsc7OOjFu2PyNcl7yxxeZD8= -golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/node/datanode.go b/node/datanode.go index 761399e..84819c9 100644 --- a/node/datanode.go +++ b/node/datanode.go @@ -37,10 +37,10 @@ func NewDataNode(hosts []string, callTimeoutMil int) *DataNode { // MustDialConnection tries to establish a connection to one of the nodes from a list of locations. // It is idempotent, while it each call will block the caller until a connection is established. func (n *DataNode) MustDialConnection(ctx context.Context) { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - n.once.Do(func() { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + n.wg.Add(len(n.hosts)) for _, h := range n.hosts { @@ -52,15 +52,17 @@ func (n *DataNode) MustDialConnection(ctx context.Context) { n.dialNode(ctx, host) }(h) } + n.wg.Wait() + n.mu.Lock() + defer n.mu.Unlock() + + if n.conn == nil { + log.Fatalf("Failed to connect to DataNode") + } }) n.wg.Wait() - - n.mu.RLock() - defer n.mu.RUnlock() - if n.conn == nil { - log.Fatalf("Failed to connect to DataNode") - } + n.once = sync.Once{} } func (n *DataNode) dialNode(ctx context.Context, host string) { diff --git a/service/service.go b/service/service.go index 24f1a30..819ea1d 100644 --- a/service/service.go +++ b/service/service.go @@ -16,8 +16,13 @@ import ( "code.vegaprotocol.io/liqbot/bot" "code.vegaprotocol.io/liqbot/config" + "code.vegaprotocol.io/liqbot/data" + "code.vegaprotocol.io/liqbot/node" "code.vegaprotocol.io/liqbot/pricing" + "code.vegaprotocol.io/liqbot/token" + "code.vegaprotocol.io/liqbot/types" "code.vegaprotocol.io/liqbot/wallet" + "code.vegaprotocol.io/liqbot/whale" ) // Bot is the generic bot interface. @@ -67,9 +72,14 @@ func NewService(config config.Config) (s *Service, err error) { bots: make(map[string]Bot), } - pe := pricing.NewEngine(*config.Pricing) + pricingEngine := pricing.NewEngine(*config.Pricing) - if err = s.initBots(pe); err != nil { + whaleService, err := getWhale(config) + if err != nil { + return nil, err + } + + if err = s.initBots(pricingEngine, whaleService); err != nil { return nil, fmt.Errorf("failed to initialise bots: %s", err.Error()) } @@ -79,6 +89,35 @@ func NewService(config config.Config) (s *Service, err error) { return s, err } +func getWhale(config config.Config) (*whale.Service, error) { + dataNode := node.NewDataNode( + config.Locations, + config.CallTimeoutMills, + ) + + faucetService := token.NewFaucetService(config.Whale.FaucetURL, config.Whale.WalletPubKey) + whaleWallet := wallet.NewClient(config.Wallet.URL) + depositStream := data.NewDepositStream(dataNode, config.Whale.WalletPubKey) + tokenService, err := token.NewService(config.Token, config.Whale.WalletPubKey) + if err != nil { + return nil, fmt.Errorf("failed to setup token service: %w", err) + } + + whaleService := whale.NewService( + dataNode, + whaleWallet, + tokenService, + faucetService, + depositStream, + config.Whale, + ) + + if err = whaleService.Start(context.Background()); err != nil { + return nil, fmt.Errorf("failed to start whale service: %w", err) + } + return whaleService, nil +} + func (s *Service) addRoutes() { s.GET("/status", s.Status) s.GET("/traders-settlement", s.TradersSettlement) @@ -122,9 +161,9 @@ func (s *Service) Stop() { } } -func (s *Service) initBots(pricingEngine PricingEngine) error { +func (s *Service) initBots(pricingEngine PricingEngine, whaleService types.WhaleService) error { for _, botcfg := range s.config.Bots { - if err := s.initBot(pricingEngine, botcfg); err != nil { + if err := s.initBot(pricingEngine, botcfg, whaleService); err != nil { return fmt.Errorf("failed to initialise bot '%s': %w", botcfg.Name, err) } } @@ -132,12 +171,10 @@ func (s *Service) initBots(pricingEngine PricingEngine) error { return nil } -func (s *Service) initBot(pricingEngine PricingEngine, botcfg config.BotConfig) error { +func (s *Service) initBot(pricingEngine PricingEngine, botcfg config.BotConfig, whaleService types.WhaleService) error { log.WithFields(log.Fields{"strategy": botcfg.StrategyDetails.String()}).Debug("read strategy config") - wc := wallet.NewClient(s.config.Wallet.URL) - - b, err := bot.New(botcfg, s.config.Locations, s.config.Token, pricingEngine, wc) + b, err := bot.New(botcfg, s.config, pricingEngine, whaleService) if err != nil { return fmt.Errorf("failed to create bot %s: %w", botcfg.Name, err) } diff --git a/token/erc20_service.go b/token/erc20_service.go index 7b1de4d..6042816 100644 --- a/token/erc20_service.go +++ b/token/erc20_service.go @@ -18,19 +18,15 @@ import ( ) type Service struct { - client *vgethereum.Client - vegaPubKey string - erc20BridgeAddress common.Address - stakingBridgeAddress common.Address - erc20TokenAddress common.Address - vegaTokenAddress common.Address - contractOwnerAddress common.Address - contractOwnerPrivateKey string - syncTimeout *time.Duration - log *log.Entry + client *vgethereum.Client + vegaPubKey string + erc20BridgeAddress common.Address + stakingBridgeAddress common.Address + syncTimeout *time.Duration + log *log.Entry } -func NewService(conf *config.TokenConfig, baseTokenAddress, vegaPubKey string) (*Service, error) { +func NewService(conf *config.TokenConfig, vegaPubKey string) (*Service, error) { ctx := context.Background() var syncTimeout time.Duration @@ -38,84 +34,95 @@ func NewService(conf *config.TokenConfig, baseTokenAddress, vegaPubKey string) ( syncTimeout = time.Duration(conf.SyncTimeoutSec) * time.Second } - client, err := vgethereum.NewClient(ctx, conf.EthereumAPIAddress, conf.ChainID) + client, err := vgethereum.NewClient(ctx, conf.EthereumAPIAddress) if err != nil { return nil, fmt.Errorf("failed to create Ethereum client: %w", err) } return &Service{ - client: client, - vegaPubKey: vegaPubKey, - erc20BridgeAddress: common.HexToAddress(conf.Erc20BridgeAddress), - stakingBridgeAddress: common.HexToAddress(conf.StakingBridgeAddress), - erc20TokenAddress: common.HexToAddress(baseTokenAddress), - vegaTokenAddress: common.HexToAddress(conf.VegaTokenAddress), - contractOwnerAddress: common.HexToAddress(conf.ContractOwnerAddress), - contractOwnerPrivateKey: conf.ContractOwnerPrivateKey, - syncTimeout: &syncTimeout, - log: log.WithFields(log.Fields{"service": "Token"}), + client: client, + vegaPubKey: vegaPubKey, + erc20BridgeAddress: common.HexToAddress(conf.Erc20BridgeAddress), + stakingBridgeAddress: common.HexToAddress(conf.StakingBridgeAddress), + syncTimeout: &syncTimeout, + log: log.WithFields(log.Fields{"service": "Token"}), }, nil } -func (s *Service) Stake(ctx context.Context, amount *num.Uint) error { - stakingBridge, err := s.client.NewStakingBridgeSession(ctx, s.contractOwnerPrivateKey, s.stakingBridgeAddress, s.syncTimeout) +func (s *Service) Stake(ctx context.Context, ownerPrivateKey, ownerAddress, vegaTokenAddress string, amount *num.Uint) (*num.Uint, error) { + stakingBridge, err := s.client.NewStakingBridgeSession(ctx, ownerPrivateKey, s.stakingBridgeAddress, s.syncTimeout) if err != nil { - return fmt.Errorf("failed to create staking bridge: %w", err) + return nil, fmt.Errorf("failed to create staking bridge: %w", err) } - vegaToken, err := s.client.NewBaseTokenSession(ctx, s.contractOwnerPrivateKey, s.vegaTokenAddress, s.syncTimeout) + vegaToken, err := s.client.NewBaseTokenSession(ctx, ownerPrivateKey, common.HexToAddress(vegaTokenAddress), s.syncTimeout) if err != nil { - return fmt.Errorf("failed to create vega token: %w", err) + return nil, fmt.Errorf("failed to create vega token: %w", err) } - if err = s.mintToken(vegaToken, s.contractOwnerAddress, amount.BigInt()); err != nil { - return fmt.Errorf("failed to mint vegaToken: %w", err) + minted, err := s.mintToken(ctx, vegaToken, common.HexToAddress(ownerAddress), amount.BigInt()) + if err != nil { + return nil, fmt.Errorf("failed to mint vegaToken: %w", err) } - if err = s.approveAndStakeToken(vegaToken, stakingBridge, amount.BigInt()); err != nil { - return fmt.Errorf("failed to approve and stake token on staking bridge: %w", err) + if err = s.approveAndStakeToken(vegaToken, stakingBridge, minted); err != nil { + return nil, fmt.Errorf("failed to approve and stake token on staking bridge: %w", err) } s.log.Debug("Stake request sent") - return nil + staked, overflow := num.UintFromBig(minted) + if overflow { + return nil, fmt.Errorf("overflow when converting minted amount to uint") + } + + return staked, nil } -func (s *Service) Deposit(ctx context.Context, amount *num.Uint) error { - erc20Token, err := s.client.NewBaseTokenSession(ctx, s.contractOwnerPrivateKey, s.erc20TokenAddress, s.syncTimeout) +func (s *Service) Deposit(ctx context.Context, ownerPrivateKey, ownerAddress, erc20TokenAddress string, amount *num.Uint) (*num.Uint, error) { + erc20Token, err := s.client.NewBaseTokenSession(ctx, ownerPrivateKey, common.HexToAddress(erc20TokenAddress), s.syncTimeout) if err != nil { - return fmt.Errorf("failed to create ERC20 token: %w", err) + return nil, fmt.Errorf("failed to create ERC20 token: %w", err) } - erc20bridge, err := s.client.NewERC20BridgeSession(ctx, s.contractOwnerPrivateKey, s.erc20BridgeAddress, s.syncTimeout) + erc20bridge, err := s.client.NewERC20BridgeSession(ctx, ownerPrivateKey, s.erc20BridgeAddress, s.syncTimeout) if err != nil { - return fmt.Errorf("failed to create staking bridge: %w", err) + return nil, fmt.Errorf("failed to create staking bridge: %w", err) } - if err = s.mintToken(erc20Token, s.contractOwnerAddress, amount.BigInt()); err != nil { - return fmt.Errorf("failed to mint erc20Token: %w", err) + minted, err := s.mintToken(ctx, erc20Token, common.HexToAddress(ownerAddress), amount.BigInt()) + if err != nil { + return nil, fmt.Errorf("failed to mint erc20Token token: %w", err) } - if err = s.approveAndDepositToken(erc20Token, erc20bridge, amount.BigInt()); err != nil { - return fmt.Errorf("failed to approve and deposit token on erc20 bridge: %w", err) + if err = s.approveAndDepositToken(erc20Token, erc20bridge, minted); err != nil { + return nil, fmt.Errorf("failed to approve and deposit token on erc20 bridge: %w", err) } s.log.Debug("Deposit request sent") - return nil + deposited, overflow := num.UintFromBig(minted) + if overflow { + return nil, fmt.Errorf("overflow when converting minted amount to uint") + } + + return deposited, nil } type token interface { MintSync(to common.Address, amount *big.Int) (*types.Transaction, error) + MintRawSync(ctx context.Context, toAddress common.Address, amount *big.Int) (*big.Int, error) ApproveSync(spender common.Address, value *big.Int) (*types.Transaction, error) + BalanceOf(owner common.Address) (*big.Int, error) + GetLastTransferValueSync(ctx context.Context, signedTx *types.Transaction) (*big.Int, error) Address() common.Address Name() (string, error) } -func (s *Service) mintToken(token token, address common.Address, amount *big.Int) error { +func (s *Service) mintToken(ctx context.Context, token token, address common.Address, amount *big.Int) (*big.Int, error) { name, err := token.Name() if err != nil { - return fmt.Errorf("failed to get name of token: %w", err) + return nil, fmt.Errorf("failed to get name of token: %w", err) } s.log.WithFields( @@ -125,18 +132,36 @@ func (s *Service) mintToken(token token, address common.Address, amount *big.Int "address": address, }).Debug("Minting new token") - if _, err = token.MintSync(address, amount); err != nil { - return fmt.Errorf("failed to call Mint contract: %w", err) + if tx, err := token.MintSync(address, amount); err == nil { + s.log.WithFields( + log.Fields{ + "token": name, + "amount": amount, + "address": address, + }).Debug("Token minted") + + minted, err := token.GetLastTransferValueSync(ctx, tx) + if err != nil { + return nil, fmt.Errorf("failed to get last transfer value: %w", err) + } + return minted, nil } - s.log.WithFields( - log.Fields{ - "token": name, - "amount": amount, - "address": address, - }).Debug("Token minted") + // plan B + minted, err := token.MintRawSync(ctx, address, amount) + if err != nil { + return nil, fmt.Errorf("failed to mint token: %w", err) + } - return nil + if minted.Cmp(amount) < 0 { + s.log.WithFields( + log.Fields{ + "minted": minted, + "amount": amount, + }).Warning("Minted amount is less than expected") + } + + return minted, nil } func (s *Service) approveAndDepositToken(token token, bridge *vgethereum.ERC20BridgeSession, amount *big.Int) error { diff --git a/token/erc20_service_test.go b/token/erc20_service_test.go new file mode 100644 index 0000000..34ca86e --- /dev/null +++ b/token/erc20_service_test.go @@ -0,0 +1,45 @@ +package token + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + + "code.vegaprotocol.io/liqbot/config" +) + +func TestService_mintToken(t *testing.T) { + conf := &config.TokenConfig{ + EthereumAPIAddress: "wss://ropsten.infura.io/ws/v3/0b0e1795edae41f59f4c99d29ba0ae8e", + } + pubKey := "0x0" + privKey := "" + tokenAddr := common.HexToAddress("0x3773A5c7aFF77e014cBF067dd31801b4C6dc4136") + toAddress := common.HexToAddress("0xb89A165EA8b619c14312dB316BaAa80D2a98B493") + + s, err := NewService(conf, pubKey) + if err != nil { + t.Fatal(err) + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*150) + defer cancel() + + erc20Token, err := s.client.NewBaseTokenSession(ctx, privKey, tokenAddr, s.syncTimeout) + if err != nil { + t.Fatal(err) + } + + amount := big.NewInt(7675000000000) + minted, err := s.mintToken(ctx, erc20Token, toAddress, amount) + if err != nil { + t.Errorf("mintToken() error = %s", err) + } + + if minted.Cmp(amount) != 0 { + t.Errorf("mintToken() = %s, want %s", minted, amount) + } +} diff --git a/token/faucet_service.go b/token/faucet_service.go new file mode 100644 index 0000000..f703f2c --- /dev/null +++ b/token/faucet_service.go @@ -0,0 +1,61 @@ +package token + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + + "code.vegaprotocol.io/liqbot/types/num" +) + +type FaucetService struct { + faucetURL string + walletPubKey string +} + +func NewFaucetService(faucetURL string, walletPubKey string) *FaucetService { + return &FaucetService{ + faucetURL: faucetURL, + walletPubKey: walletPubKey, + } +} + +func (f FaucetService) Mint(ctx context.Context, assetID string, amount *num.Uint) error { + postBody, _ := json.Marshal(struct { + Party string `json:"party"` + Amount string `json:"amount"` + Asset string `json:"asset"` + }{ + f.walletPubKey, + amount.String(), + assetID, + }) + + url := fmt.Sprintf("%s/api/v1/mint", f.faucetURL) + reqBody := bytes.NewBuffer(postBody) + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, reqBody) + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + sb := string(body) + if strings.Contains(sb, "error") { + return fmt.Errorf(sb) + } + + return nil +} diff --git a/types/interfaces.go b/types/interfaces.go new file mode 100644 index 0000000..6125477 --- /dev/null +++ b/types/interfaces.go @@ -0,0 +1,30 @@ +package types + +import ( + "context" + + ppconfig "code.vegaprotocol.io/priceproxy/config" + ppservice "code.vegaprotocol.io/priceproxy/service" + + "code.vegaprotocol.io/liqbot/types/num" +) + +// Bot is the generic bot interface. +// +//go:generate go run github.com/golang/mock/mockgen -destination mocks/bot_mock.go -package mocks code.vegaprotocol.io/liqbot/bot Bot +type Bot interface { + Start() error + Stop() + GetTraderDetails() string +} + +// PricingEngine is the source of price information from the price proxy. +// +//go:generate go run github.com/golang/mock/mockgen -destination mocks/pricingengine_mock.go -package mocks code.vegaprotocol.io/liqbot/bot PricingEngine +type PricingEngine interface { + GetPrice(pricecfg ppconfig.PriceConfig) (pi ppservice.PriceResponse, err error) +} + +type WhaleService interface { + TopUp(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error +} diff --git a/types/num/uint.go b/types/num/uint.go index 364ef13..c806347 100644 --- a/types/num/uint.go +++ b/types/num/uint.go @@ -160,6 +160,9 @@ func (u *Uint) Add(x, y *Uint) *Uint { // so x.AddSum(y, z) is equivalent to x + y + z. func (u *Uint) AddSum(vals ...*Uint) *Uint { for _, x := range vals { + if x == nil { + continue + } u.u.Add(&u.u, &x.u) } return u diff --git a/types/store.go b/types/store.go new file mode 100644 index 0000000..61535f6 --- /dev/null +++ b/types/store.go @@ -0,0 +1,69 @@ +package types + +type Store struct { + market cache[MarketData] + balance cache[Balance] +} + +func NewStore() *Store { + return &Store{ + market: newCache[MarketData](), + balance: newCache[Balance](), + } +} + +func (s *Store) Balance() Balance { + return s.balance.get() +} + +func (s *Store) Market() MarketData { + return s.market.get() +} + +func (s *Store) OpenVolume() int64 { + return s.market.get().openVolume +} + +func (s *Store) BalanceSet(sets ...func(*Balance)) { + s.balance.set(sets...) +} + +func (s *Store) MarketSet(sets ...func(*MarketData)) { + s.market.set(sets...) +} + +type cache[T any] struct { + getCh chan chan T + setCh chan []func(*T) +} + +func (c cache[T]) get() T { + r := make(chan T) + c.getCh <- r + return <-r +} + +func (c cache[T]) set(f ...func(*T)) { + c.setCh <- f +} + +func newCache[T any]() (c cache[T]) { + c.getCh = make(chan chan T) + c.setCh = make(chan []func(*T)) + + go func() { + var d T + for { + select { + case g := <-c.getCh: + g <- d + case s := <-c.setCh: + for _, fn := range s { + fn(&d) + } + } + } + }() + + return +} diff --git a/types/store_test.go b/types/store_test.go new file mode 100644 index 0000000..4958678 --- /dev/null +++ b/types/store_test.go @@ -0,0 +1,73 @@ +package types + +import ( + "testing" + + vt "code.vegaprotocol.io/vega/types" + + "code.vegaprotocol.io/liqbot/types/num" +) + +func Test_newBalanceCache(t *testing.T) { + c := newCache[Balance]() + + general := num.NewUint(12) + margin := num.NewUint(44) + bond := num.NewUint(66) + + c.set( + SetGeneral(general), + SetMargin(margin), + SetBond(bond), + ) + + if !c.get().General().EQ(general) { + t.Errorf("expected %v, got %v", general, c.get().General()) + } + if !c.get().Margin().EQ(margin) { + t.Errorf("expected %v, got %v", margin, c.get().Margin()) + } + if !c.get().Bond().EQ(bond) { + t.Errorf("expected %v, got %v", bond, c.get().Bond()) + } + if !c.get().Total().EQ(num.Sum(general, margin, bond)) { + t.Errorf("expected %v, got %v", num.Sum(general, margin, bond), c.get().Total()) + } +} + +func Test_newMarketCache(t *testing.T) { + m := newCache[MarketData]() + + m.set( + SetStaticMidPrice(num.NewUint(12)), + SetMarkPrice(num.NewUint(44)), + SetTargetStake(num.NewUint(66)), + SetSuppliedStake(num.NewUint(88)), + SetTradingMode(vt.MarketTradingModeContinuous), + SetOpenVolume(12), + ) + + if !m.get().StaticMidPrice().EQ(num.NewUint(12)) { + t.Errorf("expected %v, got %v", num.NewUint(12), m.get().StaticMidPrice()) + } + + if !m.get().MarkPrice().EQ(num.NewUint(44)) { + t.Errorf("expected %v, got %v", num.NewUint(44), m.get().MarkPrice()) + } + + if !m.get().TargetStake().EQ(num.NewUint(66)) { + t.Errorf("expected %v, got %v", num.NewUint(66), m.get().TargetStake()) + } + + if !m.get().SuppliedStake().EQ(num.NewUint(88)) { + t.Errorf("expected %v, got %v", num.NewUint(88), m.get().SuppliedStake()) + } + + if m.get().TradingMode() != vt.MarketTradingModeContinuous { + t.Errorf("expected %v, got %v", vt.MarketTradingModeContinuous, m.get().TradingMode()) + } + + if m.get().OpenVolume() != 12 { + t.Errorf("expected %v, got %v", 12, m.get().OpenVolume()) + } +} diff --git a/types/types.go b/types/types.go index db6857d..972728c 100644 --- a/types/types.go +++ b/types/types.go @@ -1,27 +1,176 @@ package types import ( + "fmt" + "code.vegaprotocol.io/protos/vega" "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/liqbot/util" ) type MarketData struct { - StaticMidPrice *num.Uint - MarkPrice *num.Uint - TargetStake *num.Uint - SuppliedStake *num.Uint - TradingMode vega.Market_TradingMode + staticMidPrice *num.Uint + markPrice *num.Uint + targetStake *num.Uint + suppliedStake *num.Uint + openVolume int64 + tradingMode vega.Market_TradingMode +} + +func SetMarketData(m *MarketData) func(md *MarketData) { + return func(md *MarketData) { + *md = *m + } +} + +func SetStaticMidPrice(staticMidPrice *num.Uint) func(md *MarketData) { + return func(md *MarketData) { + md.staticMidPrice = staticMidPrice.Clone() + } +} + +func SetMarkPrice(markPrice *num.Uint) func(md *MarketData) { + return func(md *MarketData) { + md.markPrice = markPrice.Clone() + } +} + +func SetTargetStake(targetStake *num.Uint) func(md *MarketData) { + return func(md *MarketData) { + md.targetStake = targetStake.Clone() + } +} + +func SetSuppliedStake(suppliedStake *num.Uint) func(md *MarketData) { + return func(md *MarketData) { + md.suppliedStake = suppliedStake.Clone() + } +} + +func SetTradingMode(tradingMode vega.Market_TradingMode) func(md *MarketData) { + return func(md *MarketData) { + md.tradingMode = tradingMode + } +} + +func SetOpenVolume(openVolume int64) func(md *MarketData) { + return func(md *MarketData) { + md.openVolume = openVolume + } +} + +func (md MarketData) StaticMidPrice() *num.Uint { + return md.staticMidPrice.Clone() +} + +func (md MarketData) MarkPrice() *num.Uint { + return md.markPrice.Clone() +} + +func (md MarketData) TargetStake() *num.Uint { + return md.targetStake.Clone() +} + +func (md MarketData) SuppliedStake() *num.Uint { + return md.suppliedStake.Clone() +} + +func (md MarketData) TradingMode() vega.Market_TradingMode { + return md.tradingMode +} + +func (md MarketData) OpenVolume() int64 { + return md.openVolume +} + +func FromVegaMD(marketData *vega.MarketData) (*MarketData, error) { + staticMidPrice, err := util.ConvertUint256(marketData.StaticMidPrice) + if err != nil { + return nil, fmt.Errorf("invalid static mid price: %s", err) + } + + markPrice, err := util.ConvertUint256(marketData.MarkPrice) + if err != nil { + return nil, fmt.Errorf("invalid mark price: %s", err) + } + + targetStake, err := util.ConvertUint256(marketData.TargetStake) + if err != nil { + return nil, fmt.Errorf("invalid target stake: %s", err) + } + + suppliedStake, err := util.ConvertUint256(marketData.SuppliedStake) + if err != nil { + return nil, fmt.Errorf("invalid supplied stake: %s", err) + } + + return &MarketData{ + staticMidPrice: staticMidPrice, + markPrice: markPrice, + targetStake: targetStake, + suppliedStake: suppliedStake, + tradingMode: marketData.MarketTradingMode, + }, nil } type Balance struct { - General *num.Uint - Margin *num.Uint - Bond *num.Uint + general num.Uint + margin num.Uint + bond num.Uint } func (b Balance) Total() *num.Uint { - return num.Sum(b.General, b.Margin, b.Bond) + return num.Sum(&b.general, &b.margin, &b.bond) +} + +func (b Balance) General() *num.Uint { + return &b.general +} + +func (b Balance) Margin() *num.Uint { + return &b.margin +} + +func (b Balance) Bond() *num.Uint { + return &b.bond +} + +func SetBalanceByType(typ vega.AccountType, balance *num.Uint) func(*Balance) { + switch typ { + case vega.AccountType_ACCOUNT_TYPE_GENERAL: + return SetGeneral(balance) + case vega.AccountType_ACCOUNT_TYPE_MARGIN: + return SetMargin(balance) + case vega.AccountType_ACCOUNT_TYPE_BOND: + return SetBond(balance) + } + return func(*Balance) {} +} + +func SetGeneral(general *num.Uint) func(*Balance) { + return func(b *Balance) { + b.general = fromPtr(general) + } +} + +func SetMargin(margin *num.Uint) func(*Balance) { + return func(b *Balance) { + b.margin = fromPtr(margin) + } +} + +func SetBond(bond *num.Uint) func(*Balance) { + return func(b *Balance) { + b.bond = fromPtr(bond) + } +} + +func fromPtr[T any](ptr *T) (t T) { + if ptr == nil { + return + } + return *ptr } type PauseSignal struct { diff --git a/wallet/client.go b/wallet/client.go index 499d927..023fe24 100644 --- a/wallet/client.go +++ b/wallet/client.go @@ -1,3 +1,4 @@ +// TODO: move to a shared lib? package wallet import ( @@ -7,7 +8,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net/http" "strings" @@ -114,7 +114,7 @@ func (c *Client) LoginWallet(ctx context.Context, name, passphrase string) error defer func() { _ = resp.Body.Close() }() - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("failed to read response body: %s", err) } @@ -141,7 +141,7 @@ type keyPairResponse struct { Key *types.Key `json:"key"` } -func (c Client) GenerateKeyPair(ctx context.Context, passphrase string, meta []types.Meta) (*types.Key, error) { +func (c *Client) GenerateKeyPair(ctx context.Context, passphrase string, meta []types.Meta) (*types.Key, error) { postBody, _ := json.Marshal(struct { Passphrase string `json:"passphrase"` Meta []types.Meta `json:"meta"` @@ -168,7 +168,7 @@ func (c Client) GenerateKeyPair(ctx context.Context, passphrase string, meta []t defer func() { _ = resp.Body.Close() }() - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response body: %s", err) } @@ -211,7 +211,7 @@ func (c *Client) ListPublicKeys(ctx context.Context) ([]string, error) { defer func() { _ = resp.Body.Close() }() - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response body: %s", err) } @@ -270,7 +270,7 @@ func (c *Client) SignTx(ctx context.Context, request *walletpb.SubmitTransaction defer func() { _ = resp.Body.Close() }() - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("failed to read response body: %s", err) } diff --git a/whale/interfaces.go b/whale/interfaces.go new file mode 100644 index 0000000..cc47bbc --- /dev/null +++ b/whale/interfaces.go @@ -0,0 +1,35 @@ +package whale + +import ( + "context" + "time" + + dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" + v1 "code.vegaprotocol.io/protos/vega/wallet/v1" + + "code.vegaprotocol.io/liqbot/types/num" +) + +type dataNode interface { + AssetByID(req *dataapipb.AssetByIDRequest) (*dataapipb.AssetByIDResponse, error) + PartyAccounts(req *dataapipb.PartyAccountsRequest) (response *dataapipb.PartyAccountsResponse, err error) + MustDialConnection(ctx context.Context) +} + +type walletClient interface { + LoginWallet(ctx context.Context, name, passphrase string) error + SignTx(ctx context.Context, req *v1.SubmitTransactionRequest) error +} + +type erc20Service interface { + Stake(ctx context.Context, ownerPrivateKey, ownerAddress, vegaTokenAddress string, amount *num.Uint) (*num.Uint, error) + Deposit(ctx context.Context, ownerPrivateKey, ownerAddress, tokenAddress string, amount *num.Uint) (*num.Uint, error) +} + +type faucetClient interface { + Mint(ctx context.Context, assetID string, amount *num.Uint) error +} + +type depositStream interface { + WaitForDepositFinalize(ctx context.Context, settlementAssetID string, amount *num.Uint, timeout time.Duration) error +} diff --git a/whale/service.go b/whale/service.go new file mode 100644 index 0000000..b18db77 --- /dev/null +++ b/whale/service.go @@ -0,0 +1,247 @@ +package whale + +import ( + "context" + "crypto/ecdsa" + "fmt" + "log" + "time" + + dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" + "code.vegaprotocol.io/protos/vega" + commV1 "code.vegaprotocol.io/protos/vega/commands/v1" + "code.vegaprotocol.io/protos/vega/wallet/v1" + vtypes "code.vegaprotocol.io/vega/types" + "github.com/ethereum/go-ethereum/crypto" + + "code.vegaprotocol.io/liqbot/config" + "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/liqbot/util" +) + +type Service struct { + walletName string + walletPassphrase string + walletPubKey string + node dataNode + wallet walletClient + erc20 erc20Service + faucet faucetClient + depositStream depositStream + ownerPrivateKeys map[string]string + callTimeoutMills time.Duration +} + +func NewService( + dataNode dataNode, + wallet walletClient, + erc20 erc20Service, + faucet faucetClient, + deposits depositStream, + config *config.WhaleConfig, +) *Service { + return &Service{ + node: dataNode, + wallet: wallet, + erc20: erc20, + faucet: faucet, + depositStream: deposits, + walletPubKey: config.WalletPubKey, + walletName: config.WalletName, + walletPassphrase: config.WalletPassphrase, + ownerPrivateKeys: config.OwnerPrivateKeys, + callTimeoutMills: time.Duration(config.SyncTimeoutSec) * time.Second, + } +} + +func (s *Service) Start(ctx context.Context) error { + if err := s.wallet.LoginWallet(ctx, s.walletName, s.walletPassphrase); err != nil { + return fmt.Errorf("failed to login to wallet: %w", err) + } + + s.node.MustDialConnection(ctx) + return nil +} + +func (s *Service) TopUp(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error { + if receiverAddress == s.walletPubKey { + return fmt.Errorf("the sender and receiver address cannot be the same") + } + + if err := s.ensureWhaleBalance(ctx, assetID, amount); err != nil { + return fmt.Errorf("failed to ensure enough funds: %w", err) + } + + // TODO: is Vega staked token supposed to be transferred differently? + + err := s.wallet.SignTx(ctx, &v1.SubmitTransactionRequest{ + PubKey: s.walletPubKey, + Propagate: true, + Command: &v1.SubmitTransactionRequest_Transfer{ + Transfer: &commV1.Transfer{ + FromAccountType: vtypes.AccountTypeGeneral, + To: receiverAddress, + ToAccountType: vtypes.AccountTypeGeneral, + Asset: assetID, + Amount: amount.String(), + Reference: fmt.Sprintf("Liquidity Bot '%s' Top-Up", receiverName), + Kind: &commV1.Transfer_OneOff{OneOff: &commV1.OneOffTransfer{}}, + }, + }, + }) + if err != nil { + return fmt.Errorf("failed to top-up bot '%s': %w", receiverName, err) + } + + if err = s.depositStream.WaitForDepositFinalize(ctx, assetID, amount, s.callTimeoutMills); err != nil { + return fmt.Errorf("failed to finalize deposit: %w", err) + } + + return nil +} + +func (s *Service) ensureWhaleBalance(ctx context.Context, assetID string, amount *num.Uint) error { + balance, err := s.getBalance(assetID) + if err != nil { + return fmt.Errorf("failed to check for sufficient funds: %w", err) + } + + if balance.GT(amount.Int()) { + return nil + } + + toDeposit := amount.Mul(amount, num.NewUint(1000)) // deposit more than needed + + if err = s.deposit(ctx, assetID, toDeposit); err != nil { + return fmt.Errorf("failed to deposit: %w", err) + } + + return nil +} + +func (s *Service) getBalance(assetID string) (*num.Int, error) { + // cache the balance + response, err := s.node.PartyAccounts(&dataapipb.PartyAccountsRequest{ + PartyId: s.walletPubKey, + Asset: assetID, + }) + if err != nil { + return nil, fmt.Errorf("failed to get accounts: %w", err) + } + + balance := new(num.Uint) + + for _, account := range response.Accounts { + if account.Type == vtypes.AccountTypeGeneral { + balance, err = util.ConvertUint256(account.Balance) + if err != nil { + return nil, fmt.Errorf("failed to convert account balance: %w", err) + } + } + } + + return balance.Int(), nil +} + +func (s *Service) deposit(ctx context.Context, assetID string, amount *num.Uint) error { + response, err := s.node.AssetByID(&dataapipb.AssetByIDRequest{ + Id: assetID, + }) + if err != nil { + return fmt.Errorf("failed to get asset id: %w", err) + } + + if erc20 := response.Asset.Details.GetErc20(); erc20 != nil { + return s.depositERC20(ctx, response.Asset, amount) + } else if builtin := response.Asset.Details.GetBuiltinAsset(); builtin != nil { + maxFaucet, err := util.ConvertUint256(builtin.MaxFaucetAmountMint) + if err != nil { + return fmt.Errorf("failed to convert max faucet amount: %w", err) + } + return s.depositBuiltin(ctx, assetID, amount, maxFaucet) + } + + return fmt.Errorf("unsupported asset type") +} + +func (s *Service) depositERC20(ctx context.Context, asset *vega.Asset, amount *num.Uint) error { + ownerKey, err := s.getOwnerKeyForAsset(asset.Id) + if err != nil { + return fmt.Errorf("failed to get owner key: %w", err) + } + + contractAddress := asset.Details.GetErc20().ContractAddress + added := new(num.Uint) + + if asset.Details.Symbol == "VEGA" { + added, err = s.erc20.Stake(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, amount) + } else { + added, err = s.erc20.Deposit(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, amount) + } + if err != nil { + return fmt.Errorf("failed to add erc20 token: %w", err) + } + + // TODO: check units + if added.Int().LT(amount.Int()) { + // TODO: how to proceed? + return fmt.Errorf("deposited less than requested amount") + } + + return nil +} + +func (s *Service) depositBuiltin(ctx context.Context, assetID string, amount, maxFaucet *num.Uint) error { + times := int(new(num.Uint).Div(amount, maxFaucet).Uint64() + 1) + + for i := 0; i < times; i++ { + if err := s.faucet.Mint(ctx, assetID, maxFaucet); err != nil { + return fmt.Errorf("failed to deposit: %w", err) + } + time.Sleep(2 * time.Second) // TODO: configure + } + return nil +} + +type key struct { + privateKey string + address string +} + +func (s *Service) getOwnerKeyForAsset(assetID string) (*key, error) { + ownerPrivateKey, ok := s.ownerPrivateKeys[assetID] + if !ok { + return nil, fmt.Errorf("owner private key not configured for asset '%s'", assetID) + } + + address, err := addressFromPrivateKey(ownerPrivateKey) + if err != nil { + return nil, fmt.Errorf("failed to get address from private key: %w", err) + } + + return &key{ + privateKey: ownerPrivateKey, + address: address, + }, nil +} + +func addressFromPrivateKey(privateKey string) (string, error) { + key, err := crypto.HexToECDSA(privateKey) + if err != nil { + return "", fmt.Errorf("failed to convert owner private key hash into ECDSA: %w", err) + } + + publicKeyECDSA, ok := key.Public().(*ecdsa.PublicKey) + if !ok { + return "", fmt.Errorf("cannot assert type: publicKey is not of type *ecdsa.PublicKey") + } + + address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() + return address, nil +} + +func (s *Service) slackDan(_ context.Context, address, assetID string, amount *num.Uint) error { + // TODO: slack @Dan + log.Printf("slack @Dan: %s %s %s", address, assetID, amount) + return nil +} From a81130daa237da8b542afa2d8fc256a31a9874d6 Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Wed, 28 Sep 2022 16:05:28 +0100 Subject: [PATCH 04/18] WIP --- bot/normal/interfaces.go | 20 +++++-- bot/normal/market.go | 11 ++-- bot/normal/normal.go | 30 +++++------ bot/normal/position_management.go | 10 ++-- config/config_stagnet2.yaml | 87 +++++++++++++++++++++++++++++++ data/streamingdata.go | 3 ++ wallet/client.go | 9 +++- whale/service.go | 23 ++++++-- whale/service_test.go | 64 +++++++++++++++++++++++ 9 files changed, 221 insertions(+), 36 deletions(-) create mode 100644 config/config_stagnet2.yaml create mode 100644 whale/service_test.go diff --git a/bot/normal/interfaces.go b/bot/normal/interfaces.go index 201a84f..bdb6284 100644 --- a/bot/normal/interfaces.go +++ b/bot/normal/interfaces.go @@ -6,6 +6,7 @@ import ( ppconfig "code.vegaprotocol.io/priceproxy/config" ppservice "code.vegaprotocol.io/priceproxy/service" dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" + "code.vegaprotocol.io/protos/vega" "code.vegaprotocol.io/protos/vega/wallet/v1" "code.vegaprotocol.io/liqbot/data" @@ -17,10 +18,10 @@ import ( type tradingDataService interface { MustDialConnection(ctx context.Context) Target() string - Markets(req *dataapipb.MarketsRequest) (*dataapipb.MarketsResponse, error) // BOT - AssetByID(req *dataapipb.AssetByIDRequest) (*dataapipb.AssetByIDResponse, error) // BOT + Markets(req *dataapipb.MarketsRequest) (*dataapipb.MarketsResponse, error) // TODO: bot should probably not have to worry about finding markets } +// TODO: PricingEngine response data could be cached in the data service, along with other external data sources. // PricingEngine is the source of price information from the price proxy. // //go:generate go run github.com/golang/mock/mockgen -destination mocks/pricingengine_mock.go -package mocks code.vegaprotocol.io/liqbot/bot/normal PricingEngine @@ -28,6 +29,7 @@ type PricingEngine interface { GetPrice(pricecfg ppconfig.PriceConfig) (ppservice.PriceResponse, error) } +// TODO: move all account related stuff to an account service. type WalletClient interface { CreateWallet(ctx context.Context, name, passphrase string) error LoginWallet(ctx context.Context, name, passphrase string) error @@ -37,10 +39,11 @@ type WalletClient interface { } type dataStore interface { - Balance() types.Balance + Balance() types.Balance // TODO: might not be needed by the bot. Market() types.MarketData } +// TODO: this should be in some kind of a market service type marketStream interface { Setup(walletPubKey string, pauseCh chan types.PauseSignal) WaitForStakeLinking() error @@ -48,10 +51,21 @@ type marketStream interface { WaitForProposalEnacted(pID string) error } +// TODO: this could be improved: pubKey could be specified in config, type dataStream interface { InitData(pubKey, marketID, settlementAssetID string, pauseCh chan types.PauseSignal) (data.GetDataStore, error) } +// TODO: probably should be moved to be used by a new service (account/market/service?). type whaleService interface { TopUp(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error } + +// MarketService should provide on-demand, up-to-date market data for the bot, as well as +// allow the bot to send liquidity provision, amendment and cancellation, and place orders. +type MarketService interface { + Data() types.MarketData // TODO: include current external price (no caching because it keeps changing). + ProvideLiquidity(ctx context.Context, buys, sells []*vega.LiquidityOrder) error + FlipDirection(ctx context.Context, buys, sells []*vega.LiquidityOrder) error + Order(ctx context.Context, price *num.Uint, size uint64, side vega.Side, tif vega.Order_TimeInForce, orderType vega.Order_Type, reference string) error +} diff --git a/bot/normal/market.go b/bot/normal/market.go index 22f4d44..a414fbe 100644 --- a/bot/normal/market.go +++ b/bot/normal/market.go @@ -7,13 +7,14 @@ import ( "strings" "time" + log "github.com/sirupsen/logrus" + dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" "code.vegaprotocol.io/protos/vega" commandspb "code.vegaprotocol.io/protos/vega/commands/v1" v1 "code.vegaprotocol.io/protos/vega/commands/v1" oraclesv1 "code.vegaprotocol.io/protos/vega/oracles/v1" walletpb "code.vegaprotocol.io/protos/vega/wallet/v1" - log "github.com/sirupsen/logrus" "code.vegaprotocol.io/liqbot/types/num" ) @@ -152,9 +153,9 @@ func (b *bot) sendNewMarketProposal(ctx context.Context) error { } submitTxReq := &walletpb.SubmitTransactionRequest{ - PubKey: b.walletPubKey, - Propagate: true, - Command: cmd, + PubKey: b.walletPubKey, + // Propagate: true, TODO: OK to remove? + Command: cmd, } if err := b.walletClient.SignTx(ctx, submitTxReq); err != nil { @@ -308,7 +309,7 @@ func (b *bot) getExampleMarketProposal() *v1.ProposalSubmission { }, Reference: "ProposalReference", Terms: &vega.ProposalTerms{ - ValidationTimestamp: secondsFromNowInSecs(1), + ValidationTimestamp: secondsFromNowInSecs(1), // TODO: was removed? ClosingTimestamp: secondsFromNowInSecs(10), EnactmentTimestamp: secondsFromNowInSecs(15), Change: &vega.ProposalTerms_NewMarket{ diff --git a/bot/normal/normal.go b/bot/normal/normal.go index 0cab99a..8c576af 100644 --- a/bot/normal/normal.go +++ b/bot/normal/normal.go @@ -9,10 +9,10 @@ import ( "strings" "sync" - dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" - "code.vegaprotocol.io/vegawallet/wallets" log "github.com/sirupsen/logrus" + "code.vegaprotocol.io/vegawallet/wallets" + "code.vegaprotocol.io/liqbot/config" "code.vegaprotocol.io/liqbot/types" "code.vegaprotocol.io/liqbot/types/num" @@ -46,6 +46,14 @@ type bot struct { mu sync.Mutex } +// TODO: there could be a service that would be in charge of managing the account, balance of the account, creating markets +// and simplifying the process of placing orders. +// The bot should only have to worry decision making about what orders to place and when, given the current market state. +// The service should be responsible for the following: +// - creating markets (if necessary) +// - placing orders, as produced by the bot +// - managing the account balance + // New returns a new instance of bot. func New( botConf config.BotConfig, @@ -109,17 +117,6 @@ func (b *bot) Start() error { "settlementAssetID": b.settlementAssetID, }).Info("Fetched market info") - // Use the settlementAssetID to lookup the settlement ethereum address - assetResponse, err := b.node.AssetByID(&dataapipb.AssetByIDRequest{Id: b.settlementAssetID}) - if err != nil { - return fmt.Errorf("unable to look up asset details for %s: %w", b.settlementAssetID, err) - } - - b.settlementAssetAddress = b.settlementAssetID - if erc20 := assetResponse.Asset.Details.GetErc20(); erc20 != nil { - b.settlementAssetAddress = erc20.ContractAddress - } - b.dataStore, err = b.dataStream.InitData(b.walletPubKey, b.marketID, b.settlementAssetID, pauseCh) if err != nil { return fmt.Errorf("failed to create data stream: %w", err) @@ -215,10 +212,9 @@ func (b *bot) Stop() { // GetTraderDetails returns information relating to the trader. func (b *bot) GetTraderDetails() string { jsn, _ := json.MarshalIndent(map[string]string{ - "name": b.config.Name, - "pubKey": b.walletPubKey, - "settlementVegaAssetID": b.settlementAssetID, - "settlementEthereumContractAddress": b.settlementAssetAddress, + "name": b.config.Name, + "pubKey": b.walletPubKey, + "settlementVegaAssetID": b.settlementAssetID, }, "", " ") return string(jsn) } diff --git a/bot/normal/position_management.go b/bot/normal/position_management.go index 4a365a4..966b35a 100644 --- a/bot/normal/position_management.go +++ b/bot/normal/position_management.go @@ -21,7 +21,7 @@ import ( func (b *bot) runPositionManagement(ctx context.Context) { defer b.log.Warning("PositionManagement: Stopped") - if err := b.prePositionManagement(ctx); err != nil { + if err := b.provideLiquidity(ctx); err != nil { b.log.WithFields(log.Fields{"error": err.Error()}).Error("PositionManagement: Failed to initialize") return } @@ -235,7 +235,7 @@ func (b *bot) sendLiquidityProvision(ctx context.Context, commitment *num.Uint, // call this if the position flips. func (b *bot) sendLiquidityProvisionAmendment(ctx context.Context, commitment *num.Uint, buys, sells []*vega.LiquidityOrder) error { - if commitment == num.Zero() { + if commitment.IsZero() { return b.sendLiquidityProvisionCancellation(ctx) } @@ -303,7 +303,7 @@ func (b *bot) checkPosition() (uint64, vega.Side, bool) { return size, side, shouldPlace } -func (b *bot) prePositionManagement(ctx context.Context) error { +func (b *bot) provideLiquidity(ctx context.Context) error { // We always cache off with longening shapes buyShape, sellShape, _ := b.getShape() // At the cache of each loop, wait for positive general account balance. This is in case the network has @@ -453,8 +453,8 @@ func (b *bot) calculateOrderSizes(obligation *num.Uint, liquidityOrders []*vega. } order := vega.Order{ - MarketId: b.marketID, - PartyId: b.walletPubKey, + //MarketId: b.marketID, + //PartyId: b.walletPubKey, Side: vega.Side_SIDE_BUY, Remaining: size.Uint64(), Size: size.Uint64(), diff --git a/config/config_stagnet2.yaml b/config/config_stagnet2.yaml new file mode 100644 index 0000000..f3f3a79 --- /dev/null +++ b/config/config_stagnet2.yaml @@ -0,0 +1,87 @@ +--- + +server: + env: prod # dev, prod + listen: ":7800" + logformat: text # json, text + loglevel: debug # debug, info, warning, error + +callTimeoutMills: 10000 +vegaAssetID: fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55 + +pricing: + address: + scheme: https + host: prices.ops.vega.xyz + path: /prices +wallet: + url: https://wallet.stagnet2.vega.xyz +token: + ethereumAPIAddress: wss://ropsten.infura.io/ws/v3/0b0e1795edae41f59f4c99d29ba0ae8e + erc20BridgeAddress: 0x947893AaA0A7b55f66990b3B4781514b691Fdd4a + stakingBridgeAddress: 0x7896C9491962D5839783CB6e0492ECebd34Bb35F + syncTimeoutSec: 100 +whale: + walletPubKey: 8c81d3c1e6fd8a21323ec5cd3d1b3d1a9512a53ff7964f9429f07fa2c379e084 + walletName: b00 + walletPassphrase: 123 + ownerPrivateKeys: + 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede: adef89153e4bd6b43876045efdd6818cec359340683edaec5e8588e635e8428b + faucetURL: +locations: + - n01.stagnet2.vega.xyz:3007 + - n02.stagnet2.vega.xyz:3007 + - n03.stagnet2.vega.xyz:3007 + - n04.stagnet2.vega.xyz:3007 + - n05.stagnet2.vega.xyz:3007 +bots: + + - name: b01 + instrumentBase: BTC + instrumentQuote: USD + quoteAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede + strategy: normal + strategyDetails: + expectedMarkPrice: 0 + auctionVolume: 0 + maxLong: 1000 + maxShort: 1000 + posManagementFraction: 0.25 + ordersFraction: 0.25 + seedAmount: 1000000000000000000 + seedOrderSize: 400 + commitmentAmount: 50000000000000 + fee: 0.001 + shorteningShape: + sells: + - {reference: Ask, proportion: 10, offset: 440000} # had to multiply by 10000, should it be the same for all markets? + - {reference: Ask, proportion: 20, offset: 220000} + - {reference: Ask, proportion: 30, offset: 210000} + - {reference: Ask, proportion: 40, offset: 200000} + buys: + - {reference: Bid, proportion: 40, offset: 400000} + - {reference: Bid, proportion: 30, offset: 410000} + - {reference: Bid, proportion: 20, offset: 420000} + - {reference: Bid, proportion: 10, offset: 840000} + longeningShape: + sells: + - {reference: Ask, proportion: 10, offset: 840000} + - {reference: Ask, proportion: 20, offset: 420000} + - {reference: Ask, proportion: 30, offset: 410000} + - {reference: Ask, proportion: 40, offset: 400000} + buys: + - {reference: Bid, proportion: 40, offset: 200000} + - {reference: Bid, proportion: 30, offset: 210000} + - {reference: Bid, proportion: 20, offset: 220000} + - {reference: Bid, proportion: 10, offset: 440000} + posManagementSleepMilliseconds: 5050 + marketPriceSteeringRatePerSecond: 0.1 + minPriceSteerFraction: 0.00001 + priceSteerOrderScale: 1.5 + limitOrderDistributionParams: + method: "discreteThreeLevel" + gttLengthSeconds: 60 + tgtTimeHorizonHours: 1.0 + numTicksFromMid: 5 + numIdenticalBots: 1 + targetLNVol: 0.25 diff --git a/data/streamingdata.go b/data/streamingdata.go index 6da52d7..1f6be25 100644 --- a/data/streamingdata.go +++ b/data/streamingdata.go @@ -73,6 +73,7 @@ func (d *data) setInitialData() error { return nil } +// TODO: should be used by a new market service instead of by the bot func (d *data) subscribeToMarketEvents() { req := &coreapipb.ObserveEventBusRequest{ Type: []eventspb.BusEventType{ @@ -98,6 +99,7 @@ func (d *data) subscribeToMarketEvents() { d.busEvProc.processEvents(context.Background(), "MarketData", req, proc) } +// TODO: should be used by a new account service instead of by the bot // Party related events. func (d *data) subscribeToAccountEvents() { req := &coreapipb.ObserveEventBusRequest{ @@ -130,6 +132,7 @@ func (d *data) subscribeToAccountEvents() { d.busEvProc.processEvents(context.Background(), "AccountData", req, proc) } +// TODO: should be used by a new market service instead of by the bot func (d *data) subscribePositions() { req := &coreapipb.ObserveEventBusRequest{ Type: []eventspb.BusEventType{ diff --git a/wallet/client.go b/wallet/client.go index 023fe24..6c4e849 100644 --- a/wallet/client.go +++ b/wallet/client.go @@ -11,9 +11,10 @@ import ( "net/http" "strings" - walletpb "code.vegaprotocol.io/protos/vega/wallet/v1" "github.com/golang/protobuf/jsonpb" + walletpb "code.vegaprotocol.io/protos/vega/wallet/v1" + "code.vegaprotocol.io/liqbot/types" ) @@ -23,6 +24,7 @@ type Client struct { token string name string pass string + pubKey string // TODO: setup wallet and set this } func NewClient(walletURL string) *Client { @@ -241,7 +243,12 @@ type SignTxRequest struct { Propagate bool `json:"propagate"` } +// TODO: make a wallet service that would run commands instead of tx requests func (c *Client) SignTx(ctx context.Context, request *walletpb.SubmitTransactionRequest) error { + if c.pubKey != "" { + request.PubKey = c.pubKey + } + m := jsonpb.Marshaler{Indent: " "} request.Propagate = true diff --git a/whale/service.go b/whale/service.go index b18db77..f0a8ef7 100644 --- a/whale/service.go +++ b/whale/service.go @@ -7,12 +7,13 @@ import ( "log" "time" + "github.com/ethereum/go-ethereum/crypto" + dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" "code.vegaprotocol.io/protos/vega" commV1 "code.vegaprotocol.io/protos/vega/commands/v1" "code.vegaprotocol.io/protos/vega/wallet/v1" vtypes "code.vegaprotocol.io/vega/types" - "github.com/ethereum/go-ethereum/crypto" "code.vegaprotocol.io/liqbot/config" "code.vegaprotocol.io/liqbot/types/num" @@ -65,16 +66,25 @@ func (s *Service) Start(ctx context.Context) error { func (s *Service) TopUp(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error { if receiverAddress == s.walletPubKey { - return fmt.Errorf("the sender and receiver address cannot be the same") + //return fmt.Errorf("the sender and receiver address cannot be the same") } if err := s.ensureWhaleBalance(ctx, assetID, amount); err != nil { return fmt.Errorf("failed to ensure enough funds: %w", err) } + balance, err := s.getBalance(assetID) + if err != nil { + return fmt.Errorf("failed to check for sufficient funds: %w", err) + } + + if balance.LT(amount.Int()) { + return fmt.Errorf("insufficient funds") + } + // TODO: is Vega staked token supposed to be transferred differently? - err := s.wallet.SignTx(ctx, &v1.SubmitTransactionRequest{ + err = s.wallet.SignTx(ctx, &v1.SubmitTransactionRequest{ PubKey: s.walletPubKey, Propagate: true, Command: &v1.SubmitTransactionRequest_Transfer{ @@ -173,11 +183,14 @@ func (s *Service) depositERC20(ctx context.Context, asset *vega.Asset, amount *n contractAddress := asset.Details.GetErc20().ContractAddress added := new(num.Uint) + var depFn func(ctx context.Context, ownerPrivateKey string, ownerAddress string, vegaTokenAddress string, amount *num.Uint) (*num.Uint, error) + if asset.Details.Symbol == "VEGA" { - added, err = s.erc20.Stake(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, amount) + depFn = s.erc20.Stake } else { - added, err = s.erc20.Deposit(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, amount) + depFn = s.erc20.Deposit } + added, err = depFn(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, amount) if err != nil { return fmt.Errorf("failed to add erc20 token: %w", err) } diff --git a/whale/service_test.go b/whale/service_test.go new file mode 100644 index 0000000..8250594 --- /dev/null +++ b/whale/service_test.go @@ -0,0 +1,64 @@ +package whale + +import ( + "context" + "testing" + + "code.vegaprotocol.io/liqbot/config" + "code.vegaprotocol.io/liqbot/data" + "code.vegaprotocol.io/liqbot/node" + "code.vegaprotocol.io/liqbot/token" + "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/liqbot/wallet" +) + +func TestService_TopUp(t *testing.T) { + wKey := "8528c773a4b62d7609182cb0eb66dd1545173332b569397b8b1f7c1901f90932" + wName := "your_wallet_name" + wPassphrase := "super-secret" + + dn := node.NewDataNode([]string{"localhost:3027"}, 10000) + wc := wallet.NewClient("http://localhost:1789") + es, err := token.NewService(&config.TokenConfig{ + EthereumAPIAddress: "ws://127.0.0.1:8545", + Erc20BridgeAddress: "0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54", + StakingBridgeAddress: "0x9135f5afd6F055e731bca2348429482eE614CFfA", + SyncTimeoutSec: 100, + }, wKey) + if err != nil { + t.Errorf("could not create erc20 service = %s", err) + return + } + + fc := token.NewFaucetService("http://localhost:1790", wKey) + ds := data.NewDepositStream(dn, wKey) + + dk := map[string]string{ + "993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede": "a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0", + "b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b": "a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0", + } + + conf := &config.WhaleConfig{ + WalletPubKey: wKey, + WalletName: wName, + WalletPassphrase: wPassphrase, + OwnerPrivateKeys: dk, + SyncTimeoutSec: 100, + } + + s := NewService(dn, wc, es, fc, ds, conf) + + ctx := context.Background() + + if err := s.Start(ctx); err != nil { + t.Errorf("Start() error = %s", err) + return + } + + key := "8528c773a4b62d7609182cb0eb66dd1545173332b569397b8b1f7c1901f90932" + asset := "b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b" + // asset := "993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede" + if err := s.TopUp(ctx, "some bot", key, asset, num.NewUint(1100000000000000000)); err != nil { + t.Errorf("TopUp() error = %s", err) + } +} From 8e3f030be34409dced647356fa20f663c05a2a83 Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Fri, 30 Sep 2022 05:00:59 +0100 Subject: [PATCH 05/18] WIP, mostly working --- account/interfaces.go | 17 + account/service.go | 142 +++ bot/bot.go | 26 +- bot/normal/helpers.go | 2 +- bot/normal/interfaces.go | 74 +- bot/normal/market.go | 391 ------ bot/normal/mocks/datanode_mock.go | 4 +- bot/normal/normal.go | 139 +-- bot/normal/position_management.go | 50 +- bot/normal/price_steering.go | 57 +- config/config.go | 8 + config/config.yaml | 14 +- config/config_capsule.yaml | 30 +- ...fig_stagnet2.yaml => config_stagnet3.yaml} | 31 +- config/strategy.go | 2 +- data/datarequests.go | 101 -- data/event_process.go | 25 +- data/interfaces.go | 15 +- data/mocks/datanode_mock.go | 4 +- data/streamingaccount.go | 220 ++++ data/streamingdata.go | 162 --- data/streamingdeposit.go | 59 - data/streamingmarket.go | 156 ++- go.mod | 38 +- go.sum | 1070 +---------------- market/interfaces.go | 39 + market/service.go | 466 +++++++ node/datanode.go | 5 +- service/service.go | 26 +- token/erc20_service.go | 25 +- types/interfaces.go | 5 +- types/store.go | 34 +- types/store_test.go | 3 +- types/types.go | 2 +- types/vega.go | 2 +- wallet/client.go | 3 +- whale/interfaces.go | 16 +- whale/provider.go | 355 ++++++ whale/service.go | 265 +--- whale/service_test.go | 76 +- 40 files changed, 1872 insertions(+), 2287 deletions(-) create mode 100644 account/interfaces.go create mode 100644 account/service.go delete mode 100644 bot/normal/market.go rename config/{config_stagnet2.yaml => config_stagnet3.yaml} (81%) delete mode 100644 data/datarequests.go create mode 100644 data/streamingaccount.go delete mode 100644 data/streamingdata.go delete mode 100644 data/streamingdeposit.go create mode 100644 market/interfaces.go create mode 100644 market/service.go create mode 100644 whale/provider.go diff --git a/account/interfaces.go b/account/interfaces.go new file mode 100644 index 0000000..7f3b014 --- /dev/null +++ b/account/interfaces.go @@ -0,0 +1,17 @@ +package account + +import ( + "context" + "time" + + "code.vegaprotocol.io/liqbot/data" + "code.vegaprotocol.io/liqbot/types" + "code.vegaprotocol.io/liqbot/types/num" +) + +type accountStream interface { + Init(pubKey string, pauseCh chan types.PauseSignal) + InitBalances(assetID string) (data.BalanceStore, error) + WaitForStakeLinking(pubKey string) error + WaitForDepositFinalise(ctx context.Context, walletPubKey, assetID string, amount *num.Uint, timeout time.Duration) error +} diff --git a/account/service.go b/account/service.go new file mode 100644 index 0000000..5418ced --- /dev/null +++ b/account/service.go @@ -0,0 +1,142 @@ +package account + +import ( + "context" + "fmt" + + log "github.com/sirupsen/logrus" + + "code.vegaprotocol.io/liqbot/data" + "code.vegaprotocol.io/liqbot/types" + "code.vegaprotocol.io/liqbot/types/num" +) + +type Service struct { + name string + pubKey string + assetID string + stores map[string]data.BalanceStore + accountStream accountStream + coinProvider types.CoinProvider + log *log.Entry +} + +func NewAccountService(name, assetID string, accountStream accountStream, coinProvider types.CoinProvider) *Service { + return &Service{ + name: name, + assetID: assetID, + accountStream: accountStream, + coinProvider: coinProvider, + log: log.WithField("component", "AccountService"), + } +} + +func (a *Service) Init(pubKey string, pauseCh chan types.PauseSignal) { + a.stores = make(map[string]data.BalanceStore) + a.pubKey = pubKey + a.accountStream.Init(pubKey, pauseCh) +} + +func (a *Service) EnsureBalance(ctx context.Context, assetID string, targetAmount *num.Uint, from string) error { + store, err := a.getStore(assetID) + if err != nil { + return err + } + + balanceTotal := store.Balance().Total() // TODO: should it be total balance? + + a.log.WithFields( + log.Fields{ + "balanceTotal": balanceTotal.String(), + }).Debugf("%s: Total account balance", from) + + if balanceTotal.GT(targetAmount) { + return nil + } + + a.log.WithFields( + log.Fields{ + "balanceTotal": balanceTotal.String(), + "targetAmount": targetAmount.String(), + }).Debugf("%s: Account balance is less than target amount, depositing...", from) + + if err = a.coinProvider.TopUpAsync(ctx, a.name, a.pubKey, assetID, targetAmount); err != nil { + return fmt.Errorf("failed to top up: %w", err) + } + + a.log.Debugf("%s: Waiting for top-up...", from) + + if err = a.accountStream.WaitForDepositFinalise(ctx, a.pubKey, assetID, targetAmount, 0); err != nil { + return fmt.Errorf("failed to finalise deposit: %w", err) + } + + return nil +} + +// TODO: DRY +func (a *Service) EnsureStake(ctx context.Context, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error { + if receiverPubKey == "" { + return fmt.Errorf("receiver public key is empty") + } + + store, err := a.getStore(assetID) + if err != nil { + return err + } + + balanceTotal := store.Balance().Total() // TODO: should it be total balance? + + a.log.WithFields( + log.Fields{ + "balanceTotal": balanceTotal.String(), + }).Debugf("%s: Total account stake balance", from) + + if balanceTotal.GT(targetAmount) { + return nil + } + + a.log.WithFields( + log.Fields{ + "balanceTotal": balanceTotal.String(), + "targetAmount": targetAmount.String(), + }).Debugf("%s: Account Stake balance is less than target amount, staking...", from) + + if err = a.coinProvider.StakeAsync(ctx, receiverPubKey, assetID, targetAmount); err != nil { + return fmt.Errorf("failed to stake: %w", err) + } + + a.log.Debugf("%s: Waiting for staking...", from) + + if err = a.accountStream.WaitForStakeLinking(receiverPubKey); err != nil { + return fmt.Errorf("failed to finalise stake: %w", err) + } + + return nil +} + +func (a *Service) StakeAsync(ctx context.Context, receiverPubKey, assetID string, amount *num.Uint) error { + return a.coinProvider.StakeAsync(ctx, receiverPubKey, assetID, amount) +} + +func (a *Service) Balance() types.Balance { + store, err := a.getStore(a.assetID) + if err != nil { + a.log.WithError(err).Error("failed to get balance store") + return types.Balance{} + } + return store.Balance() +} + +func (a *Service) getStore(assetID string) (_ data.BalanceStore, err error) { + store, ok := a.stores[assetID] + if !ok { + store, err = a.accountStream.InitBalances(assetID) + if err != nil { + return nil, fmt.Errorf("failed to initialise balances for '%s': %w", assetID, err) + } + + a.stores[assetID] = store + } + + return store, nil +} diff --git a/bot/bot.go b/bot/bot.go index e6a811a..2d19c7b 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -1,12 +1,17 @@ package bot import ( + "context" "errors" "fmt" + log "github.com/sirupsen/logrus" + + "code.vegaprotocol.io/liqbot/account" "code.vegaprotocol.io/liqbot/bot/normal" "code.vegaprotocol.io/liqbot/config" "code.vegaprotocol.io/liqbot/data" + "code.vegaprotocol.io/liqbot/market" "code.vegaprotocol.io/liqbot/node" "code.vegaprotocol.io/liqbot/types" "code.vegaprotocol.io/liqbot/wallet" @@ -17,7 +22,7 @@ func New( botConf config.BotConfig, conf config.Config, pricing types.PricingEngine, - whale types.WhaleService, + whale types.CoinProvider, ) (types.Bot, error) { switch botConf.Strategy { case config.BotStrategyNormal: @@ -35,25 +40,28 @@ func newNormalBot( botConf config.BotConfig, conf config.Config, pricing types.PricingEngine, - whale types.WhaleService, + whale types.CoinProvider, ) (types.Bot, error) { dataNode := node.NewDataNode( conf.Locations, conf.CallTimeoutMills, ) - marketStream := data.NewMarketStream(dataNode) - streamData := data.NewStreamData(dataNode) + log.Debug("Attempting to connect to Vega gRPC node...") + dataNode.MustDialConnection(context.Background()) // blocking + botWallet := wallet.NewClient(conf.Wallet.URL) + depositStream := data.NewAccountStream(dataNode) + accountService := account.NewAccountService(botConf.Name, botConf.SettlementAssetID, depositStream, whale) + + marketStream := data.NewMarketStream(dataNode) + marketService := market.NewService(marketStream, dataNode, botWallet, pricing, accountService, botConf, conf.VegaAssetID) return normal.New( botConf, conf.VegaAssetID, - dataNode, - marketStream, - streamData, - pricing, botWallet, - whale, + accountService, + marketService, ), nil } diff --git a/bot/normal/helpers.go b/bot/normal/helpers.go index dba4432..cbcba39 100644 --- a/bot/normal/helpers.go +++ b/bot/normal/helpers.go @@ -119,7 +119,7 @@ func generatePriceUsingDiscreteThreeLevel(m0, delta, sigma, tgtTimeHorizonYrFrac price = m0 * y - return price, err + return price, nil } func randomChoice(probabilities []float64) uint64 { diff --git a/bot/normal/interfaces.go b/bot/normal/interfaces.go index bdb6284..4fc14cb 100644 --- a/bot/normal/interfaces.go +++ b/bot/normal/interfaces.go @@ -3,32 +3,12 @@ package normal import ( "context" - ppconfig "code.vegaprotocol.io/priceproxy/config" - ppservice "code.vegaprotocol.io/priceproxy/service" - dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" - "code.vegaprotocol.io/protos/vega" - "code.vegaprotocol.io/protos/vega/wallet/v1" - - "code.vegaprotocol.io/liqbot/data" "code.vegaprotocol.io/liqbot/types" "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/vega/protos/vega" + "code.vegaprotocol.io/vega/protos/vega/wallet/v1" ) -// TradingDataService implements the gRPC service of the same name. -type tradingDataService interface { - MustDialConnection(ctx context.Context) - Target() string - Markets(req *dataapipb.MarketsRequest) (*dataapipb.MarketsResponse, error) // TODO: bot should probably not have to worry about finding markets -} - -// TODO: PricingEngine response data could be cached in the data service, along with other external data sources. -// PricingEngine is the source of price information from the price proxy. -// -//go:generate go run github.com/golang/mock/mockgen -destination mocks/pricingengine_mock.go -package mocks code.vegaprotocol.io/liqbot/bot/normal PricingEngine -type PricingEngine interface { - GetPrice(pricecfg ppconfig.PriceConfig) (ppservice.PriceResponse, error) -} - // TODO: move all account related stuff to an account service. type WalletClient interface { CreateWallet(ctx context.Context, name, passphrase string) error @@ -38,34 +18,30 @@ type WalletClient interface { SignTx(ctx context.Context, req *v1.SubmitTransactionRequest) error } -type dataStore interface { - Balance() types.Balance // TODO: might not be needed by the bot. - Market() types.MarketData -} - -// TODO: this should be in some kind of a market service -type marketStream interface { - Setup(walletPubKey string, pauseCh chan types.PauseSignal) - WaitForStakeLinking() error - WaitForProposalID() (string, error) - WaitForProposalEnacted(pID string) error -} - -// TODO: this could be improved: pubKey could be specified in config, -type dataStream interface { - InitData(pubKey, marketID, settlementAssetID string, pauseCh chan types.PauseSignal) (data.GetDataStore, error) -} +/* +// MarketService should provide on-demand, up-to-date market data for the bot, as well as +// allow the bot to send liquidity provision, amendment and cancellation, and place orders. -// TODO: probably should be moved to be used by a new service (account/market/service?). -type whaleService interface { - TopUp(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error + type MarketService interface { + Data() types.MarketData // TODO: include current external price (no caching because it keeps changing). + ProvideLiquidity(ctx context.Context, buys, sells []*vega.LiquidityOrder) error + FlipDirection(ctx context.Context, buys, sells []*vega.LiquidityOrder) error + Order(ctx context.Context, price *num.Uint, size uint64, side vega.Side, tif vega.Order_TimeInForce, orderType vega.Order_Type, reference string) error + } +*/ +type marketService interface { + Init(pubKey string, pauseCh chan types.PauseSignal) error + Start(marketID string) error + Market() types.MarketData + CanPlaceOrders() bool + SubmitOrder(ctx context.Context, order *vega.Order, from string, secondsFromNow int64) error + SeedOrders(ctx context.Context, from string) error + SetupMarket(ctx context.Context) (*vega.Market, error) + GetExternalPrice() (*num.Uint, error) } -// MarketService should provide on-demand, up-to-date market data for the bot, as well as -// allow the bot to send liquidity provision, amendment and cancellation, and place orders. -type MarketService interface { - Data() types.MarketData // TODO: include current external price (no caching because it keeps changing). - ProvideLiquidity(ctx context.Context, buys, sells []*vega.LiquidityOrder) error - FlipDirection(ctx context.Context, buys, sells []*vega.LiquidityOrder) error - Order(ctx context.Context, price *num.Uint, size uint64, side vega.Side, tif vega.Order_TimeInForce, orderType vega.Order_Type, reference string) error +type accountService interface { + Init(pubKey string, pauseCh chan types.PauseSignal) + Balance() types.Balance + EnsureBalance(ctx context.Context, assetID string, targetAmount *num.Uint, from string) error } diff --git a/bot/normal/market.go b/bot/normal/market.go deleted file mode 100644 index a414fbe..0000000 --- a/bot/normal/market.go +++ /dev/null @@ -1,391 +0,0 @@ -package normal - -import ( - "context" - "errors" - "fmt" - "strings" - "time" - - log "github.com/sirupsen/logrus" - - dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" - "code.vegaprotocol.io/protos/vega" - commandspb "code.vegaprotocol.io/protos/vega/commands/v1" - v1 "code.vegaprotocol.io/protos/vega/commands/v1" - oraclesv1 "code.vegaprotocol.io/protos/vega/oracles/v1" - walletpb "code.vegaprotocol.io/protos/vega/wallet/v1" - - "code.vegaprotocol.io/liqbot/types/num" -) - -func (b *bot) setupMarket() error { - marketsResponse, err := b.node.Markets(&dataapipb.MarketsRequest{}) - if err != nil { - return fmt.Errorf("failed to get markets: %w", err) - } - - var market *vega.Market - - if len(marketsResponse.Markets) == 0 { - market, err = b.createMarket(context.Background()) - if err != nil { - return fmt.Errorf("failed to create market: %w", err) - } - marketsResponse.Markets = append(marketsResponse.Markets, market) - } - - market, err = b.findMarket(marketsResponse.Markets) - if err != nil { - return fmt.Errorf("failed to find market: %w", err) - } - - b.marketID = market.Id - b.decimalPlaces = int(market.DecimalPlaces) - b.settlementAssetID = market.TradableInstrument.Instrument.GetFuture().SettlementAsset - b.log = b.log.WithFields(log.Fields{"marketID": b.marketID}) - - return nil -} - -func (b *bot) findMarket(markets []*vega.Market) (*vega.Market, error) { - for _, mkt := range markets { - instrument := mkt.TradableInstrument.GetInstrument() - if instrument == nil { - continue - } - - future := instrument.GetFuture() - if future == nil { - continue - } - - base := "" - quote := "" - - for _, tag := range instrument.Metadata.Tags { - parts := strings.Split(tag, ":") - if len(parts) != 2 { - continue - } - if parts[0] == "quote" { - quote = parts[1] - } - if parts[0] == "base" || parts[0] == "ticker" { - base = parts[1] - } - } - - if base != b.config.InstrumentBase || quote != b.config.InstrumentQuote { - continue - } - - return mkt, nil - } - - return nil, fmt.Errorf("failed to find futures markets: base/ticker=%s, quote=%s", b.config.InstrumentBase, b.config.InstrumentQuote) -} - -func (b *bot) createMarket(ctx context.Context) (*vega.Market, error) { - b.log.Info("Minting, staking and depositing tokens") - - amount := b.config.StrategyDetails.SeedAmount.Get() - - // TODO: is it b.settlementAssetID? - if err := b.whale.TopUp(ctx, b.config.Name, b.walletPubKey, b.settlementAssetID, amount); err != nil { - return nil, fmt.Errorf("failed to seed deposit tokens: %w", err) - } - - if err := b.whale.TopUp(ctx, b.config.Name, b.walletPubKey, b.vegaAssetID, amount); err != nil { - return nil, fmt.Errorf("failed to seed stake tokens: %w", err) - } - - b.log.Debug("Waiting for stake to propagate...") - - if err := b.marketStream.WaitForStakeLinking(); err != nil { - return nil, fmt.Errorf("failed stake linking: %w", err) - } - - b.log.Debug("Successfully linked stake") - b.log.Debug("Sending new market proposal") - - if err := b.sendNewMarketProposal(ctx); err != nil { - return nil, fmt.Errorf("failed to send new market proposal: %w", err) - } - - b.log.Debug("Waiting for proposal ID...") - - proposalID, err := b.marketStream.WaitForProposalID() - if err != nil { - return nil, fmt.Errorf("failed to wait for proposal ID: %w", err) - } - - b.log.Debug("Successfully sent new market proposal") - b.log.Debug("Sending votes for market proposal") - - if err = b.sendVote(ctx, proposalID, true); err != nil { - return nil, fmt.Errorf("failed to send vote: %w", err) - } - - b.log.Debug("Waiting for proposal to be enacted...") - - if err = b.marketStream.WaitForProposalEnacted(proposalID); err != nil { - return nil, fmt.Errorf("failed to wait for proposal to be enacted: %w", err) - } - - b.log.Debug("Market proposal successfully enacted") - - marketsResponse, err := b.node.Markets(&dataapipb.MarketsRequest{}) - if err != nil { - return nil, fmt.Errorf("failed to get markets: %w", err) - } - - if len(marketsResponse.Markets) == 0 { - return nil, errors.New("no markets created") - } - - return marketsResponse.Markets[0], nil -} - -func (b *bot) sendNewMarketProposal(ctx context.Context) error { - cmd := &walletpb.SubmitTransactionRequest_ProposalSubmission{ - ProposalSubmission: b.getExampleMarketProposal(), - } - - submitTxReq := &walletpb.SubmitTransactionRequest{ - PubKey: b.walletPubKey, - // Propagate: true, TODO: OK to remove? - Command: cmd, - } - - if err := b.walletClient.SignTx(ctx, submitTxReq); err != nil { - return fmt.Errorf("failed to sign transaction: %v", err) - } - - return nil -} - -func (b *bot) sendVote(ctx context.Context, proposalId string, vote bool) error { - value := vega.Vote_VALUE_NO - if vote { - value = vega.Vote_VALUE_YES - } - - cmd := &walletpb.SubmitTransactionRequest_VoteSubmission{ - VoteSubmission: &v1.VoteSubmission{ - ProposalId: proposalId, - Value: value, - }, - } - - submitTxReq := &walletpb.SubmitTransactionRequest{ - PubKey: b.walletPubKey, - Command: cmd, - } - - if err := b.walletClient.SignTx(ctx, submitTxReq); err != nil { - return fmt.Errorf("failed to submit Vote Submission: %w", err) - } - - return nil -} - -// TODO: make retryable. -func (b *bot) submitOrder( - ctx context.Context, - size uint64, - price *num.Uint, - side vega.Side, - tif vega.Order_TimeInForce, - orderType vega.Order_Type, - reference, - from string, - secondsFromNow int64, -) error { - // TODO: is it ok to ensure balance here? - if err := b.ensureBalance(ctx, price, from); err != nil { - return fmt.Errorf("failed to ensure balance: %w", err) - } - - cmd := &walletpb.SubmitTransactionRequest_OrderSubmission{ - OrderSubmission: &commandspb.OrderSubmission{ - MarketId: b.marketID, - Price: "", // added below - Size: size, - Side: side, - TimeInForce: tif, - ExpiresAt: 0, // added below - Type: orderType, - Reference: reference, - PeggedOrder: nil, - }, - } - - b.log.WithFields(log.Fields{ - "reference": reference, - "size": size, - "side": side, - "price": price.String(), - "tif": tif.String(), - }).Debugf("%s: Submitting order", from) - - if tif == vega.Order_TIME_IN_FORCE_GTT { - cmd.OrderSubmission.ExpiresAt = time.Now().UnixNano() + (secondsFromNow * 1000000000) - } - - if orderType != vega.Order_TYPE_MARKET { - cmd.OrderSubmission.Price = price.String() - } - - submitTxReq := &walletpb.SubmitTransactionRequest{ - PubKey: b.walletPubKey, - Command: cmd, - } - - if err := b.walletClient.SignTx(ctx, submitTxReq); err != nil { - return fmt.Errorf("failed to submit OrderSubmission: %w", err) - } - - return nil -} - -func (b *bot) seedOrders(ctx context.Context, from string) error { - b.log.Debugf("%s: Seeding orders", from) - - externalPrice, err := b.getExternalPrice() - if err != nil { - return fmt.Errorf("failed to get external price: %w", err) - } - - for i := 0; !b.canPlaceOrders(); i++ { - price := externalPrice.Clone() - tif := vega.Order_TIME_IN_FORCE_GFA - - side := vega.Side_SIDE_BUY - if i%2 == 0 { - side = vega.Side_SIDE_SELL - } - - if i == 0 { - price = num.UintChain(price).Mul(num.NewUint(105)).Div(num.NewUint(100)).Get() - tif = vega.Order_TIME_IN_FORCE_GTC - } else if i == 1 { - price = num.UintChain(price).Mul(num.NewUint(95)).Div(num.NewUint(100)).Get() - tif = vega.Order_TIME_IN_FORCE_GTC - } - - if err := b.submitOrder(ctx, - b.config.StrategyDetails.SeedOrderSize, - price, - side, - tif, - vega.Order_TYPE_LIMIT, - "MarketCreation", - from, - int64(b.config.StrategyDetails.PosManagementFraction), - ); err != nil { - return fmt.Errorf("failed to create seed order: %w", err) - } - - time.Sleep(time.Second * 2) - - if i == 100 { // TODO: make this configurable - return fmt.Errorf("seeding orders did not end the auction") - } - } - - b.log.Debugf("%s: Seeding orders finished", from) - return nil -} - -func (b *bot) canPlaceOrders() bool { - return b.Market().TradingMode() == vega.Market_TRADING_MODE_CONTINUOUS -} - -func (b *bot) getExampleMarketProposal() *v1.ProposalSubmission { - return &v1.ProposalSubmission{ - Rationale: &vega.ProposalRationale{ - Description: "some description", - }, - Reference: "ProposalReference", - Terms: &vega.ProposalTerms{ - ValidationTimestamp: secondsFromNowInSecs(1), // TODO: was removed? - ClosingTimestamp: secondsFromNowInSecs(10), - EnactmentTimestamp: secondsFromNowInSecs(15), - Change: &vega.ProposalTerms_NewMarket{ - NewMarket: b.getExampleMarket(), - }, - }, - } -} - -func (b *bot) getExampleMarket() *vega.NewMarket { - return &vega.NewMarket{ - Changes: &vega.NewMarketConfiguration{ - Instrument: &vega.InstrumentConfiguration{ - Code: fmt.Sprintf("CRYPTO:%s%s/NOV22", b.config.InstrumentBase, b.config.InstrumentQuote), - Name: fmt.Sprintf("NOV 2022 %s vs %s future", b.config.InstrumentBase, b.config.InstrumentQuote), - Product: b.getExampleProduct(), - }, - DecimalPlaces: 5, - Metadata: []string{"base:" + b.config.InstrumentBase, "quote:" + b.config.InstrumentQuote, "class:fx/crypto", "monthly", "sector:crypto"}, - RiskParameters: &vega.NewMarketConfiguration_Simple{ - Simple: &vega.SimpleModelParams{ - FactorLong: 0.15, - FactorShort: 0.25, - MaxMoveUp: 10, - MinMoveDown: -5, - ProbabilityOfTrading: 0.1, - }, - }, - }, - LiquidityCommitment: &vega.NewMarketCommitment{ - Fee: fmt.Sprint(b.config.StrategyDetails.Fee), - CommitmentAmount: b.config.StrategyDetails.CommitmentAmount, - Buys: b.config.StrategyDetails.ShorteningShape.Buys.ToVegaLiquidityOrders(), - Sells: b.config.StrategyDetails.LongeningShape.Sells.ToVegaLiquidityOrders(), - }, - } -} - -func (b *bot) getExampleProduct() *vega.InstrumentConfiguration_Future { - return &vega.InstrumentConfiguration_Future{ - Future: &vega.FutureProduct{ - SettlementAsset: b.config.SettlementAssetID, - QuoteName: fmt.Sprintf("%s%s", b.config.InstrumentBase, b.config.InstrumentQuote), - OracleSpecForSettlementPrice: &oraclesv1.OracleSpecConfiguration{ - PubKeys: []string{"0xDEADBEEF"}, - Filters: []*oraclesv1.Filter{ - { - Key: &oraclesv1.PropertyKey{ - Name: "prices.ETH.value", - Type: oraclesv1.PropertyKey_TYPE_INTEGER, - }, - Conditions: []*oraclesv1.Condition{}, - }, - }, - }, - OracleSpecForTradingTermination: &oraclesv1.OracleSpecConfiguration{ - PubKeys: []string{"0xDEADBEEF"}, - Filters: []*oraclesv1.Filter{ - { - Key: &oraclesv1.PropertyKey{ - Name: "trading.termination", - Type: oraclesv1.PropertyKey_TYPE_BOOLEAN, - }, - Conditions: []*oraclesv1.Condition{}, - }, - }, - }, - OracleSpecBinding: &vega.OracleSpecToFutureBinding{ - SettlementPriceProperty: "prices.ETH.value", - TradingTerminationProperty: "trading.termination", - }, - }, - } -} - -// secondsFromNowInSecs : Creates a timestamp relative to the current time in seconds. -func secondsFromNowInSecs(seconds int64) int64 { - return time.Now().Unix() + seconds -} diff --git a/bot/normal/mocks/datanode_mock.go b/bot/normal/mocks/datanode_mock.go index da36d66..caefa45 100644 --- a/bot/normal/mocks/datanode_mock.go +++ b/bot/normal/mocks/datanode_mock.go @@ -9,8 +9,8 @@ import ( reflect "reflect" time "time" - v1 "code.vegaprotocol.io/protos/data-node/api/v1" - v10 "code.vegaprotocol.io/protos/vega/api/v1" + v1 "code.vegaprotocol.io/vega/protos/data-node/api/v1" + v10 "code.vegaprotocol.io/vega/protos/vega/api/v1" gomock "github.com/golang/mock/gomock" ) diff --git a/bot/normal/normal.go b/bot/normal/normal.go index 8c576af..82bc229 100644 --- a/bot/normal/normal.go +++ b/bot/normal/normal.go @@ -11,22 +11,16 @@ import ( log "github.com/sirupsen/logrus" - "code.vegaprotocol.io/vegawallet/wallets" - "code.vegaprotocol.io/liqbot/config" "code.vegaprotocol.io/liqbot/types" - "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/vega/wallet/wallets" ) // bot represents one Normal liquidity bot. type bot struct { - pricingEngine PricingEngine - node tradingDataService - dataStore - marketStream marketStream - dataStream dataStream walletClient WalletClient - whale whaleService + marketService + accountService config config.BotConfig log *log.Entry @@ -36,14 +30,13 @@ type bot struct { pausePosMgmt chan struct{} pausePriceSteer chan struct{} - walletPubKey string - marketID string - decimalPlaces int - settlementAssetID string - vegaAssetID string - settlementAssetAddress string - botPaused bool - mu sync.Mutex + walletPubKey string + marketID string + decimalPlaces uint64 + settlementAssetID string + vegaAssetID string + botPaused bool + mu sync.Mutex } // TODO: there could be a service that would be in charge of managing the account, balance of the account, creating markets @@ -58,69 +51,67 @@ type bot struct { func New( botConf config.BotConfig, vegaAssetID string, - dataNode tradingDataService, - marketStream marketStream, - dataStream dataStream, - pe PricingEngine, wc WalletClient, - whale whaleService, + accountService accountService, + marketService marketService, ) *bot { return &bot{ - config: botConf, - vegaAssetID: vegaAssetID, - node: dataNode, - marketStream: marketStream, - dataStream: dataStream, + config: botConf, + settlementAssetID: botConf.SettlementAssetID, + vegaAssetID: vegaAssetID, log: log.WithFields(log.Fields{ "bot": botConf.Name, }), - pricingEngine: pe, - walletClient: wc, - whale: whale, stopPosMgmt: make(chan bool), stopPriceSteer: make(chan bool), pausePosMgmt: make(chan struct{}), pausePriceSteer: make(chan struct{}), + accountService: accountService, + marketService: marketService, + walletClient: wc, } } // Start starts the liquidity bot goroutine(s). func (b *bot) Start() error { - setupLogger(false) // TODO: pretty from config? - - b.log.Debug("Attempting to connect to Vega gRPC node...") - b.node.MustDialConnection(context.Background()) // blocking - - b.log = b.log.WithFields(log.Fields{"node": b.node.Target()}) - b.log.Info("Connected to Vega gRPC node") + setupLogger(true) // TODO: pretty from config? ctx := context.Background() - err := b.setupWallet(ctx) + walletPubKey, err := b.setupWallet(ctx) if err != nil { return fmt.Errorf("failed to setup wallet: %w", err) } + b.walletPubKey = walletPubKey + pauseCh := b.pauseChannel() - b.marketStream.Setup(b.walletPubKey, pauseCh) + b.accountService.Init(walletPubKey, pauseCh) - if err = b.setupMarket(); err != nil { + if err = b.marketService.Init(walletPubKey, pauseCh); err != nil { + return fmt.Errorf("failed to init market service: %w", err) + } + + market, err := b.SetupMarket(ctx) + if err != nil { return fmt.Errorf("failed to setup market: %w", err) } + b.marketID = market.Id + b.decimalPlaces = market.DecimalPlaces + + if err = b.marketService.Start(market.Id); err != nil { + return fmt.Errorf("failed to start market service: %w", err) + } + b.log.WithFields(log.Fields{ "id": b.marketID, "base/ticker": b.config.InstrumentBase, "quote": b.config.InstrumentQuote, "settlementAssetID": b.settlementAssetID, - }).Info("Fetched market info") - - b.dataStore, err = b.dataStream.InitData(b.walletPubKey, b.marketID, b.settlementAssetID, pauseCh) - if err != nil { - return fmt.Errorf("failed to create data stream: %w", err) - } + }).Info("Market info") ctx, cancel := context.WithCancel(ctx) @@ -191,7 +182,6 @@ func (b *bot) Pause(p types.PauseSignal) { case b.pausePriceSteer <- struct{}{}: default: } - fmt.Println("bot paused") } // Stop stops the liquidity bot goroutine(s). @@ -219,17 +209,17 @@ func (b *bot) GetTraderDetails() string { return string(jsn) } -func (b *bot) setupWallet(ctx context.Context) error { +func (b *bot) setupWallet(ctx context.Context) (string, error) { walletPassphrase := "123" if err := b.walletClient.LoginWallet(ctx, b.config.Name, walletPassphrase); err != nil { if strings.Contains(err.Error(), wallets.ErrWalletDoesNotExists.Error()) { if err = b.walletClient.CreateWallet(ctx, b.config.Name, walletPassphrase); err != nil { - return fmt.Errorf("failed to create wallet: %w", err) + return "", fmt.Errorf("failed to create wallet: %w", err) } b.log.Info("Created and logged into wallet") } else { - return fmt.Errorf("failed to log into wallet: %w", err) + return "", fmt.Errorf("failed to log into wallet: %w", err) } } @@ -237,53 +227,24 @@ func (b *bot) setupWallet(ctx context.Context) error { publicKeys, err := b.walletClient.ListPublicKeys(ctx) if err != nil { - return fmt.Errorf("failed to list public keys: %w", err) + return "", fmt.Errorf("failed to list public keys: %w", err) } + var walletPubKey string + if len(publicKeys) == 0 { key, err := b.walletClient.GenerateKeyPair(ctx, walletPassphrase, []types.Meta{}) if err != nil { - return fmt.Errorf("failed to generate keypair: %w", err) + return "", fmt.Errorf("failed to generate keypair: %w", err) } - b.walletPubKey = key.Pub - b.log.WithFields(log.Fields{"pubKey": b.walletPubKey}).Debug("Created keypair") + walletPubKey = key.Pub + b.log.WithFields(log.Fields{"pubKey": walletPubKey}).Debug("Created keypair") } else { - b.walletPubKey = publicKeys[0] - b.log.WithFields(log.Fields{"pubKey": b.walletPubKey}).Debug("Using existing keypair") + walletPubKey = publicKeys[0] + b.log.WithFields(log.Fields{"pubKey": walletPubKey}).Debug("Using existing keypair") } - b.log = b.log.WithFields(log.Fields{"pubkey": b.walletPubKey}) + b.log = b.log.WithFields(log.Fields{"pubkey": walletPubKey}) - return nil -} - -func (b *bot) ensureBalance(ctx context.Context, targetAmount *num.Uint, from string) error { - balanceTotal := b.Balance().Total() // TODO: should it be total balance? - - b.log.WithFields( - log.Fields{ - "balanceTotal": balanceTotal.String(), - }).Debugf("%s: Total account balance", from) - - if balanceTotal.GT(targetAmount) { - return nil - } - - b.log.WithFields( - log.Fields{ - "balanceTotal": balanceTotal.String(), - "targetAmount": targetAmount.String(), - }).Debugf("%s: Account balance is less than target amount, depositing...", from) - - b.Pause(types.PauseSignal{From: from, Pause: true}) - - b.log.Debugf("%s: Waiting for deposit to be finalized...", from) - - if err := b.whale.TopUp(ctx, b.config.Name, b.walletPubKey, b.settlementAssetID, targetAmount); err != nil { - return fmt.Errorf("failed to top-up tokens: %w", err) - } - - b.Pause(types.PauseSignal{From: from, Pause: true}) - - return nil + return walletPubKey, nil } diff --git a/bot/normal/position_management.go b/bot/normal/position_management.go index 966b35a..19aba6f 100644 --- a/bot/normal/position_management.go +++ b/bot/normal/position_management.go @@ -8,13 +8,13 @@ import ( "math/rand" "time" - "code.vegaprotocol.io/protos/vega" - commandspb "code.vegaprotocol.io/protos/vega/commands/v1" - walletpb "code.vegaprotocol.io/protos/vega/wallet/v1" log "github.com/sirupsen/logrus" "code.vegaprotocol.io/liqbot/types/num" "code.vegaprotocol.io/liqbot/util" + "code.vegaprotocol.io/vega/protos/vega" + commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" + walletpb "code.vegaprotocol.io/vega/protos/vega/wallet/v1" ) // TODO: maybe after staking, deposit back the same amount as staked. @@ -49,7 +49,7 @@ func (b *bot) runPositionManagement(ctx context.Context) { } // Only update liquidity and position if we are not in auction - if !b.canPlaceOrders() { + if !b.CanPlaceOrders() { if err := b.ensureCommitmentAmount(ctx); err != nil { b.log.WithFields(log.Fields{"error": err.Error()}).Warning("PositionManagement: Failed to update commitment amount") } @@ -88,7 +88,7 @@ func (b *bot) ensureCommitmentAmount(ctx context.Context) error { }, ).Debug("PositionManagement: Supplied stake is less than target stake, increasing commitment amount...") - if err = b.ensureBalance(ctx, requiredCommitment, "PositionManagement"); err != nil { + if err = b.EnsureBalance(ctx, b.settlementAssetID, requiredCommitment, "PositionManagement"); err != nil { return fmt.Errorf("failed to ensure balance: %w", err) } @@ -155,7 +155,7 @@ func (b *bot) manageDirection(ctx context.Context, previousOpenVolume int64) (in return openVolume, fmt.Errorf("failed to get required commitment amount: %w", err) } - if err = b.ensureBalance(ctx, commitment, "PositionManagement"); err != nil { + if err = b.EnsureBalance(ctx, b.settlementAssetID, commitment, "PositionManagement"); err != nil { return openVolume, fmt.Errorf("failed to ensure balance: %w", err) } @@ -186,17 +186,17 @@ func (b *bot) managePosition(ctx context.Context) error { return nil } - if err := b.submitOrder( - ctx, - size, - num.Zero(), - side, - vega.Order_TIME_IN_FORCE_IOC, - vega.Order_TYPE_MARKET, - "PosManagement", - "PositionManagement", - 0, - ); err != nil { + order := &vega.Order{ + MarketId: b.marketID, + Size: size, + Price: num.Zero().String(), + Side: side, + TimeInForce: vega.Order_TIME_IN_FORCE_GTT, + Type: vega.Order_TYPE_LIMIT, + Reference: "PosManagement", + } + + if err := b.SubmitOrder(ctx, order, "PositionManagement", 0); err != nil { return fmt.Errorf("failed to place order: %w", err) } @@ -322,7 +322,7 @@ func (b *bot) provideLiquidity(ctx context.Context) error { return nil } - if err = b.ensureBalance(ctx, commitment, "PositionManagement"); err != nil { + if err = b.EnsureBalance(ctx, b.settlementAssetID, commitment, "PositionManagement"); err != nil { return fmt.Errorf("failed to ensure balance: %w", err) } @@ -414,11 +414,17 @@ func (b *bot) placeAuctionOrders(ctx context.Context) error { side = vega.Side_SIDE_SELL } - tif := vega.Order_TIME_IN_FORCE_GTT - orderType := vega.Order_TYPE_LIMIT - ref := "AuctionOrder" + order := &vega.Order{ + MarketId: b.marketID, + Size: size.Uint64(), + Price: price.String(), + Side: side, + TimeInForce: vega.Order_TIME_IN_FORCE_GTT, + Type: vega.Order_TYPE_LIMIT, + Reference: "AuctionOrder", + } - if err := b.submitOrder(ctx, size.Uint64(), price, side, tif, orderType, ref, "PositionManagement", 330); err != nil { + if err := b.SubmitOrder(ctx, order, "PositionManagement", 330); err != nil { // We failed to send an order so stop trying to send anymore return fmt.Errorf("failed to send auction order: %w", err) } diff --git a/bot/normal/price_steering.go b/bot/normal/price_steering.go index 8987bfc..89d0896 100644 --- a/bot/normal/price_steering.go +++ b/bot/normal/price_steering.go @@ -6,23 +6,22 @@ import ( "math" "time" - ppconfig "code.vegaprotocol.io/priceproxy/config" - "code.vegaprotocol.io/protos/vega" log "github.com/sirupsen/logrus" "code.vegaprotocol.io/liqbot/config" "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/vega/protos/vega" ) func (b *bot) runPriceSteering(ctx context.Context) { defer b.log.Warning("PriceSteering: Stopped") - if !b.canPlaceOrders() { + if !b.CanPlaceOrders() { b.log.WithFields(log.Fields{ "PriceSteerOrderScale": b.config.StrategyDetails.PriceSteerOrderScale, }).Debug("PriceSteering: Cannot place orders") - if err := b.seedOrders(ctx, "PriceSteering"); err != nil { + if err := b.SeedOrders(ctx, "PriceSteering"); err != nil { b.log.WithFields(log.Fields{"error": err.Error()}).Error("PriceSteering: Failed to seed orders") return } @@ -62,7 +61,7 @@ func (b *bot) runPriceSteering(ctx context.Context) { } func (b *bot) steerPrice(ctx context.Context) error { - externalPrice, err := b.getExternalPrice() + externalPrice, err := b.GetExternalPrice() if err != nil { return fmt.Errorf("failed to get external price: %w", err) } @@ -97,15 +96,18 @@ func (b *bot) steerPrice(ctx context.Context) error { b.log.WithFields(log.Fields{"error": err.Error()}).Fatal("PriceSteering: Unable to get realistic order details for price steering") } - if err = b.submitOrder( - ctx, - size.Uint64(), - price, - side, - vega.Order_TIME_IN_FORCE_GTT, - vega.Order_TYPE_LIMIT, - "PriceSteeringOrder", - "PriceSteering", + order := &vega.Order{ + MarketId: b.marketID, + Size: size.Uint64(), + Price: price.String(), + Side: side, + TimeInForce: vega.Order_TIME_IN_FORCE_GTT, + Type: vega.Order_TYPE_LIMIT, + Reference: "PriceSteeringOrder", + } + + if err = b.SubmitOrder( + ctx, order, "PriceSteering", int64(b.config.StrategyDetails.LimitOrderDistributionParams.GttLength)); err != nil { return fmt.Errorf("failed to submit order: %w", err) } @@ -113,25 +115,6 @@ func (b *bot) steerPrice(ctx context.Context) error { return nil } -func (b *bot) getExternalPrice() (*num.Uint, error) { - externalPriceResponse, err := b.pricingEngine.GetPrice(ppconfig.PriceConfig{ - Base: b.config.InstrumentBase, - Quote: b.config.InstrumentQuote, - Wander: true, - }) - if err != nil { - return nil, fmt.Errorf("failed to get external price: %w", err) - } - - if externalPriceResponse.Price <= 0 { - return nil, fmt.Errorf("external price is zero") - } - - externalPrice := externalPriceResponse.Price * math.Pow(10, float64(b.decimalPlaces)) - externalPriceNum := num.NewUint(uint64(externalPrice)) - return externalPriceNum, nil -} - // getRealisticOrderDetails uses magic to return a realistic order price and size. func (b *bot) getRealisticOrderDetails(externalPrice *num.Uint) (*num.Uint, *num.Uint, error) { var price *num.Uint @@ -157,8 +140,16 @@ func (b *bot) getRealisticOrderDetails(externalPrice *num.Uint) (*num.Uint, *num func (b *bot) getDiscreteThreeLevelPrice(externalPrice *num.Uint) (*num.Uint, error) { // this converts something like BTCUSD 3912312345 (five decimal places) // to 39123.12345 float. + if b.decimalPlaces == uint64(len(externalPrice.String())) { + return nil, fmt.Errorf("external price has fewer digits than the market decimal places") + } + decimalPlaces := float64(b.decimalPlaces) + m0 := num.Zero().Div(externalPrice, num.NewUint(uint64(math.Pow(10, decimalPlaces)))) + if m0.IsZero() { + return nil, fmt.Errorf("external price is zero") + } tickSize := 1.0 / math.Pow(10, decimalPlaces) delta := float64(b.config.StrategyDetails.LimitOrderDistributionParams.NumTicksFromMid) * tickSize numOrdersPerSec := b.config.StrategyDetails.MarketPriceSteeringRatePerSecond diff --git a/config/config.go b/config/config.go index fd2521e..cdc502e 100644 --- a/config/config.go +++ b/config/config.go @@ -126,6 +126,14 @@ type WhaleConfig struct { OwnerPrivateKeys map[string]string `yaml:"ownerPrivateKeys"` FaucetURL string `yaml:"faucetURL"` SyncTimeoutSec int `yaml:"syncTimeoutSec"` + SlackConfig SlackConfig `yaml:"slack"` +} + +type SlackConfig struct { + AppToken string `yaml:"appToken"` + BotToken string `yaml:"botToken"` + ChannelID string `yaml:"channelID"` + Enabled bool `yaml:"enabled"` } // BotConfig specifies the configuration parameters for one bot, which talks to one market on one diff --git a/config/config.yaml b/config/config.yaml index e09d0ea..1ba2da8 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -15,19 +15,25 @@ pricing: host: prices.ops.vega.xyz path: /prices wallet: - url: http://127.0.0.1:1789 + url: http://127.0.0.1:1790 token: ethereumAPIAddress: ws://127.0.0.1:8545 erc20BridgeAddress: 0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54 stakingBridgeAddress: 0x9135f5afd6F055e731bca2348429482eE614CFfA syncTimeoutSec: 5 whale: - walletPubKey: - walletName: w00 - walletPassphrase: 123 + walletPubKey: abe253f5682703ac654c95ab8e2de4ecccdc000a2315610bc161dd194f32af0e + walletName: whale + walletPassphrase: pastazazube ownerPrivateKeys: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 + b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 faucetURL: + slack: + appToken: + botToken: + channelID: + enabled: false locations: - localhost:3007 - localhost:3017 diff --git a/config/config_capsule.yaml b/config/config_capsule.yaml index 801d74f..2875b0d 100644 --- a/config/config_capsule.yaml +++ b/config/config_capsule.yaml @@ -15,31 +15,36 @@ pricing: host: prices.ops.vega.xyz path: /prices wallet: - url: http://host.docker.internal:1789 + url: http://localhost:1789 token: - ethereumAPIAddress: ws://host.docker.internal:8545 + ethereumAPIAddress: ws://localhost:8545 erc20BridgeAddress: 0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54 stakingBridgeAddress: 0x9135f5afd6F055e731bca2348429482eE614CFfA - syncTimeoutSec: 5 + syncTimeoutSec: 100 whale: - walletPubKey: - walletName: w00 - walletPassphrase: 123 + walletPubKey: abe253f5682703ac654c95ab8e2de4ecccdc000a2315610bc161dd194f32af0e + walletName: whale + walletPassphrase: pastazazube ownerPrivateKeys: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 - faucetURL: + b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 + faucetURL: http://localhost:1790 + slack: + appToken: + botToken: + channelID: + enabled: false locations: - - https://host.docker.internal:3027 - - https://host.docker.internal:3017 - - https://host.docker.internal:3007 + - localhost:3027 + - localhost:3017 + - localhost:3007 bots: - name: w00 - callTimeoutMills: 10000 instrumentBase: BTC instrumentQuote: USD quoteAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede # 0x3773A5c7aFF77e014cBF067dd31801b4C6dc4136 + settlementAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede strategy: normal - settlementAsset: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede strategyDetails: expectedMarkPrice: 0 auctionVolume: 100 @@ -51,7 +56,6 @@ bots: seedOrderSize: 400 commitmentAmount: 50000000000000 fee: 0.01 - shorteningShape: sells: - {reference: Ask, proportion: 10, offset: 80000} diff --git a/config/config_stagnet2.yaml b/config/config_stagnet3.yaml similarity index 81% rename from config/config_stagnet2.yaml rename to config/config_stagnet3.yaml index f3f3a79..f2d1424 100644 --- a/config/config_stagnet2.yaml +++ b/config/config_stagnet3.yaml @@ -15,31 +15,38 @@ pricing: host: prices.ops.vega.xyz path: /prices wallet: - url: https://wallet.stagnet2.vega.xyz + url: https://wallet.stagnet3.vega.xyz token: ethereumAPIAddress: wss://ropsten.infura.io/ws/v3/0b0e1795edae41f59f4c99d29ba0ae8e erc20BridgeAddress: 0x947893AaA0A7b55f66990b3B4781514b691Fdd4a stakingBridgeAddress: 0x7896C9491962D5839783CB6e0492ECebd34Bb35F syncTimeoutSec: 100 whale: - walletPubKey: 8c81d3c1e6fd8a21323ec5cd3d1b3d1a9512a53ff7964f9429f07fa2c379e084 - walletName: b00 - walletPassphrase: 123 - ownerPrivateKeys: + walletPubKey: 5fec012a59da93c1b6137095cc0d3a0ecd4f1eb53b02514f6fe54694a4a52e54 + walletName: whale + walletPassphrase: pastazazube + syncTimeoutSec: 0 + ownerPrivateKeys: # assetID: privateKey 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede: adef89153e4bd6b43876045efdd6818cec359340683edaec5e8588e635e8428b - faucetURL: + b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b: adef89153e4bd6b43876045efdd6818cec359340683edaec5e8588e635e8428b + faucetURL: https://faucet.stagnet3.vega.xyz + slack: + appToken: + botToken: + channelID: + enabled: false locations: - - n01.stagnet2.vega.xyz:3007 - - n02.stagnet2.vega.xyz:3007 - - n03.stagnet2.vega.xyz:3007 - - n04.stagnet2.vega.xyz:3007 - - n05.stagnet2.vega.xyz:3007 + - n01.stagnet3.vega.xyz:3007 + - n02.stagnet3.vega.xyz:3007 + - n03.stagnet3.vega.xyz:3007 + - n04.stagnet3.vega.xyz:3007 + - n05.stagnet3.vega.xyz:3007 bots: - - name: b01 instrumentBase: BTC instrumentQuote: USD quoteAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede + settlementAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede strategy: normal strategyDetails: expectedMarkPrice: 0 diff --git a/config/strategy.go b/config/strategy.go index 4f857dc..9a3c40a 100644 --- a/config/strategy.go +++ b/config/strategy.go @@ -4,10 +4,10 @@ import ( "fmt" "strings" - "code.vegaprotocol.io/protos/vega" "github.com/hashicorp/go-multierror" "code.vegaprotocol.io/liqbot/types" + "code.vegaprotocol.io/vega/protos/vega" ) const ( diff --git a/data/datarequests.go b/data/datarequests.go deleted file mode 100644 index a9791b6..0000000 --- a/data/datarequests.go +++ /dev/null @@ -1,101 +0,0 @@ -package data - -import ( - "errors" - "fmt" - - dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" - "code.vegaprotocol.io/protos/vega" - log "github.com/sirupsen/logrus" - - "code.vegaprotocol.io/liqbot/types" - "code.vegaprotocol.io/liqbot/util" -) - -func (d *data) initOpenVolume() error { - positions, err := d.getPositions() - if err != nil { - return fmt.Errorf("failed to get position details: %w", err) - } - - var openVolume int64 - // If we have not traded yet, then we won't have a position - if positions != nil { - if len(positions) != 1 { - return errors.New("one position item required") - } - openVolume = positions[0].OpenVolume - } - - d.store.MarketSet(types.SetOpenVolume(openVolume)) - return nil -} - -// getPositions get this bot's positions. -func (d *data) getPositions() ([]*vega.Position, error) { - response, err := d.node.PositionsByParty(&dataapipb.PositionsByPartyRequest{ - PartyId: d.walletPubKey, - MarketId: d.marketID, - }) - if err != nil { - return nil, err - } - - return response.Positions, nil -} - -func (d *data) initBalance() error { - response, err := d.node.PartyAccounts(&dataapipb.PartyAccountsRequest{ - PartyId: d.walletPubKey, - Asset: d.settlementAssetID, - }) - if err != nil { - return err - } - - if len(response.Accounts) == 0 { - d.log.WithFields(log.Fields{ - "party": d.walletPubKey, - }).Warning("Party has no accounts") - return nil - } - - for _, account := range response.Accounts { - if err = d.setBalanceByType(account); err != nil { - d.log.WithFields( - log.Fields{ - "error": err.Error(), - "accountType": account.Type.String(), - }, - ).Error("failed to set account balance") - } - } - - return nil -} - -func (d *data) setBalanceByType(account *vega.Account) error { - balance, err := util.ConvertUint256(account.Balance) - if err != nil { - return fmt.Errorf("failed to convert account balance: %w", err) - } - - d.store.BalanceSet(types.SetBalanceByType(account.Type, balance)) - return nil -} - -// initMarketData gets the latest info about the market. -func (d *data) initMarketData() error { - response, err := d.node.MarketDataByID(&dataapipb.MarketDataByIDRequest{MarketId: d.marketID}) - if err != nil { - return fmt.Errorf("failed to get market data (ID:%s): %w", d.marketID, err) - } - - md, err := types.FromVegaMD(response.MarketData) - if err != nil { - return fmt.Errorf("failed to convert market data: %w", err) - } - - d.store.MarketSet(types.SetMarketData(md)) - return nil -} diff --git a/data/event_process.go b/data/event_process.go index a920ae5..3a53db3 100644 --- a/data/event_process.go +++ b/data/event_process.go @@ -6,11 +6,11 @@ import ( "fmt" "time" - coreapipb "code.vegaprotocol.io/protos/vega/api/v1" log "github.com/sirupsen/logrus" e "code.vegaprotocol.io/liqbot/errors" "code.vegaprotocol.io/liqbot/types" + coreapipb "code.vegaprotocol.io/vega/protos/vega/api/v1" ) type busEventProcessor struct { @@ -19,11 +19,24 @@ type busEventProcessor struct { pauseCh chan types.PauseSignal } -func newBusEventProcessor(node busStreamer, pauseCh chan types.PauseSignal) *busEventProcessor { - return &busEventProcessor{ - node: node, - log: log.WithFields(log.Fields{"module": "EventProcessor", "event": "EventBus"}), - pauseCh: pauseCh, +func newBusEventProcessor(node busStreamer, opts ...Option) *busEventProcessor { + b := &busEventProcessor{ + node: node, + log: log.WithFields(log.Fields{"module": "EventProcessor", "event": "EventBus"}), + } + + for _, opt := range opts { + opt(b) + } + + return b +} + +type Option func(*busEventProcessor) + +func WithPauseCh(ch chan types.PauseSignal) Option { + return func(b *busEventProcessor) { + b.pauseCh = ch } } diff --git a/data/interfaces.go b/data/interfaces.go index ca86160..a533b8c 100644 --- a/data/interfaces.go +++ b/data/interfaces.go @@ -3,15 +3,14 @@ package data import ( "context" - dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" - vegaapipb "code.vegaprotocol.io/protos/vega/api/v1" - "code.vegaprotocol.io/liqbot/types" + dataapipb "code.vegaprotocol.io/vega/protos/data-node/api/v1" + vegaapipb "code.vegaprotocol.io/vega/protos/vega/api/v1" ) // DataNode is a Vega Data node // -//go:generate go run github.com/golang/mock/mockgen -destination mocks/datanode_mock.go -package mocks code.vegaprotocol.io/liqbot/data DataNode +//go:generate go run github.com/golang/mock/mockgen -destination mocks/datanode_mock.go -package mocks code.vegaprotocol.io/liqbot/market DataNode type DataNode interface { busStreamer PartyAccounts(req *dataapipb.PartyAccountsRequest) (response *dataapipb.PartyAccountsResponse, err error) @@ -24,13 +23,13 @@ type busStreamer interface { ObserveEventBus(ctx context.Context) (client vegaapipb.CoreService_ObserveEventBusClient, err error) } -type GetDataStore interface { +type BalanceStore interface { Balance() types.Balance - Market() types.MarketData + BalanceSet(sets ...func(*types.Balance)) } -type setDataStore interface { - BalanceSet(sets ...func(*types.Balance)) +type MarketStore interface { + Market() types.MarketData OpenVolume() int64 MarketSet(sets ...func(*types.MarketData)) } diff --git a/data/mocks/datanode_mock.go b/data/mocks/datanode_mock.go index a00d4a9..e7a4432 100644 --- a/data/mocks/datanode_mock.go +++ b/data/mocks/datanode_mock.go @@ -7,8 +7,8 @@ package mocks import ( reflect "reflect" - v1 "code.vegaprotocol.io/protos/data-node/api/v1" - v10 "code.vegaprotocol.io/protos/vega/api/v1" + v1 "code.vegaprotocol.io/vega/protos/data-node/api/v1" + v10 "code.vegaprotocol.io/vega/protos/vega/api/v1" gomock "github.com/golang/mock/gomock" ) diff --git a/data/streamingaccount.go b/data/streamingaccount.go new file mode 100644 index 0000000..40c248d --- /dev/null +++ b/data/streamingaccount.go @@ -0,0 +1,220 @@ +package data + +import ( + "context" + "fmt" + "sync" + "time" + + log "github.com/sirupsen/logrus" + + "code.vegaprotocol.io/liqbot/types" + "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/liqbot/util" + dataapipb "code.vegaprotocol.io/vega/protos/data-node/api/v1" + "code.vegaprotocol.io/vega/protos/vega" + coreapipb "code.vegaprotocol.io/vega/protos/vega/api/v1" + eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" +) + +type account struct { + log *log.Entry + node DataNode + stores map[string]BalanceStore + walletPubKey string + busEvProc busEventer + + mu sync.Mutex + waitingDeposits map[string]*num.Uint +} + +func NewAccountStream(node DataNode) *account { + return &account{ + log: log.WithField("module", "AccountStreamer"), + node: node, + waitingDeposits: make(map[string]*num.Uint), + } +} + +func (a *account) Init(pubKey string, pauseCh chan types.PauseSignal) { + a.walletPubKey = pubKey + a.busEvProc = newBusEventProcessor(a.node, WithPauseCh(pauseCh)) + a.stores = make(map[string]BalanceStore) +} + +func (a *account) InitBalances(assetID string) (BalanceStore, error) { + response, err := a.node.PartyAccounts(&dataapipb.PartyAccountsRequest{ + PartyId: a.walletPubKey, + Asset: assetID, + }) + if err != nil { + return nil, err + } + + if len(response.Accounts) == 0 { + a.log.WithFields(log.Fields{ + "party": a.walletPubKey, + }).Warning("Party has no accounts") + } + + store := types.NewBalanceStore() + a.stores[assetID] = store + + for _, acc := range response.Accounts { + if err = a.setBalanceByType(acc); err != nil { + a.log.WithFields( + log.Fields{ + "error": err.Error(), + "accountType": acc.Type.String(), + }, + ).Error("failed to set account balance") + } + } + + // TODO: avoid goroutine per every asset, better use a list of assets in that one goroutine + go a.subscribeToAccountEvents(assetID) + + return store, nil +} + +func (a *account) subscribeToAccountEvents(assetID string) { + req := &coreapipb.ObserveEventBusRequest{ + Type: []eventspb.BusEventType{ + eventspb.BusEventType_BUS_EVENT_TYPE_ACCOUNT, + }, + PartyId: a.walletPubKey, + } + + proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { + for _, event := range rsp.Events { + acct := event.GetAccount() + // filter out any that are for different assets + if acct.Asset != assetID { + continue + } + + if err := a.setBalanceByType(acct); err != nil { + a.log.WithFields( + log.Fields{ + "error": err.Error(), + "accountType": acct.Type.String(), + }, + ).Error("failed to set account balance") + } + } + return false, nil + } + + a.busEvProc.processEvents(context.Background(), "AccountData", req, proc) +} + +// WaitForDepositFinalise is a blocking call that waits for the deposit finalize event to be received. +func (a *account) WaitForDepositFinalise(ctx context.Context, walletPubKey, assetID string, expectAmount *num.Uint, timeout time.Duration) error { + if exist, ok := a.getWaitingDeposit(assetID); ok { + if expectAmount.GT(exist) { + a.setWaitingDeposit(assetID, expectAmount) + } + return nil + } + + req := &coreapipb.ObserveEventBusRequest{ + Type: []eventspb.BusEventType{ + eventspb.BusEventType_BUS_EVENT_TYPE_DEPOSIT, + }, + PartyId: walletPubKey, + } + + proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { + for _, event := range rsp.Events { + dep := event.GetDeposit() + // filter out any that are for different assets, or not finalized + if dep.Asset != assetID || dep.Status != vega.Deposit_STATUS_FINALIZED { + continue + } + + gotAmount, overflow := num.UintFromString(dep.Amount, 10) + if overflow { + return false, fmt.Errorf("failed to parse deposit expectAmount %s", dep.Amount) + } + + expect, ok := a.getWaitingDeposit(assetID) + if !ok { + expect = expectAmount.Clone() + a.setWaitingDeposit(assetID, expect) + } + + if gotAmount.GTE(expect) { + a.log.WithFields(log.Fields{"amount": gotAmount}).Info("Deposit finalised") + a.deleteWaitingDeposit(assetID) + return true, nil + } + } + return false, nil + } + + if timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, timeout) + defer cancel() + } + + a.busEvProc.processEvents(ctx, "DepositData", req, proc) + return ctx.Err() +} + +func (a *account) getWaitingDeposit(assetID string) (*num.Uint, bool) { + a.mu.Lock() + defer a.mu.Unlock() + req, ok := a.waitingDeposits[assetID] + if ok { + return req.Clone(), ok + } + return nil, false +} + +func (a *account) setWaitingDeposit(assetID string, amount *num.Uint) { + a.mu.Lock() + defer a.mu.Unlock() + a.waitingDeposits[assetID] = amount.Clone() +} + +func (a *account) deleteWaitingDeposit(assetID string) { + a.mu.Lock() + defer a.mu.Unlock() + delete(a.waitingDeposits, assetID) +} + +func (a *account) WaitForStakeLinking(pubKey string) error { + req := &coreapipb.ObserveEventBusRequest{ + Type: []eventspb.BusEventType{eventspb.BusEventType_BUS_EVENT_TYPE_STAKE_LINKING}, + } + + proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { + for _, event := range rsp.GetEvents() { + stake := event.GetStakeLinking() + if stake.Party == pubKey && stake.Status == eventspb.StakeLinking_STATUS_ACCEPTED { + a.log.WithFields(log.Fields{ + "stakeID": stake.Id, + }).Info("Received stake linking") + return true, nil // stop processing + } + } + return false, nil + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*45) + defer cancel() + + a.busEvProc.processEvents(ctx, "StakeLinking", req, proc) + return ctx.Err() +} + +func (a *account) setBalanceByType(account *vega.Account) error { + balance, err := util.ConvertUint256(account.Balance) + if err != nil { + return fmt.Errorf("failed to convert account balance: %w", err) + } + + a.stores[account.Asset].BalanceSet(types.SetBalanceByType(account.Type, balance)) + return nil +} diff --git a/data/streamingdata.go b/data/streamingdata.go deleted file mode 100644 index 1f6be25..0000000 --- a/data/streamingdata.go +++ /dev/null @@ -1,162 +0,0 @@ -package data - -import ( - "context" - "fmt" - - "code.vegaprotocol.io/vega/events" - - coreapipb "code.vegaprotocol.io/protos/vega/api/v1" - eventspb "code.vegaprotocol.io/protos/vega/events/v1" - log "github.com/sirupsen/logrus" - - "code.vegaprotocol.io/liqbot/types" -) - -type data struct { - log *log.Entry - node DataNode - walletPubKey string - marketID string - settlementAssetID string - store setDataStore - busEvProc busEventer -} - -func NewStreamData( - node DataNode, -) *data { - return &data{ - log: log.WithField("module", "DataStreamer"), - node: node, - } -} - -func (d *data) InitData( - pubKey, - marketID, - settlementAssetID string, - pauseCh chan types.PauseSignal, -) (GetDataStore, error) { - store := types.NewStore() - - d.walletPubKey = pubKey - d.marketID = marketID - d.settlementAssetID = settlementAssetID - d.store = store - d.busEvProc = newBusEventProcessor(d.node, pauseCh) - - if err := d.setInitialData(); err != nil { - return nil, fmt.Errorf("failed to set initial data: %w", err) - } - - go d.subscribeToMarketEvents() - go d.subscribeToAccountEvents() - go d.subscribePositions() - - return store, nil -} - -func (d *data) setInitialData() error { - if err := d.initBalance(); err != nil { - return fmt.Errorf("failed to set account balance: %w", err) - } - - if err := d.initMarketData(); err != nil { - return fmt.Errorf("failed to get market data: %w", err) - } - - if err := d.initOpenVolume(); err != nil { - return fmt.Errorf("failed to get open volume: %w", err) - } - - return nil -} - -// TODO: should be used by a new market service instead of by the bot -func (d *data) subscribeToMarketEvents() { - req := &coreapipb.ObserveEventBusRequest{ - Type: []eventspb.BusEventType{ - eventspb.BusEventType_BUS_EVENT_TYPE_MARKET_DATA, - }, - MarketId: d.marketID, - } - - proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { - for _, event := range rsp.Events { - marketData := event.GetMarketData() - - md, err := types.FromVegaMD(marketData) - if err != nil { - return false, fmt.Errorf("failed to convert market data: %w", err) - } - - d.store.MarketSet(types.SetMarketData(md)) - } - return false, nil - } - - d.busEvProc.processEvents(context.Background(), "MarketData", req, proc) -} - -// TODO: should be used by a new account service instead of by the bot -// Party related events. -func (d *data) subscribeToAccountEvents() { - req := &coreapipb.ObserveEventBusRequest{ - Type: []eventspb.BusEventType{ - eventspb.BusEventType_BUS_EVENT_TYPE_ACCOUNT, - }, - PartyId: d.walletPubKey, - } - - proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { - for _, event := range rsp.Events { - acct := event.GetAccount() - // filter out any that are for different assets - if acct.Asset != d.settlementAssetID { - continue - } - - if err := d.setBalanceByType(acct); err != nil { - d.log.WithFields( - log.Fields{ - "error": err.Error(), - "accountType": acct.Type.String(), - }, - ).Error("failed to set account balance") - } - } - return false, nil - } - - d.busEvProc.processEvents(context.Background(), "AccountData", req, proc) -} - -// TODO: should be used by a new market service instead of by the bot -func (d *data) subscribePositions() { - req := &coreapipb.ObserveEventBusRequest{ - Type: []eventspb.BusEventType{ - eventspb.BusEventType_BUS_EVENT_TYPE_SETTLE_POSITION, - }, - PartyId: d.walletPubKey, - MarketId: d.marketID, - } - - proc := func(ev *coreapipb.ObserveEventBusResponse) (bool, error) { - ctx := context.Background() - openVolume := d.store.OpenVolume() - - for _, event := range ev.Events { - posEvt := events.SettlePositionEventFromStream(ctx, event) - - for _, p := range posEvt.Trades() { - openVolume += p.Size() - } - } - - d.store.MarketSet(types.SetOpenVolume(openVolume)) - return false, nil - } - - d.busEvProc.processEvents(context.Background(), "PositionData", req, proc) -} diff --git a/data/streamingdeposit.go b/data/streamingdeposit.go deleted file mode 100644 index 2a36907..0000000 --- a/data/streamingdeposit.go +++ /dev/null @@ -1,59 +0,0 @@ -package data - -import ( - "context" - "time" - - "code.vegaprotocol.io/protos/vega" - coreapipb "code.vegaprotocol.io/protos/vega/api/v1" - eventspb "code.vegaprotocol.io/protos/vega/events/v1" - log "github.com/sirupsen/logrus" - - "code.vegaprotocol.io/liqbot/types/num" -) - -type deposit struct { - log *log.Entry - busEvProc busEventer - walletPubKey string -} - -func NewDepositStream(node busStreamer, pubKey string) *deposit { - return &deposit{ - log: log.WithField("module", "DepositStreamer"), - busEvProc: newBusEventProcessor(node, nil), - walletPubKey: pubKey, - } -} - -// WaitForDepositFinalize is a blocking call that waits for the deposit finalize event to be received. -func (d *deposit) WaitForDepositFinalize(ctx context.Context, settlementAssetID string, amount *num.Uint, timeout time.Duration) error { - req := &coreapipb.ObserveEventBusRequest{ - Type: []eventspb.BusEventType{ - eventspb.BusEventType_BUS_EVENT_TYPE_DEPOSIT, - }, - PartyId: d.walletPubKey, - } - - proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { - for _, event := range rsp.Events { - dep := event.GetDeposit() - // filter out any that are for different assets, or not finalized - if dep.Asset != settlementAssetID || dep.Status != vega.Deposit_STATUS_FINALIZED { - continue - } - - if dep.Amount == amount.String() { - d.log.WithFields(log.Fields{"amount": amount}).Info("Deposit finalized") - return true, nil - } - } - return false, nil - } - - ctx, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - d.busEvProc.processEvents(ctx, "DepositData", req, proc) - return ctx.Err() -} diff --git a/data/streamingmarket.go b/data/streamingmarket.go index 1ed5a22..0ffed48 100644 --- a/data/streamingmarket.go +++ b/data/streamingmarket.go @@ -2,58 +2,63 @@ package data import ( "context" + "errors" + "fmt" "time" - "code.vegaprotocol.io/protos/vega" - coreapipb "code.vegaprotocol.io/protos/vega/api/v1" - eventspb "code.vegaprotocol.io/protos/vega/events/v1" + "code.vegaprotocol.io/vega/core/events" + dataapipb "code.vegaprotocol.io/vega/protos/data-node/api/v1" + "code.vegaprotocol.io/vega/protos/vega" log "github.com/sirupsen/logrus" + coreapipb "code.vegaprotocol.io/vega/protos/vega/api/v1" + eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" + "code.vegaprotocol.io/liqbot/types" ) type market struct { - node DataNode log *log.Entry + node DataNode walletPubKey string + marketID string + store MarketStore busEvProc busEventer } func NewMarketStream(node DataNode) *market { return &market{ - node: node, log: log.WithField("module", "MarketStreamer"), + node: node, } } -func (m *market) Setup(walletPubKey string, pauseCh chan types.PauseSignal) { - m.walletPubKey = walletPubKey - m.busEvProc = newBusEventProcessor(m.node, pauseCh) +func (m *market) Init(pubKey string, pauseCh chan types.PauseSignal) (MarketStore, error) { + store := types.NewMarketStore() + + m.walletPubKey = pubKey + m.store = store + m.busEvProc = newBusEventProcessor(m.node, WithPauseCh(pauseCh)) + + return store, nil } -func (m *market) WaitForStakeLinking() error { - req := &coreapipb.ObserveEventBusRequest{ - Type: []eventspb.BusEventType{eventspb.BusEventType_BUS_EVENT_TYPE_STAKE_LINKING}, +func (m *market) Subscribe(marketID string) error { + m.marketID = marketID + + if err := m.initMarketData(); err != nil { + return fmt.Errorf("failed to get market market: %w", err) } - proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { - for _, event := range rsp.GetEvents() { - if stake := event.GetStakeLinking(); stake.Party == m.walletPubKey && stake.Status == eventspb.StakeLinking_STATUS_ACCEPTED { - m.log.WithFields(log.Fields{ - "stakeID": stake.Id, - }).Info("Received stake linking") - return true, nil // stop processing - } - } - return false, nil + if err := m.initOpenVolume(); err != nil { + return fmt.Errorf("failed to get open volume: %w", err) } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*45) - defer cancel() + go m.subscribeToMarketEvents() + go m.subscribePositions() - m.busEvProc.processEvents(ctx, "StakeLinking", req, proc) - return ctx.Err() + return nil } func (m *market) WaitForProposalID() (string, error) { @@ -112,3 +117,104 @@ func (m *market) WaitForProposalEnacted(pID string) error { m.busEvProc.processEvents(ctx, "Proposals", req, proc) return ctx.Err() } + +func (m *market) subscribeToMarketEvents() { + req := &coreapipb.ObserveEventBusRequest{ + Type: []eventspb.BusEventType{ + eventspb.BusEventType_BUS_EVENT_TYPE_MARKET_DATA, + }, + MarketId: m.marketID, + } + + proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { + for _, event := range rsp.Events { + marketData := event.GetMarketData() + + md, err := types.FromVegaMD(marketData) + if err != nil { + return false, fmt.Errorf("failed to convert market market: %w", err) + } + + m.store.MarketSet(types.SetMarketData(md)) + } + return false, nil + } + + m.busEvProc.processEvents(context.Background(), "MarketData", req, proc) +} + +func (m *market) subscribePositions() { + req := &coreapipb.ObserveEventBusRequest{ + Type: []eventspb.BusEventType{ + eventspb.BusEventType_BUS_EVENT_TYPE_SETTLE_POSITION, + }, + PartyId: m.walletPubKey, + MarketId: m.marketID, + } + + proc := func(ev *coreapipb.ObserveEventBusResponse) (bool, error) { + ctx := context.Background() + openVolume := m.store.OpenVolume() + + for _, event := range ev.Events { + posEvt := events.SettlePositionEventFromStream(ctx, event) + + for _, p := range posEvt.Trades() { + openVolume += p.Size() + } + } + + m.store.MarketSet(types.SetOpenVolume(openVolume)) + return false, nil + } + + m.busEvProc.processEvents(context.Background(), "PositionData", req, proc) +} + +func (m *market) initOpenVolume() error { + positions, err := m.getPositions() + if err != nil { + return fmt.Errorf("failed to get position details: %w", err) + } + + var openVolume int64 + // If we have not traded yet, then we won't have a position + if positions != nil { + if len(positions) != 1 { + return errors.New("one position item required") + } + openVolume = positions[0].OpenVolume + } + + m.store.MarketSet(types.SetOpenVolume(openVolume)) + return nil +} + +// getPositions get this bot's positions. +func (m *market) getPositions() ([]*vega.Position, error) { + response, err := m.node.PositionsByParty(&dataapipb.PositionsByPartyRequest{ + PartyId: m.walletPubKey, + MarketId: m.marketID, + }) + if err != nil { + return nil, err + } + + return response.Positions, nil +} + +// initMarketData gets the latest info about the market. +func (m *market) initMarketData() error { + response, err := m.node.MarketDataByID(&dataapipb.MarketDataByIDRequest{MarketId: m.marketID}) + if err != nil { + return fmt.Errorf("failed to get market market (ID:%s): %w", m.marketID, err) + } + + md, err := types.FromVegaMD(response.MarketData) + if err != nil { + return fmt.Errorf("failed to convert market market: %w", err) + } + + m.store.MarketSet(types.SetMarketData(md)) + return nil +} diff --git a/go.mod b/go.mod index cd6ba92..858b56e 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,10 @@ go 1.18 require ( code.vegaprotocol.io/priceproxy v0.1.0 - code.vegaprotocol.io/protos v0.53.0 code.vegaprotocol.io/shared v0.0.0-20220813000126-79cdcc23dfd0 - code.vegaprotocol.io/vega v0.53.0 - code.vegaprotocol.io/vegawallet v0.16.1 + code.vegaprotocol.io/vega v0.56.0 github.com/ethereum/go-ethereum v1.10.21 - github.com/golang/mock v1.6.0 + github.com/golang/mock v1.6.1-0.20220512030613-73266f9366fc github.com/golang/protobuf v1.5.2 github.com/hashicorp/go-multierror v1.1.1 github.com/holiman/uint256 v1.2.0 @@ -17,23 +15,25 @@ require ( github.com/julienschmidt/httprouter v1.3.0 github.com/shopspring/decimal v1.3.1 github.com/sirupsen/logrus v1.9.0 + github.com/slack-go/slack v0.11.3 github.com/stretchr/testify v1.8.0 gonum.org/v1/gonum v0.9.1 - google.golang.org/genproto v0.0.0-20220720214146-176da50484ac + google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b google.golang.org/grpc v1.48.0 google.golang.org/protobuf v1.28.0 ) require ( - github.com/BurntSushi/toml v1.1.0 // indirect + github.com/BurntSushi/toml v1.2.0 // indirect github.com/DataDog/zstd v1.4.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/adrg/xdg v0.4.0 // indirect - github.com/btcsuite/btcd v0.22.0-beta // indirect + github.com/btcsuite/btcd v0.22.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/confio/ics23/go v0.6.3 // indirect - github.com/cosmos/iavl v0.15.3 // indirect + github.com/confio/ics23/go v0.7.0 // indirect + github.com/cosmos/gorocksdb v1.2.0 // indirect + github.com/cosmos/iavl v0.19.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set v1.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect @@ -48,9 +48,8 @@ require ( github.com/google/btree v1.0.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.9.0 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20220317090546-adb2f9614b17 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect @@ -61,21 +60,20 @@ require ( github.com/satori/go.uuid v1.2.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect - github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect - github.com/tendermint/tendermint v0.34.15 // indirect - github.com/tendermint/tm-db v0.6.6 // indirect - github.com/tklauser/go-sysconf v0.3.9 // indirect - github.com/tklauser/numcpus v0.3.0 // indirect + github.com/tendermint/tendermint v0.34.21 // indirect + github.com/tendermint/tm-db v0.6.7 // indirect + github.com/tklauser/go-sysconf v0.3.10 // indirect + github.com/tklauser/numcpus v0.4.0 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/vegaprotocol/go-slip10 v0.1.0 // indirect go.etcd.io/bbolt v1.3.6 // indirect - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 // indirect - golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect - golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect + golang.org/x/net v0.0.0-20220726230323-06994584191e // indirect + golang.org/x/sys v0.0.0-20220727055044-e65921a090b8 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect - golang.org/x/tools v0.1.9 // indirect + golang.org/x/tools v0.1.12 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index dd2bb59..a5c4823 100644 --- a/go.sum +++ b/go.sum @@ -1,203 +1,65 @@ -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= code.vegaprotocol.io/priceproxy v0.1.0 h1:PibvUIcTXS/MOIJKJwGXhJbDwg9ZPLItcIlfMvt1YO4= code.vegaprotocol.io/priceproxy v0.1.0/go.mod h1:W/CpimpwdplSLOnO1gEz2+VLQIV5I9FsDOST+1NDJ5A= -code.vegaprotocol.io/protos v0.53.0 h1:3Xl/2o+IWd3dxe1kHnLCbF5DqKq5AoiaI8rVIdpjcng= -code.vegaprotocol.io/protos v0.53.0/go.mod h1:4BqwDw6jhc/mnwbXq8ZFUtYBFCnk8tBW6zuPsBt8OrQ= code.vegaprotocol.io/shared v0.0.0-20220813000126-79cdcc23dfd0 h1:6az/9MVVpbKSkFR8mFUy6+oWWI6HfNT769fcu9WLETg= code.vegaprotocol.io/shared v0.0.0-20220813000126-79cdcc23dfd0/go.mod h1:XzX67GsyOHzvytMr0QOHX4CCTdCZDYKUUi88rx40Nt0= -code.vegaprotocol.io/vega v0.53.0 h1:GaJQxiQYbiyJZ8FZc9zV5PdzN4LR4HhgZPCN2RgVB2I= -code.vegaprotocol.io/vega v0.53.0/go.mod h1:pBUCAGr1Sv1TbpX2QlXwor21k7QhEn9kQuZ6wXGhN1I= -code.vegaprotocol.io/vegawallet v0.16.1 h1:BRRxCKMa6vaIVHBBzwlG8Sbm1+m2xh9h6QyRt2JwVJQ= -code.vegaprotocol.io/vegawallet v0.16.1/go.mod h1:rb0GMAaAFmWkjqsCpclYIPrTympq13Ciy6S9xwIfB2U= +code.vegaprotocol.io/vega v0.56.0 h1:UKk3qsk4HUvZXlKi3ZvJTKfwJFMs0GOi8uViz3EWeEM= +code.vegaprotocol.io/vega v0.56.0/go.mod h1:PRVKUFtwLhUdqSRcgpzQhoG5YCSGTWNwcXC7ddhN5Pk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= -github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= -github.com/adlio/schema v1.1.14/go.mod h1:hQveFEMiDlG/M9yz9RAajnH5DzT6nAfqOG9YkEQU2pg= github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= -github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= -github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= +github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= +github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= -github.com/confio/ics23/go v0.6.3 h1:PuGK2V1NJWZ8sSkNDq91jgT/cahFEW9RGp4Y5jxulf0= -github.com/confio/ics23/go v0.6.3/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/continuity v0.2.0/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/confio/ics23/go v0.7.0 h1:00d2kukk7sPoHWL4zZBZwzxnpA2pec1NPdwbSokJ5w8= +github.com/confio/ics23/go v0.7.0/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= -github.com/cosmos/iavl v0.15.0-rc3.0.20201009144442-230e9bdf52cd/go.mod h1:3xOIaNNX19p0QrX0VqWa6voPRoJRGGYtny+DH8NEPvE= -github.com/cosmos/iavl v0.15.0-rc5/go.mod h1:WqoPL9yPTQ85QBMT45OOUzPxG/U/JcJoN7uMjgxke/I= -github.com/cosmos/iavl v0.15.3 h1:xE9r6HW8GeKeoYJN4zefpljZ1oukVScP/7M8oj6SUts= -github.com/cosmos/iavl v0.15.3/go.mod h1:OLjQiAQ4fGD2KDZooyJG9yz+p2ao2IAYSbke8mVvSA4= +github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= +github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= +github.com/cosmos/iavl v0.19.1 h1:3gaq9b6SjiB0KBTygRnAvEGml2pQlu1TH8uma5g63Ys= +github.com/cosmos/iavl v0.19.1/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -207,131 +69,63 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/dgraph-io/badger/v2 v2.2007.1/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= -github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/badger/v2 v2.2007.3 h1:Sl9tQWz92WCbVSe8pj04Tkqlm2boW+KAxd+XSs58SQI= github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ethereum/go-ethereum v1.10.21 h1:5lqsEx92ZaZzRyOqBEXux4/UR06m296RGzN3ol3teJY= github.com/ethereum/go-ethereum v1.10.21/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= -github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= -github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= -github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= -github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= -github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= -github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= -github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= +github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/mock v1.6.1-0.20220512030613-73266f9366fc h1:DxRM2MRFDKF8JGaT1ZSsCZ9KxoOki+rrOoB011jIEDc= +github.com/golang/mock v1.6.1-0.20220512030613-73266f9366fc/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -341,582 +135,190 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.14.7/go.mod h1:oYZKL012gGh6LMyg/xA7Q2yq6j8bu0wa+9w14EEthWU= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.3/go.mod h1:lZdb/YAJUSj9OqrCHs2ihjtoO3+xK3G53wTYXFWRGDo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.9.0 h1:SLkFeyLhrg86Ny5Wme4MGGace7EHfgsb07uWX/QUGEQ= github.com/grpc-ecosystem/grpc-gateway/v2 v2.9.0/go.mod h1:z5aB5opCfWSoAzCrC18hMgjy4oWJ2dPXkn+f3kqTHxI= -github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= -github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= -github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko= github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= -github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= -github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= -github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oasisprotocol/curve25519-voi v0.0.0-20220317090546-adb2f9614b17 h1:pxR+aWfo+famermIZvD+SiDQ3qmF7Iy2VPZuEsKTMtA= github.com/oasisprotocol/curve25519-voi v0.0.0-20220317090546-adb2f9614b17/go.mod h1:WUcXjUd98qaCVFb6j8Xc87MsKeMCXDu9Nk8JRJ9SeC8= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= -github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM= github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4= github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/slack-go/slack v0.11.3 h1:GN7revxEMax4amCc3El9a+9SGnjmBvSUobs0QnO6ZO8= +github.com/slack-go/slack v0.11.3/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= -github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= -github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= -github.com/tendermint/tendermint v0.34.0-rc4/go.mod h1:yotsojf2C1QBOw4dZrTcxbyxmPUrT4hNuOQWX9XUwB4= -github.com/tendermint/tendermint v0.34.0-rc6/go.mod h1:ugzyZO5foutZImv0Iyx/gOFCX6mjJTgbLHTwi17VDVg= -github.com/tendermint/tendermint v0.34.0/go.mod h1:Aj3PIipBFSNO21r+Lq3TtzQ+uKESxkbA3yo/INM4QwQ= -github.com/tendermint/tendermint v0.34.15 h1:45OEYTBD/TL0YFn8MF7yYJvC5iubyN4AbEjctPi1UqA= -github.com/tendermint/tendermint v0.34.15/go.mod h1:/7EDAw02rD7GT8syC317cX9ZhZTCdaFVvYjU8W+yJSs= -github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI= -github.com/tendermint/tm-db v0.6.3/go.mod h1:lfA1dL9/Y/Y8wwyPp2NMLyn5P5Ptr/gvDFNWtrCWSf8= -github.com/tendermint/tm-db v0.6.6 h1:EzhaOfR0bdKyATqcd5PNeyeq8r+V4bRPHBfyFdD9kGM= -github.com/tendermint/tm-db v0.6.6/go.mod h1:wP8d49A85B7/erz/r4YbKssKw6ylsO/hKtFk7E1aWZI= -github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= -github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo= -github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= -github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ= -github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= +github.com/tendermint/tendermint v0.34.21 h1:UiGGnBFHVrZhoQVQ7EfwSOLuCtarqCSsRf8VrklqB7s= +github.com/tendermint/tendermint v0.34.21/go.mod h1:XDvfg6U7grcFTDx7VkzxnhazQ/bspGJAn4DZ6DcLLjQ= +github.com/tendermint/tm-db v0.6.7 h1:fE00Cbl0jayAoqlExN6oyQJ7fR/ZtoVOmvPJ//+shu8= +github.com/tendermint/tm-db v0.6.7/go.mod h1:byQDzFkZV1syXr/ReXS808NxA2xvyuuVgXOJ/088L6I= +github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= +github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= +github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= +github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y= github.com/vegaprotocol/decimal v1.2.1-0.20210705145732-aaa563729a0a h1:Ee7N99nGQCl0uGfT2Lea0q/BlLo7r1m/DGf7PbQdoB4= github.com/vegaprotocol/decimal v1.2.1-0.20210705145732-aaa563729a0a/go.mod h1:tTrKXoJwKjjYYCku9qigFo0fbZB9vbsXhAnjfwheAgY= github.com/vegaprotocol/go-slip10 v0.1.0 h1:pP3XF2gSKM6OuaAURobHZlXZ9AzZ5LgeWvXFudL7Mb4= github.com/vegaprotocol/go-slip10 v0.1.0/go.mod h1:zTjs9hXxElXfZvPRWWFMtEYSe7udGFP1iUM1ktJ72HI= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220314234724-5d542ad81a58/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 h1:rxKZ2gOnYxjfmakvUUqh9Gyb6KXfrj7JWTxORTYqb0E= golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= @@ -930,311 +332,101 @@ golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+o golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220726230323-06994584191e h1:wOQNKh1uuDGRnmgF0jDxh7ctgGy/3P4rYWQRVJD4/Yg= +golang.org/x/net v0.0.0-20220726230323-06994584191e/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20220727055044-e65921a090b8 h1:dyU22nBWzrmTQxtNrr4dzVOvaw35nUYE279vF9UmsI8= +golang.org/x/sys v0.0.0-20220727055044-e65921a090b8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= @@ -1244,142 +436,22 @@ gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPj gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220720214146-176da50484ac h1:EOa+Yrhx1C0O+4pHeXeWrCwdI0tWI6IfUU56Vebs9wQ= -google.golang.org/genproto v0.0.0-20220720214146-176da50484ac/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b h1:SfSkJugek6xm7lWywqth4r2iTrYLpD8lOj1nMIIhMNM= +google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1388,64 +460,30 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/market/interfaces.go b/market/interfaces.go new file mode 100644 index 0000000..168aa90 --- /dev/null +++ b/market/interfaces.go @@ -0,0 +1,39 @@ +package market + +import ( + "context" + + "code.vegaprotocol.io/liqbot/data" + "code.vegaprotocol.io/liqbot/types" + "code.vegaprotocol.io/liqbot/types/num" + ppconfig "code.vegaprotocol.io/priceproxy/config" + ppservice "code.vegaprotocol.io/priceproxy/service" + dataapipb "code.vegaprotocol.io/vega/protos/data-node/api/v1" +) + +// TODO: PricingEngine response data could be cached in the data service, along with other external data sources. +// PricingEngine is the source of price information from the price proxy. +// +//go:generate go run github.com/golang/mock/mockgen -destination mocks/pricingengine_mock.go -package mocks code.vegaprotocol.io/liqbot/bot/normal PricingEngine +type PricingEngine interface { + GetPrice(pricecfg ppconfig.PriceConfig) (ppservice.PriceResponse, error) +} + +// TODO: this could be improved: pubKey could be specified in config, +type marketStream interface { + Init(pubKey string, pauseCh chan types.PauseSignal) (data.MarketStore, error) + Subscribe(marketID string) error + WaitForProposalID() (string, error) + WaitForProposalEnacted(pID string) error +} + +type tradingDataService interface { + MustDialConnection(ctx context.Context) + Target() string + Markets(req *dataapipb.MarketsRequest) (*dataapipb.MarketsResponse, error) // TODO: bot should probably not have to worry about finding markets +} + +type accountService interface { + EnsureBalance(ctx context.Context, assetID string, targetAmount *num.Uint, from string) error + EnsureStake(ctx context.Context, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error +} diff --git a/market/service.go b/market/service.go new file mode 100644 index 0000000..ca1e6ad --- /dev/null +++ b/market/service.go @@ -0,0 +1,466 @@ +package market + +import ( + "context" + "fmt" + "math" + "strings" + "time" + + log "github.com/sirupsen/logrus" + + "code.vegaprotocol.io/liqbot/bot/normal" + "code.vegaprotocol.io/liqbot/config" + "code.vegaprotocol.io/liqbot/data" + "code.vegaprotocol.io/liqbot/types" + "code.vegaprotocol.io/liqbot/types/num" + ppconfig "code.vegaprotocol.io/priceproxy/config" + v12 "code.vegaprotocol.io/vega/protos/data-node/api/v1" + "code.vegaprotocol.io/vega/protos/vega" + commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" + v1 "code.vegaprotocol.io/vega/protos/vega/commands/v1" + oraclesv1 "code.vegaprotocol.io/vega/protos/vega/oracles/v1" + walletpb "code.vegaprotocol.io/vega/protos/vega/wallet/v1" +) + +type Service struct { + pricingEngine PricingEngine + marketStream marketStream + node tradingDataService + walletClient normal.WalletClient // TODO: wtf?! + store data.MarketStore + account accountService + config config.BotConfig + log *log.Entry + + decimalPlaces uint64 + marketID string + walletPubKey string + vegaAssetID string +} + +func NewService( + marketStream marketStream, + node tradingDataService, + walletClient normal.WalletClient, + pe PricingEngine, + account accountService, + config config.BotConfig, + vegaAssetID string, +) *Service { + s := &Service{ + marketStream: marketStream, + node: node, + walletClient: walletClient, + pricingEngine: pe, + account: account, + config: config, + vegaAssetID: vegaAssetID, + log: log.WithField("component", "MarketService"), + } + + s.log = s.log.WithFields(log.Fields{"node": s.node.Target()}) + s.log.Info("Connected to Vega gRPC node") + + return s +} + +func (m *Service) Init(pubKey string, pauseCh chan types.PauseSignal) error { + store, err := m.marketStream.Init(pubKey, pauseCh) + if err != nil { + return err + } + m.store = store + m.walletPubKey = pubKey + return nil +} + +func (m *Service) Start(marketID string) error { + m.log.Info("Starting market service") + if err := m.marketStream.Subscribe(marketID); err != nil { + return fmt.Errorf("failed to subscribe to market stream: %w", err) + } + m.marketID = marketID + return nil +} + +func (m *Service) SetPubKey(pubKey string) { + m.walletPubKey = pubKey +} + +func (m *Service) Market() types.MarketData { + return m.store.Market() +} + +func (m *Service) SetupMarket(ctx context.Context) (*vega.Market, error) { + market, err := m.FindMarket() + if err == nil { + m.log.WithField("market", market).Info("Found market") + return market, nil + } + + m.log.WithError(err).Info("Failed to find market, creating it") + + if err = m.CreateMarket(ctx); err != nil { + return nil, fmt.Errorf("failed to create market: %w", err) + } + + market, err = m.FindMarket() + if err != nil { + return nil, fmt.Errorf("failed to find market after creation: %w", err) + } + + return market, nil +} + +func (m *Service) FindMarket() (*vega.Market, error) { + marketsResponse, err := m.node.Markets(&v12.MarketsRequest{}) + if err != nil { + return nil, fmt.Errorf("failed to get markets: %w", err) + } + + for _, mkt := range marketsResponse.Markets { + instrument := mkt.TradableInstrument.GetInstrument() + if instrument == nil { + continue + } + + future := instrument.GetFuture() + if future == nil { + continue + } + + base := "" + quote := "" + + for _, tag := range instrument.Metadata.Tags { + parts := strings.Split(tag, ":") + if len(parts) != 2 { + continue + } + if parts[0] == "quote" { + quote = parts[1] + } + if parts[0] == "base" || parts[0] == "ticker" { + base = parts[1] + } + } + + if base != m.config.InstrumentBase || quote != m.config.InstrumentQuote { + continue + } + + //m.settlementAssetID = market.TradableInstrument.Instrument.GetFuture().SettlementAsset + m.log = m.log.WithFields(log.Fields{"marketID": mkt.Id}) + m.decimalPlaces = mkt.DecimalPlaces + + return mkt, nil + } + + return nil, fmt.Errorf("failed to find futures markets: base/ticker=%s, quote=%s", m.config.InstrumentBase, m.config.InstrumentQuote) +} + +func (m *Service) CreateMarket(ctx context.Context) error { + m.log.Info("Minting, staking and depositing tokens") + + seedAmount := m.config.StrategyDetails.SeedAmount.Get() + + // TODO: is it m.settlementAssetID? + if err := m.account.EnsureBalance(ctx, m.config.SettlementAssetID, seedAmount, "MarketCreation"); err != nil { + return fmt.Errorf("failed to ensure balance: %w", err) + } + + if err := m.account.EnsureStake(ctx, m.walletPubKey, m.vegaAssetID, seedAmount, "MarketCreation"); err != nil { + return fmt.Errorf("failed to ensure stake: %w", err) + } + + m.log.Debug("Successfully linked stake") + m.log.Debug("Sending new market proposal") + + if err := m.sendNewMarketProposal(ctx); err != nil { + return fmt.Errorf("failed to send new market proposal: %w", err) + } + + m.log.Debug("Waiting for proposal ID...") + + proposalID, err := m.marketStream.WaitForProposalID() + if err != nil { + return fmt.Errorf("failed to wait for proposal ID: %w", err) + } + + m.log.Debug("Successfully sent new market proposal") + m.log.Debug("Sending votes for market proposal") + + if err = m.sendVote(ctx, proposalID, true); err != nil { + return fmt.Errorf("failed to send vote: %w", err) + } + + m.log.Debug("Waiting for proposal to be enacted...") + + if err = m.marketStream.WaitForProposalEnacted(proposalID); err != nil { + return fmt.Errorf("failed to wait for proposal to be enacted: %w", err) + } + + m.log.Debug("Market proposal successfully enacted") + + return nil +} + +func (m *Service) sendNewMarketProposal(ctx context.Context) error { + cmd := &walletpb.SubmitTransactionRequest_ProposalSubmission{ + ProposalSubmission: m.getExampleMarketProposal(), + } + + submitTxReq := &walletpb.SubmitTransactionRequest{ + PubKey: m.walletPubKey, + // Propagate: true, TODO: OK to remove? + Command: cmd, + } + + if err := m.walletClient.SignTx(ctx, submitTxReq); err != nil { + return fmt.Errorf("failed to sign transaction: %v", err) + } + + return nil +} + +func (m *Service) sendVote(ctx context.Context, proposalId string, vote bool) error { + value := vega.Vote_VALUE_NO + if vote { + value = vega.Vote_VALUE_YES + } + + cmd := &walletpb.SubmitTransactionRequest_VoteSubmission{ + VoteSubmission: &v1.VoteSubmission{ + ProposalId: proposalId, + Value: value, + }, + } + + submitTxReq := &walletpb.SubmitTransactionRequest{ + PubKey: m.walletPubKey, + Command: cmd, + } + + if err := m.walletClient.SignTx(ctx, submitTxReq); err != nil { + return fmt.Errorf("failed to submit Vote Submission: %w", err) + } + + return nil +} + +func (m *Service) CanPlaceOrders() bool { + return m.Market().TradingMode() == vega.Market_TRADING_MODE_CONTINUOUS +} + +// TODO: make retryable. +func (m *Service) SubmitOrder(ctx context.Context, order *vega.Order, from string, secondsFromNow int64) error { + // TODO: is it ok to ensure balance here? + + price, overflow := num.UintFromString(order.Price, 10) + if overflow { + return fmt.Errorf("failed to parse price: overflow") + } + + if err := m.account.EnsureBalance(ctx, m.config.SettlementAssetID, price, from); err != nil { + return fmt.Errorf("failed to ensure balance: %w", err) + } + + cmd := &walletpb.SubmitTransactionRequest_OrderSubmission{ + OrderSubmission: &commandspb.OrderSubmission{ + MarketId: order.MarketId, + Price: "", // added below + Size: order.Size, + Side: order.Side, + TimeInForce: order.TimeInForce, + ExpiresAt: 0, // added below + Type: order.Type, + Reference: order.Reference, + PeggedOrder: nil, + }, + } + + m.log.WithFields(log.Fields{ + "reference": order.Reference, + "size": order.Size, + "side": order.Side, + "price": order.Price, + "tif": order.TimeInForce.String(), + }).Debugf("%s: Submitting order", from) + + if order.TimeInForce == vega.Order_TIME_IN_FORCE_GTT { + cmd.OrderSubmission.ExpiresAt = time.Now().UnixNano() + (secondsFromNow * 1000000000) + } + + if order.Type != vega.Order_TYPE_MARKET { + cmd.OrderSubmission.Price = order.Price + } + + submitTxReq := &walletpb.SubmitTransactionRequest{ + PubKey: m.walletPubKey, + Command: cmd, + } + + if err := m.walletClient.SignTx(ctx, submitTxReq); err != nil { + return fmt.Errorf("failed to submit OrderSubmission: %w", err) + } + + return nil +} + +func (m *Service) SeedOrders(ctx context.Context, from string) error { + m.log.Debugf("%s: Seeding orders", from) + + externalPrice, err := m.GetExternalPrice() + if err != nil { + return fmt.Errorf("failed to get external price: %w", err) + } + + for i := 0; !m.CanPlaceOrders(); i++ { + price := externalPrice.Clone() + tif := vega.Order_TIME_IN_FORCE_GFA + + side := vega.Side_SIDE_BUY + if i%2 == 0 { + side = vega.Side_SIDE_SELL + } + + if i == 0 { + price = num.UintChain(price).Mul(num.NewUint(105)).Div(num.NewUint(100)).Get() + tif = vega.Order_TIME_IN_FORCE_GTC + } else if i == 1 { + price = num.UintChain(price).Mul(num.NewUint(95)).Div(num.NewUint(100)).Get() + tif = vega.Order_TIME_IN_FORCE_GTC + } + + order := &vega.Order{ + MarketId: m.marketID, + Size: m.config.StrategyDetails.SeedOrderSize, + Price: price.String(), + Side: side, + TimeInForce: tif, + Type: vega.Order_TYPE_LIMIT, + Reference: "MarketCreation", + } + + if err = m.SubmitOrder(ctx, order, from, int64(m.config.StrategyDetails.PosManagementFraction)); err != nil { + return fmt.Errorf("failed to create seed order: %w", err) + } + + time.Sleep(time.Second * 2) + + if i == 100 { // TODO: make this configurable + return fmt.Errorf("seeding orders did not end the auction") + } + } + + m.log.Debugf("%s: Seeding orders finished", from) + return nil +} + +func (m *Service) GetExternalPrice() (*num.Uint, error) { + externalPriceResponse, err := m.pricingEngine.GetPrice(ppconfig.PriceConfig{ + Base: m.config.InstrumentBase, + Quote: m.config.InstrumentQuote, + Wander: true, + }) + if err != nil { + return nil, fmt.Errorf("failed to get external price: %w", err) + } + + if externalPriceResponse.Price <= 0 { + return nil, fmt.Errorf("external price is zero") + } + + externalPrice := externalPriceResponse.Price * math.Pow(10, float64(m.decimalPlaces)) + externalPriceNum := num.NewUint(uint64(externalPrice)) + return externalPriceNum, nil +} + +func (m *Service) getExampleMarketProposal() *v1.ProposalSubmission { + return &v1.ProposalSubmission{ + Rationale: &vega.ProposalRationale{ + Title: "Example Market", + Description: "some description", + }, + Reference: "ProposalReference", + Terms: &vega.ProposalTerms{ + ClosingTimestamp: secondsFromNowInSecs(10), + EnactmentTimestamp: secondsFromNowInSecs(15), + Change: &vega.ProposalTerms_NewMarket{ + NewMarket: m.getExampleMarket(), + }, + }, + } +} + +func (m *Service) getExampleMarket() *vega.NewMarket { + return &vega.NewMarket{ + Changes: &vega.NewMarketConfiguration{ + Instrument: &vega.InstrumentConfiguration{ + Code: fmt.Sprintf("CRYPTO:%s%s/NOV22", m.config.InstrumentBase, m.config.InstrumentQuote), + Name: fmt.Sprintf("NOV 2022 %s vs %s future", m.config.InstrumentBase, m.config.InstrumentQuote), + Product: m.getExampleProduct(), + }, + DecimalPlaces: 5, + Metadata: []string{"base:" + m.config.InstrumentBase, "quote:" + m.config.InstrumentQuote, "class:fx/crypto", "monthly", "sector:crypto"}, + RiskParameters: &vega.NewMarketConfiguration_Simple{ + Simple: &vega.SimpleModelParams{ + FactorLong: 0.15, + FactorShort: 0.25, + MaxMoveUp: 10, + MinMoveDown: -5, + ProbabilityOfTrading: 0.1, + }, + }, + }, + /*LiquidityCommitment: &vega.NewMarketCommitment{ + Fee: fmt.Sprint(m.config.StrategyDetails.Fee), + CommitmentAmount: m.config.StrategyDetails.CommitmentAmount, + Buys: m.config.StrategyDetails.ShorteningShape.Buys.ToVegaLiquidityOrders(), + Sells: m.config.StrategyDetails.LongeningShape.Sells.ToVegaLiquidityOrders(), + },*/ + } +} + +func (m *Service) getExampleProduct() *vega.InstrumentConfiguration_Future { + return &vega.InstrumentConfiguration_Future{ + Future: &vega.FutureProduct{ + SettlementAsset: m.config.SettlementAssetID, + QuoteName: fmt.Sprintf("%s%s", m.config.InstrumentBase, m.config.InstrumentQuote), + OracleSpecForSettlementPrice: &oraclesv1.OracleSpecConfiguration{ + PubKeys: []string{"0xDEADBEEF"}, + Filters: []*oraclesv1.Filter{ + { + Key: &oraclesv1.PropertyKey{ + Name: "prices.ETH.value", + Type: oraclesv1.PropertyKey_TYPE_INTEGER, + }, + Conditions: []*oraclesv1.Condition{}, + }, + }, + }, + OracleSpecForTradingTermination: &oraclesv1.OracleSpecConfiguration{ + PubKeys: []string{"0xDEADBEEF"}, + Filters: []*oraclesv1.Filter{ + { + Key: &oraclesv1.PropertyKey{ + Name: "trading.termination", + Type: oraclesv1.PropertyKey_TYPE_BOOLEAN, + }, + Conditions: []*oraclesv1.Condition{}, + }, + }, + }, + OracleSpecBinding: &vega.OracleSpecToFutureBinding{ + SettlementPriceProperty: "prices.ETH.value", + TradingTerminationProperty: "trading.termination", + }, + }, + } +} + +// secondsFromNowInSecs : Creates a timestamp relative to the current time in seconds. +func secondsFromNowInSecs(seconds int64) int64 { + return time.Now().Unix() + seconds +} diff --git a/node/datanode.go b/node/datanode.go index 84819c9..7775b12 100644 --- a/node/datanode.go +++ b/node/datanode.go @@ -7,12 +7,13 @@ import ( "sync" "time" - dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" - vegaapipb "code.vegaprotocol.io/protos/vega/api/v1" "google.golang.org/grpc" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials/insecure" + dataapipb "code.vegaprotocol.io/vega/protos/data-node/api/v1" + vegaapipb "code.vegaprotocol.io/vega/protos/vega/api/v1" + e "code.vegaprotocol.io/liqbot/errors" ) diff --git a/service/service.go b/service/service.go index 819ea1d..be0209e 100644 --- a/service/service.go +++ b/service/service.go @@ -9,11 +9,13 @@ import ( "sync" "time" - ppconfig "code.vegaprotocol.io/priceproxy/config" - ppservice "code.vegaprotocol.io/priceproxy/service" "github.com/julienschmidt/httprouter" log "github.com/sirupsen/logrus" + "code.vegaprotocol.io/liqbot/account" + ppconfig "code.vegaprotocol.io/priceproxy/config" + ppservice "code.vegaprotocol.io/priceproxy/service" + "code.vegaprotocol.io/liqbot/bot" "code.vegaprotocol.io/liqbot/config" "code.vegaprotocol.io/liqbot/data" @@ -97,18 +99,26 @@ func getWhale(config config.Config) (*whale.Service, error) { faucetService := token.NewFaucetService(config.Whale.FaucetURL, config.Whale.WalletPubKey) whaleWallet := wallet.NewClient(config.Wallet.URL) - depositStream := data.NewDepositStream(dataNode, config.Whale.WalletPubKey) + accountStream := data.NewAccountStream(dataNode) + tokenService, err := token.NewService(config.Token, config.Whale.WalletPubKey) if err != nil { return nil, fmt.Errorf("failed to setup token service: %w", err) } - whaleService := whale.NewService( + provider := whale.NewProvider( dataNode, - whaleWallet, tokenService, faucetService, - depositStream, + config.Whale, + ) + + accountService := account.NewAccountService("whale", "", accountStream, provider) + + whaleService := whale.NewService( + dataNode, + whaleWallet, + accountService, config.Whale, ) @@ -161,7 +171,7 @@ func (s *Service) Stop() { } } -func (s *Service) initBots(pricingEngine PricingEngine, whaleService types.WhaleService) error { +func (s *Service) initBots(pricingEngine PricingEngine, whaleService types.CoinProvider) error { for _, botcfg := range s.config.Bots { if err := s.initBot(pricingEngine, botcfg, whaleService); err != nil { return fmt.Errorf("failed to initialise bot '%s': %w", botcfg.Name, err) @@ -171,7 +181,7 @@ func (s *Service) initBots(pricingEngine PricingEngine, whaleService types.Whale return nil } -func (s *Service) initBot(pricingEngine PricingEngine, botcfg config.BotConfig, whaleService types.WhaleService) error { +func (s *Service) initBot(pricingEngine PricingEngine, botcfg config.BotConfig, whaleService types.CoinProvider) error { log.WithFields(log.Fields{"strategy": botcfg.StrategyDetails.String()}).Debug("read strategy config") b, err := bot.New(botcfg, s.config, pricingEngine, whaleService) diff --git a/token/erc20_service.go b/token/erc20_service.go index 6042816..809ba1d 100644 --- a/token/erc20_service.go +++ b/token/erc20_service.go @@ -50,6 +50,10 @@ func NewService(conf *config.TokenConfig, vegaPubKey string) (*Service, error) { } func (s *Service) Stake(ctx context.Context, ownerPrivateKey, ownerAddress, vegaTokenAddress string, amount *num.Uint) (*num.Uint, error) { + return s.StakeToAddress(ctx, ownerPrivateKey, ownerAddress, vegaTokenAddress, s.vegaPubKey, amount) +} + +func (s *Service) StakeToAddress(ctx context.Context, ownerPrivateKey, ownerAddress, vegaTokenAddress, vegaPubKey string, amount *num.Uint) (*num.Uint, error) { stakingBridge, err := s.client.NewStakingBridgeSession(ctx, ownerPrivateKey, s.stakingBridgeAddress, s.syncTimeout) if err != nil { return nil, fmt.Errorf("failed to create staking bridge: %w", err) @@ -65,7 +69,7 @@ func (s *Service) Stake(ctx context.Context, ownerPrivateKey, ownerAddress, vega return nil, fmt.Errorf("failed to mint vegaToken: %w", err) } - if err = s.approveAndStakeToken(vegaToken, stakingBridge, minted); err != nil { + if err = s.approveAndStakeToken(vegaToken, vegaPubKey, stakingBridge, minted); err != nil { return nil, fmt.Errorf("failed to approve and stake token on staking bridge: %w", err) } @@ -132,7 +136,8 @@ func (s *Service) mintToken(ctx context.Context, token token, address common.Add "address": address, }).Debug("Minting new token") - if tx, err := token.MintSync(address, amount); err == nil { + var tx *types.Transaction + if tx, err = token.MintSync(address, amount); err == nil { s.log.WithFields( log.Fields{ "token": name, @@ -147,7 +152,15 @@ func (s *Service) mintToken(ctx context.Context, token token, address common.Add return minted, nil } + s.log.WithFields(log.Fields{"error": err}).Warn("Minting token failed") + + s.log.Debug("Fallback to minting token using hack...") + // plan B + + ctx, cancel := context.WithTimeout(ctx, 6*time.Minute) // TODO: make configurable + defer cancel() + minted, err := token.MintRawSync(ctx, address, amount) if err != nil { return nil, fmt.Errorf("failed to mint token: %w", err) @@ -207,7 +220,7 @@ func (s *Service) approveAndDepositToken(token token, bridge *vgethereum.ERC20Br return nil } -func (s *Service) approveAndStakeToken(token token, bridge *vgethereum.StakingBridgeSession, amount *big.Int) error { +func (s *Service) approveAndStakeToken(token token, vegaPubKey string, bridge *vgethereum.StakingBridgeSession, amount *big.Int) error { name, err := token.Name() if err != nil { return fmt.Errorf("failed to get name of token: %w", err) @@ -224,7 +237,7 @@ func (s *Service) approveAndStakeToken(token token, bridge *vgethereum.StakingBr return fmt.Errorf("failed to approve token: %w", err) } - vegaPubKeyByte32, err := vgethereum.HexStringToByte32Array(s.vegaPubKey) + vegaPubKeyByte32, err := vgethereum.HexStringToByte32Array(vegaPubKey) if err != nil { return err } @@ -233,7 +246,7 @@ func (s *Service) approveAndStakeToken(token token, bridge *vgethereum.StakingBr log.Fields{ "token": name, "amount": amount, - "vegaPubKey": s.vegaPubKey, + "vegaPubKey": vegaPubKey, }).Debug("Staking asset") if _, err = bridge.Stake(amount, vegaPubKeyByte32); err != nil { @@ -244,7 +257,7 @@ func (s *Service) approveAndStakeToken(token token, bridge *vgethereum.StakingBr log.Fields{ "token": name, "amount": amount, - "vegaPubKey": s.vegaPubKey, + "vegaPubKey": vegaPubKey, }).Debug("Token staked") return nil diff --git a/types/interfaces.go b/types/interfaces.go index 6125477..d34237c 100644 --- a/types/interfaces.go +++ b/types/interfaces.go @@ -25,6 +25,7 @@ type PricingEngine interface { GetPrice(pricecfg ppconfig.PriceConfig) (pi ppservice.PriceResponse, err error) } -type WhaleService interface { - TopUp(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error +type CoinProvider interface { + TopUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error + StakeAsync(ctx context.Context, receiverAddress, assetID string, amount *num.Uint) error } diff --git a/types/store.go b/types/store.go index 61535f6..1599f09 100644 --- a/types/store.go +++ b/types/store.go @@ -1,34 +1,42 @@ package types -type Store struct { - market cache[MarketData] +type BalanceStore struct { balance cache[Balance] } -func NewStore() *Store { - return &Store{ - market: newCache[MarketData](), +func NewBalanceStore() *BalanceStore { + return &BalanceStore{ balance: newCache[Balance](), } } -func (s *Store) Balance() Balance { +func (s *BalanceStore) Balance() Balance { return s.balance.get() } -func (s *Store) Market() MarketData { - return s.market.get() +func (s *BalanceStore) BalanceSet(sets ...func(*Balance)) { + s.balance.set(sets...) } -func (s *Store) OpenVolume() int64 { - return s.market.get().openVolume +type MarketStore struct { + market cache[MarketData] } -func (s *Store) BalanceSet(sets ...func(*Balance)) { - s.balance.set(sets...) +func NewMarketStore() *MarketStore { + return &MarketStore{ + market: newCache[MarketData](), + } +} + +func (s *MarketStore) Market() MarketData { + return s.market.get() +} + +func (s *MarketStore) OpenVolume() int64 { + return s.market.get().openVolume } -func (s *Store) MarketSet(sets ...func(*MarketData)) { +func (s *MarketStore) MarketSet(sets ...func(*MarketData)) { s.market.set(sets...) } diff --git a/types/store_test.go b/types/store_test.go index 4958678..7035812 100644 --- a/types/store_test.go +++ b/types/store_test.go @@ -3,9 +3,8 @@ package types import ( "testing" - vt "code.vegaprotocol.io/vega/types" - "code.vegaprotocol.io/liqbot/types/num" + vt "code.vegaprotocol.io/vega/core/types" ) func Test_newBalanceCache(t *testing.T) { diff --git a/types/types.go b/types/types.go index 972728c..0890955 100644 --- a/types/types.go +++ b/types/types.go @@ -3,7 +3,7 @@ package types import ( "fmt" - "code.vegaprotocol.io/protos/vega" + "code.vegaprotocol.io/vega/protos/vega" "code.vegaprotocol.io/liqbot/types/num" "code.vegaprotocol.io/liqbot/util" diff --git a/types/vega.go b/types/vega.go index cc334f6..89dd046 100644 --- a/types/vega.go +++ b/types/vega.go @@ -1,6 +1,6 @@ package types -import "code.vegaprotocol.io/protos/vega" +import "code.vegaprotocol.io/vega/protos/vega" // Shape is the top level definition of a liquidity shape. type Shape struct { diff --git a/wallet/client.go b/wallet/client.go index 6c4e849..ed49c23 100644 --- a/wallet/client.go +++ b/wallet/client.go @@ -13,9 +13,8 @@ import ( "github.com/golang/protobuf/jsonpb" - walletpb "code.vegaprotocol.io/protos/vega/wallet/v1" - "code.vegaprotocol.io/liqbot/types" + walletpb "code.vegaprotocol.io/vega/protos/vega/wallet/v1" ) type Client struct { diff --git a/whale/interfaces.go b/whale/interfaces.go index cc47bbc..05503ee 100644 --- a/whale/interfaces.go +++ b/whale/interfaces.go @@ -2,12 +2,11 @@ package whale import ( "context" - "time" - - dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" - v1 "code.vegaprotocol.io/protos/vega/wallet/v1" + "code.vegaprotocol.io/liqbot/types" "code.vegaprotocol.io/liqbot/types/num" + dataapipb "code.vegaprotocol.io/vega/protos/data-node/api/v1" + v1 "code.vegaprotocol.io/vega/protos/vega/wallet/v1" ) type dataNode interface { @@ -22,7 +21,7 @@ type walletClient interface { } type erc20Service interface { - Stake(ctx context.Context, ownerPrivateKey, ownerAddress, vegaTokenAddress string, amount *num.Uint) (*num.Uint, error) + StakeToAddress(ctx context.Context, ownerPrivateKey, ownerAddress, vegaTokenAddress, vegaPubKey string, amount *num.Uint) (*num.Uint, error) Deposit(ctx context.Context, ownerPrivateKey, ownerAddress, tokenAddress string, amount *num.Uint) (*num.Uint, error) } @@ -30,6 +29,9 @@ type faucetClient interface { Mint(ctx context.Context, assetID string, amount *num.Uint) error } -type depositStream interface { - WaitForDepositFinalize(ctx context.Context, settlementAssetID string, amount *num.Uint, timeout time.Duration) error +type accountService interface { + Init(pubKey string, pauseCh chan types.PauseSignal) + EnsureBalance(ctx context.Context, assetID string, targetAmount *num.Uint, from string) error + EnsureStake(ctx context.Context, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error + StakeAsync(ctx context.Context, receiverPubKey, assetID string, amount *num.Uint) error } diff --git a/whale/provider.go b/whale/provider.go new file mode 100644 index 0000000..2227e41 --- /dev/null +++ b/whale/provider.go @@ -0,0 +1,355 @@ +package whale + +import ( + "context" + "crypto/ecdsa" + "fmt" + "sync" + "time" + + "github.com/ethereum/go-ethereum/crypto" + log "github.com/sirupsen/logrus" + "github.com/slack-go/slack" + + "code.vegaprotocol.io/liqbot/config" + "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/liqbot/util" + dataapipb "code.vegaprotocol.io/vega/protos/data-node/api/v1" + "code.vegaprotocol.io/vega/protos/vega" +) + +type Provider struct { + node dataNode + erc20 erc20Service + faucet faucetClient + slack slacker + ownerPrivateKeys map[string]string + + pendingDeposits map[string]pendingDeposit + mu sync.Mutex + + ensureBalanceCh chan ensureBalanceRequest + + walletPubKey string + callTimeout time.Duration + log *log.Entry +} + +type ensureBalanceRequest struct { + ctx context.Context + name string + address string + assetID string + amount *num.Uint + resp chan error +} + +type slacker struct { + *slack.Client // TODO: abstract this out + channelID string + enabled bool +} + +type pendingDeposit struct { + amount *num.Uint + timestamp string +} + +func NewProvider( + node dataNode, + erc20 erc20Service, + faucet faucetClient, + config *config.WhaleConfig, +) *Provider { + p := &Provider{ + node: node, + erc20: erc20, + faucet: faucet, + ownerPrivateKeys: config.OwnerPrivateKeys, + walletPubKey: config.WalletPubKey, + ensureBalanceCh: make(chan ensureBalanceRequest), + callTimeout: time.Duration(config.SyncTimeoutSec) * time.Second, + slack: slacker{ + Client: slack.New(config.SlackConfig.BotToken, slack.OptionAppLevelToken(config.SlackConfig.AppToken)), + channelID: config.SlackConfig.ChannelID, + enabled: config.SlackConfig.Enabled, + }, + log: log.WithFields(log.Fields{ + "whale": config.WalletName, + }), + } + + go func() { + for { + select { + case req := <-p.ensureBalanceCh: + req.resp <- p.topUpAsync(req.ctx, req.name, req.address, req.assetID, req.amount) + } + } + }() + return p +} + +func (p *Provider) TopUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) (err error) { + resp := make(chan error) + p.ensureBalanceCh <- ensureBalanceRequest{ + ctx: ctx, + name: receiverName, + address: receiverAddress, + assetID: assetID, + amount: amount, + resp: resp, + } + + if err = <-resp; err != nil { + log.Errorf("Whale: failed to ensure enough funds: %s", err) + } + return +} + +func (p *Provider) topUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) (err error) { + if existDeposit, ok := p.getPendingDeposit(assetID); ok { + existDeposit.amount = amount.Add(amount, existDeposit.amount) + if p.slack.enabled { + newTimestamp, err := p.updateDan(ctx, assetID, existDeposit.timestamp, existDeposit.amount) + if err != nil { + return fmt.Errorf("failed to update slack message: %s", err) + } + existDeposit.timestamp = newTimestamp + } + p.setPendingDeposit(assetID, existDeposit) + return + } + + if err = p.deposit(ctx, "Whale", p.walletPubKey, assetID, amount); err == nil { + return + } + + p.log.WithFields( + log.Fields{"receiverName": receiverName, "receiverAddress": receiverAddress}). + Warningf("Failed to deposit: %s", err) + + deposit := pendingDeposit{ + amount: amount, + } + + p.setPendingDeposit(assetID, deposit) + + if !p.slack.enabled { + return fmt.Errorf("failed to deposit: %w", err) + } + + p.log.Debugf("Fallback to slacking Dan...") + + deposit.timestamp, err = p.slackDan(ctx, assetID, amount) + if err != nil { + p.log.Errorf("Failed to slack Dan: %s", err) + return + } + p.setPendingDeposit(assetID, deposit) + return +} + +func (p *Provider) deposit(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error { + response, err := p.node.AssetByID(&dataapipb.AssetByIDRequest{ + Id: assetID, + }) + if err != nil { + return fmt.Errorf("failed to get asset id: %w", err) + } + + if erc20 := response.Asset.Details.GetErc20(); erc20 != nil { + err = p.depositERC20(ctx, response.Asset, amount) + } else if builtin := response.Asset.Details.GetBuiltinAsset(); builtin != nil { + err = p.depositBuiltin(ctx, assetID, amount, builtin) + } + if err != nil { + return fmt.Errorf("failed to deposit to address '%s', name '%s': %w", receiverAddress, receiverName, err) + } + + return fmt.Errorf("unsupported asset type") +} + +func (p *Provider) getPendingDeposit(assetID string) (pendingDeposit, bool) { + p.mu.Lock() + defer p.mu.Unlock() + + if p.pendingDeposits == nil { + p.pendingDeposits = make(map[string]pendingDeposit) + return pendingDeposit{}, false + } + + pending, ok := p.pendingDeposits[assetID] + return pending, ok +} + +func (p *Provider) setPendingDeposit(assetID string, pending pendingDeposit) { + p.mu.Lock() + defer p.mu.Unlock() + + if p.pendingDeposits == nil { + p.pendingDeposits = make(map[string]pendingDeposit) + } + + p.pendingDeposits[assetID] = pending +} + +func (p *Provider) StakeAsync(ctx context.Context, receiverAddress, assetID string, amount *num.Uint) error { + response, err := p.node.AssetByID(&dataapipb.AssetByIDRequest{ + Id: assetID, + }) + if err != nil { + return fmt.Errorf("failed to get asset id: %w", err) + } + erc20 := response.Asset.Details.GetErc20() + if erc20 == nil { + return fmt.Errorf("asset is not erc20") + } + + asset := response.Asset + + ownerKey, err := p.getOwnerKeyForAsset(asset.Id) + if err != nil { + return fmt.Errorf("failed to get owner for key '%s': %w", receiverAddress, err) + } + + contractAddress := asset.Details.GetErc20().ContractAddress + added := new(num.Uint) + + added, err = p.erc20.StakeToAddress(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, receiverAddress, amount) + if err != nil { + return fmt.Errorf("failed to stake Vega token for '%s': %w", receiverAddress, err) + } + + // TODO: check decimal places + if added.Int().LT(amount.Int()) { + return fmt.Errorf("staked less than requested amount") + } + + return nil +} + +func (p *Provider) depositERC20(ctx context.Context, asset *vega.Asset, amount *num.Uint) error { + ownerKey, err := p.getOwnerKeyForAsset(asset.Id) + if err != nil { + return fmt.Errorf("failed to get owner key: %w", err) + } + + contractAddress := asset.Details.GetErc20().ContractAddress + added := new(num.Uint) + + added, err = p.erc20.Deposit(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, amount) + if err != nil { + return fmt.Errorf("failed to add erc20 token: %w", err) + } + + // TODO: check decimal places + if added.Int().LT(amount.Int()) { + return fmt.Errorf("deposited less than requested amount") + } + + return nil +} + +func (p *Provider) depositBuiltin(ctx context.Context, assetID string, amount *num.Uint, builtin *vega.BuiltinAsset) error { + maxFaucet, err := util.ConvertUint256(builtin.MaxFaucetAmountMint) + if err != nil { + return fmt.Errorf("failed to convert max faucet amount: %w", err) + } + + times := int(new(num.Uint).Div(amount, maxFaucet).Uint64() + 1) + + // TODO: limit the time here! + + for i := 0; i < times; i++ { + if err := p.faucet.Mint(ctx, assetID, maxFaucet); err != nil { + return fmt.Errorf("failed to deposit: %w", err) + } + time.Sleep(2 * time.Second) // TODO: configure + } + return nil +} + +type key struct { + privateKey string + address string +} + +func (p *Provider) getOwnerKeyForAsset(assetID string) (*key, error) { + ownerPrivateKey, ok := p.ownerPrivateKeys[assetID] + if !ok { + return nil, fmt.Errorf("owner private key not configured for asset '%s'", assetID) + } + + address, err := addressFromPrivateKey(ownerPrivateKey) + if err != nil { + return nil, fmt.Errorf("failed to get address from private key: %w", err) + } + + return &key{ + privateKey: ownerPrivateKey, + address: address, + }, nil +} + +func addressFromPrivateKey(privateKey string) (string, error) { + key, err := crypto.HexToECDSA(privateKey) + if err != nil { + return "", fmt.Errorf("failed to convert owner private key hash into ECDSA: %w", err) + } + + publicKeyECDSA, ok := key.Public().(*ecdsa.PublicKey) + if !ok { + return "", fmt.Errorf("cannot assert type: publicKey is not of type *ecdsa.PublicKey") + } + + address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() + return address, nil +} + +const msgTemplate = `Hi @here! Whale wallet account with pub key %s needs %s coins of assetID %s, so that it can feed the hungry bots.` + +func (p *Provider) slackDan(ctx context.Context, assetID string, amount *num.Uint) (string, error) { + p.log.Debugf("Slack post @hungry-bots: wallet pub key: %s; asset id: %s; amount: %s", p.walletPubKey, assetID, amount.String()) + + message := fmt.Sprintf(msgTemplate, p.walletPubKey, amount.String(), assetID) + + respChannel, respTimestamp, err := p.slack.PostMessageContext( + ctx, + p.slack.channelID, + slack.MsgOptionText(message, false), + ) + if err != nil { + return "", err + } + + p.log.Debugf("Slack message successfully sent to channel %s at %s", respChannel, respTimestamp) + + time.Sleep(time.Second * 5) + + _, _, _ = p.slack.PostMessageContext( + ctx, + p.slack.channelID, + slack.MsgOptionText("I can wait...", false), + ) + return respTimestamp, nil +} + +func (p *Provider) updateDan(ctx context.Context, assetID, oldTimestamp string, amount *num.Uint) (string, error) { + p.log.Debugf("Slack update @hungry-bots: wallet pub key: %s; asset id: %s; amount: %s", p.walletPubKey, assetID, amount.String()) + + message := fmt.Sprintf(msgTemplate, p.walletPubKey, amount.String(), assetID) + + respChannel, respTimestamp, _, err := p.slack.UpdateMessageContext( + ctx, + p.slack.channelID, + oldTimestamp, + slack.MsgOptionText(message, false), + ) + if err != nil { + return "", err + } + + p.log.Debugf("Slack message successfully updated in channel %s at %s", respChannel, respTimestamp) + return respTimestamp, nil +} diff --git a/whale/service.go b/whale/service.go index f0a8ef7..bfdc768 100644 --- a/whale/service.go +++ b/whale/service.go @@ -2,259 +2,114 @@ package whale import ( "context" - "crypto/ecdsa" "fmt" - "log" - "time" - "github.com/ethereum/go-ethereum/crypto" - - dataapipb "code.vegaprotocol.io/protos/data-node/api/v1" - "code.vegaprotocol.io/protos/vega" - commV1 "code.vegaprotocol.io/protos/vega/commands/v1" - "code.vegaprotocol.io/protos/vega/wallet/v1" - vtypes "code.vegaprotocol.io/vega/types" + log "github.com/sirupsen/logrus" "code.vegaprotocol.io/liqbot/config" + "code.vegaprotocol.io/liqbot/types" "code.vegaprotocol.io/liqbot/types/num" - "code.vegaprotocol.io/liqbot/util" + vtypes "code.vegaprotocol.io/vega/core/types" + commV1 "code.vegaprotocol.io/vega/protos/vega/commands/v1" + "code.vegaprotocol.io/vega/protos/vega/wallet/v1" ) type Service struct { + node dataNode + wallet walletClient + account accountService + walletName string walletPassphrase string walletPubKey string - node dataNode - wallet walletClient - erc20 erc20Service - faucet faucetClient - depositStream depositStream - ownerPrivateKeys map[string]string - callTimeoutMills time.Duration + log *log.Entry } func NewService( dataNode dataNode, wallet walletClient, - erc20 erc20Service, - faucet faucetClient, - deposits depositStream, + account accountService, config *config.WhaleConfig, ) *Service { return &Service{ - node: dataNode, - wallet: wallet, - erc20: erc20, - faucet: faucet, - depositStream: deposits, + node: dataNode, + wallet: wallet, + + account: account, walletPubKey: config.WalletPubKey, walletName: config.WalletName, walletPassphrase: config.WalletPassphrase, - ownerPrivateKeys: config.OwnerPrivateKeys, - callTimeoutMills: time.Duration(config.SyncTimeoutSec) * time.Second, - } -} - -func (s *Service) Start(ctx context.Context) error { - if err := s.wallet.LoginWallet(ctx, s.walletName, s.walletPassphrase); err != nil { - return fmt.Errorf("failed to login to wallet: %w", err) + log: log.WithFields(log.Fields{ + "whale": config.WalletName, + }), } - - s.node.MustDialConnection(ctx) - return nil } -func (s *Service) TopUp(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error { - if receiverAddress == s.walletPubKey { - //return fmt.Errorf("the sender and receiver address cannot be the same") - } - - if err := s.ensureWhaleBalance(ctx, assetID, amount); err != nil { - return fmt.Errorf("failed to ensure enough funds: %w", err) - } +func (w *Service) Start(ctx context.Context) error { + w.log.Info("Starting whale service...") - balance, err := s.getBalance(assetID) - if err != nil { - return fmt.Errorf("failed to check for sufficient funds: %w", err) - } + pauseCh := make(chan types.PauseSignal) - if balance.LT(amount.Int()) { - return fmt.Errorf("insufficient funds") - } + go func() { + for p := range pauseCh { + w.log.Infof("Whale service paused: %v; from %s", p.Pause, p.From) + } + }() - // TODO: is Vega staked token supposed to be transferred differently? + w.account.Init(w.walletPubKey, pauseCh) - err = s.wallet.SignTx(ctx, &v1.SubmitTransactionRequest{ - PubKey: s.walletPubKey, - Propagate: true, - Command: &v1.SubmitTransactionRequest_Transfer{ - Transfer: &commV1.Transfer{ - FromAccountType: vtypes.AccountTypeGeneral, - To: receiverAddress, - ToAccountType: vtypes.AccountTypeGeneral, - Asset: assetID, - Amount: amount.String(), - Reference: fmt.Sprintf("Liquidity Bot '%s' Top-Up", receiverName), - Kind: &commV1.Transfer_OneOff{OneOff: &commV1.OneOffTransfer{}}, - }, - }, - }) - if err != nil { - return fmt.Errorf("failed to top-up bot '%s': %w", receiverName, err) - } - - if err = s.depositStream.WaitForDepositFinalize(ctx, assetID, amount, s.callTimeoutMills); err != nil { - return fmt.Errorf("failed to finalize deposit: %w", err) + if err := w.wallet.LoginWallet(ctx, w.walletName, w.walletPassphrase); err != nil { + return fmt.Errorf("failed to login to wallet: %s", err) } + w.node.MustDialConnection(ctx) return nil } -func (s *Service) ensureWhaleBalance(ctx context.Context, assetID string, amount *num.Uint) error { - balance, err := s.getBalance(assetID) - if err != nil { - return fmt.Errorf("failed to check for sufficient funds: %w", err) - } - - if balance.GT(amount.Int()) { - return nil +func (w *Service) TopUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error { + if assetID == "" { + return fmt.Errorf("assetID is empty for bot '%s'", receiverName) } - toDeposit := amount.Mul(amount, num.NewUint(1000)) // deposit more than needed - - if err = s.deposit(ctx, assetID, toDeposit); err != nil { - return fmt.Errorf("failed to deposit: %w", err) + if receiverAddress == w.walletPubKey { + return fmt.Errorf("whale and bot address cannot be the same") } - return nil -} - -func (s *Service) getBalance(assetID string) (*num.Int, error) { - // cache the balance - response, err := s.node.PartyAccounts(&dataapipb.PartyAccountsRequest{ - PartyId: s.walletPubKey, - Asset: assetID, - }) - if err != nil { - return nil, fmt.Errorf("failed to get accounts: %w", err) - } - - balance := new(num.Uint) - - for _, account := range response.Accounts { - if account.Type == vtypes.AccountTypeGeneral { - balance, err = util.ConvertUint256(account.Balance) - if err != nil { - return nil, fmt.Errorf("failed to convert account balance: %w", err) - } + go func() { + if err := w.account.EnsureBalance(ctx, assetID, amount, "Whale"); err != nil { + w.log.Errorf("Whale: failed to ensure enough funds: %s", err) + return } - } - - return balance.Int(), nil -} - -func (s *Service) deposit(ctx context.Context, assetID string, amount *num.Uint) error { - response, err := s.node.AssetByID(&dataapipb.AssetByIDRequest{ - Id: assetID, - }) - if err != nil { - return fmt.Errorf("failed to get asset id: %w", err) - } - if erc20 := response.Asset.Details.GetErc20(); erc20 != nil { - return s.depositERC20(ctx, response.Asset, amount) - } else if builtin := response.Asset.Details.GetBuiltinAsset(); builtin != nil { - maxFaucet, err := util.ConvertUint256(builtin.MaxFaucetAmountMint) + err := w.wallet.SignTx(ctx, &v1.SubmitTransactionRequest{ + PubKey: w.walletPubKey, + Propagate: true, + Command: &v1.SubmitTransactionRequest_Transfer{ + Transfer: &commV1.Transfer{ + FromAccountType: vtypes.AccountTypeGeneral, + To: receiverAddress, + ToAccountType: vtypes.AccountTypeGeneral, + Asset: assetID, + Amount: amount.String(), + Reference: fmt.Sprintf("Liquidity Bot '%s' Top-Up", receiverName), + Kind: &commV1.Transfer_OneOff{OneOff: &commV1.OneOffTransfer{}}, + }, + }, + }) if err != nil { - return fmt.Errorf("failed to convert max faucet amount: %w", err) + log.Errorf("Failed to top-up bot '%s': %s", receiverName, err) } - return s.depositBuiltin(ctx, assetID, amount, maxFaucet) - } - - return fmt.Errorf("unsupported asset type") -} - -func (s *Service) depositERC20(ctx context.Context, asset *vega.Asset, amount *num.Uint) error { - ownerKey, err := s.getOwnerKeyForAsset(asset.Id) - if err != nil { - return fmt.Errorf("failed to get owner key: %w", err) - } - - contractAddress := asset.Details.GetErc20().ContractAddress - added := new(num.Uint) - - var depFn func(ctx context.Context, ownerPrivateKey string, ownerAddress string, vegaTokenAddress string, amount *num.Uint) (*num.Uint, error) - - if asset.Details.Symbol == "VEGA" { - depFn = s.erc20.Stake - } else { - depFn = s.erc20.Deposit - } - added, err = depFn(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, amount) - if err != nil { - return fmt.Errorf("failed to add erc20 token: %w", err) - } - - // TODO: check units - if added.Int().LT(amount.Int()) { - // TODO: how to proceed? - return fmt.Errorf("deposited less than requested amount") - } - - return nil -} - -func (s *Service) depositBuiltin(ctx context.Context, assetID string, amount, maxFaucet *num.Uint) error { - times := int(new(num.Uint).Div(amount, maxFaucet).Uint64() + 1) + }() - for i := 0; i < times; i++ { - if err := s.faucet.Mint(ctx, assetID, maxFaucet); err != nil { - return fmt.Errorf("failed to deposit: %w", err) - } - time.Sleep(2 * time.Second) // TODO: configure - } return nil } -type key struct { - privateKey string - address string -} - -func (s *Service) getOwnerKeyForAsset(assetID string) (*key, error) { - ownerPrivateKey, ok := s.ownerPrivateKeys[assetID] - if !ok { - return nil, fmt.Errorf("owner private key not configured for asset '%s'", assetID) - } +func (w *Service) StakeAsync(ctx context.Context, receiverAddress, assetID string, amount *num.Uint) error { + w.log.Debugf("Staking for '%s' ...", receiverAddress) - address, err := addressFromPrivateKey(ownerPrivateKey) - if err != nil { - return nil, fmt.Errorf("failed to get address from private key: %w", err) + if err := w.account.StakeAsync(ctx, receiverAddress, assetID, amount); err != nil { + return err } - return &key{ - privateKey: ownerPrivateKey, - address: address, - }, nil -} - -func addressFromPrivateKey(privateKey string) (string, error) { - key, err := crypto.HexToECDSA(privateKey) - if err != nil { - return "", fmt.Errorf("failed to convert owner private key hash into ECDSA: %w", err) - } - - publicKeyECDSA, ok := key.Public().(*ecdsa.PublicKey) - if !ok { - return "", fmt.Errorf("cannot assert type: publicKey is not of type *ecdsa.PublicKey") - } - - address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() - return address, nil -} - -func (s *Service) slackDan(_ context.Context, address, assetID string, amount *num.Uint) error { - // TODO: slack @Dan - log.Printf("slack @Dan: %s %s %s", address, assetID, amount) return nil } diff --git a/whale/service_test.go b/whale/service_test.go index 8250594..38ad8e7 100644 --- a/whale/service_test.go +++ b/whale/service_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "code.vegaprotocol.io/liqbot/account" "code.vegaprotocol.io/liqbot/config" "code.vegaprotocol.io/liqbot/data" "code.vegaprotocol.io/liqbot/node" @@ -13,9 +14,11 @@ import ( ) func TestService_TopUp(t *testing.T) { - wKey := "8528c773a4b62d7609182cb0eb66dd1545173332b569397b8b1f7c1901f90932" - wName := "your_wallet_name" - wPassphrase := "super-secret" + t.Skip() + + wKey := "6baf7809b6143d4be4a5b641fcef29947aeaa1ab3805c5442de8a31a3449078f" + wName := "w00" + wPassphrase := "123" dn := node.NewDataNode([]string{"localhost:3027"}, 10000) wc := wallet.NewClient("http://localhost:1789") @@ -30,12 +33,8 @@ func TestService_TopUp(t *testing.T) { return } - fc := token.NewFaucetService("http://localhost:1790", wKey) - ds := data.NewDepositStream(dn, wKey) - dk := map[string]string{ "993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede": "a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0", - "b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b": "a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0", } conf := &config.WhaleConfig{ @@ -46,7 +45,14 @@ func TestService_TopUp(t *testing.T) { SyncTimeoutSec: 100, } - s := NewService(dn, wc, es, fc, ds, conf) + fc := token.NewFaucetService("http://localhost:1790", wKey) + + cp := NewProvider(dn, es, fc, conf) + + ds := data.NewAccountStream(dn) + as := account.NewAccountService("test", "asset", ds, cp) + + s := NewService(dn, wc, as, conf) ctx := context.Background() @@ -55,10 +61,54 @@ func TestService_TopUp(t *testing.T) { return } - key := "8528c773a4b62d7609182cb0eb66dd1545173332b569397b8b1f7c1901f90932" - asset := "b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b" - // asset := "993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede" - if err := s.TopUp(ctx, "some bot", key, asset, num.NewUint(1100000000000000000)); err != nil { - t.Errorf("TopUp() error = %s", err) + key := "69f684c78deefa27fd216ba771e4ca08085dea8e2b1dafd2c62352dda1e89073" + asset := "993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede" + + amount := num.NewUint(988939143512) + + s.TopUpAsync(ctx, "some bot", key, asset, amount) + + _ = s.account.EnsureBalance(ctx, asset, amount, "test") +} + +func TestService_slackDan(t *testing.T) { + t.Skip() + + type fields struct { + walletPubKey string + } + type args struct { + ctx context.Context + assetID string + amount *num.Uint + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + { + name: "slack", + fields: fields{ + walletPubKey: "6baf7809b6143d4be4a5b641fcef29947aeaa1ab3805c5442de8a31a3449078f", + }, + args: args{ + ctx: context.Background(), + assetID: "993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede", + amount: num.NewUint(988939143512), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &Provider{ + walletPubKey: tt.fields.walletPubKey, + } + if _, err := s.slackDan(tt.args.ctx, tt.args.assetID, tt.args.amount); (err != nil) != tt.wantErr { + t.Errorf("slackDan() error = %v, wantErr %v", err, tt.wantErr) + } + }) } } From d891492497e4c53efcff77619b8eb348cbb959ff Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Tue, 4 Oct 2022 17:04:00 +0100 Subject: [PATCH 06/18] Fixed bugs, improved event processing, added more logging, fixed market creation... --- account/interfaces.go | 5 +- account/service.go | 40 +++- bot/bot.go | 8 +- bot/normal/normal.go | 13 ++ bot/normal/position_management.go | 12 +- ...fig_stagnet3.yaml => config_stagnet1.yaml} | 39 ++-- data/event_process.go | 79 ++++--- data/interfaces.go | 2 +- data/streamingaccount.go | 214 ++++++++++++++---- data/streamingmarket.go | 78 +++++-- go.mod | 4 +- go.sum | 9 +- market/interfaces.go | 2 +- market/service.go | 53 +++-- node/datanode.go | 2 +- service/service.go | 2 +- token/erc20_service.go | 9 +- types/interfaces.go | 3 +- whale/interfaces.go | 2 +- whale/provider.go | 24 +- whale/service.go | 51 ++++- whale/service_test.go | 4 +- 22 files changed, 455 insertions(+), 200 deletions(-) rename config/{config_stagnet3.yaml => config_stagnet1.yaml} (69%) diff --git a/account/interfaces.go b/account/interfaces.go index 7f3b014..9b49592 100644 --- a/account/interfaces.go +++ b/account/interfaces.go @@ -7,11 +7,12 @@ import ( "code.vegaprotocol.io/liqbot/data" "code.vegaprotocol.io/liqbot/types" "code.vegaprotocol.io/liqbot/types/num" + v1 "code.vegaprotocol.io/vega/protos/vega/events/v1" ) type accountStream interface { Init(pubKey string, pauseCh chan types.PauseSignal) - InitBalances(assetID string) (data.BalanceStore, error) + GetBalances(assetID string) (data.BalanceStore, error) WaitForStakeLinking(pubKey string) error - WaitForDepositFinalise(ctx context.Context, walletPubKey, assetID string, amount *num.Uint, timeout time.Duration) error + WaitForTopUpToFinalise(ctx context.Context, evtType v1.BusEventType, walletPubKey, assetID string, amount *num.Uint, timeout time.Duration) error } diff --git a/account/service.go b/account/service.go index 5418ced..4e6e7b7 100644 --- a/account/service.go +++ b/account/service.go @@ -47,34 +47,41 @@ func (a *Service) EnsureBalance(ctx context.Context, assetID string, targetAmoun a.log.WithFields( log.Fields{ + "name": a.name, + "partyId": a.pubKey, "balanceTotal": balanceTotal.String(), }).Debugf("%s: Total account balance", from) - if balanceTotal.GT(targetAmount) { + if balanceTotal.GTE(targetAmount) { return nil } a.log.WithFields( log.Fields{ + "name": a.name, + "partyId": a.pubKey, "balanceTotal": balanceTotal.String(), "targetAmount": targetAmount.String(), }).Debugf("%s: Account balance is less than target amount, depositing...", from) - if err = a.coinProvider.TopUpAsync(ctx, a.name, a.pubKey, assetID, targetAmount); err != nil { + evtType, err := a.coinProvider.TopUpAsync(ctx, a.name, a.pubKey, assetID, targetAmount) + if err != nil { return fmt.Errorf("failed to top up: %w", err) } - a.log.Debugf("%s: Waiting for top-up...", from) + a.log.WithFields(log.Fields{"name": a.name}).Debugf("%s: Waiting for top-up...", from) - if err = a.accountStream.WaitForDepositFinalise(ctx, a.pubKey, assetID, targetAmount, 0); err != nil { + if err = a.accountStream.WaitForTopUpToFinalise(ctx, evtType, a.pubKey, assetID, targetAmount, 0); err != nil { return fmt.Errorf("failed to finalise deposit: %w", err) } + a.log.WithFields(log.Fields{"name": a.name}).Debugf("%s: Top-up complete", from) + return nil } // TODO: DRY -func (a *Service) EnsureStake(ctx context.Context, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error { +func (a *Service) EnsureStake(ctx context.Context, receiverName, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error { if receiverPubKey == "" { return fmt.Errorf("receiver public key is empty") } @@ -84,10 +91,13 @@ func (a *Service) EnsureStake(ctx context.Context, receiverPubKey, assetID strin return err } - balanceTotal := store.Balance().Total() // TODO: should it be total balance? + // TODO: how the hell do we check for stake balance?? + balanceTotal := store.Balance().Total() a.log.WithFields( log.Fields{ + "name": a.name, + "partyId": a.pubKey, "balanceTotal": balanceTotal.String(), }).Debugf("%s: Total account stake balance", from) @@ -97,15 +107,25 @@ func (a *Service) EnsureStake(ctx context.Context, receiverPubKey, assetID strin a.log.WithFields( log.Fields{ - "balanceTotal": balanceTotal.String(), - "targetAmount": targetAmount.String(), + "name": a.name, + "receiverName": receiverName, + "receiverPubKey": receiverPubKey, + "partyId": a.pubKey, + "balanceTotal": balanceTotal.String(), + "targetAmount": targetAmount.String(), }).Debugf("%s: Account Stake balance is less than target amount, staking...", from) if err = a.coinProvider.StakeAsync(ctx, receiverPubKey, assetID, targetAmount); err != nil { return fmt.Errorf("failed to stake: %w", err) } - a.log.Debugf("%s: Waiting for staking...", from) + a.log.WithFields(log.Fields{ + "name": a.name, + "receiverName": receiverName, + "receiverPubKey": receiverPubKey, + "partyId": a.pubKey, + "targetAmount": targetAmount.String(), + }).Debugf("%s: Waiting for staking...", from) if err = a.accountStream.WaitForStakeLinking(receiverPubKey); err != nil { return fmt.Errorf("failed to finalise stake: %w", err) @@ -130,7 +150,7 @@ func (a *Service) Balance() types.Balance { func (a *Service) getStore(assetID string) (_ data.BalanceStore, err error) { store, ok := a.stores[assetID] if !ok { - store, err = a.accountStream.InitBalances(assetID) + store, err = a.accountStream.GetBalances(assetID) if err != nil { return nil, fmt.Errorf("failed to initialise balances for '%s': %w", assetID, err) } diff --git a/bot/bot.go b/bot/bot.go index 2d19c7b..6ba29c2 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -51,11 +51,11 @@ func newNormalBot( dataNode.MustDialConnection(context.Background()) // blocking botWallet := wallet.NewClient(conf.Wallet.URL) - depositStream := data.NewAccountStream(dataNode) - accountService := account.NewAccountService(botConf.Name, botConf.SettlementAssetID, depositStream, whale) + accountStream := data.NewAccountStream(botConf.Name, dataNode) + accountService := account.NewAccountService(botConf.Name, botConf.SettlementAssetID, accountStream, whale) - marketStream := data.NewMarketStream(dataNode) - marketService := market.NewService(marketStream, dataNode, botWallet, pricing, accountService, botConf, conf.VegaAssetID) + marketStream := data.NewMarketStream(botConf.Name, dataNode) + marketService := market.NewService(botConf.Name, marketStream, dataNode, botWallet, pricing, accountService, botConf, conf.VegaAssetID) return normal.New( botConf, diff --git a/bot/normal/normal.go b/bot/normal/normal.go index 82bc229..1b17307 100644 --- a/bot/normal/normal.go +++ b/bot/normal/normal.go @@ -13,6 +13,7 @@ import ( "code.vegaprotocol.io/liqbot/config" "code.vegaprotocol.io/liqbot/types" + "code.vegaprotocol.io/liqbot/types/num" "code.vegaprotocol.io/vega/wallet/wallets" ) @@ -115,6 +116,18 @@ func (b *bot) Start() error { ctx, cancel := context.WithCancel(ctx) + // TODO: what to use here? + targetAmount, overflow := num.UintFromString(b.config.StrategyDetails.CommitmentAmount, 10) + if overflow { + cancel() + return fmt.Errorf("failed to parse targetAmount: overflow") + } + + if err = b.EnsureBalance(ctx, b.settlementAssetID, targetAmount, "Start"); err != nil { + cancel() + return fmt.Errorf("failed to ensure balance: %w", err) + } + go func() { defer cancel() b.runPositionManagement(ctx) diff --git a/bot/normal/position_management.go b/bot/normal/position_management.go index 19aba6f..93cb31d 100644 --- a/bot/normal/position_management.go +++ b/bot/normal/position_management.go @@ -111,11 +111,6 @@ func (b *bot) getRequiredCommitment() (*num.Uint, error) { suppliedStake := b.Market().SuppliedStake().Clone() targetStake := b.Market().TargetStake().Clone() - b.log.WithFields(log.Fields{ - "suppliedStake": suppliedStake.String(), - "targetStake": targetStake.String(), - }).Debug("PositionManagement: Checking for required commitment") - if targetStake.IsZero() { var err error targetStake, err = util.ConvertUint256(b.config.StrategyDetails.CommitmentAmount) @@ -124,6 +119,11 @@ func (b *bot) getRequiredCommitment() (*num.Uint, error) { } } + b.log.WithFields(log.Fields{ + "suppliedStake": suppliedStake.String(), + "targetStake": targetStake.String(), + }).Debug("PositionManagement: Checking for required commitment") + dx := suppliedStake.Int().Sub(targetStake.Int()) if dx.IsPositive() { @@ -178,7 +178,7 @@ func (b *bot) managePosition(ctx context.Context) error { "balanceMargin": b.Balance().Margin().String(), "openVolume": b.Market().OpenVolume(), "size": size, - "side": side, + "side": side.String(), "shouldPlace": shouldPlace, }).Debug("PositionManagement: Checking for position management") diff --git a/config/config_stagnet3.yaml b/config/config_stagnet1.yaml similarity index 69% rename from config/config_stagnet3.yaml rename to config/config_stagnet1.yaml index f2d1424..6ba5a01 100644 --- a/config/config_stagnet3.yaml +++ b/config/config_stagnet1.yaml @@ -15,38 +15,35 @@ pricing: host: prices.ops.vega.xyz path: /prices wallet: - url: https://wallet.stagnet3.vega.xyz + url: https://wallet.stagnet1.vega.xyz token: - ethereumAPIAddress: wss://ropsten.infura.io/ws/v3/0b0e1795edae41f59f4c99d29ba0ae8e - erc20BridgeAddress: 0x947893AaA0A7b55f66990b3B4781514b691Fdd4a - stakingBridgeAddress: 0x7896C9491962D5839783CB6e0492ECebd34Bb35F - syncTimeoutSec: 100 + ethereumAPIAddress: wss://sepolia.infura.io/ws/v3/0b0e1795edae41f59f4c99d29ba0ae8e + erc20BridgeAddress: 0x8b1d701979d5A78414Ca3189b932f1440b17D705 + stakingBridgeAddress: 0xeB17968d04281748dE3D0347030aa1a3185a4737 + syncTimeoutSec: 1000 whale: - walletPubKey: 5fec012a59da93c1b6137095cc0d3a0ecd4f1eb53b02514f6fe54694a4a52e54 - walletName: whale - walletPassphrase: pastazazube + walletPubKey: bdb4598b5e22ac01a812d166cc3006ac70575cd6b91b5bb4de52e158e510ec23 + walletName: whale # secret + walletPassphrase: pastazazube # secret syncTimeoutSec: 0 ownerPrivateKeys: # assetID: privateKey - 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede: adef89153e4bd6b43876045efdd6818cec359340683edaec5e8588e635e8428b - b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b: adef89153e4bd6b43876045efdd6818cec359340683edaec5e8588e635e8428b + c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d: # secret + fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55: # secret faucetURL: https://faucet.stagnet3.vega.xyz slack: - appToken: - botToken: - channelID: - enabled: false + appToken: # secret + botToken: # secret + channelID: C044R3YR08H + enabled: true locations: - - n01.stagnet3.vega.xyz:3007 - - n02.stagnet3.vega.xyz:3007 - - n03.stagnet3.vega.xyz:3007 - - n04.stagnet3.vega.xyz:3007 - - n05.stagnet3.vega.xyz:3007 + - n00.stagnet1.vega.xyz:3007 + - api.stagnet1.vega.xyz:3007 bots: - name: b01 instrumentBase: BTC instrumentQuote: USD - quoteAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede - settlementAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede + quoteAssetID: c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d + settlementAssetID: c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d strategy: normal strategyDetails: expectedMarkPrice: 0 diff --git a/data/event_process.go b/data/event_process.go index 3a53db3..86ea4e7 100644 --- a/data/event_process.go +++ b/data/event_process.go @@ -22,7 +22,7 @@ type busEventProcessor struct { func newBusEventProcessor(node busStreamer, opts ...Option) *busEventProcessor { b := &busEventProcessor{ node: node, - log: log.WithFields(log.Fields{"module": "EventProcessor", "event": "EventBus"}), + log: log.WithFields(log.Fields{"component": "EventProcessor", "event": "EventBus"}), } for _, opt := range opts { @@ -45,49 +45,60 @@ func (b *busEventProcessor) processEvents( name string, req *coreapipb.ObserveEventBusRequest, process func(*coreapipb.ObserveEventBusResponse) (bool, error), -) { - defer b.log.WithFields(log.Fields{ - "name": name, - }).Debug("Stopping event processor") +) <-chan error { + errCh := make(chan error) var stop bool - for s := b.mustGetStream(ctx, name, req); !stop; { - select { - case <-ctx.Done(): - return - default: - if s == nil { + go func() { + defer func() { + b.log.WithFields(log.Fields{ + "name": name, + }).Debug("Stopping event processor") + close(errCh) + }() + for s := b.mustGetStream(ctx, name, req); !stop; { + select { + case <-ctx.Done(): return - } - - rsp, err := s.Recv() - if err != nil { - if ctx.Err() == context.DeadlineExceeded { + default: + if s == nil { return } - b.log.WithFields( - log.Fields{ + rsp, err := s.Recv() + if err != nil { + if ctx.Err() == context.DeadlineExceeded { + return + } + + b.log.WithFields( + log.Fields{ + "error": err.Error(), + "name": name, + }, + ).Warningf("Stream closed, resubscribing...") + + b.pause(true, name) + s = b.mustGetStream(ctx, name, req) + b.pause(false, name) + continue + } + + stop, err = process(rsp) + if err != nil { + b.log.WithFields(log.Fields{ "error": err.Error(), "name": name, - }, - ).Warningf("Stream closed, resubscribing...") - - b.pause(true, name) - s = b.mustGetStream(ctx, name, req) - b.pause(false, name) - continue - } - - stop, err = process(rsp) - if err != nil { - b.log.WithFields(log.Fields{ - "error": err.Error(), - "name": name, - }).Warning("Unable to process event") + }).Warning("Unable to process event") + select { + case errCh <- err: + default: + } + } } } - } + }() + return errCh } func (b *busEventProcessor) mustGetStream( diff --git a/data/interfaces.go b/data/interfaces.go index a533b8c..17c911d 100644 --- a/data/interfaces.go +++ b/data/interfaces.go @@ -35,5 +35,5 @@ type MarketStore interface { } type busEventer interface { - processEvents(ctx context.Context, name string, req *vegaapipb.ObserveEventBusRequest, process func(*vegaapipb.ObserveEventBusResponse) (bool, error)) + processEvents(ctx context.Context, name string, req *vegaapipb.ObserveEventBusRequest, process func(*vegaapipb.ObserveEventBusResponse) (bool, error)) <-chan error } diff --git a/data/streamingaccount.go b/data/streamingaccount.go index 40c248d..c9d1553 100644 --- a/data/streamingaccount.go +++ b/data/streamingaccount.go @@ -18,19 +18,22 @@ import ( ) type account struct { - log *log.Entry - node DataNode - stores map[string]BalanceStore - walletPubKey string - busEvProc busEventer + name string + log *log.Entry + node DataNode + balanceStores *balanceStores + walletPubKey string + busEvProc busEventer mu sync.Mutex + once sync.Once waitingDeposits map[string]*num.Uint } -func NewAccountStream(node DataNode) *account { +func NewAccountStream(name string, node DataNode) *account { return &account{ - log: log.WithField("module", "AccountStreamer"), + name: name, + log: log.WithField("component", "AccountStreamer"), node: node, waitingDeposits: make(map[string]*num.Uint), } @@ -39,10 +42,18 @@ func NewAccountStream(node DataNode) *account { func (a *account) Init(pubKey string, pauseCh chan types.PauseSignal) { a.walletPubKey = pubKey a.busEvProc = newBusEventProcessor(a.node, WithPauseCh(pauseCh)) - a.stores = make(map[string]BalanceStore) + a.balanceStores = &balanceStores{ + balanceStores: make(map[string]BalanceStore), + } + + a.subscribeToAccountEvents() } -func (a *account) InitBalances(assetID string) (BalanceStore, error) { +func (a *account) GetBalances(assetID string) (BalanceStore, error) { + if store, ok := a.balanceStores.get(assetID); ok { + return store, nil + } + response, err := a.node.PartyAccounts(&dataapipb.PartyAccountsRequest{ PartyId: a.walletPubKey, Asset: assetID, @@ -53,15 +64,24 @@ func (a *account) InitBalances(assetID string) (BalanceStore, error) { if len(response.Accounts) == 0 { a.log.WithFields(log.Fields{ - "party": a.walletPubKey, - }).Warning("Party has no accounts") + "name": a.name, + "partyId": a.walletPubKey, + }).Warningf("Party has no accounts for asset %s", assetID) } store := types.NewBalanceStore() - a.stores[assetID] = store + a.balanceStores.set(assetID, store) for _, acc := range response.Accounts { - if err = a.setBalanceByType(acc); err != nil { + a.log.WithFields(log.Fields{ + "name": a.name, + "partyId": a.walletPubKey, + "accountType": acc.Type.String(), + "balance": acc.Balance, + "assetID": acc.Asset, + }).Debug("Setting initial account balance") + + if err = a.setBalanceByType(acc, store); err != nil { a.log.WithFields( log.Fields{ "error": err.Error(), @@ -71,13 +91,10 @@ func (a *account) InitBalances(assetID string) (BalanceStore, error) { } } - // TODO: avoid goroutine per every asset, better use a list of assets in that one goroutine - go a.subscribeToAccountEvents(assetID) - return store, nil } -func (a *account) subscribeToAccountEvents(assetID string) { +func (a *account) subscribeToAccountEvents() { req := &coreapipb.ObserveEventBusRequest{ Type: []eventspb.BusEventType{ eventspb.BusEventType_BUS_EVENT_TYPE_ACCOUNT, @@ -89,11 +106,12 @@ func (a *account) subscribeToAccountEvents(assetID string) { for _, event := range rsp.Events { acct := event.GetAccount() // filter out any that are for different assets - if acct.Asset != assetID { + store, ok := a.balanceStores.get(acct.Asset) + if !ok { continue } - if err := a.setBalanceByType(acct); err != nil { + if err := a.setBalanceByType(acct, store); err != nil { a.log.WithFields( log.Fields{ "error": err.Error(), @@ -105,36 +123,93 @@ func (a *account) subscribeToAccountEvents(assetID string) { return false, nil } - a.busEvProc.processEvents(context.Background(), "AccountData", req, proc) + a.busEvProc.processEvents(context.Background(), "AccountData: "+a.name, req, proc) } -// WaitForDepositFinalise is a blocking call that waits for the deposit finalize event to be received. -func (a *account) WaitForDepositFinalise(ctx context.Context, walletPubKey, assetID string, expectAmount *num.Uint, timeout time.Duration) error { +func (a *account) setBalanceByType(account *vega.Account, store BalanceStore) error { + balance, err := util.ConvertUint256(account.Balance) + if err != nil { + return fmt.Errorf("failed to convert account balance: %w", err) + } + + store.BalanceSet(types.SetBalanceByType(account.Type, balance)) + return nil +} + +// WaitForTopUpToFinalise is a blocking call that waits for the top-up finalise event to be received. +func (a *account) WaitForTopUpToFinalise( + ctx context.Context, + evtType eventspb.BusEventType, + walletPubKey, + assetID string, + expectAmount *num.Uint, + timeout time.Duration, +) error { if exist, ok := a.getWaitingDeposit(assetID); ok { - if expectAmount.GT(exist) { + if !expectAmount.EQ(exist) { a.setWaitingDeposit(assetID, expectAmount) } return nil } req := &coreapipb.ObserveEventBusRequest{ - Type: []eventspb.BusEventType{ - eventspb.BusEventType_BUS_EVENT_TYPE_DEPOSIT, - }, - PartyId: walletPubKey, + Type: []eventspb.BusEventType{evtType}, } proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { for _, event := range rsp.Events { - dep := event.GetDeposit() + var ( + status int32 + partyId string + asset string + amount string + ) + switch evtType { + case eventspb.BusEventType_BUS_EVENT_TYPE_DEPOSIT: + depEvt := event.GetDeposit() + if depEvt.Status != vega.Deposit_STATUS_FINALIZED { + if depEvt.Status == vega.Deposit_STATUS_OPEN { + continue + } else { + return true, fmt.Errorf("transfer %s failed: %s", depEvt.Id, depEvt.Status.String()) + } + } + status = int32(depEvt.Status) + partyId = depEvt.PartyId + asset = depEvt.Asset + amount = depEvt.Amount + case eventspb.BusEventType_BUS_EVENT_TYPE_TRANSFER: + depEvt := event.GetTransfer() + if depEvt.Status != eventspb.Transfer_STATUS_DONE { + if depEvt.Status == eventspb.Transfer_STATUS_PENDING { + continue + } else { + return true, fmt.Errorf("transfer %s failed: %s", depEvt.Id, depEvt.Status.String()) + } + } + + status = int32(depEvt.Status) + partyId = depEvt.To + asset = depEvt.Asset + amount = depEvt.Amount + } + // filter out any that are for different assets, or not finalized - if dep.Asset != assetID || dep.Status != vega.Deposit_STATUS_FINALIZED { + if partyId != walletPubKey || asset != assetID { continue } - gotAmount, overflow := num.UintFromString(dep.Amount, 10) + a.log.WithFields(log.Fields{ + "account.name": a.name, + "event.partyID": partyId, + "event.assetID": asset, + "event.amount": amount, + "event.status": status, + }).Debugf("Received %s event", event.Type.String()) + + gotAmount, overflow := num.UintFromString(amount, 10) if overflow { - return false, fmt.Errorf("failed to parse deposit expectAmount %s", dep.Amount) + return false, fmt.Errorf("failed to parse top-up expectAmount %s", amount) } expect, ok := a.getWaitingDeposit(assetID) @@ -144,9 +219,20 @@ func (a *account) WaitForDepositFinalise(ctx context.Context, walletPubKey, asse } if gotAmount.GTE(expect) { - a.log.WithFields(log.Fields{"amount": gotAmount}).Info("Deposit finalised") + a.log.WithFields(log.Fields{ + "name": a.name, + "partyId": walletPubKey, + "amount": gotAmount.String(), + }).Info("TopUp finalised") a.deleteWaitingDeposit(assetID) return true, nil + } else { + a.log.WithFields(log.Fields{ + "name": a.name, + "partyId": a.walletPubKey, + "gotAmount": gotAmount.String(), + "targetAmount": expect.String(), + }).Info("Received funds, but amount is less than expected") } } return false, nil @@ -158,8 +244,13 @@ func (a *account) WaitForDepositFinalise(ctx context.Context, walletPubKey, asse defer cancel() } - a.busEvProc.processEvents(ctx, "DepositData", req, proc) - return ctx.Err() + errCh := a.busEvProc.processEvents(ctx, "TopUpData: "+a.name, req, proc) + select { + case err := <-errCh: + return err + case <-ctx.Done(): + return fmt.Errorf("timed out waiting for top-up event") + } } func (a *account) getWaitingDeposit(assetID string) (*num.Uint, bool) { @@ -192,29 +283,54 @@ func (a *account) WaitForStakeLinking(pubKey string) error { proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { for _, event := range rsp.GetEvents() { stake := event.GetStakeLinking() - if stake.Party == pubKey && stake.Status == eventspb.StakeLinking_STATUS_ACCEPTED { - a.log.WithFields(log.Fields{ - "stakeID": stake.Id, - }).Info("Received stake linking") - return true, nil // stop processing + if stake.Party != pubKey { + continue + } + + if stake.Status != eventspb.StakeLinking_STATUS_ACCEPTED { + if stake.Status == eventspb.StakeLinking_STATUS_PENDING { + continue + } else { + return true, fmt.Errorf("stake linking failed: %s", stake.Status.String()) + } + } + a.log.WithFields(log.Fields{ + "name": a.name, + "partyId": stake.Party, + "stakeID": stake.Id, + }).Info("Received stake linking") + return true, nil } return false, nil } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*45) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*450) defer cancel() - a.busEvProc.processEvents(ctx, "StakeLinking", req, proc) - return ctx.Err() + errCh := a.busEvProc.processEvents(ctx, "StakeLinking: "+a.name, req, proc) + select { + case err := <-errCh: + return err + case <-ctx.Done(): + return fmt.Errorf("timed out waiting for top-up event") + } } -func (a *account) setBalanceByType(account *vega.Account) error { - balance, err := util.ConvertUint256(account.Balance) - if err != nil { - return fmt.Errorf("failed to convert account balance: %w", err) - } +type balanceStores struct { + mu sync.Mutex + balanceStores map[string]BalanceStore +} - a.stores[account.Asset].BalanceSet(types.SetBalanceByType(account.Type, balance)) - return nil +func (b *balanceStores) get(assetID string) (BalanceStore, bool) { + b.mu.Lock() + defer b.mu.Unlock() + store, ok := b.balanceStores[assetID] + return store, ok +} + +func (b *balanceStores) set(assetID string, store BalanceStore) { + b.mu.Lock() + defer b.mu.Unlock() + b.balanceStores[assetID] = store } diff --git a/data/streamingmarket.go b/data/streamingmarket.go index 0ffed48..a6972cb 100644 --- a/data/streamingmarket.go +++ b/data/streamingmarket.go @@ -19,6 +19,7 @@ import ( ) type market struct { + name string log *log.Entry node DataNode walletPubKey string @@ -27,10 +28,11 @@ type market struct { busEvProc busEventer } -func NewMarketStream(node DataNode) *market { +func NewMarketStream(name string, node DataNode) *market { return &market{ - log: log.WithField("module", "MarketStreamer"), + name: name, node: node, + log: log.WithField("component", "MarketStreamer"), } } @@ -55,8 +57,8 @@ func (m *market) Subscribe(marketID string) error { return fmt.Errorf("failed to get open volume: %w", err) } - go m.subscribeToMarketEvents() - go m.subscribePositions() + m.subscribeToMarketEvents() + m.subscribePositions() return nil } @@ -72,23 +74,35 @@ func (m *market) WaitForProposalID() (string, error) { for _, event := range rsp.GetEvents() { proposal := event.GetProposal() - if proposal.State == vega.Proposal_STATE_OPEN { - proposalID = proposal.Id + if proposal.PartyId != m.walletPubKey { + continue + } - m.log.WithFields(log.Fields{ - "proposalID": proposalID, - }).Info("Received proposal ID") - return true, nil // stop processing + if proposal.State != vega.Proposal_STATE_OPEN { + return true, fmt.Errorf("failed to propose market: %s; code: %s", + proposal.ErrorDetails, proposal.State.String()) } + + proposalID = proposal.Id + + m.log.WithFields(log.Fields{ + "proposalID": proposalID, + }).Info("Received proposal ID") + return true, nil } return false, nil } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*450) defer cancel() - m.busEvProc.processEvents(ctx, "Proposals", req, proc) - return proposalID, ctx.Err() + errCh := m.busEvProc.processEvents(ctx, "Proposals: "+m.name, req, proc) + select { + case err := <-errCh: + return proposalID, err + case <-ctx.Done(): + return "", fmt.Errorf("timed out waiting for proposal ID") + } } func (m *market) WaitForProposalEnacted(pID string) error { @@ -100,22 +114,38 @@ func (m *market) WaitForProposalEnacted(pID string) error { for _, event := range rsp.GetEvents() { proposal := event.GetProposal() - if proposal.State == vega.Proposal_STATE_ENACTED && proposal.Id == pID { - m.log.WithFields( - log.Fields{ - "proposalID": proposal.Id, - }).Debug("Proposal was enacted") - return true, nil // stop processing + if proposal.Id != pID { + continue } + + if proposal.State != vega.Proposal_STATE_ENACTED { + if proposal.State == vega.Proposal_STATE_OPEN { + continue + } + } else { + return true, fmt.Errorf("failed to enact market: %s; code: %s", + proposal.ErrorDetails, proposal.State.String()) + } + + m.log.WithFields( + log.Fields{ + "proposalID": proposal.Id, + }).Debug("Proposal was enacted") + return true, nil } return false, nil } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*450) defer cancel() - m.busEvProc.processEvents(ctx, "Proposals", req, proc) - return ctx.Err() + errCh := m.busEvProc.processEvents(ctx, "Proposals: "+m.name, req, proc) + select { + case err := <-errCh: + return err + case <-ctx.Done(): + return fmt.Errorf("timed out waiting for proposal enactment") + } } func (m *market) subscribeToMarketEvents() { @@ -140,7 +170,7 @@ func (m *market) subscribeToMarketEvents() { return false, nil } - m.busEvProc.processEvents(context.Background(), "MarketData", req, proc) + m.busEvProc.processEvents(context.Background(), "MarketData: "+m.name, req, proc) } func (m *market) subscribePositions() { @@ -168,7 +198,7 @@ func (m *market) subscribePositions() { return false, nil } - m.busEvProc.processEvents(context.Background(), "PositionData", req, proc) + m.busEvProc.processEvents(context.Background(), "PositionData: "+m.name, req, proc) } func (m *market) initOpenVolume() error { diff --git a/go.mod b/go.mod index 858b56e..18aec5b 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.18 require ( code.vegaprotocol.io/priceproxy v0.1.0 - code.vegaprotocol.io/shared v0.0.0-20220813000126-79cdcc23dfd0 - code.vegaprotocol.io/vega v0.56.0 + code.vegaprotocol.io/shared v0.0.0-20221004112011-87b09333cb77 + code.vegaprotocol.io/vega v0.56.1-0.20220926192337-0c9df7ca5232 github.com/ethereum/go-ethereum v1.10.21 github.com/golang/mock v1.6.1-0.20220512030613-73266f9366fc github.com/golang/protobuf v1.5.2 diff --git a/go.sum b/go.sum index a5c4823..d776d95 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,10 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= code.vegaprotocol.io/priceproxy v0.1.0 h1:PibvUIcTXS/MOIJKJwGXhJbDwg9ZPLItcIlfMvt1YO4= code.vegaprotocol.io/priceproxy v0.1.0/go.mod h1:W/CpimpwdplSLOnO1gEz2+VLQIV5I9FsDOST+1NDJ5A= -code.vegaprotocol.io/shared v0.0.0-20220813000126-79cdcc23dfd0 h1:6az/9MVVpbKSkFR8mFUy6+oWWI6HfNT769fcu9WLETg= -code.vegaprotocol.io/shared v0.0.0-20220813000126-79cdcc23dfd0/go.mod h1:XzX67GsyOHzvytMr0QOHX4CCTdCZDYKUUi88rx40Nt0= -code.vegaprotocol.io/vega v0.56.0 h1:UKk3qsk4HUvZXlKi3ZvJTKfwJFMs0GOi8uViz3EWeEM= -code.vegaprotocol.io/vega v0.56.0/go.mod h1:PRVKUFtwLhUdqSRcgpzQhoG5YCSGTWNwcXC7ddhN5Pk= +code.vegaprotocol.io/shared v0.0.0-20221004112011-87b09333cb77 h1:9sfXPLhPfyGiDiQ7VcMcY9RzDgQ/tvSVN9WQ00zFScI= +code.vegaprotocol.io/shared v0.0.0-20221004112011-87b09333cb77/go.mod h1:XzX67GsyOHzvytMr0QOHX4CCTdCZDYKUUi88rx40Nt0= +code.vegaprotocol.io/vega v0.56.1-0.20220926192337-0c9df7ca5232 h1:8BoG9AuczlSX4NBA2m2jDGa8sn4psgRqPR6Q+cmsaSs= +code.vegaprotocol.io/vega v0.56.1-0.20220926192337-0c9df7ca5232/go.mod h1:97cMeA2/j36cACG9fCxaHYu+ER1rEBxZUdbnbfT4pas= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -176,6 +176,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko= github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc= diff --git a/market/interfaces.go b/market/interfaces.go index 168aa90..6a10551 100644 --- a/market/interfaces.go +++ b/market/interfaces.go @@ -35,5 +35,5 @@ type tradingDataService interface { type accountService interface { EnsureBalance(ctx context.Context, assetID string, targetAmount *num.Uint, from string) error - EnsureStake(ctx context.Context, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error + EnsureStake(ctx context.Context, receiverName, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error } diff --git a/market/service.go b/market/service.go index ca1e6ad..9c468e3 100644 --- a/market/service.go +++ b/market/service.go @@ -24,6 +24,7 @@ import ( ) type Service struct { + name string pricingEngine PricingEngine marketStream marketStream node tradingDataService @@ -40,6 +41,7 @@ type Service struct { } func NewService( + name string, marketStream marketStream, node tradingDataService, walletClient normal.WalletClient, @@ -49,6 +51,7 @@ func NewService( vegaAssetID string, ) *Service { s := &Service{ + name: name, marketStream: marketStream, node: node, walletClient: walletClient, @@ -165,17 +168,40 @@ func (m *Service) CreateMarket(ctx context.Context) error { seedAmount := m.config.StrategyDetails.SeedAmount.Get() + m.log.WithFields(log.Fields{ + "amount": seedAmount.String(), + "asset": m.config.SettlementAssetID, + "name": m.name, + }).Info("Ensuring balance for market creation") + // TODO: is it m.settlementAssetID? if err := m.account.EnsureBalance(ctx, m.config.SettlementAssetID, seedAmount, "MarketCreation"); err != nil { return fmt.Errorf("failed to ensure balance: %w", err) } - if err := m.account.EnsureStake(ctx, m.walletPubKey, m.vegaAssetID, seedAmount, "MarketCreation"); err != nil { + m.log.WithFields(log.Fields{ + "amount": seedAmount.String(), + "asset": m.config.SettlementAssetID, + "name": m.name, + }).Info("Balance ensured") + + m.log.WithFields(log.Fields{ + "amount": seedAmount.String(), + "asset": m.vegaAssetID, + "name": m.name, + }).Info("Ensuring stake for market creation") + + if err := m.account.EnsureStake(ctx, m.config.Name, m.walletPubKey, m.vegaAssetID, seedAmount, "MarketCreation"); err != nil { return fmt.Errorf("failed to ensure stake: %w", err) } - m.log.Debug("Successfully linked stake") - m.log.Debug("Sending new market proposal") + m.log.WithFields(log.Fields{ + "amount": seedAmount.String(), + "asset": m.vegaAssetID, + "name": m.name, + }).Info("Successfully linked stake") + + m.log.Info("Sending new market proposal...") if err := m.sendNewMarketProposal(ctx); err != nil { return fmt.Errorf("failed to send new market proposal: %w", err) @@ -212,8 +238,7 @@ func (m *Service) sendNewMarketProposal(ctx context.Context) error { } submitTxReq := &walletpb.SubmitTransactionRequest{ - PubKey: m.walletPubKey, - // Propagate: true, TODO: OK to remove? + PubKey: m.walletPubKey, Command: cmd, } @@ -283,7 +308,7 @@ func (m *Service) SubmitOrder(ctx context.Context, order *vega.Order, from strin m.log.WithFields(log.Fields{ "reference": order.Reference, "size": order.Size, - "side": order.Side, + "side": order.Side.String(), "price": order.Price, "tif": order.TimeInForce.String(), }).Debugf("%s: Submitting order", from) @@ -385,7 +410,7 @@ func (m *Service) getExampleMarketProposal() *v1.ProposalSubmission { }, Reference: "ProposalReference", Terms: &vega.ProposalTerms{ - ClosingTimestamp: secondsFromNowInSecs(10), + ClosingTimestamp: secondsFromNowInSecs(15), EnactmentTimestamp: secondsFromNowInSecs(15), Change: &vega.ProposalTerms_NewMarket{ NewMarket: m.getExampleMarket(), @@ -414,12 +439,14 @@ func (m *Service) getExampleMarket() *vega.NewMarket { }, }, }, - /*LiquidityCommitment: &vega.NewMarketCommitment{ - Fee: fmt.Sprint(m.config.StrategyDetails.Fee), - CommitmentAmount: m.config.StrategyDetails.CommitmentAmount, - Buys: m.config.StrategyDetails.ShorteningShape.Buys.ToVegaLiquidityOrders(), - Sells: m.config.StrategyDetails.LongeningShape.Sells.ToVegaLiquidityOrders(), - },*/ + /* + TODO: is this needed? + LiquidityCommitment: &vega.NewMarketCommitment{ + Fee: fmt.Sprint(m.config.StrategyDetails.Fee), + CommitmentAmount: m.config.StrategyDetails.CommitmentAmount, + Buys: m.config.StrategyDetails.ShorteningShape.Buys.ToVegaLiquidityOrders(), + Sells: m.config.StrategyDetails.LongeningShape.Sells.ToVegaLiquidityOrders(), + },*/ } } diff --git a/node/datanode.go b/node/datanode.go index 7775b12..4aa4f02 100644 --- a/node/datanode.go +++ b/node/datanode.go @@ -146,7 +146,7 @@ func (n *DataNode) ObserveEventBus(ctx context.Context) (client vegaapipb.CoreSe return } - if n.conn.GetState() != connectivity.Ready { + if n.conn == nil || n.conn.GetState() != connectivity.Ready { err = fmt.Errorf(msg, e.ErrConnectionNotReady) return } diff --git a/service/service.go b/service/service.go index be0209e..ca1a246 100644 --- a/service/service.go +++ b/service/service.go @@ -99,7 +99,7 @@ func getWhale(config config.Config) (*whale.Service, error) { faucetService := token.NewFaucetService(config.Whale.FaucetURL, config.Whale.WalletPubKey) whaleWallet := wallet.NewClient(config.Wallet.URL) - accountStream := data.NewAccountStream(dataNode) + accountStream := data.NewAccountStream("whale", dataNode) tokenService, err := token.NewService(config.Token, config.Whale.WalletPubKey) if err != nil { diff --git a/token/erc20_service.go b/token/erc20_service.go index 809ba1d..25308dd 100644 --- a/token/erc20_service.go +++ b/token/erc20_service.go @@ -29,9 +29,10 @@ type Service struct { func NewService(conf *config.TokenConfig, vegaPubKey string) (*Service, error) { ctx := context.Background() - var syncTimeout time.Duration + var syncTimeout *time.Duration if conf.SyncTimeoutSec != 0 { - syncTimeout = time.Duration(conf.SyncTimeoutSec) * time.Second + syncTimeoutVal := time.Duration(conf.SyncTimeoutSec) * time.Second + syncTimeout = &syncTimeoutVal } client, err := vgethereum.NewClient(ctx, conf.EthereumAPIAddress) @@ -44,8 +45,8 @@ func NewService(conf *config.TokenConfig, vegaPubKey string) (*Service, error) { vegaPubKey: vegaPubKey, erc20BridgeAddress: common.HexToAddress(conf.Erc20BridgeAddress), stakingBridgeAddress: common.HexToAddress(conf.StakingBridgeAddress), - syncTimeout: &syncTimeout, - log: log.WithFields(log.Fields{"service": "Token"}), + syncTimeout: syncTimeout, + log: log.WithFields(log.Fields{"component": "TokenService"}), }, nil } diff --git a/types/interfaces.go b/types/interfaces.go index d34237c..8163482 100644 --- a/types/interfaces.go +++ b/types/interfaces.go @@ -5,6 +5,7 @@ import ( ppconfig "code.vegaprotocol.io/priceproxy/config" ppservice "code.vegaprotocol.io/priceproxy/service" + v1 "code.vegaprotocol.io/vega/protos/vega/events/v1" "code.vegaprotocol.io/liqbot/types/num" ) @@ -26,6 +27,6 @@ type PricingEngine interface { } type CoinProvider interface { - TopUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error + TopUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) (v1.BusEventType, error) StakeAsync(ctx context.Context, receiverAddress, assetID string, amount *num.Uint) error } diff --git a/whale/interfaces.go b/whale/interfaces.go index 05503ee..d943adc 100644 --- a/whale/interfaces.go +++ b/whale/interfaces.go @@ -32,6 +32,6 @@ type faucetClient interface { type accountService interface { Init(pubKey string, pauseCh chan types.PauseSignal) EnsureBalance(ctx context.Context, assetID string, targetAmount *num.Uint, from string) error - EnsureStake(ctx context.Context, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error + EnsureStake(ctx context.Context, receiverName, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error StakeAsync(ctx context.Context, receiverPubKey, assetID string, amount *num.Uint) error } diff --git a/whale/provider.go b/whale/provider.go index 2227e41..15e12a7 100644 --- a/whale/provider.go +++ b/whale/provider.go @@ -16,6 +16,7 @@ import ( "code.vegaprotocol.io/liqbot/util" dataapipb "code.vegaprotocol.io/vega/protos/data-node/api/v1" "code.vegaprotocol.io/vega/protos/vega" + v1 "code.vegaprotocol.io/vega/protos/vega/events/v1" ) type Provider struct { @@ -41,7 +42,6 @@ type ensureBalanceRequest struct { address string assetID string amount *num.Uint - resp chan error } type slacker struct { @@ -75,7 +75,8 @@ func NewProvider( enabled: config.SlackConfig.Enabled, }, log: log.WithFields(log.Fields{ - "whale": config.WalletName, + "component": "WhaleProvider", + "whaleName": config.WalletName, }), } @@ -83,31 +84,29 @@ func NewProvider( for { select { case req := <-p.ensureBalanceCh: - req.resp <- p.topUpAsync(req.ctx, req.name, req.address, req.assetID, req.amount) + if err := p.topUpAsync(req.ctx, req.name, req.address, req.assetID, req.amount); err != nil { + log.Errorf("Whale: failed to ensure enough funds: %s", err) + } } } }() return p } -func (p *Provider) TopUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) (err error) { - resp := make(chan error) +func (p *Provider) TopUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) (evt v1.BusEventType, err error) { + evt = v1.BusEventType_BUS_EVENT_TYPE_DEPOSIT // TODO: this is not always true? p.ensureBalanceCh <- ensureBalanceRequest{ ctx: ctx, name: receiverName, address: receiverAddress, assetID: assetID, amount: amount, - resp: resp, - } - - if err = <-resp; err != nil { - log.Errorf("Whale: failed to ensure enough funds: %s", err) } return } func (p *Provider) topUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) (err error) { + // TODO: remove deposit slack request, once deposited if existDeposit, ok := p.getPendingDeposit(assetID); ok { existDeposit.amount = amount.Add(amount, existDeposit.amount) if p.slack.enabled { @@ -162,12 +161,15 @@ func (p *Provider) deposit(ctx context.Context, receiverName, receiverAddress, a err = p.depositERC20(ctx, response.Asset, amount) } else if builtin := response.Asset.Details.GetBuiltinAsset(); builtin != nil { err = p.depositBuiltin(ctx, assetID, amount, builtin) + } else { + return fmt.Errorf("unsupported asset type") + } if err != nil { return fmt.Errorf("failed to deposit to address '%s', name '%s': %w", receiverAddress, receiverName, err) } - return fmt.Errorf("unsupported asset type") + return nil } func (p *Provider) getPendingDeposit(assetID string) (pendingDeposit, bool) { diff --git a/whale/service.go b/whale/service.go index bfdc768..5a0e470 100644 --- a/whale/service.go +++ b/whale/service.go @@ -11,6 +11,7 @@ import ( "code.vegaprotocol.io/liqbot/types/num" vtypes "code.vegaprotocol.io/vega/core/types" commV1 "code.vegaprotocol.io/vega/protos/vega/commands/v1" + v12 "code.vegaprotocol.io/vega/protos/vega/events/v1" "code.vegaprotocol.io/vega/protos/vega/wallet/v1" ) @@ -40,7 +41,8 @@ func NewService( walletName: config.WalletName, walletPassphrase: config.WalletPassphrase, log: log.WithFields(log.Fields{ - "whale": config.WalletName, + "component": "Whale", + "name": config.WalletName, }), } } @@ -56,31 +58,56 @@ func (w *Service) Start(ctx context.Context) error { } }() - w.account.Init(w.walletPubKey, pauseCh) - if err := w.wallet.LoginWallet(ctx, w.walletName, w.walletPassphrase); err != nil { return fmt.Errorf("failed to login to wallet: %s", err) } + w.log.Info("Attempting to connect to a node...") w.node.MustDialConnection(ctx) + w.log.Info("Connected to a node") + + w.account.Init(w.walletPubKey, pauseCh) return nil } -func (w *Service) TopUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error { +func (w *Service) TopUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) (v12.BusEventType, error) { + w.log.Debugf("Top up for '%s' ...", receiverName) + + evt := v12.BusEventType_BUS_EVENT_TYPE_TRANSFER + if assetID == "" { - return fmt.Errorf("assetID is empty for bot '%s'", receiverName) + return evt, fmt.Errorf("assetID is empty for bot '%s'", receiverName) } if receiverAddress == w.walletPubKey { - return fmt.Errorf("whale and bot address cannot be the same") + return evt, fmt.Errorf("whale and bot address cannot be the same") } go func() { - if err := w.account.EnsureBalance(ctx, assetID, amount, "Whale"); err != nil { + w.log.WithFields( + log.Fields{ + "receiverName": receiverName, + "receiverPubKey": receiverAddress, + "assetID": assetID, + "amount": amount.String(), + }).Debugf("Ensuring whale has enough balance...") + + ensureAmount := num.Zero().Mul(amount, num.NewUint(30)) + + // TODO: retry + if err := w.account.EnsureBalance(ctx, assetID, ensureAmount, "Whale"); err != nil { w.log.Errorf("Whale: failed to ensure enough funds: %s", err) return } + w.log.WithFields( + log.Fields{ + "receiverName": receiverName, + "receiverPubKey": receiverAddress, + "assetID": assetID, + "amount": amount.String(), + }).Debugf("Whale balance ensured, sending funds...") + err := w.wallet.SignTx(ctx, &v1.SubmitTransactionRequest{ PubKey: w.walletPubKey, Propagate: true, @@ -99,9 +126,17 @@ func (w *Service) TopUpAsync(ctx context.Context, receiverName, receiverAddress, if err != nil { log.Errorf("Failed to top-up bot '%s': %s", receiverName, err) } + + w.log.WithFields( + log.Fields{ + "receiverName": receiverName, + "receiverPubKey": receiverAddress, + "assetID": assetID, + "amount": amount.String(), + }).Debugf("Top-up sent") }() - return nil + return evt, nil } func (w *Service) StakeAsync(ctx context.Context, receiverAddress, assetID string, amount *num.Uint) error { diff --git a/whale/service_test.go b/whale/service_test.go index 38ad8e7..bd5c4ff 100644 --- a/whale/service_test.go +++ b/whale/service_test.go @@ -49,7 +49,7 @@ func TestService_TopUp(t *testing.T) { cp := NewProvider(dn, es, fc, conf) - ds := data.NewAccountStream(dn) + ds := data.NewAccountStream("test", dn) as := account.NewAccountService("test", "asset", ds, cp) s := NewService(dn, wc, as, conf) @@ -66,7 +66,7 @@ func TestService_TopUp(t *testing.T) { amount := num.NewUint(988939143512) - s.TopUpAsync(ctx, "some bot", key, asset, amount) + _, _ = s.TopUpAsync(ctx, "some bot", key, asset, amount) _ = s.account.EnsureBalance(ctx, asset, amount, "test") } From 93aa0343ba297784b90b8a155a239642aeba75df Mon Sep 17 00:00:00 2001 From: Merlin Gaillard Date: Wed, 5 Oct 2022 08:30:15 +0000 Subject: [PATCH 07/18] chore: temporarily bypass tests --- .github/workflows/continous-deployment.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continous-deployment.yml b/.github/workflows/continous-deployment.yml index eda0a37..fe1d9ef 100644 --- a/.github/workflows/continous-deployment.yml +++ b/.github/workflows/continous-deployment.yml @@ -7,10 +7,10 @@ name: "Continous Deployment Workflow" - v* jobs: - integration: - uses: ./.github/workflows/continous-integration.yml + #integration: + # uses: ./.github/workflows/continous-integration.yml delivery: - needs: integration + #needs: integration uses: ./.github/workflows/continous-delivery.yml update-manifest-repo: needs: delivery From 8c6ac52ddaa79f39003b53945d186b8b21a9fc7a Mon Sep 17 00:00:00 2001 From: Merlin Gaillard Date: Wed, 5 Oct 2022 09:27:00 +0000 Subject: [PATCH 08/18] chore: update docker creds vars --- .github/workflows/continous-delivery.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continous-delivery.yml b/.github/workflows/continous-delivery.yml index 83137a5..50b82ab 100644 --- a/.github/workflows/continous-delivery.yml +++ b/.github/workflows/continous-delivery.yml @@ -22,8 +22,8 @@ jobs: uses: docker/login-action@v2 if: ${{!github.event.repository.private}} with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + username: ${{ secrets.DOCKERHUB_USERNAME_2 }} + password: ${{ secrets.DOCKERHUB_TOKEN_2 }} - name: Log in to the Container registry uses: docker/login-action@v2 From 5c11111f8170ba21912eb9e02d113efbac069c01 Mon Sep 17 00:00:00 2001 From: Merlin Gaillard Date: Wed, 5 Oct 2022 09:39:56 +0000 Subject: [PATCH 09/18] chore: remove dockerhub, use ghcr --- .github/workflows/continous-delivery.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/continous-delivery.yml b/.github/workflows/continous-delivery.yml index 50b82ab..043e6ae 100644 --- a/.github/workflows/continous-delivery.yml +++ b/.github/workflows/continous-delivery.yml @@ -18,12 +18,12 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - - name: Login to DockerHub - uses: docker/login-action@v2 - if: ${{!github.event.repository.private}} - with: - username: ${{ secrets.DOCKERHUB_USERNAME_2 }} - password: ${{ secrets.DOCKERHUB_TOKEN_2 }} + #- name: Login to DockerHub + # uses: docker/login-action@v2 + # if: ${{!github.event.repository.private}} + # with: + # username: ${{ secrets.DOCKERHUB_USERNAME_2 }} + # password: ${{ secrets.DOCKERHUB_TOKEN_2 }} - name: Log in to the Container registry uses: docker/login-action@v2 From 8cc55a536aefede30726ad753916a45bb1d22cfa Mon Sep 17 00:00:00 2001 From: Merlin Gaillard Date: Wed, 5 Oct 2022 10:31:43 +0000 Subject: [PATCH 10/18] chore: bump docker base image --- .github/workflows/continous-delivery.yml | 18 +++++++++--------- Dockerfile | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/continous-delivery.yml b/.github/workflows/continous-delivery.yml index 043e6ae..726734e 100644 --- a/.github/workflows/continous-delivery.yml +++ b/.github/workflows/continous-delivery.yml @@ -4,7 +4,7 @@ name: "Continous Delivery Workflow" workflow_call: env: - GO_VERSION: "1.18.0" + GO_VERSION: "1.19.0" jobs: release-docker: @@ -42,14 +42,14 @@ jobs: tags: ghcr.io/${{ github.repository }}:latest,ghcr.io/${{ github.repository }}:${{ github.sha }},ghcr.io/${{ github.repository }}:${{ github.ref_name }} # public registry builds - - name: Build and push - uses: docker/build-push-action@v3 - if: ${{!github.event.repository.private}} - with: - context: . - build-args: GO_VERSION=${{env.GO_VERSION}} - push: true - tags: ${{ github.repository }}:latest,${{ github.repository }}:${{ github.sha }},${{ github.repository }}:${{ github.ref_name }} + #- name: Build and push + # uses: docker/build-push-action@v3 + # if: ${{!github.event.repository.private}} + # with: + # context: . + # build-args: GO_VERSION=${{env.GO_VERSION}} + # push: true + # tags: ${{ github.repository }}:latest,${{ github.repository }}:${{ github.sha }},${{ github.repository }}:${{ github.ref_name }} release-binary: diff --git a/Dockerfile b/Dockerfile index 9329131..f2fdb41 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # FILE IS AUTOMATICALLY MANAGED BY github.com/vegaprotocol/terraform//github -ARG GO_VERSION=1.18.0 +ARG GO_VERSION=1.19.0 ARG ALPINE_VERSION=3.16 FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS builder RUN mkdir /build From 0c0b19553acfd9d36b23f3119a16e6d864d36352 Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Wed, 5 Oct 2022 15:18:41 +0100 Subject: [PATCH 11/18] Fix logging and config --- bot/normal/normal.go | 22 ----------------- config/config_stagnet1.yaml | 10 ++++---- service/service.go | 47 +++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/bot/normal/normal.go b/bot/normal/normal.go index 1b17307..6300922 100644 --- a/bot/normal/normal.go +++ b/bot/normal/normal.go @@ -4,8 +4,6 @@ import ( "context" "encoding/json" "fmt" - "path" - "runtime" "strings" "sync" @@ -76,8 +74,6 @@ func New( // Start starts the liquidity bot goroutine(s). func (b *bot) Start() error { - setupLogger(true) // TODO: pretty from config? - ctx := context.Background() walletPubKey, err := b.setupWallet(ctx) @@ -141,24 +137,6 @@ func (b *bot) Start() error { return nil } -func setupLogger(pretty bool) { - log.SetReportCaller(true) - log.SetFormatter(&log.JSONFormatter{ - CallerPrettyfier: func(f *runtime.Frame) (string, string) { - filename := path.Base(f.File) - function := strings.ReplaceAll(f.Function, "code.vegaprotocol.io/", "") - idx := strings.Index(function, ".") - function = fmt.Sprintf("%s/%s/%s():%d", function[:idx], filename, function[idx+1:], f.Line) - return function, "" - }, - PrettyPrint: pretty, - DataKey: "_vals", - FieldMap: log.FieldMap{ - log.FieldKeyMsg: "_msg", - }, - }) -} - func (b *bot) pauseChannel() chan types.PauseSignal { in := make(chan types.PauseSignal) go func() { diff --git a/config/config_stagnet1.yaml b/config/config_stagnet1.yaml index 6ba5a01..c1eae93 100644 --- a/config/config_stagnet1.yaml +++ b/config/config_stagnet1.yaml @@ -3,8 +3,8 @@ server: env: prod # dev, prod listen: ":7800" - logformat: text # json, text - loglevel: debug # debug, info, warning, error + logformat: json # json_pretty, text + loglevel: debug # debug, trace, info, warning, error callTimeoutMills: 10000 vegaAssetID: fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55 @@ -23,13 +23,13 @@ token: syncTimeoutSec: 1000 whale: walletPubKey: bdb4598b5e22ac01a812d166cc3006ac70575cd6b91b5bb4de52e158e510ec23 - walletName: whale # secret - walletPassphrase: pastazazube # secret + walletName: # secret + walletPassphrase: # secret syncTimeoutSec: 0 ownerPrivateKeys: # assetID: privateKey c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d: # secret fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55: # secret - faucetURL: https://faucet.stagnet3.vega.xyz + faucetURL: https://faucet.stagnet1.vega.xyz slack: appToken: # secret botToken: # secret diff --git a/service/service.go b/service/service.go index ca1a246..20bf855 100644 --- a/service/service.go +++ b/service/service.go @@ -5,6 +5,8 @@ import ( "encoding/json" "fmt" "net/http" + "path" + "runtime" "strings" "sync" "time" @@ -74,6 +76,10 @@ func NewService(config config.Config) (s *Service, err error) { bots: make(map[string]Bot), } + if err = setupLogger(config.Server); err != nil { + return nil, fmt.Errorf("failed to setup logger: %w", err) + } + pricingEngine := pricing.NewEngine(*config.Pricing) whaleService, err := getWhale(config) @@ -91,6 +97,47 @@ func NewService(config config.Config) (s *Service, err error) { return s, err } +func setupLogger(conf *config.ServerConfig) error { + level, err := log.ParseLevel(conf.LogLevel) + if err != nil { + return fmt.Errorf("failed to parse log level: %w", err) + } + + log.SetLevel(level) + + var callerPrettyfier func(*runtime.Frame) (string, string) + + if conf.LogLevel == "debug" { + log.SetReportCaller(true) + + callerPrettyfier = func(f *runtime.Frame) (string, string) { + filename := path.Base(f.File) + function := strings.ReplaceAll(f.Function, "code.vegaprotocol.io/", "") + idx := strings.Index(function, ".") + function = fmt.Sprintf("%s/%s/%s():%d", function[:idx], filename, function[idx+1:], f.Line) + return function, "" + } + } + + var formatter log.Formatter = &log.TextFormatter{ + CallerPrettyfier: callerPrettyfier, + } + + if conf.LogFormat == "json" || conf.LogFormat == "json_pretty" { + formatter = &log.JSONFormatter{ + PrettyPrint: conf.LogFormat == "json_pretty", + DataKey: "_vals", + FieldMap: log.FieldMap{ + log.FieldKeyMsg: "_msg", + }, + CallerPrettyfier: callerPrettyfier, + } + } + + log.SetFormatter(formatter) + return nil +} + func getWhale(config config.Config) (*whale.Service, error) { dataNode := node.NewDataNode( config.Locations, From 9ccaed74bbb545d727ba6b926615b14b5f14ce3c Mon Sep 17 00:00:00 2001 From: zale144 Date: Fri, 7 Oct 2022 11:59:41 +0100 Subject: [PATCH 12/18] Update node/datanode.go Co-authored-by: peterbarrow <62435083+peterbarrow@users.noreply.github.com> --- node/datanode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/datanode.go b/node/datanode.go index 4aa4f02..483dc92 100644 --- a/node/datanode.go +++ b/node/datanode.go @@ -36,7 +36,7 @@ func NewDataNode(hosts []string, callTimeoutMil int) *DataNode { } // MustDialConnection tries to establish a connection to one of the nodes from a list of locations. -// It is idempotent, while it each call will block the caller until a connection is established. +// It is idempotent, where each call will block the caller until a connection is established. func (n *DataNode) MustDialConnection(ctx context.Context) { n.once.Do(func() { ctx, cancel := context.WithCancel(ctx) From ff120e0a6a6ea067c294227b0de05a9a013b8793 Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Mon, 10 Oct 2022 09:04:07 +0100 Subject: [PATCH 13/18] Fix pipeline --- token/erc20_service_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/token/erc20_service_test.go b/token/erc20_service_test.go index 34ca86e..6007028 100644 --- a/token/erc20_service_test.go +++ b/token/erc20_service_test.go @@ -12,6 +12,8 @@ import ( ) func TestService_mintToken(t *testing.T) { + t.Skip() + conf := &config.TokenConfig{ EthereumAPIAddress: "wss://ropsten.infura.io/ws/v3/0b0e1795edae41f59f4c99d29ba0ae8e", } From 22937a624c5f3a24720d333a7c5ce3da34f4bd96 Mon Sep 17 00:00:00 2001 From: Merlin Gaillard Date: Mon, 10 Oct 2022 08:17:39 +0000 Subject: [PATCH 14/18] chore: restore push to dockerhub --- .github/workflows/continous-delivery.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/continous-delivery.yml b/.github/workflows/continous-delivery.yml index 1da8f18..f2d5969 100644 --- a/.github/workflows/continous-delivery.yml +++ b/.github/workflows/continous-delivery.yml @@ -23,13 +23,12 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - #- name: Log in to the Container registry - # uses: docker/login-action@v2 - # if: ${{ !github.event.repository.private }} - # with: - # registry: registry.hub.docker.com - # username: ${{ secrets.DOCKERHUB_USERNAME }} - # password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Log in to the Container registry + uses: docker/login-action@v2 + if: ${{ !github.event.repository.private }} + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Log in to the Container registry From d62a6f0905f4081f21225b1ee4337ae9f4707f6b Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Mon, 10 Oct 2022 09:18:08 +0100 Subject: [PATCH 15/18] Fix pipeline #1 --- .github/workflows/continous-integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continous-integration.yml b/.github/workflows/continous-integration.yml index d8e5aef..82a213c 100644 --- a/.github/workflows/continous-integration.yml +++ b/.github/workflows/continous-integration.yml @@ -3,7 +3,7 @@ name: "Continous Integration Workflow" "on": workflow_call: pull_request: - + push: branches: @@ -34,5 +34,5 @@ jobs: uses: golangci/golangci-lint-action@v3 with: version: v1.45 + skip-go-installation: true args: --config .golangci.toml - \ No newline at end of file From 222d58d472392cdc6ff5943d11feff8b2da51738 Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Mon, 10 Oct 2022 09:52:59 +0100 Subject: [PATCH 16/18] Fix pipeline #2 --- .github/workflows/continous-integration.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continous-integration.yml b/.github/workflows/continous-integration.yml index 82a213c..9d1236f 100644 --- a/.github/workflows/continous-integration.yml +++ b/.github/workflows/continous-integration.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v3 with: - go-version: '1.19.0' + go-version: 1.19 - name: build run: make build @@ -33,6 +33,6 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: - version: v1.45 - skip-go-installation: true + version: v1.49.0 args: --config .golangci.toml + timeout: 15m From 6e10b554ed321c28ce3e82aca1185706fd05c75f Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Mon, 10 Oct 2022 09:57:31 +0100 Subject: [PATCH 17/18] Fix pipeline #3 --- .github/workflows/continous-integration.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/continous-integration.yml b/.github/workflows/continous-integration.yml index 9d1236f..375bcc7 100644 --- a/.github/workflows/continous-integration.yml +++ b/.github/workflows/continous-integration.yml @@ -35,4 +35,3 @@ jobs: with: version: v1.49.0 args: --config .golangci.toml - timeout: 15m From b44702f97dca3f74e64ff5e3b3cef1b546a7e54e Mon Sep 17 00:00:00 2001 From: Aleksandar Sukovic Date: Mon, 10 Oct 2022 11:07:06 +0100 Subject: [PATCH 18/18] Fix linting errors --- .golangci.toml | 4 ++ account/service.go | 5 +- bot/normal/helpers.go | 4 +- bot/normal/normal.go | 7 +- bot/normal/position_management.go | 2 - cmd/liqbot/doc.go | 4 +- data/streamingaccount.go | 2 - market/interfaces.go | 2 +- market/service.go | 1 - node/datanode.go | 110 ++++++++++++++---------------- pricing/pricing.go | 19 ++---- service/service.go | 8 +-- token/faucet_service.go | 4 ++ types/store.go | 6 +- types/types.go | 1 + wallet/client.go | 2 +- whale/provider.go | 35 ++++------ 17 files changed, 102 insertions(+), 114 deletions(-) diff --git a/.golangci.toml b/.golangci.toml index 27849bd..4b585fe 100644 --- a/.golangci.toml +++ b/.golangci.toml @@ -80,6 +80,10 @@ linters = ["forbidigo"] path = "cmd/" linters = ["forbidigo"] +[[issues.exclude-rules]] +path = "./" +linters = ["nosnakecase","exhaustruct"] + [[issues.exclude-rules]] path = "flags.go" linters = ["forbidigo"] diff --git a/account/service.go b/account/service.go index 4e6e7b7..ab478ce 100644 --- a/account/service.go +++ b/account/service.go @@ -80,7 +80,6 @@ func (a *Service) EnsureBalance(ctx context.Context, assetID string, targetAmoun return nil } -// TODO: DRY func (a *Service) EnsureStake(ctx context.Context, receiverName, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error { if receiverPubKey == "" { return fmt.Errorf("receiver public key is empty") @@ -147,7 +146,9 @@ func (a *Service) Balance() types.Balance { return store.Balance() } -func (a *Service) getStore(assetID string) (_ data.BalanceStore, err error) { +func (a *Service) getStore(assetID string) (data.BalanceStore, error) { + var err error + store, ok := a.stores[assetID] if !ok { store, err = a.accountStream.GetBalances(assetID) diff --git a/bot/normal/helpers.go b/bot/normal/helpers.go index cbcba39..b3f3d85 100644 --- a/bot/normal/helpers.go +++ b/bot/normal/helpers.go @@ -99,7 +99,7 @@ func discreteThreeLevelProbabilities(V []float64, muHat float64, sigmaHat float6 // generatePriceUsingDiscreteThreeLevel is a method for calculating price levels // input is a float price (so divide uint64 price by 10^{num of decimals}) // it returns a float price which you want to multiply by 10^{num of decimals} and then round. -func generatePriceUsingDiscreteThreeLevel(m0, delta, sigma, tgtTimeHorizonYrFrac, n float64) (price float64, err error) { +func generatePriceUsingDiscreteThreeLevel(m0, delta, sigma, tgtTimeHorizonYrFrac, n float64) (float64, error) { muHat := -0.5 * sigma * sigma * tgtTimeHorizonYrFrac sigmaHat := math.Sqrt(n*tgtTimeHorizonYrFrac) * sigma v := make([]float64, 3) @@ -117,7 +117,7 @@ func generatePriceUsingDiscreteThreeLevel(m0, delta, sigma, tgtTimeHorizonYrFrac shockX := v[randomChoice(probabilities)] y := math.Exp(shockX / n) - price = m0 * y + price := m0 * y return price, nil } diff --git a/bot/normal/normal.go b/bot/normal/normal.go index 6300922..c6e68bd 100644 --- a/bot/normal/normal.go +++ b/bot/normal/normal.go @@ -140,11 +140,8 @@ func (b *bot) Start() error { func (b *bot) pauseChannel() chan types.PauseSignal { in := make(chan types.PauseSignal) go func() { - for { - select { - case p := <-in: - b.Pause(p) - } + for p := range in { + b.Pause(p) } }() return in diff --git a/bot/normal/position_management.go b/bot/normal/position_management.go index 93cb31d..6e04904 100644 --- a/bot/normal/position_management.go +++ b/bot/normal/position_management.go @@ -459,8 +459,6 @@ func (b *bot) calculateOrderSizes(obligation *num.Uint, liquidityOrders []*vega. } order := vega.Order{ - //MarketId: b.marketID, - //PartyId: b.walletPubKey, Side: vega.Side_SIDE_BUY, Remaining: size.Uint64(), Size: size.Uint64(), diff --git a/cmd/liqbot/doc.go b/cmd/liqbot/doc.go index c09fb3d..d772586 100644 --- a/cmd/liqbot/doc.go +++ b/cmd/liqbot/doc.go @@ -2,6 +2,6 @@ // provide liquidity. Each bot connects to one Vega node and submits orders to // one market. // -// $ make install -// $ $GOPATH/bin/liqbot -config=config.yml +// $ make install +// $ $GOPATH/bin/liqbot -config=config.yml package main diff --git a/data/streamingaccount.go b/data/streamingaccount.go index c9d1553..0fed167 100644 --- a/data/streamingaccount.go +++ b/data/streamingaccount.go @@ -26,7 +26,6 @@ type account struct { busEvProc busEventer mu sync.Mutex - once sync.Once waitingDeposits map[string]*num.Uint } @@ -293,7 +292,6 @@ func (a *account) WaitForStakeLinking(pubKey string) error { } else { return true, fmt.Errorf("stake linking failed: %s", stake.Status.String()) } - } a.log.WithFields(log.Fields{ "name": a.name, diff --git a/market/interfaces.go b/market/interfaces.go index 6a10551..61f27e0 100644 --- a/market/interfaces.go +++ b/market/interfaces.go @@ -19,7 +19,7 @@ type PricingEngine interface { GetPrice(pricecfg ppconfig.PriceConfig) (ppservice.PriceResponse, error) } -// TODO: this could be improved: pubKey could be specified in config, +// TODO: this could be improved: pubKey could be specified in config. type marketStream interface { Init(pubKey string, pauseCh chan types.PauseSignal) (data.MarketStore, error) Subscribe(marketID string) error diff --git a/market/service.go b/market/service.go index 9c468e3..25c6979 100644 --- a/market/service.go +++ b/market/service.go @@ -153,7 +153,6 @@ func (m *Service) FindMarket() (*vega.Market, error) { continue } - //m.settlementAssetID = market.TradableInstrument.Instrument.GetFuture().SettlementAsset m.log = m.log.WithFields(log.Fields{"marketID": mkt.Id}) m.decimalPlaces = mkt.DecimalPlaces diff --git a/node/datanode.go b/node/datanode.go index 483dc92..ffb1907 100644 --- a/node/datanode.go +++ b/node/datanode.go @@ -83,7 +83,6 @@ func (n *DataNode) dialNode(ctx context.Context, host string) { n.mu.Lock() n.conn = conn n.mu.Unlock() - return } func (n *DataNode) Target() string { @@ -93,27 +92,26 @@ func (n *DataNode) Target() string { // === CoreService === // SubmitTransaction submits a signed v2 transaction. -func (n *DataNode) SubmitTransaction(req *vegaapipb.SubmitTransactionRequest) (response *vegaapipb.SubmitTransactionResponse, err error) { +func (n *DataNode) SubmitTransaction(req *vegaapipb.SubmitTransactionRequest) (*vegaapipb.SubmitTransactionResponse, error) { msg := "gRPC call failed: SubmitTransaction: %w" if n == nil { - err = fmt.Errorf(msg, e.ErrNil) - return + return nil, fmt.Errorf(msg, e.ErrNil) } if n.conn.GetState() != connectivity.Ready { - err = fmt.Errorf(msg, e.ErrConnectionNotReady) - return + return nil, fmt.Errorf(msg, e.ErrConnectionNotReady) } c := vegaapipb.NewCoreServiceClient(n.conn) ctx, cancel := context.WithTimeout(context.Background(), n.callTimeout) defer cancel() - response, err = c.SubmitTransaction(ctx, req) + response, err := c.SubmitTransaction(ctx, req) if err != nil { - err = fmt.Errorf(msg, e.ErrorDetail(err)) + return nil, fmt.Errorf(msg, e.ErrorDetail(err)) } - return + + return response, nil } // LastBlockData gets the latest blockchain data, height, hash and pow parameters. @@ -130,157 +128,153 @@ func (n *DataNode) LastBlockData() (*vegaapipb.LastBlockHeightResponse, error) { c := vegaapipb.NewCoreServiceClient(n.conn) ctx, cancel := context.WithTimeout(context.Background(), n.callTimeout) defer cancel() + var response *vegaapipb.LastBlockHeightResponse + response, err := c.LastBlockHeight(ctx, &vegaapipb.LastBlockHeightRequest{}) if err != nil { err = fmt.Errorf(msg, e.ErrorDetail(err)) } + return response, err } // ObserveEventBus opens a stream. -func (n *DataNode) ObserveEventBus(ctx context.Context) (client vegaapipb.CoreService_ObserveEventBusClient, err error) { +func (n *DataNode) ObserveEventBus(ctx context.Context) (vegaapipb.CoreService_ObserveEventBusClient, error) { msg := "gRPC call failed: ObserveEventBus: %w" if n == nil { - err = fmt.Errorf(msg, e.ErrNil) - return + return nil, fmt.Errorf(msg, e.ErrNil) } if n.conn == nil || n.conn.GetState() != connectivity.Ready { - err = fmt.Errorf(msg, e.ErrConnectionNotReady) - return + return nil, fmt.Errorf(msg, e.ErrConnectionNotReady) } c := vegaapipb.NewCoreServiceClient(n.conn) // no timeout on streams - client, err = c.ObserveEventBus(ctx) + client, err := c.ObserveEventBus(ctx) if err != nil { - err = fmt.Errorf(msg, e.ErrorDetail(err)) - return + return nil, fmt.Errorf(msg, e.ErrorDetail(err)) } - return + + return client, nil } // === TradingDataService === // PartyAccounts returns accounts for the given party. -func (n *DataNode) PartyAccounts(req *dataapipb.PartyAccountsRequest) (response *dataapipb.PartyAccountsResponse, err error) { +func (n *DataNode) PartyAccounts(req *dataapipb.PartyAccountsRequest) (*dataapipb.PartyAccountsResponse, error) { msg := "gRPC call failed (data-node): PartyAccounts: %w" if n == nil { - err = fmt.Errorf(msg, e.ErrNil) - return + return nil, fmt.Errorf(msg, e.ErrNil) } if n.conn.GetState() != connectivity.Ready { - err = fmt.Errorf(msg, e.ErrConnectionNotReady) - return + return nil, fmt.Errorf(msg, e.ErrConnectionNotReady) } c := dataapipb.NewTradingDataServiceClient(n.conn) ctx, cancel := context.WithTimeout(context.Background(), n.callTimeout) defer cancel() - response, err = c.PartyAccounts(ctx, req) + response, err := c.PartyAccounts(ctx, req) if err != nil { - err = fmt.Errorf(msg, e.ErrorDetail(err)) + return nil, fmt.Errorf(msg, e.ErrorDetail(err)) } - return + + return response, nil } // MarketDataByID returns market data for the specified market. -func (n *DataNode) MarketDataByID(req *dataapipb.MarketDataByIDRequest) (response *dataapipb.MarketDataByIDResponse, err error) { +func (n *DataNode) MarketDataByID(req *dataapipb.MarketDataByIDRequest) (*dataapipb.MarketDataByIDResponse, error) { msg := "gRPC call failed (data-node): MarketDataByID: %w" if n == nil { - err = fmt.Errorf(msg, e.ErrNil) - return + return nil, fmt.Errorf(msg, e.ErrNil) } if n.conn.GetState() != connectivity.Ready { - err = fmt.Errorf(msg, e.ErrConnectionNotReady) - return + return nil, fmt.Errorf(msg, e.ErrConnectionNotReady) } c := dataapipb.NewTradingDataServiceClient(n.conn) ctx, cancel := context.WithTimeout(context.Background(), n.callTimeout) defer cancel() - response, err = c.MarketDataByID(ctx, req) + response, err := c.MarketDataByID(ctx, req) if err != nil { - err = fmt.Errorf(msg, e.ErrorDetail(err)) + return nil, fmt.Errorf(msg, e.ErrorDetail(err)) } - return + + return response, nil } // Markets returns all markets. -func (n *DataNode) Markets(req *dataapipb.MarketsRequest) (response *dataapipb.MarketsResponse, err error) { +func (n *DataNode) Markets(req *dataapipb.MarketsRequest) (*dataapipb.MarketsResponse, error) { msg := "gRPC call failed (data-node): Markets: %w" if n == nil { - err = fmt.Errorf(msg, e.ErrNil) - return + return nil, fmt.Errorf(msg, e.ErrNil) } if n.conn.GetState() != connectivity.Ready { - err = fmt.Errorf(msg, e.ErrConnectionNotReady) - return + return nil, fmt.Errorf(msg, e.ErrConnectionNotReady) } c := dataapipb.NewTradingDataServiceClient(n.conn) ctx, cancel := context.WithTimeout(context.Background(), n.callTimeout) defer cancel() - response, err = c.Markets(ctx, req) + response, err := c.Markets(ctx, req) if err != nil { - err = fmt.Errorf(msg, e.ErrorDetail(err)) + return nil, fmt.Errorf(msg, e.ErrorDetail(err)) } - return + + return response, nil } // PositionsByParty returns positions for the given party. -func (n *DataNode) PositionsByParty(req *dataapipb.PositionsByPartyRequest) (response *dataapipb.PositionsByPartyResponse, err error) { +func (n *DataNode) PositionsByParty(req *dataapipb.PositionsByPartyRequest) (*dataapipb.PositionsByPartyResponse, error) { msg := "gRPC call failed (data-node): PositionsByParty: %w" if n == nil { - err = fmt.Errorf(msg, e.ErrNil) - return + return nil, fmt.Errorf(msg, e.ErrNil) } if n.conn.GetState() != connectivity.Ready { - err = fmt.Errorf(msg, e.ErrConnectionNotReady) - return + return nil, fmt.Errorf(msg, e.ErrConnectionNotReady) } c := dataapipb.NewTradingDataServiceClient(n.conn) ctx, cancel := context.WithTimeout(context.Background(), n.callTimeout) defer cancel() - response, err = c.PositionsByParty(ctx, req) + response, err := c.PositionsByParty(ctx, req) if err != nil { - err = fmt.Errorf(msg, e.ErrorDetail(err)) + return nil, fmt.Errorf(msg, e.ErrorDetail(err)) } - return + + return response, nil } // AssetByID returns the specified asset. -func (n *DataNode) AssetByID(req *dataapipb.AssetByIDRequest) (response *dataapipb.AssetByIDResponse, err error) { +func (n *DataNode) AssetByID(req *dataapipb.AssetByIDRequest) (*dataapipb.AssetByIDResponse, error) { msg := "gRPC call failed (data-node): AssetByID: %w" if n == nil { - err = fmt.Errorf(msg, e.ErrNil) - return + return nil, fmt.Errorf(msg, e.ErrNil) } if n.conn.GetState() != connectivity.Ready { - err = fmt.Errorf(msg, e.ErrConnectionNotReady) - return + return nil, fmt.Errorf(msg, e.ErrConnectionNotReady) } c := dataapipb.NewTradingDataServiceClient(n.conn) ctx, cancel := context.WithTimeout(context.Background(), n.callTimeout) defer cancel() - response, err = c.AssetByID(ctx, req) + response, err := c.AssetByID(ctx, req) if err != nil { - err = fmt.Errorf(msg, e.ErrorDetail(err)) + return nil, fmt.Errorf(msg, e.ErrorDetail(err)) } - return + + return response, nil } func (n *DataNode) WaitForStateChange(ctx context.Context, state connectivity.State) bool { diff --git a/pricing/pricing.go b/pricing/pricing.go index 2c2a591..d171537 100644 --- a/pricing/pricing.go +++ b/pricing/pricing.go @@ -32,7 +32,7 @@ func NewEngine(cfg config.PricingConfig) *Engine { } // GetPrice fetches a live/recent price from the price proxy. -func (e *Engine) GetPrice(pricecfg ppconfig.PriceConfig) (pi ppservice.PriceResponse, err error) { +func (e *Engine) GetPrice(pricecfg ppconfig.PriceConfig) (ppservice.PriceResponse, error) { v := url.Values{} if pricecfg.Source != "" { v.Set("source", pricecfg.Source) @@ -49,33 +49,28 @@ func (e *Engine) GetPrice(pricecfg ppconfig.PriceConfig) (pi ppservice.PriceResp req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, fullURL, nil) var resp *http.Response - resp, err = e.client.Do(req) + resp, err := e.client.Do(req) if err != nil { - err = fmt.Errorf("failed to perform HTTP request: %w", err) - return + return ppservice.PriceResponse{}, fmt.Errorf("failed to perform HTTP request: %w", err) } defer resp.Body.Close() content, err := ioutil.ReadAll(resp.Body) if err != nil { - err = fmt.Errorf("failed to read HTTP response body: %w", err) - return + return ppservice.PriceResponse{}, fmt.Errorf("failed to read HTTP response body: %w", err) } if resp.StatusCode != http.StatusOK { - err = fmt.Errorf("bad response: HTTP %d %s", resp.StatusCode, string(content)) - return + return ppservice.PriceResponse{}, fmt.Errorf("bad response: HTTP %d %s", resp.StatusCode, string(content)) } var response ppservice.PricesResponse if err = json.Unmarshal(content, &response); err != nil { - err = fmt.Errorf("failed to parse HTTP response as JSON: %w", err) - return + return ppservice.PriceResponse{}, fmt.Errorf("failed to parse HTTP response as JSON: %w", err) } if len(response.Prices) == 0 { - err = errors.New("zero-length price list from Price Proxy") - return + return ppservice.PriceResponse{}, errors.New("zero-length price list from Price Proxy") } return *response.Prices[0], nil diff --git a/service/service.go b/service/service.go index 20bf855..aeb5c04 100644 --- a/service/service.go +++ b/service/service.go @@ -68,15 +68,15 @@ type Service struct { } // NewService creates a new service instance (with optional mocks for test purposes). -func NewService(config config.Config) (s *Service, err error) { - s = &Service{ +func NewService(config config.Config) (*Service, error) { + s := &Service{ Router: httprouter.New(), config: config, bots: make(map[string]Bot), } - if err = setupLogger(config.Server); err != nil { + if err := setupLogger(config.Server); err != nil { return nil, fmt.Errorf("failed to setup logger: %w", err) } @@ -94,7 +94,7 @@ func NewService(config config.Config) (s *Service, err error) { s.addRoutes() s.server = s.getServer() - return s, err + return s, nil } func setupLogger(conf *config.ServerConfig) error { diff --git a/token/faucet_service.go b/token/faucet_service.go index f703f2c..f38d08a 100644 --- a/token/faucet_service.go +++ b/token/faucet_service.go @@ -39,6 +39,10 @@ func (f FaucetService) Mint(ctx context.Context, assetID string, amount *num.Uin reqBody := bytes.NewBuffer(postBody) req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, reqBody) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) diff --git a/types/store.go b/types/store.go index 1599f09..a79ce9e 100644 --- a/types/store.go +++ b/types/store.go @@ -55,7 +55,9 @@ func (c cache[T]) set(f ...func(*T)) { c.setCh <- f } -func newCache[T any]() (c cache[T]) { +func newCache[T any]() cache[T] { + var c cache[T] + c.getCh = make(chan chan T) c.setCh = make(chan []func(*T)) @@ -73,5 +75,5 @@ func newCache[T any]() (c cache[T]) { } }() - return + return c } diff --git a/types/types.go b/types/types.go index 0890955..c36f6ef 100644 --- a/types/types.go +++ b/types/types.go @@ -166,6 +166,7 @@ func SetBond(bond *num.Uint) func(*Balance) { } } +// nolint:nonamedreturns func fromPtr[T any](ptr *T) (t T) { if ptr == nil { return diff --git a/wallet/client.go b/wallet/client.go index ed49c23..a0689ed 100644 --- a/wallet/client.go +++ b/wallet/client.go @@ -242,7 +242,7 @@ type SignTxRequest struct { Propagate bool `json:"propagate"` } -// TODO: make a wallet service that would run commands instead of tx requests +// TODO: make a wallet service that would run commands instead of tx requests. func (c *Client) SignTx(ctx context.Context, request *walletpb.SubmitTransactionRequest) error { if c.pubKey != "" { request.PubKey = c.pubKey diff --git a/whale/provider.go b/whale/provider.go index 15e12a7..f81f4b8 100644 --- a/whale/provider.go +++ b/whale/provider.go @@ -81,20 +81,16 @@ func NewProvider( } go func() { - for { - select { - case req := <-p.ensureBalanceCh: - if err := p.topUpAsync(req.ctx, req.name, req.address, req.assetID, req.amount); err != nil { - log.Errorf("Whale: failed to ensure enough funds: %s", err) - } + for req := range p.ensureBalanceCh { + if err := p.topUpAsync(req.ctx, req.name, req.address, req.assetID, req.amount); err != nil { + log.Errorf("Whale: failed to ensure enough funds: %s", err) } } }() return p } -func (p *Provider) TopUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) (evt v1.BusEventType, err error) { - evt = v1.BusEventType_BUS_EVENT_TYPE_DEPOSIT // TODO: this is not always true? +func (p *Provider) TopUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) (v1.BusEventType, error) { p.ensureBalanceCh <- ensureBalanceRequest{ ctx: ctx, name: receiverName, @@ -102,10 +98,11 @@ func (p *Provider) TopUpAsync(ctx context.Context, receiverName, receiverAddress assetID: assetID, amount: amount, } - return + // TODO: this is not always true? + return v1.BusEventType_BUS_EVENT_TYPE_DEPOSIT, nil } -func (p *Provider) topUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) (err error) { +func (p *Provider) topUpAsync(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error { // TODO: remove deposit slack request, once deposited if existDeposit, ok := p.getPendingDeposit(assetID); ok { existDeposit.amount = amount.Add(amount, existDeposit.amount) @@ -117,11 +114,12 @@ func (p *Provider) topUpAsync(ctx context.Context, receiverName, receiverAddress existDeposit.timestamp = newTimestamp } p.setPendingDeposit(assetID, existDeposit) - return + return nil } - if err = p.deposit(ctx, "Whale", p.walletPubKey, assetID, amount); err == nil { - return + err := p.deposit(ctx, "Whale", p.walletPubKey, assetID, amount) + if err == nil { + return nil } p.log.WithFields( @@ -143,10 +141,10 @@ func (p *Provider) topUpAsync(ctx context.Context, receiverName, receiverAddress deposit.timestamp, err = p.slackDan(ctx, assetID, amount) if err != nil { p.log.Errorf("Failed to slack Dan: %s", err) - return + return err } p.setPendingDeposit(assetID, deposit) - return + return nil } func (p *Provider) deposit(ctx context.Context, receiverName, receiverAddress, assetID string, amount *num.Uint) error { @@ -163,7 +161,6 @@ func (p *Provider) deposit(ctx context.Context, receiverName, receiverAddress, a err = p.depositBuiltin(ctx, assetID, amount, builtin) } else { return fmt.Errorf("unsupported asset type") - } if err != nil { return fmt.Errorf("failed to deposit to address '%s', name '%s': %w", receiverAddress, receiverName, err) @@ -216,9 +213,8 @@ func (p *Provider) StakeAsync(ctx context.Context, receiverAddress, assetID stri } contractAddress := asset.Details.GetErc20().ContractAddress - added := new(num.Uint) - added, err = p.erc20.StakeToAddress(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, receiverAddress, amount) + added, err := p.erc20.StakeToAddress(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, receiverAddress, amount) if err != nil { return fmt.Errorf("failed to stake Vega token for '%s': %w", receiverAddress, err) } @@ -238,9 +234,8 @@ func (p *Provider) depositERC20(ctx context.Context, asset *vega.Asset, amount * } contractAddress := asset.Details.GetErc20().ContractAddress - added := new(num.Uint) - added, err = p.erc20.Deposit(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, amount) + added, err := p.erc20.Deposit(ctx, ownerKey.privateKey, ownerKey.address, contractAddress, amount) if err != nil { return fmt.Errorf("failed to add erc20 token: %w", err) }