Organization
- @Ondofinance
Engagement Type
Cantina Reviews
Period
-
Repositories
Researchers
Findings
Low Risk
1 findings
0 fixed
1 acknowledged
Informational
4 findings
2 fixed
2 acknowledged
Low Risk1 finding
Contract deviations from Notion spec
State
- Acknowledged
Severity
- Severity: Low
Submitted by
r0bert
Description
Several behaviors differ from the stated design for the Chainlink-integrated sValue oracle:
- Pause lead time not enforced:
scheduleCorporateActionPauseallows immediate or any future pause start; there is no 24h-in-advance requirement. - Freeze-at-pause-start support incomplete:
getSValueonly returns(sValue, paused)and does not includepauseStartTime,pendingSValue, or any price-at-pause-start.assetDatais public, so callers can readpauseStartTime/pendingSValuedirectly, but there is no built-in “freeze price at pause start” mechanism. - Proportional drift over elapsed time not implemented: the code uses a flat per-update cap
allowedDriftBpsplusdriftCooldown, not the “min(1%, 1% * timeDelta/24h)” proportional rule from the spec. - Non-reverting read not honored:
getSValuereverts on unknown assets instead of returning a sentinel, contradicting the “oracle should never revert execution” assumption (though acceptable if all assets are preconfigured and calls are gated). - Pause duration floor too low for intended freeze: minimum pause can be set to 600s; spec expectation implies a stronger freeze window. Admin can reduce the safety window via
setMinimumPauseDuration. - Pause metadata not surfaced in
getSValue: pending values and pause start are not in the primary read API. Consumers must readassetDatadirectly. If the intended API is onlygetSValue, this omits needed context for off-chain logic.
Recommendation
Consider aligning the contract with the published spec: enforce a minimum lead time for pauses; expose
pauseStartTimeandpendingSValue(or price-at-pause-start) in the read API to support freeze semantics, implement proportional drift relative to elapsed time, make reads non-reverting with sentinel values for unknown assets (if required by ops), raise the minimum pause floor to match the desired freeze window and surface pause metadata in the primary read path or document reliance onassetData. Document any intentional deviations if kept.Ondo Finance: Acknowledged. The original design document is somewhat out of date but we confirm that the current implementation reflects the intended and up to date spec.
Informational4 findings
Unused custom error
Description
The interface declares an
UnknownAsset()error but theSyntheticSharesOraclecontract implementation never uses it.Recommendation
Either remove the unused
UnknownAsseterror from the interface or use it consistently for missing-asset checks to align the ABI with the on-chain behavior.Ondo Finance: Fixed in PR #500.
Cantina: Fix verified.
getSValueBatch reverts on unknown asset
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
r0bert
Description
getSValueBatchcalls_getSValuefor each asset and will revert if any asset is missing (AssetNotFound). This makes the batch all-or-nothing. A single unknown asset aborts the entire call. If the intended behavior is to retrieve what is available and skip unknown entries, this revert pattern prevents partial results.Recommendation
If partial results are desired, return a sentinel/flag per index (e.g.,
sValue=0,paused=falseand a booleanfound) instead of reverting, or pre-check existence and skip/zero-fill unknown assets while still completing the batch.Ondo Finance: Acknowledged. Reverting
getSValueBatchupon nonexistent asset is the desirable behavior. Updated the design doc to reflect this.Pause flag is advisory only
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
r0bert
Description
Corporate-action pauses only toggle a boolean returned by
getSValueand have no onchain enforcement on price usage. The read path surfacespausedbut does not block consumption ofsValue:function _getSValue(address asset) internal view returns (uint128 sValue, bool paused) { Asset storage a = assetData[asset]; if (a.sValue == 0) revert AssetNotFound(); sValue = a.sValue; paused = _isPauseActive(a);}If integrators fetch only
sValueand ignorepaused, they will continue using stale or unverified multipliers during corporate events, defeating the pause intent. Because of this downstream systems that omit the pause check can ingest and act on prices that should be withheld during corporate actions.Recommendation
Require downstream consumers to gate price usage on the
pausedflag and alert/fail closed when true. Alternatively, add onchain checks in calling contracts to revert or refuse price use whenpausedis true so accidental consumption is prevented.Ondo Finance: Acknowledged. This is core to the design and will require downstream consumers to understand the meaning of "paused" in this context.
Setter path cannot be frozen via params
Description
The setter path always permits some upward drift because
allowedDriftBpsmust be > 0;setDriftParametersrejectsallowedDriftBps == 0. Operationally, this means you cannot freeze nominal updates via parameters without invoking the admin-driven pause/corporate-action flow. If the setter role is suspected compromised or you want to halt routine bumps, there is no parameter-only way to stop it.Recommendation
Consider allowing
allowedDriftBps = 0to freeze the nominal update path when desired, or add an explicit freeze flag for the setter role. Retain existing bounds checks otherwise.Ondo Finance: Fixed in PR #501.
Cantina: Fix verified.