diff --git a/src/BondDepository.sol b/src/BondDepository.sol index fd63770..c6497c6 100644 --- a/src/BondDepository.sol +++ b/src/BondDepository.sol @@ -8,6 +8,7 @@ import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IER import {NoteKeeper} from "./types/NoteKeeper.sol"; import {IBondDepository} from "./interfaces/IBondDepository.sol"; import {IWETH9} from "./interfaces/IWETH9.sol"; +import {FullMath} from "./libraries/FullMath.sol"; contract GhostBondDepository is IBondDepository, NoteKeeper { using SafeERC20 for IERC20; @@ -60,7 +61,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { if (price > maxPrice) revert DepositoryMaxPrice(price, maxPrice); expiry = term.fixedTerm ? term.vesting + currentTime : term.vesting; - payout = ((amount * 1e18) / price) / (10**metadatas[id].quoteDecimals); + payout = FullMath.mulDiv(amount, 1e18, price) / (10**metadatas[id].quoteDecimals); if (payout > market.maxPayout) revert DepositoryMaxSize(payout, market.maxPayout); market.capacity -= market.capacityInQuote ? amount : payout; @@ -116,14 +117,14 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { uint256 price = _marketPrice(id); uint256 capacity = market.capacityInQuote - ? ((market.capacity * 1e18) / price) / (10**meta.quoteDecimals) + ? FullMath.mulDiv(market.capacity, 1e18, price) / (10**meta.quoteDecimals) : market.capacity; // forge-lint: disable-next-line(unsafe-typecast) - markets[id].maxPayout = uint64((capacity * meta.depositInterval) / timeRemaining); - uint256 targetDebt = (capacity * meta.length) / timeRemaining; + markets[id].maxPayout = uint64(FullMath.mulDiv(capacity, meta.depositInterval, timeRemaining)); + uint256 targetDebt = FullMath.mulDiv(capacity, meta.length, timeRemaining); // forge-lint: disable-next-line(unsafe-typecast) - uint64 newControlVariable = uint64((price * _treasury.baseSupply()) / targetDebt); + uint64 newControlVariable = uint64(FullMath.mulDiv(price, _treasury.baseSupply(), targetDebt)); emit Tuned(id, terms[id].controlVariable, newControlVariable); @@ -157,13 +158,13 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { uint256 secondsToConclusion = _terms[1] - block.timestamp; uint256 decimals = IERC20Metadata(_quoteToken).decimals(); uint64 targetDebt = uint64(_booleans[0] - ? ((_market[0] * 1e18) / _market[1]) / 10**decimals + ? FullMath.mulDiv(_market[0], 1e18, _market[1]) / 10**decimals : _market[0]); // forge-lint: disable-next-line(unsafe-typecast) - uint64 maxPayout = uint64((targetDebt * _intervals[0]) / secondsToConclusion); - uint256 maxDebt = targetDebt + ((targetDebt * _market[2]) / 1e5); - uint256 controlVariable = (_market[1] * _treasury.baseSupply()) / targetDebt; + uint64 maxPayout = uint64(FullMath.mulDiv(targetDebt, _intervals[0], secondsToConclusion)); + uint256 maxDebt = targetDebt + FullMath.mulDiv(targetDebt, _market[2], 1e5); + uint256 controlVariable = FullMath.mulDiv(_market[1], _treasury.baseSupply(), targetDebt); id = markets.length; @@ -218,21 +219,19 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { } function marketPrice(uint256 id) public view override returns (uint256) { - uint256 nominator = currentControlVariable(id) * debtRatio(id); - return nominator / (10**metadatas[id].quoteDecimals); + return FullMath.mulDiv(currentControlVariable(id), debtRatio(id), 10**metadatas[id].quoteDecimals); } function payoutFor( uint256 id, uint256 amount ) external view override returns (uint256) { - uint256 nominator = (amount * 1e18) / marketPrice(id); + uint256 nominator = FullMath.mulDiv(amount, 1e18, marketPrice(id)); return nominator / 10**metadatas[id].quoteDecimals; } function debtRatio(uint256 id) public view override returns (uint256) { - uint256 nominator = currentDebt(id) * (10**metadatas[id].quoteDecimals); - return nominator / _treasury.baseSupply(); + return FullMath.mulDiv(currentDebt(id), 10**metadatas[id].quoteDecimals, _treasury.baseSupply()); } function currentDebt(uint256 id) public view override returns (uint256) { @@ -242,7 +241,7 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { function debtDecay(uint256 id) public view override returns (uint64) { Metadata memory meta = metadatas[id]; uint256 secondsSince = block.timestamp - meta.lastDecay; - return uint64((markets[id].totalDebt * secondsSince) / meta.length); + return uint64(FullMath.mulDiv(markets[id].totalDebt, secondsSince, meta.length)); } function currentControlVariable(uint256 id) public view override returns (uint256) { @@ -306,13 +305,11 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { } function _marketPrice(uint256 _id) internal view returns (uint256) { - uint256 nominator = terms[_id].controlVariable * _debtRatio(_id); - return nominator / (10**metadatas[_id].quoteDecimals); + return FullMath.mulDiv(terms[_id].controlVariable, _debtRatio(_id), 10**metadatas[_id].quoteDecimals); } function _debtRatio(uint256 id) internal view returns (uint256) { - uint256 nominator = markets[id].totalDebt * (10**metadatas[id].quoteDecimals); - return nominator / _treasury.baseSupply(); + return FullMath.mulDiv(markets[id].totalDebt, 10**metadatas[id].quoteDecimals, _treasury.baseSupply()); } function _controlDecay(uint256 id) @@ -331,7 +328,8 @@ contract GhostBondDepository is IBondDepository, NoteKeeper { active = secondsSince < info.timeToAdjusted; decay = active - ? (info.change * secondsSince) / info.timeToAdjusted + // forge-lint: disable-next-line(unsafe-typecast) + ? uint64(FullMath.mulDiv(info.change, secondsSince, info.timeToAdjusted)) : info.change; } } diff --git a/src/GhstERC20.sol b/src/GhstERC20.sol index e1a5720..edd638a 100644 --- a/src/GhstERC20.sol +++ b/src/GhstERC20.sol @@ -8,6 +8,7 @@ import {Nonces} from "@openzeppelin-contracts/utils/Nonces.sol"; import {ISTNK} from "./interfaces/ISTNK.sol"; import {IGHST} from "./interfaces/IGHST.sol"; +import {FullMath} from "./libraries/FullMath.sol"; contract Ghost is IGHST, ERC20, ERC20Permit, ERC20Votes { address public override staking; @@ -60,11 +61,11 @@ contract Ghost is IGHST, ERC20, ERC20Permit, ERC20Votes { } function balanceFrom(uint256 _amount) public view override returns (uint256) { - return _amount * index() / 1e18; + return FullMath.mulDiv(_amount, index(), 1e18); } function balanceTo(uint256 _amount) public view override returns (uint256) { - return _amount * 1e18 / index(); + return FullMath.mulDiv(_amount, 1e18, index()); } function nonces(address owner) public view override(ERC20Permit, Nonces) returns (uint256) { diff --git a/src/Staking.sol b/src/Staking.sol index 78d3614..d2fad5a 100644 --- a/src/Staking.sol +++ b/src/Staking.sol @@ -67,7 +67,7 @@ contract GhostStaking is IStaking, GhostAccessControlled { } else { if (locks[to] && to != msg.sender) revert ExternalDepositsLocked(); uint48 expiry = epoch.number + warmupPeriod; - IGhostWarmup(warmup).addToWarmup(amount, to, expiry); + IGhostWarmup(warmup).addToWarmup(returnAmount, to, expiry); } } diff --git a/src/StakingDistributor.sol b/src/StakingDistributor.sol index 9157c2c..c0cc6f0 100644 --- a/src/StakingDistributor.sol +++ b/src/StakingDistributor.sol @@ -5,6 +5,7 @@ import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol"; import {IUniswapV2Pair} from "@uniswap-v2-core/interfaces/IUniswapV2Pair.sol"; import {GhostAccessControlled} from "./types/GhostAccessControlled.sol"; +import {FullMath} from "./libraries/FullMath.sol"; import {ITreasury} from "./interfaces/ITreasury.sol"; import {IDistributor} from "./interfaces/IDistributor.sol"; @@ -77,7 +78,7 @@ contract GhostDistributor is IDistributor, GhostAccessControlled { } function nextRewardFor(address who) public view override returns (uint256) { - return (IERC20(ftso).balanceOf(who) * rewardRate) / DENOMINATOR; + return FullMath.mulDiv(IERC20(ftso).balanceOf(who), rewardRate, DENOMINATOR); } function setBounty(uint256 _bounty) external override onlyGovernor { @@ -109,13 +110,13 @@ contract GhostDistributor is IDistributor, GhostAccessControlled { ) revert NotPermissioned(); if ( msg.sender == authority.guardian() && - rate > rewardRate * 25 / 1_000 + rate > FullMath.mulDiv(rewardRate, 25, 1_000) ) revert AdjustmentLimit(); if (!add && rate > rewardRate) revert AdjustmentUnderflow(); adjustment = Adjust({ add: add, - rate: uint120(rate), // forge-lint: disable-line(unsafe-typecast) + rate: uint120(rate), // forge-lint: disable-line(unsafe-typecast) target: uint120(target) // forge-lint: disable-line(unsafe-typecast) }); } diff --git a/src/StandardBondingCalculator.sol b/src/StandardBondingCalculator.sol index 7a1a1f8..98931d8 100644 --- a/src/StandardBondingCalculator.sol +++ b/src/StandardBondingCalculator.sol @@ -7,6 +7,7 @@ import {IERC20Metadata} from "@openzeppelin-contracts/token/ERC20/extensions/IER import {IUniswapV2Pair} from "@uniswap-v2-core/interfaces/IUniswapV2Pair.sol"; import {IBondingCalculator} from "./interfaces/IBondingCalculator.sol"; +import {FullMath} from "./libraries/FullMath.sol"; contract GhostBondingCalculator is IBondingCalculator { using FixedPoint for *; @@ -25,10 +26,10 @@ contract GhostBondingCalculator is IBondingCalculator { uint256 decimals = token0 + token1 - IERC20Metadata(pair).decimals(); (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pair).getReserves(); - k = reserve0 * reserve1 / (10**decimals); + k = FullMath.mulDiv(reserve0, reserve1, 10**decimals); if (isCoefficient) { - k = k * 1e18 / fraction; + k = FullMath.mulDiv(k, 1e18, fraction); } } @@ -40,8 +41,8 @@ contract GhostBondingCalculator is IBondingCalculator { uint256 totalValue = getTotalValue(pair, true); uint256 totalSupply = IUniswapV2Pair(pair).totalSupply(); - value = totalValue * FixedPoint.fraction(amount, totalSupply).decode112with18() / 1e18; - value = value * fraction / 1e18; + value = FullMath.mulDiv(totalValue, FixedPoint.fraction(amount, totalSupply).decode112with18(), 1e18); + value = FullMath.mulDiv(value, fraction, 1e18); } function markdown(address pair) external view override returns (uint256 reserve) { @@ -54,8 +55,8 @@ contract GhostBondingCalculator is IBondingCalculator { reserve = reserve0; } - reserve = reserve * (2 * 1e9) / getTotalValue(pair, false); - reserve = reserve * _sqrt(fraction) / 1e9; + reserve = FullMath.mulDiv(reserve, 2 * 1e9, getTotalValue(pair, false)); + reserve = FullMath.mulDiv(reserve, _sqrt(fraction), 1e9); } function _sqrt(uint256 a) private pure returns (uint256 c) { diff --git a/src/StinkyERC20.sol b/src/StinkyERC20.sol index 83bdb17..fe12a96 100644 --- a/src/StinkyERC20.sol +++ b/src/StinkyERC20.sol @@ -9,6 +9,8 @@ import {IGHST} from "./interfaces/IGHST.sol"; import {ISTNK} from "./interfaces/ISTNK.sol"; import {IStaking} from "./interfaces/IStaking.sol"; +import {FullMath} from "./libraries/FullMath.sol"; + contract Stinky is ISTNK, ERC20Permit { uint256 private constant INITIAL_SHARES_SUPPLY = 5 * 10**15; uint256 private constant TOTAL_SHARES = type(uint256).max - (type(uint256).max % INITIAL_SHARES_SUPPLY); @@ -68,8 +70,8 @@ contract Stinky is ISTNK, ERC20Permit { newTotalSupply = _totalSupply; } else { if (currentCirculatingSupply > 0) { - supplyDelta = supplyDelta * _totalSupply / currentCirculatingSupply; - rebasePercent = supplyDelta * 1e18 / currentCirculatingSupply; + supplyDelta = FullMath.mulDiv(supplyDelta, _totalSupply, currentCirculatingSupply); + rebasePercent = FullMath.mulDiv(supplyDelta, 1e18, currentCirculatingSupply); } newTotalSupply = _totalSupply + supplyDelta; } diff --git a/src/Treasury.sol b/src/Treasury.sol index 4e88db3..976be52 100644 --- a/src/Treasury.sol +++ b/src/Treasury.sol @@ -12,6 +12,7 @@ import {IUniswapV2Router01} from "@uniswap-v2-periphery-1.1.0-beta.0/interfaces/ import {GhostAccessControlled} from "./types/GhostAccessControlled.sol"; import {Babylonian} from "./libraries/FixedPoint.sol"; +import {FullMath} from "./libraries/FullMath.sol"; import {IFTSO} from "./interfaces/IFTSO.sol"; import {ISTNK} from "./interfaces/ISTNK.sol"; @@ -387,8 +388,8 @@ contract GhostTreasury is GhostAccessControlled, ITreasury { if (permissions[STATUS.LIQUIDITYTOKEN][token]) { value = IBondingCalculator(bondCalculator[token]).valuation(token, amount); } else if (permissions[STATUS.RESERVETOKEN][token]) { - value = amount * 1e9 / (10**IERC20Metadata(token).decimals()); - value = value * IBondingCalculator(bondCalculator[token]).fraction() / 1e18; + value = FullMath.mulDiv(amount, 1e9, 10**IERC20Metadata(token).decimals()); + value = FullMath.mulDiv(value, IBondingCalculator(bondCalculator[token]).fraction(), 1e18); } } diff --git a/src/types/FrontEndRewarder.sol b/src/types/FrontEndRewarder.sol index 10e1eab..ef48efd 100644 --- a/src/types/FrontEndRewarder.sol +++ b/src/types/FrontEndRewarder.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; import {GhostAccessControlled} from "./GhostAccessControlled.sol"; import {IGhostAuthority} from "../interfaces/IGhostAuthority.sol"; +import {FullMath} from "../libraries/FullMath.sol"; import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; @@ -33,8 +34,8 @@ abstract contract FrontEndRewarder is GhostAccessControlled { uint256 payout, address referral ) internal returns (uint256) { - uint256 toDao = (payout * daoReward) / 1e4; - uint256 toRef = (payout * refReward) / 1e4; + uint256 toDao = FullMath.mulDiv(payout, daoReward, 1e4); + uint256 toRef = FullMath.mulDiv(payout, refReward, 1e4); if (whitelisted[referral]) { rewards[referral] += toRef;