To integrate Chaos Labs’ dynamic risk parameters into your protocol, you’ll need to coordinate with the Chaos labs team to deploy the RiskOracle contract. This smart contract acts as the on-chain source of truth for validated risk parameters such as caps, thresholds, and limits.
Each oracle contract is deployed for each client’s specific needs.
Step 1: Align on parameter requirements
Align with Chaos Labs on the parameters that should be automated. This should include:
- Name of parameter: A string label such as
maxOpenInterestForLongs. This is used for updateType.
- Values: The expected value and type. Chaos Labs sends this encoded as bytes.
- Market: The market that is the parameter target, which is also encoded as bytes or an address.
- Blockchain: The chain where the
RiskOracle contract is deployed.
- Additional data: Any optional metadata like source, confidence score, and strategy used.
These inputs help ensure the oracle updates are processed correctly and contextually in your protocol.
Step 2: Understand the contract interface
Understanding the contract ABI is essential for secure and reliable integration. Each function and data structure defines how your protocol communicates with the RiskOracle, ensuring that updates are posted, tracked, and consumed accurately.
For a detailed look at the implementation, the RiskOracle contract is open-sourced and available on GitHub.
Querying Oracle Data
These functions expose the current and historical state of risk parameter updates for external queries and verification.
| Function name | Parameters | Description |
|---|
getLatestUpdateByParameterAndMarket | updateType (string), market (address) | Returns the most recent update for a given parameter and market. |
getUpdateById | updateId (uint256) | Returns a specific update by its ID. |
getAllUpdateTypes | None | Returns all currently allowed update types, such as maxLeverage. |
isAuthorized | address (address) | Checks if a given address is authorized to post updates. |
Struct: RiskParameterUpdate
This struct defines the data model for each parameter update, capturing key metadata and context for auditing and integration.
| Attribute | Data type | Description |
|---|
timestamp | uint256 | The block timestamp when the update was recorded. |
newValue | bytes | The new value of the risk parameter is stored in a bytes format to support various data typologies and complexity. |
previousValue | bytes | The parameter value before the current update, enabling historical comparison and possible rollback scenarios. |
updateType | string | A categorization of the update, allowing different types of updates such as maxOpenInterestForLongs. |
updateId | uint256 | A unique identifier for the specific update. |
market | address | An identifier for the market in which the parameter update applies. |
referenceId | string | An external or internal reference ID linking to additional documentation, a dashboard, REST API, or data related to the parameter update. |
additionalData | bytes | Any additional data for the update. |
Events: ParameterUpdated
Emitted after every successful update.
| Parameter | Type | Description |
|---|
referenceId | string | Traceable identifier |
newValue | bytes | Updated value |
previousValue | bytes | Old value |
timestamp | uint256 | Time of update |
updateType | string (indexed) | What was updated |
updateId | uint256 (indexed) | Unique update ID |
market | address (indexed) | Affected market |
additionalData | bytes | Optional |
Step 3: Integrate the Oracle
Once the RiskOracle is deployed and being updated by authorized senders, you’ll want to consume those updates inside your own contracts. This section outlines a secure and flexible consumption pattern using interface-based access.
Objective
Consume parameter updates from RiskOracle and use them to dynamically adjust logic in your own smart contracts, such as update caps, fees, interest rates.
Integration Pattern
This pattern uses two interfaces:
IRiskOracle: Use this interface to fetch updates by an ID.
IAnotherContract: A mock contract you wish to update using values from the oracle.
The RiskOracleConsumer contract fetches the update, decodes the values, and forwards them into another contract.
The following code is for illustrative purposes only, has not been audited, and should not be used in production without thorough testing.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
// Oracle interface exposing the `getUpdateById()` function
interface IRiskOracle {
struct RiskParameterUpdate {
uint256 timestamp;
bytes newValue;
string referenceId;
bytes previousValue;
string updateType;
uint256 updateId;
address market;
bytes additionalData;
}
function getUpdateById(uint256 updateId) external view returns (RiskParameterUpdate memory);
}
// Mock contract that will be updated
interface IAnotherContract {
function updateParameter(uint256 newValue, address market) external;
}
contract RiskOracleConsumer {
IRiskOracle public riskOracle;
IAnotherContract public anotherContract;
constructor(address _riskOracleAddress, address _anotherContractAddress) {
riskOracle = IRiskOracle(_riskOracleAddress);
anotherContract = IAnotherContract(_anotherContractAddress);
}
/**
* @notice Requests an update by ID and decodes the newValue and market parameters.
* @param updateId The ID of the update to request.
* @return Decoded newValue as uint256 and market as address
*/
function requestAndDecodeUpdate(uint256 updateId) public view returns (uint256, address) {
IRiskOracle.RiskParameterUpdate memory update = riskOracle.getUpdateById(updateId);
uint256 decodedValue = _decodeNewValue(update.newValue);
address decodedMarket = _decodeMarket(update.market);
return (decodedValue, decodedMarket);
}
/**
* @notice Uses values decoded from Risk Oracle to interact with another contract.
* @param updateId The ID of the update to request and use.
*/
function updateAnotherContract(uint256 updateId) external {
(uint256 newValue, address market) = requestAndDecodeUpdate(updateId);
anotherContract.updateParameter(newValue, market);
}
/**
* @dev Decodes a bytes value to uint256 using abi.decode.
* @param data The bytes value to decode.
* @return The decoded uint256 value.
*/
function _decodeNewValue(bytes memory data) internal pure returns (uint256) {
return abi.decode(data, (uint256));
}
/**
* @dev Decodes a bytes value to address using abi.decode.
* @param data The bytes value to decode.
* @return The decoded address value.
*/
function _decodeMarket(bytes memory data) internal pure returns (address) {
return abi.decode(data, (address));
}
}
Breakdown Function
These helper functions handle decoding and forwarding of update data, enabling seamless integration with other contracts.
| Function | Roles or description |
|---|
requestAndDecodeUpdate() | Pulls the update by ID and decodes newValue and market. |
updateAnotherContract() | Uses the decoded values to interact with another contract. |
_decodeNewValue() | Converts bytes → uint256. Assumes newValue is a uint256. |
_decodeMarket() | Converts bytes → address. Assumes market is encoded that way. |
This consumption pattern lets you:
- Safely integrate dynamic risk parameters.
- Offload value validation and signing to Chaos Labs.
- Keep your contracts modular and upgrade-friendly.
Step 4: Go-live checklist
Before deploying your integration in production, follow these implementation and deployment steps to ensure secure and reliable operation of your RiskOracle setup.
Part A: Implementation Steps
-
Get your RiskOracle contract address
- Obtain the specific RiskOracle contract address from the Chaos Labs team
- This contract will be customized for your protocol’s needs
- Verify the contract is deployed on your target network
-
Coordinate message format
- Work with Chaos Labs to define the exact format of the parameter updates
- For example, if implementing a supply cap:
// Example parameter format for a supply cap
struct SupplyCapUpdate {
uint256 maxSupply;
address asset;
uint256 timestamp;
}
-
Setup your consumer contract
- Implement the IRiskOracle interface in your contract
- Define the parameter struct matching the agreed format
contract YourContract {
IRiskOracle public riskOracle;
constructor(address _riskOracleAddress) {
riskOracle = IRiskOracle(_riskOracleAddress);
}
function getLatestSupplyCap(address market) public view returns (uint256) {
RiskParameterUpdate memory update = riskOracle.getLatestUpdateByParameterAndMarket("supplyCap", market);
return abi.decode(update.newValue, (uint256));
}
}
-
Implement update retrieval
- Add functions to fetch and decode the latest parameter values
- Include proper error handling for cases when updates aren’t available
- Test the integration with sample data from Chaos Labs
Part B: Deployment and Monitoring
-
Set up event monitoring
- Implement monitoring for the
ParameterUpdated event
- This allows your protocol or off-chain services like (Gelato) to react to new risk parameter updates in real time
- Example monitoring setup:
riskOracle.on("ParameterUpdated", (referenceId, newValue, previousValue, timestamp, updateType, updateId, market) => {
if (updateType === "supplyCap") {
// Handle supply cap update
updateProtocolLimits(market, newValue);
}
});
-
Verify integration
- Test the complete flow with actual parameter updates
- Verify parameter values are correctly decoded and applied
- Monitor the first few updates in production closely
-
Document integration details
- Record all parameter types and their encoding formats
- Document the update frequency and monitoring setup
- Maintain contact information for the Chaos Labs team
Remember to implement proper access controls and validation checks in your consumer contract to ensure only authorized updates are processed.