diff --git a/api/exchange.go b/api/exchange.go index be753c1de..8354438dd 100644 --- a/api/exchange.go +++ b/api/exchange.go @@ -2,9 +2,12 @@ package api import ( "fmt" + "math" + "github.com/stellar/go/build" hProtocol "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/txnbuild" + "github.com/stellar/go/xdr" "github.com/stellar/kelp/model" ) @@ -222,11 +225,92 @@ type Balance struct { // ExchangeShim is the interface we use as a generic API for all crypto exchanges type ExchangeShim interface { - SubmitOps(ops []txnbuild.Operation, asyncCallback func(hash string, e error)) error - SubmitOpsSynch(ops []txnbuild.Operation, asyncCallback func(hash string, e error)) error // forced synchronous version of SubmitOps + SubmitOps(ops []build.TransactionMutator, asyncCallback func(hash string, e error)) error + SubmitOpsSynch(ops []build.TransactionMutator, asyncCallback func(hash string, e error)) error // forced synchronous version of SubmitOps GetBalanceHack(asset hProtocol.Asset) (*Balance, error) LoadOffersHack() ([]hProtocol.Offer, error) Constrainable OrderbookFetcher FillTrackable } + +// ConvertOperation2TM is a temporary adapter to support transitioning from the old Go SDK to the new SDK without having to bump the major version +func ConvertOperation2TM(ops []txnbuild.Operation) []build.TransactionMutator { + muts := []build.TransactionMutator{} + for _, o := range ops { + var mob build.ManageOfferBuilder + if mso, ok := o.(*txnbuild.ManageSellOffer); ok { + mob = build.ManageOffer( + false, + build.Amount(mso.Amount), + build.Rate{ + Selling: build.Asset{Code: mso.Selling.GetCode(), Issuer: mso.Selling.GetIssuer(), Native: mso.Selling.IsNative()}, + Buying: build.Asset{Code: mso.Buying.GetCode(), Issuer: mso.Buying.GetIssuer(), Native: mso.Buying.IsNative()}, + Price: build.Price(mso.Price), + }, + build.OfferID(mso.OfferID), + ) + if mso.SourceAccount != nil { + mob.Mutate(build.SourceAccount{AddressOrSeed: mso.SourceAccount.GetAccountID()}) + } + } else { + panic(fmt.Sprintf("could not convert txnbuild.Operation to build.TransactionMutator: %v\n", o)) + } + muts = append(muts, mob) + } + return muts +} + +// ConvertTM2Operation is a temporary adapter to support transitioning from the old Go SDK to the new SDK without having to bump the major version +func ConvertTM2Operation(muts []build.TransactionMutator) []txnbuild.Operation { + ops := []txnbuild.Operation{} + for _, m := range muts { + var mso *txnbuild.ManageSellOffer + if mob, ok := m.(build.ManageOfferBuilder); ok { + mso = convertMOB2MSO(mob) + } else if mob, ok := m.(*build.ManageOfferBuilder); ok { + mso = convertMOB2MSO(*mob) + } else { + panic(fmt.Sprintf("could not convert build.TransactionMutator to txnbuild.Operation: %v (type=%T)\n", m, m)) + } + ops = append(ops, mso) + } + return ops +} + +func convertMOB2MSO(mob build.ManageOfferBuilder) *txnbuild.ManageSellOffer { + mso := &txnbuild.ManageSellOffer{ + Amount: fmt.Sprintf("%.7f", float64(mob.MO.Amount)/math.Pow(10, 7)), + OfferID: int64(mob.MO.OfferId), + Price: fmt.Sprintf("%.7f", float64(mob.MO.Price.N)/float64(mob.MO.Price.D)), + } + if mob.O.SourceAccount != nil { + mso.SourceAccount = &txnbuild.SimpleAccount{ + AccountID: mob.O.SourceAccount.Address(), + } + } + + if mob.MO.Buying.Type == xdr.AssetTypeAssetTypeNative { + mso.Buying = txnbuild.NativeAsset{} + } else { + var tipe, code, issuer string + mob.MO.Buying.MustExtract(&tipe, &code, &issuer) + mso.Buying = txnbuild.CreditAsset{ + Code: code, + Issuer: issuer, + } + } + + if mob.MO.Selling.Type == xdr.AssetTypeAssetTypeNative { + mso.Selling = txnbuild.NativeAsset{} + } else { + var tipe, code, issuer string + mob.MO.Selling.MustExtract(&tipe, &code, &issuer) + mso.Selling = txnbuild.CreditAsset{ + Code: code, + Issuer: issuer, + } + } + + return mso +} diff --git a/api/strategy.go b/api/strategy.go index 226fc46fe..7a0de795f 100644 --- a/api/strategy.go +++ b/api/strategy.go @@ -1,25 +1,25 @@ package api import ( + "github.com/stellar/go/build" hProtocol "github.com/stellar/go/protocols/horizon" - "github.com/stellar/go/txnbuild" "github.com/stellar/kelp/model" ) // Strategy represents some logic for a bot to follow while doing market making type Strategy interface { - PruneExistingOffers(buyingAOffers []hProtocol.Offer, sellingAOffers []hProtocol.Offer) ([]txnbuild.Operation, []hProtocol.Offer, []hProtocol.Offer) + PruneExistingOffers(buyingAOffers []hProtocol.Offer, sellingAOffers []hProtocol.Offer) ([]build.TransactionMutator, []hProtocol.Offer, []hProtocol.Offer) PreUpdate(maxAssetA float64, maxAssetB float64, trustA float64, trustB float64) error - UpdateWithOps(buyingAOffers []hProtocol.Offer, sellingAOffers []hProtocol.Offer) ([]txnbuild.Operation, error) + UpdateWithOps(buyingAOffers []hProtocol.Offer, sellingAOffers []hProtocol.Offer) ([]build.TransactionMutator, error) PostUpdate() error GetFillHandlers() ([]FillHandler, error) } // SideStrategy represents a strategy on a single side of the orderbook type SideStrategy interface { - PruneExistingOffers(offers []hProtocol.Offer) ([]txnbuild.Operation, []hProtocol.Offer) + PruneExistingOffers(offers []hProtocol.Offer) ([]build.TransactionMutator, []hProtocol.Offer) PreUpdate(maxAssetA float64, maxAssetB float64, trustA float64, trustB float64) error - UpdateWithOps(offers []hProtocol.Offer) (ops []txnbuild.Operation, newTopOffer *model.Number, e error) + UpdateWithOps(offers []hProtocol.Offer) (ops []build.TransactionMutator, newTopOffer *model.Number, e error) PostUpdate() error GetFillHandlers() ([]FillHandler, error) } diff --git a/cmd/trade.go b/cmd/trade.go index b34118ed5..d2e7969de 100644 --- a/cmd/trade.go +++ b/cmd/trade.go @@ -697,7 +697,7 @@ func deleteAllOffersAndExit( l.Infof("created %d operations to delete offers\n", len(dOps)) if len(dOps) > 0 { - e := exchangeShim.SubmitOpsSynch(dOps, func(hash string, e error) { + e := exchangeShim.SubmitOpsSynch(api.ConvertOperation2TM(dOps), func(hash string, e error) { if e != nil { logger.Fatal(l, e) return diff --git a/glide.lock b/glide.lock index 8ebc07fae..f080d8e24 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 93f84071fccdfc0c10e17f1808daa7461f29f9bf3bca6dec05b4fbb9068ba487 -updated: 2019-08-30T04:28:19.867973525-07:00 +hash: fea1924a14461af263f5435ea1fc0038870ad2b82b4fe6dec3e679cfea5b5bc9 +updated: 2019-08-30T05:39:34.749230033-07:00 imports: - name: cloud.google.com/go version: 24f9f82cf8c5ed3c0303f34f76876365ce742aad @@ -106,6 +106,7 @@ imports: version: a599ed95b928a7bdcee21cee4999efd05e43c2df subpackages: - amount + - build - clients/horizonclient - crc16 - hash diff --git a/glide.yaml b/glide.yaml index 95508bf0c..a191792b2 100644 --- a/glide.yaml +++ b/glide.yaml @@ -11,6 +11,7 @@ import: - package: github.com/stellar/go version: a599ed95b928a7bdcee21cee4999efd05e43c2df subpackages: + - build - clients/horizonclient - support/config - support/errors diff --git a/plugins/batchedExchange.go b/plugins/batchedExchange.go index 2af7fe220..001835a0c 100644 --- a/plugins/batchedExchange.go +++ b/plugins/batchedExchange.go @@ -10,6 +10,7 @@ import ( "math/rand" + "github.com/stellar/go/build" hProtocol "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/txnbuild" "github.com/stellar/kelp/api" @@ -174,12 +175,14 @@ func (b BatchedExchange) GetLatestTradeCursor() (interface{}, error) { } // SubmitOpsSynch is the forced synchronous version of SubmitOps below (same for batchedExchange) -func (b BatchedExchange) SubmitOpsSynch(ops []txnbuild.Operation, asyncCallback func(hash string, e error)) error { +func (b BatchedExchange) SubmitOpsSynch(ops []build.TransactionMutator, asyncCallback func(hash string, e error)) error { return b.SubmitOps(ops, asyncCallback) } // SubmitOps performs any finalization or submission step needed by the exchange -func (b BatchedExchange) SubmitOps(ops []txnbuild.Operation, asyncCallback func(hash string, e error)) error { +func (b BatchedExchange) SubmitOps(opsOld []build.TransactionMutator, asyncCallback func(hash string, e error)) error { + ops := api.ConvertTM2Operation(opsOld) + var e error b.commands, e = b.Ops2Commands(ops, b.baseAsset, b.quoteAsset) if e != nil { diff --git a/plugins/composeStrategy.go b/plugins/composeStrategy.go index 4eff925fd..359f3e1f4 100644 --- a/plugins/composeStrategy.go +++ b/plugins/composeStrategy.go @@ -6,9 +6,9 @@ import ( "github.com/stellar/kelp/api" "github.com/stellar/kelp/model" + "github.com/stellar/go/build" hProtocol "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/support/errors" - "github.com/stellar/go/txnbuild" "github.com/stellar/kelp/support/utils" ) @@ -39,7 +39,7 @@ func makeComposeStrategy( } // PruneExistingOffers impl -func (s *composeStrategy) PruneExistingOffers(buyingAOffers []hProtocol.Offer, sellingAOffers []hProtocol.Offer) ([]txnbuild.Operation, []hProtocol.Offer, []hProtocol.Offer) { +func (s *composeStrategy) PruneExistingOffers(buyingAOffers []hProtocol.Offer, sellingAOffers []hProtocol.Offer) ([]build.TransactionMutator, []hProtocol.Offer, []hProtocol.Offer) { pruneOps1, newBuyingAOffers := s.buyStrat.PruneExistingOffers(buyingAOffers) pruneOps2, newSellingAOffers := s.sellStrat.PruneExistingOffers(sellingAOffers) pruneOps1 = append(pruneOps1, pruneOps2...) @@ -71,7 +71,7 @@ func (s *composeStrategy) PreUpdate(maxAssetBase float64, maxAssetQuote float64, func (s *composeStrategy) UpdateWithOps( buyingAOffers []hProtocol.Offer, sellingAOffers []hProtocol.Offer, -) ([]txnbuild.Operation, error) { +) ([]build.TransactionMutator, error) { // buy side, flip newTopBuyPrice because it will be inverted from this parent strategy's context of base/quote buyOps, newTopBuyPriceInverted, e1 := s.buyStrat.UpdateWithOps(buyingAOffers) newTopBuyPrice := model.InvertNumber(newTopBuyPriceInverted) @@ -79,7 +79,7 @@ func (s *composeStrategy) UpdateWithOps( sellOps, _, e2 := s.sellStrat.UpdateWithOps(sellingAOffers) // check for errors - ops := []txnbuild.Operation{} + ops := []build.TransactionMutator{} if e1 != nil && e2 != nil { return ops, fmt.Errorf("errors on both sides: buying (= %s) and selling (= %s)", e1, e2) } else if e1 != nil { diff --git a/plugins/deleteSideStrategy.go b/plugins/deleteSideStrategy.go index 3d07d4b66..350419e91 100644 --- a/plugins/deleteSideStrategy.go +++ b/plugins/deleteSideStrategy.go @@ -3,6 +3,7 @@ package plugins import ( "log" + "github.com/stellar/go/build" hProtocol "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/txnbuild" "github.com/stellar/kelp/api" @@ -33,14 +34,14 @@ func makeDeleteSideStrategy( } // PruneExistingOffers impl -func (s *deleteSideStrategy) PruneExistingOffers(offers []hProtocol.Offer) ([]txnbuild.Operation, []hProtocol.Offer) { +func (s *deleteSideStrategy) PruneExistingOffers(offers []hProtocol.Offer) ([]build.TransactionMutator, []hProtocol.Offer) { log.Printf("deleteSideStrategy: deleting %d offers\n", len(offers)) pruneOps := []txnbuild.Operation{} for i := 0; i < len(offers); i++ { pOp := s.sdex.DeleteOffer(offers[i]) pruneOps = append(pruneOps, &pOp) } - return pruneOps, []hProtocol.Offer{} + return api.ConvertOperation2TM(pruneOps), []hProtocol.Offer{} } // PreUpdate impl @@ -49,8 +50,8 @@ func (s *deleteSideStrategy) PreUpdate(maxAssetBase float64, maxAssetQuote float } // UpdateWithOps impl -func (s *deleteSideStrategy) UpdateWithOps(offers []hProtocol.Offer) (ops []txnbuild.Operation, newTopOffer *model.Number, e error) { - return []txnbuild.Operation{}, nil, nil +func (s *deleteSideStrategy) UpdateWithOps(offers []hProtocol.Offer) (ops []build.TransactionMutator, newTopOffer *model.Number, e error) { + return []build.TransactionMutator{}, nil, nil } // PostUpdate impl diff --git a/plugins/mirrorStrategy.go b/plugins/mirrorStrategy.go index fe58437dc..cd05af6c4 100644 --- a/plugins/mirrorStrategy.go +++ b/plugins/mirrorStrategy.go @@ -5,6 +5,7 @@ import ( "log" "sync" + "github.com/stellar/go/build" hProtocol "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/txnbuild" "github.com/stellar/kelp/api" @@ -192,8 +193,8 @@ func makeMirrorStrategy(sdex *SDEX, ieif *IEIF, pair *model.TradingPair, baseAss } // PruneExistingOffers deletes any extra offers -func (s *mirrorStrategy) PruneExistingOffers(buyingAOffers []hProtocol.Offer, sellingAOffers []hProtocol.Offer) ([]txnbuild.Operation, []hProtocol.Offer, []hProtocol.Offer) { - return []txnbuild.Operation{}, buyingAOffers, sellingAOffers +func (s *mirrorStrategy) PruneExistingOffers(buyingAOffers []hProtocol.Offer, sellingAOffers []hProtocol.Offer) ([]build.TransactionMutator, []hProtocol.Offer, []hProtocol.Offer) { + return []build.TransactionMutator{}, buyingAOffers, sellingAOffers } // PreUpdate changes the strategy's state in prepration for the update @@ -230,7 +231,7 @@ func (s *mirrorStrategy) recordBalances() error { func (s *mirrorStrategy) UpdateWithOps( buyingAOffers []hProtocol.Offer, sellingAOffers []hProtocol.Offer, -) ([]txnbuild.Operation, error) { +) ([]build.TransactionMutator, error) { ob, e := s.exchange.GetOrderBook(s.backingPair, s.orderbookDepth) if e != nil { return nil, e @@ -295,7 +296,7 @@ func (s *mirrorStrategy) UpdateWithOps( ops = append(ops, sellOps...) } - return ops, nil + return api.ConvertOperation2TM(ops), nil } func (s *mirrorStrategy) updateLevels( diff --git a/plugins/sdex.go b/plugins/sdex.go index 964354fcd..980661a2a 100644 --- a/plugins/sdex.go +++ b/plugins/sdex.go @@ -12,6 +12,7 @@ import ( "github.com/nikhilsaraf/go-tools/multithreading" "github.com/pkg/errors" + "github.com/stellar/go/build" "github.com/stellar/go/clients/horizonclient" hProtocol "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/txnbuild" @@ -358,17 +359,19 @@ func (sdex *SDEX) createModifySellOffer(offer *hProtocol.Offer, selling hProtoco } // SubmitOpsSynch is the forced synchronous version of SubmitOps below -func (sdex *SDEX) SubmitOpsSynch(ops []txnbuild.Operation, asyncCallback func(hash string, e error)) error { +func (sdex *SDEX) SubmitOpsSynch(ops []build.TransactionMutator, asyncCallback func(hash string, e error)) error { return sdex.submitOps(ops, asyncCallback, false) } // SubmitOps submits the passed in operations to the network asynchronously in a single transaction -func (sdex *SDEX) SubmitOps(ops []txnbuild.Operation, asyncCallback func(hash string, e error)) error { +func (sdex *SDEX) SubmitOps(ops []build.TransactionMutator, asyncCallback func(hash string, e error)) error { return sdex.submitOps(ops, asyncCallback, true) } // submitOps submits the passed in operations to the network in a single transaction. Asynchronous or not based on flag. -func (sdex *SDEX) submitOps(ops []txnbuild.Operation, asyncCallback func(hash string, e error), asyncMode bool) error { +func (sdex *SDEX) submitOps(opsOld []build.TransactionMutator, asyncCallback func(hash string, e error), asyncMode bool) error { + ops := api.ConvertTM2Operation(opsOld) + sdex.incrementSeqNum() tx := txnbuild.Transaction{ // sequence number is decremented here because Transaction.Build auto increments sequence number diff --git a/plugins/sellSideStrategy.go b/plugins/sellSideStrategy.go index 4c864f1d9..84a737157 100644 --- a/plugins/sellSideStrategy.go +++ b/plugins/sellSideStrategy.go @@ -4,6 +4,7 @@ import ( "fmt" "log" + "github.com/stellar/go/build" hProtocol "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/txnbuild" "github.com/stellar/kelp/api" @@ -67,7 +68,7 @@ func makeSellSideStrategy( } // PruneExistingOffers impl -func (s *sellSideStrategy) PruneExistingOffers(offers []hProtocol.Offer) ([]txnbuild.Operation, []hProtocol.Offer) { +func (s *sellSideStrategy) PruneExistingOffers(offers []hProtocol.Offer) ([]build.TransactionMutator, []hProtocol.Offer) { // figure out which offers we want to prune shouldPrune := computeOffersToPrune(offers, s.desiredLevels) @@ -91,7 +92,7 @@ func (s *sellSideStrategy) PruneExistingOffers(offers []hProtocol.Offer) ([]txnb // base and quote here refers to the bot's base and quote, not the base and quote of the sellSideStrategy log.Printf("offer | %s | level=%d | curPriceQuote=%.8f | curAmtBase=%.8f | pruning=%v\n", s.action, i+1, curPrice, curAmount, isPruning) } - return pruneOps, updatedOffers + return api.ConvertOperation2TM(pruneOps), updatedOffers } // computeOffersToPrune returns a list of bools representing whether we should prune the offer at that position or not @@ -259,7 +260,8 @@ func (s *sellSideStrategy) createPrecedingOffers( } // UpdateWithOps impl -func (s *sellSideStrategy) UpdateWithOps(offers []hProtocol.Offer) (ops []txnbuild.Operation, newTopOffer *model.Number, e error) { +func (s *sellSideStrategy) UpdateWithOps(offers []hProtocol.Offer) (opsOld []build.TransactionMutator, newTopOffer *model.Number, e error) { + var ops []txnbuild.Operation deleteOps := []txnbuild.Operation{} // first we want to re-create any offers that precede our existing offers and are additions to the existing offers that we have @@ -325,7 +327,7 @@ func (s *sellSideStrategy) UpdateWithOps(offers []hProtocol.Offer) (ops []txnbui // prepend deleteOps because we want to delete offers first so we "free" up our liabilities capacity to place the new/modified offers ops = append(deleteOps, ops...) - return ops, newTopOffer, nil + return api.ConvertOperation2TM(ops), newTopOffer, nil } // PostUpdate impl diff --git a/terminator/terminator.go b/terminator/terminator.go index ddacccc9d..ecf2efe46 100644 --- a/terminator/terminator.go +++ b/terminator/terminator.go @@ -10,6 +10,7 @@ import ( "github.com/stellar/go/clients/horizonclient" hProtocol "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/txnbuild" + "github.com/stellar/kelp/api" "github.com/stellar/kelp/model" "github.com/stellar/kelp/plugins" "github.com/stellar/kelp/support/utils" @@ -105,7 +106,7 @@ func (t *Terminator) run() { } log.Printf("updating delete timestamp to %s\n", tsMillisStr) - e = t.sdex.SubmitOps(ops, nil) + e = t.sdex.SubmitOps(api.ConvertOperation2TM(ops), nil) if e != nil { log.Println(e) } @@ -174,7 +175,7 @@ func (t *Terminator) deleteOffers(sellOffers []hProtocol.Offer, buyOffers []hPro log.Printf("deleting %d offers and 5 data entries, updating delete timestamp to %s\n", numOffers, tsMillisStr) if len(ops) > 0 { - e := t.sdex.SubmitOps(ops, nil) + e := t.sdex.SubmitOps(api.ConvertOperation2TM(ops), nil) if e != nil { log.Println(e) return diff --git a/trader/trader.go b/trader/trader.go index 17a4f8dbf..2abcfe1ab 100644 --- a/trader/trader.go +++ b/trader/trader.go @@ -8,6 +8,7 @@ import ( "time" "github.com/nikhilsaraf/go-tools/multithreading" + "github.com/stellar/go/build" "github.com/stellar/go/clients/horizonclient" hProtocol "github.com/stellar/go/protocols/horizon" "github.com/stellar/go/txnbuild" @@ -150,7 +151,7 @@ func (t *Trader) deleteAllOffers() { log.Printf("created %d operations to delete offers\n", len(dOps)) if len(dOps) > 0 { - e := t.exchangeShim.SubmitOps(dOps, nil) + e := t.exchangeShim.SubmitOps(api.ConvertOperation2TM(dOps), nil) if e != nil { log.Println(e) return @@ -192,7 +193,7 @@ func (t *Trader) update() { } // delete excess offers - var pruneOps []txnbuild.Operation + var pruneOps []build.TransactionMutator pruneOps, t.buyingAOffers, t.sellingAOffers = t.strategy.PruneExistingOffers(t.buyingAOffers, t.sellingAOffers) log.Printf("created %d operations to prune excess offers\n", len(pruneOps)) if len(pruneOps) > 0 { @@ -217,7 +218,7 @@ func (t *Trader) update() { return } - ops, e := t.strategy.UpdateWithOps(t.buyingAOffers, t.sellingAOffers) + opsOld, e := t.strategy.UpdateWithOps(t.buyingAOffers, t.sellingAOffers) log.Printf("liabilities at the end of a call to UpdateWithOps\n") t.sdex.IEIF().LogAllLiabilities(t.assetBase, t.assetQuote) if e != nil { @@ -228,6 +229,7 @@ func (t *Trader) update() { return } + ops := api.ConvertTM2Operation(opsOld) for i, filter := range t.submitFilters { ops, e = filter.Apply(ops, t.sellingAOffers, t.buyingAOffers) if e != nil { @@ -239,7 +241,7 @@ func (t *Trader) update() { log.Printf("created %d operations to update existing offers\n", len(ops)) if len(ops) > 0 { - e = t.exchangeShim.SubmitOps(ops, nil) + e = t.exchangeShim.SubmitOps(api.ConvertOperation2TM(ops), nil) if e != nil { log.Println(e) t.deleteAllOffers()