Skip to content

fix: detect managed oov2 in monitors#4940

Merged
Reinis-FRP merged 3 commits intomasterfrom
reinis-frp/monitors-detect-moov2
Mar 26, 2026
Merged

fix: detect managed oov2 in monitors#4940
Reinis-FRP merged 3 commits intomasterfrom
reinis-frp/monitors-detect-moov2

Conversation

@Reinis-FRP
Copy link
Contributor

@Reinis-FRP Reinis-FRP commented Mar 25, 2026

Motivation

Managed Optimistic Oracle V2 requests currently generate the standard OOv2 Oracle UI link in monitor alerts. That points reviewers to the wrong UI variant.

This PR fixes the link generation inside the monitor so managed OOv2 requests resolve to oracleType=Managed+Optimistic+Oracle+V2.

Summary

This PR updates OptimisticOracleContractMonitor to detect managed OOv2 contracts by reading the current EIP-1967 implementation slot.

If the configured oracle type is OptimisticOracleV2 and the contract is a UUPS proxy, _generateUILink now emits the managed UI link. The upgraded-managed dispute-window detection keeps its existing semantics, but now uses a fresh current-slot read on each proposal poll so long-running monitor processes can observe a later proxy upgrade without restart.

Details

_generateUILink is now async and all monitor alert paths await it before logging request, proposal, dispute, and settlement messages.

Managed OOv2 UI-link detection is local to packages/monitors/src/OptimisticOracleContractMonitor.js. It treats an OOv2 contract as managed when the current EIP-1967 implementation slot is non-empty.

For managed detection, the current-slot read is still cached per monitor instance, and rejected reads clear the cache before rethrowing. That preserves single-flight behavior on success while allowing managed detection to retry after transient RPC/storage failures instead of getting stuck on a permanently rejected promise.

_detectManagedOOV2Upgraded still requires managedOOV2PreUpgradeBlock and compares the current implementation to the pre-upgrade implementation. To support long-running processes, it now reads the current implementation fresh on each proposal poll, while caching the historical pre-upgrade-slot read.

Testing

  • Ran end-to-end test, running the code as in production
  • New unit tests created
  • Existing tests adequate, no new tests required
  • All existing tests pass
  • Untested

Added coverage for:

  • managed OOv2 UI link generation for OOv2 UUPS proxies
  • caching of managed detection for repeated UI-link generation
  • retrying managed detection after an initial storage-read failure
  • refreshing current implementation on repeated upgrade checks while reusing the cached pre-upgrade implementation

Executed:

./node_modules/.bin/mocha --timeout 30000 packages/monitors/test/OptimisticOracleContractMonitor.js
node -c packages/monitors/src/OptimisticOracleContractMonitor.js
node -c packages/monitors/test/OptimisticOracleContractMonitor.js

Observed result:

  • 23 passing (1m) for the monitor suite

Issue(s)

Fixes: https://linear.app/uma/issue/FE-520/moov2-proposal-links-in-oo-live-feed-use-incorrect-oracletype

Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
@linear
Copy link

linear bot commented Mar 25, 2026

@Reinis-FRP Reinis-FRP marked this pull request as ready for review March 25, 2026 10:33
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 501f329620

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +446 to +450
if (this.currentImplementationPromise === null) {
this.currentImplementationPromise = this.web3.eth.getStorageAt(
this.optimisticOracleContract.options.address,
EIP1967_IMPLEMENTATION_SLOT
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset cached implementation after storage-read failure

_getCurrentImplementation caches the raw promise from getStorageAt, so a transient RPC error on the first call stores a rejected promise permanently. After that, every _isManagedOOV2() call reuses the same rejection and always falls back to Optimistic+Oracle+V2, so managed OOv2 links stay incorrect for the lifetime of the monitor process even if the node recovers. Clearing/retrying the cache on rejection would avoid this sticky failure mode.

Useful? React with 👍 / 👎.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in a759001dd.

_getCurrentImplementation() now clears currentImplementationPromise on rejection before rethrowing, so a transient getStorageAt failure no longer poisons managed OOv2 detection for the lifetime of the monitor process.

Added a regression test that exercises the fail-then-succeed path:

  • first _generateUILink() call falls back to Optimistic+Oracle+V2
  • second _generateUILink() call retries the storage read and returns Managed+Optimistic+Oracle+V2
  • storage-read call count is 2

Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
}

_getCurrentImplementation() {
if (this.currentImplementationPromise === null) {
Copy link
Contributor

@md0x md0x Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small concern: currentImplementationPromise stays cached after the first successful read, so a long-running monitor may miss a later proxy upgrade. Simplest fix may be to skip permanent caching here, or refresh it per poll or with a short TTL.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 6e7deb2f2.

I kept the managed-vs-regular OOv2 detection cached, but split upgrade detection off that cache.

_isManagedOOV2() still uses the cached/retryable current-slot read because that is just acting as a type discriminator for the monitored OOv2 address. _detectManagedOOV2Upgraded() now reads the current implementation fresh on each proposal poll and compares it against a cached pre-upgrade implementation read at managedOOV2PreUpgradeBlock.

That keeps the Cloud Run case cheap, but also lets a long-running monitor observe a later proxy upgrade without restart.

Added regression coverage for the long-running path as well: two successive upgrade checks now re-read the current slot, reuse the cached pre-upgrade slot, and flip from false to true when the mocked current implementation changes.

break;
case OptimisticOracleType.OptimisticOracleV2:
oracleType = "Optimistic+Oracle+V2";
oracleType = (await this._isManagedOOV2()) ? "Managed+Optimistic+Oracle+V2" : "Optimistic+Oracle+V2";
Copy link
Contributor

@md0x md0x Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice addition. One thing to watch: this reads the current proxy implementation when choosing the OOv2 UI type, so historical events can flip to the managed route after an upgrade or backfill. Could we resolve the implementation at the event block here instead?

Copy link
Contributor Author

@Reinis-FRP Reinis-FRP Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think event-block resolution is the right behavior here.
This check is not trying to reconstruct the historical implementation for a given event. It’s only being used to distinguish ManagedOptimisticOracleV2 from regular OptimisticOracleV2, since both share the same oracleType in the monitor config and ABI. The UUPS implementation slot is acting as a type discriminator for the ManagedOptimisticOracleV2 (as it is upgradeable), not as historical event-state lookup.

Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
@Reinis-FRP Reinis-FRP requested a review from md0x March 26, 2026 15:59
@Reinis-FRP Reinis-FRP merged commit ab824f9 into master Mar 26, 2026
7 checks passed
@Reinis-FRP Reinis-FRP deleted the reinis-frp/monitors-detect-moov2 branch March 26, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants