aere-contracts/contracts/AereMiningSubscription.sol
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
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.
2026-07-20 01:02:37 +03:00

169 lines
6.8 KiB
Solidity
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 {}
}