took out UBI and cleaned up
This commit is contained in:
parent
5db3ecb3d7
commit
092f88a668
9 changed files with 88 additions and 598 deletions
|
|
@ -1,7 +1,6 @@
|
|||
pragma solidity ^0.8.19;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import {TwabController} from "pt-v5-twab-controller/TwabController.sol";
|
||||
import "@uniswap-v3-core/interfaces/IUniswapV3Factory.sol";
|
||||
import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";
|
||||
import "../src/Harberg.sol";
|
||||
|
|
@ -28,24 +27,16 @@ contract DeployScript is Script {
|
|||
vm.startBroadcast(privateKey);
|
||||
//address sender = vm.addr(privateKey);
|
||||
|
||||
TwabController tc;
|
||||
if (twabc == address(0)) {
|
||||
tc = TwabController(twabc);
|
||||
} else {
|
||||
// in case you want to deploy an new TwabController
|
||||
tc = new TwabController(60 * 60, uint32(block.timestamp));
|
||||
}
|
||||
Harberg harb = new Harberg("Harbergerger Tax", "HARB", tc);
|
||||
Harberg harb = new Harberg("Harbergerger Tax", "HARB");
|
||||
token0isWeth = address(weth) < address(harb);
|
||||
Stake stake = new Stake(address(harb));
|
||||
Stake stake = new Stake(address(harb), feeDest);
|
||||
harb.setStakingPool(address(stake));
|
||||
IUniswapV3Factory factory = IUniswapV3Factory(v3Factory);
|
||||
address liquidityPool = factory.createPool(weth, address(harb), FEE);
|
||||
IUniswapV3Pool(liquidityPool).initializePoolFor1Cent(token0isWeth);
|
||||
harb.setLiquidityPool(liquidityPool);
|
||||
Sentimenter sentimenter = new Sentimenter();
|
||||
bytes memory params = abi.encodeWithSignature("initialize(address,address)", address(harb),address(stake));
|
||||
ERC1967Proxy proxy = new ERC1967Proxy(address(sentimenter), params);
|
||||
Sentimenter sentimenter = new Sentimenter();
|
||||
bytes memory params = abi.encodeWithSignature("initialize(address,address)", address(harb),address(stake));
|
||||
ERC1967Proxy proxy = new ERC1967Proxy(address(sentimenter), params);
|
||||
LiquidityManager liquidityManager = new LiquidityManager(v3Factory, weth, address(harb), address(proxy));
|
||||
liquidityManager.setFeeDestination(feeDest);
|
||||
// note: this delayed initialization is not a security issue.
|
||||
|
|
|
|||
|
|
@ -3,59 +3,25 @@ pragma solidity ^0.8.19;
|
|||
|
||||
import {ERC20} from "@openzeppelin/token/ERC20/ERC20.sol";
|
||||
import {ERC20Permit} from "@openzeppelin/token/ERC20/extensions/ERC20Permit.sol";
|
||||
import {SafeCast} from "@openzeppelin/utils/math/SafeCast.sol";
|
||||
import {Math} from "@openzeppelin/utils/math/Math.sol";
|
||||
import {TwabController} from "pt-v5-twab-controller/TwabController.sol";
|
||||
|
||||
/**
|
||||
* @title Harberg ERC20 Token
|
||||
* @notice This contract implements an ERC20 token with mechanisms for minting, burning, and transferring tokens,
|
||||
* integrated with a TWAB controller for time-weighted balance tracking. This token supports a novel economic model
|
||||
* that finances Universal Basic Income (UBI) through a Harberger tax applied to staked tokens. The liquidity manager
|
||||
* manages token supply to stabilize market liquidity.
|
||||
* @title stakeable ERC20 Token
|
||||
* @notice This contract implements an ERC20 token with mechanisms for minting and burning in which a single account (staking Pool) is proportionally receiving a share. Only the liquidity manager has permission to manage token supply.
|
||||
*/
|
||||
contract Harberg is ERC20, ERC20Permit {
|
||||
using Math for uint256;
|
||||
// Total tax collected so far
|
||||
uint256 public sumTaxCollected;
|
||||
// Constant for UNI V3 1% fee tier pools
|
||||
uint24 private constant FEE = uint24(10_000);
|
||||
// Minimum fraction of the total supply required for staking to prevent fragmentation of staking positions
|
||||
uint256 private constant MIN_STAKE_FRACTION = 3000;
|
||||
// Maximum fraction of the total supply allowed for staking to manage gas costs
|
||||
uint256 private constant MAX_STAKE_FRACTION = 100000;
|
||||
// Period offset for TWAB calculations
|
||||
uint256 private immutable PERIOD_OFFSET;
|
||||
// Minimum period length for TWAB observations
|
||||
uint256 private immutable PERIOD_LENGTH;
|
||||
|
||||
// Immutable reference to the TWAB controller
|
||||
TwabController private immutable twabController;
|
||||
// Address of the liquidity manager
|
||||
address private liquidityManager;
|
||||
// Address of the staking pool
|
||||
address private stakingPool;
|
||||
// Address of the liquidity pool
|
||||
address private liquidityPool;
|
||||
|
||||
// Address of the tax pool
|
||||
address public constant TAX_POOL = address(2);
|
||||
// Previous total supply for staking calculations
|
||||
uint256 public previousTotalSupply;
|
||||
// Minimum fraction of the total supply required for staking
|
||||
uint256 public minStakeSupplyFraction;
|
||||
|
||||
// Structure to hold UBI title information for each account
|
||||
struct UbiTitle {
|
||||
uint256 sumTaxCollected;
|
||||
uint256 time;
|
||||
}
|
||||
|
||||
// Mapping to store UBI titles for each account
|
||||
mapping(address => UbiTitle) public ubiTitles;
|
||||
|
||||
// Custom errors
|
||||
error ZeroAddressInConstructor();
|
||||
error ZeroAddressInSetter();
|
||||
error AddressAlreadySet();
|
||||
|
||||
|
|
@ -69,30 +35,10 @@ contract Harberg is ERC20, ERC20Permit {
|
|||
* @notice Constructor for the Harberg token
|
||||
* @param name_ The name of the token
|
||||
* @param symbol_ The symbol of the token
|
||||
* @param twabController_ The TWAB controller contract
|
||||
*/
|
||||
constructor(string memory name_, string memory symbol_, TwabController twabController_)
|
||||
constructor(string memory name_, string memory symbol_)
|
||||
ERC20(name_, symbol_)
|
||||
ERC20Permit(name_)
|
||||
{
|
||||
if (address(0) == address(twabController_)) revert ZeroAddressInConstructor();
|
||||
twabController = twabController_;
|
||||
PERIOD_OFFSET = twabController.PERIOD_OFFSET();
|
||||
PERIOD_LENGTH = twabController.PERIOD_LENGTH();
|
||||
minStakeSupplyFraction = MIN_STAKE_FRACTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Sets the address for the liquidityPool. Used once post-deployment to initialize the contract.
|
||||
* @dev Should be called only once right after the contract deployment to set the liquidity pool address.
|
||||
* Throws AddressAlreadySet if called more than once.
|
||||
* @param liquidityPool_ The address of the liquidity pool.
|
||||
*/
|
||||
function setLiquidityPool(address liquidityPool_) external {
|
||||
if (address(0) == liquidityPool_) revert ZeroAddressInSetter();
|
||||
if (liquidityPool != address(0)) revert AddressAlreadySet();
|
||||
liquidityPool = liquidityPool_;
|
||||
}
|
||||
ERC20Permit(name_){}
|
||||
|
||||
/**
|
||||
* @notice Sets the address for the liquidityManager. Used once post-deployment to initialize the contract.
|
||||
|
|
@ -122,8 +68,8 @@ contract Harberg is ERC20, ERC20Permit {
|
|||
* @notice Returns the addresses of the periphery contracts
|
||||
* @return The addresses of the TWAB controller, liquidity manager, staking pool, and liquidity pool
|
||||
*/
|
||||
function peripheryContracts() external view returns (address, address, address, address) {
|
||||
return (address(twabController), liquidityManager, stakingPool, liquidityPool);
|
||||
function peripheryContracts() external view returns (address, address) {
|
||||
return (liquidityManager, stakingPool);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -131,7 +77,7 @@ contract Harberg is ERC20, ERC20Permit {
|
|||
* @return The minimum stake amount
|
||||
*/
|
||||
function minStake() external view returns (uint256) {
|
||||
return previousTotalSupply / minStakeSupplyFraction;
|
||||
return previousTotalSupply / MIN_STAKE_FRACTION;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -141,9 +87,17 @@ contract Harberg is ERC20, ERC20Permit {
|
|||
* @param _amount The number of tokens to mint.
|
||||
*/
|
||||
function mint(uint256 _amount) external onlyLiquidityManager {
|
||||
_mint(address(liquidityManager), _amount);
|
||||
if (_amount > 0) {
|
||||
// make sure staking pool grows proportional to economy
|
||||
uint256 stakingPoolBalance = balanceOf(stakingPool);
|
||||
if (stakingPoolBalance > 0) {
|
||||
uint256 newStake = stakingPoolBalance * _amount / (totalSupply() - stakingPoolBalance);
|
||||
_mint(stakingPool, newStake);
|
||||
}
|
||||
_mint(address(liquidityManager), _amount);
|
||||
}
|
||||
if (previousTotalSupply == 0) {
|
||||
previousTotalSupply = twabController.totalSupply(address(this));
|
||||
previousTotalSupply = totalSupply();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -154,7 +108,15 @@ contract Harberg is ERC20, ERC20Permit {
|
|||
* @param _amount The number of tokens to burn.
|
||||
*/
|
||||
function burn(uint256 _amount) external onlyLiquidityManager {
|
||||
_burn(address(liquidityManager), _amount);
|
||||
if (_amount > 0) {
|
||||
// shrink staking pool proportional to economy
|
||||
uint256 stakingPoolBalance = balanceOf(stakingPool);
|
||||
if (stakingPoolBalance > 0) {
|
||||
uint256 excessStake = stakingPoolBalance * _amount / (totalSupply() - stakingPoolBalance);
|
||||
_burn(stakingPool, excessStake);
|
||||
}
|
||||
_burn(address(liquidityManager), _amount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -165,32 +127,6 @@ contract Harberg is ERC20, ERC20Permit {
|
|||
previousTotalSupply = _ts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Sets the minimum stake supply fraction
|
||||
* @param _mssf The minimum stake supply fraction value
|
||||
*/
|
||||
function setMinStakeSupplyFraction(uint256 _mssf) external onlyLiquidityManager {
|
||||
require(_mssf >= MIN_STAKE_FRACTION, "minStakeSupplyFraction below allowed min");
|
||||
require(_mssf <= MAX_STAKE_FRACTION, "minStakeSupplyFraction above allow max");
|
||||
minStakeSupplyFraction = _mssf;
|
||||
}
|
||||
|
||||
/* ============ Public ERC20 Overrides ============ */
|
||||
|
||||
/**
|
||||
* @inheritdoc ERC20
|
||||
*/
|
||||
function balanceOf(address _account) public view override(ERC20) returns (uint256) {
|
||||
return twabController.balanceOf(address(this), _account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc ERC20
|
||||
*/
|
||||
function totalSupply() public view override(ERC20) returns (uint256) {
|
||||
return twabController.totalSupply(address(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Returns the outstanding supply, excluding the balances of the liquidity pool and liquidity manager
|
||||
* @return The outstanding supply
|
||||
|
|
@ -199,144 +135,4 @@ contract Harberg is ERC20, ERC20Permit {
|
|||
return totalSupply() - balanceOf(liquidityManager);
|
||||
}
|
||||
|
||||
/* ============ Internal ERC20 Overrides ============ */
|
||||
|
||||
/**
|
||||
* @notice Mints tokens to `_receiver` and increases the total supply.
|
||||
* @dev Emits a {Transfer} event with `from` set to the zero address.
|
||||
* @dev `receiver` cannot be the zero address.
|
||||
* @param receiver Address that will receive the minted tokens
|
||||
* @param amount Tokens to mint
|
||||
*/
|
||||
function _mint(address receiver, uint256 amount) internal override {
|
||||
if (amount > 0) {
|
||||
// make sure staking pool grows proportional to economy
|
||||
uint256 stakingPoolBalance = balanceOf(stakingPool);
|
||||
if (stakingPoolBalance > 0) {
|
||||
uint256 newStake = stakingPoolBalance * amount / (totalSupply() - stakingPoolBalance);
|
||||
twabController.mint(stakingPool, SafeCast.toUint96(newStake));
|
||||
emit Transfer(address(0), stakingPool, newStake);
|
||||
}
|
||||
twabController.mint(receiver, SafeCast.toUint96(amount));
|
||||
emit Transfer(address(0), receiver, amount);
|
||||
if (ubiTitles[receiver].time == 0 && amount > 0) {
|
||||
// new account, start UBI title
|
||||
ubiTitles[receiver].sumTaxCollected = sumTaxCollected;
|
||||
ubiTitles[receiver].time = block.timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Burns tokens from `_owner` and decreases the total supply.
|
||||
* @dev Emits a {Transfer} event with `to` set to the zero address.
|
||||
* @dev `_owner` must have at least `_amount` tokens.
|
||||
* @param owner Address that will have tokens burnt
|
||||
* @param amount Tokens to burn
|
||||
*/
|
||||
function _burn(address owner, uint256 amount) internal override {
|
||||
if (amount > 0) {
|
||||
// shrink staking pool proportional to economy
|
||||
uint256 stakingPoolBalance = balanceOf(stakingPool);
|
||||
if (stakingPoolBalance > 0) {
|
||||
uint256 excessStake = stakingPoolBalance * amount / (totalSupply() - stakingPoolBalance);
|
||||
twabController.burn(stakingPool, SafeCast.toUint96(excessStake));
|
||||
emit Transfer(stakingPool, address(0), excessStake);
|
||||
}
|
||||
twabController.burn(owner, SafeCast.toUint96(amount));
|
||||
emit Transfer(owner, address(0), amount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Transfers tokens from one account to another.
|
||||
* @dev Emits a {Transfer} event.
|
||||
* @dev `_from` cannot be the zero address.
|
||||
* @dev `_to` cannot be the zero address.
|
||||
* @dev `_from` must have a balance of at least `_amount`.
|
||||
* @param _from Address to transfer from
|
||||
* @param _to Address to transfer to
|
||||
* @param _amount The amount of tokens to transfer
|
||||
*/
|
||||
function _transfer(address _from, address _to, uint256 _amount) internal override {
|
||||
if (_to == TAX_POOL) {
|
||||
unchecked {
|
||||
sumTaxCollected += _amount;
|
||||
}
|
||||
} else if (ubiTitles[_to].time == 0 && _amount > 0) {
|
||||
// new account, start UBI title
|
||||
ubiTitles[_to].sumTaxCollected = sumTaxCollected;
|
||||
ubiTitles[_to].time = block.timestamp;
|
||||
}
|
||||
twabController.transfer(_from, _to, SafeCast.toUint96(_amount));
|
||||
emit Transfer(_from, _to, _amount);
|
||||
}
|
||||
|
||||
/* ============ UBI stuff ============ */
|
||||
|
||||
/**
|
||||
* @notice Calculates the UBI due to an account based on time-weighted average balances.
|
||||
* @dev Uses historic TWAB data to determine an account's proportionate share of collected taxes since last claim.
|
||||
* @param _account The account whose UBI is being calculated.
|
||||
* @param lastTaxClaimed The timestamp of the last UBI claim.
|
||||
* @param _sumTaxCollected The tax collected up to the last claim.
|
||||
* @return amountDue The amount of UBI due to the account.
|
||||
* @return lastPeriodEndAt The timestamp marking the end of the last period considered for UBI calculation.
|
||||
*/
|
||||
function ubiDue(address _account, uint256 lastTaxClaimed, uint256 _sumTaxCollected) internal view returns (uint256 amountDue, uint256 lastPeriodEndAt) {
|
||||
lastPeriodEndAt = ((block.timestamp - PERIOD_OFFSET) / uint256(PERIOD_LENGTH)) * PERIOD_LENGTH + PERIOD_OFFSET - 1;
|
||||
if (lastTaxClaimed == 0 || lastTaxClaimed > lastPeriodEndAt || lastPeriodEndAt - lastTaxClaimed < PERIOD_LENGTH) {
|
||||
return (0, lastPeriodEndAt);
|
||||
}
|
||||
uint256 accountTwab = twabController.getTwabBetween(address(this), _account, lastTaxClaimed, lastPeriodEndAt);
|
||||
uint256 stakeTwab = twabController.getTwabBetween(address(this), stakingPool, lastTaxClaimed, lastPeriodEndAt);
|
||||
uint256 poolTwab = twabController.getTwabBetween(address(this), liquidityPool, lastTaxClaimed, lastPeriodEndAt);
|
||||
uint256 taxTwab = twabController.getTwabBetween(address(this), TAX_POOL, lastTaxClaimed, lastPeriodEndAt);
|
||||
uint256 totalSupplyTwab = twabController.getTotalSupplyTwabBetween(address(this), lastTaxClaimed, lastPeriodEndAt);
|
||||
|
||||
//uint256 taxCollectedSinceLastClaim = sumTaxCollected - _sumTaxCollected;
|
||||
uint256 taxCollectedSinceLastClaim;
|
||||
if (sumTaxCollected >= _sumTaxCollected) {
|
||||
taxCollectedSinceLastClaim = sumTaxCollected - _sumTaxCollected;
|
||||
} else {
|
||||
// Handle the wrap-around case
|
||||
taxCollectedSinceLastClaim = type(uint256).max - _sumTaxCollected + sumTaxCollected + 1;
|
||||
}
|
||||
amountDue = taxCollectedSinceLastClaim.mulDiv(accountTwab, (totalSupplyTwab - stakeTwab - poolTwab - taxTwab), Math.Rounding.Down);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates the UBI due to an account based on time-weighted average balances.
|
||||
* @dev Uses historic TWAB data to determine an account's proportionate share of collected taxes since last claim.
|
||||
* @param _account The account whose UBI is being calculated.
|
||||
* @return amountDue The amount of UBI due to the account.
|
||||
* @return lastPeriodEndAt The timestamp marking the end of the last period considered for UBI calculation.
|
||||
*/
|
||||
function getUbiDue(address _account) public view returns (uint256 amountDue, uint256 lastPeriodEndAt) {
|
||||
UbiTitle storage lastUbiTitle = ubiTitles[_account];
|
||||
return ubiDue(_account, lastUbiTitle.time, lastUbiTitle.sumTaxCollected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Claims the calculated UBI amount for the caller.
|
||||
* @dev Transfers the due UBI from the tax pool to the account, updating the UBI title.
|
||||
* Emits Transfer event on successful transfer.
|
||||
* @param _account The account claiming the UBI.
|
||||
* @return ubiAmountDue The amount of UBI claimed.
|
||||
*/
|
||||
function claimUbi(address _account) external returns (uint256 ubiAmountDue) {
|
||||
UbiTitle storage lastUbiTitle = ubiTitles[_account];
|
||||
uint256 lastPeriodEndAt;
|
||||
(ubiAmountDue, lastPeriodEndAt) = ubiDue(_account, lastUbiTitle.time, lastUbiTitle.sumTaxCollected);
|
||||
|
||||
if (ubiAmountDue > 0) {
|
||||
|
||||
ubiTitles[_account].sumTaxCollected = sumTaxCollected;
|
||||
ubiTitles[_account].time = lastPeriodEndAt;
|
||||
twabController.transfer(TAX_POOL, _account, SafeCast.toUint96(ubiAmountDue));
|
||||
emit Transfer(TAX_POOL, _account, ubiAmountDue);
|
||||
} else {
|
||||
revert("No UBI to claim.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ contract LiquidityManager {
|
|||
pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey));
|
||||
harb = Harberg(_harb);
|
||||
token0isWeth = _WETH9 < _harb;
|
||||
sentimenter = Sentimenter(_sentimenter);
|
||||
sentimenter = Sentimenter(_sentimenter);
|
||||
}
|
||||
|
||||
/// @notice Callback function that Uniswap V3 calls for liquidity actions requiring minting or burning of tokens.
|
||||
|
|
@ -125,10 +125,6 @@ contract LiquidityManager {
|
|||
feeDestination = feeDestination_;
|
||||
}
|
||||
|
||||
function setMinStakeSupplyFraction(uint256 mssf_) external onlyFeeDestination {
|
||||
harb.setMinStakeSupplyFraction(mssf_);
|
||||
}
|
||||
|
||||
function setRecenterAccess(address addr) external onlyFeeDestination {
|
||||
recenterAccess = addr;
|
||||
}
|
||||
|
|
@ -433,13 +429,15 @@ contract LiquidityManager {
|
|||
/// @notice Adjusts liquidity positions in response to an increase or decrease in the Harberg token's price.
|
||||
/// @dev This function should be called when significant price movement is detected. It recalibrates the liquidity ranges to align with the new market conditions.
|
||||
function recenter() external returns (bool isUp, uint256 sentiment) {
|
||||
if (recenterAccess != address(0)) {
|
||||
require(msg.sender == recenterAccess, "access denied");
|
||||
}
|
||||
// Fetch the current tick from the Uniswap V3 pool
|
||||
(, int24 currentTick, , , , , ) = pool.slot0();
|
||||
// check slippage with oracle
|
||||
require(_isPriceStable(currentTick), "price deviated from oracle");
|
||||
|
||||
if (recenterAccess != address(0)) {
|
||||
require(msg.sender == recenterAccess, "access denied");
|
||||
} else {
|
||||
// check slippage with oracle
|
||||
require(_isPriceStable(currentTick), "price deviated from oracle");
|
||||
}
|
||||
|
||||
isUp = false;
|
||||
// check how price moved
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ contract Stake {
|
|||
}
|
||||
|
||||
Harberg private immutable harberg;
|
||||
address private immutable taxPool;
|
||||
address private immutable taxReceiver;
|
||||
|
||||
uint256 public immutable totalSupply;
|
||||
uint256 public outstandingStake;
|
||||
|
|
@ -78,10 +78,10 @@ contract Stake {
|
|||
/// @notice Initializes the stake contract with references to the Harberg contract and sets the initial position ID.
|
||||
/// @param _harberg Address of the Harberg contract which this Stake contract interacts with.
|
||||
/// @dev Sets up the total supply based on the decimals of the Harberg token plus a fixed offset.
|
||||
constructor(address _harberg) {
|
||||
constructor(address _harberg, address _taxReceiver) {
|
||||
harberg = Harberg(_harberg);
|
||||
taxReceiver = _taxReceiver;
|
||||
totalSupply = 10 ** (harberg.decimals() + DECIMAL_OFFSET);
|
||||
taxPool = Harberg(_harberg).TAX_POOL();
|
||||
// start counting somewhere
|
||||
nextPositionId = 654321;
|
||||
// Initialize totalSharesAtTaxRate array
|
||||
|
|
@ -125,7 +125,7 @@ contract Stake {
|
|||
delete pos.creationTime;
|
||||
delete pos.share;
|
||||
}
|
||||
SafeERC20.safeTransfer(harberg, taxPool, taxAmountDue);
|
||||
SafeERC20.safeTransfer(harberg, taxReceiver, taxAmountDue);
|
||||
}
|
||||
|
||||
/// @dev Internal function to close a staking position, transferring the remaining Harberg tokens back to the owner after tax payment.
|
||||
|
|
|
|||
|
|
@ -3,25 +3,19 @@ pragma solidity ^0.8.19;
|
|||
|
||||
import "forge-std/Test.sol";
|
||||
import "forge-std/console.sol";
|
||||
import {TwabController} from "pt-v5-twab-controller/TwabController.sol";
|
||||
import {IERC20} from "@openzeppelin/token/ERC20/IERC20.sol";
|
||||
import "../src/Harberg.sol";
|
||||
|
||||
contract HarbergTest is Test {
|
||||
TwabController tc;
|
||||
Harberg harberg;
|
||||
address stakingPool;
|
||||
address liquidityPool;
|
||||
address liquidityManager;
|
||||
address taxPool;
|
||||
|
||||
function setUp() public {
|
||||
tc = new TwabController(60 * 60, uint32(block.timestamp));
|
||||
harberg = new Harberg("HARB", "HARB", tc);
|
||||
taxPool = harberg.TAX_POOL();
|
||||
harberg = new Harberg("HARB", "HARB");
|
||||
stakingPool = makeAddr("stakingPool");
|
||||
harberg.setStakingPool(stakingPool);
|
||||
liquidityPool = makeAddr("liquidityPool");
|
||||
harberg.setLiquidityPool(liquidityPool);
|
||||
liquidityManager = makeAddr("liquidityManager");
|
||||
harberg.setLiquidityManager(liquidityManager);
|
||||
}
|
||||
|
|
@ -46,11 +40,9 @@ contract HarbergTest is Test {
|
|||
assertEq(harberg.symbol(), "HARB");
|
||||
|
||||
// Confirm that the TwabController address is correctly set
|
||||
(address _tc, address _lm, address _sp, address _lp) = harberg.peripheryContracts();
|
||||
assertEq(_tc, address(tc));
|
||||
(address _lm, address _sp) = harberg.peripheryContracts();
|
||||
assertEq(_lm, liquidityManager);
|
||||
assertEq(_sp, stakingPool);
|
||||
assertEq(_lp, liquidityPool);
|
||||
}
|
||||
|
||||
function testMintWithEmptyStakingPool() public {
|
||||
|
|
@ -134,17 +126,20 @@ contract HarbergTest is Test {
|
|||
uint256 initialTotalSupply = harberg.totalSupply();
|
||||
uint256 initialStakingPoolBalance = harberg.balanceOf(stakingPool);
|
||||
|
||||
mintAmount = bound(mintAmount, 0, 500 * 1e18);
|
||||
mintAmount = bound(mintAmount, 1, 500 * 1e18);
|
||||
uint256 expectedNewStake = initialStakingPoolBalance * mintAmount / (initialTotalSupply - initialStakingPoolBalance);
|
||||
|
||||
// Expect Transfer events
|
||||
vm.expectEmit(true, true, true, true, address(harberg));
|
||||
emit IERC20.Transfer(address(0), address(liquidityManager), mintAmount);
|
||||
|
||||
vm.prank(address(liquidityManager));
|
||||
harberg.mint(mintAmount);
|
||||
|
||||
uint256 expectedStakingPoolBalance = initialStakingPoolBalance + expectedNewStake;
|
||||
uint256 expectedTotalSupply = initialTotalSupply + mintAmount + expectedNewStake;
|
||||
|
||||
assertEq(harberg.totalSupply(), expectedTotalSupply, "Total supply did not match expected after mint.");
|
||||
assertEq(harberg.balanceOf(stakingPool), expectedStakingPoolBalance, "Staking pool balance did not adjust correctly after mint.");
|
||||
assertEq(harberg.totalSupply(), expectedTotalSupply, "Total supply did not match expected after mint.");
|
||||
}
|
||||
|
||||
// Fuzz test for burn function with varying stake amounts
|
||||
|
|
@ -172,284 +167,7 @@ contract HarbergTest is Test {
|
|||
uint256 expectedStakingPoolBalance = initialStakingPoolBalance - expectedExcessStake;
|
||||
uint256 expectedTotalSupply = initialTotalSupply - burnAmount - expectedExcessStake;
|
||||
|
||||
assertEq(harberg.totalSupply(), expectedTotalSupply, "Total supply did not match expected after burn.");
|
||||
assertEq(harberg.balanceOf(stakingPool), expectedStakingPoolBalance, "Staking pool balance did not adjust correctly after burn.");
|
||||
assertEq(harberg.totalSupply(), expectedTotalSupply, "Total supply did not match expected after burn.");
|
||||
}
|
||||
|
||||
function testTaxAccumulation() public {
|
||||
uint256 taxAmount = 100 * 1e18; // 100 HARB tokens
|
||||
|
||||
vm.prank(address(liquidityManager));
|
||||
harberg.mint(taxAmount);
|
||||
|
||||
// Initial tax collected should be zero
|
||||
assertEq(harberg.sumTaxCollected(), 0, "Initial tax collected should be zero.");
|
||||
|
||||
// Simulate sending tokens to the taxPool
|
||||
vm.prank(address(liquidityManager));
|
||||
harberg.transfer(taxPool, taxAmount);
|
||||
|
||||
// Check that sumTaxCollected has been updated correctly
|
||||
assertEq(harberg.sumTaxCollected(), taxAmount, "Tax collected not updated correctly after transfer to taxPool.");
|
||||
}
|
||||
|
||||
function testUBIClaimBySingleAccountOverTime() public {
|
||||
uint256 initialSupply = 1000 * 1e18; // 1000 HARB tokens
|
||||
uint256 taxAmount = 200 * 1e18; // 200 HARB tokens to be collected as tax
|
||||
address user = makeAddr("alice");
|
||||
|
||||
// Setup initial supply and distribute to user
|
||||
vm.prank(address(liquidityManager));
|
||||
harberg.mint(initialSupply + taxAmount);
|
||||
vm.prank(address(liquidityManager));
|
||||
harberg.transfer(user, initialSupply);
|
||||
|
||||
// Simulate tax collection
|
||||
vm.prank(address(liquidityManager));
|
||||
harberg.transfer(taxPool, taxAmount);
|
||||
|
||||
// Simulate time passage to ensure TWAB is recorded over time
|
||||
vm.warp(block.timestamp + 30 days);
|
||||
|
||||
// Assert initial user balance and sumTaxCollected before claiming UBI
|
||||
assertEq(harberg.balanceOf(user), initialSupply, "User should hold the entire initial supply.");
|
||||
assertEq(harberg.sumTaxCollected(), taxAmount, "Tax collected should match the tax amount transferred.");
|
||||
|
||||
// User claims UBI
|
||||
vm.prank(user);
|
||||
harberg.claimUbi(user);
|
||||
|
||||
// Compute expected UBI
|
||||
// Assume the user is the only one holding tokens, they get all the collected taxes.
|
||||
uint256 expectedUbiAmount = taxAmount;
|
||||
|
||||
// Verify UBI claim
|
||||
uint256 postClaimBalance = harberg.balanceOf(user);
|
||||
assertEq(postClaimBalance, initialSupply + expectedUbiAmount, "User's balance after claiming UBI is incorrect.");
|
||||
|
||||
// Ensure that claiming doesn't affect the total supply
|
||||
uint256 expectedTotalSupply = initialSupply + taxAmount; // Include minted tax
|
||||
assertEq(harberg.totalSupply(), expectedTotalSupply, "Total supply should be unchanged after UBI claim.");
|
||||
}
|
||||
|
||||
function testUBIClaimBySingleAccountWithWraparound() public {
|
||||
uint256 initialSupply = 1000 * 1e18; // 1000 HARB tokens
|
||||
uint256 taxAmount = 200 * 1e18; // 200 HARB tokens to be collected as tax
|
||||
uint256 nearMaxUint = type(uint256).max - 10;
|
||||
address user = makeAddr("alice");
|
||||
|
||||
// Set sumTaxCollected to near max value to simulate wrap-around
|
||||
vm.store(
|
||||
address(harberg),
|
||||
bytes32(uint256(9)),
|
||||
bytes32(nearMaxUint)
|
||||
);
|
||||
|
||||
// Read the value back to confirm it's set correctly
|
||||
assertEq(harberg.sumTaxCollected(), nearMaxUint, "Initial sumTaxCollected should be near max uint256");
|
||||
|
||||
// Setup initial supply and distribute to user
|
||||
vm.prank(address(liquidityManager));
|
||||
harberg.mint(initialSupply + taxAmount);
|
||||
vm.prank(address(liquidityManager));
|
||||
harberg.transfer(user, initialSupply);
|
||||
|
||||
// Simulate tax collection to cause overflow
|
||||
vm.prank(address(liquidityManager));
|
||||
harberg.transfer(taxPool, taxAmount);
|
||||
|
||||
// Simulate time passage to ensure TWAB is recorded over time
|
||||
vm.warp(block.timestamp + 30 days);
|
||||
|
||||
// Verify the new value of sumTaxCollected after overflow
|
||||
uint256 newSumTaxCollected = harberg.sumTaxCollected();
|
||||
assertGt(taxAmount, newSumTaxCollected, "sumTaxCollected should have wrapped around and be less than taxAmount");
|
||||
|
||||
// User claims UBI
|
||||
vm.prank(user);
|
||||
harberg.claimUbi(user);
|
||||
|
||||
// Compute expected UBI
|
||||
// Assume the user is the only one holding tokens, they get all the collected taxes.
|
||||
uint256 expectedUbiAmount = taxAmount;
|
||||
|
||||
// Verify UBI claim
|
||||
uint256 postClaimBalance = harberg.balanceOf(user);
|
||||
assertApproxEqRel(postClaimBalance, initialSupply + expectedUbiAmount, 1 * 1e14, "User's balance after claiming UBI is incorrect.");
|
||||
|
||||
// Ensure that claiming doesn't affect the total supply
|
||||
uint256 expectedTotalSupply = initialSupply + taxAmount; // Include minted tax
|
||||
assertEq(harberg.totalSupply(), expectedTotalSupply, "Total supply should be unchanged after UBI claim.");
|
||||
}
|
||||
|
||||
function testUBIClaimByMultipleAccountsWithDifferentHoldingPeriods() public {
|
||||
uint256 initialSupply = 1000 * 1e18; // 1000 HARB tokens
|
||||
uint256 taxAmount = 300 * 1e18; // 300 HARB tokens to be collected as tax
|
||||
address account1 = makeAddr("alice");
|
||||
address account2 = makeAddr("bob");
|
||||
address account3 = makeAddr("charly");
|
||||
|
||||
// Setup initial supply and distribute to users
|
||||
vm.startPrank(address(liquidityManager));
|
||||
harberg.mint(initialSupply);
|
||||
harberg.transfer(account1, 400 * 1e18); // Account 1 gets 400 tokens
|
||||
harberg.transfer(account2, 300 * 1e18); // Account 2 gets 300 tokens
|
||||
harberg.transfer(account3, 300 * 1e18); // Account 3 gets 300 tokens
|
||||
vm.stopPrank();
|
||||
|
||||
uint256 startTime = block.timestamp;
|
||||
|
||||
// Simulate different holding periods
|
||||
vm.warp(block.timestamp + 10 days); // Fast forward 10 days
|
||||
vm.prank(account1);
|
||||
harberg.transfer(account2, 100 * 1e18); // Account 1 transfers 100 tokens to Account 2
|
||||
|
||||
vm.warp(block.timestamp + 20 days); // Fast forward another 20 days
|
||||
vm.prank(account2);
|
||||
harberg.transfer(account3, 100 * 1e18); // Account 2 transfers 100 tokens to Account 3
|
||||
|
||||
// Simulate tax collection after the transactions
|
||||
vm.startPrank(address(liquidityManager));
|
||||
harberg.mint(taxAmount);
|
||||
harberg.transfer(taxPool, taxAmount);
|
||||
vm.stopPrank();
|
||||
|
||||
// Assert sumTaxCollected before claiming UBI
|
||||
assertEq(harberg.sumTaxCollected(), taxAmount, "Tax collected should match the tax amount transferred.");
|
||||
|
||||
// Each account claims UBI
|
||||
vm.prank(account1);
|
||||
harberg.claimUbi(account1);
|
||||
vm.prank(account2);
|
||||
harberg.claimUbi(account2);
|
||||
vm.prank(account3);
|
||||
harberg.claimUbi(account3);
|
||||
|
||||
|
||||
// Assert the post-claim balances reflect the TWAB calculations
|
||||
{
|
||||
uint256 totalDistributed = harberg.balanceOf(account1) + harberg.balanceOf(account2) + harberg.balanceOf(account3) - initialSupply;
|
||||
// Tolerance setup: 0.01% of the total tax amount
|
||||
uint256 lowerBound = taxAmount - (taxAmount / 10000);
|
||||
assertTrue(totalDistributed >= lowerBound && totalDistributed <= totalDistributed, "Total distributed UBI does not match the total tax collected within an acceptable tolerance range.");
|
||||
}
|
||||
|
||||
// Calculate expected UBI amounts based on simplified TWAB assumptions
|
||||
// These should be replaced with actual TWAB calculations from your contract
|
||||
uint256 totalPeriod = block.timestamp - startTime;
|
||||
uint256 account1TWAB = (400 * 1e18 * 10 days + 300 * 1e18 * (totalPeriod - 10 days)) / totalPeriod;
|
||||
uint256 account2TWAB = (300 * 1e18 * 10 days + 400 * 1e18 * 20 days + 300 * 1e18 * (totalPeriod - 30 days)) / totalPeriod;
|
||||
uint256 account3TWAB = (300 * 1e18 * 30 days + 400 * 1e18 * (totalPeriod - 30 days)) / totalPeriod;
|
||||
|
||||
uint256 totalTWAB = account1TWAB + account2TWAB + account3TWAB;
|
||||
|
||||
// Calculate exact expected UBI payouts
|
||||
uint256 expectedBalance1 = (taxAmount * account1TWAB) / totalTWAB;
|
||||
uint256 expectedBalance2 = (taxAmount * account2TWAB) / totalTWAB;
|
||||
uint256 expectedBalance3 = (taxAmount * account3TWAB) / totalTWAB;
|
||||
|
||||
// Assert the post-claim balances reflect the TWAB calculations with a smaller rounding tolerance
|
||||
// 1 * 1e14; // 0.0001 HARB token tolerance for rounding errors
|
||||
assertApproxEqRel(harberg.balanceOf(account1) - 300 * 1e18, expectedBalance1, 1 * 1e14, "Account 1's balance after claiming UBI is incorrect.");
|
||||
assertApproxEqRel(harberg.balanceOf(account2) - 300 * 1e18, expectedBalance2, 1 * 1e14, "Account 2's balance after claiming UBI is incorrect.");
|
||||
assertApproxEqRel(harberg.balanceOf(account3) - 400 * 1e18, expectedBalance3, 1 * 1e14, "Account 3's balance after claiming UBI is incorrect.");
|
||||
}
|
||||
|
||||
function testUBIClaimWithoutAnyTaxCollected() public {
|
||||
uint256 initialSupply = 1000 * 1e18; // 1000 HARB tokens
|
||||
address user = makeAddr("alice");
|
||||
|
||||
// Setup initial supply and allocate to user
|
||||
vm.startPrank(address(liquidityManager));
|
||||
harberg.mint(initialSupply);
|
||||
harberg.transfer(user, initialSupply);
|
||||
vm.stopPrank();
|
||||
|
||||
// Ensure no tax has been collected yet
|
||||
assertEq(harberg.sumTaxCollected(), 0, "Initial tax collected should be zero.");
|
||||
|
||||
// Simulate time passage to ensure TWAB is recorded
|
||||
vm.warp(block.timestamp + 30 days);
|
||||
|
||||
// User attempts to claim UBI
|
||||
vm.prank(user);
|
||||
vm.expectRevert("No UBI to claim."); // Assuming your contract reverts with a message when there's no UBI to claim
|
||||
harberg.claimUbi(user);
|
||||
|
||||
// Ensure the user's balance remains unchanged as no UBI should be distributed
|
||||
assertEq(harberg.balanceOf(user), initialSupply, "User's balance should not change after attempting to claim UBI without any taxes collected.");
|
||||
|
||||
// Check if sumTaxCollected remains zero after the claim attempt
|
||||
assertEq(harberg.sumTaxCollected(), 0, "No tax should be collected, and sumTaxCollected should remain zero after the claim attempt.");
|
||||
}
|
||||
|
||||
function testEdgeCaseWithMaximumTaxCollection() public {
|
||||
uint256 initialSupply = 1e24; // Large number of HARB tokens to simulate realistic large-scale deployment
|
||||
uint256 maxTaxAmount = type(uint96).max - initialSupply; // Setting max tax just below overflow threshold when added to total supply
|
||||
|
||||
address account1 = makeAddr("alice");
|
||||
|
||||
// Setup initial supply and allocate to user
|
||||
vm.startPrank(address(liquidityManager));
|
||||
harberg.mint(initialSupply + maxTaxAmount);
|
||||
harberg.transfer(account1, initialSupply);
|
||||
harberg.transfer(taxPool, maxTaxAmount); // Simulate tax collection at the theoretical maximum
|
||||
vm.stopPrank();
|
||||
|
||||
// Assert that maximum tax was collected
|
||||
assertEq(harberg.sumTaxCollected(), maxTaxAmount, "Max tax collected should match the max tax amount transferred.");
|
||||
|
||||
// Simulate time passage and UBI claim
|
||||
vm.warp(block.timestamp + 30 days);
|
||||
|
||||
// Account 1 claims UBI
|
||||
vm.prank(account1);
|
||||
harberg.claimUbi(account1);
|
||||
|
||||
// Check if the account's balance increased correctly
|
||||
uint256 expectedBalance = initialSupply + maxTaxAmount; // This assumes the entire tax pool goes to one account, simplify as needed
|
||||
assertEq(harberg.balanceOf(account1), expectedBalance, "Account 1's balance after claiming UBI with max tax collection is incorrect.");
|
||||
|
||||
// Verify that no taxes are left unclaimed
|
||||
assertEq(harberg.balanceOf(taxPool), 0, "All taxes should be claimed after the UBI claim.");
|
||||
}
|
||||
|
||||
|
||||
// TODO: why is this test passing even though it exceeds MAX_CARDINALITY?
|
||||
function testTwabBeyondBuffer() public {
|
||||
uint256 initialSupply = 1000 * 1e18; // 1000 HARB tokens
|
||||
uint256 taxAmount = 300 * 1e18; // 300 HARB tokens to be collected as tax
|
||||
|
||||
address account1 = makeAddr("alice");
|
||||
|
||||
// Setup initial supply and allocate to user
|
||||
vm.startPrank(address(liquidityManager));
|
||||
harberg.mint(initialSupply + taxAmount);
|
||||
harberg.transfer(account1, initialSupply / 800);
|
||||
harberg.transfer(taxPool, taxAmount); // Simulate tax collection at the theoretical maximum
|
||||
harberg.transfer(liquidityPool, harberg.balanceOf(address(liquidityManager)));
|
||||
vm.stopPrank();
|
||||
vm.warp(block.timestamp + 1 hours);
|
||||
|
||||
|
||||
// Simulate updates over a longer period, e.g., enough to potentially wrap the buffer.
|
||||
uint numHours = 399; // More than 365 to potentially test buffer wrapping (MAX_CARDINALITY)
|
||||
for (uint i = 0; i < numHours; i++) {
|
||||
vm.prank(liquidityPool);
|
||||
harberg.transfer(account1, initialSupply / 800);
|
||||
vm.warp(block.timestamp + 1 hours); // Fast-forward time by one hour.
|
||||
}
|
||||
|
||||
// Account 1 claims UBI
|
||||
vm.prank(account1);
|
||||
uint256 ubiCollected = harberg.claimUbi(account1);
|
||||
|
||||
// Check if the account's balance increased correctly
|
||||
uint256 expectedBalance = (initialSupply / 2) + ubiCollected; // This assumes the entire tax pool goes to one account, simplify as needed
|
||||
assertApproxEqRel(harberg.balanceOf(account1), expectedBalance, 1 * 1e18, "Account 1's balance after claiming UBI with max tax collection is incorrect.");
|
||||
|
||||
// Verify that no taxes are left unclaimed
|
||||
assertEq(harberg.balanceOf(taxPool), 0, "All taxes should be claimed after the UBI claim.");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import "forge-std/Test.sol";
|
|||
import "@aperture/uni-v3-lib/TickMath.sol";
|
||||
import {LiquidityAmounts} from "@aperture/uni-v3-lib/LiquidityAmounts.sol";
|
||||
import {WETH} from "solmate/tokens/WETH.sol";
|
||||
import {TwabController} from "pt-v5-twab-controller/TwabController.sol";
|
||||
import {PoolAddress, PoolKey} from "@aperture/uni-v3-lib/PoolAddress.sol";
|
||||
import "@uniswap-v3-core/interfaces/IUniswapV3Factory.sol";
|
||||
import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";
|
||||
|
|
@ -60,8 +59,6 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager {
|
|||
function setUpCustomToken0(bool token0shouldBeWeth) public {
|
||||
factory = UniswapHelpers.deployUniswapFactory();
|
||||
|
||||
TwabController tc = new TwabController(60 * 60 * 24, uint32(block.timestamp));
|
||||
|
||||
bool setupComplete = false;
|
||||
uint retryCount = 0;
|
||||
while (!setupComplete && retryCount < 5) {
|
||||
|
|
@ -71,7 +68,7 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager {
|
|||
}
|
||||
|
||||
weth = IWETH9(address(new WETH()));
|
||||
harberg = new Harberg("HARB", "HARB", tc);
|
||||
harberg = new Harberg("HARB", "HARB");
|
||||
|
||||
// Check if the setup meets the required condition
|
||||
if (token0shouldBeWeth == address(weth) < address(harberg)) {
|
||||
|
|
@ -90,7 +87,7 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager {
|
|||
token0isWeth = address(weth) < address(harberg);
|
||||
pool.initializePoolFor1Cent(token0isWeth);
|
||||
|
||||
stake = new Stake(address(harberg));
|
||||
stake = new Stake(address(harberg), feeDestination);
|
||||
harberg.setStakingPool(address(stake));
|
||||
Sentimenter senti = Sentimenter(address(new MockSentimenter()));
|
||||
senti.initialize(address(harberg), address(stake));
|
||||
|
|
@ -98,7 +95,6 @@ contract LiquidityManagerTest is UniswapTestBase, CSVManager {
|
|||
lm.setFeeDestination(feeDestination);
|
||||
vm.prank(feeDestination);
|
||||
harberg.setLiquidityManager(address(lm));
|
||||
harberg.setLiquidityPool(address(pool));
|
||||
vm.deal(address(lm), 10 ether);
|
||||
initializePositionsCSV(); // Set up the CSV header
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ pragma solidity ^0.8.19;
|
|||
|
||||
import "forge-std/Test.sol";
|
||||
import "forge-std/console.sol";
|
||||
import {TwabController} from "pt-v5-twab-controller/TwabController.sol";
|
||||
import "../src/Harberg.sol";
|
||||
import {TooMuchSnatch, Stake} from "../src/Stake.sol";
|
||||
import "../src/Sentimenter.sol";
|
||||
|
|
@ -11,29 +10,22 @@ import {ERC1967Proxy} from "@openzeppelin/proxy/ERC1967/ERC1967Proxy.sol";
|
|||
import {MockSentimenter} from "./mocks/MockSentimenter.sol";
|
||||
|
||||
contract SentimenterTest is Test {
|
||||
TwabController tc;
|
||||
Harberg harberg;
|
||||
Stake stake;
|
||||
Sentimenter sentimenter;
|
||||
address liquidityPool;
|
||||
address liquidityManager;
|
||||
address taxPool;
|
||||
|
||||
function setUp() public {
|
||||
tc = new TwabController(60 * 60, uint32(block.timestamp));
|
||||
harberg = new Harberg("HARB", "HARB", tc);
|
||||
taxPool = harberg.TAX_POOL();
|
||||
stake = new Stake(address(harberg));
|
||||
harberg = new Harberg("HARB", "HARB");
|
||||
stake = new Stake(address(harberg), makeAddr("taxRecipient"));
|
||||
harberg.setStakingPool(address(stake));
|
||||
liquidityPool = makeAddr("liquidityPool");
|
||||
harberg.setLiquidityPool(liquidityPool);
|
||||
liquidityManager = makeAddr("liquidityManager");
|
||||
harberg.setLiquidityManager(liquidityManager);
|
||||
// deploy upgradeable tuner contract
|
||||
Sentimenter _sentimenter = new Sentimenter();
|
||||
bytes memory params = abi.encodeWithSignature("initialize(address,address)", address(harberg),address(stake));
|
||||
ERC1967Proxy proxy = new ERC1967Proxy(address(_sentimenter), params);
|
||||
sentimenter = Sentimenter(address(proxy));
|
||||
// deploy upgradeable tuner contract
|
||||
Sentimenter _sentimenter = new Sentimenter();
|
||||
bytes memory params = abi.encodeWithSignature("initialize(address,address)", address(harberg),address(stake));
|
||||
ERC1967Proxy proxy = new ERC1967Proxy(address(_sentimenter), params);
|
||||
sentimenter = Sentimenter(address(proxy));
|
||||
}
|
||||
|
||||
function doSnatch(address staker, uint256 amount, uint32 taxRate) private returns (uint256 positionId) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import "@aperture/uni-v3-lib/TickMath.sol";
|
|||
import {LiquidityAmounts} from "@aperture/uni-v3-lib/LiquidityAmounts.sol";
|
||||
import "../src/interfaces/IWETH9.sol";
|
||||
import {WETH} from "solmate/tokens/WETH.sol";
|
||||
import {TwabController} from "pt-v5-twab-controller/TwabController.sol";
|
||||
import {PoolAddress, PoolKey} from "@aperture/uni-v3-lib/PoolAddress.sol";
|
||||
import "@uniswap-v3-core/interfaces/IUniswapV3Factory.sol";
|
||||
import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";
|
||||
|
|
@ -18,11 +17,16 @@ import {UniswapTestBase} from "./helpers/UniswapTestBase.sol";
|
|||
import {CSVHelper} from "./helpers/CSVHelper.sol";
|
||||
import {CSVManager} from "./helpers/CSVManager.sol";
|
||||
import "../src/Sentimenter.sol";
|
||||
import "./mocks/MockSentimenter.sol";
|
||||
|
||||
address constant TAX_POOL = address(2);
|
||||
// default fee of 1%
|
||||
uint24 constant FEE = uint24(10_000);
|
||||
|
||||
// Dummy.sol
|
||||
contract Dummy {
|
||||
// This contract can be empty as it is only used to affect the nonce
|
||||
}
|
||||
|
||||
contract SimulationsTest is UniswapTestBase, CSVManager {
|
||||
using UniswapHelpers for IUniswapV3Pool;
|
||||
using CSVHelper for *;
|
||||
|
|
@ -71,27 +75,33 @@ contract SimulationsTest is UniswapTestBase, CSVManager {
|
|||
Action[] txns;
|
||||
}
|
||||
|
||||
|
||||
// Utility to deploy dummy contracts
|
||||
function deployDummies(uint count) internal {
|
||||
for (uint i = 0; i < count; i++) {
|
||||
new Dummy(); // Just increment the nonce
|
||||
}
|
||||
}
|
||||
|
||||
function setUp() public {
|
||||
factory = UniswapHelpers.deployUniswapFactory();
|
||||
|
||||
weth = IWETH9(address(new WETH()));
|
||||
TwabController tc = new TwabController(60 * 60, uint32(block.timestamp));
|
||||
harberg = new Harberg("Harberg", "HRB", tc);
|
||||
harberg = new Harberg("Harberg", "HRB");
|
||||
pool = IUniswapV3Pool(factory.createPool(address(weth), address(harberg), FEE));
|
||||
|
||||
poolKey = PoolAddress.getPoolKey(address(weth), address(harberg), FEE);
|
||||
token0isWeth = address(weth) < address(harberg);
|
||||
//pool.initializePoolFor1Cent(token0isWeth);
|
||||
|
||||
stakingPool = new Stake(address(harberg));
|
||||
stakingPool = new Stake(address(harberg), feeDestination);
|
||||
harberg.setStakingPool(address(stakingPool));
|
||||
Sentimenter senti = new Sentimenter();
|
||||
senti.initialize(address(harberg), address(stakingPool));
|
||||
Sentimenter senti = new Sentimenter();
|
||||
senti.initialize(address(harberg), address(stakingPool));
|
||||
lm = new LiquidityManager(address(factory), address(weth), address(harberg), address(senti));
|
||||
lm.setFeeDestination(feeDestination);
|
||||
vm.prank(feeDestination);
|
||||
harberg.setLiquidityManager(address(lm));
|
||||
harberg.setLiquidityPool(address(pool));
|
||||
vm.deal(address(lm), 1 ether);
|
||||
timeOnRecenter = block.timestamp;
|
||||
initializeTimeSeriesCSV();
|
||||
|
|
@ -100,8 +110,6 @@ contract SimulationsTest is UniswapTestBase, CSVManager {
|
|||
function setUpCustomToken0(bool token0shouldBeWeth) public {
|
||||
factory = UniswapHelpers.deployUniswapFactory();
|
||||
|
||||
TwabController tc = new TwabController(60 * 60 * 24, uint32(block.timestamp));
|
||||
|
||||
bool setupComplete = false;
|
||||
uint retryCount = 0;
|
||||
while (!setupComplete && retryCount < 5) {
|
||||
|
|
@ -111,7 +119,7 @@ contract SimulationsTest is UniswapTestBase, CSVManager {
|
|||
}
|
||||
|
||||
weth = IWETH9(address(new WETH()));
|
||||
harberg = new Harberg("HARB", "HARB", tc);
|
||||
harberg = new Harberg("HARB", "HARB");
|
||||
|
||||
// Check if the setup meets the required condition
|
||||
if (token0shouldBeWeth == address(weth) < address(harberg)) {
|
||||
|
|
@ -128,15 +136,14 @@ contract SimulationsTest is UniswapTestBase, CSVManager {
|
|||
pool = IUniswapV3Pool(factory.createPool(address(weth), address(harberg), FEE));
|
||||
|
||||
token0isWeth = address(weth) < address(harberg);
|
||||
stake = new Stake(address(harberg));
|
||||
harberg.setStakingPool(address(stake));
|
||||
Sentimenter senti = Sentimenter(address(new MockSentimenter()));
|
||||
senti.initialize(address(harberg), address(stake));
|
||||
stakingPool = new Stake(address(harberg), feeDestination);
|
||||
harberg.setStakingPool(address(stakingPool));
|
||||
Sentimenter senti = Sentimenter(address(new MockSentimenter()));
|
||||
senti.initialize(address(harberg), address(stakingPool));
|
||||
lm = new LiquidityManager(address(factory), address(weth), address(harberg), address(senti));
|
||||
lm.setFeeDestination(feeDestination);
|
||||
vm.prank(feeDestination);
|
||||
harberg.setLiquidityManager(address(lm));
|
||||
harberg.setLiquidityPool(address(pool));
|
||||
vm.deal(address(lm), 10 ether);
|
||||
initializePositionsCSV(); // Set up the CSV header
|
||||
}
|
||||
|
|
@ -215,8 +222,7 @@ contract SimulationsTest is UniswapTestBase, CSVManager {
|
|||
//(sentiment, avgTaxRate) = stakingPool.getSentiment();
|
||||
newRow = string.concat(newRow,
|
||||
",", CSVHelper.uintToStr(avgTaxRate),
|
||||
",", CSVHelper.uintToStr(sentiment),
|
||||
",", CSVHelper.uintToStr(harberg.sumTaxCollected() / 1e18)
|
||||
",", CSVHelper.uintToStr(sentiment)
|
||||
);
|
||||
} else {
|
||||
newRow = string.concat(newRow, ", 0, 100, 95, 25, 0");
|
||||
|
|
|
|||
|
|
@ -3,30 +3,23 @@ pragma solidity ^0.8.19;
|
|||
|
||||
import "forge-std/Test.sol";
|
||||
import "forge-std/console.sol";
|
||||
import {TwabController} from "pt-v5-twab-controller/TwabController.sol";
|
||||
import "../src/Harberg.sol";
|
||||
import {TooMuchSnatch, Stake} from "../src/Stake.sol";
|
||||
|
||||
contract StakeTest is Test {
|
||||
TwabController tc;
|
||||
Harberg harberg;
|
||||
Stake stakingPool;
|
||||
address liquidityPool;
|
||||
address liquidityManager;
|
||||
address taxPool;
|
||||
|
||||
event PositionCreated(uint256 indexed positionId, address indexed owner, uint256 harbergDeposit, uint256 share, uint32 taxRate);
|
||||
event PositionRemoved(uint256 indexed positionId, address indexed owner, uint256 harbergPayout);
|
||||
|
||||
|
||||
function setUp() public {
|
||||
tc = new TwabController(60 * 60, uint32(block.timestamp));
|
||||
harberg = new Harberg("HARB", "HARB", tc);
|
||||
taxPool = harberg.TAX_POOL();
|
||||
stakingPool = new Stake(address(harberg));
|
||||
harberg = new Harberg("HARB", "HARB");
|
||||
stakingPool = new Stake(address(harberg), makeAddr("taxRecipient"));
|
||||
harberg.setStakingPool(address(stakingPool));
|
||||
liquidityPool = makeAddr("liquidityPool");
|
||||
harberg.setLiquidityPool(liquidityPool);
|
||||
liquidityManager = makeAddr("liquidityManager");
|
||||
harberg.setLiquidityManager(liquidityManager);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue