Aere Network public source. Everything here can be checked against the live chain (chain id 2800, https://rpc.aere.network). Scope note, stated up front rather than buried: consensus on chain 2800 is classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at the signature, precompile, account and transport layers. Nothing here makes the consensus post-quantum, and no document in it should be read as claiming so.
169 lines
6.8 KiB
Solidity
169 lines
6.8 KiB
Solidity
// SPDX-License-Identifier: MIT
|
||
pragma solidity ^0.8.19;
|
||
|
||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
||
|
||
/**
|
||
* @title AereMiningSubscription (AireFlow)
|
||
* @notice Cloud mining subscriptions paid in AERE
|
||
* @dev Whitepaper §5.3 — "Revolutionary Subscription Mining Platform"
|
||
* Users pay a monthly AERE fee for guaranteed hash rate allocation,
|
||
* receive proportional block rewards distributed by the foundation.
|
||
*
|
||
* Reference whitepaper §5.3:
|
||
* AireFlow 15.0 TH/s example: $300/month → ~30 AERE/month rewards (illustrative).
|
||
*/
|
||
contract AereMiningSubscription is Ownable, ReentrancyGuard {
|
||
enum Tier { BASIC, PREMIUM, ENTERPRISE, AIREFLOW }
|
||
|
||
struct TierConfig {
|
||
uint256 monthlyPriceAere; // wei
|
||
uint256 hashRateThs; // TH/s allocated
|
||
bool active;
|
||
}
|
||
|
||
struct Subscription {
|
||
Tier tier;
|
||
uint256 startBlock;
|
||
uint256 expiresAt; // block.timestamp
|
||
uint256 totalPaid;
|
||
uint256 unclaimedRewards;
|
||
uint256 lastRewardBlock;
|
||
}
|
||
|
||
mapping(Tier => TierConfig) public tiers;
|
||
mapping(address => Subscription) public subscriptions;
|
||
address[] public subscribers;
|
||
mapping(address => uint256) private subscriberIndex;
|
||
|
||
uint256 public constant SUBSCRIPTION_PERIOD = 30 days;
|
||
uint256 public networkHashRateThs = 2500; // bootstrap value, governance-updatable
|
||
uint256 public blockReward = 1.25 ether; // matches whitepaper §5.3 (1.25 AERE/block)
|
||
uint256 public difficultyAdjustmentBps = 10750; // Dt = 1.075 example
|
||
|
||
address public foundationTreasury;
|
||
|
||
event TierConfigured(Tier indexed tier, uint256 priceAere, uint256 hashRateThs);
|
||
event Subscribed(address indexed user, Tier tier, uint256 expiresAt);
|
||
event RewardsAccrued(address indexed user, uint256 amount);
|
||
event RewardsClaimed(address indexed user, uint256 amount);
|
||
event TreasurySet(address indexed treasury);
|
||
|
||
constructor(address _foundationTreasury) {
|
||
foundationTreasury = _foundationTreasury;
|
||
|
||
// Default tier configuration (whitepaper-aligned, governance-mutable)
|
||
tiers[Tier.BASIC] = TierConfig({monthlyPriceAere: 10 ether, hashRateThs: 1, active: true});
|
||
tiers[Tier.PREMIUM] = TierConfig({monthlyPriceAere: 50 ether, hashRateThs: 6, active: true});
|
||
tiers[Tier.ENTERPRISE] = TierConfig({monthlyPriceAere: 200 ether, hashRateThs: 30, active: true});
|
||
tiers[Tier.AIREFLOW] = TierConfig({monthlyPriceAere: 100 ether, hashRateThs: 15, active: true});
|
||
}
|
||
|
||
function configureTier(Tier tier, uint256 priceAere, uint256 hashRateThs) external onlyOwner {
|
||
tiers[tier] = TierConfig({monthlyPriceAere: priceAere, hashRateThs: hashRateThs, active: true});
|
||
emit TierConfigured(tier, priceAere, hashRateThs);
|
||
}
|
||
|
||
function setTreasury(address _treasury) external onlyOwner {
|
||
foundationTreasury = _treasury;
|
||
emit TreasurySet(_treasury);
|
||
}
|
||
|
||
function setNetworkHashRate(uint256 _ths) external onlyOwner {
|
||
require(_ths > 0, "AereMining: hashrate>0");
|
||
networkHashRateThs = _ths;
|
||
}
|
||
|
||
/**
|
||
* @notice Subscribe (or extend) to a mining tier. Pay msg.value in native AERE.
|
||
*/
|
||
function subscribe(Tier tier) external payable nonReentrant {
|
||
TierConfig memory cfg = tiers[tier];
|
||
require(cfg.active, "AereMining: tier inactive");
|
||
require(msg.value >= cfg.monthlyPriceAere, "AereMining: insufficient payment");
|
||
|
||
Subscription storage s = subscriptions[msg.sender];
|
||
|
||
// Settle pending rewards before changing tier/period
|
||
if (s.tier == tier && s.expiresAt > block.timestamp) {
|
||
_accrue(msg.sender);
|
||
} else {
|
||
// New tier or expired — reset rewards baseline
|
||
s.lastRewardBlock = block.number;
|
||
}
|
||
|
||
if (s.expiresAt < block.timestamp) {
|
||
s.startBlock = block.number;
|
||
s.expiresAt = block.timestamp + SUBSCRIPTION_PERIOD;
|
||
} else {
|
||
s.expiresAt += SUBSCRIPTION_PERIOD;
|
||
}
|
||
s.tier = tier;
|
||
s.totalPaid += msg.value;
|
||
|
||
// Track unique subscribers
|
||
if (subscriberIndex[msg.sender] == 0) {
|
||
subscribers.push(msg.sender);
|
||
subscriberIndex[msg.sender] = subscribers.length;
|
||
}
|
||
|
||
// Forward payment to foundation treasury
|
||
(bool ok, ) = foundationTreasury.call{value: msg.value}("");
|
||
require(ok, "AereMining: treasury transfer failed");
|
||
|
||
emit Subscribed(msg.sender, tier, s.expiresAt);
|
||
}
|
||
|
||
/**
|
||
* @notice Pending rewards (in AERE wei) for a subscriber.
|
||
* @dev Formula (whitepaper §5.3):
|
||
* R = B × HR × (1 - Dt) × T
|
||
* where HR = userHashRate / networkHashRate
|
||
*/
|
||
function pendingRewards(address user) public view returns (uint256) {
|
||
Subscription memory s = subscriptions[user];
|
||
if (s.expiresAt < block.timestamp || s.lastRewardBlock == 0) return s.unclaimedRewards;
|
||
uint256 blocksElapsed = block.number - s.lastRewardBlock;
|
||
uint256 userHashRate = tiers[s.tier].hashRateThs;
|
||
// Compute (1 - Dt). difficultyAdjustmentBps is e.g. 10750 = 1.075.
|
||
// (1 - Dt) is negative if Dt > 1, but in the whitepaper formula difficulty
|
||
// damps reward, not amplifies. Use (10000 - (difficultyAdjustmentBps - 10000))
|
||
// capped to non-negative if Dt < 2.
|
||
int256 difficultyFactor = int256(20000) - int256(difficultyAdjustmentBps);
|
||
if (difficultyFactor < 0) difficultyFactor = 0;
|
||
uint256 dampedReward = (blockReward * uint256(difficultyFactor)) / 10000;
|
||
uint256 reward = (dampedReward * userHashRate * blocksElapsed) / networkHashRateThs;
|
||
return s.unclaimedRewards + reward;
|
||
}
|
||
|
||
function _accrue(address user) internal {
|
||
uint256 reward = pendingRewards(user);
|
||
Subscription storage s = subscriptions[user];
|
||
if (reward > s.unclaimedRewards) {
|
||
s.unclaimedRewards = reward;
|
||
emit RewardsAccrued(user, reward);
|
||
}
|
||
s.lastRewardBlock = block.number;
|
||
}
|
||
|
||
function claim() external nonReentrant {
|
||
_accrue(msg.sender);
|
||
Subscription storage s = subscriptions[msg.sender];
|
||
uint256 amount = s.unclaimedRewards;
|
||
require(amount > 0, "AereMining: nothing to claim");
|
||
s.unclaimedRewards = 0;
|
||
require(address(this).balance >= amount, "AereMining: pool insufficient - needs foundation top-up");
|
||
(bool ok, ) = msg.sender.call{value: amount}("");
|
||
require(ok, "AereMining: transfer failed");
|
||
emit RewardsClaimed(msg.sender, amount);
|
||
}
|
||
|
||
function subscriberCount() external view returns (uint256) {
|
||
return subscribers.length;
|
||
}
|
||
|
||
/// @notice Allow foundation to top up the reward pool
|
||
receive() external payable {}
|
||
}
|