package keeper import ( "fmt" "strings" "time" "github.com/0glabs/0g-chain/x/evmutil/types" sdk "github.com/cosmos/cosmos-sdk/types" ) // sequenceManager implements the SequenceManager interface type sequenceManager struct { keeper Keeper } // NewSequenceManager creates a new sequence manager func NewSequenceManager(k Keeper) types.SequenceManager { return &sequenceManager{ keeper: k, } } // GetNextSequence implements SequenceManager func (sm *sequenceManager) GetNextSequence(ctx sdk.Context, addr sdk.AccAddress) (uint64, error) { acc := sm.keeper.accountKeeper.GetAccount(ctx, addr) if acc == nil { return 0, fmt.Errorf("account not found: %s", addr) } return acc.GetSequence(), nil } // ValidateSequence implements SequenceManager func (sm *sequenceManager) ValidateSequence(ctx sdk.Context, addr sdk.AccAddress, sequence uint64) error { expected, err := sm.GetNextSequence(ctx, addr) if err != nil { return fmt.Errorf("failed to get account sequence: %w", err) } if sequence != expected { return fmt.Errorf("%w: got %d, expected %d", types.ErrSequenceMismatch, sequence, expected) } return nil } // IncrementSequence implements SequenceManager func (sm *sequenceManager) IncrementSequence(ctx sdk.Context, addr sdk.AccAddress) error { acc := sm.keeper.accountKeeper.GetAccount(ctx, addr) if acc == nil { return fmt.Errorf("account not found: %s", addr) } if err := acc.SetSequence(acc.GetSequence() + 1); err != nil { return fmt.Errorf("failed to increment sequence: %w", err) } sm.keeper.accountKeeper.SetAccount(ctx, acc) return nil } // ExecuteWithRetry executes a message with retry mechanism for sequence mismatch func (k Keeper) ExecuteWithRetry( ctx sdk.Context, msg sdk.Msg, config types.RetryConfig, ) error { attempt := 0 backoff := config.BackoffInterval for attempt < config.MaxAttempts { err := k.Execute(ctx, msg) if err == nil { return nil } if !isSequenceMismatchError(err) { return err } // Wait before retry time.Sleep(backoff) // Exponential backoff backoff = time.Duration(float64(backoff) * 1.5) if backoff > config.MaxBackoff { backoff = config.MaxBackoff } attempt++ } return fmt.Errorf("%w: sequence mismatch persists", types.ErrMaxRetriesReached) } // isSequenceMismatchError checks if an error is a sequence mismatch error func isSequenceMismatchError(err error) bool { if err == nil { return false } return strings.Contains(err.Error(), "account sequence mismatch") } // Execute executes a message func (k Keeper) Execute(ctx sdk.Context, msg sdk.Msg) error { // Implementation depends on message type switch m := msg.(type) { case *types.MsgConvertCoinToERC20: _, err := k.ConvertCoinToERC20(ctx, sdk.AccAddress(m.Initiator), m.Receiver, *m.Amount) return err case *types.MsgConvertERC20ToCoin: _, err := k.ConvertERC20ToCoin(ctx, m.Initiator, sdk.AccAddress(m.Receiver), m.ZgChainERC20Address, m.Amount) return err default: return fmt.Errorf("unsupported message type: %T", msg) } }