Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
nadim-az committed Jan 7, 2025
1 parent 5505f8e commit 4c616c2
Showing 1 changed file with 127 additions and 92 deletions.
219 changes: 127 additions & 92 deletions cmd/solvercli/cmd/initiate_timeout.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
package cmd

import (
"encoding/hex"
"fmt"
"math/big"
"os/signal"
"strings"
"syscall"
"time"

"github.com/skip-mev/go-fast-solver/db/gen/db"
"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/contracts/fast_transfer_gateway"
"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/skip-mev/go-fast-solver/db/gen/db"

"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/skip-mev/go-fast-solver/shared/config"
"github.com/skip-mev/go-fast-solver/shared/contracts/fast_transfer_gateway"
"github.com/skip-mev/go-fast-solver/shared/keys"
"github.com/skip-mev/go-fast-solver/shared/lmt"
"github.com/spf13/cobra"
"go.uber.org/zap"
"golang.org/x/net/context"
Expand All @@ -32,20 +31,19 @@ var initiateTimeoutCmd = &cobra.Command{
Long: `Initiate timeout for an expired order that hasn't been filled. Note that the timeout transaction needs
to be relayed separately.`,
Example: `solver initiate-timeout \
--order-id <order_id> \
--tx-hash <tx_hash> \
--chain-id <chain_id>`,
--source-chain-id <chain_id> \
--destination-chain-id <chain_id>`,
Run: initiateTimeout,
}

func init() {
rootCmd.AddCommand(initiateTimeoutCmd)
initiateTimeoutCmd.Flags().String("order-id", "", "ID of the order to timeout")
initiateTimeoutCmd.Flags().String("chain-id", "", "Chain ID where the order was created")
initiateTimeoutCmd.Flags().String("source-chain-id", "", "Source chain ID")
initiateTimeoutCmd.Flags().String("destination-chain-id", "", "Destination chain ID")
initiateTimeoutCmd.Flags().String("tx-hash", "", "Transaction hash that created the order")
initiateTimeoutCmd.Flags().String("checkpoint-storage-location-override", "{}", "map of validator addresses to storage locations")

requiredFlags := []string{"order-id", "chain-id", "tx-hash"}
requiredFlags := []string{"source-chain-id", "destination-chain-id", "tx-hash"}
for _, flag := range requiredFlags {
if err := initiateTimeoutCmd.MarkFlagRequired(flag); err != nil {
panic(fmt.Sprintf("failed to mark %s flag as required: %v", flag, err))
Expand All @@ -60,139 +58,146 @@ func initiateTimeout(cmd *cobra.Command, args []string) {
lmt.ConfigureLogger()
ctx = lmt.LoggerContext(ctx)

orderID, chainID, timeoutTxHash, err := getRequiredFlags(cmd)
sourceChainID, destinationChainID, timeoutTxHash, err := getRequiredFlags(cmd)
if err != nil {
lmt.Logger(ctx).Error("Failed to get required flags", zap.Error(err))
return
}

cfg, keyStore, err := setupConfig(cmd)
if err != nil {
lmt.Logger(ctx).Error("Failed to setup config", zap.Error(err))
return
}
ctx = config.ConfigReaderContext(ctx, config.NewConfigReader(*cfg))

sourceChainConfig, err := config.GetConfigReader(ctx).GetChainConfig(chainID)
sourceChainConfig, err := config.GetConfigReader(ctx).GetChainConfig(sourceChainID)
if err != nil {
lmt.Logger(ctx).Error("source chain not found in config", zap.String("sourceChainID", chainID))
lmt.Logger(ctx).Error("Source chain not found in config", zap.String("sourceChainID", sourceChainID))
return
}

if sourceChainConfig.Type != config.ChainType_EVM {
lmt.Logger(ctx).Error(
"source chain must be of type evm",
zap.String("sourceChainID", chainID),
zap.String("sourceChainType", string(sourceChainConfig.Type)),
)
lmt.Logger(ctx).Error("Source chain must be an EVM chain",
zap.String("sourceChainID", sourceChainID),
zap.String("chainType", string(sourceChainConfig.Type)))
return
}
gatewayAddr := sourceChainConfig.FastTransferContractAddress

client, gateway, err := setupGatewayContract(ctx, sourceChainConfig, gatewayAddr)
if err != nil {
lmt.Logger(ctx).Error("Failed to setup fast transfer gateway contract", zap.Error(err))
sourceGatewayAddr := sourceChainConfig.FastTransferContractAddress
if sourceGatewayAddr == "" {
lmt.Logger(ctx).Error("Gateway contract address not found in config", zap.String("sourceChainID", sourceChainID))
return
}

order, err := getOrderFromContract(ctx, gateway, client, orderID, chainID, gatewayAddr, timeoutTxHash)
order, err := getEVMOrderDetails(ctx, sourceChainConfig, sourceChainID, destinationChainID, sourceGatewayAddr, timeoutTxHash)
if err != nil {
lmt.Logger(ctx).Error("Failed to get order from contract", zap.Error(err))
lmt.Logger(ctx).Error("Failed to get order details", zap.Error(err))
return
}

if err := verifyOrderTimeout(ctx, order); err != nil {
lmt.Logger(ctx).Error("Failed to verify order timeout", zap.Error(err))
return
}

destinationChainConfig, err := config.GetConfigReader(ctx).GetChainConfig(destinationChainID)
if err != nil {
lmt.Logger(ctx).Error("Destination chain not found in config", zap.String("destinationChainID", destinationChainID))
return
}

if destinationChainConfig.Type != config.ChainType_COSMOS {
lmt.Logger(ctx).Error("Destination chain must be a Cosmos chain",
zap.String("destinationChainID", destinationChainID),
zap.String("chainType", string(destinationChainConfig.Type)))
return
}

destinationGatewayAddr := destinationChainConfig.FastTransferContractAddress
if destinationGatewayAddr == "" {
lmt.Logger(ctx).Error("Gateway contract address not found in config", zap.String("destinationChainID", destinationChainID))
return
}

cosmosTxExecutor := cosmos.DefaultSerializedCosmosTxExecutor()
clientManager := clientmanager.NewClientManager(keyStore, cosmosTxExecutor)

bridgeClient, err := clientManager.GetClient(ctx, chainID)
bridgeClient, err := clientManager.GetClient(ctx, destinationChainID)
if err != nil {
lmt.Logger(ctx).Error("Failed to create bridge client", zap.Error(err))
return
}

timeoutTxHash, _, _, err = bridgeClient.InitiateTimeout(ctx, *order, gatewayAddr)
timeoutTxHash, _, _, err = bridgeClient.InitiateTimeout(ctx, *order, destinationGatewayAddr)
if err != nil {
lmt.Logger(ctx).Error("Failed to initiate timeout", zap.Error(err))
return
}

fmt.Printf("Successfully initiated timeout for order %s\n", orderID)
fmt.Printf("Successfully initiated timeout for order %s\n", order.OrderID)
fmt.Printf("Timeout transaction hash: %s\n", timeoutTxHash)
fmt.Printf("Note: You must relay this transaction using:\n")
fmt.Printf("solver relay --origin-chain-id %s --origin-tx-hash %s\n", destinationChainID, timeoutTxHash)
}

func getRequiredFlags(cmd *cobra.Command) (string, string, string, error) {
orderID, err := cmd.Flags().GetString("order-id")
func getEVMOrderDetails(ctx context.Context, chainConfig config.ChainConfig, chainID, destinationChainID, gatewayAddr, txHash string) (*db.Order, error) {
client, err := ethclient.Dial(chainConfig.EVM.RPC)
if err != nil {
return "", "", "", fmt.Errorf("failed to get order-id: %w", err)
return nil, fmt.Errorf("failed to connect to EVM network: %w", err)
}
defer client.Close()

chainID, err := cmd.Flags().GetString("chain-id")
gateway, err := fast_transfer_gateway.NewFastTransferGateway(common.HexToAddress(gatewayAddr), client)
if err != nil {
return "", "", "", fmt.Errorf("failed to get chain-id: %w", err)
return nil, fmt.Errorf("failed to create gateway contract: %w", err)
}

txHash, err := cmd.Flags().GetString("tx-hash")
tx, isPending, err := client.TransactionByHash(ctx, common.HexToHash(txHash))
if err != nil {
return "", "", "", fmt.Errorf("failed to get tx-hash: %w", err)
return nil, fmt.Errorf("failed to get transaction: %w", err)
}

return orderID, chainID, txHash, nil
}

func setupConfig(cmd *cobra.Command) (*config.Config, keys.KeyStore, error) {
configPath, err := cmd.Flags().GetString("config")
if err != nil {
return nil, nil, fmt.Errorf("failed to get config path: %w", err)
if isPending {
return nil, fmt.Errorf("transaction is still pending")
}

cfg, err := config.LoadConfig(configPath)
receipt, err := client.TransactionReceipt(ctx, tx.Hash())
if err != nil {
return nil, nil, fmt.Errorf("unable to load config: %w", err)
return nil, fmt.Errorf("failed to get transaction receipt: %w", err)
}

keyStoreType, err := cmd.Flags().GetString("key-store-type")
contractABI, err := abi.JSON(strings.NewReader(fast_transfer_gateway.FastTransferGatewayABI))
if err != nil {
return nil, nil, fmt.Errorf("failed to get key-store-type: %w", err)
return nil, fmt.Errorf("failed to parse ABI: %w", err)
}

keysPath, err := cmd.Flags().GetString("keys")
if err != nil {
return nil, nil, fmt.Errorf("failed to get keys path: %w", err)
var orderIDBytes common.Hash
for _, log := range receipt.Logs {
if len(log.Topics) > 0 {
if log.Topics[0] == contractABI.Events["OrderSubmitted"].ID {
orderIDBytes = log.Topics[1]
fmt.Println("DEBUG: Found OrderSubmitted event, raw order ID bytes:", log.Topics[1].Hex())
fmt.Println("DEBUG: Event Data:", hex.EncodeToString(log.Data))
break
}
}
}

keyStore, err := keys.GetKeyStore(keyStoreType, keys.GetKeyStoreOpts{KeyFilePath: keysPath})
if err != nil {
return nil, nil, fmt.Errorf("unable to load keystore: %w", err)
if orderIDBytes == (common.Hash{}) {
return nil, fmt.Errorf("OrderSubmitted event not found in transaction logs")
}

return &cfg, keyStore, nil
}
orderID := strings.ToUpper(orderIDBytes.Hex()[2:])

func getOrderFromContract(ctx context.Context, gateway *fast_transfer_gateway.FastTransferGateway, client *ethclient.Client, orderID string, chainID string, gatewayAddr string, txHash string) (*db.Order, error) {
orderStatus, err := gateway.OrderStatuses(nil, common.HexToHash(orderID))
orderStatus, err := gateway.OrderStatuses(nil, orderIDBytes)
if err != nil {
return nil, fmt.Errorf("failed to get order status: %w", err)
fmt.Println("Error checking order status:", err)
}

if orderStatus != 0 { // 0 = Pending
return nil, fmt.Errorf("order is not in pending status (status: %d)", orderStatus)
}

tx, isPending, err := client.TransactionByHash(ctx, common.HexToHash(txHash))
if err != nil {
return nil, fmt.Errorf("failed to get transaction: %w", err)
}
if isPending {
return nil, fmt.Errorf("transaction is still pending")
}

contractABI, err := abi.JSON(strings.NewReader(fast_transfer_gateway.FastTransferGatewayABI))
if err != nil {
return nil, fmt.Errorf("failed to parse ABI: %w", err)
}

method, err := contractABI.MethodById(tx.Data()[:4])
if err != nil {
return nil, fmt.Errorf("failed to get method from transaction data: %w", err)
Expand All @@ -211,45 +216,75 @@ func getOrderFromContract(ctx context.Context, gateway *fast_transfer_gateway.Fa
recipient := args[1].([32]byte)
amountIn := args[2].(*big.Int)
amountOut := args[3].(*big.Int)
destinationDomain := args[4].(uint32)
timeoutTimestamp := args[5].(uint64)

order := &db.Order{
return &db.Order{
OrderID: orderID,
SourceChainID: chainID,
DestinationChainID: fmt.Sprintf("%d", destinationDomain),
DestinationChainID: destinationChainID,
TimeoutTimestamp: time.Unix(int64(timeoutTimestamp), 0),
Sender: sender[:],
Recipient: recipient[:],
AmountIn: amountIn.String(),
AmountOut: amountOut.String(),
Nonce: int64(tx.Nonce()),
SourceChainGatewayContractAddress: gatewayAddr,
}, nil
}

func getRequiredFlags(cmd *cobra.Command) (string, string, string, error) {
sourceChainId, err := cmd.Flags().GetString("source-chain-id")
if err != nil {
return "", "", "", fmt.Errorf("failed to get source-chain-id: %w", err)
}

return order, nil
}
destinationChainId, err := cmd.Flags().GetString("destination-chain-id")
if err != nil {
return "", "", "", fmt.Errorf("failed to get destination-chain-id: %w", err)
}

func verifyOrderTimeout(ctx context.Context, order *db.Order) error {
if !time.Now().UTC().After(order.TimeoutTimestamp) {
return fmt.Errorf("order has not timed out yet (timeout: %s, current: %s)",
order.TimeoutTimestamp.UTC(),
time.Now().UTC())
txHash, err := cmd.Flags().GetString("tx-hash")
if err != nil {
return "", "", "", fmt.Errorf("failed to get tx-hash: %w", err)
}
return nil

return sourceChainId, destinationChainId, txHash, nil
}

func setupGatewayContract(ctx context.Context, sourceChainConfig config.ChainConfig, gatewayAddr string) (*ethclient.Client, *fast_transfer_gateway.FastTransferGateway, error) {
client, err := ethclient.Dial(sourceChainConfig.EVM.RPC)
func setupConfig(cmd *cobra.Command) (*config.Config, keys.KeyStore, error) {
configPath, err := cmd.Flags().GetString("config")
if err != nil {
lmt.Logger(ctx).Error("Failed to connect to the network", zap.Error(err))
return nil, nil, fmt.Errorf("failed to connect to the network: %w", err)
return nil, nil, fmt.Errorf("failed to get config path: %w", err)
}

gateway, err := fast_transfer_gateway.NewFastTransferGateway(common.HexToAddress(gatewayAddr), client)
cfg, err := config.LoadConfig(configPath)
if err != nil {
return nil, nil, fmt.Errorf("failed to create gateway contract: %w", err)
return nil, nil, fmt.Errorf("unable to load config: %w", err)
}

return client, gateway, nil
keyStoreType, err := cmd.Flags().GetString("key-store-type")
if err != nil {
return nil, nil, fmt.Errorf("failed to get key-store-type: %w", err)
}

keysPath, err := cmd.Flags().GetString("keys")
if err != nil {
return nil, nil, fmt.Errorf("failed to get keys path: %w", err)
}

keyStore, err := keys.GetKeyStore(keyStoreType, keys.GetKeyStoreOpts{KeyFilePath: keysPath})
if err != nil {
return nil, nil, fmt.Errorf("unable to load keystore: %w", err)
}

return &cfg, keyStore, nil
}

func verifyOrderTimeout(ctx context.Context, order *db.Order) error {
if !time.Now().UTC().After(order.TimeoutTimestamp) {
return fmt.Errorf("order has not timed out yet (timeout: %s, current: %s)",
order.TimeoutTimestamp.UTC(),
time.Now().UTC())
}
return nil
}

0 comments on commit 4c616c2

Please sign in to comment.