Initial commit with 🏗️ create-eth @ 2.0.4
This commit is contained in:
73
packages/hardhat/contracts/00_Whitelist/SimpleOracle.sol
Normal file
73
packages/hardhat/contracts/00_Whitelist/SimpleOracle.sol
Normal file
@@ -0,0 +1,73 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.0 <0.9.0;
|
||||
|
||||
contract SimpleOracle {
|
||||
/////////////////
|
||||
/// Errors //////
|
||||
/////////////////
|
||||
|
||||
error OnlyOwner();
|
||||
|
||||
//////////////////////
|
||||
/// State Variables //
|
||||
//////////////////////
|
||||
|
||||
uint256 public price;
|
||||
uint256 public timestamp;
|
||||
address public owner;
|
||||
|
||||
////////////////
|
||||
/// Events /////
|
||||
////////////////
|
||||
|
||||
event PriceUpdated(uint256 newPrice);
|
||||
|
||||
///////////////////
|
||||
/// Constructor ///
|
||||
///////////////////
|
||||
|
||||
constructor(address _owner) {
|
||||
owner = _owner;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
/// Modifiers /////
|
||||
///////////////////
|
||||
|
||||
/**
|
||||
* @notice Modifier to restrict function access to the contract owner
|
||||
* @dev Currently disabled to make it easy for you to impersonate the owner
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
// Intentionally removing the owner requirement to make it easy for you to impersonate the owner
|
||||
// if (msg.sender != owner) revert OnlyOwner();
|
||||
_;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
/// Functions /////
|
||||
///////////////////
|
||||
|
||||
/**
|
||||
* @notice Updates the oracle price with a new value (only contract owner)
|
||||
* @dev Sets the price and records the current block timestamp for freshness tracking.
|
||||
* Emits PriceUpdated event upon successful update.
|
||||
* @param _newPrice The new price value to set for this oracle
|
||||
*/
|
||||
function setPrice(uint256 _newPrice) public onlyOwner {
|
||||
price = _newPrice;
|
||||
timestamp = block.timestamp;
|
||||
emit PriceUpdated(_newPrice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Returns the current price and its timestamp
|
||||
* @dev Provides both the stored price value and when it was last updated.
|
||||
* Used by aggregators to determine price freshness.
|
||||
* @return price The current price stored in this oracle
|
||||
* @return timestamp The block timestamp when the price was last updated
|
||||
*/
|
||||
function getPrice() public view returns (uint256, uint256) {
|
||||
return (price, timestamp);
|
||||
}
|
||||
}
|
||||
89
packages/hardhat/contracts/00_Whitelist/WhitelistOracle.sol
Normal file
89
packages/hardhat/contracts/00_Whitelist/WhitelistOracle.sol
Normal file
@@ -0,0 +1,89 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.0 <0.9.0;
|
||||
|
||||
import "./SimpleOracle.sol";
|
||||
import { StatisticsUtils } from "../utils/StatisticsUtils.sol";
|
||||
|
||||
contract WhitelistOracle {
|
||||
using StatisticsUtils for uint256[];
|
||||
|
||||
/////////////////
|
||||
/// Errors //////
|
||||
/////////////////
|
||||
|
||||
error OnlyOwner();
|
||||
error IndexOutOfBounds();
|
||||
error NoOraclesAvailable();
|
||||
|
||||
//////////////////////
|
||||
/// State Variables //
|
||||
//////////////////////
|
||||
|
||||
address public owner;
|
||||
SimpleOracle[] public oracles;
|
||||
uint256 public constant STALE_DATA_WINDOW = 24 seconds;
|
||||
|
||||
////////////////
|
||||
/// Events /////
|
||||
////////////////
|
||||
|
||||
event OracleAdded(address oracleAddress, address oracleOwner);
|
||||
event OracleRemoved(address oracleAddress);
|
||||
|
||||
///////////////////
|
||||
/// Modifiers /////
|
||||
///////////////////
|
||||
|
||||
/**
|
||||
* @notice Modifier to restrict function access to the contract owner
|
||||
* @dev Currently disabled to make it easy for you to impersonate the owner
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
// if (msg.sender != owner) revert OnlyOwner();
|
||||
_;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
/// Constructor ///
|
||||
///////////////////
|
||||
|
||||
constructor() {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
/// Functions /////
|
||||
///////////////////
|
||||
|
||||
/**
|
||||
* @notice Adds a new oracle to the whitelist by deploying a SimpleOracle contract (only contract owner)
|
||||
* @dev Creates a new SimpleOracle instance and adds it to the oracles array.
|
||||
* @param _owner The address that will own the newly created oracle and can update its price
|
||||
*/
|
||||
function addOracle(address _owner) public onlyOwner {}
|
||||
|
||||
/**
|
||||
* @notice Removes an oracle from the whitelist by its array index (only contract owner)
|
||||
* @dev Uses swap-and-pop pattern for gas-efficient removal. Order is not preserved.
|
||||
* Reverts with IndexOutOfBounds, if the provided index is >= oracles.length.
|
||||
* @param index The index of the oracle to remove from the oracles array
|
||||
*/
|
||||
function removeOracle(uint256 index) public onlyOwner {}
|
||||
|
||||
/**
|
||||
* @notice Returns the aggregated price from all active oracles using median calculation
|
||||
* @dev Filters oracles with timestamps older than STALE_DATA_WINDOW, then calculates median
|
||||
* of remaining valid prices. Uses StatisticsUtils for sorting and median calculation.
|
||||
* @return The median price from all active oracles
|
||||
*/
|
||||
function getPrice() public view returns (uint256) {}
|
||||
|
||||
/**
|
||||
* @notice Returns the addresses of all oracles that have updated their price within the last STALE_DATA_WINDOW
|
||||
* @dev Iterates through all oracles and filters those with recent timestamps (within STALE_DATA_WINDOW).
|
||||
* Uses a temporary array to collect active nodes, then creates a right-sized return array
|
||||
* for gas optimization.
|
||||
* @return An array of addresses representing the currently active oracle contracts
|
||||
*/
|
||||
function getActiveOracleNodes() public view returns (address[] memory) {}
|
||||
}
|
||||
65
packages/hardhat/contracts/01_Staking/OracleToken.sol
Normal file
65
packages/hardhat/contracts/01_Staking/OracleToken.sol
Normal file
@@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.0 <0.9.0;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
|
||||
contract ORA is ERC20, Ownable {
|
||||
//////////////////
|
||||
/// Constants ////
|
||||
//////////////////
|
||||
|
||||
// 0.5 ETH = 100 ORA => 1 ETH = 200 ORA (18 decimals)
|
||||
uint256 public constant ORA_PER_ETH = 200;
|
||||
|
||||
//////////////////////
|
||||
/// State Variables //
|
||||
//////////////////////
|
||||
|
||||
////////////////
|
||||
/// Events /////
|
||||
////////////////
|
||||
|
||||
event OraPurchased(address indexed buyer, uint256 ethIn, uint256 oraOut);
|
||||
event EthWithdrawn(address indexed to, uint256 amount);
|
||||
|
||||
/////////////////
|
||||
/// Errors //////
|
||||
/////////////////
|
||||
|
||||
error EthTransferFailed();
|
||||
|
||||
constructor() ERC20("Oracle Token", "ORA") Ownable(msg.sender) {
|
||||
// Mint initial supply to the contract deployer
|
||||
_mint(msg.sender, 1000000000000 ether);
|
||||
}
|
||||
|
||||
function mint(address to, uint256 amount) public onlyOwner {
|
||||
_mint(to, amount);
|
||||
}
|
||||
|
||||
function burn(uint256 amount) public {
|
||||
_burn(msg.sender, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Buy ORA at a fixed rate by sending ETH. Mints directly to the buyer.
|
||||
*/
|
||||
receive() external payable {
|
||||
_buy(msg.sender);
|
||||
}
|
||||
|
||||
function buy() external payable {
|
||||
_buy(msg.sender);
|
||||
}
|
||||
|
||||
function quoteOra(uint256 ethAmountWei) public pure returns (uint256) {
|
||||
return ethAmountWei * ORA_PER_ETH;
|
||||
}
|
||||
|
||||
function _buy(address buyer) internal {
|
||||
uint256 oraOut = quoteOra(msg.value);
|
||||
_mint(buyer, oraOut);
|
||||
emit OraPurchased(buyer, msg.value, oraOut);
|
||||
}
|
||||
}
|
||||
236
packages/hardhat/contracts/01_Staking/StakingOracle.sol
Normal file
236
packages/hardhat/contracts/01_Staking/StakingOracle.sol
Normal file
@@ -0,0 +1,236 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.0 <0.9.0;
|
||||
|
||||
import { ORA } from "./OracleToken.sol";
|
||||
import { StatisticsUtils } from "../utils/StatisticsUtils.sol";
|
||||
|
||||
contract StakingOracle {
|
||||
using StatisticsUtils for uint256[];
|
||||
|
||||
/////////////////
|
||||
/// Errors //////
|
||||
/////////////////
|
||||
|
||||
error NodeNotRegistered();
|
||||
error InsufficientStake();
|
||||
error NodeAlreadyRegistered();
|
||||
error NoRewardsAvailable();
|
||||
error OnlyPastBucketsAllowed();
|
||||
error NodeAlreadySlashed();
|
||||
error AlreadyReportedInCurrentBucket();
|
||||
error NotDeviated();
|
||||
error WaitingPeriodNotOver();
|
||||
error InvalidPrice();
|
||||
error IndexOutOfBounds();
|
||||
error NodeNotAtGivenIndex();
|
||||
error TransferFailed();
|
||||
error MedianNotRecorded();
|
||||
error BucketMedianAlreadyRecorded();
|
||||
error NodeDidNotReport();
|
||||
|
||||
//////////////////////
|
||||
/// State Variables //
|
||||
//////////////////////
|
||||
|
||||
ORA public oracleToken;
|
||||
|
||||
struct OracleNode {
|
||||
uint256 stakedAmount;
|
||||
uint256 lastReportedBucket;
|
||||
uint256 reportCount;
|
||||
uint256 claimedReportCount;
|
||||
uint256 firstBucket; // block when node registered
|
||||
bool active;
|
||||
}
|
||||
|
||||
struct BlockBucket {
|
||||
mapping(address => bool) slashedOffenses;
|
||||
address[] reporters;
|
||||
uint256[] prices;
|
||||
uint256 medianPrice;
|
||||
}
|
||||
|
||||
mapping(address => OracleNode) public nodes;
|
||||
mapping(uint256 => BlockBucket) public blockBuckets; // one bucket per 24 blocks
|
||||
address[] public nodeAddresses;
|
||||
|
||||
uint256 public constant MINIMUM_STAKE = 100 ether;
|
||||
uint256 public constant BUCKET_WINDOW = 24; // 24 blocks
|
||||
uint256 public constant SLASHER_REWARD_PERCENTAGE = 10;
|
||||
uint256 public constant REWARD_PER_REPORT = 1 ether; // ORA Token reward per report
|
||||
uint256 public constant INACTIVITY_PENALTY = 1 ether;
|
||||
uint256 public constant MISREPORT_PENALTY = 100 ether;
|
||||
uint256 public constant MAX_DEVIATION_BPS = 1000; // 10% default threshold
|
||||
uint256 public constant WAITING_PERIOD = 2; // 2 buckets after last report before exit allowed
|
||||
|
||||
////////////////
|
||||
/// Events /////
|
||||
////////////////
|
||||
|
||||
event NodeRegistered(address indexed node, uint256 stakedAmount);
|
||||
event PriceReported(address indexed node, uint256 price, uint256 bucketNumber);
|
||||
event BucketMedianRecorded(uint256 indexed bucketNumber, uint256 medianPrice);
|
||||
event NodeSlashed(address indexed node, uint256 amount);
|
||||
event NodeRewarded(address indexed node, uint256 amount);
|
||||
event StakeAdded(address indexed node, uint256 amount);
|
||||
event NodeExited(address indexed node, uint256 amount);
|
||||
|
||||
///////////////////
|
||||
/// Modifiers /////
|
||||
///////////////////
|
||||
|
||||
/**
|
||||
* @notice Modifier to restrict function access to registered oracle nodes
|
||||
* @dev Checks if the sender has a registered node in the mapping
|
||||
*/
|
||||
modifier onlyNode() {
|
||||
if (nodes[msg.sender].active == false) revert NodeNotRegistered();
|
||||
_;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
/// Constructor ///
|
||||
///////////////////
|
||||
|
||||
constructor(address oraTokenAddress) {
|
||||
oracleToken = ORA(payable(oraTokenAddress));
|
||||
}
|
||||
|
||||
///////////////////
|
||||
/// Functions /////
|
||||
///////////////////
|
||||
|
||||
/**
|
||||
* @notice Registers a new oracle node with initial ORA token stake
|
||||
* @dev Creates a new OracleNode struct and adds the sender to the nodeAddresses array.
|
||||
* Requires minimum stake amount and prevents duplicate registrations.
|
||||
*/
|
||||
function registerNode(uint256 amount) public {}
|
||||
|
||||
/**
|
||||
* @notice Updates the price reported by an oracle node (only registered nodes)
|
||||
* @dev Updates the node's lastReportedBucket and price in that bucket. Requires sufficient stake.
|
||||
* Enforces that previous report's bucket must have its median recorded before allowing new report.
|
||||
* This creates a chain of finalized buckets, ensuring all past reports are accountable.
|
||||
* @param price The new price value to report
|
||||
*/
|
||||
function reportPrice(uint256 price) public onlyNode {}
|
||||
|
||||
/**
|
||||
* @notice Allows active and inactive nodes to claim accumulated ORA token rewards
|
||||
* @dev Calculates rewards based on time elapsed since last claim.
|
||||
*/
|
||||
function claimReward() public {}
|
||||
|
||||
/**
|
||||
* @notice Allows a registered node to increase its ORA token stake
|
||||
*/
|
||||
function addStake(uint256 amount) public onlyNode {}
|
||||
|
||||
/**
|
||||
* @notice Records the median price for a bucket once sufficient reports are available
|
||||
* @dev Anyone who uses the oracle's price feed can call this function to record the median price for a bucket.
|
||||
* @param bucketNumber The bucket number to finalize
|
||||
*/
|
||||
function recordBucketMedian(uint256 bucketNumber) public {}
|
||||
|
||||
/**
|
||||
* @notice Slashes a node for giving a price that is deviated too far from the average
|
||||
* @param nodeToSlash The address of the node to slash
|
||||
* @param bucketNumber The bucket number to slash the node from
|
||||
* @param reportIndex The index of node in the prices and reporters arrays
|
||||
* @param nodeAddressesIndex The index of the node to slash in the nodeAddresses array
|
||||
*/
|
||||
function slashNode(
|
||||
address nodeToSlash,
|
||||
uint256 bucketNumber,
|
||||
uint256 reportIndex,
|
||||
uint256 nodeAddressesIndex
|
||||
) public {}
|
||||
|
||||
/**
|
||||
* @notice Allows a registered node to exit the system and withdraw their stake
|
||||
* @dev Removes the node from the system and sends the stake to the node.
|
||||
* Requires that the the initial waiting period has passed to ensure the
|
||||
* node has been slashed if it reported a bad price before allowing it to exit.
|
||||
* @param index The index of the node to remove in nodeAddresses
|
||||
*/
|
||||
function exitNode(uint256 index) public onlyNode {}
|
||||
|
||||
////////////////////////
|
||||
/// View Functions /////
|
||||
////////////////////////
|
||||
|
||||
/**
|
||||
* @notice Returns the current bucket number
|
||||
* @dev Returns the current bucket number based on the block number
|
||||
* @return The current bucket number
|
||||
*/
|
||||
function getCurrentBucketNumber() public view returns (uint256) {
|
||||
return (block.number / BUCKET_WINDOW) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Returns the list of registered oracle node addresses
|
||||
* @return Array of registered oracle node addresses
|
||||
*/
|
||||
function getNodeAddresses() public view returns (address[] memory) {}
|
||||
|
||||
/**
|
||||
* @notice Returns the stored median price from the most recently completed bucket
|
||||
* @dev Requires that the median for the bucket be recorded via recordBucketMedian
|
||||
* @return The median price for the last finalized bucket
|
||||
*/
|
||||
function getLatestPrice() public view returns (uint256) {}
|
||||
|
||||
/**
|
||||
* @notice Returns the stored median price from a specified bucket
|
||||
* @param bucketNumber The bucket number to read the median price from
|
||||
* @return The median price stored for the bucket
|
||||
*/
|
||||
function getPastPrice(uint256 bucketNumber) public view returns (uint256) {}
|
||||
|
||||
/**
|
||||
* @notice Returns the price and slashed status of a node at a given bucket
|
||||
* @param nodeAddress The address of the node to get the data for
|
||||
* @param bucketNumber The bucket number to get the data from
|
||||
* @return price The price of the node at the specified bucket
|
||||
* @return slashed The slashed status of the node at the specified bucket
|
||||
*/
|
||||
function getSlashedStatus(
|
||||
address nodeAddress,
|
||||
uint256 bucketNumber
|
||||
) public view returns (uint256 price, bool slashed) {}
|
||||
|
||||
/**
|
||||
* @notice Returns the effective stake accounting for inactivity penalties via missed buckets
|
||||
* @dev Effective stake = stakedAmount - (missedBuckets * INACTIVITY_PENALTY), floored at 0
|
||||
*/
|
||||
function getEffectiveStake(address nodeAddress) public view returns (uint256) {}
|
||||
|
||||
/**
|
||||
* @notice Returns the addresses of nodes in a bucket whose reported price deviates beyond the threshold
|
||||
* @param bucketNumber The bucket number to get the outliers from
|
||||
* @return Array of node addresses considered outliers
|
||||
*/
|
||||
function getOutlierNodes(uint256 bucketNumber) public view returns (address[] memory) {}
|
||||
|
||||
//////////////////////////
|
||||
/// Internal Functions ///
|
||||
//////////////////////////
|
||||
|
||||
/**
|
||||
* @notice Removes a node from the nodeAddresses array
|
||||
* @param nodeAddress The address of the node to remove
|
||||
* @param index The index of the node to remove
|
||||
*/
|
||||
function _removeNode(address nodeAddress, uint256 index) internal {}
|
||||
|
||||
/**
|
||||
* @notice Checks if the price deviation is greater than the threshold
|
||||
* @param reportedPrice The price reported by the node
|
||||
* @param medianPrice The average price of the bucket
|
||||
* @return True if the price deviation is greater than the threshold, false otherwise
|
||||
*/
|
||||
function _checkPriceDeviated(uint256 reportedPrice, uint256 medianPrice) internal pure returns (bool) {}
|
||||
}
|
||||
42
packages/hardhat/contracts/02_Optimistic/Decider.sol
Normal file
42
packages/hardhat/contracts/02_Optimistic/Decider.sol
Normal file
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.0 <0.9.0;
|
||||
|
||||
contract Decider {
|
||||
address public owner;
|
||||
IOptimisticOracle public oracle;
|
||||
|
||||
event DisputeSettled(uint256 indexed assertionId, bool resolvedValue);
|
||||
|
||||
constructor(address _oracle) {
|
||||
owner = msg.sender;
|
||||
oracle = IOptimisticOracle(_oracle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Settle a dispute by determining the true/false outcome
|
||||
* @param assertionId The ID of the assertion to settle
|
||||
* @param resolvedValue The true/false outcome determined by the decider
|
||||
*/
|
||||
function settleDispute(uint256 assertionId, bool resolvedValue) external {
|
||||
require(assertionId >= 1, "Invalid assertion ID");
|
||||
|
||||
// Call the oracle's settleAssertion function
|
||||
oracle.settleAssertion(assertionId, resolvedValue);
|
||||
|
||||
emit DisputeSettled(assertionId, resolvedValue);
|
||||
}
|
||||
|
||||
function setOracle(address newOracle) external {
|
||||
require(msg.sender == owner, "Only owner can set oracle");
|
||||
oracle = IOptimisticOracle(newOracle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Allow the contract to receive ETH
|
||||
*/
|
||||
receive() external payable {}
|
||||
}
|
||||
|
||||
interface IOptimisticOracle {
|
||||
function settleAssertion(uint256, bool) external;
|
||||
}
|
||||
211
packages/hardhat/contracts/02_Optimistic/OptimisticOracle.sol
Normal file
211
packages/hardhat/contracts/02_Optimistic/OptimisticOracle.sol
Normal file
@@ -0,0 +1,211 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.0 <0.9.0;
|
||||
|
||||
contract OptimisticOracle {
|
||||
////////////////
|
||||
/// Enums //////
|
||||
////////////////
|
||||
|
||||
enum State {
|
||||
Invalid,
|
||||
Asserted,
|
||||
Proposed,
|
||||
Disputed,
|
||||
Settled,
|
||||
Expired
|
||||
}
|
||||
|
||||
/////////////////
|
||||
/// Errors //////
|
||||
/////////////////
|
||||
|
||||
error AssertionNotFound();
|
||||
error AssertionProposed();
|
||||
error InvalidValue();
|
||||
error InvalidTime();
|
||||
error ProposalDisputed();
|
||||
error NotProposedAssertion();
|
||||
error AlreadyClaimed();
|
||||
error AlreadySettled();
|
||||
error AwaitingDecider();
|
||||
error NotDisputedAssertion();
|
||||
error OnlyDecider();
|
||||
error OnlyOwner();
|
||||
error TransferFailed();
|
||||
|
||||
//////////////////////
|
||||
/// State Variables //
|
||||
//////////////////////
|
||||
|
||||
struct EventAssertion {
|
||||
address asserter;
|
||||
address proposer;
|
||||
address disputer;
|
||||
bool proposedOutcome;
|
||||
bool resolvedOutcome;
|
||||
uint256 reward;
|
||||
uint256 bond;
|
||||
uint256 startTime;
|
||||
uint256 endTime;
|
||||
bool claimed;
|
||||
address winner;
|
||||
string description;
|
||||
}
|
||||
|
||||
uint256 public constant MINIMUM_ASSERTION_WINDOW = 3 minutes;
|
||||
uint256 public constant DISPUTE_WINDOW = 3 minutes;
|
||||
address public decider;
|
||||
address public owner;
|
||||
uint256 public nextAssertionId = 1;
|
||||
mapping(uint256 => EventAssertion) public assertions;
|
||||
|
||||
////////////////
|
||||
/// Events /////
|
||||
////////////////
|
||||
|
||||
event EventAsserted(uint256 assertionId, address asserter, string description, uint256 reward);
|
||||
event OutcomeProposed(uint256 assertionId, address proposer, bool outcome);
|
||||
event OutcomeDisputed(uint256 assertionId, address disputer);
|
||||
event AssertionSettled(uint256 assertionId, bool outcome, address winner);
|
||||
event DeciderUpdated(address oldDecider, address newDecider);
|
||||
event RewardClaimed(uint256 assertionId, address winner, uint256 amount);
|
||||
event RefundClaimed(uint256 assertionId, address asserter, uint256 amount);
|
||||
|
||||
///////////////////
|
||||
/// Modifiers /////
|
||||
///////////////////
|
||||
|
||||
/**
|
||||
* @notice Modifier to restrict function access to the designated decider
|
||||
* @dev Ensures only the decider can settle disputed assertions
|
||||
*/
|
||||
modifier onlyDecider() {
|
||||
if (msg.sender != decider) revert OnlyDecider();
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Modifier to restrict function access to the contract owner
|
||||
* @dev Ensures only the owner can update critical contract parameters
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
if (msg.sender != owner) revert OnlyOwner();
|
||||
_;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
/// Constructor ///
|
||||
///////////////////
|
||||
|
||||
constructor(address _decider) {
|
||||
decider = _decider;
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
/// Functions /////
|
||||
///////////////////
|
||||
|
||||
/**
|
||||
* @notice Updates the decider address (only contract owner)
|
||||
* @dev Changes the address authorized to settle disputed assertions.
|
||||
* Emits DeciderUpdated event with old and new addresses.
|
||||
* @param _decider The new address that will act as decider for disputed assertions
|
||||
*/
|
||||
function setDecider(address _decider) external onlyOwner {
|
||||
address oldDecider = address(decider);
|
||||
decider = _decider;
|
||||
emit DeciderUpdated(oldDecider, _decider);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Returns the complete assertion details for a given assertion ID
|
||||
* @dev Provides access to all fields of the EventAssertion struct
|
||||
* @param assertionId The unique identifier of the assertion to retrieve
|
||||
* @return The complete EventAssertion struct containing all assertion data
|
||||
*/
|
||||
function getAssertion(uint256 assertionId) external view returns (EventAssertion memory) {
|
||||
return assertions[assertionId];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Creates a new assertion about an event with a true/false outcome
|
||||
* @dev Requires ETH payment as reward for correct proposers. Bond requirement is 2x the reward.
|
||||
* Sets default timestamps if not provided. Validates timing requirements.
|
||||
* @param description Human-readable description of the event (e.g. "Did X happen by time Y?")
|
||||
* @param startTime When proposals can begin (0 for current block timestamp)
|
||||
* @param endTime When the assertion expires (0 for startTime + minimum window)
|
||||
* @return The unique assertion ID for the newly created assertion
|
||||
*/
|
||||
function assertEvent(
|
||||
string memory description,
|
||||
uint256 startTime,
|
||||
uint256 endTime
|
||||
) external payable returns (uint256) {}
|
||||
|
||||
/**
|
||||
* @notice Proposes the outcome (true or false) for an asserted event
|
||||
* @dev Requires bonding ETH equal to 2x the original reward. Sets dispute window deadline.
|
||||
* Can only be called once per assertion and within the assertion time window.
|
||||
* @param assertionId The unique identifier of the assertion to propose an outcome for
|
||||
* @param outcome The proposed boolean outcome (true or false) for the event
|
||||
*/
|
||||
function proposeOutcome(uint256 assertionId, bool outcome) external payable {}
|
||||
|
||||
/**
|
||||
* @notice Disputes a proposed outcome by bonding ETH
|
||||
* @dev Requires bonding ETH equal to the bond amount. Can only dispute once per assertion
|
||||
* and must be within the dispute window after proposal.
|
||||
* @param assertionId The unique identifier of the assertion to dispute
|
||||
*/
|
||||
function disputeOutcome(uint256 assertionId) external payable {}
|
||||
|
||||
/**
|
||||
* @notice Claims reward for undisputed assertions after dispute window expires
|
||||
* @dev Anyone can trigger this function. Transfers reward + bond to the proposer.
|
||||
* Can only be called after dispute window has passed without disputes.
|
||||
* @param assertionId The unique identifier of the assertion to claim rewards for
|
||||
*/
|
||||
function claimUndisputedReward(uint256 assertionId) external {}
|
||||
|
||||
/**
|
||||
* @notice Claims reward for disputed assertions after decider settlement
|
||||
* @dev Anyone can trigger this function. Pays decider fee and transfers remaining rewards to winner.
|
||||
* Can only be called after decider has settled the dispute.
|
||||
* @param assertionId The unique identifier of the disputed assertion to claim rewards for
|
||||
*/
|
||||
function claimDisputedReward(uint256 assertionId) external {}
|
||||
|
||||
/**
|
||||
* @notice Claims refund for assertions that receive no proposals before deadline
|
||||
* @dev Anyone can trigger this function. Returns the original reward to the asserter.
|
||||
* Can only be called after assertion deadline has passed without any proposals.
|
||||
* @param assertionId The unique identifier of the expired assertion to claim refund for
|
||||
*/
|
||||
function claimRefund(uint256 assertionId) external {}
|
||||
|
||||
/**
|
||||
* @notice Resolves disputed assertions by determining the correct outcome (only decider)
|
||||
* @dev Sets the resolved outcome and determines winner based on proposal accuracy.
|
||||
* @param assertionId The unique identifier of the disputed assertion to settle
|
||||
* @param resolvedOutcome The decider's determination of the true outcome
|
||||
*/
|
||||
function settleAssertion(uint256 assertionId, bool resolvedOutcome) external onlyDecider {}
|
||||
|
||||
/**
|
||||
* @notice Returns the current state of an assertion based on its lifecycle stage
|
||||
* @dev Evaluates assertion progress through states: Invalid, Asserted, Proposed, Disputed, Settled, Expired
|
||||
* @param assertionId The unique identifier of the assertion to check state for
|
||||
* @return The current State enum value representing the assertion's status
|
||||
*/
|
||||
function getState(uint256 assertionId) external view returns (State) {}
|
||||
|
||||
/**
|
||||
* @notice Returns the final resolved outcome of a settled assertion
|
||||
* @dev For undisputed assertions, returns the proposed outcome after dispute window.
|
||||
* For disputed assertions, returns the decider's resolved outcome.
|
||||
* @param assertionId The unique identifier of the assertion to get resolution for
|
||||
* @return The final boolean outcome of the assertion
|
||||
*/
|
||||
function getResolution(uint256 assertionId) external view returns (bool) {}
|
||||
}
|
||||
54
packages/hardhat/contracts/utils/StatisticsUtils.sol
Normal file
54
packages/hardhat/contracts/utils/StatisticsUtils.sol
Normal file
@@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.8.0 <0.9.0;
|
||||
|
||||
library StatisticsUtils {
|
||||
/////////////////
|
||||
/// Errors //////
|
||||
/////////////////
|
||||
|
||||
error EmptyArray();
|
||||
|
||||
///////////////////
|
||||
/// Functions /////
|
||||
///////////////////
|
||||
|
||||
/**
|
||||
* @notice Sorts an array of uint256 values in ascending order using selection sort
|
||||
* @dev Uses selection sort algorithm which is not gas-efficient but acceptable for small arrays.
|
||||
* This implementation mimics the early MakerDAO Medianizer exactly.
|
||||
* Modifies the input array in-place.
|
||||
* @param arr The array of uint256 values to sort in ascending order
|
||||
*/
|
||||
function sort(uint256[] memory arr) internal pure {
|
||||
uint256 n = arr.length;
|
||||
for (uint256 i = 0; i < n; i++) {
|
||||
uint256 minIndex = i;
|
||||
for (uint256 j = i + 1; j < n; j++) {
|
||||
if (arr[j] < arr[minIndex]) {
|
||||
minIndex = j;
|
||||
}
|
||||
}
|
||||
if (minIndex != i) {
|
||||
(arr[i], arr[minIndex]) = (arr[minIndex], arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates the median value from a sorted array of uint256 values
|
||||
* @dev For arrays with even length, returns the average of the two middle elements.
|
||||
* For arrays with odd length, returns the middle element.
|
||||
* Assumes the input array is already sorted in ascending order.
|
||||
* @param arr The sorted array of uint256 values to calculate median from
|
||||
* @return The median value as a uint256
|
||||
*/
|
||||
function getMedian(uint256[] memory arr) internal pure returns (uint256) {
|
||||
uint256 length = arr.length;
|
||||
if (length == 0) revert EmptyArray();
|
||||
if (length % 2 == 0) {
|
||||
return (arr[length / 2 - 1] + arr[length / 2]) / 2;
|
||||
} else {
|
||||
return arr[length / 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user