Guardian's review of Risk Oracle for GMX, published May 2026. The report records 13 findings across 2 review rounds, including 2 medium and 4 low.
- Published
- Review window
- May 18 to 25, 2026
- Rounds
- Main Review, Remediation Review
- Language
- Solidity
- Chains
- Arbitrum, Avalanche
- Sector
- Perpetuals, Infrastructure
- 0 Critical
- 0 High
- 2 Medium
- 4 Low
- 7 Informational
Scope
-
GuardianOrg/gmx-synthetics-private-team1-1779120112532
c49ebdea008c2d75803a
Findings 13
Main Review
8 findings · May 18 to 22, 2026-
M-01 Medium Funding/Borrowing Update Influence Stale Markets Configuration Resolved
Description
RiskOracleConfigallows theRISK_ORACLErole to directly update funding and borrowing rate parameters without first settling the market’s accrued funding and borrowing state. Since the next market update calculates elapsed time from the previous update timestamp using the current config, a newly set rate can be applied to the entire unsettled interval. This can overcharge or undercharge open positions and distort claimable funding or borrowing fees and therefore lead to step-wise jumps in positions health. This can even lead to positions being suddenly liquidatable. The more stale a market is the bigger the impact of such config changes.Recommendation
Consider to update the market state (accrue funding, borrowing, etc.) before changing configurations that have any influence on a stale market state.
Resolution
Guardian: The remediation for stale borrowing accrual only settles borrowing before updates to
BASE_BORROWING_FACTORandABOVE_OPTIMAL_USAGE_BORROWING_FACTOR. However,RiskOracleConfigalso allows theRISK_ORACLErole to updateOPTIMAL_USAGE_FACTOR, which is part of the kink borrowing-rate formula. When it is non-zero,MarketUtils.getBorrowingFactorPerSeconduses it to route intogetKinkBorrowingFactor:uint256 optimalUsageFactor = getOptimalUsageFactor(dataStore, market.marketToken, isLong); if (optimalUsageFactor != 0) { return getKinkBorrowingFactor( dataStore, market, isLong, reservedUsd, poolUsd, optimalUsageFactor ); }Inside
getKinkBorrowingFactor, the value controls the kink threshold and the divisor used to calculate the above-optimal portion of the borrowing rate. Despite this,_isRiskOracleBorrowingBaseKeyexcludesOPTIMAL_USAGE_FACTOR. As a result,_settleBorrowingIfRequiredreturns without settling accrued borrowing before the newOPTIMAL_USAGE_FACTORis stored. The next borrowing update can then apply the new kink threshold to the full elapsed stale interval sincecumulativeBorrowingFactorUpdatedAt. This can overcharge or undercharge open positions for the stale interval and may affect position health and liquidation eligibility. The impact depends on market staleness, utilization, and the size of the optimal usage factor change.Recommendation Include
OPTIMAL_USAGE_FACTORin_isRiskOracleBorrowingBaseKey. -
M-02 Medium RiskOracleConfig is not granted CONTROLLER role Configuration Resolved
Description
RiskOracleConfigrequires theCONTROLLERrole to function because it writes throughDataStoreand emits throughEventEmitter, both of which areonlyController-gated. The deploy scriptdeployRiskOracleConfig.tsattempts to grantCONTROLLERafter deployment:afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract, "CONTROLLER"); },However,
grantRoleIfNotGrantedimmediately returns on existing mainnet deployment networks:if (hre.gmx.isExistingMainnetDeployment) { return; }Affected flows include:
setRiskOracleMarketEnabled, because it callsDataStore.setBool.setBool,setAddress,setBytes32,setUint, andsetInt, because they call the correspondingDataStore.set*method.- All successful setter flows, because they emit through
EventEmitter, which also requiresCONTROLLER
Recommendation
Add
RiskOracleConfigtorequiredRolesForContracts.CONTROLLERinconfig/roles.ts, and ensure the mainnet role update transaction grants the deployedRiskOracleConfigaddress theCONTROLLERrole. -
L-01 Low Missing Cross-Parameter Sanity Checks Configuration Resolved
Description
RiskOracleConfigallows theRISK_ORACLErole to directly update sensitive market parameters. Right now, values are mostly checked against broad per-key bounds, but important cross-parameter invariants are not enforced. For example,MIN_COLLATERAL_FACTORshould remain greater than or equal toMIN_COLLATERAL_FACTOR_FOR_LIQUIDATION; otherwise users may be able to open or increase positions that are immediately liquidatable.Recommendation
Consider to add invariant checks before applying risk oracle updates.
-
L-02 Low Risk Oracle Config Accepts Non-Market Addresses Configuration Resolved
Description
RiskOracleConfig.setRiskOracleMarketEnabledonly checks that the givenmarket != address(0)before settingRISK_ORACLE_MARKET_ENABLED. It does not verify that the supplied address is a registered GMX market.Recommendation
Consider to validate that the given market exists in GMX.
-
L-03 Low Unnecessary Setters Exposed Warning Resolved
Description
RiskOracleConfigshould only exposesetUintfor the currently allowlisted risk-oracle parameters, because those parameters are consumed by the protocol asuint256configs. Instead, the contract exposes generic setters for multiple DataStore types:setBoolsetAddresssetBytes32setUintsetIntBecause the same base-key allowlist is shared by all setters, aRISK_ORACLEaccount can accidentally call the wrong setter for a critical risk parameter. The transaction can succeed and emit a config event, but the actual uint config used by the protocol remains unchanged.
Recommendation
Remove the non-uint setters from
RiskOracleConfigif the internal risk oracle only needs to updateuintrisk parameters. -
L-04 Low RISK_ORACLE Role Missing In Scripts Warning Resolved
Description
The
RISK_ORACLEis missing in multiple scripts:validateRolesUtilsonly uses theRolelibrary and not the newRole2libraryprintRoleslacks the role inknownRolesvalidateContractDeploymentlacks the role inroleLabelsvalidateDeploymentUtilsdoes not include it in the external-role allowlist
Recommendation
Consider to add the
RISK_ORACLEto scripts wherever it is needed. -
I-01 Informational Unused Imports Superfluous Code Resolved
Description
RiskOracleConfig.solimportsKeys2,Precision,Cast, andMarketUtils, but none of these are used.Recommendation
Consider to remove unused code.
-
I-02 Informational
_validateKeyperforms a redundant role check Access Control ResolvedDescription
All public risk-oracle setters are already protected by the
onlyRiskOraclemodifier. However,_validateKeychecks the same role again:if (roleStore.hasRole(msg.sender, Role2.RISK_ORACLE)) { ... return; } revert Errors.InvalidBaseKey(baseKey);This role check is redundant for the current setters. Additionally, if
_validateKeyis reused by a future function that is not protected byonlyRiskOracle, callers without the role would receiveInvalidBaseKeyinstead of the more accurateUnauthorizederror.Recommendation
Remove the role check from
_validateKeyand rely on the externalonlyRiskOraclemodifier for authorization.
Remediation Review
5 findings · May 25, 2026-
I-01 Informational Coarser operational controls than
ConfigSyncerBest Practices AcknowledgedDescription
The older Chaos Labs risk-oracle flow used
ConfigSynceras an adapter between the external Risk Oracle and GMX config writes. Before applying an update,ConfigSyncerchecked multiple operational disable flags:- global sync feature disable;
- per-market disable;
- per-parameter disable;
- per-market-parameter disable.
RiskOracleConfiguses a simpler direct-write model. ACONFIG_KEEPERcan enable or disable all risk-oracle updates for a market throughsetRiskOracleMarketEnabled, and aRISK_ORACLEaccount can then write allowlisted parameters for enabled markets. This is simpler and may be intentional, but it is also less granular. If the internal risk oracle has an issue with one parameter family, such as funding, borrowing, or caps, operators can only disable the whole market or revoke theRISK_ORACLErole. They cannot pause a single base key or a single market/base-key pair while allowing other risk-oracle updates to continue.
Recommendation
Consider whether
RiskOracleConfigshould include finer emergency controls similar toConfigSyncer, especially:- a global risk-oracle config feature disable;
- a per-base-key disable;
- optionally, a per-market/base-key disable.
If this coarser control model is intentional, document the operational runbook for pausing faulty risk-oracle updates, including whether operators should disable affected markets or revoke the
RISK_ORACLErole.
-
I-02 Informational Allowlist differs from previous
ConfigSyncerWarning AcknowledgedDescription
The new
RiskOracleConfigallowlist is not a direct replacement for the previous Chaos LabsConfigSyncerrisk-oracle surface.ConfigSyncercan apply updates for the following base keys thatRiskOracleConfigdoes not allow:SWAP_IMPACT_FACTORSWAP_IMPACT_EXPONENT_FACTORBORROWING_FACTORBORROWING_EXPONENT_FACTORRESERVE_FACTOROPEN_INTEREST_RESERVE_FACTORConversely,RiskOracleConfigadds support for some keys thatConfigSyncerdid not support, including:MAX_POSITION_IMPACT_FACTORGLV_MAX_MARKET_TOKEN_BALANCE_USDGLV_MAX_MARKET_TOKEN_BALANCE_AMOUNTFUNDING_FACTORFUNDING_EXPONENT_FACTORTHRESHOLD_FOR_STABLE_FUNDINGTHRESHOLD_FOR_DECREASE_FUNDINGMIN_COLLATERAL_FACTORMIN_COLLATERAL_FACTOR_FOR_OPEN_INTEREST_MULTIPLIERMIN_COLLATERAL_FACTOR_FOR_LIQUIDATIONThis may be intentional if the internal Risk Oracle is meant to manage a different parameter set than the Chaos Labs integration. If not, the migration could silently remove automated risk-oracle management for swap impact, legacy borrowing, and reserve-factor parameters.
Recommendation
Confirm whether the new allowlist intentionally drops the
ConfigSyncer-only keys. -
I-03 Informational Incorrect NatSpec Documentation Resolved
Description
The NatSpec of the
_validateKeyAndMarketfunction states out that the function returns an address but it returns a market now.Recommendation
Consider to update the NatSpec.
-
I-04 Informational Unused EventUtils Superfluous Code Resolved
Description
EventUtils.IntItemsis unused now.Recommendation
Consider to remove unused code.
-
I-05 Informational Stale updateMarketConfigUtils Script Configuration Resolved
Description
The list of RISK_ORACLE managed keys in the
updateMarketConfigUtils.tsscript is stale.Recommendation
Consider to update this script.
No findings match.
More from GMX
All 52 reports-
Incentives Contracts
17 findings 17 findings: 3 medium, 3 low, 11 informational -
Donation Contracts
25 findings 25 findings: 1 medium, 6 low, 18 informational -
V2.3 Pre-Review
7 findings 7 findings: 7 informational -
Token Delisting Procedure
24 findings4 high 24 findings: 4 high, 12 medium, 3 low, 5 informational
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.
