Address
0x9dc97ca4fb3a023f8d0033d5e29338ddf58abc57Current Holdings
$0.00
TXs sent
not counted
First Active
2026-04-07
block 26,225,391
Last Active
164 days ago
block 26,227,735
Funded By
not identified
Net worth historyi
No net-worth snapshots recorded yet
exact matchGeniusBatchBuyersolc 0.8.34+commit.80d5c536runtime exact · creation exact
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/**
* @title GeniusBatchBuyer V2 — ArtistTokenFactory Edition
*
* @dev Two functions changed from V1:
*
* _purchaseToken: Previously bought various ecosystem tokens.
* Now always buys eDAI and sends it directly to the
* ArtistTokenFactory (streamingRewardsAddress).
* Factory accepts raw eDAI transfers — no function call needed.
*
* _fundStreamingPool: Previously called fundPool() on StreamingRewards.
* Now a no-op (eDAI already sent in _purchaseToken).
* Kept for call-chain compatibility.
*
* @dev Everything else is identical to V1:
* - Three PLS taxes (oracle, mefi, mysteryBox) before purchase
* - Health-based batch allocation (repurposed for future multi-factory support)
* - Token inventory system (tracks eDAI purchased and released)
* - Batch performance analytics
* - Auto-discovery and evolution engine
*
* @dev Post-deploy setup:
* 1. setEcosystemContracts(artistFactoryAddress)
* — points streamingRewardsAddress at the factory
* 2. No other changes needed — factory accepts raw eDAI
*/
interface IERC20Extended is IERC20 {
function decimals() external view returns (uint8);
}
interface IDEXRouter {
function WPLS() external pure returns (address);
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function getAmountsOut(uint amountIn, address[] calldata path)
external view returns (uint[] memory amounts);
}
interface IWPLS is IERC20Extended {
function deposit() external payable;
}
interface IStreamingRewards {
function getAllSupportedTokens() external view returns (address[] memory);
function getPoolInfo(address token) external view returns (
uint256 balance,
uint256 totalDeposited,
uint256 totalDistributed,
uint256 distributionRate,
bool isActive,
uint256 nextDistribution,
uint8 decimals
);
function fundPool(address token, uint256 amount) external;
function isTokenSupported(address tokenAddress) external view returns (bool);
}
contract GeniusBatchBuyer is Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;
// ============ CONSTANTS ============
address public constant WPLS = 0xA1077a294dDE1B09bB078844df40758a5D0f9a27;
address public constant EDAI = 0xefD766cCb38EaF1dfd701853BFCe31359239F305;
address public constant MYFI_TOKEN = 0x11D8C297BAEEA38E00F1a0FB7b288219a69c6522; // ⚠️ UPDATE TO MYFI ADDRESS BEFORE DEPLOY
address public constant PULSEX_ROUTER = 0x165C3410fC91EF562C50559f7d2289fEbed552d9;
address public constant ORACLE_ADDRESS = 0x17e7b189982D8Df2539d059b46467a09a7bCB91D;
uint256 public constant SYNC_INTERVAL = 3600;
uint256 public constant MAX_TOKENS_PER_QUERY = 50;
// ============ TAX CONFIGURATION ============
uint256 public oracleTaxBps = 200;
uint256 public myfiTaxBps = 200;
uint256 public mysteryBoxTaxBps = 200;
address public myfiAddress;
address public mysteryBoxAddress;
// ============ USD HEALTH THRESHOLDS ============
uint256 public constant CRITICAL_HEALTH = 20;
uint256 public constant LOW_HEALTH = 40;
uint256 public constant MEDIUM_HEALTH = 60;
uint256 public constant GOOD_HEALTH = 80;
uint256 public constant EXCELLENT_HEALTH = 100;
uint256 public constant CRITICAL_RELEASE_THRESHOLD = 20;
uint256 public constant LOW_RELEASE_THRESHOLD = 40;
uint256 public constant MEDIUM_RELEASE_THRESHOLD = 60;
uint256 public constant BUFFER_RELEASE_THRESHOLD = 80;
uint256 public constant MIN_HOLD_TIME = 1800;
uint256 public constant MAX_HOLD_TIME = 86400;
// ============ PRICE STATE ============
// ============ CORE STATE ============
IDEXRouter public immutable router;
address public keyTokenAddress;
address public streamingRewardsAddress; // V2: points at ArtistTokenFactory
// V2: Dual-token purchase destinations
// factoryAddress receives eDAI → used for artist token minting + creator payments
// jackpotAddress receives MYFI → funds weekly streaming competition prizes
// streamingRewardsAddress is kept for IStreamingRewards discovery compat
address public factoryAddress; // ArtistTokenFactory — receives eDAI
address public jackpotAddress; // StreamingRewardsV6 — receives MYFI
// Split of purchase allocation between eDAI (factory) and MYFI (jackpot)
// Default 70% eDAI / 30% MEFI. Must sum to 10000.
uint256 public edaiSplitBps = 7000; // 70% → factory as eDAI
uint256 public myfiSplitBps = 3000; // 30% → jackpot as MYFI
bool public useStreamingRewards = true;
bool public autoDiscoveryEnabled = true;
bool public healthBasedAllocation = true;
bool public batchesInitialized = false;
bool public autoReleaseEnabled = true;
address[] public batch1Tokens;
address[] public batch2Tokens;
address[] public batch3Tokens;
// ============ BATCH PERFORMANCE TRACKING ============
mapping(uint8 => uint256) public batchSuccessRates;
mapping(uint8 => uint256) public batchExecutionCounts;
mapping(uint8 => uint256) public batchTotalVolume;
mapping(uint8 => uint256) public batchLastExecution;
// ============ TOKEN INVENTORY SYSTEM ============
struct TokenInventory {
uint256 balance;
uint256 reservedAmount;
uint256 lastReleaseTime;
uint256 totalPurchased;
uint256 totalReleased;
bool autoReleaseEnabled;
}
mapping(address => TokenInventory) public tokenInventories;
address[] public inventoryTokens;
uint256 public lastInventoryCheck;
mapping(address => uint256) public poolHealthScores;
mapping(address => uint256) public lastHealthUpdate;
mapping(address => uint256) public tokenPurchaseCount;
mapping(address => uint256) public totalTokensPurchased;
uint256 public lastSyncTime;
uint256 public syncCount;
uint256 public discoveryAttempts;
struct SystemStats {
uint256 totalBatches;
uint256 totalVolumePLS;
uint256 totalTaxPaid;
uint256 successfulSwaps;
uint256 failedSwaps;
uint256 poolsFunded;
uint256 tokensDiscovered;
uint256 lastOperationTime;
uint256 tokensStored;
uint256 tokensReleased;
}
SystemStats public stats;
// ============ EVENTS ============
event GeniusActivated(uint256 timestamp, address keyToken);
event EcosystemDiscovered(string component, address contractAddress, uint256 attempt);
event TokenBatchOptimized(uint8 indexed batchId, uint256 tokenCount, uint256 avgHealth);
event HealthBasedPurchase(address indexed token, uint256 plsAmount, uint256 tokensReceived, uint256 healthScore);
event OracleTaxPayment(uint256 plsAmount, uint256 timestamp);
event MyfiTaxPayment(uint256 plsAmount, uint256 timestamp);
event MysteryBoxTaxPayment(uint256 plsAmount, uint256 timestamp);
event StreamingPoolFunded(address indexed token, uint256 amount, uint256 newBalance);
event BatchExecutionComplete(uint8 indexed batchId, uint256 plsProcessed, uint256 tokensAffected);
event EmergencyMode(string reason, bool active);
event TokensStored(address indexed token, uint256 amount, uint256 totalInventory);
event TokensAutoReleased(address indexed token, uint256 amount, uint256 healthScore, string reason);
event InventoryRebalanced(uint256 tokensChecked, uint256 tokensReleased, uint256 totalValue);
event EmergencyRelease(address indexed token, uint256 amount, string reason);
event BatchPerformanceUpdate(uint8 indexed batchId, uint256 executionCount, uint256 successRate, string performanceGrade);
event BatchMilestone(uint8 indexed batchId, uint256 milestone, uint256 totalVolume);
event TaxConfigUpdated(uint256 oracleBps, uint256 mefiBps, uint256 mysteryBoxBps);
event TaxAddressUpdated(address mefi, address mysteryBox);
// V2
event EdaiBoughtAndSent(uint256 plsSpent, uint256 edaiReceived, address factory);
event MyfiBoughtAndSent(uint256 plsSpent, uint256 mefiReceived, address jackpot);
// ============ CONSTRUCTOR ============
constructor(
address _keyToken,
address _streamingRewards,
address _myfiAddress,
address _mysteryBoxAddress
) Ownable(msg.sender) {
router = IDEXRouter(PULSEX_ROUTER);
lastSyncTime = block.timestamp;
if (_keyToken != address(0)) {
keyTokenAddress = _keyToken;
emit EcosystemDiscovered("KEY_TOKEN", _keyToken, ++discoveryAttempts);
}
if (_streamingRewards != address(0)) {
streamingRewardsAddress = _streamingRewards;
emit EcosystemDiscovered("STREAMING_REWARDS", _streamingRewards, ++discoveryAttempts);
}
if (_myfiAddress != address(0)) myfiAddress = _myfiAddress;
if (_mysteryBoxAddress != address(0)) mysteryBoxAddress = _mysteryBoxAddress;
_initializeEmergencyFallback();
emit GeniusActivated(block.timestamp, msg.sender);
}
// ============ CORE BATCH FUNCTIONS ============
function buyBatch1() external payable nonReentrant {
_authenticateAndActivate();
require(msg.value > 0, "NP");
_evolveIfNeeded();
_executeBatch(1, msg.value);
}
function buyBatch2() external payable nonReentrant {
_authenticateAndActivate();
require(msg.value > 0, "NP");
_evolveIfNeeded();
_executeBatch(2, msg.value);
}
function buyBatch3() external payable nonReentrant {
_authenticateAndActivate();
require(msg.value > 0, "NP");
_evolveIfNeeded();
_executeBatch(3, msg.value);
}
// ============ AUTHENTICATION ============
function _authenticateAndActivate() internal {
if (keyTokenAddress == address(0) && autoDiscoveryEnabled) {
keyTokenAddress = msg.sender;
emit EcosystemDiscovered("KEY_TOKEN", msg.sender, ++discoveryAttempts);
emit GeniusActivated(block.timestamp, msg.sender);
}
require(
msg.sender == keyTokenAddress ||
msg.sender == owner() ||
hasRole("OPERATOR", msg.sender),
"A"
);
}
// ============ EVOLUTION ENGINE ============
function _evolveIfNeeded() internal {
if (block.timestamp - lastSyncTime < SYNC_INTERVAL) return;
_discoverEcosystem();
if (autoReleaseEnabled) _performIntelligentReleases();
lastSyncTime = block.timestamp;
syncCount++;
}
// ============ PRICE FEED ============
// ============ ECOSYSTEM DISCOVERY ============
function _discoverEcosystem() internal {
if (streamingRewardsAddress == address(0) && autoDiscoveryEnabled) {
_discoverStreamingRewards();
}
if (!batchesInitialized && streamingRewardsAddress != address(0)) {
_initializeFromStreamingRewards();
}
}
function _discoverStreamingRewards() internal {
address[] memory candidates = _getStreamingRewardsCandidates();
for (uint i = 0; i < candidates.length; i++) {
if (candidates[i] != address(0) && _validateStreamingRewards(candidates[i])) {
streamingRewardsAddress = candidates[i];
emit EcosystemDiscovered("STREAMING_REWARDS", candidates[i], ++discoveryAttempts);
break;
}
}
}
// ============ TOKEN INTELLIGENCE & BATCH OPTIMIZATION ============
function _distributePrioritizedTokens(address[] memory tokens) internal {
uint256 criticalCount = 0;
uint256 mediumCount = 0;
uint256 healthyCount = 0;
for (uint i = 0; i < tokens.length; i++) {
uint256 health = poolHealthScores[tokens[i]];
if (health <= LOW_HEALTH) { batch1Tokens.push(tokens[i]); criticalCount++; }
else if (health <= MEDIUM_HEALTH) { batch2Tokens.push(tokens[i]); mediumCount++; }
else { batch3Tokens.push(tokens[i]); healthyCount++; }
}
if (batch1Tokens.length == 0) { batch1Tokens.push(EDAI); batch1Tokens.push(MYFI_TOKEN); }
if (batch2Tokens.length == 0) { batch2Tokens.push(EDAI); batch2Tokens.push(MYFI_TOKEN); }
if (batch3Tokens.length == 0) { batch3Tokens.push(EDAI); batch3Tokens.push(MYFI_TOKEN); }
emit TokenBatchOptimized(1, batch1Tokens.length, 0);
emit TokenBatchOptimized(2, batch2Tokens.length, 0);
emit TokenBatchOptimized(3, batch3Tokens.length, 0);
}
// ============ BATCH EXECUTION ============
function _executeBatch(uint8 batchId, uint256 plsAmount) internal {
uint256 remaining = plsAmount;
if (oracleTaxBps > 0) {
uint256 tax = (plsAmount * oracleTaxBps) / 10000;
if (tax > 0 && tax <= remaining) {
remaining -= tax;
_payTax(ORACLE_ADDRESS, tax);
emit OracleTaxPayment(tax, block.timestamp);
}
}
if (myfiTaxBps > 0 && myfiAddress != address(0)) {
uint256 tax = (plsAmount * myfiTaxBps) / 10000;
if (tax > 0 && tax <= remaining) {
remaining -= tax;
_payTax(myfiAddress, tax);
emit MyfiTaxPayment(tax, block.timestamp);
}
}
if (mysteryBoxTaxBps > 0 && mysteryBoxAddress != address(0)) {
uint256 tax = (plsAmount * mysteryBoxTaxBps) / 10000;
if (tax > 0 && tax <= remaining) {
remaining -= tax;
_payTax(mysteryBoxAddress, tax);
emit MysteryBoxTaxPayment(tax, block.timestamp);
}
}
address[] memory tokens = _getBatchTokens(batchId);
require(tokens.length > 0, "E");
uint256[] memory allocations = _calculateIntelligentAllocations(tokens, remaining, batchId);
uint256 successfulPurchases = 0;
for (uint i = 0; i < tokens.length; i++) {
if (allocations[i] > 0 && _executePurchase(tokens[i], allocations[i])) {
successfulPurchases++;
}
}
_updateIntelligence(batchId, plsAmount, successfulPurchases);
uint256 rate = batchSuccessRates[batchId];
string memory grade;
if (rate >= 90) grade = "EXCELLENT";
else if (rate >= 70) grade = "GOOD";
else if (rate >= 50) grade = "FAIR";
else if (rate >= 30) grade = "POOR";
else grade = "CRITICAL";
emit BatchPerformanceUpdate(batchId, batchExecutionCounts[batchId], rate, grade);
if (batchExecutionCounts[batchId] % 25 == 0) {
emit BatchMilestone(batchId, batchExecutionCounts[batchId], batchTotalVolume[batchId]);
}
emit BatchExecutionComplete(batchId, plsAmount, successfulPurchases);
}
function _payTax(address recipient, uint256 amount) internal {
(bool success, ) = recipient.call{value: amount}("");
if (success) {
stats.totalTaxPaid += amount;
} else {
(bool ok,) = payable(owner()).call{value: amount}("");
require(ok, "X");
}
}
function _getBiasFactor(uint8 batchId, uint256 health) internal pure returns (uint256) {
if (health <= CRITICAL_HEALTH) return batchId == 1 ? 300 : 200;
if (batchId == 1) {
return health <= LOW_HEALTH ? 300 : 75;
} else if (batchId == 2) {
if (health <= LOW_HEALTH) return 150;
if (health <= MEDIUM_HEALTH) return 300;
return 75;
} else {
if (health <= LOW_HEALTH) return 125;
if (health <= MEDIUM_HEALTH) return 150;
return 300;
}
}
function _calculateIntelligentAllocations(
address[] memory tokens,
uint256 totalPLS,
uint8 batchId
) internal view returns (uint256[] memory) {
uint256[] memory allocations = new uint256[](tokens.length);
// V2: For EDAI/MYFI tokens, use the configured split ratio
// For any other tokens (legacy), fall back to health-based
bool hasSplitTokens = false;
for (uint i = 0; i < tokens.length; i++) {
if (tokens[i] == EDAI || tokens[i] == MYFI_TOKEN) {
hasSplitTokens = true;
break;
}
}
if (hasSplitTokens) {
uint256 allocated = 0;
for (uint i = 0; i < tokens.length; i++) {
if (tokens[i] == EDAI) {
allocations[i] = (totalPLS * edaiSplitBps) / 10000;
allocated += allocations[i];
} else if (tokens[i] == MYFI_TOKEN) {
allocations[i] = (totalPLS * myfiSplitBps) / 10000;
allocated += allocations[i];
}
}
// Any dust from rounding goes to the last EDAI token
uint256 dust = totalPLS - allocated;
if (dust > 0) {
for (uint i = 0; i < tokens.length; i++) {
if (tokens[i] == EDAI) { allocations[i] += dust; break; }
}
}
return allocations;
}
// Legacy health-based allocation
if (!healthBasedAllocation) {
uint256 plsPerToken = totalPLS / tokens.length;
for (uint i = 0; i < tokens.length; i++) allocations[i] = plsPerToken;
return allocations;
}
uint256 totalInversePriority = 0;
uint256[] memory priorities = new uint256[](tokens.length);
for (uint i = 0; i < tokens.length; i++) {
uint256 health = poolHealthScores[tokens[i]];
uint256 baseWeight = health > 0 ? (200 - health) : 100;
priorities[i] = (baseWeight * _getBiasFactor(batchId, health)) / 100;
totalInversePriority += priorities[i];
}
if (totalInversePriority > 0) {
uint256 allocated = 0;
for (uint i = 0; i < tokens.length - 1; i++) {
allocations[i] = (totalPLS * priorities[i]) / totalInversePriority;
allocated += allocations[i];
}
allocations[tokens.length - 1] = totalPLS - allocated;
}
return allocations;
}
function _executePurchase(address token, uint256 plsAmount) internal returns (bool) {
uint256 tokensReceived = _purchaseToken(token, plsAmount);
if (tokensReceived > 0) {
_storeTokensInInventory(EDAI, tokensReceived); // track eDAI in inventory
tokenPurchaseCount[token]++;
totalTokensPurchased[token] += tokensReceived;
emit HealthBasedPurchase(token, plsAmount, tokensReceived, poolHealthScores[token]);
return true;
}
return false;
}
// ============ V2: CORE PURCHASE FUNCTION ============
/**
* @dev V2: Routes purchase based on token parameter.
* EDAI → buys eDAI, sends to factoryAddress (artist token engine)
* MYFI → buys MYFI, sends to jackpotAddress (weekly prize pool)
* Both destinations accept raw transfers — no function call needed.
*
* Split is controlled by edaiSplitBps / myfiSplitBps.
* Batch tokens are initialised as [EDAI, MYFI_TOKEN] so the
* existing health allocation naturally splits each batch.
*/
function _purchaseToken(address token, uint256 plsAmount) internal returns (uint256) {
bool isEdai = (token == EDAI);
bool isMefi = (token == MYFI_TOKEN);
if (!isEdai && !isMefi) return 0; // unknown token — skip
address[] memory path = new address[](2);
path[0] = WPLS;
path[1] = token;
uint256 balanceBefore = IERC20(token).balanceOf(address(this));
try router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: plsAmount}(
0, path, address(this), block.timestamp + 300
) {
uint256 received = IERC20(token).balanceOf(address(this)) - balanceBefore;
if (received > 0) {
if (isEdai && factoryAddress != address(0)) {
IERC20(EDAI).safeTransfer(factoryAddress, received);
emit EdaiBoughtAndSent(plsAmount, received, factoryAddress);
} else if (isMefi && jackpotAddress != address(0)) {
IERC20(MYFI_TOKEN).safeTransfer(jackpotAddress, received);
emit MyfiBoughtAndSent(plsAmount, received, jackpotAddress);
}
}
stats.successfulSwaps++;
return received;
} catch {
stats.failedSwaps++;
return 0;
}
}
// ============ INVENTORY MANAGEMENT ============
function _distributeTokensIntelligently(address token, uint256 amount) internal {
_storeTokensInInventory(token, amount);
uint256 healthScore = poolHealthScores[token];
if (healthScore <= CRITICAL_RELEASE_THRESHOLD) {
_executeAutoRelease(token, "CRITICAL_HEALTH");
} else if (healthScore <= LOW_RELEASE_THRESHOLD) {
uint256 releaseAmount = tokenInventories[token].balance * 75 / 100;
if (releaseAmount > 0) _executePartialRelease(token, releaseAmount, "LOW_HEALTH");
}
}
function _storeTokensInInventory(address token, uint256 amount) internal {
TokenInventory storage inv = tokenInventories[token];
if (inv.balance == 0) {
inventoryTokens.push(token);
inv.autoReleaseEnabled = true;
}
inv.balance += amount;
inv.totalPurchased += amount;
inv.lastReleaseTime = block.timestamp;
stats.tokensStored += amount;
emit TokensStored(token, amount, inv.balance);
}
function _performIntelligentReleases() internal {
for (uint i = 0; i < inventoryTokens.length && i < 20; i++) {
address token = inventoryTokens[i];
TokenInventory storage inv = tokenInventories[token];
if (!inv.autoReleaseEnabled || inv.balance == 0) continue;
uint256 releaseAmount = _calculateOptimalRelease(
token,
poolHealthScores[token],
block.timestamp - inv.lastReleaseTime
);
if (releaseAmount > 0) _executePartialRelease(token, releaseAmount, "AUTO_EVOLUTION");
}
}
function _calculateOptimalRelease(
address token,
uint256 healthScore,
uint256 timeSinceLastRelease
) internal view returns (uint256) {
uint256 available = tokenInventories[token].balance;
if (available == 0) return 0;
if (timeSinceLastRelease >= MAX_HOLD_TIME) return available;
if (timeSinceLastRelease < MIN_HOLD_TIME && healthScore > CRITICAL_RELEASE_THRESHOLD) return 0;
if (healthScore <= CRITICAL_RELEASE_THRESHOLD) return available;
else if (healthScore <= LOW_RELEASE_THRESHOLD) return available * 75 / 100;
else if (healthScore <= MEDIUM_RELEASE_THRESHOLD) return available * 50 / 100;
else if (healthScore <= BUFFER_RELEASE_THRESHOLD) return available * 25 / 100;
return 0;
}
function _executePartialRelease(address token, uint256 amount, string memory reason) internal {
TokenInventory storage inv = tokenInventories[token];
require(inv.balance >= amount, "B");
inv.balance -= amount;
inv.totalReleased += amount;
inv.lastReleaseTime = block.timestamp;
stats.tokensReleased += amount;
uint256 healthScore = poolHealthScores[token];
if (useStreamingRewards && _fundStreamingPool(token, amount)) {
stats.poolsFunded++;
emit TokensAutoReleased(token, amount, healthScore, reason);
return;
}
IERC20(token).safeTransfer(owner(), amount);
emit TokensAutoReleased(token, amount, healthScore, "TO_OWNER");
}
function _executeAutoRelease(address token, string memory reason) internal {
uint256 fullBalance = tokenInventories[token].balance;
if (fullBalance > 0) _executePartialRelease(token, fullBalance, reason);
}
// ============ V2: CONVERT TOKENS TO eDAI/MEFI AND ROUTE ============
/**
* @dev Converts any ERC20 token received (e.g. farming fees) into eDAI and MEFI
* using a two-hop path: token → WPLS → eDAI, then splits per edaiSplitBps.
* Called by _executePartialRelease when inventory tokens need routing.
* WPLS is used as intermediary because every token has a WPLS pair on PulseX.
*/
function _fundStreamingPool(address token, uint256 amount) internal returns (bool) {
if (amount == 0) return false;
if (factoryAddress == address(0) && jackpotAddress == address(0)) return false;
// Special case: if token is already eDAI, skip swap and route directly
if (token == EDAI) {
return _routeEdai(amount);
}
// Special case: if token is WPLS, skip first hop
if (token == WPLS) {
return _wplsToEdaiAndRoute(amount);
}
// General case: token → WPLS → eDAI (two-hop)
uint256 wplsBefore = IERC20(WPLS).balanceOf(address(this));
// Approve router for input token
IERC20(token).approve(address(router), amount);
address[] memory path1 = new address[](2);
path1[0] = token;
path1[1] = WPLS;
try router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
amount, 0, path1, address(this), block.timestamp + 300
) {
uint256 wplsReceived = IERC20(WPLS).balanceOf(address(this)) - wplsBefore;
if (wplsReceived == 0) return false;
return _wplsToEdaiAndRoute(wplsReceived);
} catch {
IERC20(token).approve(address(router), 0);
return false;
}
}
/**
* @dev Swaps WPLS → eDAI then splits to factory and jackpot per edaiSplitBps.
*/
function _wplsToEdaiAndRoute(uint256 wplsAmount) internal returns (bool) {
uint256 edaiBefore = IERC20(EDAI).balanceOf(address(this));
IWPLS(WPLS).approve(address(router), wplsAmount);
address[] memory path = new address[](2);
path[0] = WPLS;
path[1] = EDAI;
try router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
wplsAmount, 0, path, address(this), block.timestamp + 300
) {
uint256 edaiReceived = IERC20(EDAI).balanceOf(address(this)) - edaiBefore;
if (edaiReceived == 0) return false;
return _routeEdai(edaiReceived);
} catch {
IWPLS(WPLS).approve(address(router), 0);
return false;
}
}
/**
* @dev Routes eDAI to factory and buys MYFI for jackpot per edaiSplitBps.
* eDAI split → factory (raw transfer)
* MYFI split → swap eDAI→MYFI → jackpot (raw transfer)
*/
function _routeEdai(uint256 edaiAmount) internal returns (bool) {
if (edaiAmount == 0) return false;
uint256 forFactory = (edaiAmount * edaiSplitBps) / 10000;
uint256 forJackpot = edaiAmount - forFactory;
// eDAI → factory (raw transfer, factory reads live balance)
if (forFactory > 0 && factoryAddress != address(0)) {
IERC20(EDAI).safeTransfer(factoryAddress, forFactory);
emit EdaiBoughtAndSent(0, forFactory, factoryAddress);
}
// eDAI → MYFI → jackpot
if (forJackpot > 0 && jackpotAddress != address(0)) {
uint256 mefiBefore = IERC20(MYFI_TOKEN).balanceOf(address(this));
IERC20(EDAI).approve(address(router), forJackpot);
address[] memory path = new address[](2);
path[0] = EDAI;
path[1] = MYFI_TOKEN;
try router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
forJackpot, 0, path, address(this), block.timestamp + 300
) {
uint256 mefiReceived = IERC20(MYFI_TOKEN).balanceOf(address(this)) - mefiBefore;
if (mefiReceived > 0) {
IERC20(MYFI_TOKEN).safeTransfer(jackpotAddress, mefiReceived);
emit MyfiBoughtAndSent(0, mefiReceived, jackpotAddress);
}
} catch {
IERC20(EDAI).approve(address(router), 0);
// Swap failed — send eDAI to factory as fallback rather than lose it
if (forJackpot > 0 && factoryAddress != address(0)) {
IERC20(EDAI).safeTransfer(factoryAddress, forJackpot);
emit EdaiBoughtAndSent(0, forJackpot, factoryAddress);
}
}
}
return true;
}
// ============ INTELLIGENCE & ANALYTICS ============
function _updateIntelligence(uint8 batchId, uint256 plsAmount, uint256 successfulPurchases) internal {
stats.totalBatches += 1;
stats.totalVolumePLS += plsAmount;
stats.lastOperationTime = block.timestamp;
batchExecutionCounts[batchId]++;
batchTotalVolume[batchId] += plsAmount;
batchLastExecution[batchId] = block.timestamp;
address[] memory batchTokens = _getBatchTokens(batchId);
uint256 batchSuccessRate = batchTokens.length > 0
? (successfulPurchases * 100) / batchTokens.length : 0;
batchSuccessRates[batchId] = batchSuccessRate;
}
function _initializeFromStreamingRewards() internal {
try IStreamingRewards(streamingRewardsAddress).getAllSupportedTokens()
returns (address[] memory supportedTokens) {
if (supportedTokens.length > 0) {
_distributePrioritizedTokens(supportedTokens);
batchesInitialized = true;
}
} catch {
// V2: Factory doesn't implement IStreamingRewards — use EDAI as default batch token
batch1Tokens.push(EDAI);
batch2Tokens.push(EDAI);
batch3Tokens.push(EDAI);
batchesInitialized = true;
}
}
// ============ DISCOVERY CANDIDATES ============
function _getStreamingRewardsCandidates() internal pure returns (address[] memory) {
address[] memory c = new address[](3);
c[0] = 0x3f629eE74E1523e1E27742b0dE4706fAC838456F;
return c;
}
function _validateStreamingRewards(address candidate) internal view returns (bool) {
if (candidate == address(0) || candidate.code.length == 0) return false;
try IStreamingRewards(candidate).getAllSupportedTokens() returns (address[] memory tokens) {
return tokens.length > 0;
} catch { return false; }
}
// ============ UTILITIES ============
function _getBatchTokens(uint8 batchId) internal view returns (address[] memory) {
if (batchId == 1) return batch1Tokens;
if (batchId == 2) return batch2Tokens;
if (batchId == 3) return batch3Tokens;
revert("D");
}
function _initializeEmergencyFallback() internal {
// V2: Two purchase targets — eDAI for factory, MYFI for jackpot
// Each batch gets both so the split logic applies on every call
batch1Tokens.push(EDAI);
batch1Tokens.push(MYFI_TOKEN);
batch2Tokens.push(EDAI);
batch2Tokens.push(MYFI_TOKEN);
batch3Tokens.push(EDAI);
batch3Tokens.push(MYFI_TOKEN);
}
bytes32 private constant OPERATOR_ROLE = keccak256("OPERATOR");
function hasRole(string memory role, address account) internal view returns (bool) {
if (keccak256(abi.encodePacked(role)) == OPERATOR_ROLE) return account == owner();
return false;
}
// ============ PUBLIC INVENTORY MANAGEMENT ============
function checkAndReleaseTokens() external {
require(block.timestamp - lastInventoryCheck >= 1800, "F");
uint256 tokensChecked = 0;
uint256 tokensReleased_ = 0;
uint256 totalValueReleased = 0;
for (uint i = 0; i < inventoryTokens.length && i < MAX_TOKENS_PER_QUERY; i++) {
address token = inventoryTokens[i];
TokenInventory storage inv = tokenInventories[token];
if (!inv.autoReleaseEnabled || inv.balance == 0) continue;
tokensChecked++;
uint256 releaseAmount = _calculateOptimalRelease(
token,
poolHealthScores[token],
block.timestamp - inv.lastReleaseTime
);
if (releaseAmount > 0) {
_executePartialRelease(token, releaseAmount, _getHealthReason(poolHealthScores[token]));
tokensReleased_++;
totalValueReleased += releaseAmount;
}
}
lastInventoryCheck = block.timestamp;
emit InventoryRebalanced(tokensChecked, tokensReleased_, totalValueReleased);
}
function _getHealthReason(uint256 healthScore) internal pure returns (string memory) {
if (healthScore <= CRITICAL_RELEASE_THRESHOLD) return "CRITICAL_HEALTH";
if (healthScore <= LOW_RELEASE_THRESHOLD) return "LOW_HEALTH";
if (healthScore <= MEDIUM_RELEASE_THRESHOLD) return "MEDIUM_HEALTH";
if (healthScore <= BUFFER_RELEASE_THRESHOLD) return "BUFFER_HEALTH";
return "MAINTENANCE_RELEASE";
}
// ============ ADMIN ============
function setEcosystemContracts(address _streamingRewards) external onlyOwner {
if (_streamingRewards != address(0)) {
streamingRewardsAddress = _streamingRewards;
emit EcosystemDiscovered("STREAMING_REWARDS", _streamingRewards, ++discoveryAttempts);
}
}
/// @notice Set the ArtistTokenFactory address — receives eDAI purchases.
function setFactoryAddress(address _factory) external onlyOwner {
require(_factory != address(0), "Z");
factoryAddress = _factory;
}
/// @notice Set the StreamingRewardsV6 address — receives MYFI purchases for jackpot.
function setJackpotAddress(address _jackpot) external onlyOwner {
require(_jackpot != address(0), "Z");
jackpotAddress = _jackpot;
}
/// @notice Set the eDAI/MEFI purchase split. Must sum to 10000.
/// e.g. 7000/3000 = 70% eDAI to factory, 30% MYFI to jackpot.
function setPurchaseSplit(uint256 _edaiBps, uint256 _mefiBps) external onlyOwner {
require(_edaiBps + _mefiBps == 10000, "S");
edaiSplitBps = _edaiBps;
myfiSplitBps = _mefiBps;
}
function setMyfiAddress(address _mefi) external onlyOwner {
require(_mefi != address(0), "Z");
emit TaxAddressUpdated(_mefi, mysteryBoxAddress);
myfiAddress = _mefi;
}
function setMysteryBoxAddress(address _mysteryBox) external onlyOwner {
require(_mysteryBox != address(0), "Z");
emit TaxAddressUpdated(myfiAddress, _mysteryBox);
mysteryBoxAddress = _mysteryBox;
}
function setOracleTax(uint256 _bps) external onlyOwner {
require(_bps <= 500, "M");
oracleTaxBps = _bps;
emit TaxConfigUpdated(oracleTaxBps, myfiTaxBps, mysteryBoxTaxBps);
}
function setMyfiTax(uint256 _bps) external onlyOwner {
require(_bps <= 500, "M");
myfiTaxBps = _bps;
emit TaxConfigUpdated(oracleTaxBps, myfiTaxBps, mysteryBoxTaxBps);
}
function setMysteryBoxTax(uint256 _bps) external onlyOwner {
require(_bps <= 500, "M");
mysteryBoxTaxBps = _bps;
emit TaxConfigUpdated(oracleTaxBps, myfiTaxBps, mysteryBoxTaxBps);
}
function forceEvolution() external onlyOwner {
lastSyncTime = 0;
_evolveIfNeeded();
}
function toggleIntelligence(bool _healthBased, bool _autoDiscovery, bool _autoRelease) external onlyOwner {
healthBasedAllocation = _healthBased;
autoDiscoveryEnabled = _autoDiscovery;
autoReleaseEnabled = _autoRelease;
}
function manualReleaseToken(address token, uint256 amount) external onlyOwner {
require(tokenInventories[token].balance >= amount, "B");
_executePartialRelease(token, amount, "MANUAL_OVERRIDE");
}
function emergencyReleaseAll() external onlyOwner {
for (uint i = 0; i < inventoryTokens.length; i++) {
address token = inventoryTokens[i];
uint256 balance = tokenInventories[token].balance;
if (balance > 0) {
_executePartialRelease(token, balance, "EMERGENCY_RELEASE");
emit EmergencyRelease(token, balance, "ADMIN_TRIGGERED");
}
}
}
function setTokenAutoRelease(address token, bool enabled) external onlyOwner {
tokenInventories[token].autoReleaseEnabled = enabled;
}
function emergencyWithdraw(address token) external onlyOwner {
if (token == address(0)) {
(bool ok,) = payable(owner()).call{value: address(this).balance}("");
require(ok, "X");
} else {
IERC20(token).safeTransfer(owner(), IERC20(token).balanceOf(address(this)));
}
}
// ============ VIEW FUNCTIONS ============
function getEcosystemStatus() external view returns (
address keyToken,
address streamingRewards,
address mefi,
address mysteryBox,
bool autoDiscovery,
bool healthAllocation,
uint256 discoveryAttempts_
) {
return (
keyTokenAddress, streamingRewardsAddress,
myfiAddress, mysteryBoxAddress,
autoDiscoveryEnabled, healthBasedAllocation, discoveryAttempts
);
}
function getTaxConfig() external view returns (
uint256 oracleBps,
uint256 mefiBps,
uint256 mysteryBoxBps,
uint256 totalTaxBps,
uint256 purchaseBps
) {
totalTaxBps = oracleTaxBps + myfiTaxBps + mysteryBoxTaxBps;
purchaseBps = 10000 - totalTaxBps;
return (oracleTaxBps, myfiTaxBps, mysteryBoxTaxBps, totalTaxBps, purchaseBps);
}
/**
* @notice Returns the current eDAI/MEFI purchase split and destinations.
* Frontend shows this on the tokenomics/about page.
*/
function getPurchaseSplit() external view returns (
uint256 edaiBps,
uint256 mefiBps_,
address factory_,
address jackpot_
) {
return (edaiSplitBps, myfiSplitBps, factoryAddress, jackpotAddress);
}
receive() external payable {}
}