Address
0xe4374ad7eeb1bd69cf8ea9bba71412e2a94c28f6Current Holdings
$0.00
TXs sent
not counted
First Active
2025-09-23
block 24,580,976
Last Active
36 days ago
block 27,246,930
Net worth historyi
213 snapshots · to block 27,475,412coverage change 26 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Augcoverage change 27 Aug
partial matchTreasurysolc 0.8.20+commit.a1b79de6runtime partial · creation partial
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {console} from "forge-std/console.sol";
import {IPoolV3} from "../Lending/interfaces/IPoolV3.sol";
// Interface for tokens with underlying
interface ITokenWithUnderlying {
function underlyingToken() external view returns (address);
}
// Staking contract interface
interface IStakingContract {
function notifyRewardAmount(address _rewardsToken, uint256 reward) external;
}
// Interface for WPLS unwrapping
interface IWPLS {
function withdraw(uint256 amount) external;
}
interface IDEXRouter {
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactTokensForETH(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function getAmountsOut(
uint amountIn,
address[] calldata path
) external view returns (uint[] memory amounts);
}
contract Treasury is Ownable {
using SafeERC20 for IERC20;
// Distribution addresses
address public dev1;
address public dev2;
address public buyBurnContract = 0xBd48026E337f1419EC97F780b2045eb0ef2E0467;
address public stakingContract;
address public reserveWallet = 0x0f1062EFd80a07e84D37CeA24046635e264a9D86;
// Distribution percentages (basis points, 10000 = 100%)
uint256 public profitSharePercentage = 6000; // 60% of distributable
uint256 public dev1Percentage = 2000; // 20% of distributable
uint256 public dev2Percentage = 500; // 5% of distributable
uint256 public buyBurnPercentage = 500; // 5% of distributable
uint256 public reservePercentage = 1000; // 10% - stays in contract for bad debt coverage
// Time lock for distributions
uint256 public distribution_duration;
uint256 public lastDistribution;
// Global switch for buy burn behavior
bool public sendToOwnerMode; // false = use buy burn, true = send to owner
// DEX Router for swaps (hardcoded PulseChain router)
IDEXRouter public constant dexRouter =
IDEXRouter(0x165C3410fC91EF562C50559f7d2289fEbed552d9);
IDEXRouter public constant backupRouter =
IDEXRouter(0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02);
address public constant PLS = 0xA1077a294dDE1B09bB078844df40758a5D0f9a27;
// Whitelisted tokens for staking rewards
mapping(address => bool) public whitelistedTokens;
address[] public whitelistedTokensList;
// Events
event FeesDistributed(address tokenDistributed, uint256 totalUnderlying);
event StakingContractUpdated(
address indexed oldContract,
address indexed newContract
);
event TokenWhitelisted(address indexed token);
event TokenRemovedFromWhitelist(address indexed token);
event FallbackToOwner(
address indexed token,
uint256 amount,
address indexed owner
);
constructor() Ownable(msg.sender) {}
/**
* @notice Initializes the Treasury contract
* @param _dev1 Address for dev1 distributions
* @param _dev2 Address for dev2 distributions
* @param _stakingContract Address of the staking contract
*/
function initialize(
address _dev1,
address _dev2,
address _stakingContract
) external onlyOwner {
dev1 = _dev1;
dev2 = _dev2;
stakingContract = _stakingContract;
}
receive() external payable {}
/**
* @notice Distributes fees for all whitelisted tokens that have balances
* @dev Processes all available pool tokens and updates last distribution timestamp
*/
function distributeAllFees() public {
require(
block.timestamp >= lastDistribution + distribution_duration,
"Distribution cooldown not met"
);
uint256 lngt = whitelistedTokensList.length;
for (uint256 i; i < lngt; i++) {
_distributeFees(IPoolV3(whitelistedTokensList[i]));
}
lastDistribution = block.timestamp;
}
/**
* @notice Internal function that handles the core distribution logic
* @dev Calculates and distributes fees based on percentages, reserves are what's left in contract
* @param poolToken The pool token to distribute fees from
*/
function _distributeFees(IPoolV3 poolToken) internal {
uint256 poolTokenBalance = IERC20(poolToken).balanceOf(address(this));
if (poolTokenBalance == 0) return;
// Calculate distribution amounts from total balance
uint256 reserveAmount = (poolTokenBalance * reservePercentage) / 10000;
uint256 profitAmount = (poolTokenBalance * profitSharePercentage) /
10000;
uint256 dev1Amount = (poolTokenBalance * dev1Percentage) / 10000;
uint256 dev2Amount = (poolTokenBalance * dev2Percentage) / 10000;
uint256 buyBurnAmount = (poolTokenBalance * buyBurnPercentage) / 10000;
uint256 underlyingReceived = (poolToken).redeem(
buyBurnAmount,
address(this),
address(this)
);
if (underlyingReceived == 0) return;
IStakingContract(stakingContract).notifyRewardAmount(
address(poolToken),
profitAmount
);
poolToken.transfer(dev1, dev1Amount);
poolToken.transfer(dev2, dev2Amount);
poolToken.transfer(reserveWallet, reserveAmount);
// Use global switch to determine buy burn behavior
if (sendToOwnerMode) {
IERC20(poolToken.underlyingToken()).transfer(
owner(),
underlyingReceived
);
} else {
_swapToPLSAndSend(poolToken.underlyingToken(), underlyingReceived);
}
emit FeesDistributed(poolToken.underlyingToken(), underlyingReceived);
}
/**
* @notice Swaps underlying tokens to native PLS and sends to buy burn contract
* @dev Handles WPLS unwrapping and DEX swaps with fallback mechanisms
* @param underlyingTokenAddress The underlying token to swap
* @param amount Amount of underlying to swap
*/
function _swapToPLSAndSend(
address underlyingTokenAddress,
uint256 amount
) internal {
if (underlyingTokenAddress == PLS) {
IWPLS(PLS).withdraw(amount);
buyBurnContract.call{value: amount}("");
} else {
address[] memory path = new address[](2);
path[0] = underlyingTokenAddress;
path[1] = PLS;
try
dexRouter.swapExactTokensForETH(
amount,
0,
path,
address(buyBurnContract),
block.timestamp + 300
)
{
return;
} catch {
try
backupRouter.swapExactTokensForETH(
amount,
0,
path,
address(buyBurnContract),
block.timestamp + 300
)
{
// Backup swap succeeded
return;
} catch {
// Both routers failed, send tokens to owner
IERC20(underlyingTokenAddress).transfer(owner(), amount);
emit FallbackToOwner(
underlyingTokenAddress,
amount,
owner()
);
}
}
}
}
/**
* @notice Updates dev1 address
* @param _dev1 New dev1 address
*/
function setDev1(address _dev1) external onlyOwner {
require(_dev1 != address(0), "Invalid dev1 address");
dev1 = _dev1;
}
/**
* @notice Updates dev2 address
* @param _dev2 New dev2 address
*/
function setDev2(address _dev2) external onlyOwner {
require(_dev2 != address(0), "Invalid dev2 address");
dev2 = _dev2;
}
/**
* @notice Updates profit share percentage
* @param _profitSharePercentage New profit share percentage (basis points)
*/
function setProfitSharePercentage(
uint256 _profitSharePercentage
) external onlyOwner {
profitSharePercentage = _profitSharePercentage;
}
/**
* @notice Updates dev1 percentage
* @param _dev1Percentage New dev1 percentage (basis points)
*/
function setDev1Percentage(uint256 _dev1Percentage) external onlyOwner {
dev1Percentage = _dev1Percentage;
}
/**
* @notice Updates dev2 percentage
* @param _dev2Percentage New dev2 percentage (basis points)
*/
function setDev2Percentage(uint256 _dev2Percentage) external onlyOwner {
dev2Percentage = _dev2Percentage;
}
/**
* @notice Updates buy burn percentage
* @param _buyBurnPercentage New buy burn percentage (basis points)
*/
function setBuyBurnPercentage(
uint256 _buyBurnPercentage
) external onlyOwner {
buyBurnPercentage = _buyBurnPercentage;
}
/**
* @notice Updates reserve percentage
* @param _reservePercentage New reserve percentage (basis points)
*/
function setReservePercentage(
uint256 _reservePercentage
) external onlyOwner {
reservePercentage = _reservePercentage;
}
/**
* @notice Updates reserve wallet address
* @param _reserveWallet New reserve wallet address
*/
function setReserveWallet(address _reserveWallet) external onlyOwner {
require(_reserveWallet != address(0), "Invalid reserve wallet address");
reserveWallet = _reserveWallet;
}
/**
* @notice Updates buy burn contract address
* @param _buyBurnContract New buy burn contract address (can be zero to disable)
*/
function setBuyBurnContract(address _buyBurnContract) external onlyOwner {
buyBurnContract = _buyBurnContract;
}
/**
* @notice Sets the staking contract address
* @param _stakingContract Address of the staking contract
*/
function setStakingContract(address _stakingContract) external onlyOwner {
require(
_stakingContract != address(0),
"Invalid staking contract address"
);
address oldContract = stakingContract;
stakingContract = _stakingContract;
uint256 length = whitelistedTokensList.length;
for (uint256 i; i < length; i++) {
IERC20(whitelistedTokensList[i]).approve(
address(_stakingContract),
type(uint256).max
);
address underlying = ITokenWithUnderlying(whitelistedTokensList[i])
.underlyingToken();
require(underlying != address(0), "Invalid token address");
IERC20(underlying).approve(address(dexRouter), type(uint256).max);
IERC20(underlying).approve(
address(backupRouter),
type(uint256).max
);
}
emit StakingContractUpdated(oldContract, _stakingContract);
}
/**
* @notice Sets the distribution duration (cooldown period between distributions)
* @param _distributionDuration New distribution duration in seconds
*/
function setDistributionDuration(
uint256 _distributionDuration
) external onlyOwner {
require(_distributionDuration != 0, "Invalid distribution duration");
distribution_duration = _distributionDuration;
}
/**
* @notice Toggle between sending to buy burn contract or directly to owner
* @param _sendToOwnerMode True to send to owner, false to use buy burn mechanism
*/
function setSendToOwnerMode(bool _sendToOwnerMode) external onlyOwner {
sendToOwnerMode = _sendToOwnerMode;
}
function sadaasasasasasd() public view returns (uint256 bb) {
bb = 233231;
}
uint256 lelf = 1312;
/**
* @notice Adds a token to the whitelist for staking distributions
* @param token Token address to whitelist
*/
function addWhitelistedToken(address token) public onlyOwner {
require(!whitelistedTokens[token], "Token already whitelisted");
address underlying = ITokenWithUnderlying(token).underlyingToken();
require(underlying != address(0), "Invalid token address");
// Approve both routers
IERC20(underlying).approve(address(dexRouter), type(uint256).max);
IERC20(underlying).approve(address(backupRouter), type(uint256).max);
IERC20(token).approve(address(stakingContract), type(uint256).max);
whitelistedTokens[token] = true;
whitelistedTokensList.push(token);
emit TokenWhitelisted(token);
}
/**
* @notice Removes a token from the whitelist
* @param token Token address to remove
*/
function removeWhitelistedToken(address token) external onlyOwner {
whitelistedTokens[token] = false;
// Remove from array
for (uint256 i = 0; i < whitelistedTokensList.length; i++) {
if (whitelistedTokensList[i] == token) {
whitelistedTokensList[i] = whitelistedTokensList[
whitelistedTokensList.length - 1
];
whitelistedTokensList.pop();
break;
}
}
emit TokenRemovedFromWhitelist(token);
}
/**
* @notice Adds multiple tokens to the whitelist
* @param tokens Array of token addresses to whitelist
*/
function addMultipleWhitelistedTokens(
address[] calldata tokens
) external onlyOwner {
for (uint256 i = 0; i < tokens.length; i++) {
if (tokens[i] != address(0) && !whitelistedTokens[tokens[i]]) {
addWhitelistedToken(tokens[i]);
}
}
}
/**
* @notice Allows owner to withdraw any token from the contract
* @param token Token address to withdraw
* @param amount Amount to withdraw
*/
function withdrawaToken(address token, uint256 amount) external onlyOwner {
IERC20(token).safeTransfer(owner(), amount);
}
/**
* @notice Allows owner to withdraw native PLS from the contract
* @param amount Amount of native PLS to withdraw
*/
function withdrawaNative(uint256 amount) external onlyOwner {
(bool success, ) = owner().call{value: amount}("");
require(success, "Native PLS transfer failed");
}
/**
* @notice View function to check when next distribution is available
* @return Time until next distribution is available (0 if available now)
*/
function timeUntilNextDistribution() external view returns (uint256) {
uint256 nextDistribution = lastDistribution + distribution_duration;
if (block.timestamp >= nextDistribution) {
return 0;
}
return nextDistribution - block.timestamp;
}
/**
* @notice View function to preview distribution amounts
* @param poolToken The pool token to preview distribution for
* @return profitAmount Amount that would go to profit share
* @return dev1Amount Amount that would go to dev1
* @return dev2Amount Amount that would go to dev2
* @return buyBurnAmount Amount that would be used for buy burn
* @return reserveAmount Amount that would remain in contract as reserves
*/
function previewDistribution(
address poolToken
)
external
view
returns (
uint256 profitAmount,
uint256 dev1Amount,
uint256 dev2Amount,
uint256 buyBurnAmount,
uint256 reserveAmount
)
{
uint256 poolTokenBalance = IERC20(poolToken).balanceOf(address(this));
if (poolTokenBalance == 0) {
return (0, 0, 0, 0, 0);
}
// Use hardcoded percentages matching _distributeFees function
reserveAmount = (poolTokenBalance * reservePercentage) / 10000;
profitAmount = (poolTokenBalance * profitSharePercentage) / 10000;
dev1Amount = (poolTokenBalance * dev1Percentage) / 10000;
dev2Amount = (poolTokenBalance * dev2Percentage) / 10000;
buyBurnAmount = (poolTokenBalance * buyBurnPercentage) / 10000;
}
/**
* @notice Gets all whitelisted tokens
* @return Array of whitelisted token addresses
*/
function getWhitelistedTokens() external view returns (address[] memory) {
return whitelistedTokensList;
}
/**
* @notice Gets the count of whitelisted tokens
* @return Number of whitelisted tokens
*/
function getWhitelistedTokensCount() external view returns (uint256) {
return whitelistedTokensList.length;
}
/**
* @notice Checks if a token is whitelisted
* @param token Token address to check
* @return True if token is whitelisted
*/
function isWhitelisted(address token) external view returns (bool) {
return whitelistedTokens[token];
}
}