@@ -389,6 +389,13 @@ func (k *keeper) SetParams(ctx sdk.Context, p types.Params) error {
389389 return err
390390 }
391391
392+ // Determine which sources are being removed so we can clean up their
393+ // latestPriceID entries. Without this cleanup the EndBlocker would
394+ // continue to discover (and skip) stale entries every block, and — more
395+ // critically — the orphaned latestPriceID state prevents the aggregator
396+ // from recovering after a remove-then-re-add cycle.
397+ oldParams , oldErr := k .Params .Get (ctx )
398+
392399 if err := k .Params .Set (ctx , p ); err != nil {
393400 return err
394401 }
@@ -423,6 +430,34 @@ func (k *keeper) SetParams(ctx sdk.Context, p types.Params) error {
423430 }
424431 }
425432 }
433+
434+ // Clean up latestPriceID entries for sources that were removed.
435+ // This prevents orphaned state from polluting the EndBlocker walk
436+ // and ensures a re-added source starts fresh.
437+ if oldErr == nil {
438+ newSourceSet := make (map [string ]struct {}, len (p .Sources ))
439+ for _ , s := range p .Sources {
440+ newSourceSet [s ] = struct {}{}
441+ }
442+
443+ for _ , s := range oldParams .Sources {
444+ if _ , ok := newSourceSet [s ]; ok {
445+ continue
446+ }
447+
448+ // Source was removed — resolve its ID and delete latestPriceID entries.
449+ sID , err := k .sourceID .Get (ctx , s )
450+ if err != nil {
451+ // No sourceID mapping means no latestPriceID to clean up.
452+ continue
453+ }
454+
455+ if err := k .removeSourceLatestPriceIDs (ctx , sID ); err != nil {
456+ return err
457+ }
458+ }
459+ }
460+
426461 // call hooks
427462 for _ , hook := range k .hooks .onSetParams {
428463 hook (ctx , p )
@@ -431,6 +466,31 @@ func (k *keeper) SetParams(ctx sdk.Context, p types.Params) error {
431466 return nil
432467}
433468
469+ // removeSourceLatestPriceIDs deletes all latestPriceID entries for the given
470+ // source ID. This is called when a source is removed from params.Sources so
471+ // that the EndBlocker no longer discovers stale entries
472+ func (k * keeper ) removeSourceLatestPriceIDs (ctx sdk.Context , sourceID uint32 ) error {
473+ var toDelete []types.PriceDataID
474+
475+ err := k .latestPriceID .Walk (ctx , nil , func (key types.PriceDataID , _ types.PriceLatestDataState ) (bool , error ) {
476+ if key .Source == sourceID {
477+ toDelete = append (toDelete , key )
478+ }
479+ return false , nil
480+ })
481+ if err != nil {
482+ return err
483+ }
484+
485+ for _ , key := range toDelete {
486+ if err := k .latestPriceID .Remove (ctx , key ); err != nil {
487+ return err
488+ }
489+ }
490+
491+ return nil
492+ }
493+
434494// GetParams returns the current x/oracle module parameters.
435495func (k * keeper ) GetParams (ctx sdk.Context ) (types.Params , error ) {
436496 return k .Params .Get (ctx )
0 commit comments