diff --git a/README.md b/README.md index 4367912..015729a 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ policy.json --spend-limit 10000adym \ --rollapp rollapp1 \ --denoms "adym,uatom" \ - --min-lp-fee-percentage "0.1" \ + --min-fee-percentage "0.1" \ --max-price 10000adym \ --operator-fee-share 0.1 \ --settlement-validated --fees 1dym -y diff --git a/cmd/cmd.go b/cmd/cmd.go index 196c439..55b9f81 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -34,8 +34,8 @@ var RootCmd = &cobra.Command{ var initCmd = &cobra.Command{ Use: "init", - Short: "Initialize the order client", - Long: `Initialize the order client by generating a config file with default values.`, + Short: "Initialize the eibc client", + Long: `Initialize the eibc client by generating a config file with default values.`, Run: func(cmd *cobra.Command, args []string) { cfg := config.Config{} if err := viper.Unmarshal(&cfg); err != nil { @@ -61,8 +61,8 @@ var initCmd = &cobra.Command{ var startCmd = &cobra.Command{ Use: "start", - Short: "Start the order client", - Long: `Start the order client that scans for demand orders and fulfills them.`, + Short: "Start the eibc client", + Long: `Start the eibc client that scans for demand orders and fulfills them.`, Run: func(cmd *cobra.Command, args []string) { viper.AutomaticEnv() @@ -91,7 +91,7 @@ var startCmd = &cobra.Command{ oc, err := eibc.NewOrderClient(cfg, logger) if err != nil { - log.Fatalf("failed to create order client: %v", err) + log.Fatalf("failed to create eibc client: %v", err) } if cfg.Fulfillers.Scale == 0 { @@ -100,7 +100,7 @@ var startCmd = &cobra.Command{ } if err := oc.Start(cmd.Context()); err != nil { - log.Fatalf("failed to start order client: %v", err) + log.Fatalf("failed to start eibc client: %v", err) } }, } diff --git a/config/config.go b/config/config.go index 483a28e..fbb349d 100644 --- a/config/config.go +++ b/config/config.go @@ -52,8 +52,9 @@ type OperatorConfig struct { } type ValidationConfig struct { - FallbackLevel ValidationLevel `mapstructure:"fallback_level"` - ValidationWaitTime time.Duration `mapstructure:"validation_wait_time"` + FallbackLevel ValidationLevel `mapstructure:"fallback_level"` + WaitTime time.Duration `mapstructure:"wait_time"` + Interval time.Duration `mapstructure:"interval"` } type RollappConfig struct { @@ -79,7 +80,8 @@ const ( defaultMaxOrdersPerTx = 10 defaultOrderRefreshInterval = 30 * time.Second defaultValidationFallbackLevel = "p2p" - defaultValidationWaitTime = "60m" + defaultValidationWaitTime = "61m" + defaultOrderValidationInterval = "5m" ) type ValidationLevel string @@ -125,7 +127,8 @@ func InitConfig() { viper.SetDefault("fulfillers.policy_address", "") viper.SetDefault("validation.fallback_level", defaultValidationFallbackLevel) - viper.SetDefault("validation.validation_wait_time", defaultValidationWaitTime) + viper.SetDefault("validation.wait_time", defaultValidationWaitTime) + viper.SetDefault("validation.interval", defaultOrderValidationInterval) viper.SetDefault("rollapps.example_1234-1.full_nodes", []string{"http://localhost:26657"}) viper.SetDefault("rollapps.example_1234-1.min_confirmations", "1") diff --git a/eibc/lp.go b/eibc/lp.go index 3e6c238..f53f7dc 100644 --- a/eibc/lp.go +++ b/eibc/lp.go @@ -104,6 +104,11 @@ func (or *orderTracker) loadLPs(ctx context.Context) error { continue } + if grant.Granter == "" || grant.Grantee == "" { + or.logger.Error("invalid grant", zap.Any("grant", grant)) + continue + } + g := new(types.FulfillOrderAuthorization) if err = proto.Unmarshal(grant.Authorization.Value, g); err != nil { return fmt.Errorf("failed to unmarshal grant: %w", err) diff --git a/eibc/order_client.go b/eibc/order_client.go index 8dacc99..d4075d6 100644 --- a/eibc/order_client.go +++ b/eibc/order_client.go @@ -7,9 +7,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/bech32" - "go.uber.org/zap" - "github.com/dymensionxyz/cosmosclient/cosmosclient" + "go.uber.org/zap" "github.com/dymensionxyz/eibc-client/config" ) @@ -30,9 +29,7 @@ func NewOrderClient(cfg config.Config, logger *zap.Logger) (*orderClient, error) //nolint:gosec subscriberID := fmt.Sprintf("eibc-client-%d", rand.Int()) - orderCh := make(chan []*demandOrder, config.NewOrderBufferSize) - fulfilledOrdersCh := make(chan *orderBatch, config.NewOrderBufferSize) // TODO: make buffer size configurable hubClient, err := getHubClient(cfg) if err != nil { @@ -57,12 +54,12 @@ func NewOrderClient(cfg config.Config, logger *zap.Logger) (*orderClient, error) cfg.Fulfillers.PolicyAddress, minOperatorFeeShare, fullNodeClient, - fulfilledOrdersCh, subscriberID, cfg.Fulfillers.BatchSize, &cfg.Validation, orderCh, cfg.OrderPolling.Interval, // we can use the same interval for order polling and LP balance checking + cfg.Validation.Interval, logger, ) @@ -145,7 +142,6 @@ func NewOrderClient(cfg config.Config, logger *zap.Logger) (*orderClient, error) cfg.Fulfillers.PolicyAddress, cClient, orderCh, - fulfilledOrdersCh, ordTracker.releaseAllReservedOrdersFunds, ordTracker.debitAllReservedOrdersFunds, ) diff --git a/eibc/order_client_test.go b/eibc/order_client_test.go index 4b50efb..85f974a 100644 --- a/eibc/order_client_test.go +++ b/eibc/order_client_test.go @@ -14,6 +14,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/authz" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/dymensionxyz/cosmosclient/cosmosclient" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" rpcclient "github.com/tendermint/tendermint/rpc/client" @@ -21,8 +22,6 @@ import ( "go.uber.org/zap" "google.golang.org/grpc" - "github.com/dymensionxyz/cosmosclient/cosmosclient" - "github.com/dymensionxyz/eibc-client/config" "github.com/dymensionxyz/eibc-client/types" ) @@ -40,6 +39,7 @@ func TestOrderClient(t *testing.T) { fullNodeClient *nodeClient pollOrders []Order eventOrders []Order + updateOrders []Order expectLPFulfilledOrderIDs map[string]string // orderID -> lpAddress }{ { @@ -57,7 +57,8 @@ func TestOrderClient(t *testing.T) { BatchSize: 4, }, Validation: config.ValidationConfig{ - ValidationWaitTime: time.Second, + WaitTime: time.Second, + Interval: time.Second, }, }, lpConfigs: []lpConfig{ @@ -148,22 +149,25 @@ func TestOrderClient(t *testing.T) { pollOrders: []Order{ { EibcOrderId: "order1", - Price: "80stake", + Price: "80", Fee: "12stake", RollappId: "rollapp1", ProofHeight: "1", + BlockHeight: "1", }, { EibcOrderId: "order2", - Price: "202stake", - Fee: "25stake", + Price: "202", + Fee: "2stake", // too low - won't fulfill RollappId: "rollapp2", ProofHeight: "2", + BlockHeight: "2", }, { EibcOrderId: "order5", - Price: "201stake", + Price: "201", Fee: "50stake", RollappId: "rollapp1", ProofHeight: "5", + BlockHeight: "5", }, }, eventOrders: []Order{ @@ -187,6 +191,16 @@ func TestOrderClient(t *testing.T) { ProofHeight: "6", }, }, + updateOrders: []Order{ + { + EibcOrderId: "order2", + Price: "202", + Fee: "25stake", // update so it will fulfill + RollappId: "rollapp2", + ProofHeight: "2", + BlockHeight: "2", + }, + }, expectLPFulfilledOrderIDs: map[string]string{ "order1": "lp-3-address", // lp3 (lp1 and lp3 selected because they fulfill for rollapp1, lp3 preferred because operator fee is higher) // "order2": "", // not fulfilled (lp1 has not enough balance, lp2 does not fulfill stake orders, lp3 does not fulfill for rollapp2) @@ -216,6 +230,7 @@ func TestOrderClient(t *testing.T) { lpAddr := fmt.Sprintf("lp-%d-address", i+1) grants = append(grants, &authz.GrantAuthorization{ Granter: lpAddr, + Grantee: "policyAddress", Authorization: a, }) lpBalances[lpAddr] = g.balance @@ -258,6 +273,19 @@ func TestOrderClient(t *testing.T) { } } + for _, order := range tt.updateOrders { + oc.orderEventer.eventClient.(*mockNodeClient).updateOrderCh <- coretypes.ResultEvent{ + Events: map[string][]string{ + updatedFeeEvent + ".order_id": {order.EibcOrderId}, + updatedFeeEvent + ".price": {order.Price}, + updatedFeeEvent + ".packet_status": {"PENDING"}, + updatedFeeEvent + ".new_fee": {order.Fee}, + updatedFeeEvent + ".rollapp_id": {order.RollappId}, + updatedFeeEvent + ".proof_height": {order.ProofHeight}, + }, + } + } + // wait a bit for the client to fulfill orders time.Sleep(3 * time.Second) @@ -295,7 +323,6 @@ func setupTestOrderClient( ) (*orderClient, error) { logger, _ := zap.NewDevelopment() orderCh := make(chan []*demandOrder, config.NewOrderBufferSize) - fulfilledOrdersCh := make(chan *orderBatch, config.NewOrderBufferSize) // tracker trackerClient := hubClient @@ -308,12 +335,12 @@ func setupTestOrderClient( "policyAddress", minOperatorFeeShare, fullNodeClient, - fulfilledOrdersCh, "subscriber", cfg.Fulfillers.BatchSize, &cfg.Validation, orderCh, cfg.OrderPolling.Interval, + cfg.Validation.Interval, logger, ) ordTracker.getLPGrants = grantsFn @@ -324,7 +351,7 @@ func setupTestOrderClient( eventerClient := hubClient eventerClient.finalizeOrderCh = make(chan coretypes.ResultEvent, 1) eventerClient.addOrderCh = make(chan coretypes.ResultEvent, 1) - eventerClient.stateInfoCh = make(chan coretypes.ResultEvent, 1) + eventerClient.updateOrderCh = make(chan coretypes.ResultEvent, 1) eventer := newOrderEventer( cosmosclient.Client{ @@ -353,7 +380,6 @@ func setupTestOrderClient( "policyAddress", &hc, orderCh, - fulfilledOrdersCh, ordTracker.releaseAllReservedOrdersFunds, ordTracker.debitAllReservedOrdersFunds, ) @@ -429,7 +455,7 @@ type mockNodeClient struct { rpcclient.Client finalizeOrderCh chan coretypes.ResultEvent addOrderCh chan coretypes.ResultEvent - stateInfoCh chan coretypes.ResultEvent + updateOrderCh chan coretypes.ResultEvent } func (m *mockNodeClient) Start() error { @@ -445,9 +471,11 @@ func (m *mockNodeClient) BroadcastTx(string, ...sdk.Msg) (cosmosclient.Response, } func (m *mockNodeClient) Subscribe(_ context.Context, _ string, query string, _ ...int) (out <-chan coretypes.ResultEvent, err error) { - switch query { - case fmt.Sprintf("%s.is_fulfilled='false'", createdEvent): + switch { + case strings.Contains(query, createdEvent): return m.addOrderCh, nil + case strings.Contains(query, updatedFeeEvent): + return m.updateOrderCh, nil } return nil, fmt.Errorf("invalid query") } diff --git a/eibc/order_eventer.go b/eibc/order_eventer.go index 24f38ce..3b4537c 100644 --- a/eibc/order_eventer.go +++ b/eibc/order_eventer.go @@ -41,7 +41,8 @@ func newOrderEventer( } const ( - createdEvent = "dymensionxyz.dymension.eibc.EventDemandOrderCreated" + createdEvent = "dymensionxyz.dymension.eibc.EventDemandOrderCreated" + updatedFeeEvent = "dymensionxyz.dymension.eibc.EventDemandOrderFeeUpdated" ) func (e *orderEventer) start(ctx context.Context) error { @@ -53,42 +54,53 @@ func (e *orderEventer) start(ctx context.Context) error { return fmt.Errorf("failed to subscribe to pending demand orders: %w", err) } + if err := e.subscribeToUpdatedDemandOrders(ctx); err != nil { + return fmt.Errorf("failed to subscribe to updated demand orders: %w", err) + } + return nil } -func (e *orderEventer) enqueueEventOrders(_ context.Context, res tmtypes.ResultEvent) error { - newOrders := e.parseOrdersFromEvents(res) - if len(newOrders) == 0 { +func (e *orderEventer) enqueueEventOrders(_ context.Context, eventName string, res tmtypes.ResultEvent) error { + orders := e.parseOrdersFromEvents(eventName, res) + if len(orders) == 0 { return nil } + d := "updated" + if eventName == createdEvent { + d = "new" + } + e.orderTracker.trackOrders(orders...) + if e.logger.Level() <= zap.DebugLevel { - ids := make([]string, 0, len(newOrders)) - for _, order := range newOrders { + ids := make([]string, 0, len(orders)) + for _, order := range orders { ids = append(ids, order.id) } - e.logger.Debug("new demand orders", zap.Strings("ids", ids)) + e.logger.Debug(fmt.Sprintf("%s demand orders", d), zap.Strings("ids", ids)) } else { - e.logger.Info("new demand orders", zap.Int("count", len(newOrders))) + e.logger.Info(fmt.Sprintf("%s demand orders", d), zap.Int("count", len(orders))) } - e.orderTracker.addOrder(newOrders...) - return nil } -func (e *orderEventer) parseOrdersFromEvents(res tmtypes.ResultEvent) []*demandOrder { - ids := res.Events[createdEvent+".order_id"] +func (e *orderEventer) parseOrdersFromEvents(eventName string, res tmtypes.ResultEvent) []*demandOrder { + ids := res.Events[eventName+".order_id"] if len(ids) == 0 { return nil } - prices := res.Events[createdEvent+".price"] - fees := res.Events[createdEvent+".fee"] - statuses := res.Events[createdEvent+".packet_status"] - rollapps := res.Events[createdEvent+".rollapp_id"] - heights := res.Events[createdEvent+".proof_height"] + prices := res.Events[eventName+".price"] + amounts := res.Events[eventName+".amount"] + fees := res.Events[eventName+".new_fee"] + if eventName == createdEvent { + fees = res.Events[eventName+".fee"] + } + rollapps := res.Events[eventName+".rollapp_id"] + proofHeights := res.Events[eventName+".proof_height"] newOrders := make([]*demandOrder, 0, len(ids)) for i, id := range ids { @@ -102,30 +114,44 @@ func (e *orderEventer) parseOrdersFromEvents(res tmtypes.ResultEvent) []*demandO continue } + amount, ok := sdk.NewIntFromString(amounts[i]) + if !ok { + e.logger.Error("failed to parse amount", zap.String("amount", amounts[i])) + continue + } + fee, err := sdk.ParseCoinNormalized(fees[i]) if err != nil { e.logger.Error("failed to parse fee", zap.Error(err)) continue } - height, err := strconv.ParseInt(heights[i], 10, 64) + proofHeight, err := strconv.ParseInt(proofHeights[i], 10, 64) if err != nil { - e.logger.Error("failed to parse block height", zap.Error(err)) + e.logger.Error("failed to parse proof height", zap.Error(err)) continue } - validationWaitTime := e.orderTracker.validation.ValidationWaitTime - validDeadline := time.Now().Add(validationWaitTime) + if eventName == updatedFeeEvent { + existOrder, ok := e.orderTracker.pool.getOrder(id) + if ok { + // update the fee and price of the order + existOrder.fee = fee + existOrder.price = price + e.orderTracker.pool.upsertOrder(existOrder) + continue + } + } order := &demandOrder{ id: id, denom: fee.Denom, price: price, + amount: amount, fee: fee, - status: statuses[i], rollappId: rollapps[i], - proofHeight: height, - validDeadline: validDeadline, + proofHeight: proofHeight, + validDeadline: time.Now().Add(e.orderTracker.validation.WaitTime), from: "event", } @@ -145,11 +171,16 @@ func (e *orderEventer) parseOrdersFromEvents(res tmtypes.ResultEvent) []*demandO } func (e *orderEventer) subscribeToPendingDemandOrders(ctx context.Context) error { - query := fmt.Sprintf("%s.is_fulfilled='false'", createdEvent) - return e.subscribeToEvent(ctx, "pending demand", query, e.enqueueEventOrders) + query := fmt.Sprintf("%s.packet_status='PENDING'", createdEvent) + return e.subscribeToEvent(ctx, createdEvent, query, e.enqueueEventOrders) +} + +func (e *orderEventer) subscribeToUpdatedDemandOrders(ctx context.Context) error { + query := fmt.Sprintf("%s.packet_status='PENDING'", updatedFeeEvent) + return e.subscribeToEvent(ctx, updatedFeeEvent, query, e.enqueueEventOrders) } -func (e *orderEventer) subscribeToEvent(ctx context.Context, event string, query string, callback func(ctx context.Context, event tmtypes.ResultEvent) error) error { +func (e *orderEventer) subscribeToEvent(ctx context.Context, event string, query string, callback func(ctx context.Context, name string, event tmtypes.ResultEvent) error) error { resCh, err := e.eventClient.Subscribe(ctx, e.subscriberID, query) if err != nil { return fmt.Errorf("failed to subscribe to %s events: %w", event, err) @@ -159,7 +190,7 @@ func (e *orderEventer) subscribeToEvent(ctx context.Context, event string, query for { select { case res := <-resCh: - if err := callback(ctx, res); err != nil { + if err := callback(ctx, event, res); err != nil { e.logger.Error(fmt.Sprintf("failed to process %s event", event), zap.Error(err)) } case <-ctx.Done(): diff --git a/eibc/order_fulfiller.go b/eibc/order_fulfiller.go index cfd64d8..67be3e9 100644 --- a/eibc/order_fulfiller.go +++ b/eibc/order_fulfiller.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "slices" "strings" "github.com/cosmos/cosmos-sdk/client" @@ -28,7 +29,6 @@ type orderFulfiller struct { releaseAllReservedOrdersFunds func(demandOrder ...*demandOrder) debitAllReservedOrdersFunds func(demandOrder ...*demandOrder) newOrdersCh chan []*demandOrder - fulfilledOrdersCh chan<- *orderBatch } type cosmosClient interface { @@ -43,7 +43,6 @@ func newOrderFulfiller( policyAddress string, cClient cosmosClient, newOrdersCh chan []*demandOrder, - fulfilledOrdersCh chan<- *orderBatch, releaseAllReservedOrdersFunds func(demandOrder ...*demandOrder), debitAllReservedOrdersFunds func(demandOrder ...*demandOrder), ) (*orderFulfiller, error) { @@ -52,7 +51,6 @@ func newOrderFulfiller( policyAddress: policyAddress, operatorAddress: operatorAddress, client: cClient, - fulfilledOrdersCh: fulfilledOrdersCh, newOrdersCh: newOrdersCh, releaseAllReservedOrdersFunds: releaseAllReservedOrdersFunds, debitAllReservedOrdersFunds: debitAllReservedOrdersFunds, @@ -90,17 +88,14 @@ func (ol *orderFulfiller) processBatch(batch []*demandOrder) error { } var ( - ids []string - lps []string - lpMap = make(map[string]struct{}) + ids []string + lps []string ) for _, order := range batch { ids = append(ids, order.id) - lpMap[order.lpAddress] = struct{}{} - } - - for l := range lpMap { - lps = append(lps, l) + if !slices.Contains(lps, order.lpAddress) { + lps = append(lps, order.lpAddress) + } } ol.logger.Info("fulfilling orders", zap.Strings("ids", ids), zap.Strings("lps", lps)) @@ -114,16 +109,6 @@ func (ol *orderFulfiller) processBatch(batch []*demandOrder) error { ol.logger.Info("orders fulfilled", zap.Strings("ids", ids)) - go func() { - if len(ids) == 0 { - return - } - - ol.fulfilledOrdersCh <- &orderBatch{ - orders: batch, - } - }() - return nil } diff --git a/eibc/order_poller.go b/eibc/order_poller.go index fb8708d..7c992b9 100644 --- a/eibc/order_poller.go +++ b/eibc/order_poller.go @@ -23,14 +23,11 @@ type orderPoller struct { indexerClient *http.Client logger *zap.Logger - getOrders func() ([]Order, error) - orderTracker *orderTracker - - skippedOrders map[string]struct{} + getOrders func() ([]Order, error) + orderTracker *orderTracker + lastBlockHeight uint64 } -const maxSkippedOrders = 1000 - func newOrderPoller( chainID string, orderTracker *orderTracker, @@ -44,14 +41,13 @@ func newOrderPoller( logger: logger.With(zap.String("module", "order-poller")), orderTracker: orderTracker, indexerClient: &http.Client{Timeout: 25 * time.Second}, - skippedOrders: make(map[string]struct{}), } o.getOrders = o.getDemandOrdersFromIndexer return o } const ( - ordersQuery = `{"query": "{ibcTransferDetails(filter: {network: {equalTo: \"%s\"} status: {equalTo: EibcPending}}) {nodes { eibcOrderId amount proofHeight price rollappId eibcFee }}}"}` + ordersQuery = `{"query": "{ibcTransferDetails(filter: {network: {equalTo: \"%s\"} status: {equalTo: EibcPending}, blockHeight: { greaterThan: \"%s\" }}) {nodes { eibcOrderId amount proofHeight blockHeight price rollappId eibcFee }}}"}` ) type Order struct { @@ -61,6 +57,7 @@ type Order struct { Fee string `json:"eibcFee"` RollappId string `json:"rollappId"` ProofHeight string `json:"proofHeight"` + BlockHeight string `json:"blockHeight"` } type ordersResponse struct { @@ -99,16 +96,20 @@ func (p *orderPoller) pollPendingDemandOrders() error { demandOrders := make([]Order, 0, len(newDemandOrders)) for _, order := range newDemandOrders { - if _, ok := p.skippedOrders[order.EibcOrderId]; ok { + blockHeight, err := strconv.ParseUint(order.BlockHeight, 10, 64) + if err != nil { + p.logger.Error("failed to parse block height", zap.Error(err)) continue } + if blockHeight > p.lastBlockHeight { + p.lastBlockHeight = blockHeight + } demandOrders = append(demandOrders, order) } newOrders := p.convertOrders(demandOrders) if len(newOrders) == 0 { - p.logger.Debug("no new orders") return nil } @@ -122,7 +123,7 @@ func (p *orderPoller) pollPendingDemandOrders() error { p.logger.Info("new demand orders", zap.Int("count", len(newOrders))) } - p.orderTracker.addOrder(newOrders...) + p.orderTracker.trackOrders(newOrders...) return nil } @@ -147,6 +148,12 @@ func (p *orderPoller) convertOrders(demandOrders []Order) (orders []*demandOrder continue } + amount, ok := sdk.NewIntFromString(order.Amount) + if !ok { + p.logger.Error("failed to parse amount", zap.String("amount", order.Amount)) + continue + } + priceInt, ok := sdk.NewIntFromString(order.Price) if !ok { p.logger.Error("failed to parse price", zap.String("price", order.Price)) @@ -161,17 +168,25 @@ func (p *orderPoller) convertOrders(demandOrders []Order) (orders []*demandOrder continue } - validationWaitTime := p.orderTracker.validation.ValidationWaitTime - validDeadline := time.Now().Add(validationWaitTime) + // in case tracked order got updated + existOrder, ok := p.orderTracker.pool.getOrder(order.EibcOrderId) + if ok { + // update the fee and price of the order + existOrder.fee = fee + existOrder.price = price + p.orderTracker.pool.upsertOrder(existOrder) + continue + } newOrder := &demandOrder{ id: order.EibcOrderId, price: price, + amount: amount, fee: fee, denom: fee.Denom, rollappId: order.RollappId, proofHeight: proofHeight, - validDeadline: validDeadline, + validDeadline: time.Now().Add(p.orderTracker.validation.WaitTime), from: "indexer", } @@ -181,9 +196,6 @@ func (p *orderPoller) convertOrders(demandOrders []Order) (orders []*demandOrder if err := p.orderTracker.findLPForOrder(newOrder); err != nil { p.logger.Debug("failed to find LP for order", zap.Error(err), zap.String("order_id", newOrder.id)) - if len(p.skippedOrders) < maxSkippedOrders { - p.skippedOrders[newOrder.id] = struct{}{} - } continue } @@ -197,9 +209,7 @@ func (p *orderPoller) convertOrders(demandOrders []Order) (orders []*demandOrder } func (p *orderPoller) getDemandOrdersFromIndexer() ([]Order, error) { - p.logger.Debug("getting demand orders from indexer") - - queryStr := fmt.Sprintf(ordersQuery, p.chainID) + queryStr := fmt.Sprintf(ordersQuery, p.chainID, fmt.Sprint(p.lastBlockHeight)) body := strings.NewReader(queryStr) resp, err := p.indexerClient.Post(p.indexerURL, "application/json", body) diff --git a/eibc/order_tracker.go b/eibc/order_tracker.go index 72e68be..2fb4036 100644 --- a/eibc/order_tracker.go +++ b/eibc/order_tracker.go @@ -38,9 +38,9 @@ type orderTracker struct { batchSize int validation *config.ValidationConfig toCheckOrdersCh chan []*demandOrder - fulfilledOrdersCh chan *orderBatch subscriberID string balanceRefreshInterval time.Duration + validateOrdersInterval time.Duration } type ( @@ -53,12 +53,12 @@ func newOrderTracker( policyAddress string, minOperatorFeeShare sdk.Dec, fullNodeClient *nodeClient, - fulfilledOrdersCh chan *orderBatch, subscriberID string, batchSize int, validation *config.ValidationConfig, ordersCh chan<- []*demandOrder, - balanceRefreshInterval time.Duration, + balanceRefreshInterval, + validateOrdersInterval time.Duration, logger *zap.Logger, ) *orderTracker { azc := authz.NewQueryClient(hubClient.Context()) @@ -70,7 +70,6 @@ func newOrderTracker( minOperatorFeeShare: minOperatorFeeShare, fullNodeClient: fullNodeClient, pool: orderPool{orders: make(map[string]*demandOrder)}, - fulfilledOrdersCh: fulfilledOrdersCh, lps: make(map[string]*lp), batchSize: batchSize, validation: validation, @@ -79,8 +78,8 @@ func newOrderTracker( logger: logger.With(zap.String("module", "order-resolver")), subscriberID: subscriberID, balanceRefreshInterval: balanceRefreshInterval, + validateOrdersInterval: validateOrdersInterval, toCheckOrdersCh: make(chan []*demandOrder, batchSize), - fulfilledOrders: make(map[string]*demandOrder), } } @@ -91,9 +90,8 @@ func (or *orderTracker) start(ctx context.Context) error { go or.lpLoader(ctx) go or.balanceRefresher(ctx) - go or.pullOrders(ctx) + go or.orderValidator(ctx) go or.enqueueValidOrders(ctx) - go or.fulfilledOrdersWorker(ctx) return nil } @@ -112,8 +110,8 @@ func (or *orderTracker) lpLoader(ctx context.Context) { } } -func (or *orderTracker) pullOrders(ctx context.Context) { - ticker := time.NewTicker(2 * time.Second) +func (or *orderTracker) orderValidator(ctx context.Context) { + ticker := time.NewTicker(or.validateOrdersInterval) for { select { case <-ctx.Done(): @@ -163,20 +161,29 @@ func (or *orderTracker) getValidAndRetryOrders(ctx context.Context, orders []*de } valid, err := or.fullNodeClient.BlockValidated(ctx, order.rollappId, order.proofHeight, expectedValidationLevel) if err != nil { - or.logger.Error("failed to check validation of block", zap.Error(err)) + or.logger.Error("failed to check validation of block", zap.String("order_id", order.id), zap.Error(err)) continue } if valid { + or.pool.removeOrder(order.id) validOrders = append(validOrders, order) continue } if or.isOrderExpired(order) { - or.releaseAllReservedOrdersFunds(order) + or.evictOrder(order) or.logger.Debug("order has expired", zap.String("id", order.id)) continue } or.logger.Debug("order is not valid yet", zap.String("id", order.id), zap.String("from", order.from)) + + if !or.orderFulfillable(order) { + or.evictOrder(order) + or.logger.Debug("order is not fulfillable anymore", zap.String("id", order.id)) + continue + } + // order is not valid yet, so add it back to the pool + order.checking = false invalidOrders = append(invalidOrders, order) } return @@ -187,7 +194,21 @@ func (or *orderTracker) isOrderExpired(order *demandOrder) bool { return time.Now().After(order.validDeadline) } -func (or *orderTracker) addOrder(orders ...*demandOrder) { +func (or *orderTracker) orderFulfillable(order *demandOrder) bool { + if err := or.findLPForOrder(order); err != nil { + or.logger.Debug("failed to find LP for order", zap.Error(err), zap.String("order_id", order.id)) + return false + } + + return true +} + +func (or *orderTracker) evictOrder(order *demandOrder) { + or.pool.removeOrder(order.id) + or.releaseAllReservedOrdersFunds(order) +} + +func (or *orderTracker) trackOrders(orders ...*demandOrder) { // - in mode "sequencer" we send a batch directly to be fulfilled, // and any orders that overflow the batch are added to the pool // - in mode "p2p" and "settlement" all orders are added to the pool @@ -207,52 +228,14 @@ func (or *orderTracker) addOrder(orders ...*demandOrder) { or.outputOrdersCh <- batchToSend orders = batchToPool } - or.pool.addOrder(orders...) + or.pool.upsertOrder(orders...) go or.checkOrders() } -func (or *orderTracker) fulfilledOrdersWorker(ctx context.Context) { - for { - select { - case batch := <-or.fulfilledOrdersCh: - if err := or.addFulfilledOrders(batch); err != nil { - or.logger.Error("failed to add fulfilled orders", zap.Error(err)) - } - case <-ctx.Done(): - return - } - } -} - -// addFulfilledOrders adds the fulfilled orders to the fulfilledOrders cache, and removes them from the orderPool. -// It also persists the state to the database. -func (or *orderTracker) addFulfilledOrders(batch *orderBatch) error { - or.fomu.Lock() - for _, order := range batch.orders { - if len(order.price) == 0 { - continue - } - // add to cache - or.fulfilledOrders[order.id] = order - or.pool.removeOrder(order.id) // just in case it's still in the pool - } - or.fomu.Unlock() - return nil -} - func (or *orderTracker) canFulfillOrder(order *demandOrder) bool { if !or.isRollappSupported(order.rollappId) { return false } - - if or.isOrderFulfilled(order.id) { - return false - } - // we are already processing this order - if or.isOrderInPool(order.id) { - return false - } - return true } @@ -265,6 +248,17 @@ func (or *orderTracker) findLPForOrder(order *demandOrder) error { return fmt.Errorf("no LPs found for order: %s", strings.Join(lpMiss, "; ")) } + if order.lpAddress != "" { + // check if the LP is still valid + for _, l := range lps { + if l.address == order.lpAddress { + // in case it changed + order.settlementValidated = l.rollapps[order.rollappId].settlementValidated + return nil + } + } + } + // randomize the list of LPs to avoid always selecting the same one // this is important for the case where multiple LPs have the same operatorFeeShare // and the same settlementValidated status @@ -322,8 +316,7 @@ func (or *orderTracker) filterLPsForOrder(order *demandOrder) ([]*lp, []string) } // check the fee is at least the minimum for what the lp wants - amountDec := sdk.NewDecFromInt(order.price[0].Amount.Add(order.fee.Amount)) - minFee := amountDec.Mul(rollapp.minFeePercentage).RoundInt() + minFee := sdk.NewDecFromInt(order.fee.Amount).Mul(rollapp.minFeePercentage).RoundInt() if order.fee.Amount.LT(minFee) { lpSkip = append(lpSkip, fmt.Sprintf("%s: min_fee", lp.address)) @@ -364,7 +357,3 @@ func (or *orderTracker) isOrderFulfilled(id string) bool { _, ok := or.fulfilledOrders[id] return ok } - -func (or *orderTracker) isOrderInPool(id string) bool { - return or.pool.hasOrder(id) -} diff --git a/eibc/pool.go b/eibc/pool.go index dfef2a3..f354e07 100644 --- a/eibc/pool.go +++ b/eibc/pool.go @@ -14,6 +14,7 @@ func (op *orderPool) addOrder(order ...*demandOrder) { for _, o := range order { // skip if the order is already in the pool + // this can happen if the order is updated if op.orders[o.id] != nil { continue } @@ -21,19 +22,28 @@ func (op *orderPool) addOrder(order ...*demandOrder) { } } -func (op *orderPool) removeOrder(id string) { +func (op *orderPool) getOrder(id string) (*demandOrder, bool) { op.opmu.Lock() defer op.opmu.Unlock() - delete(op.orders, id) + order, ok := op.orders[id] + return order, ok +} + +func (op *orderPool) upsertOrder(order ...*demandOrder) { + op.opmu.Lock() + defer op.opmu.Unlock() + + for _, o := range order { + op.orders[o.id] = o + } } -func (op *orderPool) hasOrder(id string) bool { +func (op *orderPool) removeOrder(id string) { op.opmu.Lock() defer op.opmu.Unlock() - _, ok := op.orders[id] - return ok + delete(op.orders, id) } func (op *orderPool) popOrders(limit int) []*demandOrder { @@ -42,8 +52,11 @@ func (op *orderPool) popOrders(limit int) []*demandOrder { var orders []*demandOrder for _, order := range op.orders { + if order.checking { + continue + } orders = append(orders, order) - delete(op.orders, order.id) + order.checking = true if len(orders) == limit { break } diff --git a/eibc/types.go b/eibc/types.go index 5ff3f5e..0bcbcb9 100644 --- a/eibc/types.go +++ b/eibc/types.go @@ -14,15 +14,16 @@ type demandOrder struct { id string denom string price sdk.Coins + amount sdk.Int fee sdk.Coin rollappId string - status string proofHeight int64 validDeadline time.Time settlementValidated bool operatorFeePart sdk.Dec lpAddress string from string + checking bool } type account struct { diff --git a/go.mod b/go.mod index 48a1e5c..6733bd8 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/dymensionxyz/eibc-client -go 1.22.2 +go 1.23.1 require ( cosmossdk.io/errors v1.0.1 diff --git a/types/tx.pb.go b/types/tx.pb.go index 7c9e45a..c26098b 100644 --- a/types/tx.pb.go +++ b/types/tx.pb.go @@ -36,26 +36,28 @@ type MsgFulfillOrderAuthorized struct { RollappId string `protobuf:"bytes,2,opt,name=rollapp_id,json=rollappId,proto3" json:"rollapp_id,omitempty"` // price is the price of the demand order Price github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,3,rep,name=price,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"price"` + // amount is the amount of the IBC transfer + Amount types.IntProto `protobuf:"bytes,4,opt,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.IntProto" json:"amount"` // lp_address is the bech32-encoded address of the account which the authorization was granted from. // This account will receive the price amount at the finalization phase. - LpAddress string `protobuf:"bytes,4,opt,name=lp_address,json=lpAddress,proto3" json:"lp_address,omitempty"` + LpAddress string `protobuf:"bytes,5,opt,name=lp_address,json=lpAddress,proto3" json:"lp_address,omitempty"` // operator_fee_address is an optional bech32-encoded address of an account that would collect the operator_fee_part // if it's empty, the operator_fee_part will go to the operator_address - OperatorFeeAddress string `protobuf:"bytes,5,opt,name=operator_fee_address,json=operatorFeeAddress,proto3" json:"operator_fee_address,omitempty"` + OperatorFeeAddress string `protobuf:"bytes,6,opt,name=operator_fee_address,json=operatorFeeAddress,proto3" json:"operator_fee_address,omitempty"` // expected_fee is the nominal fee set in the order. - ExpectedFee string `protobuf:"bytes,6,opt,name=expected_fee,json=expectedFee,proto3" json:"expected_fee,omitempty"` + ExpectedFee string `protobuf:"bytes,7,opt,name=expected_fee,json=expectedFee,proto3" json:"expected_fee,omitempty"` // operator_fee_share is the share of the fee earnings that goes to the operator // it will be deduced from the fee of the demand order and paid out immediately - OperatorFeeShare types.DecProto `protobuf:"bytes,7,opt,name=operator_fee_share,json=operatorFeeShare,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecProto" json:"operator_fee_share"` + OperatorFeeShare types.DecProto `protobuf:"bytes,8,opt,name=operator_fee_share,json=operatorFeeShare,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecProto" json:"operator_fee_share"` // settlement_validated signals if the block behind the demand order needs to be "settlement validated" or not - SettlementValidated bool `protobuf:"varint,8,opt,name=settlement_validated,json=settlementValidated,proto3" json:"settlement_validated,omitempty"` + SettlementValidated bool `protobuf:"varint,9,opt,name=settlement_validated,json=settlementValidated,proto3" json:"settlement_validated,omitempty"` } func (m *MsgFulfillOrderAuthorized) Reset() { *m = MsgFulfillOrderAuthorized{} } func (m *MsgFulfillOrderAuthorized) String() string { return proto.CompactTextString(m) } func (*MsgFulfillOrderAuthorized) ProtoMessage() {} func (*MsgFulfillOrderAuthorized) Descriptor() ([]byte, []int) { - return fileDescriptor_d7e8da19b02f0731, []int{0} + return fileDescriptor_47537f11f512b254, []int{2} } func (m *MsgFulfillOrderAuthorized) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -105,6 +107,13 @@ func (m *MsgFulfillOrderAuthorized) GetPrice() github_com_cosmos_cosmos_sdk_type return nil } +func (m *MsgFulfillOrderAuthorized) GetAmount() types.IntProto { + if m != nil { + return m.Amount + } + return types.IntProto{} +} + func (m *MsgFulfillOrderAuthorized) GetLpAddress() string { if m != nil { return m.LpAddress @@ -145,41 +154,54 @@ func init() { } func init() { - proto.RegisterFile("dymensionxyz/dymension/eibc/tx_auth.proto", fileDescriptor_d7e8da19b02f0731) + proto.RegisterFile("dymensionxyz/dymension/eibc/tx_auth.proto", fileDescriptor_47537f11f512b254) } -var fileDescriptor_d7e8da19b02f0731 = []byte{ - // 468 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xcf, 0x6e, 0xd3, 0x30, - 0x18, 0x6f, 0x28, 0xdb, 0x3a, 0x17, 0x09, 0x14, 0x2a, 0x91, 0x0e, 0x2d, 0x2b, 0x9c, 0x02, 0x88, - 0x78, 0x59, 0x6f, 0xdc, 0x56, 0x50, 0xa5, 0x09, 0x21, 0x50, 0x91, 0x38, 0x70, 0x89, 0x9c, 0xf8, - 0x5b, 0x6a, 0x91, 0xc4, 0x96, 0xed, 0x56, 0xed, 0x4e, 0x88, 0x27, 0xe0, 0x39, 0x38, 0xf1, 0x18, - 0x3b, 0xee, 0xc8, 0x09, 0x50, 0x7b, 0xe0, 0x01, 0x78, 0x01, 0xe4, 0xc4, 0x29, 0x45, 0x02, 0x89, - 0x93, 0xf3, 0xfb, 0xe7, 0xfc, 0x3e, 0xeb, 0x43, 0x0f, 0xe8, 0xb2, 0x80, 0x52, 0x31, 0x5e, 0x2e, - 0x96, 0x17, 0x78, 0x03, 0x30, 0xb0, 0x24, 0xc5, 0x7a, 0x11, 0x93, 0x99, 0x9e, 0x86, 0x42, 0x72, - 0xcd, 0xdd, 0xbb, 0xdb, 0xd6, 0x70, 0x03, 0x42, 0x63, 0x3d, 0xb8, 0x93, 0x72, 0x55, 0x70, 0x85, - 0x0b, 0x95, 0xe1, 0x79, 0x64, 0x8e, 0x3a, 0x75, 0xd0, 0xaf, 0x85, 0xb8, 0x42, 0xb8, 0x06, 0x56, - 0xea, 0x65, 0x3c, 0xe3, 0x35, 0x6f, 0xbe, 0x2c, 0xeb, 0xdb, 0x9b, 0x12, 0xa2, 0x00, 0xcf, 0xa3, - 0x04, 0x34, 0x89, 0x70, 0xca, 0x59, 0x59, 0xeb, 0xf7, 0x7f, 0xb6, 0x51, 0xff, 0x85, 0xca, 0xc6, - 0xb3, 0xfc, 0x9c, 0xe5, 0xf9, 0x4b, 0x49, 0x41, 0x9e, 0xce, 0xf4, 0x94, 0x4b, 0x76, 0x01, 0xd4, - 0xed, 0xa3, 0x0e, 0x37, 0x54, 0xcc, 0xa8, 0xe7, 0x0c, 0x9c, 0x60, 0x7f, 0xb2, 0x57, 0xe1, 0x33, - 0xea, 0x1e, 0x22, 0x24, 0x79, 0x9e, 0x13, 0x21, 0x8c, 0x78, 0xad, 0x12, 0xf7, 0x2d, 0x73, 0x46, - 0x5d, 0x82, 0x76, 0x84, 0x64, 0x29, 0x78, 0xed, 0x41, 0x3b, 0xe8, 0x9e, 0xf4, 0x43, 0xdb, 0xd5, - 0xf4, 0x08, 0x6d, 0x8f, 0xf0, 0x29, 0x67, 0xe5, 0xe8, 0xf8, 0xf2, 0xeb, 0x51, 0xeb, 0xd3, 0xb7, - 0xa3, 0x20, 0x63, 0x7a, 0x3a, 0x4b, 0xc2, 0x94, 0x17, 0x76, 0x30, 0x7b, 0x3c, 0x56, 0xf4, 0x1d, - 0xd6, 0x4b, 0x01, 0xaa, 0x0a, 0xa8, 0x49, 0x7d, 0xb3, 0x69, 0x90, 0x8b, 0x98, 0x50, 0x2a, 0x41, - 0x29, 0xef, 0x7a, 0xdd, 0x20, 0x17, 0xa7, 0x35, 0xe1, 0x1e, 0xa3, 0x1e, 0x17, 0x20, 0x89, 0xe6, - 0x32, 0x3e, 0x07, 0xd8, 0x18, 0x77, 0x2a, 0xa3, 0xdb, 0x68, 0x63, 0x80, 0x26, 0x71, 0x0f, 0xdd, - 0x80, 0x85, 0x80, 0x54, 0x03, 0x35, 0x09, 0x6f, 0xb7, 0x72, 0x76, 0x1b, 0x6e, 0x0c, 0xe0, 0xbe, - 0x77, 0x90, 0xfb, 0xc7, 0xad, 0x6a, 0x4a, 0x24, 0x78, 0x7b, 0x03, 0x27, 0xe8, 0x9e, 0x1c, 0xfe, - 0x75, 0xc8, 0x67, 0x90, 0xbe, 0x32, 0x4f, 0x3d, 0x1a, 0xda, 0x41, 0x1f, 0xfd, 0xc7, 0xa0, 0x4d, - 0x68, 0x72, 0x6b, 0xab, 0xe7, 0x6b, 0xf3, 0x2f, 0x37, 0x42, 0x3d, 0x05, 0x5a, 0xe7, 0x50, 0x40, - 0xa9, 0xe3, 0x39, 0xc9, 0x19, 0x25, 0x1a, 0xa8, 0xd7, 0x19, 0x38, 0x41, 0x67, 0x72, 0xfb, 0xb7, - 0xf6, 0xa6, 0x91, 0x9e, 0xdc, 0xfc, 0xf0, 0xe3, 0xf3, 0xc3, 0xad, 0xc7, 0x1a, 0x3d, 0xbf, 0x5c, - 0xf9, 0xce, 0xd5, 0xca, 0x77, 0xbe, 0xaf, 0x7c, 0xe7, 0xe3, 0xda, 0x6f, 0x5d, 0xad, 0xfd, 0xd6, - 0x97, 0xb5, 0xdf, 0x7a, 0x1b, 0x6d, 0x95, 0xfb, 0xc7, 0x32, 0xcf, 0x87, 0x78, 0x61, 0x37, 0xda, - 0x74, 0x4d, 0x76, 0xab, 0x4d, 0x1a, 0xfe, 0x0a, 0x00, 0x00, 0xff, 0xff, 0xf1, 0x5a, 0xae, 0x40, - 0xfd, 0x02, 0x00, 0x00, +var fileDescriptor_47537f11f512b254 = []byte{ + // 676 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0x4f, 0x4f, 0x13, 0x41, + 0x14, 0xef, 0x58, 0x28, 0x30, 0x60, 0x80, 0xb1, 0x81, 0xb6, 0xca, 0x02, 0xc5, 0x43, 0x23, 0xba, + 0x4b, 0xc1, 0x98, 0xc8, 0xc1, 0x04, 0x24, 0x24, 0xc4, 0x10, 0x4d, 0x8d, 0x1e, 0xbc, 0x34, 0xdb, + 0x9d, 0xc7, 0xb2, 0xba, 0xbb, 0xb3, 0xd9, 0x99, 0x96, 0xc2, 0xc9, 0xc8, 0x17, 0x30, 0xc6, 0xf8, + 0x21, 0x3c, 0xf9, 0x31, 0x38, 0x72, 0xf4, 0x24, 0x06, 0x0e, 0x7e, 0x0d, 0x33, 0xfb, 0xaf, 0xdb, + 0x16, 0x90, 0x7a, 0xda, 0x7d, 0x7f, 0x7e, 0xef, 0xfd, 0xe6, 0xbd, 0xf9, 0xed, 0xe2, 0xfb, 0xf4, + 0xd0, 0x01, 0x97, 0x5b, 0xcc, 0x6d, 0x1f, 0x1e, 0x69, 0x89, 0xa1, 0x81, 0xd5, 0x30, 0x34, 0xd1, + 0x56, 0x3d, 0x9f, 0x09, 0x46, 0xee, 0xa6, 0xb3, 0xd4, 0xc4, 0x50, 0x65, 0x56, 0x69, 0xd6, 0x60, + 0xdc, 0x61, 0x5c, 0x73, 0xb8, 0xa9, 0xb5, 0xaa, 0xf2, 0x11, 0xa2, 0x4a, 0xc5, 0x30, 0x50, 0x0f, + 0x2c, 0x2d, 0x34, 0xa2, 0x50, 0xde, 0x64, 0x26, 0x0b, 0xfd, 0xf2, 0x2d, 0xf2, 0x2a, 0x51, 0xa5, + 0x86, 0xce, 0x41, 0x6b, 0x55, 0x1b, 0x20, 0xf4, 0xaa, 0x66, 0x30, 0xcb, 0x0d, 0xe3, 0xe5, 0xaf, + 0x08, 0x4f, 0xee, 0x72, 0x73, 0xbb, 0x69, 0xef, 0x59, 0xb6, 0xfd, 0xd2, 0xa7, 0xe0, 0x93, 0x65, + 0x3c, 0xbd, 0x17, 0xda, 0xe0, 0xd7, 0x75, 0x4a, 0x7d, 0xe0, 0xbc, 0x80, 0x16, 0x50, 0x65, 0xac, + 0x36, 0x95, 0x04, 0x36, 0x42, 0x3f, 0x29, 0xe2, 0x51, 0x26, 0x51, 0x75, 0x8b, 0x16, 0x6e, 0x05, + 0x39, 0x23, 0x81, 0xbd, 0x43, 0xc9, 0x22, 0x9e, 0x80, 0xb6, 0x07, 0x86, 0x00, 0x5a, 0xdf, 0x03, + 0x28, 0x64, 0x83, 0xf0, 0x78, 0xec, 0xdb, 0x06, 0x58, 0x9f, 0xf9, 0xf4, 0xe7, 0xc7, 0x83, 0xfe, + 0x6e, 0xe5, 0x22, 0x9e, 0xed, 0x61, 0x55, 0x03, 0xee, 0x31, 0x97, 0x43, 0xf9, 0x6c, 0x08, 0x17, + 0x7b, 0x62, 0x1b, 0x4d, 0xb1, 0xcf, 0x7c, 0xeb, 0x08, 0x68, 0x17, 0x1d, 0xd4, 0x4d, 0x67, 0x0e, + 0x63, 0x9f, 0xd9, 0xb6, 0xee, 0x79, 0x1d, 0xae, 0x63, 0x91, 0x67, 0x87, 0x12, 0x1d, 0x0f, 0x7b, + 0xbe, 0x65, 0x48, 0x9a, 0xd9, 0xca, 0xf8, 0x6a, 0x51, 0x8d, 0xa6, 0x2b, 0x27, 0xa7, 0x46, 0x93, + 0x53, 0x9f, 0x33, 0xcb, 0xdd, 0x5c, 0x39, 0xf9, 0x35, 0x9f, 0xf9, 0x7e, 0x36, 0x5f, 0x31, 0x2d, + 0xb1, 0xdf, 0x6c, 0xa8, 0x06, 0x73, 0xa2, 0x55, 0x44, 0x8f, 0x47, 0x9c, 0x7e, 0xd0, 0xc4, 0xa1, + 0x07, 0x3c, 0x00, 0xf0, 0x5a, 0x58, 0x99, 0xbc, 0xc7, 0x39, 0xdd, 0x61, 0x4d, 0x57, 0x14, 0x86, + 0x16, 0x50, 0x65, 0x7c, 0x75, 0xee, 0xd2, 0x1e, 0x3b, 0xae, 0x78, 0x25, 0x77, 0xb3, 0xb9, 0x16, + 0xf5, 0x59, 0xbe, 0x41, 0x9f, 0x18, 0x54, 0x8b, 0x3a, 0xc8, 0xd3, 0xda, 0x5e, 0xb2, 0xbd, 0xe1, + 0xf0, 0xb4, 0xb6, 0x17, 0xaf, 0x6d, 0x05, 0xe7, 0x99, 0x07, 0xbe, 0x2e, 0x98, 0x2f, 0x77, 0x93, + 0x24, 0xe6, 0x82, 0x44, 0x12, 0xc7, 0xb6, 0x01, 0x62, 0x44, 0xef, 0x36, 0x47, 0xfa, 0xb6, 0x49, + 0x3e, 0x22, 0x4c, 0xba, 0xaa, 0xf2, 0x7d, 0xdd, 0x87, 0xc2, 0xe8, 0x35, 0x87, 0xdd, 0x02, 0x63, + 0xf0, 0xc3, 0xc6, 0xa0, 0xda, 0x54, 0x8a, 0xe7, 0x6b, 0xd9, 0x8b, 0x54, 0x71, 0x9e, 0x83, 0x10, + 0x36, 0x38, 0xe0, 0x8a, 0x7a, 0x4b, 0xb7, 0x2d, 0xaa, 0x0b, 0xa0, 0x85, 0xb1, 0x05, 0x54, 0x19, + 0xad, 0xdd, 0xe9, 0xc4, 0xde, 0xc6, 0xa1, 0xf5, 0x49, 0x79, 0x07, 0x53, 0xc3, 0x2a, 0x2f, 0xe1, + 0xc5, 0x2b, 0x2f, 0x58, 0x72, 0x0d, 0x8f, 0x11, 0xce, 0xef, 0x72, 0xf3, 0x8d, 0x27, 0x8b, 0x6c, + 0x81, 0xa3, 0xbb, 0x34, 0x54, 0xcf, 0x12, 0xbe, 0xcd, 0x0e, 0xdc, 0x3e, 0xe5, 0x4c, 0x04, 0xce, + 0x1b, 0xa8, 0x66, 0x16, 0x8f, 0xb8, 0x70, 0x90, 0x12, 0x4c, 0xce, 0x85, 0x03, 0xa9, 0x15, 0x22, + 0x79, 0x76, 0xd7, 0x2e, 0x2b, 0xf8, 0xde, 0x65, 0x24, 0x62, 0x96, 0xab, 0xdf, 0xb2, 0x38, 0xbb, + 0xcb, 0x4d, 0x22, 0xf0, 0x44, 0x97, 0xc4, 0x1f, 0xaa, 0xd7, 0x7c, 0x7e, 0xd4, 0x9e, 0xd3, 0x97, + 0x1e, 0x0f, 0x92, 0x9d, 0x4c, 0x28, 0x43, 0xbe, 0x20, 0x3c, 0x73, 0x85, 0x4e, 0x9f, 0x0c, 0x52, + 0xb2, 0x83, 0x2b, 0x3d, 0xfb, 0x3f, 0x5c, 0x8a, 0xd4, 0x31, 0xc2, 0xd3, 0xfd, 0x5b, 0xab, 0xfe, + 0xab, 0x6e, 0x1f, 0xa4, 0xf4, 0x74, 0x60, 0x48, 0x87, 0xc5, 0xe6, 0x8b, 0x93, 0x73, 0x05, 0x9d, + 0x9e, 0x2b, 0xe8, 0xf7, 0xb9, 0x82, 0x3e, 0x5f, 0x28, 0x99, 0xd3, 0x0b, 0x25, 0xf3, 0xf3, 0x42, + 0xc9, 0xbc, 0xab, 0xa6, 0x04, 0x70, 0xc5, 0x9f, 0xa4, 0xb5, 0xa6, 0xb5, 0xa3, 0xdf, 0x89, 0xd4, + 0x43, 0x23, 0x17, 0x7c, 0xcb, 0xd7, 0xfe, 0x06, 0x00, 0x00, 0xff, 0xff, 0xd5, 0x90, 0xe0, 0x20, + 0x7a, 0x06, 0x00, 0x00, } func (m *MsgFulfillOrderAuthorized) Marshal() (dAtA []byte, err error) { @@ -210,7 +232,7 @@ func (m *MsgFulfillOrderAuthorized) MarshalToSizedBuffer(dAtA []byte) (int, erro dAtA[i] = 0 } i-- - dAtA[i] = 0x40 + dAtA[i] = 0x48 } { size, err := m.OperatorFeeShare.MarshalToSizedBuffer(dAtA[:i]) @@ -221,28 +243,38 @@ func (m *MsgFulfillOrderAuthorized) MarshalToSizedBuffer(dAtA []byte) (int, erro i = encodeVarintTx(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x3a + dAtA[i] = 0x42 if len(m.ExpectedFee) > 0 { i -= len(m.ExpectedFee) copy(dAtA[i:], m.ExpectedFee) i = encodeVarintTx(dAtA, i, uint64(len(m.ExpectedFee))) i-- - dAtA[i] = 0x32 + dAtA[i] = 0x3a } if len(m.OperatorFeeAddress) > 0 { i -= len(m.OperatorFeeAddress) copy(dAtA[i:], m.OperatorFeeAddress) i = encodeVarintTx(dAtA, i, uint64(len(m.OperatorFeeAddress))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x32 } if len(m.LpAddress) > 0 { i -= len(m.LpAddress) copy(dAtA[i:], m.LpAddress) i = encodeVarintTx(dAtA, i, uint64(len(m.LpAddress))) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x2a + } + { + size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x22 if len(m.Price) > 0 { for iNdEx := len(m.Price) - 1; iNdEx >= 0; iNdEx-- { { @@ -306,6 +338,8 @@ func (m *MsgFulfillOrderAuthorized) Size() (n int) { n += 1 + l + sovTx(uint64(l)) } } + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) l = len(m.LpAddress) if l > 0 { n += 1 + l + sovTx(uint64(l)) @@ -462,6 +496,39 @@ func (m *MsgFulfillOrderAuthorized) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LpAddress", wireType) } @@ -493,7 +560,7 @@ func (m *MsgFulfillOrderAuthorized) Unmarshal(dAtA []byte) error { } m.LpAddress = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field OperatorFeeAddress", wireType) } @@ -525,7 +592,7 @@ func (m *MsgFulfillOrderAuthorized) Unmarshal(dAtA []byte) error { } m.OperatorFeeAddress = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: + case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExpectedFee", wireType) } @@ -557,7 +624,7 @@ func (m *MsgFulfillOrderAuthorized) Unmarshal(dAtA []byte) error { } m.ExpectedFee = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 7: + case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field OperatorFeeShare", wireType) } @@ -590,7 +657,7 @@ func (m *MsgFulfillOrderAuthorized) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 8: + case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SettlementValidated", wireType) }