- Change SQLite database path from `/home/oib/windsurf/aitbc/data/` to `/opt/data/` - Fix foreign key references to use correct table names (users, wallets, gpu_registry) - Replace governance router with new governance and community routers - Add multi-modal RL router to main application - Simplify DEPLOYMENT_READINESS_REPORT.md to focus on production deployment status - Update governance router with decentralized DAO voting
758 lines
25 KiB
Solidity
758 lines
25 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
|
import "@openzeppelin/contracts/security/Pausable.sol";
|
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import "./AIPowerRental.sol";
|
|
import "./PerformanceVerifier.sol";
|
|
|
|
/**
|
|
* @title Dynamic Pricing
|
|
* @dev Advanced dynamic pricing contract with supply/demand analysis and automated price adjustment
|
|
* @notice Implements data-driven pricing for AI power marketplace with ZK-based verification
|
|
*/
|
|
contract DynamicPricing is Ownable, ReentrancyGuard, Pausable {
|
|
|
|
// State variables
|
|
AIPowerRental public aiPowerRental;
|
|
PerformanceVerifier public performanceVerifier;
|
|
IERC20 public aitbcToken;
|
|
|
|
uint256 public priceUpdateCounter;
|
|
uint256 public basePricePerHour = 1e16; // 0.01 AITBC per hour
|
|
uint256 public minPricePerHour = 1e15; // 0.001 AITBC minimum
|
|
uint256 public maxPricePerHour = 1e18; // 0.1 AITBC maximum
|
|
uint256 public priceVolatilityThreshold = 2000; // 20% in basis points
|
|
uint256 public priceUpdateInterval = 3600; // 1 hour
|
|
uint256 public marketDataRetentionPeriod = 7 days;
|
|
uint256 public smoothingFactor = 50; // 50% smoothing in basis points
|
|
uint256 public surgeMultiplier = 300; // 3x surge pricing max
|
|
uint256 public discountMultiplier = 50; // 50% minimum price
|
|
|
|
// Structs
|
|
struct MarketData {
|
|
uint256 totalSupply;
|
|
uint256 totalDemand;
|
|
uint256 activeProviders;
|
|
uint256 activeConsumers;
|
|
uint256 averagePrice;
|
|
uint256 priceVolatility;
|
|
uint256 utilizationRate;
|
|
uint256 lastUpdateTime;
|
|
uint256 totalVolume;
|
|
uint256 transactionCount;
|
|
uint256 averageResponseTime;
|
|
uint256 averageAccuracy;
|
|
uint256 marketSentiment;
|
|
bool isMarketActive;
|
|
}
|
|
|
|
struct PriceHistory {
|
|
uint256 timestamp;
|
|
uint256 price;
|
|
uint256 supply;
|
|
uint256 demand;
|
|
uint256 volume;
|
|
PriceChangeType changeType;
|
|
uint256 changePercentage;
|
|
}
|
|
|
|
struct ProviderPricing {
|
|
address provider;
|
|
uint256 currentPrice;
|
|
uint256 basePrice;
|
|
uint256 reputationScore;
|
|
uint256 utilizationRate;
|
|
uint256 performanceScore;
|
|
uint256 demandScore;
|
|
uint256 supplyScore;
|
|
uint256 lastUpdateTime;
|
|
PricingStrategy strategy;
|
|
uint256 priceAdjustmentFactor;
|
|
}
|
|
|
|
struct RegionalPricing {
|
|
string region;
|
|
uint256 regionalMultiplier;
|
|
uint256 localSupply;
|
|
uint256 localDemand;
|
|
uint256 averagePrice;
|
|
uint256 lastUpdateTime;
|
|
uint256 competitionLevel;
|
|
uint256 infrastructureCost;
|
|
}
|
|
|
|
struct DemandForecast {
|
|
uint256 forecastPeriod;
|
|
uint256 predictedDemand;
|
|
uint256 confidence;
|
|
uint256 forecastTime;
|
|
uint256 actualDemand;
|
|
uint256 forecastAccuracy;
|
|
}
|
|
|
|
struct PriceAlert {
|
|
uint256 alertId;
|
|
address subscriber;
|
|
PriceAlertType alertType;
|
|
uint256 thresholdPrice;
|
|
uint256 currentPrice;
|
|
bool isActive;
|
|
uint256 lastTriggered;
|
|
string notificationMethod;
|
|
}
|
|
|
|
// Enums
|
|
enum PriceChangeType {
|
|
Increase,
|
|
Decrease,
|
|
Stable,
|
|
Surge,
|
|
Discount
|
|
}
|
|
|
|
enum PricingStrategy {
|
|
Fixed,
|
|
Dynamic,
|
|
Competitive,
|
|
PerformanceBased,
|
|
TimeBased,
|
|
DemandBased
|
|
}
|
|
|
|
enum MarketCondition {
|
|
Oversupply,
|
|
Balanced,
|
|
Undersupply,
|
|
Surge,
|
|
Crash
|
|
}
|
|
|
|
enum PriceAlertType {
|
|
PriceAbove,
|
|
PriceBelow,
|
|
VolatilityHigh,
|
|
TrendChange
|
|
}
|
|
|
|
// Mappings
|
|
mapping(uint256 => MarketData) public marketDataHistory;
|
|
mapping(uint256 => PriceHistory[]) public priceHistory;
|
|
mapping(address => ProviderPricing) public providerPricing;
|
|
mapping(string => RegionalPricing) public regionalPricing;
|
|
mapping(uint256 => DemandForecast) public demandForecasts;
|
|
mapping(uint256 => PriceAlert) public priceAlerts;
|
|
mapping(address => uint256[]) public providerPriceHistory;
|
|
mapping(string => uint256[]) public regionalPriceHistory;
|
|
mapping(address => bool) public authorizedPriceOracles;
|
|
mapping(uint256 => bool) public isValidPriceUpdate;
|
|
|
|
// Arrays for tracking
|
|
string[] public supportedRegions;
|
|
uint256[] public activePriceAlerts;
|
|
uint256[] public recentPriceUpdates;
|
|
|
|
// Events
|
|
event MarketDataUpdated(
|
|
uint256 indexed timestamp,
|
|
uint256 totalSupply,
|
|
uint256 totalDemand,
|
|
uint256 averagePrice,
|
|
MarketCondition marketCondition
|
|
);
|
|
|
|
event PriceCalculated(
|
|
uint256 indexed timestamp,
|
|
uint256 newPrice,
|
|
uint256 oldPrice,
|
|
PriceChangeType changeType,
|
|
uint256 changePercentage
|
|
);
|
|
|
|
event ProviderPriceUpdated(
|
|
address indexed provider,
|
|
uint256 newPrice,
|
|
PricingStrategy strategy,
|
|
uint256 adjustmentFactor
|
|
);
|
|
|
|
event RegionalPriceUpdated(
|
|
string indexed region,
|
|
uint256 newMultiplier,
|
|
uint256 localSupply,
|
|
uint256 localDemand
|
|
);
|
|
|
|
event DemandForecastCreated(
|
|
uint256 indexed forecastPeriod,
|
|
uint256 predictedDemand,
|
|
uint256 confidence,
|
|
uint256 forecastTime
|
|
);
|
|
|
|
event PriceAlertTriggered(
|
|
uint256 indexed alertId,
|
|
address indexed subscriber,
|
|
PriceAlertType alertType,
|
|
uint256 currentPrice,
|
|
uint256 thresholdPrice
|
|
);
|
|
|
|
event SurgePricingActivated(
|
|
uint256 surgeMultiplier,
|
|
uint256 duration,
|
|
string reason
|
|
);
|
|
|
|
event DiscountPricingActivated(
|
|
uint256 discountMultiplier,
|
|
uint256 duration,
|
|
string reason
|
|
);
|
|
|
|
event MarketConditionChanged(
|
|
MarketCondition oldCondition,
|
|
MarketCondition newCondition,
|
|
uint256 timestamp
|
|
);
|
|
|
|
event PriceOracleAuthorized(
|
|
address indexed oracle,
|
|
uint256 reputationScore
|
|
);
|
|
|
|
event PriceOracleRevoked(
|
|
address indexed oracle,
|
|
string reason
|
|
);
|
|
|
|
// Modifiers
|
|
modifier onlyAuthorizedPriceOracle() {
|
|
require(authorizedPriceOracles[msg.sender], "Not authorized price oracle");
|
|
_;
|
|
}
|
|
|
|
modifier validPriceUpdate(uint256 _timestamp) {
|
|
require(block.timestamp - _timestamp <= priceUpdateInterval, "Price update too old");
|
|
_;
|
|
}
|
|
|
|
modifier validProvider(address _provider) {
|
|
require(_provider != address(0), "Invalid provider address");
|
|
_;
|
|
}
|
|
|
|
modifier validRegion(string memory _region) {
|
|
require(bytes(_region).length > 0, "Invalid region");
|
|
_;
|
|
}
|
|
|
|
// Constructor
|
|
constructor(
|
|
address _aiPowerRental,
|
|
address _performanceVerifier,
|
|
address _aitbcToken
|
|
) {
|
|
aiPowerRental = AIPowerRental(_aiPowerRental);
|
|
performanceVerifier = PerformanceVerifier(_performanceVerifier);
|
|
aitbcToken = IERC20(_aitbcToken);
|
|
priceUpdateCounter = 0;
|
|
|
|
// Initialize supported regions
|
|
supportedRegions.push("us-east");
|
|
supportedRegions.push("us-west");
|
|
supportedRegions.push("eu-central");
|
|
supportedRegions.push("eu-west");
|
|
supportedRegions.push("ap-southeast");
|
|
supportedRegions.push("ap-northeast");
|
|
}
|
|
|
|
/**
|
|
* @dev Updates market data and recalculates prices
|
|
* @param _totalSupply Total compute power supply
|
|
* @param _totalDemand Total compute power demand
|
|
* @param _activeProviders Number of active providers
|
|
* @param _activeConsumers Number of active consumers
|
|
* @param _totalVolume Total transaction volume
|
|
* @param _transactionCount Number of transactions
|
|
* @param _averageResponseTime Average response time
|
|
* @param _averageAccuracy Average accuracy
|
|
* @param _marketSentiment Market sentiment score (0-100)
|
|
*/
|
|
function updateMarketData(
|
|
uint256 _totalSupply,
|
|
uint256 _totalDemand,
|
|
uint256 _activeProviders,
|
|
uint256 _activeConsumers,
|
|
uint256 _totalVolume,
|
|
uint256 _transactionCount,
|
|
uint256 _averageResponseTime,
|
|
uint256 _averageAccuracy,
|
|
uint256 _marketSentiment
|
|
) external onlyAuthorizedPriceOracle nonReentrant whenNotPaused {
|
|
require(_totalSupply > 0, "Invalid supply");
|
|
require(_totalDemand > 0, "Invalid demand");
|
|
|
|
uint256 timestamp = block.timestamp;
|
|
uint256 priceUpdateId = priceUpdateCounter++;
|
|
|
|
// Calculate utilization rate
|
|
uint256 utilizationRate = (_totalDemand * 10000) / _totalSupply;
|
|
|
|
// Get previous market data for comparison
|
|
MarketData storage previousData = marketDataHistory[priceUpdateId > 0 ? priceUpdateId - 1 : 0];
|
|
|
|
// Calculate new average price
|
|
uint256 newAveragePrice = _calculateDynamicPrice(
|
|
_totalSupply,
|
|
_totalDemand,
|
|
utilizationRate,
|
|
_marketSentiment,
|
|
previousData.averagePrice
|
|
);
|
|
|
|
// Calculate price volatility
|
|
uint256 priceVolatility = 0;
|
|
if (previousData.averagePrice > 0) {
|
|
if (newAveragePrice > previousData.averagePrice) {
|
|
priceVolatility = ((newAveragePrice - previousData.averagePrice) * 10000) / previousData.averagePrice;
|
|
} else {
|
|
priceVolatility = ((previousData.averagePrice - newAveragePrice) * 10000) / previousData.averagePrice;
|
|
}
|
|
}
|
|
|
|
// Store market data
|
|
marketDataHistory[priceUpdateId] = MarketData({
|
|
totalSupply: _totalSupply,
|
|
totalDemand: _totalDemand,
|
|
activeProviders: _activeProviders,
|
|
activeConsumers: _activeConsumers,
|
|
averagePrice: newAveragePrice,
|
|
priceVolatility: priceVolatility,
|
|
utilizationRate: utilizationRate,
|
|
lastUpdateTime: timestamp,
|
|
totalVolume: _totalVolume,
|
|
transactionCount: _transactionCount,
|
|
averageResponseTime: _averageResponseTime,
|
|
averageAccuracy: _averageAccuracy,
|
|
marketSentiment: _marketSentiment,
|
|
isMarketActive: _activeProviders > 0 && _activeConsumers > 0
|
|
});
|
|
|
|
// Determine market condition
|
|
MarketCondition currentCondition = _determineMarketCondition(utilizationRate, priceVolatility);
|
|
|
|
// Store price history
|
|
PriceChangeType changeType = _determinePriceChangeType(previousData.averagePrice, newAveragePrice);
|
|
uint256 changePercentage = previousData.averagePrice > 0 ?
|
|
((newAveragePrice - previousData.averagePrice) * 10000) / previousData.averagePrice : 0;
|
|
|
|
priceHistory[priceUpdateId].push(PriceHistory({
|
|
timestamp: timestamp,
|
|
price: newAveragePrice,
|
|
supply: _totalSupply,
|
|
demand: _totalDemand,
|
|
volume: _totalVolume,
|
|
changeType: changeType,
|
|
changePercentage: changePercentage
|
|
}));
|
|
|
|
// Update provider prices
|
|
_updateProviderPrices(newAveragePrice, utilizationRate);
|
|
|
|
// Update regional prices
|
|
_updateRegionalPrices(_totalSupply, _totalDemand);
|
|
|
|
// Check price alerts
|
|
_checkPriceAlerts(newAveragePrice);
|
|
|
|
// Apply surge or discount pricing if needed
|
|
_applySpecialPricing(currentCondition, priceVolatility);
|
|
|
|
isValidPriceUpdate[priceUpdateId] = true;
|
|
recentPriceUpdates.push(priceUpdateId);
|
|
|
|
emit MarketDataUpdated(timestamp, _totalSupply, _totalDemand, newAveragePrice, currentCondition);
|
|
emit PriceCalculated(timestamp, newAveragePrice, previousData.averagePrice, changeType, changePercentage);
|
|
}
|
|
|
|
/**
|
|
* @dev Calculates dynamic price based on market conditions
|
|
* @param _supply Total supply
|
|
* @param _demand Total demand
|
|
* @param _utilizationRate Utilization rate in basis points
|
|
* @param _marketSentiment Market sentiment (0-100)
|
|
* @param _previousPrice Previous average price
|
|
*/
|
|
function _calculateDynamicPrice(
|
|
uint256 _supply,
|
|
uint256 _demand,
|
|
uint256 _utilizationRate,
|
|
uint256 _marketSentiment,
|
|
uint256 _previousPrice
|
|
) internal view returns (uint256) {
|
|
// Base price calculation
|
|
uint256 newPrice = basePricePerHour;
|
|
|
|
// Supply/demand adjustment
|
|
if (_demand > _supply) {
|
|
uint256 demandPremium = ((_demand - _supply) * 10000) / _supply;
|
|
newPrice = (newPrice * (10000 + demandPremium)) / 10000;
|
|
} else if (_supply > _demand) {
|
|
uint256 supplyDiscount = ((_supply - _demand) * 10000) / _supply;
|
|
newPrice = (newPrice * (10000 - supplyDiscount)) / 10000;
|
|
}
|
|
|
|
// Utilization rate adjustment
|
|
if (_utilizationRate > 8000) { // > 80% utilization
|
|
uint256 utilizationPremium = (_utilizationRate - 8000) / 2;
|
|
newPrice = (newPrice * (10000 + utilizationPremium)) / 10000;
|
|
} else if (_utilizationRate < 2000) { // < 20% utilization
|
|
uint256 utilizationDiscount = (2000 - _utilizationRate) / 4;
|
|
newPrice = (newPrice * (10000 - utilizationDiscount)) / 10000;
|
|
}
|
|
|
|
// Market sentiment adjustment
|
|
if (_marketSentiment > 70) { // High sentiment
|
|
newPrice = (newPrice * 10500) / 10000; // 5% premium
|
|
} else if (_marketSentiment < 30) { // Low sentiment
|
|
newPrice = (newPrice * 9500) / 10000; // 5% discount
|
|
}
|
|
|
|
// Smoothing with previous price
|
|
if (_previousPrice > 0) {
|
|
newPrice = (newPrice * (10000 - smoothingFactor) + _previousPrice * smoothingFactor) / 10000;
|
|
}
|
|
|
|
// Apply price bounds
|
|
if (newPrice < minPricePerHour) {
|
|
newPrice = minPricePerHour;
|
|
} else if (newPrice > maxPricePerHour) {
|
|
newPrice = maxPricePerHour;
|
|
}
|
|
|
|
return newPrice;
|
|
}
|
|
|
|
/**
|
|
* @dev Updates provider-specific pricing
|
|
* @param _marketAveragePrice Current market average price
|
|
* @param _marketUtilizationRate Market utilization rate
|
|
*/
|
|
function _updateProviderPrices(uint256 _marketAveragePrice, uint256 _marketUtilizationRate) internal {
|
|
// This would typically iterate through all active providers
|
|
// For now, we'll update based on provider performance and reputation
|
|
|
|
// Implementation would include:
|
|
// 1. Get provider performance metrics
|
|
// 2. Calculate provider-specific adjustments
|
|
// 3. Update provider pricing based on strategy
|
|
// 4. Emit ProviderPriceUpdated events
|
|
}
|
|
|
|
/**
|
|
* @dev Updates regional pricing
|
|
* @param _totalSupply Total supply
|
|
* @param _totalDemand Total demand
|
|
*/
|
|
function _updateRegionalPrices(uint256 _totalSupply, uint256 _totalDemand) internal {
|
|
for (uint256 i = 0; i < supportedRegions.length; i++) {
|
|
string memory region = supportedRegions[i];
|
|
RegionalPricing storage regional = regionalPricing[region];
|
|
|
|
// Calculate regional supply/demand (simplified)
|
|
uint256 regionalSupply = (_totalSupply * regionalPricing[region].localSupply) / 100;
|
|
uint256 regionalDemand = (_totalDemand * regionalPricing[region].localDemand) / 100;
|
|
|
|
// Calculate regional multiplier
|
|
uint256 newMultiplier = 10000; // Base multiplier
|
|
if (regionalDemand > regionalSupply) {
|
|
newMultiplier = (newMultiplier * 11000) / 10000; // 10% premium
|
|
} else if (regionalSupply > regionalDemand) {
|
|
newMultiplier = (newMultiplier * 9500) / 10000; // 5% discount
|
|
}
|
|
|
|
regional.regionalMultiplier = newMultiplier;
|
|
regional.lastUpdateTime = block.timestamp;
|
|
|
|
emit RegionalPriceUpdated(region, newMultiplier, regionalSupply, regionalDemand);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dev Determines market condition based on utilization and volatility
|
|
* @param _utilizationRate Utilization rate in basis points
|
|
* @param _priceVolatility Price volatility in basis points
|
|
*/
|
|
function _determineMarketCondition(uint256 _utilizationRate, uint256 _priceVolatility) internal pure returns (MarketCondition) {
|
|
if (_utilizationRate > 9000) {
|
|
return MarketCondition.Surge;
|
|
} else if (_utilizationRate > 7000) {
|
|
return MarketCondition.Undersupply;
|
|
} else if (_utilizationRate > 3000) {
|
|
return MarketCondition.Balanced;
|
|
} else if (_utilizationRate > 1000) {
|
|
return MarketCondition.Oversupply;
|
|
} else {
|
|
return MarketCondition.Crash;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dev Determines price change type
|
|
* @param _oldPrice Previous price
|
|
* @param _newPrice New price
|
|
*/
|
|
function _determinePriceChangeType(uint256 _oldPrice, uint256 _newPrice) internal pure returns (PriceChangeType) {
|
|
if (_oldPrice == 0) {
|
|
return PriceChangeType.Stable;
|
|
}
|
|
|
|
uint256 changePercentage = 0;
|
|
if (_newPrice > _oldPrice) {
|
|
changePercentage = ((_newPrice - _oldPrice) * 10000) / _oldPrice;
|
|
} else {
|
|
changePercentage = ((_oldPrice - _newPrice) * 10000) / _oldPrice;
|
|
}
|
|
|
|
if (changePercentage < 500) { // < 5%
|
|
return PriceChangeType.Stable;
|
|
} else if (changePercentage > 2000) { // > 20%
|
|
return _newPrice > _oldPrice ? PriceChangeType.Surge : PriceChangeType.Discount;
|
|
} else {
|
|
return _newPrice > _oldPrice ? PriceChangeType.Increase : PriceChangeType.Decrease;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dev Applies special pricing based on market conditions
|
|
* @param _condition Current market condition
|
|
* @param _volatility Price volatility
|
|
*/
|
|
function _applySpecialPricing(MarketCondition _condition, uint256 _volatility) internal {
|
|
if (_condition == MarketCondition.Surge) {
|
|
emit SurgePricingActivated(surgeMultiplier, 3600, "High demand detected");
|
|
} else if (_condition == MarketCondition.Crash) {
|
|
emit DiscountPricingActivated(discountMultiplier, 3600, "Low demand detected");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dev Creates demand forecast
|
|
* @param _forecastPeriod Period to forecast (in seconds)
|
|
* @param _predictedDemand Predicted demand
|
|
* @param _confidence Confidence level (0-100)
|
|
*/
|
|
function createDemandForecast(
|
|
uint256 _forecastPeriod,
|
|
uint256 _predictedDemand,
|
|
uint256 _confidence
|
|
) external onlyAuthorizedPriceOracle nonReentrant whenNotPaused {
|
|
require(_forecastPeriod > 0, "Invalid forecast period");
|
|
require(_predictedDemand > 0, "Invalid predicted demand");
|
|
require(_confidence <= 100, "Invalid confidence");
|
|
|
|
uint256 forecastId = priceUpdateCounter++;
|
|
|
|
demandForecasts[forecastId] = DemandForecast({
|
|
forecastPeriod: _forecastPeriod,
|
|
predictedDemand: _predictedDemand,
|
|
confidence: _confidence,
|
|
forecastTime: block.timestamp,
|
|
actualDemand: 0,
|
|
forecastAccuracy: 0
|
|
});
|
|
|
|
emit DemandForecastCreated(_forecastPeriod, _predictedDemand, _confidence, block.timestamp);
|
|
}
|
|
|
|
/**
|
|
* @dev Creates price alert
|
|
* @param _subscriber Address to notify
|
|
* @param _alertType Type of alert
|
|
* @param _thresholdPrice Threshold price
|
|
* @param _notificationMethod Notification method
|
|
*/
|
|
function createPriceAlert(
|
|
address _subscriber,
|
|
PriceAlertType _alertType,
|
|
uint256 _thresholdPrice,
|
|
string memory _notificationMethod
|
|
) external nonReentrant whenNotPaused returns (uint256) {
|
|
require(_subscriber != address(0), "Invalid subscriber");
|
|
require(_thresholdPrice > 0, "Invalid threshold price");
|
|
|
|
uint256 alertId = priceUpdateCounter++;
|
|
|
|
priceAlerts[alertId] = PriceAlert({
|
|
alertId: alertId,
|
|
subscriber: _subscriber,
|
|
alertType: _alertType,
|
|
thresholdPrice: _thresholdPrice,
|
|
currentPrice: 0,
|
|
isActive: true,
|
|
lastTriggered: 0,
|
|
notificationMethod: _notificationMethod
|
|
});
|
|
|
|
activePriceAlerts.push(alertId);
|
|
|
|
return alertId;
|
|
}
|
|
|
|
/**
|
|
* @dev Gets current market price
|
|
* @param _provider Provider address (optional, for provider-specific pricing)
|
|
* @param _region Region (optional, for regional pricing)
|
|
*/
|
|
function getMarketPrice(address _provider, string memory _region)
|
|
external
|
|
view
|
|
returns (uint256)
|
|
{
|
|
uint256 basePrice = basePricePerHour;
|
|
|
|
// Get latest market data
|
|
if (priceUpdateCounter > 0) {
|
|
basePrice = marketDataHistory[priceUpdateCounter - 1].averagePrice;
|
|
}
|
|
|
|
// Apply regional multiplier if specified
|
|
if (bytes(_region).length > 0) {
|
|
RegionalPricing storage regional = regionalPricing[_region];
|
|
basePrice = (basePrice * regional.regionalMultiplier) / 10000;
|
|
}
|
|
|
|
// Apply provider-specific pricing if specified
|
|
if (_provider != address(0)) {
|
|
ProviderPricing storage provider = providerPricing[_provider];
|
|
if (provider.currentPrice > 0) {
|
|
basePrice = provider.currentPrice;
|
|
}
|
|
}
|
|
|
|
return basePrice;
|
|
}
|
|
|
|
/**
|
|
* @dev Gets market data
|
|
* @param _timestamp Timestamp to get data for (0 for latest)
|
|
*/
|
|
function getMarketData(uint256 _timestamp)
|
|
external
|
|
view
|
|
returns (MarketData memory)
|
|
{
|
|
if (_timestamp == 0 && priceUpdateCounter > 0) {
|
|
return marketDataHistory[priceUpdateCounter - 1];
|
|
}
|
|
|
|
// Find closest timestamp
|
|
for (uint256 i = priceUpdateCounter; i > 0; i--) {
|
|
if (marketDataHistory[i - 1].lastUpdateTime <= _timestamp) {
|
|
return marketDataHistory[i - 1];
|
|
}
|
|
}
|
|
|
|
revert("No market data found for timestamp");
|
|
}
|
|
|
|
/**
|
|
* @dev Gets price history
|
|
* @param _count Number of historical entries to return
|
|
*/
|
|
function getPriceHistory(uint256 _count)
|
|
external
|
|
view
|
|
returns (PriceHistory[] memory)
|
|
{
|
|
uint256 startIndex = priceUpdateCounter > _count ? priceUpdateCounter - _count : 0;
|
|
uint256 length = priceUpdateCounter - startIndex;
|
|
|
|
PriceHistory[] memory history = new PriceHistory[](length);
|
|
|
|
for (uint256 i = 0; i < length; i++) {
|
|
history[i] = priceHistory[startIndex + i][0];
|
|
}
|
|
|
|
return history;
|
|
}
|
|
|
|
/**
|
|
* @dev Authorizes a price oracle
|
|
* @param _oracle Address of the oracle
|
|
*/
|
|
function authorizePriceOracle(address _oracle) external onlyOwner {
|
|
require(_oracle != address(0), "Invalid oracle address");
|
|
authorizedPriceOracles[_oracle] = true;
|
|
emit PriceOracleAuthorized(_oracle, 0);
|
|
}
|
|
|
|
/**
|
|
* @dev Revokes price oracle authorization
|
|
* @param _oracle Address of the oracle
|
|
*/
|
|
function revokePriceOracle(address _oracle) external onlyOwner {
|
|
authorizedPriceOracles[_oracle] = false;
|
|
emit PriceOracleRevoked(_oracle, "Authorization revoked");
|
|
}
|
|
|
|
/**
|
|
* @dev Updates base pricing parameters
|
|
* @param _basePrice New base price
|
|
* @param _minPrice New minimum price
|
|
* @param _maxPrice New maximum price
|
|
*/
|
|
function updateBasePricing(
|
|
uint256 _basePrice,
|
|
uint256 _minPrice,
|
|
uint256 _maxPrice
|
|
) external onlyOwner {
|
|
require(_minPrice > 0 && _maxPrice > _minPrice, "Invalid price bounds");
|
|
require(_basePrice >= _minPrice && _basePrice <= _maxPrice, "Base price out of bounds");
|
|
|
|
basePricePerHour = _basePrice;
|
|
minPricePerHour = _minPrice;
|
|
maxPricePerHour = _maxPrice;
|
|
}
|
|
|
|
/**
|
|
* @dev Emergency pause function
|
|
*/
|
|
function pause() external onlyOwner {
|
|
_pause();
|
|
}
|
|
|
|
/**
|
|
* @dev Unpause function
|
|
*/
|
|
function unpause() external onlyOwner {
|
|
_unpause();
|
|
}
|
|
|
|
// Internal function to check price alerts
|
|
function _checkPriceAlerts(uint256 _currentPrice) internal {
|
|
for (uint256 i = 0; i < activePriceAlerts.length; i++) {
|
|
uint256 alertId = activePriceAlerts[i];
|
|
PriceAlert storage alert = priceAlerts[alertId];
|
|
|
|
if (!alert.isActive) continue;
|
|
|
|
bool shouldTrigger = false;
|
|
|
|
if (alert.alertType == PriceAlertType.PriceAbove && _currentPrice > alert.thresholdPrice) {
|
|
shouldTrigger = true;
|
|
} else if (alert.alertType == PriceAlertType.PriceBelow && _currentPrice < alert.thresholdPrice) {
|
|
shouldTrigger = true;
|
|
}
|
|
|
|
if (shouldTrigger) {
|
|
alert.lastTriggered = block.timestamp;
|
|
emit PriceAlertTriggered(alertId, alert.subscriber, alert.alertType, _currentPrice, alert.thresholdPrice);
|
|
}
|
|
}
|
|
}
|
|
}
|