diff --git a/account/interfaces.go b/account/interfaces.go deleted file mode 100644 index 9b49592..0000000 --- a/account/interfaces.go +++ /dev/null @@ -1,18 +0,0 @@ -package account - -import ( - "context" - "time" - - "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) - GetBalances(assetID string) (data.BalanceStore, error) - WaitForStakeLinking(pubKey string) 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 deleted file mode 100644 index ab478ce..0000000 --- a/account/service.go +++ /dev/null @@ -1,163 +0,0 @@ -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{ - "name": a.name, - "partyId": a.pubKey, - "balanceTotal": balanceTotal.String(), - }).Debugf("%s: Total account balance", from) - - 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) - - 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.WithFields(log.Fields{"name": a.name}).Debugf("%s: Waiting for top-up...", from) - - 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 -} - -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") - } - - store, err := a.getStore(assetID) - if err != nil { - return err - } - - // 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) - - if balanceTotal.GT(targetAmount) { - return nil - } - - a.log.WithFields( - log.Fields{ - "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.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) - } - - 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, error) { - var err error - - store, ok := a.stores[assetID] - if !ok { - store, err = a.accountStream.GetBalances(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 6ba29c2..ac7d840 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -5,28 +5,28 @@ import ( "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" + "code.vegaprotocol.io/shared/libs/account" + "code.vegaprotocol.io/shared/libs/node" + btypes "code.vegaprotocol.io/shared/libs/types" + "code.vegaprotocol.io/shared/libs/wallet" + "code.vegaprotocol.io/vega/logging" ) // New returns a new Bot instance. func New( + log *logging.Logger, botConf config.BotConfig, conf config.Config, pricing types.PricingEngine, - whale types.CoinProvider, + whale account.CoinProvider, ) (types.Bot, error) { switch botConf.Strategy { case config.BotStrategyNormal: - bot, err := newNormalBot(botConf, conf, pricing, whale) + bot, err := newNormalBot(log, botConf, conf, pricing, whale) if err != nil { return nil, fmt.Errorf("failed to create normal bot '%s': %w", botConf.Name, err) } @@ -37,10 +37,11 @@ func New( } func newNormalBot( + log *logging.Logger, botConf config.BotConfig, conf config.Config, pricing types.PricingEngine, - whale types.CoinProvider, + whale account.CoinProvider, ) (types.Bot, error) { dataNode := node.NewDataNode( conf.Locations, @@ -48,20 +49,23 @@ func newNormalBot( ) log.Debug("Attempting to connect to Vega gRPC node...") - dataNode.MustDialConnection(context.Background()) // blocking + ctx := context.Background() + dataNode.MustDialConnection(ctx) // blocking - botWallet := wallet.NewClient(conf.Wallet.URL) - accountStream := data.NewAccountStream(botConf.Name, dataNode) - accountService := account.NewAccountService(botConf.Name, botConf.SettlementAssetID, accountStream, whale) + conf.Wallet.Name = botConf.Name + conf.Wallet.Passphrase = "supersecret" + botWallet, err := wallet.NewWalletV2Service(log, conf.Wallet) + if err != nil { + return nil, fmt.Errorf("failed to create bot wallet: %w", err) + } - marketStream := data.NewMarketStream(botConf.Name, dataNode) - marketService := market.NewService(botConf.Name, marketStream, dataNode, botWallet, pricing, accountService, botConf, conf.VegaAssetID) + pubKey := botWallet.PublicKey() + pauseCh := make(chan btypes.PauseSignal) + accountStream := account.NewStream(log, botConf.Name, dataNode, pauseCh) + marketStream := market.NewStream(log, botConf.Name, pubKey, dataNode, pauseCh) + accountService := account.NewService(log, botConf.Name, pubKey, accountStream, whale) + marketService := market.NewService(log, dataNode, botWallet, pricing, accountService, marketStream, botConf, conf.VegaAssetID) + bot := normal.New(log, botConf, conf.VegaAssetID, accountService, marketService, pauseCh) - return normal.New( - botConf, - conf.VegaAssetID, - botWallet, - accountService, - marketService, - ), nil + return bot, nil } diff --git a/bot/mocks/bot_mock.go b/bot/mocks/bot_mock.go deleted file mode 100644 index 66a11f4..0000000 --- a/bot/mocks/bot_mock.go +++ /dev/null @@ -1,74 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: code.vegaprotocol.io/liqbot/bot (interfaces: Bot) - -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" -) - -// MockBot is a mock of Bot interface. -type MockBot struct { - ctrl *gomock.Controller - recorder *MockBotMockRecorder -} - -// MockBotMockRecorder is the mock recorder for MockBot. -type MockBotMockRecorder struct { - mock *MockBot -} - -// NewMockBot creates a new mock instance. -func NewMockBot(ctrl *gomock.Controller) *MockBot { - mock := &MockBot{ctrl: ctrl} - mock.recorder = &MockBotMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockBot) EXPECT() *MockBotMockRecorder { - return m.recorder -} - -// GetTraderDetails mocks base method. -func (m *MockBot) GetTraderDetails() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTraderDetails") - ret0, _ := ret[0].(string) - return ret0 -} - -// GetTraderDetails indicates an expected call of GetTraderDetails. -func (mr *MockBotMockRecorder) GetTraderDetails() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTraderDetails", reflect.TypeOf((*MockBot)(nil).GetTraderDetails)) -} - -// Start mocks base method. -func (m *MockBot) Start() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Start") - ret0, _ := ret[0].(error) - return ret0 -} - -// Start indicates an expected call of Start. -func (mr *MockBotMockRecorder) Start() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockBot)(nil).Start)) -} - -// Stop mocks base method. -func (m *MockBot) Stop() { - m.ctrl.T.Helper() - m.ctrl.Call(m, "Stop") -} - -// Stop indicates an expected call of Stop. -func (mr *MockBotMockRecorder) Stop() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockBot)(nil).Stop)) -} diff --git a/bot/mocks/pricingengine_mock.go b/bot/mocks/pricingengine_mock.go deleted file mode 100644 index df494ad..0000000 --- a/bot/mocks/pricingengine_mock.go +++ /dev/null @@ -1,51 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: code.vegaprotocol.io/liqbot/bot (interfaces: PricingEngine) - -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - - config "code.vegaprotocol.io/priceproxy/config" - service "code.vegaprotocol.io/priceproxy/service" - gomock "github.com/golang/mock/gomock" -) - -// MockPricingEngine is a mock of PricingEngine interface. -type MockPricingEngine struct { - ctrl *gomock.Controller - recorder *MockPricingEngineMockRecorder -} - -// MockPricingEngineMockRecorder is the mock recorder for MockPricingEngine. -type MockPricingEngineMockRecorder struct { - mock *MockPricingEngine -} - -// NewMockPricingEngine creates a new mock instance. -func NewMockPricingEngine(ctrl *gomock.Controller) *MockPricingEngine { - mock := &MockPricingEngine{ctrl: ctrl} - mock.recorder = &MockPricingEngineMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockPricingEngine) EXPECT() *MockPricingEngineMockRecorder { - return m.recorder -} - -// GetPrice mocks base method. -func (m *MockPricingEngine) GetPrice(arg0 config.PriceConfig) (service.PriceResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPrice", arg0) - ret0, _ := ret[0].(service.PriceResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPrice indicates an expected call of GetPrice. -func (mr *MockPricingEngineMockRecorder) GetPrice(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPrice", reflect.TypeOf((*MockPricingEngine)(nil).GetPrice), arg0) -} diff --git a/bot/normal/interfaces.go b/bot/normal/interfaces.go index 4fc14cb..b6d42d6 100644 --- a/bot/normal/interfaces.go +++ b/bot/normal/interfaces.go @@ -3,45 +3,25 @@ package normal import ( "context" - "code.vegaprotocol.io/liqbot/types" - "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/shared/libs/cache" + "code.vegaprotocol.io/shared/libs/num" "code.vegaprotocol.io/vega/protos/vega" - "code.vegaprotocol.io/vega/protos/vega/wallet/v1" ) -// 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 - ListPublicKeys(ctx context.Context) ([]string, error) - GenerateKeyPair(ctx context.Context, passphrase string, meta []types.Meta) (*types.Key, error) - SignTx(ctx context.Context, req *v1.SubmitTransactionRequest) 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 marketService interface { - Init(pubKey string, pauseCh chan types.PauseSignal) error - Start(marketID string) error - Market() types.MarketData + Market() cache.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) + EnsureCommitmentAmount(ctx context.Context) error GetExternalPrice() (*num.Uint, error) + GetShape() ([]*vega.LiquidityOrder, []*vega.LiquidityOrder, string) + CheckPosition() (uint64, vega.Side, bool) + SendLiquidityProvisionAmendment(ctx context.Context, commitment *num.Uint, buys, sells []*vega.LiquidityOrder) error + SeedOrders(ctx context.Context) 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 + Balance(ctx context.Context, assetID string) cache.Balance + EnsureBalance(ctx context.Context, assetID string, balanceFn func(cache.Balance) *num.Uint, targetAmount *num.Uint, dp, scale uint64, from string) error } diff --git a/bot/normal/mocks/datanode_mock.go b/bot/normal/mocks/datanode_mock.go deleted file mode 100644 index caefa45..0000000 --- a/bot/normal/mocks/datanode_mock.go +++ /dev/null @@ -1,203 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: code.vegaprotocol.io/liqbot/bot/normal (interfaces: DataNode) - -// Package mocks is a generated GoMock package. -package mocks - -import ( - url "net/url" - reflect "reflect" - time "time" - - 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" -) - -// MockDataNode is a mock of DataNode interface. -type MockDataNode struct { - ctrl *gomock.Controller - recorder *MockDataNodeMockRecorder -} - -// MockDataNodeMockRecorder is the mock recorder for MockDataNode. -type MockDataNodeMockRecorder struct { - mock *MockDataNode -} - -// NewMockDataNode creates a new mock instance. -func NewMockDataNode(ctrl *gomock.Controller) *MockDataNode { - mock := &MockDataNode{ctrl: ctrl} - mock.recorder = &MockDataNodeMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDataNode) EXPECT() *MockDataNodeMockRecorder { - return m.recorder -} - -// AssetByID mocks base method. -func (m *MockDataNode) AssetByID(arg0 *v1.AssetByIDRequest) (*v1.AssetByIDResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AssetByID", arg0) - ret0, _ := ret[0].(*v1.AssetByIDResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// AssetByID indicates an expected call of AssetByID. -func (mr *MockDataNodeMockRecorder) AssetByID(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssetByID", reflect.TypeOf((*MockDataNode)(nil).AssetByID), arg0) -} - -// GetAddress mocks base method. -func (m *MockDataNode) GetAddress() (url.URL, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAddress") - ret0, _ := ret[0].(url.URL) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAddress indicates an expected call of GetAddress. -func (mr *MockDataNodeMockRecorder) GetAddress() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAddress", reflect.TypeOf((*MockDataNode)(nil).GetAddress)) -} - -// GetVegaTime mocks base method. -func (m *MockDataNode) GetVegaTime() (time.Time, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVegaTime") - ret0, _ := ret[0].(time.Time) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetVegaTime indicates an expected call of GetVegaTime. -func (mr *MockDataNodeMockRecorder) GetVegaTime() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVegaTime", reflect.TypeOf((*MockDataNode)(nil).GetVegaTime)) -} - -// LastBlockHeight mocks base method. -func (m *MockDataNode) LastBlockHeight() (uint64, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LastBlockHeight") - ret0, _ := ret[0].(uint64) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// LastBlockHeight indicates an expected call of LastBlockHeight. -func (mr *MockDataNodeMockRecorder) LastBlockHeight() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LastBlockHeight", reflect.TypeOf((*MockDataNode)(nil).LastBlockHeight)) -} - -// MarketDataByID mocks base method. -func (m *MockDataNode) MarketDataByID(arg0 *v1.MarketDataByIDRequest) (*v1.MarketDataByIDResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MarketDataByID", arg0) - ret0, _ := ret[0].(*v1.MarketDataByIDResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// MarketDataByID indicates an expected call of MarketDataByID. -func (mr *MockDataNodeMockRecorder) MarketDataByID(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarketDataByID", reflect.TypeOf((*MockDataNode)(nil).MarketDataByID), arg0) -} - -// Markets mocks base method. -func (m *MockDataNode) Markets(arg0 *v1.MarketsRequest) (*v1.MarketsResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Markets", arg0) - ret0, _ := ret[0].(*v1.MarketsResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Markets indicates an expected call of Markets. -func (mr *MockDataNodeMockRecorder) Markets(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Markets", reflect.TypeOf((*MockDataNode)(nil).Markets), arg0) -} - -// ObserveEventBus mocks base method. -func (m *MockDataNode) ObserveEventBus() (v10.CoreService_ObserveEventBusClient, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ObserveEventBus") - ret0, _ := ret[0].(v10.CoreService_ObserveEventBusClient) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ObserveEventBus indicates an expected call of ObserveEventBus. -func (mr *MockDataNodeMockRecorder) ObserveEventBus() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ObserveEventBus", reflect.TypeOf((*MockDataNode)(nil).ObserveEventBus)) -} - -// PartyAccounts mocks base method. -func (m *MockDataNode) PartyAccounts(arg0 *v1.PartyAccountsRequest) (*v1.PartyAccountsResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PartyAccounts", arg0) - ret0, _ := ret[0].(*v1.PartyAccountsResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PartyAccounts indicates an expected call of PartyAccounts. -func (mr *MockDataNodeMockRecorder) PartyAccounts(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PartyAccounts", reflect.TypeOf((*MockDataNode)(nil).PartyAccounts), arg0) -} - -// PositionsByParty mocks base method. -func (m *MockDataNode) PositionsByParty(arg0 *v1.PositionsByPartyRequest) (*v1.PositionsByPartyResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PositionsByParty", arg0) - ret0, _ := ret[0].(*v1.PositionsByPartyResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PositionsByParty indicates an expected call of PositionsByParty. -func (mr *MockDataNodeMockRecorder) PositionsByParty(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PositionsByParty", reflect.TypeOf((*MockDataNode)(nil).PositionsByParty), arg0) -} - -// PositionsSubscribe mocks base method. -func (m *MockDataNode) PositionsSubscribe(arg0 *v1.PositionsSubscribeRequest) (v1.TradingDataService_PositionsSubscribeClient, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PositionsSubscribe", arg0) - ret0, _ := ret[0].(v1.TradingDataService_PositionsSubscribeClient) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PositionsSubscribe indicates an expected call of PositionsSubscribe. -func (mr *MockDataNodeMockRecorder) PositionsSubscribe(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PositionsSubscribe", reflect.TypeOf((*MockDataNode)(nil).PositionsSubscribe), arg0) -} - -// SubmitTransaction mocks base method. -func (m *MockDataNode) SubmitTransaction(arg0 *v10.SubmitTransactionRequest) (*v10.SubmitTransactionResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitTransaction", arg0) - ret0, _ := ret[0].(*v10.SubmitTransactionResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SubmitTransaction indicates an expected call of SubmitTransaction. -func (mr *MockDataNodeMockRecorder) SubmitTransaction(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitTransaction", reflect.TypeOf((*MockDataNode)(nil).SubmitTransaction), arg0) -} diff --git a/bot/normal/mocks/pricingengine_mock.go b/bot/normal/mocks/pricingengine_mock.go deleted file mode 100644 index 4a65579..0000000 --- a/bot/normal/mocks/pricingengine_mock.go +++ /dev/null @@ -1,51 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: code.vegaprotocol.io/liqbot/bot/normal (interfaces: PricingEngine) - -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - - config "code.vegaprotocol.io/priceproxy/config" - service "code.vegaprotocol.io/priceproxy/service" - gomock "github.com/golang/mock/gomock" -) - -// MockPricingEngine is a mock of PricingEngine interface. -type MockPricingEngine struct { - ctrl *gomock.Controller - recorder *MockPricingEngineMockRecorder -} - -// MockPricingEngineMockRecorder is the mock recorder for MockPricingEngine. -type MockPricingEngineMockRecorder struct { - mock *MockPricingEngine -} - -// NewMockPricingEngine creates a new mock instance. -func NewMockPricingEngine(ctrl *gomock.Controller) *MockPricingEngine { - mock := &MockPricingEngine{ctrl: ctrl} - mock.recorder = &MockPricingEngineMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockPricingEngine) EXPECT() *MockPricingEngineMockRecorder { - return m.recorder -} - -// GetPrice mocks base method. -func (m *MockPricingEngine) GetPrice(arg0 config.PriceConfig) (service.PriceResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPrice", arg0) - ret0, _ := ret[0].(service.PriceResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPrice indicates an expected call of GetPrice. -func (mr *MockPricingEngineMockRecorder) GetPrice(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPrice", reflect.TypeOf((*MockPricingEngine)(nil).GetPrice), arg0) -} diff --git a/bot/normal/normal.go b/bot/normal/normal.go index c6e68bd..f9a8bb5 100644 --- a/bot/normal/normal.go +++ b/bot/normal/normal.go @@ -4,30 +4,26 @@ import ( "context" "encoding/json" "fmt" - "strings" "sync" - log "github.com/sirupsen/logrus" - "code.vegaprotocol.io/liqbot/config" - "code.vegaprotocol.io/liqbot/types" - "code.vegaprotocol.io/liqbot/types/num" - "code.vegaprotocol.io/vega/wallet/wallets" + "code.vegaprotocol.io/shared/libs/types" + "code.vegaprotocol.io/vega/logging" ) // bot represents one Normal liquidity bot. type bot struct { - walletClient WalletClient marketService accountService config config.BotConfig - log *log.Entry + log *logging.Logger stopPosMgmt chan bool stopPriceSteer chan bool pausePosMgmt chan struct{} pausePriceSteer chan struct{} + pauseChannel chan types.PauseSignal walletPubKey string marketID string @@ -38,37 +34,27 @@ 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( + log *logging.Logger, botConf config.BotConfig, vegaAssetID string, - wc WalletClient, accountService accountService, marketService marketService, + pauseChannel chan types.PauseSignal, ) *bot { return &bot{ config: botConf, settlementAssetID: botConf.SettlementAssetID, vegaAssetID: vegaAssetID, - log: log.WithFields(log.Fields{ - "bot": botConf.Name, - }), - - stopPosMgmt: make(chan bool), - stopPriceSteer: make(chan bool), - pausePosMgmt: make(chan struct{}), - pausePriceSteer: make(chan struct{}), - accountService: accountService, - marketService: marketService, - walletClient: wc, + log: log, + stopPosMgmt: make(chan bool), + stopPriceSteer: make(chan bool), + pausePosMgmt: make(chan struct{}), + pausePriceSteer: make(chan struct{}), + accountService: accountService, + marketService: marketService, + pauseChannel: pauseChannel, } } @@ -76,20 +62,11 @@ func New( func (b *bot) Start() error { ctx := context.Background() - walletPubKey, err := b.setupWallet(ctx) - if err != nil { - return fmt.Errorf("failed to setup wallet: %w", err) - } - - b.walletPubKey = walletPubKey - - pauseCh := b.pauseChannel() - - b.accountService.Init(walletPubKey, pauseCh) - - if err = b.marketService.Init(walletPubKey, pauseCh); err != nil { - return fmt.Errorf("failed to init market service: %w", err) - } + go func() { + for p := range b.pauseChannel { + b.Pause(p) + } + }() market, err := b.SetupMarket(ctx) if err != nil { @@ -99,31 +76,15 @@ func (b *bot) Start() error { 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("Market info") + b.log.With( + logging.String("id", b.marketID), + logging.String("base/ticker", b.config.InstrumentBase), + logging.String("quote", b.config.InstrumentQuote), + logging.String("settlementAssetID", b.settlementAssetID), + ).Info("Market info") 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) @@ -137,14 +98,8 @@ func (b *bot) Start() error { return nil } -func (b *bot) pauseChannel() chan types.PauseSignal { - in := make(chan types.PauseSignal) - go func() { - for p := range in { - b.Pause(p) - } - }() - return in +func (b *bot) PauseChannel() chan types.PauseSignal { + return b.pauseChannel } // Pause pauses the liquidity bot goroutine(s). @@ -153,10 +108,10 @@ func (b *bot) Pause(p types.PauseSignal) { defer b.mu.Unlock() if p.Pause && !b.botPaused { - b.log.WithFields(log.Fields{"From": p.From}).Info("Pausing bot") + b.log.With(logging.String("From", p.From)).Info("Pausing bot") b.botPaused = true } else if !p.Pause && b.botPaused { - b.log.WithFields(log.Fields{"From": p.From}).Info("Resuming bot") + b.log.With(logging.String("From", p.From)).Info("Resuming bot") b.botPaused = false } else { return @@ -196,43 +151,3 @@ func (b *bot) GetTraderDetails() string { }, "", " ") return string(jsn) } - -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) - } - b.log.Info("Created and logged into wallet") - } else { - return "", fmt.Errorf("failed to log into wallet: %w", err) - } - } - - b.log.Info("Logged into wallet") - - publicKeys, err := b.walletClient.ListPublicKeys(ctx) - if err != nil { - 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) - } - walletPubKey = key.Pub - b.log.WithFields(log.Fields{"pubKey": walletPubKey}).Debug("Created keypair") - } else { - walletPubKey = publicKeys[0] - b.log.WithFields(log.Fields{"pubKey": walletPubKey}).Debug("Using existing keypair") - } - - b.log = b.log.WithFields(log.Fields{"pubkey": walletPubKey}) - - return walletPubKey, nil -} diff --git a/bot/normal/position_management.go b/bot/normal/position_management.go index 6e04904..0e05f5b 100644 --- a/bot/normal/position_management.go +++ b/bot/normal/position_management.go @@ -2,29 +2,17 @@ package normal import ( "context" - "errors" "fmt" - "math" - "math/rand" "time" - log "github.com/sirupsen/logrus" - - "code.vegaprotocol.io/liqbot/types/num" - "code.vegaprotocol.io/liqbot/util" + "code.vegaprotocol.io/shared/libs/cache" + "code.vegaprotocol.io/shared/libs/num" + "code.vegaprotocol.io/vega/logging" "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. func (b *bot) runPositionManagement(ctx context.Context) { - defer b.log.Warning("PositionManagement: Stopped") - - if err := b.provideLiquidity(ctx); err != nil { - b.log.WithFields(log.Fields{"error": err.Error()}).Error("PositionManagement: Failed to initialize") - return - } + defer b.log.Warn("PositionManagement: Stopped") sleepTime := time.Duration(b.config.StrategyDetails.PosManagementSleepMilliseconds) * time.Millisecond previousOpenVolume := int64(0) @@ -32,15 +20,13 @@ func (b *bot) runPositionManagement(ctx context.Context) { for { select { case <-b.pausePosMgmt: - b.log.Warning("PositionManagement: Paused") + b.log.Warn("PositionManagement: Paused") <-b.pausePosMgmt b.log.Info("PositionManagement: Resumed") case <-b.stopPosMgmt: return case <-ctx.Done(): - b.log.WithFields(log.Fields{ - "error": ctx.Err(), - }).Warning("PositionManagement: Stopped by context") + b.log.Warn("PositionManagement: Stopped by context") return default: err := doze(sleepTime, b.stopPosMgmt) @@ -48,139 +34,67 @@ func (b *bot) runPositionManagement(ctx context.Context) { return } - // 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("PositionManagement: Failed to update commitment amount") - } + if err := b.EnsureCommitmentAmount(ctx); err != nil { + b.log.Warn("PositionManagement: Failed to update commitment amount", logging.Error(err)) + } - if err = b.placeAuctionOrders(ctx); err != nil { - b.log.WithFields(log.Fields{"error": err.Error()}).Warning("PositionManagement: Failed to place auction orders") + if !b.CanPlaceOrders() { + if err := b.SeedOrders(ctx); err != nil { + b.log.Warn("PositionManagement: Failed to seed orders", logging.Error(err)) + return } continue } previousOpenVolume, err = b.manageDirection(ctx, previousOpenVolume) if err != nil { - b.log.WithFields(log.Fields{"error": err.Error()}).Warning("PositionManagement: Failed to change LP direction") + b.log.Warn("PositionManagement: Failed to change LP direction", logging.Error(err)) } if err = b.managePosition(ctx); err != nil { - b.log.WithFields(log.Fields{"error": err.Error()}).Warning("PositionManagement: Failed to manage position") + b.log.Warn("PositionManagement: Failed to manage position", logging.Error(err)) } } } } -func (b *bot) ensureCommitmentAmount(ctx context.Context) error { - requiredCommitment, err := b.getRequiredCommitment() - if err != nil { - return fmt.Errorf("failed to get new commitment amount: %w", err) - } - - if requiredCommitment.IsZero() { - return nil - } - - b.log.WithFields( - log.Fields{ - "newCommitment": requiredCommitment.String(), - }, - ).Debug("PositionManagement: Supplied stake is less than target stake, increasing commitment amount...") - - if err = b.EnsureBalance(ctx, b.settlementAssetID, requiredCommitment, "PositionManagement"); err != nil { - return fmt.Errorf("failed to ensure balance: %w", err) - } - - buys, sells, _ := b.getShape() - - b.log.WithFields( - log.Fields{ - "newCommitment": requiredCommitment.String(), - }, - ).Debug("PositionManagement: Sending new commitment amount...") - - 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.Market().SuppliedStake().Clone() - targetStake := b.Market().TargetStake().Clone() - - 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) - } - } - - 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() { - return num.Zero(), nil - } - - return num.Zero().Add(targetStake, dx.Uint()), nil -} - func (b *bot) manageDirection(ctx context.Context, previousOpenVolume int64) (int64, error) { openVolume := b.Market().OpenVolume() - buyShape, sellShape, shape := b.getShape() + buyShape, sellShape, shape := b.GetShape() + from := "PositionManagement" - b.log.WithFields(log.Fields{ - "openVolume": b.Market().OpenVolume(), - "previousOpenVolume": previousOpenVolume, - "shape": shape, - }).Debug("PositionManagement: Checking for direction change") + b.log.With( + logging.Int64("openVolume", b.Market().OpenVolume()), + logging.Int64("previousOpenVolume", previousOpenVolume), + logging.String("shape", shape), + ).Debug(from + ": Checking for direction change") - // If we flipped then send the new LP order - if !b.shouldAmend(openVolume, previousOpenVolume) { - return openVolume, nil + // If we flipped then amend the Liquidity Provision + if !((openVolume > 0 && previousOpenVolume <= 0) || (openVolume < 0 && previousOpenVolume >= 0)) { + return previousOpenVolume, nil } - b.log.WithFields(log.Fields{"shape": shape}).Debug("PositionManagement: Flipping LP direction") - - commitment, err := b.getRequiredCommitment() - if err != nil { - return openVolume, fmt.Errorf("failed to get required commitment amount: %w", err) - } + b.log.With(logging.String("shape", shape)).Debug(from + ": Flipping LP direction") - if err = b.EnsureBalance(ctx, b.settlementAssetID, commitment, "PositionManagement"); 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, nil, buyShape, sellShape); err != nil { return openVolume, fmt.Errorf("failed to send liquidity provision amendment: %w", err) } return openVolume, nil } -func (b *bot) shouldAmend(openVolume, previousOpenVolume int64) bool { - 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.Market().MarkPrice().String(), - "balanceGeneral": b.Balance().General().String(), - "balanceMargin": b.Balance().Margin().String(), - "openVolume": b.Market().OpenVolume(), - "size": size, - "side": side.String(), - "shouldPlace": shouldPlace, - }).Debug("PositionManagement: Checking for position management") + size, side, shouldPlace := b.CheckPosition() + b.log.With( + logging.String("currentPrice", b.Market().MarkPrice().String()), + logging.String("balance.General", cache.General(b.Balance(ctx, b.settlementAssetID)).String()), + logging.String("balance.Margin", cache.Margin(b.Balance(ctx, b.settlementAssetID)).String()), + logging.String("balance.Bond", cache.Bond(b.Balance(ctx, b.settlementAssetID)).String()), + logging.Int64("openVolume", b.Market().OpenVolume()), + logging.Uint64("size", size), + logging.String("side", side.String()), + logging.Bool("shouldPlace", shouldPlace), + ).Debug("PositionManagement: Checking for position management") if !shouldPlace { return nil @@ -191,8 +105,8 @@ func (b *bot) managePosition(ctx context.Context) error { Size: size, Price: num.Zero().String(), Side: side, - TimeInForce: vega.Order_TIME_IN_FORCE_GTT, - Type: vega.Order_TYPE_LIMIT, + TimeInForce: vega.Order_TIME_IN_FORCE_IOC, + Type: vega.Order_TYPE_MARKET, Reference: "PosManagement", } @@ -202,295 +116,3 @@ func (b *bot) managePosition(ctx context.Context) error { return nil } - -func (b *bot) sendLiquidityProvision(ctx context.Context, commitment *num.Uint, buys, sells []*vega.LiquidityOrder) error { - submitTxReq := &walletpb.SubmitTransactionRequest{ - PubKey: b.walletPubKey, - Command: &walletpb.SubmitTransactionRequest_LiquidityProvisionSubmission{ - LiquidityProvisionSubmission: &commandspb.LiquidityProvisionSubmission{ - MarketId: b.marketID, - CommitmentAmount: commitment.String(), - Fee: b.config.StrategyDetails.Fee, - Sells: sells, - Buys: buys, - }, - }, - } - - b.log.WithFields(log.Fields{ - "commitment": commitment.String(), - "balanceTotal": b.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.String(), - "balanceTotal": b.Balance().Total().String(), - }).Debug("PositionManagement: Submitted LiquidityProvisionSubmission") - return nil -} - -// call this if the position flips. -func (b *bot) sendLiquidityProvisionAmendment(ctx context.Context, commitment *num.Uint, buys, sells []*vega.LiquidityOrder) error { - if commitment.IsZero() { - return b.sendLiquidityProvisionCancellation(ctx) - } - - submitTxReq := &walletpb.SubmitTransactionRequest{ - PubKey: b.walletPubKey, - Command: &walletpb.SubmitTransactionRequest_LiquidityProvisionAmendment{ - LiquidityProvisionAmendment: &commandspb.LiquidityProvisionAmendment{ - MarketId: b.marketID, - CommitmentAmount: commitment.String(), - Fee: b.config.StrategyDetails.Fee, - Sells: sells, - Buys: buys, - }, - }, - } - - if err := b.walletClient.SignTx(ctx, submitTxReq); err != nil { - return fmt.Errorf("failed to submit LiquidityProvisionAmendment: %w", err) - } - - b.log.WithFields(log.Fields{ - "commitment": commitment.String(), - }).Debug("PositionManagement: Submitted LiquidityProvisionAmendment") - return nil -} - -func (b *bot) sendLiquidityProvisionCancellation(ctx context.Context) error { - submitTxReq := &walletpb.SubmitTransactionRequest{ - PubKey: b.walletPubKey, - Command: &walletpb.SubmitTransactionRequest_LiquidityProvisionCancellation{ - LiquidityProvisionCancellation: &commandspb.LiquidityProvisionCancellation{ - MarketId: b.marketID, - }, - }, - } - - if err := b.walletClient.SignTx(ctx, submitTxReq); err != nil { - return fmt.Errorf("failed to submit LiquidityProvisionCancellation: %w", err) - } - - b.log.WithFields(log.Fields{ - "commitment": "0", - "balanceTotal": b.Balance().Total().String(), - }).Debug("PositionManagement: Submitted LiquidityProvisionAmendment") - - return nil -} - -func (b *bot) checkPosition() (uint64, vega.Side, bool) { - size := uint64(0) - side := vega.Side_SIDE_UNSPECIFIED - shouldPlace := true - 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() - side = vega.Side_SIDE_SELL - } else if openVolume < 0 && num.NewUint(uint64(-openVolume)).GT(b.config.StrategyDetails.MaxShort.Get()) { - size = mulFrac(num.NewUint(uint64(-openVolume)), b.config.StrategyDetails.PosManagementFraction, 15).Uint64() - side = vega.Side_SIDE_BUY - } else { - shouldPlace = false - } - - return size, side, shouldPlace -} - -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 - // been restarted. - if err := b.checkInitialMargin(buyShape, sellShape); err != nil { - return fmt.Errorf("failed initial margin check: %w", err) - } - - 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, b.settlementAssetID, commitment, "PositionManagement"); 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 { - return fmt.Errorf("failed to send liquidity provision order: %w", err) - } - - return nil -} - -func (b *bot) getShape() ([]*vega.LiquidityOrder, []*vega.LiquidityOrder, string) { - // We always cache off with longening shapes - shape := "longening" - buyShape := b.config.StrategyDetails.LongeningShape.Buys.ToVegaLiquidityOrders() - sellShape := b.config.StrategyDetails.LongeningShape.Sells.ToVegaLiquidityOrders() - - if b.Market().OpenVolume() > 0 { - shape = "shortening" - buyShape = b.config.StrategyDetails.ShorteningShape.Buys.ToVegaLiquidityOrders() - sellShape = b.config.StrategyDetails.ShorteningShape.Sells.ToVegaLiquidityOrders() - } - - return buyShape, sellShape, shape -} - -func (b *bot) checkInitialMargin(buyShape, sellShape []*vega.LiquidityOrder) error { - // Turn the shapes into a set of orders scaled by commitment - obligation := b.Balance().Total() - buyOrders := b.calculateOrderSizes(obligation, buyShape) - sellOrders := b.calculateOrderSizes(obligation, sellShape) - - buyRisk := 0.01 - sellRisk := 0.01 - - buyCost := b.calculateMarginCost(buyRisk, buyOrders) - sellCost := b.calculateMarginCost(sellRisk, sellOrders) - - shapeMarginCost := num.Max(buyCost, sellCost) - avail := mulFrac(b.Balance().General(), b.config.StrategyDetails.OrdersFraction, 15) - - if !avail.LT(shapeMarginCost) { - return nil - } - - missingPercent := "Inf" - - if !avail.IsZero() { - x := num.UintChain(shapeMarginCost).Sub(avail).Mul(num.NewUint(100)).Div(avail).Get() - missingPercent = fmt.Sprintf("%v%%", x) - } - - b.log.WithFields(log.Fields{ - "available": avail.String(), - "cost": shapeMarginCost.String(), - "missing": num.Zero().Sub(avail, shapeMarginCost).String(), - "missingPercent": missingPercent, - }).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") -} - -// Divide the auction amount into 10 orders and place them randomly -// 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.Market().MarkPrice().IsZero() { - b.log.Debug("PositionManagement: No current price to place auction orders") - return nil - } - - b.log.WithFields(log.Fields{"currentPrice": b.Market().MarkPrice().String()}).Debug("PositionManagement: Placing auction orders") - - // Place the random orders split into - totalVolume := num.Zero() - - rand.Seed(time.Now().UnixNano()) - - for totalVolume.LT(b.config.StrategyDetails.AuctionVolume.Get()) { - time.Sleep(time.Second * 2) - - 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.Market().MarkPrice(), num.NewUint(uint64(rand.Int63n(100)-50))) - side := vega.Side_SIDE_BUY - // #nosec G404 - if rand.Intn(2) == 0 { - side = vega.Side_SIDE_SELL - } - - 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, 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) - } - - totalVolume = num.Zero().Add(totalVolume, size) - } - - b.log.WithFields(log.Fields{"totalVolume": totalVolume.String()}).Debug("PositionManagement: Placed auction orders") - - return nil -} - -// calculateOrderSizes calculates the size of the orders using the total commitment, price, distance from mid and chance -// of trading liquidity.supplied.updateSizes(obligation, currentPrice, liquidityOrders, true, minPrice, maxPrice). -func (b *bot) calculateOrderSizes(obligation *num.Uint, liquidityOrders []*vega.LiquidityOrder) []*vega.Order { - orders := make([]*vega.Order, 0, len(liquidityOrders)) - // Work out the total proportion for the shape - totalProportion := num.Zero() - for _, order := range liquidityOrders { - totalProportion.Add(totalProportion, num.NewUint(uint64(order.Proportion))) - } - - // Now size up the orders and create the real order objects - for _, lo := range liquidityOrders { - size := num.UintChain(obligation.Clone()). - Mul(num.NewUint(uint64(lo.Proportion))). - Mul(num.NewUint(10)). - Div(totalProportion).Div(b.Market().MarkPrice()).Get() - peggedOrder := vega.PeggedOrder{ - Reference: lo.Reference, - Offset: lo.Offset, - } - - order := vega.Order{ - Side: vega.Side_SIDE_BUY, - Remaining: size.Uint64(), - Size: size.Uint64(), - TimeInForce: vega.Order_TIME_IN_FORCE_GTC, - Type: vega.Order_TYPE_LIMIT, - PeggedOrder: &peggedOrder, - } - orders = append(orders, &order) - } - - return orders -} - -// calculateMarginCost estimates the margin cost of the set of orders. -func (b *bot) calculateMarginCost(risk float64, orders []*vega.Order) *num.Uint { - margins := make([]*num.Uint, len(orders)) - - for i, order := range orders { - if order.Side == vega.Side_SIDE_BUY { - margins[i] = num.NewUint(1 + order.Size) - } else { - margins[i] = num.NewUint(order.Size) - } - } - - totalMargin := num.UintChain(num.Zero()).Add(margins...).Mul(b.Market().MarkPrice()).Get() - return mulFrac(totalMargin, risk, 15) -} - -func mulFrac(n *num.Uint, x float64, precision float64) *num.Uint { - val := num.NewUint(uint64(x * math.Pow(10, precision))) - val.Mul(val, n) - val.Div(val, num.NewUint(uint64(math.Pow(10, precision)))) - return val -} diff --git a/bot/normal/price_steering.go b/bot/normal/price_steering.go index 89d0896..49a4c30 100644 --- a/bot/normal/price_steering.go +++ b/bot/normal/price_steering.go @@ -6,41 +6,28 @@ import ( "math" "time" - log "github.com/sirupsen/logrus" - "code.vegaprotocol.io/liqbot/config" - "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/shared/libs/cache" + "code.vegaprotocol.io/shared/libs/num" + "code.vegaprotocol.io/vega/logging" "code.vegaprotocol.io/vega/protos/vega" ) func (b *bot) runPriceSteering(ctx context.Context) { - defer b.log.Warning("PriceSteering: Stopped") - - 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 { - b.log.WithFields(log.Fields{"error": err.Error()}).Error("PriceSteering: Failed to seed orders") - return - } - } + defer b.log.Warn("PriceSteering: Stopped") sleepTime := 1000.0 / b.config.StrategyDetails.MarketPriceSteeringRatePerSecond for { select { case <-b.pausePriceSteer: - b.log.Warning("PriceSteering: Paused") + b.log.Warn("PriceSteering: Paused") <-b.pausePriceSteer b.log.Info("Price steering resumed") case <-b.stopPriceSteer: return case <-ctx.Done(): - b.log.WithFields(log.Fields{ - "error": ctx.Err(), - }).Warning("PriceSteering: Stopped by context") + b.log.Warn("PriceSteering: Stopped by context") return default: if err := doze(time.Duration(sleepTime)*time.Millisecond, b.stopPriceSteer); err != nil { @@ -49,10 +36,9 @@ func (b *bot) runPriceSteering(ctx context.Context) { err := b.steerPrice(ctx) if err != nil { - b.log.WithFields(log.Fields{ - "error": err.Error(), - "sleepTime": sleepTime, - }).Warning("PriceSteering: Error during price steering") + b.log.With( + logging.Float64("sleepTime", sleepTime)). + Warn("PriceSteering: Error during price steering", logging.Error(err)) } sleepTime = b.moveSteerSleepTime(sleepTime, err != nil) @@ -76,14 +62,14 @@ func (b *bot) steerPrice(ctx context.Context) error { side = vega.Side_SIDE_SELL } - b.log.WithFields(log.Fields{ - "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("PriceSteering: Steering info") + b.log.With( + logging.String("currentPrice", staticMidPrice.String()), + logging.String("externalPrice", externalPrice.String()), + logging.String("diff", currentDiff.String()), + logging.String("currentDiffFraction", currentDiffFraction.String()), + logging.String("minPriceSteerFraction", minPriceSteerFraction.String()), + logging.String("shouldMove", map[vega.Side]string{vega.Side_SIDE_BUY: "UP", vega.Side_SIDE_SELL: "DN"}[side]), + ).Debug("PriceSteering: Steering info") if !currentDiffFraction.GreaterThan(minPriceSteerFraction) { b.log.Debug("PriceSteering: Current difference is not higher than minimum price steering fraction") @@ -93,7 +79,7 @@ func (b *bot) steerPrice(ctx context.Context) error { // 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("PriceSteering: Unable to get realistic order details for price steering") + b.log.Fatal("PriceSteering: Unable to get realistic order details for price steering", logging.Error(err)) } order := &vega.Order{ @@ -104,11 +90,18 @@ func (b *bot) steerPrice(ctx context.Context) error { TimeInForce: vega.Order_TIME_IN_FORCE_GTT, Type: vega.Order_TYPE_LIMIT, Reference: "PriceSteeringOrder", + // ExpiresAt: time.Now().Add(10 * time.Minute).Unix(), TODO + } + + expectedBalance := price.Mul(price, size) + + if err := b.EnsureBalance(ctx, b.config.SettlementAssetID, cache.General, expectedBalance, b.decimalPlaces, b.config.StrategyDetails.TopUpScale, "PriceSteering"); err != nil { + return fmt.Errorf("failed to ensure balance: %w", err) } if err = b.SubmitOrder( ctx, order, "PriceSteering", - int64(b.config.StrategyDetails.LimitOrderDistributionParams.GttLength)); err != nil { + int64(b.config.StrategyDetails.LimitOrderDistributionParams.GttLengthSeconds)); err != nil { return fmt.Errorf("failed to submit order: %w", err) } @@ -132,7 +125,7 @@ func (b *bot) getRealisticOrderDetails(externalPrice *num.Uint) (*num.Uint, *num return nil, nil, fmt.Errorf("method for generating price distributions not recognised") } // TODO: size? - size := mulFrac(num.NewUint(1), b.config.StrategyDetails.PriceSteerOrderScale, 15) + size := num.MulFrac(num.NewUint(1), b.config.StrategyDetails.PriceSteerOrderScale, 15) return price, size, nil } diff --git a/cmd/liqbot/main.go b/cmd/liqbot/main.go index b389b1a..99d10f2 100644 --- a/cmd/liqbot/main.go +++ b/cmd/liqbot/main.go @@ -2,7 +2,6 @@ package main import ( "flag" - "fmt" "math/rand" "os" "os/signal" @@ -11,10 +10,10 @@ import ( "time" "github.com/jinzhu/configor" - log "github.com/sirupsen/logrus" "code.vegaprotocol.io/liqbot/config" "code.vegaprotocol.io/liqbot/service" + "code.vegaprotocol.io/vega/logging" ) var ( @@ -32,8 +31,17 @@ func main() { flag.BoolVar(&configVersion, "version", false, "Show version") flag.Parse() + log := logging.NewDevLogger() + + if configName == "" { + log.Fatal("config was not provided") + } + if configVersion { - fmt.Printf("version %v (%v)\n", Version, VersionHash) + log.With( + logging.String("version", Version), + logging.String("version_hash", VersionHash), + ).Info("liqbot version") return } @@ -42,42 +50,29 @@ func main() { var cfg config.Config // https://github.com/jinzhu/configor/issues/40 if err := configor.Load(&cfg, configName); err != nil && !strings.Contains(err.Error(), "should be struct") { - log.WithFields(log.Fields{ - "error": err.Error(), - }).Fatal("Failed to read config") + log.Fatal("Failed to read config", logging.Error(err)) } if err := cfg.CheckConfig(); err != nil { - log.WithFields(log.Fields{ - "error": err.Error(), - }).Fatal("Config checks failed") + log.Fatal("Config checks failed", logging.Error(err)) } - if err := cfg.ConfigureLogging(); err != nil { - log.WithFields(log.Fields{ - "error": err.Error(), - }).Fatal("Failed to load config") - } + log = cfg.ConfigureLogging() - log.WithFields(log.Fields{ - "version": Version, - "hash": VersionHash, - }).Info("Version") + log.With( + logging.String("version", Version), + logging.String("hash", VersionHash), + ).Info("Version") - s, err := service.NewService(cfg) + s, err := service.NewService(log, cfg) if err != nil { - log.WithFields(log.Fields{ - "error": err.Error(), - }).Fatal("Failed to create service") + log.Fatal("Failed to create service", logging.Error(err)) } go func() { err := s.Start() if err != nil && err.Error() != "http: Server closed" { - log.WithFields(log.Fields{ - "listen": cfg.Server.Listen, - "error": err.Error(), - }).Fatal("Could not listen") + log.With(logging.String("listen", cfg.Server.Listen)).Fatal("Could not listen", logging.Error(err)) } }() c := make(chan os.Signal, 2) diff --git a/config/config.go b/config/config.go index cdc502e..8da557e 100644 --- a/config/config.go +++ b/config/config.go @@ -5,24 +5,27 @@ package config import ( "fmt" "net/url" - "time" - log "github.com/sirupsen/logrus" + "go.uber.org/zap" - "code.vegaprotocol.io/liqbot/errors" + tconfig "code.vegaprotocol.io/shared/libs/erc20/config" + "code.vegaprotocol.io/shared/libs/errors" + "code.vegaprotocol.io/shared/libs/wallet" + wconfig "code.vegaprotocol.io/shared/libs/whale/config" + "code.vegaprotocol.io/vega/logging" ) // Config describes the top level config file format. type Config struct { Server *ServerConfig `yaml:"server"` - 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"` + CallTimeoutMills int `yaml:"callTimeoutMills"` + VegaAssetID string `yaml:"vegaAssetID"` + Pricing *PricingConfig `yaml:"pricing"` + Wallet *wallet.Config `yaml:"wallet"` + Whale *wconfig.WhaleConfig `yaml:"whale"` + Token *tconfig.TokenConfig `yaml:"token"` + Locations []string `yaml:"locations"` Bots []BotConfig `yaml:"bots"` } @@ -61,57 +64,47 @@ func (cfg *Config) CheckConfig() error { if err := bot.StrategyDetails.validateStrategyConfig(); err != nil { return fmt.Errorf("failed to validate strategy config for bot '%s': %s", bot.Name, err) } + + if bot.SettlementAssetID == "" { + return fmt.Errorf("missing settlement asset ID for bot '%s'", bot.Name) + } } return nil } // ConfigureLogging configures logging. -func (cfg *Config) ConfigureLogging() error { - if cfg.Server.Env != "prod" { - // https://github.com/sirupsen/logrus#logging-method-name - // This slows down logging (by a factor of 2). - log.SetReportCaller(true) +func (cfg *Config) ConfigureLogging() *logging.Logger { + logCfg := logging.NewDefaultConfig() + + if cfg.Server.LogEncoding != "" { + logCfg.Custom.Zap.Encoding = cfg.Server.LogEncoding + } + + if cfg.Server.Env != "" { + logCfg.Environment = cfg.Server.Env } - switch cfg.Server.LogFormat { - case "json": - log.SetFormatter(&log.JSONFormatter{ - TimestampFormat: time.RFC3339Nano, - }) - case "textcolour": - log.SetFormatter(&log.TextFormatter{ - ForceColors: true, - FullTimestamp: true, - TimestampFormat: time.RFC3339Nano, - }) - case "textnocolour": - log.SetFormatter(&log.TextFormatter{ - DisableColors: true, - FullTimestamp: true, - TimestampFormat: time.RFC3339Nano, - }) - default: - log.SetFormatter(&log.TextFormatter{ - FullTimestamp: true, - TimestampFormat: time.RFC3339Nano, - }) // with colour if TTY, without otherwise + log := logging.NewLoggerFromConfig(logCfg) + if logCfg.Environment != "prod" { + log.Logger = log.Logger.WithOptions(zap.AddCaller(), zap.AddCallerSkip(1)) } - if loglevel, err := log.ParseLevel(cfg.Server.LogLevel); err == nil { + if loglevel, err := logging.ParseLevel(cfg.Server.LogLevel); err == nil { log.SetLevel(loglevel) } else { - log.SetLevel(log.WarnLevel) + log.SetLevel(logging.WarnLevel) } - return nil + + return log } // ServerConfig describes the settings for running the liquidity bot. type ServerConfig struct { - Env string - Listen string - LogFormat string - LogLevel string + Env string + Listen string + LogEncoding string + LogLevel string } // PricingConfig describes the settings for contacting the price proxy. @@ -119,23 +112,6 @@ 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"` - 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 // Vega node. type BotConfig struct { @@ -149,9 +125,6 @@ type BotConfig struct { // InstrumentQuote is the quote asset of the instrument. InstrumentQuote string `yaml:"instrumentQuote"` - // 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"` @@ -161,15 +134,3 @@ type BotConfig struct { // StrategyDetails contains the parameters needed by the strategy algorithm. StrategyDetails Strategy `yaml:"strategyDetails"` } - -// WalletConfig describes the settings for running an internal wallet server. -type WalletConfig struct { - URL string `yaml:"url"` -} - -type TokenConfig struct { - 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 1ba2da8..0e0dd49 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -3,61 +3,101 @@ server: env: prod # dev, prod listen: ":7800" - logformat: text # json, text + logencoding: json # console loglevel: debug # debug, info, warning, error +# timeout for sending requests to datanode callTimeoutMills: 10000 +# Vega Token asset vegaAssetID: b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b +# pricing engine URL pricing: address: scheme: https host: prices.ops.vega.xyz path: /prices + +# wallet used by the bots wallet: - url: http://127.0.0.1:1790 + # address of the node. Alternatively, the `networkFileURL` can be used to specify the node addresses + nodeURL: localhost:3027 + # path on local system where the wallets (and network config files) will be stored + storePath: /tmp/wallet/liqbot + # name of the wallet + name: liqbot + # password for the wallet + passphrase: liqbot-secret + +# erc20 token service token: - ethereumAPIAddress: ws://127.0.0.1:8545 + # address of the Ethereum API + ethereumAPIAddress: ws://localhost:8545 + # address of the ERC20 token bridge contract - used for depositing erc20BridgeAddress: 0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54 + # address of the staking bridge contract stakingBridgeAddress: 0x9135f5afd6F055e731bca2348429482eE614CFfA - syncTimeoutSec: 5 + # timeout for Ethereum transactions + syncTimeoutSec: 100 + +# whale service - coin provider for the bots whale: - walletPubKey: abe253f5682703ac654c95ab8e2de4ecccdc000a2315610bc161dd194f32af0e - walletName: whale - walletPassphrase: pastazazube + # wallet for the whale service + wallet: + # wallet name + name: whale + # password for the wallet + passphrase: pastazazube + # address of the node. Alternatively, the `networkFileURL` can be used to specify the node addresses + nodeURL: localhost:3027 + # path on local system where the wallets (and network config files) will be stored + storePath: /tmp/wallet/whale + # a map of asset ID to private key of the token owner or account with access to minting ownerPrivateKeys: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 - faucetURL: + # when ensuring that the whale service account has enough funds, in case it's insufficient, top up the ensuring amount multiplied by this `topUpScale` + # so we don't have to top up the account every time it's insufficient + topUpScale: 10 + # URL of the faucet API + faucetURL: http://localhost:1790 + # maximum rate for calling the faucet + faucetRateLimit: 2s + # slack client credentials (fallback if minting/depositing is not possible) slack: - appToken: - botToken: + appToken: # secret + botToken: # secret channelID: enabled: false + +# addresses of the nodes locations: - - localhost:3007 - - localhost:3017 - localhost:3027 + +# bot configurations bots: - name: w00 - callTimeoutMills: 10000 instrumentBase: BTC instrumentQuote: USD - quoteAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede # 0x3773A5c7aFF77e014cBF067dd31801b4C6dc4136 settlementAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede strategy: normal strategyDetails: - expectedMarkPrice: 0 auctionVolume: 100 maxLong: 100 maxShort: 100 posManagementFraction: 0.25 ordersFraction: 0.25 - seedAmount: 1000000000000000000 + # amount to be used for initial deposit and stake on the bot account + stakeAmount: 1000000000000000000 + # size of the order for auction orders seedOrderSize: 400 + # the maximum number of orders the bot will place in order to get the market out of auction + seedOrderCount: 100 + # when ensuring that the bot account has enough funds, in case it's insufficient, top up the ensuring amount multiplied by this `topUpScale` + topUpScale: 10 + # the amount of the liquidity to be provided by the bot commitmentAmount: 50000000000000 fee: 0.01 - shorteningShape: sells: - {reference: Ask, proportion: 10, offset: 80000} diff --git a/config/config_capsule.yaml b/config/config_capsule.yaml deleted file mode 100644 index 2875b0d..0000000 --- a/config/config_capsule.yaml +++ /dev/null @@ -1,91 +0,0 @@ ---- - -server: - env: prod # dev, prod - listen: ":7800" - logformat: text # json, text - loglevel: debug # debug, info, warning, error - -callTimeoutMills: 10000 -vegaAssetID: b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b - -pricing: - address: - scheme: https - host: prices.ops.vega.xyz - path: /prices -wallet: - url: http://localhost:1789 -token: - ethereumAPIAddress: ws://localhost:8545 - erc20BridgeAddress: 0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54 - stakingBridgeAddress: 0x9135f5afd6F055e731bca2348429482eE614CFfA - syncTimeoutSec: 100 -whale: - walletPubKey: abe253f5682703ac654c95ab8e2de4ecccdc000a2315610bc161dd194f32af0e - walletName: whale - walletPassphrase: pastazazube - ownerPrivateKeys: - 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 - b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b: a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0 - faucetURL: http://localhost:1790 - slack: - appToken: - botToken: - channelID: - enabled: false -locations: - - localhost:3027 - - localhost:3017 - - localhost:3007 -bots: - - name: w00 - instrumentBase: BTC - instrumentQuote: USD - quoteAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede # 0x3773A5c7aFF77e014cBF067dd31801b4C6dc4136 - settlementAssetID: 993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede - strategy: normal - strategyDetails: - expectedMarkPrice: 0 - auctionVolume: 100 - maxLong: 100 - maxShort: 100 - posManagementFraction: 0.25 - ordersFraction: 0.25 - seedAmount: 1000000000000000000 - seedOrderSize: 400 - commitmentAmount: 50000000000000 - fee: 0.01 - shorteningShape: - sells: - - {reference: Ask, proportion: 10, offset: 80000} - - {reference: Ask, proportion: 20, offset: 40000} - - {reference: Ask, proportion: 30, offset: 20000} - - {reference: Ask, proportion: 40, offset: 10000} - buys: - - {reference: Bid, proportion: 40, offset: 20000} - - {reference: Bid, proportion: 30, offset: 40000} - - {reference: Bid, proportion: 20, offset: 80000} - - {reference: Bid, proportion: 10, offset: 160000} - longeningShape: - sells: - - {reference: Ask, proportion: 10, offset: 160000} - - {reference: Ask, proportion: 20, offset: 80000} - - {reference: Ask, proportion: 30, offset: 40000} - - {reference: Ask, proportion: 40, offset: 20000} - buys: - - {reference: Bid, proportion: 40, offset: 10000} - - {reference: Bid, proportion: 30, offset: 20000} - - {reference: Bid, proportion: 20, offset: 40000} - - {reference: Bid, proportion: 10, offset: 80000} - posManagementSleepMilliseconds: 5050 - marketPriceSteeringRatePerSecond: 0.25 - minPriceSteerFraction: 0.00005 - priceSteerOrderScale: 10.5 - limitOrderDistributionParams: - method: "discreteThreeLevel" - gttLengthSeconds: 60 - tgtTimeHorizonHours: 1.0 - numTicksFromMid: 5 - numIdenticalBots: 1 - targetLNVol: 0.25 diff --git a/config/config_stagnet1.yaml b/config/config_stagnet1.yaml deleted file mode 100644 index c1eae93..0000000 --- a/config/config_stagnet1.yaml +++ /dev/null @@ -1,91 +0,0 @@ ---- - -server: - env: prod # dev, prod - listen: ":7800" - logformat: json # json_pretty, text - loglevel: debug # debug, trace, info, warning, error - -callTimeoutMills: 10000 -vegaAssetID: fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55 - -pricing: - address: - scheme: https - host: prices.ops.vega.xyz - path: /prices -wallet: - url: https://wallet.stagnet1.vega.xyz -token: - ethereumAPIAddress: wss://sepolia.infura.io/ws/v3/0b0e1795edae41f59f4c99d29ba0ae8e - erc20BridgeAddress: 0x8b1d701979d5A78414Ca3189b932f1440b17D705 - stakingBridgeAddress: 0xeB17968d04281748dE3D0347030aa1a3185a4737 - syncTimeoutSec: 1000 -whale: - walletPubKey: bdb4598b5e22ac01a812d166cc3006ac70575cd6b91b5bb4de52e158e510ec23 - walletName: # secret - walletPassphrase: # secret - syncTimeoutSec: 0 - ownerPrivateKeys: # assetID: privateKey - c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d: # secret - fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55: # secret - faucetURL: https://faucet.stagnet1.vega.xyz - slack: - appToken: # secret - botToken: # secret - channelID: C044R3YR08H - enabled: true -locations: - - n00.stagnet1.vega.xyz:3007 - - api.stagnet1.vega.xyz:3007 -bots: - - name: b01 - instrumentBase: BTC - instrumentQuote: USD - quoteAssetID: c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d - settlementAssetID: c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d - 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/config/config_test.go b/config/config_test.go index abc524b..e98ab66 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -4,10 +4,13 @@ import ( "strings" "testing" - "code.vegaprotocol.io/liqbot/config" - "code.vegaprotocol.io/liqbot/errors" - "github.com/stretchr/testify/assert" + + "code.vegaprotocol.io/liqbot/config" + tconfig "code.vegaprotocol.io/shared/libs/erc20/config" + "code.vegaprotocol.io/shared/libs/errors" + "code.vegaprotocol.io/shared/libs/wallet" + wconfig "code.vegaprotocol.io/shared/libs/whale/config" ) func TestCheckConfig(t *testing.T) { @@ -24,15 +27,15 @@ func TestCheckConfig(t *testing.T) { err = cfg.CheckConfig() assert.True(t, strings.HasPrefix(err.Error(), errors.ErrMissingEmptyConfigSection.Error())) - cfg.Wallet = &config.WalletConfig{} + cfg.Wallet = &wallet.Config{} err = cfg.CheckConfig() assert.True(t, strings.HasPrefix(err.Error(), errors.ErrMissingEmptyConfigSection.Error())) - cfg.Whale = &config.WhaleConfig{} + cfg.Whale = &wconfig.WhaleConfig{} err = cfg.CheckConfig() assert.True(t, strings.HasPrefix(err.Error(), errors.ErrMissingEmptyConfigSection.Error())) - cfg.Token = &config.TokenConfig{} + cfg.Token = &tconfig.TokenConfig{} err = cfg.CheckConfig() assert.True(t, strings.HasPrefix(err.Error(), errors.ErrMissingEmptyConfigSection.Error())) @@ -54,21 +57,5 @@ func TestCheckConfig(t *testing.T) { } cfg.Bots = append(cfg.Bots, botConfig) err = cfg.CheckConfig() - assert.NoError(t, err) -} - -func TestConfigureLogging(t *testing.T) { - cfg := new(config.Config) - cfg.Server = &config.ServerConfig{} - - var servercfg config.ServerConfig - err := cfg.ConfigureLogging() - assert.NoError(t, err) - - servercfg.LogLevel = "info" - for _, lf := range []string{"json", "textcolour", "textnocolour", "fred"} { - servercfg.LogFormat = lf - err = cfg.ConfigureLogging() - assert.NoError(t, err) - } + assert.EqualError(t, err, "missing settlement asset ID for bot 'test'") } diff --git a/config/strategy.go b/config/strategy.go index 9a3c40a..dd589f6 100644 --- a/config/strategy.go +++ b/config/strategy.go @@ -6,7 +6,6 @@ import ( "github.com/hashicorp/go-multierror" - "code.vegaprotocol.io/liqbot/types" "code.vegaprotocol.io/vega/protos/vega" ) @@ -16,20 +15,18 @@ const ( // Strategy describes parameters for the bot's strategy. type Strategy struct { - // ExpectedMarkPrice (optional) specifies the expected mark price for a market that may not yet - // have a mark price. It is used to calculate margin cost of orders meeting liquidity - // requirement. - ExpectedMarkPrice Uint `yaml:"expectedMarkPrice"` - - // AuctionVolume ... - AuctionVolume Uint `yaml:"auctionVolume"` - - // SeedAmount is the amount of tokens to mint, deposit and stake - SeedAmount Uint `yaml:"seedAmount"` + // StakeAmount is the amount of tokens to stake + StakeAmount Uint `yaml:"stakeAmount"` // SeedOrderSize is the size of the seed orders that tries to get the market out of auction SeedOrderSize uint64 `yaml:"seedOrderSize"` + // SeedOrderCount is the number of seed orders that tries to get the market out of auction + SeedOrderCount int `yaml:"seedOrderCount"` + + // TopUpScale is the scale of the top-up amount. + TopUpScale uint64 `yaml:"topUpScale"` + // CommitmentAmount is the amount of stake for the LP CommitmentAmount string `yaml:"commitmentAmount"` @@ -80,8 +77,7 @@ type Strategy struct { func (s Strategy) String() string { return fmt.Sprintf( - `normal.Strategy{ExpectedMarkPrice=%d, -AuctionVolume=%d, + `normal.Strategy{ MaxLong=%d, MaxShort=%d, PosManagementFraction=%f, @@ -94,8 +90,6 @@ MinPriceSteerFraction=%f, PriceSteerOrderScale=%f, LimitOrderDistributionParams=TBD(*LODParamsConfig), TargetLNVol=%f}`, - s.ExpectedMarkPrice, - s.AuctionVolume, s.MaxLong, s.MaxShort, s.PosManagementFraction, @@ -132,7 +126,7 @@ func (s Strategy) validateStrategyConfig() error { // LimitOrderDistParams for configuring the way price steering orders are sent. type LimitOrderDistParams struct { Method SteeringMethod `yaml:"method"` - GttLength uint64 `yaml:"gttLengthSeconds"` + GttLengthSeconds uint64 `yaml:"gttLengthSeconds"` TgtTimeHorizonHours float64 `yaml:"tgtTimeHorizonHours"` NumTicksFromMid uint64 `yaml:"numTicksFromMid"` NumIdenticalBots int `yaml:"numIdenticalBots"` @@ -144,13 +138,6 @@ type Shape struct { Buys LiquidityOrders `yaml:"buys"` } -func (s Shape) ToVegaShape() types.Shape { - return types.Shape{ - Sells: s.Sells.ToVegaLiquidityOrders(), - Buys: s.Buys.ToVegaLiquidityOrders(), - } -} - type LiquidityOrders []LiquidityOrder func (l LiquidityOrders) ToVegaLiquidityOrders() []*vega.LiquidityOrder { diff --git a/config/uint.go b/config/uint.go index 8ed97db..94621ba 100644 --- a/config/uint.go +++ b/config/uint.go @@ -3,7 +3,7 @@ package config import ( "fmt" - "code.vegaprotocol.io/liqbot/types/num" + "code.vegaprotocol.io/shared/libs/num" ) // Uint is for storing a num.Uint as a string in a config file. diff --git a/data/event_process.go b/data/event_process.go deleted file mode 100644 index 86ea4e7..0000000 --- a/data/event_process.go +++ /dev/null @@ -1,178 +0,0 @@ -package data - -import ( - "context" - "errors" - "fmt" - "time" - - 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 { - node busStreamer - log *log.Entry - pauseCh chan types.PauseSignal -} - -func newBusEventProcessor(node busStreamer, opts ...Option) *busEventProcessor { - b := &busEventProcessor{ - node: node, - log: log.WithFields(log.Fields{"component": "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 - } -} - -func (b *busEventProcessor) processEvents( - ctx context.Context, - name string, - req *coreapipb.ObserveEventBusRequest, - process func(*coreapipb.ObserveEventBusResponse) (bool, error), -) <-chan error { - errCh := make(chan error) - - var stop bool - 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 - default: - if s == nil { - return - } - - 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, - }).Warning("Unable to process event") - select { - case errCh <- err: - default: - } - } - } - } - }() - return errCh -} - -func (b *busEventProcessor) mustGetStream( - ctx context.Context, - name string, - req *coreapipb.ObserveEventBusRequest, -) coreapipb.CoreService_ObserveEventBusClient { - var ( - s coreapipb.CoreService_ObserveEventBusClient - err error - ) - - attempt := 0 - sleepTime := time.Second * 3 - - for s, err = b.getStream(ctx, req); err != nil; s, err = b.getStream(ctx, req) { - if errors.Unwrap(err).Error() == e.ErrConnectionNotReady.Error() { - b.log.WithFields(log.Fields{ - "name": name, - "error": err.Error(), - "attempt": attempt, - }).Warning("Node is not ready, reconnecting") - - b.node.MustDialConnection(ctx) - - b.log.WithFields(log.Fields{ - "name": name, - "attempt": attempt, - }).Debug("Node reconnected, reattempting to subscribe to stream") - } else if ctx.Err() == context.DeadlineExceeded { - b.log.WithFields(log.Fields{ - "name": name, - }).Warning("Deadline exceeded. Stopping event processor") - - break - } else { - attempt++ - - b.log.WithFields(log.Fields{ - "name": name, - "error": err.Error(), - "attempt": attempt, - }).Errorf("Failed to subscribe to stream, retrying in %s...", sleepTime) - - time.Sleep(sleepTime) - } - } - - b.log.WithFields(log.Fields{ - "name": name, - "attempt": attempt, - }).Debug("Stream subscribed") - - return s -} - -func (b *busEventProcessor) getStream(ctx context.Context, req *coreapipb.ObserveEventBusRequest) (coreapipb.CoreService_ObserveEventBusClient, error) { - s, err := b.node.ObserveEventBus(ctx) - if err != nil { - return nil, err - } - // Then we subscribe to the data - if err = s.SendMsg(req); err != nil { - return nil, fmt.Errorf("failed to send event bus request for stream: %w", err) - } - return s, nil -} - -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 deleted file mode 100644 index 17c911d..0000000 --- a/data/interfaces.go +++ /dev/null @@ -1,39 +0,0 @@ -package data - -import ( - "context" - - "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/market DataNode -type DataNode interface { - 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 BalanceStore interface { - Balance() types.Balance - BalanceSet(sets ...func(*types.Balance)) -} - -type MarketStore interface { - Market() types.MarketData - OpenVolume() int64 - MarketSet(sets ...func(*types.MarketData)) -} - -type busEventer interface { - processEvents(ctx context.Context, name string, req *vegaapipb.ObserveEventBusRequest, process func(*vegaapipb.ObserveEventBusResponse) (bool, error)) <-chan error -} diff --git a/data/mocks/datanode_mock.go b/data/mocks/datanode_mock.go deleted file mode 100644 index e7a4432..0000000 --- a/data/mocks/datanode_mock.go +++ /dev/null @@ -1,111 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: code.vegaprotocol.io/liqbot/data (interfaces: DataNode) - -// Package mocks is a generated GoMock package. -package mocks - -import ( - reflect "reflect" - - 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" -) - -// MockDataNode is a mock of DataNode interface. -type MockDataNode struct { - ctrl *gomock.Controller - recorder *MockDataNodeMockRecorder -} - -// MockDataNodeMockRecorder is the mock recorder for MockDataNode. -type MockDataNodeMockRecorder struct { - mock *MockDataNode -} - -// NewMockDataNode creates a new mock instance. -func NewMockDataNode(ctrl *gomock.Controller) *MockDataNode { - mock := &MockDataNode{ctrl: ctrl} - mock.recorder = &MockDataNodeMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockDataNode) EXPECT() *MockDataNodeMockRecorder { - return m.recorder -} - -// MarketDataByID mocks base method. -func (m *MockDataNode) MarketDataByID(arg0 *v1.MarketDataByIDRequest) (*v1.MarketDataByIDResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MarketDataByID", arg0) - ret0, _ := ret[0].(*v1.MarketDataByIDResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// MarketDataByID indicates an expected call of MarketDataByID. -func (mr *MockDataNodeMockRecorder) MarketDataByID(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarketDataByID", reflect.TypeOf((*MockDataNode)(nil).MarketDataByID), arg0) -} - -// ObserveEventBus mocks base method. -func (m *MockDataNode) ObserveEventBus() (v10.CoreService_ObserveEventBusClient, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ObserveEventBus") - ret0, _ := ret[0].(v10.CoreService_ObserveEventBusClient) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ObserveEventBus indicates an expected call of ObserveEventBus. -func (mr *MockDataNodeMockRecorder) ObserveEventBus() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ObserveEventBus", reflect.TypeOf((*MockDataNode)(nil).ObserveEventBus)) -} - -// PartyAccounts mocks base method. -func (m *MockDataNode) PartyAccounts(arg0 *v1.PartyAccountsRequest) (*v1.PartyAccountsResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PartyAccounts", arg0) - ret0, _ := ret[0].(*v1.PartyAccountsResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PartyAccounts indicates an expected call of PartyAccounts. -func (mr *MockDataNodeMockRecorder) PartyAccounts(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PartyAccounts", reflect.TypeOf((*MockDataNode)(nil).PartyAccounts), arg0) -} - -// PositionsByParty mocks base method. -func (m *MockDataNode) PositionsByParty(arg0 *v1.PositionsByPartyRequest) (*v1.PositionsByPartyResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PositionsByParty", arg0) - ret0, _ := ret[0].(*v1.PositionsByPartyResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PositionsByParty indicates an expected call of PositionsByParty. -func (mr *MockDataNodeMockRecorder) PositionsByParty(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PositionsByParty", reflect.TypeOf((*MockDataNode)(nil).PositionsByParty), arg0) -} - -// PositionsSubscribe mocks base method. -func (m *MockDataNode) PositionsSubscribe(arg0 *v1.PositionsSubscribeRequest) (v1.TradingDataService_PositionsSubscribeClient, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PositionsSubscribe", arg0) - ret0, _ := ret[0].(v1.TradingDataService_PositionsSubscribeClient) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PositionsSubscribe indicates an expected call of PositionsSubscribe. -func (mr *MockDataNodeMockRecorder) PositionsSubscribe(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PositionsSubscribe", reflect.TypeOf((*MockDataNode)(nil).PositionsSubscribe), arg0) -} diff --git a/data/streamingaccount.go b/data/streamingaccount.go deleted file mode 100644 index 0fed167..0000000 --- a/data/streamingaccount.go +++ /dev/null @@ -1,334 +0,0 @@ -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 { - name string - log *log.Entry - node DataNode - balanceStores *balanceStores - walletPubKey string - busEvProc busEventer - - mu sync.Mutex - waitingDeposits map[string]*num.Uint -} - -func NewAccountStream(name string, node DataNode) *account { - return &account{ - name: name, - log: log.WithField("component", "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.balanceStores = &balanceStores{ - balanceStores: make(map[string]BalanceStore), - } - - a.subscribeToAccountEvents() -} - -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, - }) - if err != nil { - return nil, err - } - - if len(response.Accounts) == 0 { - a.log.WithFields(log.Fields{ - "name": a.name, - "partyId": a.walletPubKey, - }).Warningf("Party has no accounts for asset %s", assetID) - } - - store := types.NewBalanceStore() - a.balanceStores.set(assetID, store) - - for _, acc := range response.Accounts { - 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(), - "accountType": acc.Type.String(), - }, - ).Error("failed to set account balance") - } - } - - return store, nil -} - -func (a *account) subscribeToAccountEvents() { - 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 - store, ok := a.balanceStores.get(acct.Asset) - if !ok { - continue - } - - if err := a.setBalanceByType(acct, store); 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: "+a.name, req, proc) -} - -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.EQ(exist) { - a.setWaitingDeposit(assetID, expectAmount) - } - return nil - } - - req := &coreapipb.ObserveEventBusRequest{ - Type: []eventspb.BusEventType{evtType}, - } - - proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { - for _, event := range rsp.Events { - 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 partyId != walletPubKey || asset != assetID { - continue - } - - 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 top-up expectAmount %s", amount) - } - - expect, ok := a.getWaitingDeposit(assetID) - if !ok { - expect = expectAmount.Clone() - a.setWaitingDeposit(assetID, expect) - } - - if gotAmount.GTE(expect) { - 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 - } - - if timeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, timeout) - defer cancel() - } - - 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) { - 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 { - 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*450) - defer cancel() - - 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") - } -} - -type balanceStores struct { - mu sync.Mutex - balanceStores map[string]BalanceStore -} - -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 deleted file mode 100644 index a6972cb..0000000 --- a/data/streamingmarket.go +++ /dev/null @@ -1,250 +0,0 @@ -package data - -import ( - "context" - "errors" - "fmt" - "time" - - "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 { - name string - log *log.Entry - node DataNode - walletPubKey string - marketID string - store MarketStore - busEvProc busEventer -} - -func NewMarketStream(name string, node DataNode) *market { - return &market{ - name: name, - node: node, - log: log.WithField("component", "MarketStreamer"), - } -} - -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) Subscribe(marketID string) error { - m.marketID = marketID - - if err := m.initMarketData(); err != nil { - return fmt.Errorf("failed to get market market: %w", err) - } - - if err := m.initOpenVolume(); err != nil { - return fmt.Errorf("failed to get open volume: %w", err) - } - - m.subscribeToMarketEvents() - m.subscribePositions() - - return nil -} - -func (m *market) WaitForProposalID() (string, error) { - req := &coreapipb.ObserveEventBusRequest{ - Type: []eventspb.BusEventType{eventspb.BusEventType_BUS_EVENT_TYPE_PROPOSAL}, - } - - var proposalID string - - proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { - for _, event := range rsp.GetEvents() { - proposal := event.GetProposal() - - if proposal.PartyId != m.walletPubKey { - continue - } - - 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*450) - defer cancel() - - 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 { - req := &coreapipb.ObserveEventBusRequest{ - Type: []eventspb.BusEventType{eventspb.BusEventType_BUS_EVENT_TYPE_PROPOSAL}, - } - - proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { - for _, event := range rsp.GetEvents() { - proposal := event.GetProposal() - - 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*450) - defer cancel() - - 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() { - 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: "+m.name, 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: "+m.name, 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/errors/doc.go b/errors/doc.go deleted file mode 100644 index ebd73e7..0000000 --- a/errors/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package errors contains simple error variables. -package errors diff --git a/errors/error_detail.go b/errors/error_detail.go deleted file mode 100644 index c20e91b..0000000 --- a/errors/error_detail.go +++ /dev/null @@ -1,34 +0,0 @@ -package errors - -import ( - "errors" - "fmt" - "strings" - - "google.golang.org/grpc/status" -) - -// ErrorDetail takes an error, converts it to a gRPC Status object, and pulls out the code, message, and (most -// importantly) the details list. -func ErrorDetail(err error) error { - errStatus, _ := status.FromError(err) - var b strings.Builder - b.WriteString("gRPCError{code=") - b.WriteString(errStatus.Code().String()) - b.WriteString(" message='") - b.WriteString(errStatus.Message()) - b.WriteString("'") - details := errStatus.Details() - if len(details) > 0 { - b.WriteString(" details=[") - for i, d := range details { - if i > 0 { - b.WriteString(", ") - } - b.WriteString(fmt.Sprintf("%v", d)) - } - b.WriteString("]") - } - b.WriteString("}") - return errors.New(b.String()) -} diff --git a/errors/error_detail_test.go b/errors/error_detail_test.go deleted file mode 100644 index f2f1c17..0000000 --- a/errors/error_detail_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package errors - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/require" - gstatus "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/anypb" -) - -func TestErrorDetail_Unknown(t *testing.T) { - e := errors.New("bzzt") - e2 := ErrorDetail(e) - require.NotNil(t, e2) - require.Equal(t, "gRPCError{code=Unknown message='bzzt'}", e2.Error()) -} - -func TestErrorDetail_Simple(t *testing.T) { - e := status.Error(codes.InvalidArgument, "x should be y") - e2 := ErrorDetail(e) - require.NotNil(t, e2) - require.Equal(t, "gRPCError{code=InvalidArgument message='x should be y'}", e2.Error()) -} - -func TestErrorDetail_WithDetails(t *testing.T) { - e1 := gstatus.Status{ - Code: int32(codes.PermissionDenied), - Message: "missing token", - Details: []*anypb.Any{ - // { - // // TODO: Add a real Detail that ends up in the message - // TypeUrl: "bzzt", - // Value: nil, - // }, - }, - } - e := status.ErrorProto(&e1) - e2 := ErrorDetail(e) - require.NotNil(t, e2) - // require.Equal(t, "gRPCError{code=PermissionDenied message='missing token' details=[proto: not found]}", e2.Error()) - require.Equal(t, "gRPCError{code=PermissionDenied message='missing token'}", e2.Error()) -} diff --git a/errors/errors.go b/errors/errors.go deleted file mode 100644 index 52a3928..0000000 --- a/errors/errors.go +++ /dev/null @@ -1,14 +0,0 @@ -package errors - -import "errors" - -var ( - // ErrConnectionNotReady indicated that the network connection to the gRPC server is not ready. - ErrConnectionNotReady = errors.New("gRPC connection not ready") - - // ErrNil indicates that a nil/null pointer was encountered. - ErrNil = errors.New("nil pointer") - - // ErrMissingEmptyConfigSection indicates that a required config file section is missing (not present) or empty (zero-length). - ErrMissingEmptyConfigSection = errors.New("config file section is missing/empty") -) diff --git a/go.mod b/go.mod index 18aec5b..eb703d1 100644 --- a/go.mod +++ b/go.mod @@ -1,39 +1,36 @@ module code.vegaprotocol.io/liqbot -go 1.18 +go 1.19 require ( code.vegaprotocol.io/priceproxy v0.1.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 + code.vegaprotocol.io/shared v0.0.0-20230118150809-e8c5b9cceca4 + code.vegaprotocol.io/vega v0.66.2-0.20230112212708-7bd93a8ea2a0 + github.com/ethereum/go-ethereum v1.10.21 // indirect 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 github.com/jinzhu/configor v1.2.1 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/shopspring/decimal v1.3.1 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + github.com/slack-go/slack v0.11.4 // indirect github.com/stretchr/testify v1.8.0 - gonum.org/v1/gonum v0.9.1 - google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b - google.golang.org/grpc v1.48.0 - google.golang.org/protobuf v1.28.0 + go.uber.org/zap v1.23.0 + gonum.org/v1/gonum v0.12.0 ) require ( - github.com/BurntSushi/toml v1.2.0 // indirect + github.com/BurntSushi/toml v1.2.1 // 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.1 // indirect + github.com/btcsuite/btcd v0.22.2 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // 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/cosmos/iavl v0.19.4 // 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 @@ -44,40 +41,48 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect 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/v2 v2.9.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/holiman/uint256 v1.2.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20220317090546-adb2f9614b17 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rjeczalik/notify v0.9.1 // indirect - github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa // indirect + github.com/sasha-s/go-deadlock v0.3.1 // indirect 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/tendermint/tendermint v0.34.21 // indirect + github.com/tendermint/tendermint v0.34.24 // 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-20220525230936-793ad666bf5e // indirect - golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 // 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 + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + golang.org/x/crypto v0.1.0 // indirect + golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect + golang.org/x/net v0.1.0 // indirect + golang.org/x/sys v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect - golang.org/x/tools v0.1.12 // indirect + golang.org/x/tools v0.2.0 // indirect + google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a // indirect + google.golang.org/grpc v1.50.1 // indirect + google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 // 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 gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/shopspring/decimal => github.com/vegaprotocol/decimal v1.2.1-0.20210705145732-aaa563729a0a +replace github.com/shopspring/decimal => github.com/vegaprotocol/decimal v1.3.1-uint256 diff --git a/go.sum b/go.sum index d776d95..661fcc6 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,12 @@ -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= 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-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= +code.vegaprotocol.io/shared v0.0.0-20230118150809-e8c5b9cceca4 h1:meu+4ObRSlDDYD8CLWoCgp7IM2C381JN9oBa0Lj5j4U= +code.vegaprotocol.io/shared v0.0.0-20230118150809-e8c5b9cceca4/go.mod h1:2BDmtSsLVsgQRm03psa7YOAtQ0u1kQZMZFDnsuJnVrk= +code.vegaprotocol.io/vega v0.66.2-0.20230112212708-7bd93a8ea2a0 h1:8sC+YJRc6YG0ezbxYEkE3R1Q/+rrwkpZSluOCwQmZrE= +code.vegaprotocol.io/vega v0.66.2-0.20230112212708-7bd93a8ea2a0/go.mod h1:Y5hTb3sE5JuB2WUOeSHsQl3p7aF3nuv2+HS1svgn6NM= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -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/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 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/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= @@ -21,33 +16,24 @@ github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9 github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -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 v0.22.2 h1:vBZ+lGGd1XubpOWO67ITJpAEsICWhA0YzqkcpkgNBfo= +github.com/btcsuite/btcd v0.22.2/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/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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= 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/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/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-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-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/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= @@ -55,8 +41,8 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 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/cosmos/iavl v0.19.4 h1:t82sN+Y0WeqxDLJRSpNd8YFX5URIrT+p8n6oJbJ2Dok= +github.com/cosmos/iavl v0.19.4/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.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= @@ -78,32 +64,17 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn 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/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= -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.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -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-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= 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/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.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/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-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= 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= @@ -116,24 +87,17 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP 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.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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 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.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= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 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.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -142,23 +106,18 @@ 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 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.5.0/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/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -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/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/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 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/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -184,8 +143,6 @@ github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= 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.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= @@ -196,13 +153,14 @@ 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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 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/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -224,38 +182,33 @@ github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 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/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -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/common v0.35.0 h1:Eyr+Pw2VymWejHqCugNaQXkAi6KayVNxaHeu6khmFBE= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= 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 v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 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.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -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/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/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/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/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -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/slack-go/slack v0.11.4 h1:ojSa7KlPm3PqY2AomX4VTxEsK5eci5JaxCjlzGV5zoM= +github.com/slack-go/slack v0.11.4/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= @@ -269,6 +222,7 @@ github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57N github.com/stretchr/objx v0.1.0/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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -278,8 +232,8 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 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/tendermint/tendermint v0.34.21 h1:UiGGnBFHVrZhoQVQ7EfwSOLuCtarqCSsRf8VrklqB7s= -github.com/tendermint/tendermint v0.34.21/go.mod h1:XDvfg6U7grcFTDx7VkzxnhazQ/bspGJAn4DZ6DcLLjQ= +github.com/tendermint/tendermint v0.34.24 h1:879MKKJWYYPJEMMKME+DWUTY4V9f/FBpnZDI82ky+4k= +github.com/tendermint/tendermint v0.34.24/go.mod h1:rXVrl4OYzmIa1I91av3iLv2HS0fGSiucyW9J4aMTpKI= 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= @@ -290,8 +244,8 @@ github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2n github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 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/decimal v1.3.1-uint256 h1:Aj//9joGGuz+dAKo6W/r9Rt1HUXYrjH7oerdCg1q/So= +github.com/vegaprotocol/decimal v1.3.1-uint256/go.mod h1:+mRbjtsnpvm5Qw6aiLEf3I6SHICNB4nhMTmH9y8hMtg= github.com/vegaprotocol/go-slip10 v0.1.0 h1:pP3XF2gSKM6OuaAURobHZlXZ9AzZ5LgeWvXFudL7Mb4= github.com/vegaprotocol/go-slip10 v0.1.0/go.mod h1:zTjs9hXxElXfZvPRWWFMtEYSe7udGFP1iUM1ktJ72HI= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -301,78 +255,49 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec 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.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= 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-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -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-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -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= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= 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.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-20190108225652-1e06a53dbb7e/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-20190620200207-3b0461eec859/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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 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/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-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= 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-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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190215142949-d0b11bdaac8a/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-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -385,8 +310,6 @@ golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -396,77 +319,48 @@ golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBc 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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/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.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/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/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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-20180525024113-a5b4c53f6e8b/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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/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-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= 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/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= -gonum.org/v1/gonum v0.9.1 h1:HCWmqqNoELL0RAQeKBXWtkp04mGk8koafcB4He6+uhc= -gonum.org/v1/gonum v0.9.1/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= -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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -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.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +gonum.org/v1/gonum v0.12.0 h1:xKuo6hzt+gMav00meVPUlXwSdoEJP46BR+wdxQEFK2o= +gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a h1:GH6UPn3ixhWcKDhpnEC55S75cerLPdpp3hrhfKYjZgw= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= 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= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -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.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= +google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 h1:KR8+MyP7/qOlV+8Af01LtjL04bu7on42eVsxT4EyBQk= +google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -477,14 +371,11 @@ gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHN 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/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.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= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/market/interfaces.go b/market/interfaces.go index 61f27e0..def3612 100644 --- a/market/interfaces.go +++ b/market/interfaces.go @@ -3,37 +3,42 @@ 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" + "code.vegaprotocol.io/shared/libs/cache" + "code.vegaprotocol.io/shared/libs/num" + dataapipb "code.vegaprotocol.io/vega/protos/data-node/api/v2" + "code.vegaprotocol.io/vega/protos/vega" + vegaapipb "code.vegaprotocol.io/vega/protos/vega/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 + Store() marketStore + Subscribe(ctx context.Context, marketID string) error + waitForProposalID() (string, error) + waitForProposalEnacted(pID string) error + waitForLiquidityProvision(ctx context.Context, ref string) error } -type tradingDataService interface { +type dataNode interface { + MarketDataByID(ctx context.Context, req *dataapipb.GetLatestMarketDataRequest) (*vega.MarketData, error) + PositionsByParty(ctx context.Context, req *dataapipb.ListPositionsRequest) ([]*vega.Position, error) + ObserveEventBus(ctx context.Context) (client vegaapipb.CoreService_ObserveEventBusClient, err error) MustDialConnection(ctx context.Context) Target() string - Markets(req *dataapipb.MarketsRequest) (*dataapipb.MarketsResponse, error) // TODO: bot should probably not have to worry about finding markets + Markets(ctx context.Context, req *dataapipb.ListMarketsRequest) ([]*vega.Market, 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 + Balance(ctx context.Context, assetID string) cache.Balance + EnsureBalance(ctx context.Context, assetID string, balanceFn func(cache.Balance) *num.Uint, targetAmount *num.Uint, dp, scale uint64, from string) error EnsureStake(ctx context.Context, receiverName, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error } + +type marketStore interface { + Market() cache.MarketData + OpenVolume() int64 + MarketSet(sets ...func(*cache.MarketData)) +} + +type busEventer interface { + ProcessEvents(ctx context.Context, name string, req *vegaapipb.ObserveEventBusRequest, process func(*vegaapipb.ObserveEventBusResponse) (bool, error)) <-chan error +} diff --git a/market/service.go b/market/service.go index 25c6979..80d05dd 100644 --- a/market/service.go +++ b/market/service.go @@ -2,113 +2,107 @@ package market import ( "context" + "errors" "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" + itypes "code.vegaprotocol.io/liqbot/types" ppconfig "code.vegaprotocol.io/priceproxy/config" - v12 "code.vegaprotocol.io/vega/protos/data-node/api/v1" + "code.vegaprotocol.io/shared/libs/cache" + "code.vegaprotocol.io/shared/libs/num" + "code.vegaprotocol.io/shared/libs/wallet" + "code.vegaprotocol.io/vega/logging" + v12 "code.vegaprotocol.io/vega/protos/data-node/api/v2" "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" + oraclesv1 "code.vegaprotocol.io/vega/protos/vega/data/v1" walletpb "code.vegaprotocol.io/vega/protos/vega/wallet/v1" ) type Service struct { - name string - pricingEngine PricingEngine + pricingEngine itypes.PricingEngine marketStream marketStream - node tradingDataService - walletClient normal.WalletClient // TODO: wtf?! - store data.MarketStore + node dataNode + wallet wallet.WalletV2 account accountService config config.BotConfig - log *log.Entry + log *logging.Logger - decimalPlaces uint64 - marketID string - walletPubKey string - vegaAssetID string + marketDecimalPlaces uint64 + marketID string + vegaAssetID string } func NewService( - name string, - marketStream marketStream, - node tradingDataService, - walletClient normal.WalletClient, - pe PricingEngine, + log *logging.Logger, + node dataNode, + wallet wallet.WalletV2, + pe itypes.PricingEngine, account accountService, + marketStream marketStream, config config.BotConfig, vegaAssetID string, ) *Service { - s := &Service{ - name: name, + return &Service{ marketStream: marketStream, node: node, - walletClient: walletClient, + wallet: wallet, pricingEngine: pe, account: account, config: config, vegaAssetID: vegaAssetID, - log: log.WithField("component", "MarketService"), + log: log.Named("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) Market() cache.MarketData { + return m.marketStream.Store().Market() } -func (m *Service) Init(pubKey string, pauseCh chan types.PauseSignal) error { - store, err := m.marketStream.Init(pubKey, pauseCh) +// SetupMarket creates a market if it doesn't exist, provides liquidity and gets the market into continuous trading mode. +func (m *Service) SetupMarket(ctx context.Context) (*vega.Market, error) { + market, err := m.ProvideMarket(ctx) if err != nil { - return err + return nil, fmt.Errorf("failed to provide market: %w", 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) + if err := m.marketStream.Subscribe(ctx, market.Id); err != nil { + return nil, fmt.Errorf("failed to subscribe to market stream: %w", err) } - m.marketID = marketID - return nil -} + m.marketID = market.Id -func (m *Service) SetPubKey(pubKey string) { - m.walletPubKey = pubKey -} + if err = m.ProvideLiquidity(ctx); err != nil { + return nil, fmt.Errorf("failed to provide liquidity: %w", err) + } -func (m *Service) Market() types.MarketData { - return m.store.Market() + if !m.CanPlaceOrders() { + if err = m.SeedOrders(ctx); err != nil { + return nil, fmt.Errorf("failed to seed orders: %w", err) + } + } + + return market, nil } -func (m *Service) SetupMarket(ctx context.Context) (*vega.Market, error) { - market, err := m.FindMarket() +func (m *Service) ProvideMarket(ctx context.Context) (*vega.Market, error) { + market, err := m.findMarket(ctx) if err == nil { - m.log.WithField("market", market).Info("Found market") + m.log.With(logging.Market(market)).Info("Found market") return market, nil } - m.log.WithError(err).Info("Failed to find market, creating it") + m.log.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() + market, err = m.findMarket(ctx) if err != nil { return nil, fmt.Errorf("failed to find market after creation: %w", err) } @@ -116,13 +110,13 @@ func (m *Service) SetupMarket(ctx context.Context) (*vega.Market, error) { return market, nil } -func (m *Service) FindMarket() (*vega.Market, error) { - marketsResponse, err := m.node.Markets(&v12.MarketsRequest{}) +func (m *Service) findMarket(ctx context.Context) (*vega.Market, error) { + marketsResponse, err := m.node.Markets(ctx, &v12.ListMarketsRequest{}) if err != nil { return nil, fmt.Errorf("failed to get markets: %w", err) } - for _, mkt := range marketsResponse.Markets { + for _, mkt := range marketsResponse { instrument := mkt.TradableInstrument.GetInstrument() if instrument == nil { continue @@ -153,8 +147,8 @@ func (m *Service) FindMarket() (*vega.Market, error) { continue } - m.log = m.log.WithFields(log.Fields{"marketID": mkt.Id}) - m.decimalPlaces = mkt.DecimalPlaces + m.log = m.log.With(logging.MarketID(mkt.Id)) + m.marketDecimalPlaces = mkt.DecimalPlaces return mkt, nil } @@ -165,40 +159,40 @@ func (m *Service) FindMarket() (*vega.Market, error) { func (m *Service) CreateMarket(ctx context.Context) error { m.log.Info("Minting, staking and depositing tokens") - seedAmount := m.config.StrategyDetails.SeedAmount.Get() + stakeAmount := m.config.StrategyDetails.StakeAmount.Get() - m.log.WithFields(log.Fields{ - "amount": seedAmount.String(), - "asset": m.config.SettlementAssetID, - "name": m.name, - }).Info("Ensuring balance for market creation") + m.log.With( + logging.String("amount", stakeAmount.String()), + logging.String("asset", m.config.SettlementAssetID), + logging.String("name", m.config.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 { + // TODO: this is probably unnecessary + if err := m.account.EnsureBalance(ctx, m.config.SettlementAssetID, cache.General, stakeAmount, m.marketDecimalPlaces, 1, "MarketCreation"); err != nil { return fmt.Errorf("failed to ensure balance: %w", err) } - m.log.WithFields(log.Fields{ - "amount": seedAmount.String(), - "asset": m.config.SettlementAssetID, - "name": m.name, - }).Info("Balance ensured") + m.log.With( + logging.String("amount", stakeAmount.String()), + logging.String("asset", m.config.SettlementAssetID), + logging.String("name", m.config.Name), + ).Info("Balance ensured") - m.log.WithFields(log.Fields{ - "amount": seedAmount.String(), - "asset": m.vegaAssetID, - "name": m.name, - }).Info("Ensuring stake for market creation") + m.log.With( + logging.String("amount", stakeAmount.String()), + logging.String("asset", m.vegaAssetID), + logging.String("name", m.config.Name), + ).Info("Ensuring stake for market creation") - if err := m.account.EnsureStake(ctx, m.config.Name, m.walletPubKey, m.vegaAssetID, seedAmount, "MarketCreation"); err != nil { + if err := m.account.EnsureStake(ctx, m.config.Name, m.wallet.PublicKey(), m.vegaAssetID, stakeAmount, "MarketCreation"); err != nil { return fmt.Errorf("failed to ensure stake: %w", err) } - m.log.WithFields(log.Fields{ - "amount": seedAmount.String(), - "asset": m.vegaAssetID, - "name": m.name, - }).Info("Successfully linked stake") + m.log.With( + logging.String("amount", stakeAmount.String()), + logging.String("asset", m.vegaAssetID), + logging.String("name", m.config.Name), + ).Info("Successfully linked stake") m.log.Info("Sending new market proposal...") @@ -208,7 +202,7 @@ func (m *Service) CreateMarket(ctx context.Context) error { m.log.Debug("Waiting for proposal ID...") - proposalID, err := m.marketStream.WaitForProposalID() + proposalID, err := m.marketStream.waitForProposalID() if err != nil { return fmt.Errorf("failed to wait for proposal ID: %w", err) } @@ -222,7 +216,7 @@ func (m *Service) CreateMarket(ctx context.Context) error { m.log.Debug("Waiting for proposal to be enacted...") - if err = m.marketStream.WaitForProposalEnacted(proposalID); err != nil { + if err = m.marketStream.waitForProposalEnacted(proposalID); err != nil { return fmt.Errorf("failed to wait for proposal to be enacted: %w", err) } @@ -237,11 +231,10 @@ func (m *Service) sendNewMarketProposal(ctx context.Context) error { } submitTxReq := &walletpb.SubmitTransactionRequest{ - PubKey: m.walletPubKey, Command: cmd, } - if err := m.walletClient.SignTx(ctx, submitTxReq); err != nil { + if _, err := m.wallet.SendTransaction(ctx, submitTxReq); err != nil { return fmt.Errorf("failed to sign transaction: %v", err) } @@ -262,34 +255,304 @@ func (m *Service) sendVote(ctx context.Context, proposalId string, vote bool) er } submitTxReq := &walletpb.SubmitTransactionRequest{ - PubKey: m.walletPubKey, Command: cmd, } - if err := m.walletClient.SignTx(ctx, submitTxReq); err != nil { + if _, err := m.wallet.SendTransaction(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 +func (m *Service) ProvideLiquidity(ctx context.Context) error { + commitment, err := m.GetRequiredCommitment() + if err != nil { + return fmt.Errorf("failed to get required commitment: %w", err) + } + + if commitment.IsZero() { + return nil + } + + if err = m.account.EnsureBalance(ctx, m.config.SettlementAssetID, cache.GeneralAndBond, commitment, m.marketDecimalPlaces, 2, "MarketCreation"); err != nil { + return fmt.Errorf("failed to ensure balance: %w", err) + } + + // We always cache off with longening shapes + buyShape, sellShape, _ := m.GetShape() + // At the cache of each loop, wait for positive general account balance. This is in case the network has + // been restarted. + if err := m.CheckInitialMargin(ctx, buyShape, sellShape); err != nil { + return fmt.Errorf("failed initial margin check: %w", err) + } + + // Submit LP order to market. + if err = m.SendLiquidityProvision(ctx, commitment, buyShape, sellShape); err != nil { + return fmt.Errorf("failed to send liquidity provision order: %w", err) + } + + return nil } -// 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? +func (m *Service) CheckInitialMargin(ctx context.Context, buyShape, sellShape []*vega.LiquidityOrder) error { + // Turn the shapes into a set of orders scaled by commitment + obligation := cache.GeneralAndBond(m.account.Balance(ctx, m.config.SettlementAssetID)) + buyOrders := m.calculateOrderSizes(obligation, buyShape) + sellOrders := m.calculateOrderSizes(obligation, sellShape) + + buyRisk := 0.01 + sellRisk := 0.01 + + buyCost := m.calculateMarginCost(buyRisk, buyOrders) + sellCost := m.calculateMarginCost(sellRisk, sellOrders) + + shapeMarginCost := num.Max(buyCost, sellCost) + avail := num.MulFrac(cache.General(m.account.Balance(ctx, m.config.SettlementAssetID)), m.config.StrategyDetails.OrdersFraction, 15) + + if !avail.LT(shapeMarginCost) { + return nil + } + + missingPercent := "Inf" + + if !avail.IsZero() { + x := num.UintChain(shapeMarginCost).Sub(avail).Mul(num.NewUint(100)).Div(avail).Get() + missingPercent = fmt.Sprintf("%v%%", x) + } + + m.log.With( + logging.String("available", avail.String()), + logging.String("cost", shapeMarginCost.String()), + logging.String("missing", num.Zero().Sub(avail, shapeMarginCost).String()), + logging.String("missingPercent", missingPercent), + ).Error("Not enough collateral to safely keep orders up given current price, risk parameters and supplied default shapes.") + + return errors.New("not enough collateral") +} + +// calculateOrderSizes calculates the size of the orders using the total commitment, price, distance from mid and chance +// of trading liquidity.supplied.updateSizes(obligation, currentPrice, liquidityOrders, true, minPrice, maxPrice). +func (m *Service) calculateOrderSizes(obligation *num.Uint, liquidityOrders []*vega.LiquidityOrder) []*vega.Order { + orders := make([]*vega.Order, 0, len(liquidityOrders)) + // Work out the total proportion for the shape + totalProportion := num.Zero() + for _, order := range liquidityOrders { + totalProportion.Add(totalProportion, num.NewUint(uint64(order.Proportion))) + } + + // Now size up the orders and create the real order objects + for _, lo := range liquidityOrders { + size := num.UintChain(obligation.Clone()). + Mul(num.NewUint(uint64(lo.Proportion))). + Mul(num.NewUint(10)). + Div(totalProportion).Div(m.Market().MarkPrice()).Get() + peggedOrder := vega.PeggedOrder{ + Reference: lo.Reference, + Offset: lo.Offset, + } - price, overflow := num.UintFromString(order.Price, 10) - if overflow { - return fmt.Errorf("failed to parse price: overflow") + order := vega.Order{ + Side: vega.Side_SIDE_BUY, + Remaining: size.Uint64(), + Size: size.Uint64(), + TimeInForce: vega.Order_TIME_IN_FORCE_GTC, + Type: vega.Order_TYPE_LIMIT, + PeggedOrder: &peggedOrder, + } + orders = append(orders, &order) } - if err := m.account.EnsureBalance(ctx, m.config.SettlementAssetID, price, from); err != nil { + return orders +} + +// calculateMarginCost estimates the margin cost of the set of orders. +func (m *Service) calculateMarginCost(risk float64, orders []*vega.Order) *num.Uint { + margins := make([]*num.Uint, len(orders)) + + for i, order := range orders { + if order.Side == vega.Side_SIDE_BUY { + margins[i] = num.NewUint(1 + order.Size) + } else { + margins[i] = num.NewUint(order.Size) + } + } + + totalMargin := num.UintChain(num.Zero()).Add(margins...).Mul(m.Market().MarkPrice()).Get() + return num.MulFrac(totalMargin, risk, 15) +} + +func (m *Service) GetShape() ([]*vega.LiquidityOrder, []*vega.LiquidityOrder, string) { + // We always cache off with longening shapes + shape := "longening" + buyShape := m.config.StrategyDetails.LongeningShape.Buys.ToVegaLiquidityOrders() + sellShape := m.config.StrategyDetails.LongeningShape.Sells.ToVegaLiquidityOrders() + + if m.Market().OpenVolume() > 0 { + shape = "shortening" + buyShape = m.config.StrategyDetails.ShorteningShape.Buys.ToVegaLiquidityOrders() + sellShape = m.config.StrategyDetails.ShorteningShape.Sells.ToVegaLiquidityOrders() + } + + return buyShape, sellShape, shape +} + +func (m *Service) CheckPosition() (uint64, vega.Side, bool) { + size := uint64(0) + side := vega.Side_SIDE_UNSPECIFIED + shouldPlace := true + openVolume := m.Market().OpenVolume() + + if openVolume >= 0 && num.NewUint(uint64(openVolume)).GT(m.config.StrategyDetails.MaxLong.Get()) { + size = num.MulFrac(num.NewUint(uint64(openVolume)), m.config.StrategyDetails.PosManagementFraction, 15).Uint64() + side = vega.Side_SIDE_SELL + } else if openVolume < 0 && num.NewUint(uint64(-openVolume)).GT(m.config.StrategyDetails.MaxShort.Get()) { + size = num.MulFrac(num.NewUint(uint64(-openVolume)), m.config.StrategyDetails.PosManagementFraction, 15).Uint64() + side = vega.Side_SIDE_BUY + } else { + shouldPlace = false + } + + return size, side, shouldPlace +} + +func (m *Service) SendLiquidityProvision(ctx context.Context, commitment *num.Uint, buys, sells []*vega.LiquidityOrder) error { + ref := m.config.Name + "-" + commitment.String() + submitTxReq := &walletpb.SubmitTransactionRequest{ + Command: &walletpb.SubmitTransactionRequest_LiquidityProvisionSubmission{ + LiquidityProvisionSubmission: &commandspb.LiquidityProvisionSubmission{ + MarketId: m.marketID, + CommitmentAmount: commitment.String(), + Fee: m.config.StrategyDetails.Fee, + Sells: sells, + Buys: buys, + Reference: ref, + }, + }, + } + + m.log.With(logging.String("commitment", commitment.String())).Debug("Submitting LiquidityProvisionSubmission...") + + if _, err := m.wallet.SendTransaction(ctx, submitTxReq); err != nil { + return fmt.Errorf("failed to submit LiquidityProvisionSubmission: %w", err) + } + + if err := m.marketStream.waitForLiquidityProvision(ctx, ref); err != nil { + return fmt.Errorf("failed to wait for liquidity provision to be active: %w", err) + } + + m.log.With(logging.String("commitment", commitment.String())).Debug("Submitted LiquidityProvisionSubmission") + + return nil +} + +// call this if the position flips. +func (m *Service) SendLiquidityProvisionAmendment(ctx context.Context, commitment *num.Uint, buys, sells []*vega.LiquidityOrder) error { + var commitmentAmount string + if commitment != nil { + if commitment.IsZero() { + return m.SendLiquidityProvisionCancellation(ctx) + } + commitmentAmount = commitment.String() + } + + submitTxReq := &walletpb.SubmitTransactionRequest{ + Command: &walletpb.SubmitTransactionRequest_LiquidityProvisionAmendment{ + LiquidityProvisionAmendment: &commandspb.LiquidityProvisionAmendment{ + MarketId: m.marketID, + CommitmentAmount: commitmentAmount, + Fee: m.config.StrategyDetails.Fee, + Sells: sells, + Buys: buys, + }, + }, + } + + if _, err := m.wallet.SendTransaction(ctx, submitTxReq); err != nil { + return fmt.Errorf("failed to submit LiquidityProvisionAmendment: %w", err) + } + + m.log.With(logging.String("commitment", commitmentAmount)).Debug("Submitted LiquidityProvisionAmendment") + return nil +} + +func (m *Service) EnsureCommitmentAmount(ctx context.Context) error { + requiredCommitment, err := m.GetRequiredCommitment() + if err != nil { + return fmt.Errorf("failed to get new commitment amount: %w", err) + } + + if requiredCommitment.IsZero() { + return nil + } + + m.log.With(logging.String("newCommitment", requiredCommitment.String())).Debug("Supplied stake is less than target stake, increasing commitment amount...") + + if err = m.account.EnsureBalance(ctx, m.config.SettlementAssetID, cache.GeneralAndBond, requiredCommitment, m.marketDecimalPlaces, 2, "MarketService"); err != nil { return fmt.Errorf("failed to ensure balance: %w", err) } + buys, sells, _ := m.GetShape() + + m.log.With(logging.String("newCommitment", requiredCommitment.String())).Debug("Sending new commitment amount...") + + if err = m.SendLiquidityProvisionAmendment(ctx, requiredCommitment, buys, sells); err != nil { + return fmt.Errorf("failed to update commitment amount: %w", err) + } + + return nil +} + +func (m *Service) SendLiquidityProvisionCancellation(ctx context.Context) error { + submitTxReq := &walletpb.SubmitTransactionRequest{ + Command: &walletpb.SubmitTransactionRequest_LiquidityProvisionCancellation{ + LiquidityProvisionCancellation: &commandspb.LiquidityProvisionCancellation{ + MarketId: m.marketID, + }, + }, + } + + if _, err := m.wallet.SendTransaction(ctx, submitTxReq); err != nil { + return fmt.Errorf("failed to submit LiquidityProvisionCancellation: %w", err) + } + + m.log.With(logging.String("commitment", "0")).Debug("Submitted LiquidityProvisionCancellation") + + return nil +} + +func (m *Service) GetRequiredCommitment() (*num.Uint, error) { + suppliedStake := m.Market().SuppliedStake().Clone() + targetStake := m.Market().TargetStake().Clone() + + if targetStake.IsZero() { + var err error + targetStake, err = num.ConvertUint256(m.config.StrategyDetails.CommitmentAmount) + if err != nil { + return nil, fmt.Errorf("failed to convert commitment amount: %w", err) + } + } + + m.log.With( + logging.String("suppliedStake", suppliedStake.String()), + logging.String("targetStake", targetStake.String()), + ).Debug("Checking for required commitment") + + dx := suppliedStake.Int().Sub(targetStake.Int()) + + if dx.IsPositive() { + return num.Zero(), nil + } + + return num.Zero().Add(targetStake, dx.Uint()), 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 { cmd := &walletpb.SubmitTransactionRequest_OrderSubmission{ OrderSubmission: &commandspb.OrderSubmission{ MarketId: order.MarketId, @@ -304,14 +567,6 @@ 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.String(), - "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) } @@ -320,66 +575,96 @@ func (m *Service) SubmitOrder(ctx context.Context, order *vega.Order, from strin cmd.OrderSubmission.Price = order.Price } + m.log.With( + logging.Int64("expiresAt", cmd.OrderSubmission.ExpiresAt), + logging.String("types", order.Type.String()), + logging.String("reference", order.Reference), + logging.Uint64("size", order.Size), + logging.String("side", order.Side.String()), + logging.String("price", order.Price), + logging.String("tif", order.TimeInForce.String()), + ).Debugf("%s: Submitting order", from) + submitTxReq := &walletpb.SubmitTransactionRequest{ - PubKey: m.walletPubKey, Command: cmd, } - if err := m.walletClient.SignTx(ctx, submitTxReq); err != nil { + if _, err := m.wallet.SendTransaction(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) - +func (m *Service) SeedOrders(ctx context.Context) error { 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 + orders, totalCost := m.createSeedAuctionOrders(externalPrice.Clone()) + m.log.With( + logging.String("externalPrice", externalPrice.String()), + logging.String("totalCost", totalCost.String()), + logging.String("balance.General", cache.General(m.account.Balance(ctx, m.config.SettlementAssetID)).String()), + ).Debug("Seeding auction orders") + + if err := m.account.EnsureBalance(ctx, m.config.SettlementAssetID, cache.General, totalCost, m.marketDecimalPlaces, 2, "MarketService"); err != nil { + return fmt.Errorf("failed to ensure balance: %w", err) + } + + for _, order := range orders { + if err = m.SubmitOrder(ctx, order, "MarketService", int64(m.config.StrategyDetails.PosManagementFraction)); err != nil { + return fmt.Errorf("failed to create seed order: %w", err) + } + + if m.CanPlaceOrders() { + m.log.Debug("Trading mode is continuous") + return nil + } + + time.Sleep(time.Second * 2) + } + + return fmt.Errorf("seeding orders did not end the auction") +} +func (m *Service) createSeedAuctionOrders(externalPrice *num.Uint) ([]*vega.Order, *num.Uint) { + tif := vega.Order_TIME_IN_FORCE_GTC + count := m.config.StrategyDetails.SeedOrderCount + orders := make([]*vega.Order, count) + totalCost := num.NewUint(0) + size := m.config.StrategyDetails.SeedOrderSize + + for i := 0; i < count; i++ { side := vega.Side_SIDE_BUY if i%2 == 0 { side = vega.Side_SIDE_SELL } - if i == 0 { + price := externalPrice.Clone() + + switch i { + case 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 { + case 1: price = num.UintChain(price).Mul(num.NewUint(95)).Div(num.NewUint(100)).Get() - tif = vega.Order_TIME_IN_FORCE_GTC } - order := &vega.Order{ + totalCost.Add(totalCost, num.Zero().Mul(price.Clone(), num.NewUint(size))) + + orders[i] = &vega.Order{ MarketId: m.marketID, - Size: m.config.StrategyDetails.SeedOrderSize, + Size: size, 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") + Reference: "AuctionOrder", } } - m.log.Debugf("%s: Seeding orders finished", from) - return nil + return orders, totalCost } func (m *Service) GetExternalPrice() (*num.Uint, error) { @@ -396,7 +681,7 @@ func (m *Service) GetExternalPrice() (*num.Uint, error) { return nil, fmt.Errorf("external price is zero") } - externalPrice := externalPriceResponse.Price * math.Pow(10, float64(m.decimalPlaces)) + externalPrice := externalPriceResponse.Price * math.Pow(10, float64(m.marketDecimalPlaces)) externalPriceNum := num.NewUint(uint64(externalPrice)) return externalPriceNum, nil } @@ -437,15 +722,8 @@ func (m *Service) getExampleMarket() *vega.NewMarket { ProbabilityOfTrading: 0.1, }, }, + LpPriceRange: "25", }, - /* - 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(), - },*/ } } @@ -454,32 +732,64 @@ func (m *Service) getExampleProduct() *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, + DataSourceSpecForSettlementData: &vega.DataSourceDefinition{ + SourceType: &vega.DataSourceDefinition_External{ + External: &vega.DataSourceDefinitionExternal{ + SourceType: &vega.DataSourceDefinitionExternal_Oracle{ + Oracle: &vega.DataSourceSpecConfiguration{ + Filters: []*oraclesv1.Filter{ + { + Key: &oraclesv1.PropertyKey{ + Name: "prices.ETH.value", + Type: oraclesv1.PropertyKey_TYPE_INTEGER, + }, + Conditions: []*oraclesv1.Condition{}, + }, + }, + Signers: []*oraclesv1.Signer{ + { + Signer: &oraclesv1.Signer_PubKey{ + PubKey: &oraclesv1.PubKey{ + Key: "0xDEADBEEF", + }, + }, + }, + }, + }, }, - Conditions: []*oraclesv1.Condition{}, }, }, }, - OracleSpecForTradingTermination: &oraclesv1.OracleSpecConfiguration{ - PubKeys: []string{"0xDEADBEEF"}, - Filters: []*oraclesv1.Filter{ - { - Key: &oraclesv1.PropertyKey{ - Name: "trading.termination", - Type: oraclesv1.PropertyKey_TYPE_BOOLEAN, + DataSourceSpecForTradingTermination: &vega.DataSourceDefinition{ + SourceType: &vega.DataSourceDefinition_External{ + External: &vega.DataSourceDefinitionExternal{ + SourceType: &vega.DataSourceDefinitionExternal_Oracle{ + Oracle: &vega.DataSourceSpecConfiguration{ + Filters: []*oraclesv1.Filter{ + { + Key: &oraclesv1.PropertyKey{ + Name: "trading.termination", + Type: oraclesv1.PropertyKey_TYPE_BOOLEAN, + }, + Conditions: []*oraclesv1.Condition{}, + }, + }, + Signers: []*oraclesv1.Signer{ + { + Signer: &oraclesv1.Signer_PubKey{ + PubKey: &oraclesv1.PubKey{ + Key: "0xDEADBEEF", + }, + }, + }, + }, + }, }, - Conditions: []*oraclesv1.Condition{}, }, }, }, - OracleSpecBinding: &vega.OracleSpecToFutureBinding{ - SettlementPriceProperty: "prices.ETH.value", + DataSourceSpecBinding: &vega.DataSourceSpecToFutureBinding{ + SettlementDataProperty: "prices.ETH.value", TradingTerminationProperty: "trading.termination", }, }, diff --git a/market/streamingmarket.go b/market/streamingmarket.go new file mode 100644 index 0000000..c0dc438 --- /dev/null +++ b/market/streamingmarket.go @@ -0,0 +1,313 @@ +package market + +import ( + "context" + "errors" + "fmt" + "time" + + "code.vegaprotocol.io/shared/libs/cache" + sevents "code.vegaprotocol.io/shared/libs/events" + "code.vegaprotocol.io/shared/libs/types" + "code.vegaprotocol.io/vega/core/events" + "code.vegaprotocol.io/vega/logging" + dataapipb "code.vegaprotocol.io/vega/protos/data-node/api/v2" + "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 market struct { + name string + log *logging.Logger + node dataNode + pubKey string + marketID string + store marketStore + busEvProc busEventer +} + +func NewStream(log *logging.Logger, name, pubKey string, node dataNode, pauseCh chan types.PauseSignal) *market { + return &market{ + name: name, + node: node, + pubKey: pubKey, + store: cache.NewMarketStore(), + log: log.Named("MarketStreamer"), + busEvProc: sevents.NewBusEventProcessor(log, node, sevents.WithPauseCh(pauseCh)), + } +} + +func (m *market) Store() marketStore { + return m.store +} + +func (m *market) Subscribe(ctx context.Context, marketID string) error { + m.marketID = marketID + + if err := m.initMarketData(ctx); err != nil { + return fmt.Errorf("failed to get market: %w", err) + } + + if err := m.initOpenVolume(ctx); err != nil { + return fmt.Errorf("failed to get open volume: %w", err) + } + + m.subscribeToMarketEvents() + m.subscribePositions() + m.subscribeToOrderEvents() + + return nil +} + +func (m *market) waitForLiquidityProvision(ctx context.Context, ref string) error { + req := &coreapipb.ObserveEventBusRequest{ + Type: []eventspb.BusEventType{eventspb.BusEventType_BUS_EVENT_TYPE_LIQUIDITY_PROVISION}, + MarketId: m.marketID, + } + + proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { + for _, event := range rsp.GetEvents() { + lp := event.GetLiquidityProvision() + + if lp.Reference != ref { + continue + } + + switch lp.Status { + case vega.LiquidityProvision_STATUS_ACTIVE, vega.LiquidityProvision_STATUS_PENDING: + m.log.With( + logging.String("liquidityProvisionID", lp.Id), + logging.String("liquidityProvisionRef", lp.Reference), + logging.String("liquidityProvisionStatus", lp.Status.String()), + ).Debugf("liquidity provision state") + return true, nil + default: + return true, fmt.Errorf("failed to process liquidity provision: %s, %s, %s", lp.Id, lp.Reference, lp.Status.String()) + } + } + return false, nil + } + + ctx, cancel := context.WithTimeout(ctx, time.Second*450) + defer cancel() + + errCh := m.busEvProc.ProcessEvents(ctx, "Orders: "+m.name, req, proc) + select { + case err := <-errCh: + return err + case <-ctx.Done(): + return fmt.Errorf("timed out waiting for order acceptance") + } +} + +func (m *market) waitForProposalID() (string, error) { + req := &coreapipb.ObserveEventBusRequest{ + Type: []eventspb.BusEventType{eventspb.BusEventType_BUS_EVENT_TYPE_PROPOSAL}, + } + + var proposalID string + + proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { + for _, event := range rsp.GetEvents() { + proposal := event.GetProposal() + + if proposal.PartyId != m.pubKey { + continue + } + + if proposal.State != vega.Proposal_STATE_OPEN { + return true, fmt.Errorf("failed to propose market: %v; code: %s", + proposal.ErrorDetails, proposal.State.String()) + } + + proposalID = proposal.Id + + m.log.With(logging.ProposalID(proposalID)).Info("Received proposal ID") + return true, nil + } + return false, nil + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*450) + defer cancel() + + 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 { + req := &coreapipb.ObserveEventBusRequest{ + Type: []eventspb.BusEventType{eventspb.BusEventType_BUS_EVENT_TYPE_PROPOSAL}, + } + + proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { + for _, event := range rsp.GetEvents() { + proposal := event.GetProposal() + + 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: %v; code: %s", + proposal.ErrorDetails, proposal.State.String()) + } + + m.log.With(logging.ProposalID(proposal.Id)).Info("Proposal was enacted") + return true, nil + } + return false, nil + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*450) + defer cancel() + + 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() { + 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 := cache.FromVegaMD(marketData) + if err != nil { + return false, fmt.Errorf("failed to convert market market: %w", err) + } + + m.store.MarketSet(cache.SetMarketData(md)) + } + return false, nil + } + + m.busEvProc.ProcessEvents(context.Background(), "MarketData: "+m.name, req, proc) +} + +func (m *market) subscribeToOrderEvents() { + req := &coreapipb.ObserveEventBusRequest{ + Type: []eventspb.BusEventType{ + eventspb.BusEventType_BUS_EVENT_TYPE_ORDER, + }, + PartyId: m.pubKey, + MarketId: m.marketID, + } + + proc := func(rsp *coreapipb.ObserveEventBusResponse) (bool, error) { + for _, event := range rsp.Events { + order := event.GetOrder() + + if order.Status == vega.Order_STATUS_REJECTED { + m.log.With( + logging.String("orderID", order.Id), + logging.String("order.status", order.Status.String()), + logging.String("order.PartyId", order.PartyId), + logging.String("order.marketID", order.MarketId), + logging.String("order.reference", order.Reference), + logging.String("order.reason", order.Reason.String()), + ).Warn("Order was rejected") + } + } + return false, nil + } + + m.busEvProc.ProcessEvents(context.Background(), "Order: "+m.name, req, proc) +} + +func (m *market) subscribePositions() { + req := &coreapipb.ObserveEventBusRequest{ + Type: []eventspb.BusEventType{ + eventspb.BusEventType_BUS_EVENT_TYPE_SETTLE_POSITION, + }, + PartyId: m.pubKey, + 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(cache.SetOpenVolume(openVolume)) + return false, nil + } + + m.busEvProc.ProcessEvents(context.Background(), "PositionData: "+m.name, req, proc) +} + +func (m *market) initOpenVolume(ctx context.Context) error { + positions, err := m.getPositions(ctx) + 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(cache.SetOpenVolume(openVolume)) + return nil +} + +// getPositions get this bot's positions. +func (m *market) getPositions(ctx context.Context) ([]*vega.Position, error) { + response, err := m.node.PositionsByParty(ctx, &dataapipb.ListPositionsRequest{ + PartyId: m.pubKey, + MarketId: m.marketID, + }) + if err != nil { + return nil, err + } + + return response, nil +} + +// initMarketData gets the latest info about the market. +func (m *market) initMarketData(ctx context.Context) error { + response, err := m.node.MarketDataByID(ctx, &dataapipb.GetLatestMarketDataRequest{MarketId: m.marketID}) + if err != nil { + return fmt.Errorf("failed to get market (ID:%s): %w", m.marketID, err) + } + + md, err := cache.FromVegaMD(response) + if err != nil { + return fmt.Errorf("failed to convert market market: %w", err) + } + + m.store.MarketSet(cache.SetMarketData(md)) + return nil +} diff --git a/node/datanode.go b/node/datanode.go deleted file mode 100644 index ffb1907..0000000 --- a/node/datanode.go +++ /dev/null @@ -1,282 +0,0 @@ -package node - -import ( - "context" - "fmt" - "log" - "sync" - "time" - - "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" -) - -// DataNode stores state for a Vega Data node. -type DataNode struct { - hosts []string // format: host:port - callTimeout time.Duration - conn *grpc.ClientConn - mu sync.RWMutex - wg sync.WaitGroup - once sync.Once -} - -// NewDataNode returns a new node. -func NewDataNode(hosts []string, callTimeoutMil int) *DataNode { - return &DataNode{ - hosts: hosts, - callTimeout: time.Duration(callTimeoutMil) * time.Millisecond, - } -} - -// MustDialConnection tries to establish a connection to one of the nodes from a list of locations. -// 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) - defer cancel() - - 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.wg.Wait() - n.mu.Lock() - defer n.mu.Unlock() - - if n.conn == nil { - log.Fatalf("Failed to connect to DataNode") - } - }) - - n.wg.Wait() - n.once = sync.Once{} -} - -func (n *DataNode) dialNode(ctx context.Context, host string) { - conn, err := grpc.DialContext( - ctx, - host, - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithBlock(), - ) - if err != nil { - if err != context.Canceled { - log.Printf("Failed to dial node '%s': %s\n", host, err) - } - return - } - - n.mu.Lock() - n.conn = conn - n.mu.Unlock() -} - -func (n *DataNode) Target() string { - return n.conn.Target() -} - -// === CoreService === - -// SubmitTransaction submits a signed v2 transaction. -func (n *DataNode) SubmitTransaction(req *vegaapipb.SubmitTransactionRequest) (*vegaapipb.SubmitTransactionResponse, error) { - msg := "gRPC call failed: SubmitTransaction: %w" - if n == nil { - return nil, fmt.Errorf(msg, e.ErrNil) - } - - if n.conn.GetState() != connectivity.Ready { - 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) - if err != nil { - return nil, fmt.Errorf(msg, e.ErrorDetail(err)) - } - - return response, nil -} - -// LastBlockData gets the latest blockchain data, height, hash and pow parameters. -func (n *DataNode) LastBlockData() (*vegaapipb.LastBlockHeightResponse, error) { - msg := "gRPC call failed: LastBlockData: %w" - if n == nil { - return nil, fmt.Errorf(msg, e.ErrNil) - } - - if n.conn.GetState() != connectivity.Ready { - return nil, fmt.Errorf(msg, e.ErrConnectionNotReady) - } - - 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) (vegaapipb.CoreService_ObserveEventBusClient, error) { - msg := "gRPC call failed: ObserveEventBus: %w" - if n == nil { - return nil, fmt.Errorf(msg, e.ErrNil) - } - - if n.conn == nil || n.conn.GetState() != connectivity.Ready { - return nil, fmt.Errorf(msg, e.ErrConnectionNotReady) - } - - c := vegaapipb.NewCoreServiceClient(n.conn) - // no timeout on streams - client, err := c.ObserveEventBus(ctx) - if err != nil { - return nil, fmt.Errorf(msg, e.ErrorDetail(err)) - } - - return client, nil -} - -// === TradingDataService === - -// PartyAccounts returns accounts for the given party. -func (n *DataNode) PartyAccounts(req *dataapipb.PartyAccountsRequest) (*dataapipb.PartyAccountsResponse, error) { - msg := "gRPC call failed (data-node): PartyAccounts: %w" - if n == nil { - return nil, fmt.Errorf(msg, e.ErrNil) - } - - if n.conn.GetState() != connectivity.Ready { - 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) - if err != nil { - return nil, fmt.Errorf(msg, e.ErrorDetail(err)) - } - - return response, nil -} - -// MarketDataByID returns market data for the specified market. -func (n *DataNode) MarketDataByID(req *dataapipb.MarketDataByIDRequest) (*dataapipb.MarketDataByIDResponse, error) { - msg := "gRPC call failed (data-node): MarketDataByID: %w" - if n == nil { - return nil, fmt.Errorf(msg, e.ErrNil) - } - - if n.conn.GetState() != connectivity.Ready { - 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) - if err != nil { - return nil, fmt.Errorf(msg, e.ErrorDetail(err)) - } - - return response, nil -} - -// Markets returns all markets. -func (n *DataNode) Markets(req *dataapipb.MarketsRequest) (*dataapipb.MarketsResponse, error) { - msg := "gRPC call failed (data-node): Markets: %w" - if n == nil { - return nil, fmt.Errorf(msg, e.ErrNil) - } - - if n.conn.GetState() != connectivity.Ready { - 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) - if err != nil { - return nil, fmt.Errorf(msg, e.ErrorDetail(err)) - } - - return response, nil -} - -// PositionsByParty returns positions for the given party. -func (n *DataNode) PositionsByParty(req *dataapipb.PositionsByPartyRequest) (*dataapipb.PositionsByPartyResponse, error) { - msg := "gRPC call failed (data-node): PositionsByParty: %w" - if n == nil { - return nil, fmt.Errorf(msg, e.ErrNil) - } - - if n.conn.GetState() != connectivity.Ready { - 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) - if err != nil { - return nil, fmt.Errorf(msg, e.ErrorDetail(err)) - } - - return response, nil -} - -// AssetByID returns the specified asset. -func (n *DataNode) AssetByID(req *dataapipb.AssetByIDRequest) (*dataapipb.AssetByIDResponse, error) { - msg := "gRPC call failed (data-node): AssetByID: %w" - if n == nil { - return nil, fmt.Errorf(msg, e.ErrNil) - } - - if n.conn.GetState() != connectivity.Ready { - 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) - if err != nil { - return nil, fmt.Errorf(msg, e.ErrorDetail(err)) - } - - return response, nil -} - -func (n *DataNode) WaitForStateChange(ctx context.Context, state connectivity.State) bool { - return n.conn.WaitForStateChange(ctx, state) -} diff --git a/node/doc.go b/node/doc.go deleted file mode 100644 index 6187ebc..0000000 --- a/node/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package node contains functionality for interacting with Vega data nodes. -package node diff --git a/service/service.go b/service/service.go index aeb5c04..7222378 100644 --- a/service/service.go +++ b/service/service.go @@ -5,28 +5,24 @@ import ( "encoding/json" "fmt" "net/http" - "path" - "runtime" + "net/url" "strings" "sync" "time" "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" - "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" + "code.vegaprotocol.io/shared/libs/account" + "code.vegaprotocol.io/shared/libs/erc20" + "code.vegaprotocol.io/shared/libs/faucet" + "code.vegaprotocol.io/shared/libs/node" + "code.vegaprotocol.io/shared/libs/wallet" + "code.vegaprotocol.io/shared/libs/whale" + "code.vegaprotocol.io/vega/logging" ) // Bot is the generic bot interface. @@ -38,13 +34,6 @@ type Bot interface { 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/service PricingEngine -type PricingEngine interface { - GetPrice(pricecfg ppconfig.PriceConfig) (pi ppservice.PriceResponse, err error) -} - // SimpleResponse is used to show if a request succeeded or not, without giving any more detail. type SimpleResponse struct { Success bool `json:"success"` @@ -58,6 +47,7 @@ type ErrorResponse struct { // Service is the HTTP service. type Service struct { *httprouter.Router + log *logging.Logger config config.Config @@ -68,26 +58,23 @@ type Service struct { } // NewService creates a new service instance (with optional mocks for test purposes). -func NewService(config config.Config) (*Service, error) { +func NewService(log *logging.Logger, config config.Config) (*Service, error) { s := &Service{ Router: httprouter.New(), + log: log.Named("service"), config: config, 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) + whaleService, err := getWhale(log, config) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create whale service: %w", err) } - if err = s.initBots(pricingEngine, whaleService); err != nil { + if err = s.initBots(log, pricingEngine, whaleService); err != nil { return nil, fmt.Errorf("failed to initialise bots: %s", err.Error()) } @@ -97,87 +84,44 @@ func NewService(config config.Config) (*Service, error) { return s, nil } -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) { +func getWhale(log *logging.Logger, 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) - accountStream := data.NewAccountStream("whale", dataNode) + ctx := context.Background() + log.Info("Attempting to connect to a node...") + dataNode.MustDialConnection(ctx) + log.Info("Connected to a node") - tokenService, err := token.NewService(config.Token, config.Whale.WalletPubKey) + faucetURL, err := url.Parse(config.Whale.FaucetURL) if err != nil { - return nil, fmt.Errorf("failed to setup token service: %w", err) + return nil, fmt.Errorf("failed to parse faucet URL: %w", err) } - provider := whale.NewProvider( - dataNode, - tokenService, - faucetService, - config.Whale, - ) - - accountService := account.NewAccountService("whale", "", accountStream, provider) - - whaleService := whale.NewService( - dataNode, - whaleWallet, - accountService, - config.Whale, - ) + faucetService := faucet.New(*faucetURL) + whaleWallet, err := wallet.NewWalletV2Service(log, config.Whale.Wallet) + if err != nil { + return nil, fmt.Errorf("failed to create wallet: %w", err) + } - if err = whaleService.Start(context.Background()); err != nil { - return nil, fmt.Errorf("failed to start whale service: %w", err) + tokenService, err := erc20.NewService(log, config.Token) + if err != nil { + return nil, fmt.Errorf("failed to setup token service: %w", err) } - return whaleService, nil + + streamWhale := account.NewStream(log, "provider-whale", dataNode, nil) + provider := whale.NewProvider(log, dataNode, tokenService, streamWhale, config.Whale) + pubKey := whaleWallet.PublicKey() + accountService := account.NewService(log, "whale", pubKey, streamWhale, provider) + return whale.NewService(log, dataNode, whaleWallet, accountService, streamWhale, faucetService, config.Whale), nil } func (s *Service) addRoutes() { - s.GET("/status", s.Status) + s.GET("/status", s.Status) // TODO s.GET("/traders-settlement", s.TradersSettlement) + // TODO: add bots to create and maintain more markets } func (s *Service) getServer() *http.Server { @@ -195,32 +139,26 @@ func (s *Service) getServer() *http.Server { // Start starts the HTTP server, and returns the server's exit error (if any). func (s *Service) Start() error { - log.WithFields(log.Fields{ - "listen": s.config.Server.Listen, - }).Info("Listening") + s.log.With(logging.String("listen", s.config.Server.Listen)).Info("Listening") return s.server.ListenAndServe() } // Stop stops the HTTP service. func (s *Service) Stop() { wait := time.Duration(3) * time.Second - log.WithFields(log.Fields{ - "listen": s.config.Server.Listen, - }).Info("Shutting down") + s.log.With(logging.String("listen", s.config.Server.Listen)).Info("Shutting down") ctx, cancel := context.WithTimeout(context.Background(), wait) defer cancel() err := s.server.Shutdown(ctx) if err != nil { - log.WithFields(log.Fields{ - "err": err.Error(), - }).Info("Server shutdown failed") + s.log.Error("Server shutdown failed", logging.Error(err)) } } -func (s *Service) initBots(pricingEngine PricingEngine, whaleService types.CoinProvider) error { +func (s *Service) initBots(log *logging.Logger, pricingEngine types.PricingEngine, whaleService account.CoinProvider) error { for _, botcfg := range s.config.Bots { - if err := s.initBot(pricingEngine, botcfg, whaleService); err != nil { + if err := s.initBot(log, pricingEngine, botcfg, whaleService); err != nil { return fmt.Errorf("failed to initialise bot '%s': %w", botcfg.Name, err) } } @@ -228,10 +166,12 @@ func (s *Service) initBots(pricingEngine PricingEngine, whaleService types.CoinP return nil } -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") +func (s *Service) initBot(log *logging.Logger, pricingEngine types.PricingEngine, botcfg config.BotConfig, whaleService account.CoinProvider) error { + log = log.Named(fmt.Sprintf("bot: %s", botcfg.Name)) + + log.With(logging.String("strategy", botcfg.StrategyDetails.String())).Debug("read strategy config") - b, err := bot.New(botcfg, s.config, pricingEngine, whaleService) + b, err := bot.New(log, botcfg, s.config, pricingEngine, whaleService) if err != nil { return fmt.Errorf("failed to create bot %s: %w", botcfg.Name, err) } @@ -241,9 +181,7 @@ func (s *Service) initBot(pricingEngine PricingEngine, botcfg config.BotConfig, s.bots[botcfg.Name] = b - log.WithFields(log.Fields{ - "name": botcfg.Name, - }).Info("Initialised bot") + log.Info("bot initialised") if err = b.Start(); err != nil { return fmt.Errorf("failed to start bot %s: %w", botcfg.Name, err) diff --git a/token/erc20_service.go b/token/erc20_service.go deleted file mode 100644 index 25308dd..0000000 --- a/token/erc20_service.go +++ /dev/null @@ -1,265 +0,0 @@ -package token - -import ( - "context" - "fmt" - "math/big" - "time" - - 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" - - "code.vegaprotocol.io/liqbot/config" - "code.vegaprotocol.io/liqbot/types/num" -) - -type Service struct { - client *vgethereum.Client - vegaPubKey string - erc20BridgeAddress common.Address - stakingBridgeAddress common.Address - syncTimeout *time.Duration - log *log.Entry -} - -func NewService(conf *config.TokenConfig, vegaPubKey string) (*Service, error) { - ctx := context.Background() - - var syncTimeout *time.Duration - if conf.SyncTimeoutSec != 0 { - syncTimeoutVal := time.Duration(conf.SyncTimeoutSec) * time.Second - syncTimeout = &syncTimeoutVal - } - - 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), - syncTimeout: syncTimeout, - log: log.WithFields(log.Fields{"component": "TokenService"}), - }, nil -} - -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) - } - - vegaToken, err := s.client.NewBaseTokenSession(ctx, ownerPrivateKey, common.HexToAddress(vegaTokenAddress), s.syncTimeout) - if err != nil { - return nil, fmt.Errorf("failed to create vega token: %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, vegaPubKey, 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") - - 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, 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 nil, fmt.Errorf("failed to create ERC20 token: %w", err) - } - - erc20bridge, err := s.client.NewERC20BridgeSession(ctx, ownerPrivateKey, s.erc20BridgeAddress, s.syncTimeout) - if err != nil { - return nil, fmt.Errorf("failed to create staking bridge: %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, minted); err != nil { - return nil, fmt.Errorf("failed to approve and deposit token on erc20 bridge: %w", err) - } - - s.log.Debug("Deposit request sent") - - 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(ctx context.Context, token token, address common.Address, amount *big.Int) (*big.Int, error) { - name, err := token.Name() - if err != nil { - return nil, fmt.Errorf("failed to get name of token: %w", err) - } - - s.log.WithFields( - log.Fields{ - "token": name, - "amount": amount, - "address": address, - }).Debug("Minting new token") - - var tx *types.Transaction - 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{"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) - } - - 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 { - name, err := token.Name() - if err != nil { - return fmt.Errorf("failed to get name of token: %w", err) - } - - s.log.WithFields( - log.Fields{ - "token": name, - "amount": amount, - "address": bridge.Address(), - }).Debug("Approving token") - - if _, err = token.ApproveSync(bridge.Address(), amount); err != nil { - return fmt.Errorf("failed to approve token: %w", err) - } - - s.log.WithFields( - log.Fields{ - "token": name, - "amount": amount, - "address": bridge.Address(), - }).Debug("Depositing asset") - - vegaPubKeyByte32, err := vgethereum.HexStringToByte32Array(s.vegaPubKey) - if err != nil { - return err - } - - if _, err = bridge.DepositAssetSync(token.Address(), amount, vegaPubKeyByte32); err != nil { - return fmt.Errorf("failed to deposit asset: %w", err) - } - - s.log.WithFields( - log.Fields{ - "token": name, - "amount": amount, - "address": bridge.Address(), - }).Debug("Token deposited") - - return nil -} - -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) - } - - s.log.WithFields( - log.Fields{ - "token": name, - "amount": amount, - "address": bridge.Address(), - }).Debug("Approving token") - - if _, err = token.ApproveSync(bridge.Address(), amount); err != nil { - return fmt.Errorf("failed to approve token: %w", err) - } - - vegaPubKeyByte32, err := vgethereum.HexStringToByte32Array(vegaPubKey) - if err != nil { - return err - } - - s.log.WithFields( - log.Fields{ - "token": name, - "amount": amount, - "vegaPubKey": vegaPubKey, - }).Debug("Staking asset") - - if _, err = bridge.Stake(amount, vegaPubKeyByte32); err != nil { - return fmt.Errorf("failed to stake asset: %w", err) - } - - s.log.WithFields( - log.Fields{ - "token": name, - "amount": amount, - "vegaPubKey": vegaPubKey, - }).Debug("Token staked") - - return nil -} diff --git a/token/erc20_service_test.go b/token/erc20_service_test.go deleted file mode 100644 index 6007028..0000000 --- a/token/erc20_service_test.go +++ /dev/null @@ -1,47 +0,0 @@ -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) { - t.Skip() - - 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 deleted file mode 100644 index f38d08a..0000000 --- a/token/faucet_service.go +++ /dev/null @@ -1,65 +0,0 @@ -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) - if err != nil { - return err - } - - 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 index 8163482..e22e944 100644 --- a/types/interfaces.go +++ b/types/interfaces.go @@ -1,13 +1,9 @@ package types import ( - "context" - 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" + "code.vegaprotocol.io/shared/libs/types" ) // Bot is the generic bot interface. @@ -17,6 +13,7 @@ type Bot interface { Start() error Stop() GetTraderDetails() string + PauseChannel() chan types.PauseSignal } // PricingEngine is the source of price information from the price proxy. @@ -25,8 +22,3 @@ type Bot interface { type PricingEngine interface { GetPrice(pricecfg ppconfig.PriceConfig) (pi ppservice.PriceResponse, err error) } - -type CoinProvider interface { - 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/types/num/chain.go b/types/num/chain.go deleted file mode 100644 index 326185f..0000000 --- a/types/num/chain.go +++ /dev/null @@ -1,141 +0,0 @@ -package num - -import "github.com/shopspring/decimal" - -// UChain ... -type UChain struct { - z *Uint -} - -// UintChain returns a Uint that supports chainable operations -// The Uint passed to the constructor is the value that will be updated, so be careful -// Things like x := NewUint(0).Add(y, z) -// x.Mul(x, foo) -// can be written as: -// x := UintChain(NewUint(0)).Add(y, z).Mul(foo).Get(). -func UintChain(z *Uint) *UChain { - return &UChain{ - z: z, - } -} - -// Get gets the result of the chained operation (the value of the wrapped uint). -func (c *UChain) Get() *Uint { - return c.z -} - -// Add is equivalent to AddSum. -func (c *UChain) Add(vals ...*Uint) *UChain { - if len(vals) == 0 { - return c - } - c.z.AddSum(vals...) - return c -} - -// Sub subtracts any numbers from the chainable value. -func (c *UChain) Sub(vals ...*Uint) *UChain { - for _, v := range vals { - c.z.Sub(c.z, v) - } - return c -} - -// Mul multiplies the current value by x. -func (c *UChain) Mul(x *Uint) *UChain { - c.z.Mul(c.z, x) - return c -} - -// Div divides the current value by x. -func (c *UChain) Div(x *Uint) *UChain { - c.z.Div(c.z, x) - return c -} - -// DecRounding ... -type DecRounding int - -// constants. -const ( - DecFloor DecRounding = iota - DecRound - DecCeil -) - -// DChain ... -type DChain struct { - d Decimal -} - -// UintDecChain returns a chainable decimal from a given uint -// this moves the conversion stuff out from the caller. -func UintDecChain(u *Uint) *DChain { - // @TODO once the updates to the decimal file are merged, call the coversion function from that file. - return &DChain{ - d: decimal.NewFromBigInt(u.u.ToBig(), 0), - } -} - -// DecChain offers the same chainable interface for decimals. -func DecChain(d Decimal) *DChain { - return &DChain{ - d: d, - } -} - -// Get returns the final value. -func (d *DChain) Get() Decimal { - return d.d -} - -// GetUint returns the decimal as a uint, returns true on overflow -// pass in type of rounding to apply -// not that the rounding does not affect the underlying decimal value -// rounding is applied to a copy only. -func (d *DChain) GetUint(round DecRounding) (*Uint, bool) { - v := d.d - switch round { - case DecFloor: - v = v.Floor() - case DecCeil: - v = v.Ceil() - case DecRound: - v = v.Round(0) // we're converting to Uint, so round to 0 places. - } - return UintFromBig(v.BigInt()) -} - -// Add adds any number of decimals together. -func (d *DChain) Add(vals ...Decimal) *DChain { - for _, v := range vals { - d.d = d.d.Add(v) - } - return d -} - -// Sub subtracts any number of decimals from the chainable value. -func (d *DChain) Sub(vals ...Decimal) *DChain { - for _, v := range vals { - d.d = d.d.Sub(v) - } - return d -} - -// Mul multiplies, obviously. -func (d *DChain) Mul(x Decimal) *DChain { - d.d = d.d.Mul(x) - return d -} - -// Div divides. -func (d *DChain) Div(x Decimal) *DChain { - d.d = d.d.Div(x) - return d -} - -// DivRound divides with a specified precision. -func (d *DChain) DivRound(x Decimal, precision int32) *DChain { - d.d = d.d.DivRound(x, precision) - return d -} diff --git a/types/num/chain_test.go b/types/num/chain_test.go deleted file mode 100644 index 1ff48c5..0000000 --- a/types/num/chain_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package num_test - -import ( - "testing" - - "code.vegaprotocol.io/liqbot/types/num" - - "github.com/shopspring/decimal" - "github.com/stretchr/testify/require" -) - -func TestUintChain(t *testing.T) { - start := num.NewUint(100) - // use the non-chainable API to get the expected result - vals := map[uint64]*num.Uint{ - 1: nil, - 2: nil, - 3: nil, - 4: nil, - 5: nil, - 6: nil, - 7: nil, - 8: nil, - 9: nil, - } - for k := range vals { - vals[k] = num.NewUint(k) - } - expect := num.Sum(start, vals[3], vals[5]) // some stuff - expect.AddSum(vals[9], vals[1]) - expect.Sub(expect, vals[7]) - expect.Mul(expect, vals[4]) - expect.Div(expect, vals[2]) - - // write as one-liner - got := num.UintChain(num.Sum(start, vals[3], vals[5])).Add(vals[9], vals[1]).Sub(vals[7]).Mul(vals[4]).Div(vals[2]).Get() - - require.Equal(t, expect.String(), got.String()) -} - -func TestDecChain(t *testing.T) { - start := decimal.NewFromInt(100) - vals := map[int64]num.Decimal{ - 1: decimal.Zero, - 2: decimal.Zero, - 3: decimal.Zero, - 4: decimal.Zero, - 5: decimal.Zero, - 6: decimal.Zero, - 7: decimal.Zero, - 8: decimal.Zero, - 9: decimal.Zero, - } - for k := range vals { - vals[k] = decimal.NewFromInt(k) - } - - expect := start.Add(vals[3]).Add(vals[5]).Add(vals[9].Add(vals[1])).Sub(vals[7]).Mul(vals[4]).Div(vals[2]) - - // we could just assign got at the end, but we want to check for rounding - chain := num.DecChain(start).Add(vals[3], vals[5], vals[9].Add(vals[1])).Sub(vals[7]).Mul(vals[4]).Div(vals[2]) - - require.Equal(t, expect, chain.Get()) - - t.Run("compare rounding division", func(t *testing.T) { - expect = expect.DivRound(vals[8], 1) // 27.75 -> 27.8 - chain.DivRound(vals[8], 1) - require.Equal(t, expect, chain.Get()) - }) - - t.Run("compare Uint rounding", func(t *testing.T) { - floor, round, ceil := expect.Floor(), expect.Round(0), expect.Ceil() - uf, _ := chain.GetUint(num.DecFloor) - require.Equal(t, floor.String(), uf.String()) - ur, _ := chain.GetUint(num.DecRound) - require.Equal(t, round.String(), ur.String()) - uc, _ := chain.GetUint(num.DecCeil) - require.Equal(t, ceil.String(), uc.String()) - }) -} - -// same as the TestDecChain, only we're starting with a Uint. -func TestDecChainFromUint(t *testing.T) { - sInt := int64(100) - start := decimal.NewFromInt(sInt) - uStart := num.NewUint(uint64(sInt)) - vals := map[int64]num.Decimal{ - 1: decimal.Zero, - 2: decimal.Zero, - 3: decimal.Zero, - 4: decimal.Zero, - 5: decimal.Zero, - 6: decimal.Zero, - 7: decimal.Zero, - 8: decimal.Zero, - 9: decimal.Zero, - } - for k := range vals { - vals[k] = decimal.NewFromInt(k) - } - - expect := start.Add(vals[3]).Add(vals[5]).Add(vals[9].Add(vals[1])).Sub(vals[7]).Mul(vals[4]).Div(vals[2]) - - // we could just assign got at the end, but we want to check for rounding - chain := num.UintDecChain(uStart).Add(vals[3], vals[5], vals[9].Add(vals[1])).Sub(vals[7]).Mul(vals[4]).Div(vals[2]) - - require.Equal(t, expect, chain.Get()) - - t.Run("compare rounding division", func(t *testing.T) { - expect = expect.DivRound(vals[8], 1) // 27.75 -> 27.8 - chain.DivRound(vals[8], 1) - require.Equal(t, expect, chain.Get()) - }) - - t.Run("compare Uint rounding", func(t *testing.T) { - floor, round, ceil := expect.Floor(), expect.Round(0), expect.Ceil() - uf, _ := chain.GetUint(num.DecFloor) - require.Equal(t, floor.String(), uf.String()) - ur, _ := chain.GetUint(num.DecRound) - require.Equal(t, round.String(), ur.String()) - uc, _ := chain.GetUint(num.DecCeil) - require.Equal(t, ceil.String(), uc.String()) - }) -} diff --git a/types/num/decimal.go b/types/num/decimal.go deleted file mode 100644 index 1d8257f..0000000 --- a/types/num/decimal.go +++ /dev/null @@ -1,71 +0,0 @@ -package num - -import ( - "math/big" - - "github.com/shopspring/decimal" -) - -// Decimal is a shopspring.Decimal. -type Decimal = decimal.Decimal - -var ( - dzero = decimal.Zero - maxDecimal = decimal.NewFromBigInt(maxU256, 0) -) - -// DecimalZero ... -func DecimalZero() Decimal { - return dzero -} - -// MaxDecimal ... -func MaxDecimal() Decimal { - return maxDecimal -} - -// NewDecimalFromFloat ... -func NewDecimalFromFloat(f float64) Decimal { - return decimal.NewFromFloat(f) -} - -// NewDecimalFromBigInt ... -func NewDecimalFromBigInt(value *big.Int, exp int32) Decimal { - return decimal.NewFromBigInt(value, exp) -} - -// DecimalFromUint ... -func DecimalFromUint(u *Uint) Decimal { - return decimal.NewFromUint(&u.u) -} - -// DecimalFromInt64 ... -func DecimalFromInt64(i int64) Decimal { - return decimal.NewFromInt(i) -} - -// DecimalFromFloat ... -func DecimalFromFloat(v float64) Decimal { - return decimal.NewFromFloat(v) -} - -// DecimalFromString ... -func DecimalFromString(s string) (Decimal, error) { - return decimal.NewFromString(s) -} - -// MaxD ... -func MaxD(a, b Decimal) Decimal { - if a.GreaterThan(b) { - return a - } - return b -} - -// MinD ... -func MinD(a, b Decimal) Decimal { - if a.LessThan(b) { - return a - } - return b -} diff --git a/types/num/int.go b/types/num/int.go deleted file mode 100644 index 4b7fb58..0000000 --- a/types/num/int.go +++ /dev/null @@ -1,197 +0,0 @@ -package num - -// Int a wrapper to a signed big int. -type Int struct { - // The unsigned version of the integer. - U *Uint - // The sign of the integer true = positive, false = negative. - s bool -} - -// IntFromUint ... -func IntFromUint(u *Uint, s bool) *Int { - cp := &Int{ - s: s, - U: u.Clone(), - } - 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 -// true if < 0 -// false if >= 0. -func (i *Int) IsNegative() bool { - return !i.s && !i.U.IsZero() -} - -// IsPositive tests if the stored value is positive -// true if > 0 -// false if <= 0. -func (i *Int) IsPositive() bool { - return i.s && !i.U.IsZero() -} - -// IsZero tests if the stored value is zero -// true if == 0. -func (i *Int) IsZero() bool { - return i.U.IsZero() -} - -// FlipSign changes the sign of the number from - to + and back again. -func (i *Int) FlipSign() { - i.s = !i.s -} - -// Clone creates a copy of the object so nothing is shared. -func (i Int) Clone() *Int { - return &Int{ - U: i.U.Clone(), - s: i.s, - } -} - -// GT returns if i > o. -func (i Int) GT(o *Int) bool { - if i.IsNegative() { - if o.IsPositive() || o.IsZero() { - return false - } - return i.U.LT(o.U) - } - if i.IsPositive() { - if o.IsZero() || o.IsNegative() { - return true - } - return i.U.GT(o.U) - } - return o.IsNegative() -} - -// LT returns if i < o. -func (i Int) LT(o *Int) bool { - if i.IsNegative() { - if o.IsPositive() || o.IsZero() { - return true - } - return i.U.GT(o.U) - } - if i.IsPositive() { - if o.IsZero() || o.IsNegative() { - return false - } - return i.U.LT(o.U) - } - return o.IsPositive() -} - -// Int64 ... -func (i Int) Int64() int64 { - val := int64(i.U.Uint64()) - if i.IsNegative() { - return -val - } - return val -} - -// String returns a string version of the number. -func (i Int) String() string { - val := i.U.String() - if i.IsNegative() { - return "-" + val - } - return val -} - -// Add will add the passed in value to the base value -// i = i + a. -func (i *Int) Add(a *Int) *Int { - // Handle cases where we have a zero. - if a.IsZero() { - return i - } - if i.IsZero() { - i.U.Set(a.U) - i.s = a.s - return i - } - - // Handle the easy cases were both are the same sign. - if i.IsPositive() && a.IsPositive() { - i.U.Add(i.U, a.U) - return i - } - - if i.IsNegative() && a.IsNegative() { - i.U.Add(i.U, a.U) - return i - } - - // Now the cases where the signs are different. - if i.IsNegative() { - if i.U.GTE(a.U) { - // abs(i) >= a. - i.U.Sub(i.U, a.U) - } else { - // abs(i) < a. - i.U.Sub(a.U, i.U) - i.s = true - } - return i - } - if i.U.GTE(a.U) { - // i >= abs(a). - i.U.Sub(i.U, a.U) - } else { - // i < abs(a). - i.U.Sub(a.U, i.U) - i.s = false - } - return i -} - -// Sub will subtract the passed in value from the base value -// i = i - a. -func (i *Int) Sub(a *Int) *Int { - a.FlipSign() - i.Add(a) - a.FlipSign() - return i -} - -// AddSum adds all of the parameters to i -// i = i + a + b + c. -func (i *Int) AddSum(vals ...*Int) *Int { - for _, x := range vals { - i.Add(x) - } - return i -} - -// SubSum subtracts all of the parameters from i -// i = i - a - b - c. -func (i *Int) SubSum(vals ...*Int) *Int { - for _, x := range vals { - i.Sub(x) - } - return i -} - -// NewInt creates a new Int with the value of the -// int64 passed as a parameter. -func NewInt(val int64) *Int { - if val < 0 { - return &Int{ - U: NewUint(uint64(-val)), - s: false, - } - } - return &Int{ - U: NewUint(uint64(val)), - s: true, - } -} diff --git a/types/num/int_test.go b/types/num/int_test.go deleted file mode 100644 index b8267a6..0000000 --- a/types/num/int_test.go +++ /dev/null @@ -1,238 +0,0 @@ -package num_test - -import ( - "math/rand" - "testing" - - "code.vegaprotocol.io/liqbot/types/num" - "github.com/stretchr/testify/assert" -) - -func TestInt256Constructors(t *testing.T) { - // Positive number - var value1 int64 = 42 - n := num.NewInt(value1) - assert.Equal(t, uint64(value1), n.U.Uint64()) - assert.Equal(t, true, n.IsPositive()) - assert.Equal(t, false, n.IsNegative()) - assert.Equal(t, false, n.IsZero()) - - // Negative number - var value2 int64 = -42 - n = num.NewInt(value2) - assert.Equal(t, uint64(-value2), n.U.Uint64()) - assert.Equal(t, false, n.IsPositive()) - assert.Equal(t, true, n.IsNegative()) - assert.Equal(t, false, n.IsZero()) - - // Zero - var value3 int64 = 0 - n = num.NewInt(value3) - assert.Equal(t, uint64(value3), n.U.Uint64()) - assert.Equal(t, false, n.IsPositive()) - assert.Equal(t, false, n.IsNegative()) - assert.Equal(t, true, n.IsZero()) -} - -func TestIntFromUint(t *testing.T) { - n := num.NewUint(100) - - // Test making a positive value - i := num.IntFromUint(n, true) - assert.Equal(t, uint64(100), i.U.Uint64()) - assert.Equal(t, true, i.IsPositive()) - assert.Equal(t, false, i.IsNegative()) - assert.Equal(t, false, i.IsZero()) - - // Test making a negative value - i = num.IntFromUint(n, false) - assert.Equal(t, uint64(100), i.U.Uint64()) - assert.Equal(t, false, i.IsPositive()) - assert.Equal(t, true, i.IsNegative()) - assert.Equal(t, false, i.IsZero()) -} - -func TestFlipSign(t *testing.T) { - n := num.NewInt(100) - assert.Equal(t, uint64(100), n.U.Uint64()) - assert.Equal(t, true, n.IsPositive()) - assert.Equal(t, false, n.IsNegative()) - assert.Equal(t, false, n.IsZero()) - - n.FlipSign() - assert.Equal(t, uint64(100), n.U.Uint64()) - assert.Equal(t, false, n.IsPositive()) - assert.Equal(t, true, n.IsNegative()) - assert.Equal(t, false, n.IsZero()) -} - -func TestClone(t *testing.T) { - n := num.NewInt(100) - n2 := n.Clone() - - n2.FlipSign() - assert.Equal(t, true, n.IsPositive()) - assert.Equal(t, true, n2.IsNegative()) - - n.AddSum(num.NewInt(50)) - assert.Equal(t, uint64(150), n.U.Uint64()) - assert.Equal(t, uint64(100), n2.U.Uint64()) -} - -func TestGT(t *testing.T) { - mid := num.NewInt(0) - low := num.NewInt(-10) - high := num.NewInt(10) - - assert.Equal(t, true, mid.GT(low)) - assert.Equal(t, false, mid.GT(high)) - assert.Equal(t, false, low.GT(mid)) - assert.Equal(t, false, low.GT(high)) - assert.Equal(t, true, high.GT(mid)) - assert.Equal(t, true, high.GT(low)) - - assert.Equal(t, false, mid.GT(mid)) - assert.Equal(t, false, low.GT(low)) - assert.Equal(t, false, high.GT(high)) -} - -func TestLT(t *testing.T) { - mid := num.NewInt(0) - low := num.NewInt(-10) - high := num.NewInt(10) - - assert.Equal(t, false, mid.LT(low)) - assert.Equal(t, true, mid.LT(high)) - assert.Equal(t, true, low.LT(mid)) - assert.Equal(t, true, low.LT(high)) - assert.Equal(t, false, high.LT(mid)) - assert.Equal(t, false, high.LT(low)) - - assert.Equal(t, false, mid.LT(mid)) - assert.Equal(t, false, low.LT(low)) - assert.Equal(t, false, high.LT(high)) -} - -func TestString(t *testing.T) { - mid := num.NewInt(0) - low := num.NewInt(-10) - high := num.NewInt(10) - - assert.Equal(t, "0", mid.String()) - assert.Equal(t, "-10", low.String()) - assert.Equal(t, "10", high.String()) -} - -func TestAdd(t *testing.T) { - // Add positive to zero - i := num.NewInt(0) - i.Add(num.NewInt(10)) - assert.Equal(t, "10", i.String()) - - // Add negative to zero - i = num.NewInt(0) - i.Add(num.NewInt(-10)) - assert.Equal(t, "-10", i.String()) - - // Add zero to negative - i = num.NewInt(0) - i.Add(num.NewInt(0)) - assert.Equal(t, "0", i.String()) - - // Add zero to positive - i = num.NewInt(10) - i.Add(num.NewInt(0)) - assert.Equal(t, "10", i.String()) - - // Add zero to zero - i = num.NewInt(0) - i.Add(num.NewInt(0)) - assert.Equal(t, "0", i.String()) - - // Add positive to positive - i = num.NewInt(10) - i.Add(num.NewInt(15)) - assert.Equal(t, "25", i.String()) - - // Add negative to negative - i = num.NewInt(-10) - i.Add(num.NewInt(-15)) - assert.Equal(t, "-25", i.String()) - - // Add positive to negative (no sign flip) - i = num.NewInt(-15) - i.Add(num.NewInt(10)) - assert.Equal(t, "-5", i.String()) - - // Add positive to negative (sign flip) - i = num.NewInt(-10) - i.Add(num.NewInt(15)) - assert.Equal(t, "5", i.String()) - - // Add negative to positive (no sign flip) - i = num.NewInt(10) - i.Add(num.NewInt(-5)) - assert.Equal(t, "5", i.String()) - - // Add negative to positive (sign flip) - i = num.NewInt(10) - i.Add(num.NewInt(-15)) - assert.Equal(t, "-5", i.String()) -} - -func TestAddSum(t *testing.T) { - num1 := num.NewInt(10) - num2 := num.NewInt(20) - num3 := num.NewInt(-15) - num4 := num.NewInt(-30) - num5 := num.NewInt(10) - - result := num1.AddSum(num2, num3, num4, num5) - assert.Equal(t, "-5", result.String()) -} - -func TestSubSum(t *testing.T) { - num1 := num.NewInt(10) - num2 := num.NewInt(20) - num3 := num.NewInt(-15) - num4 := num.NewInt(-30) - num5 := num.NewInt(10) - - result := num1.SubSum(num2, num3, num4, num5) - assert.Equal(t, "25", result.String()) -} - -func TestBruteForce(t *testing.T) { - t.Run("brute force adds", testAddLoop) - t.Run("brute force subs", testSubLoop) -} - -func testAddLoop(t *testing.T) { - for c := 0; c < 10000; c++ { - num1 := rand.Int63n(100) - 50 - num2 := rand.Int63n(100) - 50 - - bigNum1 := num.NewInt(num1) - bigNum2 := num.NewInt(num2) - - bigNum1.Add(bigNum2) - - assert.Equal(t, num1+num2, bigNum1.Int64()) - // fmt.Println(num1, num2, num1-num2, bigNum1.String()) - } -} - -func testSubLoop(t *testing.T) { - for c := 0; c < 10000; c++ { - num1 := rand.Int63n(100) - 50 - num2 := rand.Int63n(100) - 50 - - bigNum1 := num.NewInt(num1) - bigNum2 := num.NewInt(num2) - - bigNum1.Sub(bigNum2) - - assert.Equal(t, num1-num2, bigNum1.Int64()) - // fmt.Println(num1, num2, num1-num2, bigNum1.String()) - } -} diff --git a/types/num/uint.go b/types/num/uint.go deleted file mode 100644 index c806347..0000000 --- a/types/num/uint.go +++ /dev/null @@ -1,421 +0,0 @@ -// TODO: shared libs? -package num - -import ( - "fmt" - "math/big" - - "github.com/holiman/uint256" -) - -var ( - // max uint256 value. - big1 = big.NewInt(1) - maxU256 = new(big.Int).Sub(new(big.Int).Lsh(big1, 256), big1) - - // initialise max variable. - maxUint = setMaxUint() - zero = NewUint(0) -) - -// Uint A wrapper for a big unsigned int. -type Uint struct { - u uint256.Int -} - -// NewUint creates a new Uint with the value of the -// uint64 passed as a parameter. -func NewUint(val uint64) *Uint { - return &Uint{*uint256.NewInt(val)} -} - -// Zero ... -func Zero() *Uint { - return zero.Clone() -} - -// only called once, to initialise maxUint. -func setMaxUint() *Uint { - b, _ := UintFromBig(maxU256) - return b -} - -// MaxUint returns max value for uint256. -func MaxUint() *Uint { - return maxUint.Clone() -} - -// Min returns the smallest of the 2 numbers. -func Min(a, b *Uint) *Uint { - if a.LT(b) { - return a.Clone() - } - return b.Clone() -} - -// Max returns the largest of the 2 numbers. -func Max(a, b *Uint) *Uint { - if a.GT(b) { - return a.Clone() - } - return b.Clone() -} - -// UintFromBig construct a new Uint with a big.Int -// returns true if overflow happened. -func UintFromBig(b *big.Int) (*Uint, bool) { - u, ok := uint256.FromBig(b) - // ok means an overflow happened - if ok { - return NewUint(0), true - } - return &Uint{*u}, false -} - -// UintFromBytes allows for the conversion from Uint.Bytes() back to a Uint. -func UintFromBytes(b []byte) *Uint { - u := &Uint{ - u: uint256.Int{}, - } - u.u.SetBytes(b) - return u -} - -// UintFromDecimal returns a decimal version of the Uint, setting the bool to true if overflow occurred. -func UintFromDecimal(d Decimal) (*Uint, bool) { - u, ok := d.Uint() - return &Uint{*u}, ok -} - -// ToDecimal returns the value of the Uint as a Decimal. -func (u *Uint) ToDecimal() Decimal { - return DecimalFromUint(u) -} - -// UintFromString created a new Uint from a string -// interpreted using the give base. -// A big.Int is used to read the string, so -// all error related to big.Int parsing applied here. -// will return true if an error/overflow happened. -func UintFromString(str string, base int) (*Uint, bool) { - b, ok := big.NewInt(0).SetString(str, base) - if !ok { - return NewUint(0), true - } - return UintFromBig(b) -} - -// Sum just removes the need to write num.NewUint(0).Sum(x, y, z) -// so you can write num.Sum(x, y, z) instead, equivalent to x + y + z. -func Sum(vals ...*Uint) *Uint { - return NewUint(0).AddSum(vals...) -} - -// Set ... -func (u *Uint) Set(oth *Uint) *Uint { - u.u.Set(&oth.u) - return u -} - -// SetUint64 ... -func (u *Uint) SetUint64(val uint64) *Uint { - u.u.SetUint64(val) - return u -} - -// Uint64 ... -func (u Uint) Uint64() uint64 { - return u.u.Uint64() -} - -// BigInt ... -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) - retVal, _ := d.Float64() - return retVal -} - -// Add will add x and y then store the result -// into u -// this is equivalent to: -// `u = x + y` -// u is returned for convenience, no -// new variable is created. -func (u *Uint) Add(x, y *Uint) *Uint { - u.u.Add(&x.u, &y.u) - return u -} - -// AddSum adds multiple values at the same time to a given 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 -} - -// AddOverflow will subtract y to x then store the result -// into u -// this is equivalent to: -// `u = x - y` -// u is returned for convenience, no -// new variable is created. -// False is returned if an overflow occurred. -func (u *Uint) AddOverflow(x, y *Uint) (*Uint, bool) { - _, ok := u.u.AddOverflow(&x.u, &y.u) - return u, ok -} - -// Sub will subtract y from x then store the result -// into u -// this is equivalent to: -// `u = x - y` -// u is returned for convenience, no -// new variable is created. -func (u *Uint) Sub(x, y *Uint) *Uint { - u.u.Sub(&x.u, &y.u) - return u -} - -// SubOverflow will subtract y to x then store the result -// into u -// this is equivalent to: -// `u = x - y` -// u is returned for convenience, no -// new variable is created. -// False is returned if an overflow occurred. -func (u *Uint) SubOverflow(x, y *Uint) (*Uint, bool) { - _, ok := u.u.SubOverflow(&x.u, &y.u) - return u, ok -} - -// Delta will subtract y from x and store the result -// unless x-y overflowed, in which case the neg field will be set -// and the result of y - x is set instead. -func (u *Uint) Delta(x, y *Uint) (*Uint, bool) { - // y is the bigger value - swap the two. - if y.GT(x) { - _ = u.Sub(y, x) - return u, true - } - _ = u.Sub(x, y) - return u, false -} - -// DeltaI will subtract y from x and store the result. -func (u *Uint) DeltaI(x, y *Uint) *Int { - d, s := u.Delta(x, y) - return IntFromUint(d, !s) -} - -// Mul will multiply x and y then store the result -// into u -// this is equivalent to: -// `u = x * y` -// u is returned for convenience, no -// new variable is created. -func (u *Uint) Mul(x, y *Uint) *Uint { - u.u.Mul(&x.u, &y.u) - return u -} - -// Div will divide x by y then store the result -// into u -// this is equivalent to: -// `u = x / y` -// u is returned for convenience, no -// new variable is created. -func (u *Uint) Div(x, y *Uint) *Uint { - u.u.Div(&x.u, &y.u) - return u -} - -// Mod sets u to the modulus x%y for y != 0 and returns u. -// If y == 0, u is set to 0. -func (u *Uint) Mod(x, y *Uint) *Uint { - u.u.Mod(&x.u, &y.u) - return u -} - -// Exp ... -func (u *Uint) Exp(x, y *Uint) *Uint { - u.u.Exp(&x.u, &y.u) - return u -} - -// LT with check if the value stored in u is -// lesser than oth -// this is equivalent to: -// `u < oth`. -func (u Uint) LT(oth *Uint) bool { - return u.u.Lt(&oth.u) -} - -// LTUint64 with check if the value stored in u is -// lesser than oth -// this is equivalent to: -// `u < oth`. -func (u Uint) LTUint64(oth uint64) bool { - return u.u.LtUint64(oth) -} - -// LTE with check if the value stored in u is -// lesser than or equal to oth -// this is equivalent to: -// `u <= oth`. -func (u Uint) LTE(oth *Uint) bool { - return u.u.Lt(&oth.u) || u.u.Eq(&oth.u) -} - -// LTEUint64 with check if the value stored in u is -// lesser than or equal to oth -// this is equivalent to: -// `u <= oth`. -func (u Uint) LTEUint64(oth uint64) bool { - return u.u.LtUint64(oth) || u.EQUint64(oth) -} - -// EQ with check if the value stored in u is -// equal to oth -// this is equivalent to: -// `u == oth`. -func (u Uint) EQ(oth *Uint) bool { - return u.u.Eq(&oth.u) -} - -// EQUint64 with check if the value stored in u is -// equal to oth -// this is equivalent to: -// `u == oth`. -func (u Uint) EQUint64(oth uint64) bool { - return u.u.Eq(uint256.NewInt(oth)) -} - -// NEQ with check if the value stored in u is -// different than oth -// this is equivalent to: -// `u != oth`. -func (u Uint) NEQ(oth *Uint) bool { - return !u.u.Eq(&oth.u) -} - -// NEQUint64 with check if the value stored in u is -// different than oth -// this is equivalent to: -// `u != oth`. -func (u Uint) NEQUint64(oth uint64) bool { - return !u.u.Eq(uint256.NewInt(oth)) -} - -// GT with check if the value stored in u is -// greater than oth -// this is equivalent to: -// `u > oth`. -func (u Uint) GT(oth *Uint) bool { - return u.u.Gt(&oth.u) -} - -// GTUint64 with check if the value stored in u is -// greater than oth -// this is equivalent to: -// `u > oth`. -func (u Uint) GTUint64(oth uint64) bool { - return u.u.GtUint64(oth) -} - -// GTE with check if the value stored in u is -// greater than or equal to oth -// this is equivalent to: -// `u >= oth`. -func (u Uint) GTE(oth *Uint) bool { - return u.u.Gt(&oth.u) || u.u.Eq(&oth.u) -} - -// GTEUint64 with check if the value stored in u is -// greater than or equal to oth -// this is equivalent to: -// `u >= oth`. -func (u Uint) GTEUint64(oth uint64) bool { - return u.u.GtUint64(oth) || u.EQUint64(oth) -} - -// IsZero return whether u == 0 or not. -func (u Uint) IsZero() bool { - return u.u.IsZero() -} - -// IsNegative returns whether the value is < 0. -func (u Uint) IsNegative() bool { - return u.u.Sign() == -1 -} - -// Copy create a copy of the uint -// this if the equivalent to: -// u = x. -func (u *Uint) Copy(x *Uint) *Uint { - u.u = x.u - return u -} - -// Clone create copy of this value -// this is the equivalent to: -// x := u. -func (u Uint) Clone() *Uint { - return &Uint{u.u} -} - -// Hex returns the hexadecimal representation -// of the stored value. -func (u Uint) Hex() string { - return u.u.Hex() -} - -// String returns the stored value as a string -// this is internally using big.Int.String(). -func (u Uint) String() string { - return u.u.ToBig().String() -} - -// Format implement fmt.Formatter. -func (u Uint) Format(s fmt.State, ch rune) { - u.u.Format(s, ch) -} - -// Bytes return the internal representation -// of the Uint as [32]bytes, BigEndian encoded -// array. -func (u Uint) Bytes() [32]byte { - return u.u.Bytes32() -} - -// UintToUint64 convert a uint to uint64 -// return 0 if nil. -func UintToUint64(u *Uint) uint64 { - if u != nil { - return u.Uint64() - } - return 0 -} - -// UintToString convert a uint to uint64 -// return "0" if nil. -func UintToString(u *Uint) string { - if u != nil { - return u.String() - } - return "0" -} diff --git a/types/num/uint_test.go b/types/num/uint_test.go deleted file mode 100644 index 926a7a3..0000000 --- a/types/num/uint_test.go +++ /dev/null @@ -1,182 +0,0 @@ -package num_test - -import ( - "fmt" - "math/big" - "testing" - - "code.vegaprotocol.io/liqbot/types/num" - - "github.com/stretchr/testify/assert" -) - -func TestUint256Constructors(t *testing.T) { - var expected uint64 = 42 - - t.Run("test from uint64", func(t *testing.T) { - n := num.NewUint(expected) - assert.Equal(t, expected, n.Uint64()) - }) - - t.Run("test from string", func(t *testing.T) { - n, ok := num.UintFromString("42", 10) - assert.False(t, ok) - assert.Equal(t, expected, n.Uint64()) - }) - - t.Run("test from big", func(t *testing.T) { - n, ok := num.UintFromBig(big.NewInt(int64(expected))) - assert.False(t, ok) - assert.Equal(t, expected, n.Uint64()) - }) -} - -func TestUint256Clone(t *testing.T) { - var ( - expect1 uint64 = 42 - expect2 uint64 = 84 - first = num.NewUint(expect1) - second = first.Clone() - ) - - assert.Equal(t, expect1, first.Uint64()) - assert.Equal(t, expect1, second.Uint64()) - - // now we change second value, and ensure 1 hasn't changed - second.Add(second, num.NewUint(42)) - - assert.Equal(t, expect1, first.Uint64()) - assert.Equal(t, expect2, second.Uint64()) -} - -func TestUint256Copy(t *testing.T) { - var ( - expect1 uint64 = 42 - expect2 uint64 = 84 - first = num.NewUint(expect1) - second = num.NewUint(expect2) - ) - - assert.Equal(t, expect1, first.Uint64()) - assert.Equal(t, expect2, second.Uint64()) - - // now we copy first into second - second.Copy(first) - - // we check that first and second have the same value - assert.Equal(t, expect1, first.Uint64()) - assert.Equal(t, expect1, second.Uint64()) - - // and now we will update first to have expect2 value - // and make sure it haven't changed second - first.SetUint64(expect2) - assert.Equal(t, expect2, first.Uint64()) - assert.Equal(t, expect1, second.Uint64()) -} - -func TestUInt256IsZero(t *testing.T) { - zero := num.NewUint(0) - assert.True(t, zero.IsZero()) -} - -func TestUint256Print(t *testing.T) { - var ( - expected = "42" - n = num.NewUint(42) - ) - - assert.Equal(t, expected, fmt.Sprintf("%v", n)) -} - -func TestUint256Delta(t *testing.T) { - data := []struct { - x, y, z uint64 - neg bool - }{ - { - x: 1234, - y: 1230, - z: 4, - neg: false, - }, - { - x: 1230, - y: 1234, - z: 4, - neg: true, - }, - } - for _, set := range data { - exp := num.NewUint(set.z) - x, y := num.NewUint(set.x), num.NewUint(set.y) - got, neg := num.NewUint(0).Delta(x, y) - assert.Equal(t, exp.String(), got.String()) - assert.Equal(t, set.neg, neg) - } -} - -func TestSum(t *testing.T) { - data := []struct { - x, y, z uint64 - exp uint64 - }{ - { - x: 1, - y: 2, - z: 3, - exp: 1 + 2 + 3, - }, - { - x: 123, - y: 456, - z: 789, - exp: 123 + 456 + 789, - }, - } - for _, set := range data { - x, y, z := num.NewUint(set.x), num.NewUint(set.y), num.NewUint(set.z) - exp := num.NewUint(set.exp) - zero := num.NewUint(0) - fSum := num.Sum(x, y, z) - assert.Equal(t, exp.String(), fSum.String()) - ptr := zero.AddSum(x, y, z) - assert.Equal(t, exp.String(), zero.String()) - assert.Equal(t, zero, ptr) - // compare to manual: - manual := num.NewUint(0) - manual = manual.Add(manual, x) - assert.NotEqual(t, exp.String(), manual.String(), "manual x only") - manual = manual.Add(manual, y) - assert.NotEqual(t, exp.String(), manual.String(), "manual x+y only") - manual = manual.Add(manual, z) - assert.Equal(t, exp.String(), manual.String()) - } -} - -func TestDeferDoCopy(t *testing.T) { - var ( - expected1 uint64 = 42 - expected2 uint64 = 84 - n1 = num.NewUint(42) - ) - - n2 := *n1 - - assert.Equal(t, expected1, n1.Uint64()) - assert.Equal(t, expected1, n2.Uint64()) - - n2.SetUint64(expected2) - assert.Equal(t, expected1, n1.Uint64()) - assert.Equal(t, expected2, n2.Uint64()) -} - -func TestDeltaI(t *testing.T) { - n1 := num.NewUint(10) - n2 := num.NewUint(25) - - r1 := num.Zero().DeltaI(n1.Clone(), n2.Clone()) - assert.Equal(t, "-15", r1.String()) - - r2 := num.Zero().DeltaI(n2.Clone(), n1.Clone()) - assert.Equal(t, "15", r2.String()) -} diff --git a/types/store.go b/types/store.go deleted file mode 100644 index a79ce9e..0000000 --- a/types/store.go +++ /dev/null @@ -1,79 +0,0 @@ -package types - -type BalanceStore struct { - balance cache[Balance] -} - -func NewBalanceStore() *BalanceStore { - return &BalanceStore{ - balance: newCache[Balance](), - } -} - -func (s *BalanceStore) Balance() Balance { - return s.balance.get() -} - -func (s *BalanceStore) BalanceSet(sets ...func(*Balance)) { - s.balance.set(sets...) -} - -type MarketStore struct { - market cache[MarketData] -} - -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 *MarketStore) 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]() cache[T] { - var 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 c -} diff --git a/types/store_test.go b/types/store_test.go deleted file mode 100644 index 7035812..0000000 --- a/types/store_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package types - -import ( - "testing" - - "code.vegaprotocol.io/liqbot/types/num" - vt "code.vegaprotocol.io/vega/core/types" -) - -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 deleted file mode 100644 index c36f6ef..0000000 --- a/types/types.go +++ /dev/null @@ -1,180 +0,0 @@ -package types - -import ( - "fmt" - - "code.vegaprotocol.io/vega/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 - 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 -} - -func (b Balance) Total() *num.Uint { - 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) - } -} - -// nolint:nonamedreturns -func fromPtr[T any](ptr *T) (t T) { - if ptr == nil { - return - } - return *ptr -} - -type PauseSignal struct { - From string - Pause bool -} diff --git a/types/vega.go b/types/vega.go deleted file mode 100644 index 89dd046..0000000 --- a/types/vega.go +++ /dev/null @@ -1,9 +0,0 @@ -package types - -import "code.vegaprotocol.io/vega/protos/vega" - -// Shape is the top level definition of a liquidity shape. -type Shape struct { - Sells []*vega.LiquidityOrder - Buys []*vega.LiquidityOrder -} diff --git a/types/wallet.go b/types/wallet.go deleted file mode 100644 index d1167fb..0000000 --- a/types/wallet.go +++ /dev/null @@ -1,13 +0,0 @@ -package types - -type Meta struct { - Key string `json:"key"` - Value string `json:"value"` -} - -type Key struct { - Pub string `json:"pub"` - Algo string `json:"algo"` - Tainted bool `json:"tainted"` - Meta []Meta `json:"meta"` -} diff --git a/util/num.go b/util/num.go deleted file mode 100644 index 6d065c5..0000000 --- a/util/num.go +++ /dev/null @@ -1,16 +0,0 @@ -package util - -import ( - "errors" - - "code.vegaprotocol.io/liqbot/types/num" -) - -func ConvertUint256(valueStr string) (*num.Uint, error) { - value, overflowed := num.UintFromString(valueStr, 10) - if overflowed { - return nil, errors.New("invalid uint256, needs to be base 10") - } - - return value, nil -} diff --git a/wallet/client.go b/wallet/client.go deleted file mode 100644 index a0689ed..0000000 --- a/wallet/client.go +++ /dev/null @@ -1,295 +0,0 @@ -// TODO: move to a shared lib? -package wallet - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "strings" - - "github.com/golang/protobuf/jsonpb" - - "code.vegaprotocol.io/liqbot/types" - walletpb "code.vegaprotocol.io/vega/protos/vega/wallet/v1" -) - -type Client struct { - walletURL string - client *http.Client - token string - name string - pass string - pubKey string // TODO: setup wallet and set this -} - -func NewClient(walletURL string) *Client { - return &Client{ - walletURL: walletURL, - client: http.DefaultClient, - } -} - -func (c *Client) CreateWallet(ctx context.Context, name, passphrase string) error { - postBody, _ := json.Marshal(struct { - Wallet string `json:"wallet"` - Passphrase string `json:"passphrase"` - }{ - Wallet: name, - Passphrase: passphrase, - }) - - req, err := http.NewRequestWithContext( - ctx, - http.MethodPost, - fmt.Sprintf("%s/api/v1/wallets", c.walletURL), - bytes.NewBuffer(postBody), - ) - if err != nil { - return fmt.Errorf("failed to create request: %v", err) - } - - resp, err := c.client.Do(req) - if err != nil { - return fmt.Errorf("failed to create wallet at vegawallet API: %v", err) - } - - defer func() { _ = resp.Body.Close() }() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %s", err) - } - - sb := string(body) - if strings.Contains(sb, "error") { - return fmt.Errorf("error response: %s", sb) - } - - type walletResponse struct { - Token string `json:"token"` - } - - result := new(walletResponse) - if err := json.Unmarshal([]byte(sb), result); err != nil { - return fmt.Errorf("failed to parse response: %s", err) - } - - c.token = result.Token - c.name = name - c.pass = passphrase - - return nil -} - -type tokenResponse struct { - Token string `json:"token"` -} - -func (c *Client) LoginWallet(ctx context.Context, name, passphrase string) error { - postBody, _ := json.Marshal(struct { - Wallet string `json:"wallet"` - Passphrase string `json:"passphrase"` - }{ - Wallet: name, - Passphrase: passphrase, - }) - - req, err := http.NewRequestWithContext( - ctx, - http.MethodPost, - fmt.Sprintf("%s/api/v1/auth/token", c.walletURL), - bytes.NewBuffer(postBody), - ) - if err != nil { - return fmt.Errorf("failed to create request: %v", err) - } - - resp, err := c.client.Do(req) - if err != nil { - return fmt.Errorf("failed to login with vegawallet API: %v", err) - } - - defer func() { _ = resp.Body.Close() }() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %s", err) - } - - sb := string(body) - if strings.Contains(sb, "error") { - return errors.New(sb) - } - - result := new(tokenResponse) - - if err := json.Unmarshal([]byte(sb), &result); err != nil { - return fmt.Errorf("failed to parse response: %s", err) - } - - c.token = result.Token - c.name = name - c.pass = passphrase - - return nil -} - -type keyPairResponse struct { - Key *types.Key `json:"key"` -} - -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"` - }{ - Passphrase: passphrase, - Meta: meta, - }) - - req, err := http.NewRequestWithContext( - ctx, - http.MethodPost, - fmt.Sprintf("%s/api/v1/keys", c.walletURL), - bytes.NewBuffer(postBody)) - if err != nil { - return nil, fmt.Errorf("failed to create request: %v", err) - } - - req.Header.Add("Authorization", "Bearer "+c.token) - - resp, err := c.client.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to create request: %v", err) - } - - defer func() { _ = resp.Body.Close() }() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response body: %s", err) - } - - sb := string(body) - if strings.Contains(sb, "error") { - return nil, fmt.Errorf("error response: %s", sb) - } - - result := new(keyPairResponse) - - if err := json.Unmarshal([]byte(sb), result); err != nil { - return nil, fmt.Errorf("failed to parse response: %s", err) - } - - return result.Key, nil -} - -type listKeysResponse struct { - Keys []types.Key `json:"keys"` -} - -func (c *Client) ListPublicKeys(ctx context.Context) ([]string, error) { - req, err := http.NewRequestWithContext( - ctx, - http.MethodGet, - fmt.Sprintf("%s/api/v1/keys", c.walletURL), - nil, - ) - if err != nil { - return nil, fmt.Errorf("failed to create request: %v", err) - } - - req.Header.Add("Authorization", "Bearer "+c.token) - - resp, err := c.client.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to create request: %v", err) - } - - defer func() { _ = resp.Body.Close() }() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response body: %s", err) - } - - sb := string(body) - if strings.Contains(sb, "error") { - return nil, fmt.Errorf("error response: %s", sb) - } - - result := new(listKeysResponse) - - if err := json.Unmarshal([]byte(sb), result); err != nil { - return nil, fmt.Errorf("failed to parse response: %s", err) - } - - pubKeys := make([]string, len(result.Keys)) - - for i := range result.Keys { - pubKeys[i] = result.Keys[i].Pub - } - - return pubKeys, nil -} - -type SignTxRequest struct { - PubKey string `json:"pubKey"` - 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 - - data, err := m.MarshalToString(request) - if err != nil { - return fmt.Errorf("couldn't marshal input data: %w", err) - } - - req, err := http.NewRequestWithContext( - ctx, - http.MethodPost, - fmt.Sprintf("%s/api/v1/command/sync", c.walletURL), - bytes.NewBuffer([]byte(data)), - ) - if err != nil { - return fmt.Errorf("failed to create request: %v", err) - } - - req.Header.Add("Authorization", "Bearer "+c.token) - - resp, err := c.client.Do(req) - if err != nil { - return fmt.Errorf("failed to create request: %v", err) - } - - defer func() { _ = resp.Body.Close() }() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read response body: %s", err) - } - - sb := string(body) - if strings.Contains(sb, "error") { - if sb == `{"error":"session not found"}` { - if err := c.LoginWallet(ctx, c.name, c.pass); err != nil { - return fmt.Errorf("failed to login with vegawallet API: %v", err) - } - } - return fmt.Errorf("error response: %s", sb) - } - - return nil -} diff --git a/whale/interfaces.go b/whale/interfaces.go deleted file mode 100644 index d943adc..0000000 --- a/whale/interfaces.go +++ /dev/null @@ -1,37 +0,0 @@ -package whale - -import ( - "context" - - "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 { - 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 { - 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) -} - -type faucetClient interface { - Mint(ctx context.Context, assetID string, amount *num.Uint) 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, 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 deleted file mode 100644 index f81f4b8..0000000 --- a/whale/provider.go +++ /dev/null @@ -1,352 +0,0 @@ -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" - v1 "code.vegaprotocol.io/vega/protos/vega/events/v1" -) - -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 -} - -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{ - "component": "WhaleProvider", - "whaleName": config.WalletName, - }), - } - - go func() { - 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) (v1.BusEventType, error) { - p.ensureBalanceCh <- ensureBalanceRequest{ - ctx: ctx, - name: receiverName, - address: receiverAddress, - assetID: assetID, - amount: amount, - } - // 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) 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 { - 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 nil - } - - err := p.deposit(ctx, "Whale", p.walletPubKey, assetID, amount) - if err == nil { - return nil - } - - 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 err - } - p.setPendingDeposit(assetID, deposit) - return nil -} - -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) - } 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 nil -} - -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, 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, 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 deleted file mode 100644 index 5a0e470..0000000 --- a/whale/service.go +++ /dev/null @@ -1,150 +0,0 @@ -package whale - -import ( - "context" - "fmt" - - log "github.com/sirupsen/logrus" - - "code.vegaprotocol.io/liqbot/config" - "code.vegaprotocol.io/liqbot/types" - "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" -) - -type Service struct { - node dataNode - wallet walletClient - account accountService - - walletName string - walletPassphrase string - walletPubKey string - log *log.Entry -} - -func NewService( - dataNode dataNode, - wallet walletClient, - account accountService, - config *config.WhaleConfig, -) *Service { - return &Service{ - node: dataNode, - wallet: wallet, - - account: account, - walletPubKey: config.WalletPubKey, - walletName: config.WalletName, - walletPassphrase: config.WalletPassphrase, - log: log.WithFields(log.Fields{ - "component": "Whale", - "name": config.WalletName, - }), - } -} - -func (w *Service) Start(ctx context.Context) error { - w.log.Info("Starting whale service...") - - pauseCh := make(chan types.PauseSignal) - - go func() { - for p := range pauseCh { - w.log.Infof("Whale service paused: %v; from %s", p.Pause, p.From) - } - }() - - 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) (v12.BusEventType, error) { - w.log.Debugf("Top up for '%s' ...", receiverName) - - evt := v12.BusEventType_BUS_EVENT_TYPE_TRANSFER - - if assetID == "" { - return evt, fmt.Errorf("assetID is empty for bot '%s'", receiverName) - } - - if receiverAddress == w.walletPubKey { - return evt, fmt.Errorf("whale and bot address cannot be the same") - } - - go func() { - 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, - 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 { - 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 evt, nil -} - -func (w *Service) StakeAsync(ctx context.Context, receiverAddress, assetID string, amount *num.Uint) error { - w.log.Debugf("Staking for '%s' ...", receiverAddress) - - if err := w.account.StakeAsync(ctx, receiverAddress, assetID, amount); err != nil { - return err - } - - return nil -} diff --git a/whale/service_test.go b/whale/service_test.go deleted file mode 100644 index bd5c4ff..0000000 --- a/whale/service_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package whale - -import ( - "context" - "testing" - - "code.vegaprotocol.io/liqbot/account" - "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) { - t.Skip() - - wKey := "6baf7809b6143d4be4a5b641fcef29947aeaa1ab3805c5442de8a31a3449078f" - wName := "w00" - wPassphrase := "123" - - 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 - } - - dk := map[string]string{ - "993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede": "a37f4c2a678aefb5037bf415a826df1540b330b7e471aa54184877ba901b9ef0", - } - - conf := &config.WhaleConfig{ - WalletPubKey: wKey, - WalletName: wName, - WalletPassphrase: wPassphrase, - OwnerPrivateKeys: dk, - SyncTimeoutSec: 100, - } - - fc := token.NewFaucetService("http://localhost:1790", wKey) - - cp := NewProvider(dn, es, fc, conf) - - ds := data.NewAccountStream("test", dn) - as := account.NewAccountService("test", "asset", ds, cp) - - s := NewService(dn, wc, as, conf) - - ctx := context.Background() - - if err := s.Start(ctx); err != nil { - t.Errorf("Start() error = %s", err) - return - } - - 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) - } - }) - } -}