-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add cobra command to batch settle pending settlements #77
base: main
Are you sure you want to change the base?
Changes from all commits
e59c26b
22decdb
484c6e0
fab4a22
1f46917
5b9b327
fa6e56e
a4073be
9352100
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"github.com/skip-mev/go-fast-solver/shared/oracle" | ||
"os/signal" | ||
"strconv" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/skip-mev/go-fast-solver/hyperlane" | ||
"github.com/skip-mev/go-fast-solver/ordersettler/types" | ||
"github.com/skip-mev/go-fast-solver/shared/clients/coingecko" | ||
"github.com/skip-mev/go-fast-solver/shared/clients/utils" | ||
"github.com/skip-mev/go-fast-solver/shared/contracts/fast_transfer_gateway" | ||
"github.com/skip-mev/go-fast-solver/shared/evmrpc" | ||
"github.com/skip-mev/go-fast-solver/shared/txexecutor/evm" | ||
|
||
"github.com/skip-mev/go-fast-solver/db/gen/db" | ||
"github.com/skip-mev/go-fast-solver/ordersettler" | ||
"github.com/skip-mev/go-fast-solver/shared/clientmanager" | ||
"github.com/skip-mev/go-fast-solver/shared/config" | ||
"github.com/skip-mev/go-fast-solver/shared/keys" | ||
"github.com/skip-mev/go-fast-solver/shared/lmt" | ||
"github.com/skip-mev/go-fast-solver/shared/txexecutor/cosmos" | ||
"github.com/spf13/cobra" | ||
"go.uber.org/zap" | ||
"golang.org/x/net/context" | ||
) | ||
|
||
var settleCmd = &cobra.Command{ | ||
Use: "settle-orders", | ||
Short: "Settle pending order batches", | ||
Long: `Settle all pending order batches immediately without any threshold checks (ignoring configured BatchUUSDCSettleUpThreshold).`, | ||
Example: `solver settle-orders`, | ||
Run: settleOrders, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(settleCmd) | ||
} | ||
|
||
func settleOrders(cmd *cobra.Command, args []string) { | ||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) | ||
defer cancel() | ||
|
||
lmt.ConfigureLogger() | ||
ctx = lmt.LoggerContext(ctx) | ||
|
||
keyStoreType, err := cmd.Flags().GetString("key-store-type") | ||
if err != nil { | ||
lmt.Logger(ctx).Error("Failed to get key-store-type", zap.Error(err)) | ||
return | ||
} | ||
|
||
keysPath, err := cmd.Flags().GetString("keys") | ||
if err != nil { | ||
lmt.Logger(ctx).Error("Failed to get keys path", zap.Error(err)) | ||
return | ||
} | ||
|
||
configPath, err := cmd.Flags().GetString("config") | ||
if err != nil { | ||
lmt.Logger(ctx).Error("Failed to get config path", zap.Error(err)) | ||
return | ||
} | ||
|
||
cfg, err := config.LoadConfig(configPath) | ||
if err != nil { | ||
lmt.Logger(ctx).Error("Unable to load config", zap.Error(err)) | ||
return | ||
} | ||
|
||
ctx = config.ConfigReaderContext(ctx, config.NewConfigReader(cfg)) | ||
|
||
keyStore, err := keys.GetKeyStore(keyStoreType, keys.GetKeyStoreOpts{KeyFilePath: keysPath}) | ||
if err != nil { | ||
lmt.Logger(ctx).Fatal("Unable to load keystore", zap.Error(err)) | ||
} | ||
|
||
cosmosTxExecutor := cosmos.DefaultSerializedCosmosTxExecutor() | ||
evmTxExecutor := evm.DefaultEVMTxExecutor() | ||
|
||
clientManager := clientmanager.NewClientManager(keyStore, cosmosTxExecutor) | ||
|
||
database, err := setupDatabase(ctx, cmd) | ||
if err != nil { | ||
lmt.Logger(ctx).Fatal("Failed to setup database", zap.Error(err)) | ||
} | ||
|
||
evmManager := evmrpc.NewEVMRPCClientManager() | ||
rateLimitedClient := utils.DefaultRateLimitedHTTPClient(3) | ||
coingeckoClient := coingecko.NewCoingeckoClient(rateLimitedClient, "https://api.coingecko.com/api/v3/", "") | ||
cachedCoinGeckoClient := coingecko.NewCachedPriceClient(coingeckoClient, 15*time.Minute) | ||
txPriceOracle := oracle.NewOracle(cachedCoinGeckoClient) | ||
|
||
hype, err := hyperlane.NewMultiClientFromConfig(ctx, evmManager, keyStore, txPriceOracle, evmTxExecutor) | ||
if err != nil { | ||
lmt.Logger(ctx).Fatal("creating hyperlane multi client from config", zap.Error(err)) | ||
} | ||
|
||
relayer := hyperlane.NewRelayer(hype, make(map[string]string)) | ||
relayerRunner := hyperlane.NewRelayerRunner(database, hype, relayer) | ||
|
||
settler, err := ordersettler.NewOrderSettler(ctx, database, clientManager, relayerRunner) | ||
if err != nil { | ||
lmt.Logger(ctx).Error("creating order settler", zap.Error(err)) | ||
return | ||
} | ||
|
||
chains, err := config.GetConfigReader(ctx).GetAllChainConfigsOfType(config.ChainType_COSMOS) | ||
if err != nil { | ||
lmt.Logger(ctx).Error("error getting Cosmos chains", zap.Error(err)) | ||
return | ||
} | ||
|
||
var pendingSettlements []db.OrderSettlement | ||
for _, chain := range chains { | ||
if chain.FastTransferContractAddress == "" { | ||
continue | ||
} | ||
|
||
bridgeClient, err := clientManager.GetClient(ctx, chain.ChainID) | ||
if err != nil { | ||
lmt.Logger(ctx).Error("failed to get client", | ||
zap.String("chainID", chain.ChainID), | ||
zap.Error(err)) | ||
continue | ||
} | ||
|
||
fills, err := bridgeClient.OrderFillsByFiller(ctx, chain.FastTransferContractAddress, chain.SolverAddress) | ||
if err != nil { | ||
lmt.Logger(ctx).Error("getting order fills", | ||
zap.String("chainID", chain.ChainID), | ||
zap.Error(err)) | ||
continue | ||
} | ||
|
||
// For each fill, check if it needs settlement | ||
for _, fill := range fills { | ||
sourceChainID, err := config.GetConfigReader(ctx).GetChainIDByHyperlaneDomain(strconv.Itoa(int(fill.SourceDomain))) | ||
if err != nil { | ||
lmt.Logger(ctx).Error("failed to get source chain ID", | ||
zap.Uint32("domain", fill.SourceDomain), | ||
zap.Error(err)) | ||
continue | ||
} | ||
|
||
sourceGatewayAddress, err := config.GetConfigReader(ctx).GetGatewayContractAddress(sourceChainID) | ||
if err != nil { | ||
lmt.Logger(ctx).Error("getting source gateway address", | ||
zap.String("chainID", sourceChainID), | ||
zap.Error(err)) | ||
continue | ||
} | ||
|
||
sourceBridgeClient, err := clientManager.GetClient(ctx, sourceChainID) | ||
if err != nil { | ||
lmt.Logger(ctx).Error("getting source chain client", | ||
zap.String("chainID", sourceChainID), | ||
zap.Error(err)) | ||
continue | ||
} | ||
|
||
status, err := sourceBridgeClient.OrderStatus(ctx, sourceGatewayAddress, fill.OrderID) | ||
if err != nil { | ||
lmt.Logger(ctx).Error("getting order status", | ||
zap.String("orderID", fill.OrderID), | ||
zap.Error(err)) | ||
continue | ||
} | ||
|
||
if status != fast_transfer_gateway.OrderStatusUnfilled { | ||
continue | ||
} | ||
|
||
pendingSettlements = append(pendingSettlements, db.OrderSettlement{ | ||
SourceChainID: sourceChainID, | ||
DestinationChainID: chain.ChainID, | ||
SourceChainGatewayContractAddress: sourceGatewayAddress, | ||
OrderID: fill.OrderID, | ||
}) | ||
} | ||
} | ||
|
||
if len(pendingSettlements) == 0 { | ||
fmt.Println("No pending settlement batches found") | ||
return | ||
} | ||
|
||
batches := types.IntoSettlementBatchesByChains(pendingSettlements) | ||
fmt.Printf("Found %d pending settlement batches\n", len(batches)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here too |
||
|
||
hashes, err := settler.SettleBatches(ctx, batches) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that this will initiate settlements but not relay the hyperlane message to the destination. How were you intending for the relay to complete? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the batch relays There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The hyperlane validator set usually takes a bit to generate the checkpoints signatures. What happens if those signatures aren't generated here? Were you able to test this? |
||
if err != nil { | ||
lmt.Logger(ctx).Error("settling pending batches", zap.Error(err)) | ||
return | ||
} | ||
|
||
for i, batch := range batches { | ||
hash := hashes[i] | ||
|
||
fmt.Printf("Initiated settlement batch %d:\n", i+1) | ||
fmt.Printf("Source Chain: %s\n", batch.SourceChainID()) | ||
fmt.Printf("Destination Chain: %s\n", batch.DestinationChainID()) | ||
fmt.Printf("Number of Orders: %d\n", len(batch.OrderIDs())) | ||
fmt.Printf("Transaction Hash: %s\n", hash) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use the logging library