ghost-dao-contracts/src/TreasuryExtender.sol
Uncle Fatso 8ac8b67767
get rid of annoying foundry warnings
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2026-03-09 00:37:41 +03:00

250 lines
8.1 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
import {ITreasury} from "./interfaces/ITreasury.sol";
import {IAllocator} from "./interfaces/IAllocator.sol";
import {ITreasuryExtender} from "./interfaces/ITreasuryExtender.sol";
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
contract TreasuryExtender is GhostAccessControlled, ITreasuryExtender {
using SafeERC20 for IERC20;
ITreasury public immutable treasury; // forge-lint: disable-line(screaming-snake-case-immutable)
IAllocator[] public allocators;
mapping(IAllocator => mapping(uint256 => AllocatorData)) public allocatorData;
constructor(address treasuryAddress, address authorityAddress)
GhostAccessControlled(IGhostAuthority(authorityAddress))
{
treasury = ITreasury(treasuryAddress);
allocators.push(IAllocator(address(0)));
}
function _allocatorActivated(IAllocator.AllocatorStatus status) internal pure {
if (IAllocator.AllocatorStatus.ACTIVATED != status) revert AllocatorNotActivated();
}
function _allocatorOffline(IAllocator.AllocatorStatus status) internal pure {
if (IAllocator.AllocatorStatus.OFFLINE != status) revert AllocatorNotOffline();
}
function _onlyAllocator(
IAllocator byStateId,
address sender,
uint256 id
) internal pure {
if (IAllocator(sender) != byStateId) revert OnlyAllocator(id, sender);
}
function registerDeposit(address newAllocator) external override onlyGuardian {
IAllocator allocator = IAllocator(newAllocator);
uint256 id = allocators.length;
allocators.push(allocator);
allocator.addId(id);
emit NewDepositRegistered(
newAllocator,
address(allocator.tokens()[allocator.tokenIds(id)]),
id
);
}
function setAllocatorLimits(
uint256 id,
AllocatorLimits calldata limits
) external override onlyGuardian {
IAllocator allocator = allocators[id];
_allocatorOffline(allocator.status());
allocatorData[allocator][id].limits = limits;
emit AllocatorLimitsChanged(id, limits.allocated, limits.loss);
}
function report(
uint256 id,
uint128 gain,
uint128 loss
) external override {
IAllocator allocator = allocators[id];
AllocatorData storage data = allocatorData[allocator][id];
AllocatorPerformance memory perf = data.performance;
IAllocator.AllocatorStatus status = allocator.status();
_onlyAllocator(allocator, msg.sender, id);
if (status == IAllocator.AllocatorStatus.OFFLINE) revert AllocatorOffline();
if (gain >= loss) {
if (loss == type(uint128).max) {
AllocatorData storage newAllocatorData =
allocatorData[allocators[allocators.length - 1]][id];
newAllocatorData.holdings.allocated = data.holdings.allocated;
newAllocatorData.performance.gain = data.performance.gain;
data.holdings.allocated = 0;
perf.gain = 0;
perf.loss = 0;
emit AllocatorReportedMigration(id);
} else {
perf.gain += gain;
emit AllocatorReportedGain(id, gain);
}
} else {
data.holdings.allocated -= loss;
perf.loss += loss;
emit AllocatorReportedLoss(id, loss);
}
data.performance = perf;
}
function requestFundsFromTreasury(
uint256 id,
uint256 amount
) external override onlyGuardian {
IAllocator allocator = allocators[id];
AllocatorData memory data = allocatorData[allocator][id];
address token = address(allocator.tokens()[allocator.tokenIds(id)]);
uint256 value = treasury.tokenValue(token, amount);
_allocatorActivated(allocator.status());
_allocatorBelowLimit(data, amount);
treasury.manage(token, amount);
allocatorData[allocator][id].holdings.allocated += amount;
IERC20(token).safeTransfer(address(allocator), amount);
emit AllocatorFunded(id, amount, value);
}
function returnFundsToTreasury(
uint256 id,
uint256 amount
) external override onlyGuardian {
IAllocator allocator = allocators[id];
uint256 allocated = allocatorData[allocator][id].holdings.allocated;
uint128 gain = allocatorData[allocator][id].performance.gain;
address token = address(allocator.tokens()[allocator.tokenIds(id)]);
if (amount > allocated) {
amount -= allocated;
if (amount > gain) {
amount = allocated + gain;
gain = 0;
} else {
// forge-lint: disable-next-line(unsafe-typecast)
gain -= uint128(amount);
amount += allocated;
}
allocated = 0;
} else {
allocated -= amount;
}
uint256 value = treasury.tokenValue(token, amount);
_allowTreasuryWithdrawal(IERC20(token));
IERC20(token).safeTransferFrom(address(allocator), address(this), amount);
allocatorData[allocator][id].holdings.allocated = allocated;
if (allocated == 0) allocatorData[allocator][id].performance.gain = gain;
assert(treasury.deposit(token, amount, value) == 0);
emit AllocatedWithdrawal(id, amount, value);
}
function returnRewardsToTreasury(
uint256 id,
address token,
uint256 amount
) external override {
_returnRewardsToTreasury(allocators[id], IERC20(token), amount);
}
function returnRewardsToTreasury(
address allocatorAddress,
address token,
uint256 amount
) external override {
_returnRewardsToTreasury(IAllocator(allocatorAddress), IERC20(token), amount);
}
function getAllocatorById(uint256 id)
external
view
override
returns (address)
{
return address(allocators[id]);
}
function getTotalAllocatorCount()
external
view
returns (uint256)
{
return allocators.length;
}
function getAllocatorLimits(uint256 id)
external
view
override
returns (AllocatorLimits memory)
{
return allocatorData[allocators[id]][id].limits;
}
function getAllocatorPerformance(uint256 id)
external
view
override
returns (AllocatorPerformance memory)
{
return allocatorData[allocators[id]][id].performance;
}
function getAllocatorAllocated(uint256 id)
external
view
override
returns (uint256)
{
return allocatorData[allocators[id]][id].holdings.allocated;
}
function _returnRewardsToTreasury(
IAllocator allocator,
IERC20 token,
uint256 amount
) internal onlyGuardian {
uint256 balance = token.balanceOf(address(allocator));
amount = balance < amount ? balance : amount;
uint256 value = treasury.tokenValue(address(token), amount);
_allowTreasuryWithdrawal(token);
token.safeTransferFrom(address(allocator), address(this), amount);
assert(treasury.deposit(address(token), amount, value) == 0);
emit AllocatorRewardsWithdrawal(address(allocator), amount, value);
}
function _allowTreasuryWithdrawal(IERC20 token) internal {
if (token.allowance(address(this), address(treasury)) == 0) {
token.approve(address(treasury), type(uint256).max);
}
}
function _allocatorBelowLimit(
AllocatorData memory data,
uint256 amount
) internal pure {
uint256 newAllocated = data.holdings.allocated + amount;
if (newAllocated > data.limits.allocated) {
revert AllocatorMaxAllocation(newAllocated, data.limits.allocated);
}
}
}