diff --git a/cmd/cmd.go b/cmd/cmd.go index fd549d6..196c439 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -42,10 +42,10 @@ var initCmd = &cobra.Command{ log.Fatalf("failed to unmarshal config: %v", err) } - // if bot key dir doesn't exist, create it - if _, err := os.Stat(cfg.Bots.KeyringDir); os.IsNotExist(err) { - if err := os.MkdirAll(cfg.Bots.KeyringDir, 0o755); err != nil { - log.Fatalf("failed to create bot key directory: %v", err) + // if fulfiller key dir doesn't exist, create it + if _, err := os.Stat(cfg.Fulfillers.KeyringDir); os.IsNotExist(err) { + if err := os.MkdirAll(cfg.Fulfillers.KeyringDir, 0o755); err != nil { + log.Fatalf("failed to create fulfiller key directory: %v", err) } } @@ -94,8 +94,8 @@ var startCmd = &cobra.Command{ log.Fatalf("failed to create order client: %v", err) } - if cfg.Bots.NumberOfBots == 0 { - log.Println("no bots to start") + if cfg.Fulfillers.Scale == 0 { + log.Println("no fulfillers to start") return } @@ -125,11 +125,11 @@ func buildLogger(logLevel string) (*zap.Logger, error) { var scaleCmd = &cobra.Command{ Use: "scale [count]", - Short: "scale bot count", - Long: `scale the number of bot accounts that fulfill the eibc orders`, + Short: "scale fulfiller count", + Long: `scale the number of accounts that fulfill demand orders`, Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { - newBotCount, err := strconv.Atoi(args[0]) + newFulfillerCount, err := strconv.Atoi(args[0]) if err != nil { return } @@ -148,21 +148,21 @@ var scaleCmd = &cobra.Command{ return } - err = utils.UpdateViperConfig("bots.number_of_bots", newBotCount, viper.ConfigFileUsed()) + err = utils.UpdateViperConfig("fulfillers.scale", newFulfillerCount, viper.ConfigFileUsed()) if err != nil { return } fmt.Printf( - "bot count successfully scaled to %d, please restart the eibc process if it's running\n", - newBotCount, + "fulfiller count successfully scaled to %d, please restart the eibc process if it's running\n", + newFulfillerCount, ) }, } var versionCmd = &cobra.Command{ Use: "version", - Short: "Print the version of roller", + Short: "Print the version of eibc-client", Run: func(cmd *cobra.Command, args []string) { fmt.Println(version.BuildVersion) }, @@ -173,7 +173,6 @@ func init() { RootCmd.AddCommand(initCmd) RootCmd.AddCommand(startCmd) RootCmd.AddCommand(scaleCmd) - RootCmd.AddCommand(versionCmd) cobra.OnInitialize(config.InitConfig) diff --git a/config/config.go b/config/config.go index b461b64..483a28e 100644 --- a/config/config.go +++ b/config/config.go @@ -17,7 +17,7 @@ type Config struct { Rollapps map[string]RollappConfig `mapstructure:"rollapps"` Operator OperatorConfig `mapstructure:"operator"` - Bots BotConfig `mapstructure:"bots"` + Fulfillers FulfillerConfig `mapstructure:"fulfillers"` Validation ValidationConfig `mapstructure:"validation"` LogLevel string `mapstructure:"log_level"` @@ -34,13 +34,13 @@ type GasConfig struct { Fees string `mapstructure:"fees"` } -type BotConfig struct { - NumberOfBots int `mapstructure:"number_of_bots"` +type FulfillerConfig struct { + Scale int `mapstructure:"scale"` OperatorAddress string `mapstructure:"operator_address"` PolicyAddress string `mapstructure:"policy_address"` KeyringBackend cosmosaccount.KeyringBackend `mapstructure:"keyring_backend"` KeyringDir string `mapstructure:"keyring_dir"` - MaxOrdersPerTx int `mapstructure:"max_orders_per_tx"` + BatchSize int `mapstructure:"batch_size"` } type OperatorConfig struct { @@ -70,11 +70,11 @@ const ( defaultGasFees = "3000000000000000" + defaultHubDenom testKeyringBackend = "test" - BotNamePrefix = "bot-" + FulfillerNamePrefix = "bot-" defaultOperatorAccountName = "client" defaultOperatorGroupID = 1 defaultOperatorMinFeeShare = 0.1 - defaultNumberOfBots = 30 + defaultFulfillerScale = 30 NewOrderBufferSize = 100 defaultMaxOrdersPerTx = 10 defaultOrderRefreshInterval = 30 * time.Second @@ -118,11 +118,11 @@ func InitConfig() { viper.SetDefault("operator.group_id", defaultOperatorGroupID) viper.SetDefault("operator.min_fee_share", defaultOperatorMinFeeShare) - viper.SetDefault("bots.keyring_backend", testKeyringBackend) - viper.SetDefault("bots.keyring_dir", defaultHomeDir) - viper.SetDefault("bots.number_of_bots", defaultNumberOfBots) - viper.SetDefault("bots.max_orders_per_tx", defaultMaxOrdersPerTx) - viper.SetDefault("bots.policy_address", "") + viper.SetDefault("fulfillers.keyring_backend", testKeyringBackend) + viper.SetDefault("fulfillers.keyring_dir", defaultHomeDir) + viper.SetDefault("fulfillers.scale", defaultFulfillerScale) + viper.SetDefault("fulfillers.max_orders_per_tx", defaultMaxOrdersPerTx) + viper.SetDefault("fulfillers.policy_address", "") viper.SetDefault("validation.fallback_level", defaultValidationFallbackLevel) viper.SetDefault("validation.validation_wait_time", defaultValidationWaitTime) diff --git a/eibc/account.go b/eibc/account.go index f3113bf..63ffc46 100644 --- a/eibc/account.go +++ b/eibc/account.go @@ -14,12 +14,11 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/feegrant" "github.com/cosmos/cosmos-sdk/x/group" + "github.com/dymensionxyz/cosmosclient/cosmosclient" "github.com/google/uuid" "go.uber.org/zap" "google.golang.org/grpc/status" - "github.com/dymensionxyz/cosmosclient/cosmosclient" - "github.com/dymensionxyz/eibc-client/config" ) @@ -45,7 +44,7 @@ func addAccount(client cosmosclient.Client, name string) (string, error) { return address.String(), nil } -func getBotAccounts(client cosmosclient.Client) (accs []account, err error) { +func getFulfillerAccounts(client cosmosclient.Client) (accs []account, err error) { var accounts []account accounts, err = listAccounts(client) if err != nil { @@ -53,7 +52,7 @@ func getBotAccounts(client cosmosclient.Client) (accs []account, err error) { } for _, acc := range accounts { - if !strings.HasPrefix(acc.Name, config.BotNamePrefix) { + if !strings.HasPrefix(acc.Name, config.FulfillerNamePrefix) { continue } accs = append(accs, acc) @@ -83,15 +82,15 @@ func listAccounts(client cosmosclient.Client) ([]account, error) { return accounts, nil } -func createBotAccounts(client cosmosclient.Client, count int) (accs []account, err error) { +func createFulfillerAccounts(client cosmosclient.Client, count int) (accs []account, err error) { for range count { - botName := fmt.Sprintf("%s%s", config.BotNamePrefix, uuid.New().String()[0:5]) - addr, err := addAccount(client, botName) + fulfillerName := fmt.Sprintf("%s%s", config.FulfillerNamePrefix, uuid.New().String()[0:5]) + addr, err := addAccount(client, fulfillerName) if err != nil { return nil, fmt.Errorf("failed to create account: %w", err) } acc := account{ - Name: botName, + Name: fulfillerName, Address: addr, } accs = append(accs, acc) @@ -155,38 +154,38 @@ func waitForTx(client cosmosClient, txHash string) (*tx.GetTxResponse, error) { } } -func addBotAccounts(numBots int, clientConfig config.ClientConfig, logger *zap.Logger) ([]account, error) { - cosmosClient, err := cosmosclient.New(config.GetCosmosClientOptions(clientConfig)...) +func addFulfillerAccounts(scale int, clientConfig config.ClientConfig, logger *zap.Logger) ([]account, error) { + cClient, err := cosmosclient.New(config.GetCosmosClientOptions(clientConfig)...) if err != nil { - return nil, fmt.Errorf("failed to create cosmos client for bot: %w", err) + return nil, fmt.Errorf("failed to create cosmos client for fulfiller: %w", err) } - accs, err := getBotAccounts(cosmosClient) + accs, err := getFulfillerAccounts(cClient) if err != nil { - return nil, fmt.Errorf("failed to get bot accounts: %w", err) + return nil, fmt.Errorf("failed to get fulfiller accounts: %w", err) } - numFoundBots := len(accs) + numFoundFulfillers := len(accs) - botsAccountsToCreate := max(0, numBots) - numFoundBots - if botsAccountsToCreate > 0 { - logger.Info("creating bot accounts", zap.Int("accounts", botsAccountsToCreate)) + fulfillersAccountsToCreate := max(0, scale) - numFoundFulfillers + if fulfillersAccountsToCreate > 0 { + logger.Info("creating fulfiller accounts", zap.Int("accounts", fulfillersAccountsToCreate)) } - newAccs, err := createBotAccounts(cosmosClient, botsAccountsToCreate) + newAccs, err := createFulfillerAccounts(cClient, fulfillersAccountsToCreate) if err != nil { - return nil, fmt.Errorf("failed to create bot accounts: %w", err) + return nil, fmt.Errorf("failed to create fulfiller accounts: %w", err) } accs = slices.Concat(accs, newAccs) - if len(accs) < numBots { - return nil, fmt.Errorf("expected %d bot accounts, got %d", numBots, len(accs)) + if len(accs) < scale { + return nil, fmt.Errorf("expected %d fulfiller accounts, got %d", scale, len(accs)) } return accs, nil } -func addBotsToGroup(operatorName, operatorAddress string, groupID int, client cosmosclient.Client, newAccs []account) error { +func addFulfillersToGroup(operatorName, operatorAddress string, groupID int, client cosmosclient.Client, newAccs []account) error { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() @@ -311,7 +310,7 @@ func hasFeeGranted(client cosmosClient, granterAddr, granteeAddr string) (bool, return false, nil } -func addFeeGrantToBot(client cosmosClient, fromName string, granterAddr sdk.AccAddress, granteeAddr ...sdk.AccAddress) error { +func addFeeGrantToFulfiller(client cosmosClient, fromName string, granterAddr sdk.AccAddress, granteeAddr ...sdk.AccAddress) error { msgs := make([]sdk.Msg, 0, len(granteeAddr)) for _, addr := range granteeAddr { msg, err := feegrant.NewMsgGrantAllowance( diff --git a/eibc/lp.go b/eibc/lp.go index f7520d2..754fb90 100644 --- a/eibc/lp.go +++ b/eibc/lp.go @@ -2,12 +2,17 @@ package eibc import ( "context" + "fmt" "sync" "time" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/gogoproto/proto" "go.uber.org/zap" + + "github.com/dymensionxyz/eibc-client/types" ) type lp struct { @@ -31,6 +36,12 @@ func (l *lp) hasBalance(amount sdk.Coins) bool { return l.spendableBalance().IsAllGTE(amount) } +func (l *lp) getBalance() sdk.Coins { + l.bmu.Lock() + defer l.bmu.Unlock() + return l.balance +} + func (l *lp) setBalance(balance sdk.Coins) { l.bmu.Lock() defer l.bmu.Unlock() @@ -57,6 +68,7 @@ func (l *lp) releaseFunds(amount sdk.Coins) { func (l *lp) debitReservedFunds(amount sdk.Coins) { l.bmu.Lock() defer l.bmu.Unlock() + var fail bool l.reservedFunds, fail = l.reservedFunds.SafeSub(amount...) if fail { @@ -76,11 +88,75 @@ func (l *lp) spendableBalance() sdk.Coins { return l.balance.Sub(l.reservedFunds...) } +func (or *orderTracker) loadLPs(ctx context.Context) error { + grants, err := or.getLPGrants(ctx, &authz.QueryGranteeGrantsRequest{ + Grantee: or.policyAddress, + }) + if err != nil { + return fmt.Errorf("failed to get LP grants: %w", err) + } + + or.lpmu.Lock() + defer or.lpmu.Unlock() + + for _, grant := range grants.Grants { + if grant.Authorization == nil { + continue + } + + g := new(types.FulfillOrderAuthorization) + if err = proto.Unmarshal(grant.Authorization.Value, g); err != nil { + return fmt.Errorf("failed to unmarshal grant: %w", err) + } + + resp, err := or.getBalances(ctx, &banktypes.QuerySpendableBalancesRequest{ + Address: grant.Granter, + }) + if err != nil { + return fmt.Errorf("failed to get LP balances: %w", err) + } + + lp := &lp{ + address: grant.Granter, + rollapps: make(map[string]rollappCriteria), + balance: resp.Balances, + } + + for _, rollapp := range g.Rollapps { + // check the operator fee is the minimum for what the operator wants + if rollapp.OperatorFeeShare.Dec.LT(or.minOperatorFeeShare) { + continue + } + + denoms := make(map[string]bool) + for _, denom := range rollapp.Denoms { + denoms[denom] = true + } + lp.rollapps[rollapp.RollappId] = rollappCriteria{ + rollappID: rollapp.RollappId, + denoms: denoms, + maxPrice: rollapp.MaxPrice, + minFeePercentage: rollapp.MinLpFeePercentage.Dec, + operatorFeeShare: rollapp.OperatorFeeShare.Dec, + settlementValidated: rollapp.SettlementValidated, + } + } + + if len(lp.rollapps) == 0 { + continue + } + + or.lps[grant.Granter] = lp + } + + return nil +} + func (or *orderTracker) releaseAllReservedOrdersFunds(demandOrder ...*demandOrder) { for _, order := range demandOrder { or.lpmu.Lock() if lp, ok := or.lps[order.lpAddress]; ok { - lp.releaseFunds(order.amount) + lp.releaseFunds(order.price) } or.lpmu.Unlock() } @@ -90,7 +166,7 @@ func (or *orderTracker) debitAllReservedOrdersFunds(demandOrder ...*demandOrder) for _, order := range demandOrder { or.lpmu.Lock() if lp, ok := or.lps[order.lpAddress]; ok { - lp.debitReservedFunds(order.amount) + lp.debitReservedFunds(order.price) } or.lpmu.Unlock() } @@ -110,7 +186,7 @@ func (or *orderTracker) balanceRefresher(ctx context.Context) { func (or *orderTracker) refreshBalances(ctx context.Context) { for _, lp := range or.lps { - resp, err := or.getBalances(ctx, &types.QuerySpendableBalancesRequest{ + resp, err := or.getBalances(ctx, &banktypes.QuerySpendableBalancesRequest{ Address: lp.address, }) if err != nil { diff --git a/eibc/node_client.go b/eibc/node_client.go index b2541af..d4d8219 100644 --- a/eibc/node_client.go +++ b/eibc/node_client.go @@ -29,7 +29,8 @@ type blockValidatedResponse struct { type JSONResponse struct { Jsonrpc string `json:"jsonrpc"` Result struct { - Result string `json:"Result"` + ChainID string `json:"ChainID"` + Result string `json:"Result"` } `json:"result"` Id int `json:"id"` } @@ -45,10 +46,16 @@ const ( func newNodeClient(rollapps map[string]config.RollappConfig) (*nodeClient, error) { for rollappID, cfg := range rollapps { if cfg.MinConfirmations == 0 { - return nil, fmt.Errorf("rollapp ID %s: minimum validated nodes must be greater than 0", rollappID) + return nil, fmt.Errorf( + "rollapp ID %s: minimum validated nodes must be greater than 0", + rollappID, + ) } if len(cfg.FullNodes) < cfg.MinConfirmations { - return nil, fmt.Errorf("rollapp ID %s: not enough locations to validate blocks", rollappID) + return nil, fmt.Errorf( + "rollapp ID %s: not enough locations to validate blocks", + rollappID, + ) } } n := &nodeClient{ @@ -65,7 +72,12 @@ const ( blockValidatedPath = "/block_validated" ) -func (c *nodeClient) BlockValidated(ctx context.Context, rollappID string, height int64, expectedValidationLevel validationLevel) (bool, error) { +func (c *nodeClient) BlockValidated( + ctx context.Context, + rollappID string, + height int64, + expectedValidationLevel validationLevel, +) (bool, error) { var validatedNodes int32 var wg sync.WaitGroup rollappConfig := c.rollapps[rollappID] @@ -75,7 +87,13 @@ func (c *nodeClient) BlockValidated(ctx context.Context, rollappID string, heigh wg.Add(1) go func(ctx context.Context, rollappID, location string) { defer wg.Done() - valid, err := c.nodeBlockValidated(ctx, rollappID, location, height, expectedValidationLevel) + valid, err := c.nodeBlockValidated( + ctx, + rollappID, + location, + height, + expectedValidationLevel, + ) if err != nil { errChan <- err return @@ -110,7 +128,12 @@ func (c *nodeClient) nodeBlockValidated( } if validated.ChainID != rollappID { - return false, fmt.Errorf("invalid chain ID! want: %s, got: %s, height: %d", rollappID, validated.ChainID, height) + return false, fmt.Errorf( + "invalid chain ID! want: %s, got: %s, height: %d", + rollappID, + validated.ChainID, + height, + ) } return validated.Result >= expectedValidationLevel, nil @@ -137,7 +160,8 @@ func (c *nodeClient) getHttp(ctx context.Context, url string) (*blockValidatedRe return nil, fmt.Errorf("failed to parse result: %w", err) } blockValidated := &blockValidatedResponse{ - Result: validationLevel(result), + ChainID: jsonResp.Result.ChainID, + Result: validationLevel(result), } return blockValidated, err } diff --git a/eibc/order_client.go b/eibc/order_client.go index 12c17df..8dacc99 100644 --- a/eibc/order_client.go +++ b/eibc/order_client.go @@ -15,9 +15,9 @@ import ( ) type orderClient struct { - logger *zap.Logger - config config.Config - bots map[string]*orderFulfiller + logger *zap.Logger + config config.Config + fulfillers map[string]*orderFulfiller orderEventer *orderEventer orderPoller *orderPoller @@ -44,8 +44,8 @@ func NewOrderClient(cfg config.Config, logger *zap.Logger) (*orderClient, error) return nil, fmt.Errorf("failed to create full node clients: %w", err) } - // create bots - bots := make(map[string]*orderFulfiller) + // create fulfillers + fulfillers := make(map[string]*orderFulfiller) minOperatorFeeShare, err := sdk.NewDecFromStr(cfg.Operator.MinFeeShare) if err != nil { @@ -54,13 +54,12 @@ func NewOrderClient(cfg config.Config, logger *zap.Logger) (*orderClient, error) ordTracker := newOrderTracker( hubClient, - cfg.Bots.PolicyAddress, + cfg.Fulfillers.PolicyAddress, minOperatorFeeShare, fullNodeClient, fulfilledOrdersCh, - bots, subscriberID, - cfg.Bots.MaxOrdersPerTx, + cfg.Fulfillers.BatchSize, &cfg.Validation, orderCh, cfg.OrderPolling.Interval, // we can use the same interval for order polling and LP balance checking @@ -84,7 +83,7 @@ func NewOrderClient(cfg config.Config, logger *zap.Logger) (*orderClient, error) operatorClient, err := cosmosclient.New(config.GetCosmosClientOptions(operatorClientCfg)...) if err != nil { - return nil, fmt.Errorf("failed to create cosmos client for bot: %w", err) + return nil, fmt.Errorf("failed to create cosmos client for fulfiller: %w", err) } operatorName := cfg.Operator.AccountName @@ -93,16 +92,16 @@ func NewOrderClient(cfg config.Config, logger *zap.Logger) (*orderClient, error) return nil, fmt.Errorf("failed to get operator address: %w", err) } - botClientCfg := config.ClientConfig{ - HomeDir: cfg.Bots.KeyringDir, - KeyringBackend: cfg.Bots.KeyringBackend, + fulfillerClientCfg := config.ClientConfig{ + HomeDir: cfg.Fulfillers.KeyringDir, + KeyringBackend: cfg.Fulfillers.KeyringBackend, NodeAddress: cfg.NodeAddress, GasFees: cfg.Gas.Fees, GasPrices: cfg.Gas.Prices, FeeGranter: operatorAddress.String(), } - accs, err := addBotAccounts(cfg.Bots.NumberOfBots, botClientCfg, logger) + accs, err := addFulfillerAccounts(cfg.Fulfillers.Scale, fulfillerClientCfg, logger) if err != nil { return nil, err } @@ -111,12 +110,12 @@ func NewOrderClient(cfg config.Config, logger *zap.Logger) (*orderClient, error) granteeAddrs := make([]sdk.AccAddress, 0, len(accs)) primeAddrs := make([]string, 0, len(accs)) - var botIdx int - for botIdx = range cfg.Bots.NumberOfBots { - acc := accs[botIdx] + var fulfillerIdx int + for fulfillerIdx = range cfg.Fulfillers.Scale { + acc := accs[fulfillerIdx] exist, err := accountExists(operatorClient.Context(), acc.Address) if err != nil { - return nil, fmt.Errorf("failed to check if bot account exists: %w", err) + return nil, fmt.Errorf("failed to check if fulfiller account exists: %w", err) } if !exist { primeAddrs = append(primeAddrs, acc.Address) @@ -129,44 +128,49 @@ func NewOrderClient(cfg config.Config, logger *zap.Logger) (*orderClient, error) if !hasGrant { _, granteeAddr, err := bech32.DecodeAndConvert(acc.Address) if err != nil { - return nil, fmt.Errorf("failed to decode bot address: %w", err) + return nil, fmt.Errorf("failed to decode fulfiller address: %w", err) } granteeAddrs = append(granteeAddrs, granteeAddr) } - b, err := buildBot( + cClient, err := cosmosclient.New(config.GetCosmosClientOptions(fulfillerClientCfg)...) + if err != nil { + return nil, fmt.Errorf("failed to create cosmos client for fulfiller: %s;err: %w", acc.Name, err) + } + + f, err := newOrderFulfiller( acc, operatorAddress.String(), logger, - cfg.Bots, - botClientCfg, + cfg.Fulfillers.PolicyAddress, + cClient, orderCh, fulfilledOrdersCh, ordTracker.releaseAllReservedOrdersFunds, ordTracker.debitAllReservedOrdersFunds, ) if err != nil { - return nil, fmt.Errorf("failed to create bot: %w", err) + return nil, fmt.Errorf("failed to create fulfiller: %w", err) } - bots[b.account.Name] = b + fulfillers[f.account.Name] = f activeAccs = append(activeAccs, acc) } if len(primeAddrs) > 0 { - logger.Info("priming bot accounts", zap.Strings("addresses", primeAddrs)) + logger.Info("priming fulfiller accounts", zap.Strings("addresses", primeAddrs)) if err = primeAccounts(operatorClient, operatorName, operatorAddress, primeAddrs...); err != nil { - return nil, fmt.Errorf("failed to prime bot account: %w", err) + return nil, fmt.Errorf("failed to prime fulfiller account: %w", err) } } if len(granteeAddrs) > 0 { - logger.Info("adding fee grant to bot accounts", zap.Strings("addresses", primeAddrs)) - if err = addFeeGrantToBot(operatorClient, operatorName, operatorAddress, granteeAddrs...); err != nil { - return nil, fmt.Errorf("failed to add grant to bot: %w", err) + logger.Info("adding fee grant to fulfiller accounts", zap.Strings("addresses", primeAddrs)) + if err = addFeeGrantToFulfiller(operatorClient, operatorName, operatorAddress, granteeAddrs...); err != nil { + return nil, fmt.Errorf("failed to add grant to fulfiller: %w", err) } } - err = addBotsToGroup(operatorName, operatorAddress.String(), cfg.Operator.GroupID, operatorClient, activeAccs) + err = addFulfillersToGroup(operatorName, operatorAddress.String(), cfg.Operator.GroupID, operatorClient, activeAccs) if err != nil { return nil, err } @@ -174,7 +178,7 @@ func NewOrderClient(cfg config.Config, logger *zap.Logger) (*orderClient, error) oc := &orderClient{ orderEventer: eventer, orderTracker: ordTracker, - bots: bots, + fulfillers: fulfillers, config: cfg, logger: logger, } @@ -194,11 +198,11 @@ func NewOrderClient(cfg config.Config, logger *zap.Logger) (*orderClient, error) func getHubClient(cfg config.Config) (cosmosclient.Client, error) { // init cosmos client for order fetcher hubClientCfg := config.ClientConfig{ - HomeDir: cfg.Bots.KeyringDir, + HomeDir: cfg.Fulfillers.KeyringDir, NodeAddress: cfg.NodeAddress, GasFees: cfg.Gas.Fees, GasPrices: cfg.Gas.Prices, - KeyringBackend: cfg.Bots.KeyringBackend, + KeyringBackend: cfg.Fulfillers.KeyringBackend, } hubClient, err := cosmosclient.New(config.GetCosmosClientOptions(hubClientCfg)...) @@ -244,13 +248,13 @@ func (oc *orderClient) Start(ctx context.Context) error { } } - oc.logger.Info("starting bots...") + oc.logger.Info("starting fulfillers...") - // start bots - for _, b := range oc.bots { + // start fulfillers + for _, b := range oc.fulfillers { go func() { if err := b.start(ctx); err != nil { - oc.logger.Error("failed to bot", zap.Error(err)) + oc.logger.Error("failed to start fulfiller", zap.Error(err)) } }() } diff --git a/eibc/order_client_test.go b/eibc/order_client_test.go index 4e2c006..8720b5c 100644 --- a/eibc/order_client_test.go +++ b/eibc/order_client_test.go @@ -46,15 +46,15 @@ func TestOrderClient(t *testing.T) { name: "p2p mode, orders from poller: fulfilled", config: config.Config{ OrderPolling: config.OrderPollingConfig{ - Interval: time.Second, + Interval: 5 * time.Second, Enabled: true, }, Operator: config.OperatorConfig{ MinFeeShare: "0.1", }, - Bots: config.BotConfig{ - NumberOfBots: 3, - MaxOrdersPerTx: 4, + Fulfillers: config.FulfillerConfig{ + Scale: 3, + BatchSize: 4, }, Validation: config.ValidationConfig{ ValidationWaitTime: time.Second, @@ -148,43 +148,43 @@ func TestOrderClient(t *testing.T) { pollOrders: []Order{ { EibcOrderId: "order1", - Amount: "80", + Price: "80stake", Fee: "12stake", RollappId: "rollapp1", - BlockHeight: "1", + ProofHeight: "1", }, { EibcOrderId: "order2", - Amount: "202", + Price: "202stake", Fee: "25stake", RollappId: "rollapp2", - BlockHeight: "2", + ProofHeight: "2", }, { EibcOrderId: "order5", - Amount: "201", + Price: "201stake", Fee: "50stake", RollappId: "rollapp1", - BlockHeight: "5", + ProofHeight: "5", }, }, eventOrders: []Order{ { EibcOrderId: "order3", - Amount: "100adym", + Price: "100adym", Fee: "20adym", RollappId: "rollapp2", - BlockHeight: "3", + ProofHeight: "3", }, { EibcOrderId: "order4", - Amount: "250adym", + Price: "250adym", Fee: "35adym", RollappId: "rollapp2", - BlockHeight: "4", + ProofHeight: "4", }, { EibcOrderId: "order6", - Amount: "250adym", + Price: "250adym", Fee: "35adym", RollappId: "rollapp2", - BlockHeight: "6", + ProofHeight: "6", }, }, expectLPFulfilledOrderIDs: map[string]string{ @@ -249,11 +249,11 @@ func TestOrderClient(t *testing.T) { oc.orderEventer.eventClient.(*mockNodeClient).addOrderCh <- coretypes.ResultEvent{ Events: map[string][]string{ createdEvent + ".order_id": {order.EibcOrderId}, - createdEvent + ".price": {order.Amount}, + createdEvent + ".price": {order.Price}, createdEvent + ".packet_status": {"PENDING"}, createdEvent + ".fee": {order.Fee}, createdEvent + ".rollapp_id": {order.RollappId}, - createdEvent + ".proof_height": {order.BlockHeight}, + createdEvent + ".proof_height": {order.ProofHeight}, }, } } @@ -270,14 +270,14 @@ func TestOrderClient(t *testing.T) { lpAddr, ok := tt.expectLPFulfilledOrderIDs[o.id] require.True(t, ok) require.Equal(t, lpAddr, o.lpAddress) - expectTotalLPSpent[o.lpAddress] = expectTotalLPSpent[o.lpAddress].Add(o.amount...) + expectTotalLPSpent[o.lpAddress] = expectTotalLPSpent[o.lpAddress].Add(o.price...) } for _, lp := range oc.orderTracker.lps { assert.Truef(t, lp.reservedFunds.Empty(), "lp %s has reserved funds; got: %s", lp.address, lp.reservedFunds) expectBalance := lpBalances[lp.address].Sub(expectTotalLPSpent[lp.address]...) - assert.Truef(t, expectBalance.IsEqual(lp.balance), - "lp %s balance is not correct; expect: %s, got: %s", lp.address, expectBalance, lp.balance) + assert.Truef(t, expectBalance.IsEqual(lp.getBalance()), + "lp %s balance is not correct; expect: %s, got: %s", lp.address, expectBalance, lp.getBalance()) } }) } @@ -300,8 +300,8 @@ func setupTestOrderClient( // tracker trackerClient := hubClient - // bots - bots := make(map[string]*orderFulfiller) + // fulfillers + fulfiller := make(map[string]*orderFulfiller) ordTracker := newOrderTracker( &trackerClient, @@ -309,9 +309,8 @@ func setupTestOrderClient( minOperatorFeeShare, fullNodeClient, fulfilledOrdersCh, - bots, "subscriber", - cfg.Bots.MaxOrdersPerTx, + cfg.Fulfillers.BatchSize, &cfg.Validation, orderCh, cfg.OrderPolling.Interval, @@ -339,27 +338,30 @@ func setupTestOrderClient( chainID := "test-chain-id" - for i := range cfg.Bots.NumberOfBots { - botName := fmt.Sprintf("bot-%d", i+1) + for i := range cfg.Fulfillers.Scale { + fulfillerName := fmt.Sprintf("fulfiller-%d", i+1) hc := hubClient acc := account{ - Name: botName, - Address: botName + "-address", + Name: fulfillerName, + Address: fulfillerName + "-address", } - b := newOrderFulfiller( - orderCh, - fulfilledOrdersCh, - &hc, + b, err := newOrderFulfiller( acc, - "policyAddress", "operatorAddress", + logger, + "policyAddress", + &hc, + orderCh, + fulfilledOrdersCh, ordTracker.releaseAllReservedOrdersFunds, ordTracker.debitAllReservedOrdersFunds, - logger, ) + if err != nil { + return nil, err + } b.FulfillDemandOrders = fulfillOrdersFn - bots[b.account.Name] = b + fulfiller[b.account.Name] = b } // poller @@ -378,7 +380,7 @@ func setupTestOrderClient( oc := &orderClient{ orderEventer: eventer, orderTracker: ordTracker, - bots: bots, + fulfillers: fulfiller, config: cfg, logger: logger, orderPoller: poller, diff --git a/eibc/order_eventer.go b/eibc/order_eventer.go index cdfcb23..19b9896 100644 --- a/eibc/order_eventer.go +++ b/eibc/order_eventer.go @@ -120,12 +120,13 @@ func (e *orderEventer) parseOrdersFromEvents(res tmtypes.ResultEvent) []*demandO order := &demandOrder{ id: id, denom: fee.Denom, - amount: price, + price: price, fee: fee, status: statuses[i], rollappId: rollapps[i], proofHeight: height, validDeadline: validDeadline, + from: "event", } if !e.orderTracker.canFulfillOrder(order) { diff --git a/eibc/order_fulfiller.go b/eibc/order_fulfiller.go index 7b172ff..cfd64d8 100644 --- a/eibc/order_fulfiller.go +++ b/eibc/order_fulfiller.go @@ -14,7 +14,6 @@ import ( "github.com/dymensionxyz/cosmosclient/cosmosclient" - "github.com/dymensionxyz/eibc-client/config" "github.com/dymensionxyz/eibc-client/types" ) @@ -38,61 +37,30 @@ type cosmosClient interface { } func newOrderFulfiller( - newOrdersCh chan []*demandOrder, - fulfilledOrdersCh chan<- *orderBatch, - client cosmosClient, acc account, - policyAddress string, operatorAddress string, + logger *zap.Logger, + policyAddress string, + cClient cosmosClient, + newOrdersCh chan []*demandOrder, + fulfilledOrdersCh chan<- *orderBatch, releaseAllReservedOrdersFunds func(demandOrder ...*demandOrder), debitAllReservedOrdersFunds func(demandOrder ...*demandOrder), - logger *zap.Logger, -) *orderFulfiller { +) (*orderFulfiller, error) { o := &orderFulfiller{ account: acc, policyAddress: policyAddress, operatorAddress: operatorAddress, - client: client, + client: cClient, fulfilledOrdersCh: fulfilledOrdersCh, newOrdersCh: newOrdersCh, releaseAllReservedOrdersFunds: releaseAllReservedOrdersFunds, debitAllReservedOrdersFunds: debitAllReservedOrdersFunds, logger: logger.With(zap.String("module", "order-fulfiller"), - zap.String("bot-name", acc.Name), zap.String("address", acc.Address)), + zap.String("name", acc.Name), zap.String("address", acc.Address)), } o.FulfillDemandOrders = o.fulfillAuthorizedDemandOrders - return o -} - -// add command that creates all the bots to be used? - -func buildBot( - acc account, - operatorAddress string, - logger *zap.Logger, - cfg config.BotConfig, - clientCfg config.ClientConfig, - newOrderCh chan []*demandOrder, - fulfilledCh chan *orderBatch, - releaseAllReservedOrdersFunds func(demandOrder ...*demandOrder), - debitAllReservedOrdersFunds func(demandOrder ...*demandOrder), -) (*orderFulfiller, error) { - cosmosClient, err := cosmosclient.New(config.GetCosmosClientOptions(clientCfg)...) - if err != nil { - return nil, fmt.Errorf("failed to create cosmos client for bot: %s;err: %w", acc.Name, err) - } - - return newOrderFulfiller( - newOrderCh, - fulfilledCh, - cosmosClient, - acc, - cfg.PolicyAddress, - operatorAddress, - releaseAllReservedOrdersFunds, - debitAllReservedOrdersFunds, - logger, - ), nil + return o, nil } func (ol *orderFulfiller) start(ctx context.Context) error { @@ -121,16 +89,25 @@ func (ol *orderFulfiller) processBatch(batch []*demandOrder) error { return nil } - var ids []string + var ( + ids []string + lps []string + lpMap = make(map[string]struct{}) + ) for _, order := range batch { ids = append(ids, order.id) + lpMap[order.lpAddress] = struct{}{} + } + + for l := range lpMap { + lps = append(lps, l) } - ol.logger.Info("fulfilling orders", zap.Strings("ids", ids)) + ol.logger.Info("fulfilling orders", zap.Strings("ids", ids), zap.Strings("lps", lps)) if err := ol.FulfillDemandOrders(batch...); err != nil { ol.releaseAllReservedOrdersFunds(batch...) - return fmt.Errorf("failed to fulfill orders: ids: %v; %w", ids, err) + return fmt.Errorf("failed to fulfill orders: ids: %v; lps: %v; %w", ids, lps, err) } else { ol.debitAllReservedOrdersFunds(batch...) } @@ -143,8 +120,7 @@ func (ol *orderFulfiller) processBatch(batch []*demandOrder) error { } ol.fulfilledOrdersCh <- &orderBatch{ - orders: batch, - fulfiller: ol.account.Address, // TODO + orders: batch, } }() @@ -166,7 +142,7 @@ func (ol *orderFulfiller) fulfillAuthorizedDemandOrders(demandOrder ...*demandOr order.lpAddress, ol.operatorAddress, order.fee.Amount.String(), - order.amount, + order.price, order.operatorFeePart, order.settlementValidated, ) diff --git a/eibc/order_poller.go b/eibc/order_poller.go index 11084e5..157930d 100644 --- a/eibc/order_poller.go +++ b/eibc/order_poller.go @@ -48,15 +48,16 @@ func newOrderPoller( } const ( - ordersQuery = `{"query": "{ibcTransferDetails(filter: {network: {equalTo: \"%s\"} status: {equalTo: EibcPending}}) {nodes { eibcOrderId amount destinationChannel blockHeight rollappId eibcFee }}}"}` + ordersQuery = `{"query": "{ibcTransferDetails(filter: {network: {equalTo: \"%s\"} status: {equalTo: EibcPending}}) {nodes { eibcOrderId amount proofHeight rollappId eibcFee }}}"}` ) type Order struct { EibcOrderId string `json:"eibcOrderId"` Amount string `json:"amount"` + Price string `json:"price"` Fee string `json:"eibcFee"` RollappId string `json:"rollappId"` - BlockHeight string `json:"blockHeight"` + ProofHeight string `json:"proofHeight"` } type ordersResponse struct { @@ -128,18 +129,17 @@ func (p *orderPoller) convertOrders(demandOrders []Order) (orders []*demandOrder continue } - amountStr := fmt.Sprintf("%s%s", order.Amount, fee.Denom) - amount, err := sdk.ParseCoinsNormalized(amountStr) + price, err := sdk.ParseCoinsNormalized(order.Price) if err != nil { p.logger.Error("failed to parse amount", zap.Error(err)) continue } - var blockHeight int64 - if order.BlockHeight != "" { - blockHeight, err = strconv.ParseInt(order.BlockHeight, 10, 64) + var proofHeight int64 + if order.ProofHeight != "" { + proofHeight, err = strconv.ParseInt(order.ProofHeight, 10, 64) if err != nil { - p.logger.Error("failed to parse block height", zap.Error(err)) + p.logger.Error("failed to parse proof height", zap.Error(err)) continue } } @@ -149,12 +149,13 @@ func (p *orderPoller) convertOrders(demandOrders []Order) (orders []*demandOrder newOrder := &demandOrder{ id: order.EibcOrderId, - amount: amount, + price: price, fee: fee, denom: fee.Denom, rollappId: order.RollappId, - proofHeight: blockHeight, + proofHeight: proofHeight, validDeadline: validDeadline, + from: "indexer", } if !p.orderTracker.canFulfillOrder(newOrder) { diff --git a/eibc/order_tracker.go b/eibc/order_tracker.go index 830a24b..7efa21b 100644 --- a/eibc/order_tracker.go +++ b/eibc/order_tracker.go @@ -11,12 +11,10 @@ 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/cosmos/gogoproto/proto" "go.uber.org/zap" "google.golang.org/grpc" "github.com/dymensionxyz/eibc-client/config" - "github.com/dymensionxyz/eibc-client/types" ) type orderTracker struct { @@ -24,7 +22,6 @@ type orderTracker struct { getLPGrants getLPGrantsFn fullNodeClient *nodeClient logger *zap.Logger - bots map[string]*orderFulfiller policyAddress string fomu sync.Mutex @@ -39,6 +36,7 @@ type orderTracker struct { batchSize int validation *config.ValidationConfig + toCheckOrdersCh chan []*demandOrder fulfilledOrdersCh chan *orderBatch subscriberID string balanceRefreshInterval time.Duration @@ -55,7 +53,6 @@ func newOrderTracker( minOperatorFeeShare sdk.Dec, fullNodeClient *nodeClient, fulfilledOrdersCh chan *orderBatch, - bots map[string]*orderFulfiller, subscriberID string, batchSize int, validation *config.ValidationConfig, @@ -73,7 +70,6 @@ func newOrderTracker( fullNodeClient: fullNodeClient, pool: orderPool{orders: make(map[string]*demandOrder)}, fulfilledOrdersCh: fulfilledOrdersCh, - bots: bots, lps: make(map[string]*lp), batchSize: batchSize, validation: validation, @@ -82,35 +78,27 @@ func newOrderTracker( logger: logger.With(zap.String("module", "order-resolver")), subscriberID: subscriberID, balanceRefreshInterval: balanceRefreshInterval, + toCheckOrdersCh: make(chan []*demandOrder, batchSize), fulfilledOrders: make(map[string]*demandOrder), } } func (or *orderTracker) start(ctx context.Context) error { - if err := or.refreshLPs(ctx); err != nil { - return fmt.Errorf("failed to load LPs: %w", err) - } - - or.selectOrdersWorker(ctx) - - go or.fulfilledOrdersWorker(ctx) - - return nil -} - -func (or *orderTracker) refreshLPs(ctx context.Context) error { if err := or.loadLPs(ctx); err != nil { return fmt.Errorf("failed to load LPs: %w", err) } go or.lpLoader(ctx) go or.balanceRefresher(ctx) + go or.pullOrders(ctx) + go or.enqueueValidOrders(ctx) + go or.fulfilledOrdersWorker(ctx) return nil } func (or *orderTracker) lpLoader(ctx context.Context) { - t := time.NewTicker(5 * time.Minute) + t := time.NewTicker(30 * time.Second) for { select { case <-ctx.Done(): @@ -123,112 +111,38 @@ func (or *orderTracker) lpLoader(ctx context.Context) { } } -func (or *orderTracker) loadLPs(ctx context.Context) error { - grants, err := or.getLPGrants(ctx, &authz.QueryGranteeGrantsRequest{ - Grantee: or.policyAddress, - }) - if err != nil { - return fmt.Errorf("failed to get LP grants: %w", err) - } - - or.lpmu.Lock() - defer or.lpmu.Unlock() - - for _, grant := range grants.Grants { - if grant.Authorization == nil { - continue - } - - g := new(types.FulfillOrderAuthorization) - if err = proto.Unmarshal(grant.Authorization.Value, g); err != nil { - return fmt.Errorf("failed to unmarshal grant: %w", err) - } - - resp, err := or.getBalances(ctx, &banktypes.QuerySpendableBalancesRequest{ - Address: grant.Granter, - }) - if err != nil { - return fmt.Errorf("failed to get LP balances: %w", err) - } - - lp := &lp{ - address: grant.Granter, - rollapps: make(map[string]rollappCriteria), - balance: resp.Balances, - } - - for _, rollapp := range g.Rollapps { - // check the operator fee is the minimum for what the operator wants - if rollapp.OperatorFeeShare.Dec.LT(or.minOperatorFeeShare) { - continue - } - - denoms := make(map[string]bool) - for _, denom := range rollapp.Denoms { - denoms[denom] = true - } - lp.rollapps[rollapp.RollappId] = rollappCriteria{ - rollappID: rollapp.RollappId, - denoms: denoms, - maxPrice: rollapp.MaxPrice, - minFeePercentage: rollapp.MinLpFeePercentage.Dec, - operatorFeeShare: rollapp.OperatorFeeShare.Dec, - settlementValidated: rollapp.SettlementValidated, - } - } - - if len(lp.rollapps) == 0 { - continue - } - - or.lps[grant.Granter] = lp - } - - return nil -} - -// Demand orders are first added: -// - in sequencer mode, the first batch is sent to the output channel, and the rest of the orders are added to the pool -// - in p2p and settlement mode, all orders are added to the pool -// Then, the orders are periodically popped (fetched and deleted) from the pool and checked for validity. -// If the order is valid, it is sent to the output channel. -// If the order is not valid, it is added back to the pool. -// After the order validity deadline is expired, the order is removed permanently. -// Once an order is fulfilled, it is removed from the store -func (or *orderTracker) selectOrdersWorker(ctx context.Context) { - toCheckOrdersCh := make(chan []*demandOrder, or.batchSize) - go or.pullOrders(ctx, toCheckOrdersCh) - go or.enqueueValidOrders(ctx, toCheckOrdersCh) -} - -func (or *orderTracker) pullOrders(ctx context.Context, toCheckOrdersCh chan []*demandOrder) { - ticker := time.NewTicker(500 * time.Millisecond) +func (or *orderTracker) pullOrders(ctx context.Context) { + ticker := time.NewTicker(2 * time.Second) for { select { case <-ctx.Done(): return case <-ticker.C: - orders := or.pool.popOrders(or.batchSize) - if len(orders) == 0 { - continue - } - // in "sequencer" mode send the orders directly to be fulfilled, - // in other modes, send the orders to be checked for validity - if or.validation.FallbackLevel == config.ValidationModeSequencer { - or.outputOrdersCh <- orders - } else { - toCheckOrdersCh <- orders - } + or.checkOrders() } } } -func (or *orderTracker) enqueueValidOrders(ctx context.Context, toCheckOrdersCh <-chan []*demandOrder) { +func (or *orderTracker) checkOrders() { + orders := or.pool.popOrders(or.batchSize) + if len(orders) == 0 { + return + } + // in "sequencer" mode send the orders directly to be fulfilled, + // in other modes, send the orders to be checked for validity + if or.validation.FallbackLevel == config.ValidationModeSequencer { + or.outputOrdersCh <- orders + } else { + or.toCheckOrdersCh <- orders + } +} + +func (or *orderTracker) enqueueValidOrders(ctx context.Context) { for { select { case <-ctx.Done(): return - case orders := <-toCheckOrdersCh: + case orders := <-or.toCheckOrdersCh: validOrders, retryOrders := or.getValidAndRetryOrders(ctx, orders) if len(validOrders) > 0 { or.outputOrdersCh <- validOrders @@ -260,7 +174,7 @@ func (or *orderTracker) getValidAndRetryOrders(ctx context.Context, orders []*de 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)) + or.logger.Debug("order is not valid yet", zap.String("id", order.id), zap.String("from", order.from)) // order is not valid yet, so add it back to the pool invalidOrders = append(invalidOrders, order) } @@ -268,6 +182,7 @@ func (or *orderTracker) getValidAndRetryOrders(ctx context.Context, orders []*de } func (or *orderTracker) isOrderExpired(order *demandOrder) bool { + fmt.Printf("order.id: %s; order.validDeadline: %v\n", order.id, order.validDeadline) return time.Now().After(order.validDeadline) } @@ -292,6 +207,7 @@ func (or *orderTracker) addOrder(orders ...*demandOrder) { orders = batchToPool } or.pool.addOrder(orders...) + go or.checkOrders() } func (or *orderTracker) fulfilledOrdersWorker(ctx context.Context) { @@ -312,7 +228,7 @@ func (or *orderTracker) fulfilledOrdersWorker(ctx context.Context) { func (or *orderTracker) addFulfilledOrders(batch *orderBatch) error { or.fomu.Lock() for _, order := range batch.orders { - if len(order.amount) == 0 { + if len(order.price) == 0 { continue } // add to cache @@ -345,7 +261,8 @@ func (or *orderTracker) findLPForOrder(order *demandOrder) error { lps := or.filterLPsForOrder(order) if len(lps) == 0 { - return fmt.Errorf("no LPs found for order") + or.logger.Debug("no LPs found for order", zap.String("id", order.id)) + return nil } // randomize the list of LPs to avoid always selecting the same one @@ -363,7 +280,7 @@ func (or *orderTracker) findLPForOrder(order *demandOrder) error { order.operatorFeePart = bestLP.rollapps[order.rollappId].operatorFeeShare // optimistically deduct from the LP's balance - bestLP.reserveFunds(order.amount) + bestLP.reserveFunds(order.price) return nil } @@ -377,7 +294,7 @@ func shuffleLPs(lps []*lp) { func (or *orderTracker) filterLPsForOrder(order *demandOrder) []*lp { lps := make([]*lp, 0, len(or.lps)) for _, lp := range or.lps { - amount := order.amount + amount := order.price if !lp.hasBalance(amount) { continue } @@ -394,13 +311,13 @@ func (or *orderTracker) filterLPsForOrder(order *demandOrder) []*lp { } // check the order price does not exceed the max price - if rollapp.maxPrice.IsAllPositive() && order.amount.IsAnyGT(rollapp.maxPrice) { + if rollapp.maxPrice.IsAllPositive() && order.price.IsAnyGT(rollapp.maxPrice) { continue } // check the fee is at least the minimum for what the lp wants operatorFee := sdk.NewDecFromInt(order.fee.Amount).Mul(rollapp.operatorFeeShare).RoundInt() - amountDec := sdk.NewDecFromInt(order.amount[0].Amount.Add(order.fee.Amount)) + amountDec := sdk.NewDecFromInt(order.price[0].Amount.Add(order.fee.Amount)) minLPFee := amountDec.Mul(rollapp.minFeePercentage).RoundInt() lpFee := order.fee.Amount.Sub(operatorFee) diff --git a/eibc/types.go b/eibc/types.go index d06ecc4..5ff3f5e 100644 --- a/eibc/types.go +++ b/eibc/types.go @@ -7,14 +7,13 @@ import ( ) type orderBatch struct { - orders []*demandOrder - fulfiller string + orders []*demandOrder } type demandOrder struct { id string denom string - amount sdk.Coins + price sdk.Coins fee sdk.Coin rollappId string status string @@ -23,6 +22,7 @@ type demandOrder struct { settlementValidated bool operatorFeePart sdk.Dec lpAddress string + from string } type account struct {