编译器
0.8.17+commit.8df45f5f
文件 1 的 29:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 29:AggregatorV3Interface.sol
pragma solidity ^0.8.17;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
文件 3 的 29:BAMM.sol
pragma solidity ^0.8.17;
import "./../StabilityPool.sol";
import "./CropJoinAdapter.sol";
import "./PriceFormula.sol";
import "./../Interfaces/IPriceFeed.sol";
import "./../Interfaces/ITHUSDToken.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./../Dependencies/Ownable.sol";
import "./../Dependencies/AggregatorV3Interface.sol";
import "./../Dependencies/CheckContract.sol";
import "./../Dependencies/SendCollateral.sol";
import "./YieldBoxRebase.sol";
contract BAMM is CropJoinAdapter, PriceFormula, Ownable, CheckContract, SendCollateral {
using YieldBoxRebase for uint256;
AggregatorV3Interface public immutable priceAggregator;
AggregatorV3Interface public thusd2UsdPriceAggregator;
ITHUSDToken public immutable thusdToken;
StabilityPool immutable public SP;
IERC20 public immutable collateralERC20;
address payable public immutable feePool;
uint256 public constant MAX_FEE = 100;
uint256 public fee = 0;
uint256 public A = 20;
uint256 public constant MIN_A = 1;
uint256 public constant MAX_A = 200;
uint256 public immutable maxDiscount;
uint256 constant public PRECISION = 1e18;
address public bProtocolOwner;
event ParamsSet(uint256 A, uint256 fee);
event UserDeposit(address indexed user, uint256 thusdAmount, uint256 numShares);
event UserWithdraw(address indexed user, uint256 thusdAmount, uint256 collateralAmount, uint256 numShares);
event RebalanceSwap(address indexed user, uint256 thusdAmount, uint256 collateralAmount, uint256 timestamp);
constructor(
address _priceAggregator,
address payable _SP,
address _thusdToken,
address _collateralERC20,
uint256 _maxDiscount,
address payable _feePool,
address _bProtocolOwner
)
{
checkContract(_priceAggregator);
checkContract(_thusdToken);
checkContract(_SP);
if (_collateralERC20 != address(0)) {
checkContract(_collateralERC20);
}
priceAggregator = AggregatorV3Interface(_priceAggregator);
thusdToken = ITHUSDToken(_thusdToken);
SP = StabilityPool(_SP);
collateralERC20 = IERC20(_collateralERC20);
feePool = _feePool;
maxDiscount = _maxDiscount;
require(
Ownable(_SP).owner() != address(0) ||
IStabilityPool(_SP).collateralAddress() == _collateralERC20,
"The same collateral address must be used for the entire set of contracts"
);
require(_bProtocolOwner != address(0), "B.Protocol owner must be specified");
bProtocolOwner = _bProtocolOwner;
}
modifier onlyBProtocolOwner() {
require(msg.sender == bProtocolOwner, "Ownable: caller is not the B.Protocol owner");
_;
}
function setTHUSD2UsdPriceAggregator(
address _thusd2UsdPriceAggregator
) external onlyOwner {
require(address(thusd2UsdPriceAggregator) == address(0), "set: price aggregator already set");
checkContract(_thusd2UsdPriceAggregator);
thusd2UsdPriceAggregator = AggregatorV3Interface(_thusd2UsdPriceAggregator);
}
function setParams(uint256 _A, uint256 _fee) external onlyBProtocolOwner {
require(_fee <= MAX_FEE, "setParams: fee is too big");
require(_A >= MIN_A, "setParams: A too small");
require(_A <= MAX_A, "setParams: A too big");
fee = _fee;
A = _A;
emit ParamsSet(_A, _fee);
}
function fetchPrice() public view returns(uint256) {
(uint256 chainlinkLatestAnswer, uint256 chainlinkDecimals) = fetchChainlink(priceAggregator);
uint256 chainlinkFactor = 10 ** chainlinkDecimals;
return chainlinkLatestAnswer * PRECISION / chainlinkFactor;
}
function fetchChainlink(AggregatorV3Interface aggregator) internal view returns(uint256, uint256) {
uint256 chainlinkDecimals;
uint256 chainlinkLatestAnswer;
uint256 chainlinkTimestamp;
try aggregator.decimals() returns (uint8 _chainlinkDecimals) {
chainlinkDecimals = _chainlinkDecimals;
} catch {
return (0, 0);
}
try aggregator.latestRoundData() returns
(
uint80 ,
int256 answer,
uint256 ,
uint256 timestamp,
uint80
)
{
if (answer < 0) {
return (0, 0);
}
chainlinkLatestAnswer = uint256(answer);
chainlinkTimestamp = timestamp;
} catch {
return (0, 0);
}
if(chainlinkTimestamp + 1 hours < block.timestamp) return (0, 0);
return (chainlinkLatestAnswer, chainlinkDecimals);
}
function getCollateralBalance() public view returns (uint256 collateralValue) {
collateralValue = SP.getDepositorCollateralGain(address(this));
if (address(collateralERC20) == address(0)) {
collateralValue += address(this).balance;
} else {
collateralValue += collateralERC20.balanceOf(address(this));
}
}
function deposit(uint256 thusdAmount) external {
uint256 thusdValue = SP.getCompoundedTHUSDDeposit(address(this));
uint256 collateralValue = getCollateralBalance();
uint256 price = fetchPrice();
require(collateralValue == 0 || price > 0, "deposit: chainlink is down");
uint256 totalValue = thusdValue + collateralValue * price / PRECISION;
require(totalValue > 0 || total == 0, "deposit: system is rekt");
uint256 newShare = thusdAmount._toShares(total, totalValue, true);
require(thusdToken.transferFrom(msg.sender, address(this), thusdAmount), "deposit: transferFrom failed");
thusdToken.increaseAllowance(address(SP), thusdAmount);
SP.provideToSP(thusdAmount);
mint(msg.sender, newShare);
emit UserDeposit(msg.sender, thusdAmount, newShare);
}
function withdraw(uint256 numShares) external {
uint256 thusdValue = SP.getCompoundedTHUSDDeposit(address(this));
uint256 collateralValue = getCollateralBalance();
uint256 thusdAmount = numShares._toAmount(total, thusdValue, true);
uint256 collateralAmount = numShares._toAmount(total, collateralValue, false);
SP.withdrawFromSP(thusdAmount);
burn(msg.sender, numShares);
if(thusdAmount > 0) thusdToken.transfer(msg.sender, thusdAmount);
emit UserWithdraw(msg.sender, thusdAmount, collateralAmount, numShares);
if(collateralAmount == 0) {
return;
}
sendCollateral(collateralERC20, msg.sender, collateralAmount);
}
function addBps(uint256 n, int bps) internal pure returns(uint) {
require(bps <= 10000, "reduceBps: bps exceeds max");
require(bps >= -10000, "reduceBps: bps exceeds min");
return n * uint256(10000 + bps) / 10000;
}
function compensateForTHUSDDeviation(uint256 collateralAmount) public view returns(uint256 newCollateralAmount) {
if (address(thusd2UsdPriceAggregator) == address(0)) {
return collateralAmount;
}
(uint256 chainlinkLatestAnswer, uint256 chainlinkDecimals) = fetchChainlink(thusd2UsdPriceAggregator);
if (chainlinkLatestAnswer == 0) {
return collateralAmount;
}
if(chainlinkLatestAnswer > 10 ** chainlinkDecimals ) {
newCollateralAmount = collateralAmount * chainlinkLatestAnswer / (10 ** chainlinkDecimals);
}
else newCollateralAmount = collateralAmount;
}
function getSwapCollateralAmount(uint256 thusdQty) public view returns(uint256 collateralAmount, uint256 feeTHUSDAmount) {
uint256 thusdBalance = SP.getCompoundedTHUSDDeposit(address(this));
uint256 collateralBalance = getCollateralBalance();
uint256 collateral2usdPrice = fetchPrice();
if(collateral2usdPrice == 0) return (0, 0);
uint256 collateralUsdValue = collateralBalance * collateral2usdPrice / PRECISION;
uint256 maxReturn = addBps(thusdQty * PRECISION / collateral2usdPrice, int(maxDiscount));
uint256 xQty = thusdQty;
uint256 xBalance = thusdBalance;
uint256 yBalance = thusdBalance + (collateralUsdValue * 2);
uint256 usdReturn = getReturn(xQty, xBalance, yBalance, A);
uint256 basicCollateralReturn = usdReturn * PRECISION / collateral2usdPrice;
basicCollateralReturn = compensateForTHUSDDeviation(basicCollateralReturn);
if(collateralBalance < basicCollateralReturn) basicCollateralReturn = collateralBalance;
if(maxReturn < basicCollateralReturn) basicCollateralReturn = maxReturn;
collateralAmount = basicCollateralReturn;
feeTHUSDAmount = addBps(thusdQty, int(fee)) - thusdQty;
}
function swap(uint256 thusdAmount, uint256 minCollateralReturn, address payable dest) public returns(uint) {
require(minCollateralReturn > 0, "swap: min return must not be zero");
(uint256 collateralAmount, uint256 feeAmount) = getSwapCollateralAmount(thusdAmount);
require(collateralAmount >= minCollateralReturn, "swap: low return");
thusdToken.transferFrom(msg.sender, address(this), thusdAmount);
thusdToken.increaseAllowance(address(SP), thusdAmount - feeAmount);
SP.provideToSP(thusdAmount - feeAmount);
if(feeAmount > 0) thusdToken.transfer(feePool, feeAmount);
sendCollateral(collateralERC20, dest, collateralAmount);
emit RebalanceSwap(msg.sender, thusdAmount, collateralAmount, block.timestamp);
return collateralAmount;
}
function trade(
IERC20 ,
uint256 srcAmount,
IERC20 ,
address payable destAddress,
uint256 ,
bool
) external payable returns (bool) {
return swap(srcAmount, 1, destAddress) > 0;
}
function getConversionRate(
IERC20 ,
IERC20 ,
uint256 srcQty,
uint256
) external view returns (uint256) {
(uint256 collateralQty, ) = getSwapCollateralAmount(srcQty);
return collateralQty * PRECISION / srcQty;
}
receive() external payable {
require(address(collateralERC20) == address(0), "ERC20 collateral needed, not ETH");
}
function transferBProtocolOwnership(address newOwner) public onlyBProtocolOwner {
require(newOwner != address(0), "Ownable: new B.Protocol owner is the zero address");
address oldOwner = bProtocolOwner;
bProtocolOwner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 4 的 29:BaseMath.sol
pragma solidity ^0.8.17;
contract BaseMath {
uint256 constant public DECIMAL_PRECISION = 1e18;
}
文件 5 的 29:CheckContract.sol
pragma solidity ^0.8.17;
contract CheckContract {
function checkContract(address _account) internal view {
require(_account != address(0), "Account cannot be zero address");
uint256 size;
assembly { size := extcodesize(_account) }
require(size > 0, "Account code size cannot be zero");
}
}
文件 6 的 29:CropJoinAdapter.sol
pragma solidity ^0.8.17;
contract CropJoinAdapter {
string constant public name = "B.AMM THUSD-COLLATERAL";
string constant public symbol = "THUSDCOLL";
uint256 constant public decimals = 18;
uint256 internal total;
mapping (address => uint256) public stake;
event Join(uint256 val);
event Exit(uint256 val);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
function totalSupply() public view returns (uint256) {
return total;
}
function balanceOf(address owner) public view returns (uint256 balance) {
balance = stake[owner];
}
function mint(address to, uint256 value) virtual internal {
if (value > 0) {
total += value;
stake[to] += value;
}
emit Join(value);
emit Transfer(address(0), to, value);
}
function burn(address owner, uint256 value) virtual internal {
if (value > 0) {
total -= value;
stake[owner] -= value;
}
emit Exit(value);
emit Transfer(owner, address(0), value);
}
}
文件 7 的 29:IActivePool.sol
pragma solidity ^0.8.17;
import "./IPool.sol";
interface IActivePool is IPool {
event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event ActivePoolTHUSDDebtUpdated(uint256 _THUSDDebt);
event ActivePoolCollateralBalanceUpdated(uint256 _collateral);
event CollateralAddressChanged(address _newCollateralAddress);
event CollSurplusPoolAddressChanged(address _newCollSurplusPoolAddress);
function sendCollateral(address _account, uint256 _amount) external;
function updateCollateralBalance(uint256 _amount) external;
function collateralAddress() external view returns(address);
}
文件 8 的 29:IBorrowerOperations.sol
pragma solidity ^0.8.17;
interface IBorrowerOperations {
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event ActivePoolAddressChanged(address _activePoolAddress);
event DefaultPoolAddressChanged(address _defaultPoolAddress);
event StabilityPoolAddressChanged(address _stabilityPoolAddress);
event GasPoolAddressChanged(address _gasPoolAddress);
event CollSurplusPoolAddressChanged(address _collSurplusPoolAddress);
event PriceFeedAddressChanged(address _newPriceFeedAddress);
event SortedTrovesAddressChanged(address _sortedTrovesAddress);
event THUSDTokenAddressChanged(address _thusdTokenAddress);
event PCVAddressChanged(address _pcvAddress);
event CollateralAddressChanged(address _newCollateralAddress);
event TroveCreated(address indexed _borrower, uint256 arrayIndex);
event TroveUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 stake, uint8 operation);
event THUSDBorrowingFeePaid(address indexed _borrower, uint256 _THUSDFee);
function setAddresses(
address _troveManagerAddress,
address _activePoolAddress,
address _defaultPoolAddress,
address _stabilityPoolAddress,
address _gasPoolAddress,
address _collSurplusPoolAddress,
address _priceFeedAddress,
address _sortedTrovesAddress,
address _thusdTokenAddress,
address _pcvAddress,
address _collateralAddress
) external;
function openTrove(uint256 _maxFee, uint256 _THUSDAmount, uint256 _assetAmount, address _upperHint, address _lowerHint) external payable;
function addColl(uint256 _assetAmount, address _upperHint, address _lowerHint) external payable;
function moveCollateralGainToTrove(address _user, uint256 _assetAmount, address _upperHint, address _lowerHint) external payable;
function withdrawColl(uint256 _amount, address _upperHint, address _lowerHint) external;
function withdrawTHUSD(uint256 _maxFee, uint256 _amount, address _upperHint, address _lowerHint) external;
function repayTHUSD(uint256 _amount, address _upperHint, address _lowerHint) external;
function closeTrove() external;
function adjustTrove(uint256 _maxFee, uint256 _collWithdrawal, uint256 _debtChange, bool isDebtIncrease, uint256 _assetAmount, address _upperHint, address _lowerHint) external payable;
function claimCollateral() external;
function getCompositeDebt(uint256 _debt) external pure returns (uint);
function collateralAddress() external view returns(address);
}
文件 9 的 29:IDefaultPool.sol
pragma solidity ^0.8.17;
import "./IPool.sol";
interface IDefaultPool is IPool {
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event DefaultPoolTHUSDDebtUpdated(uint256 _THUSDDebt);
event DefaultPoolCollateralBalanceUpdated(uint256 _collateral);
event CollateralAddressChanged(address _newCollateralAddress);
function sendCollateralToActivePool(uint256 _amount) external;
function updateCollateralBalance(uint256 _amount) external;
function collateralAddress() external view returns(address);
}
文件 10 的 29:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
文件 11 的 29:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 12 的 29:IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 13 的 29:IERC2612.sol
pragma solidity ^0.8.17;
interface IERC2612 {
function permit(address owner, address spender, uint256 amount,
uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
function nonces(address owner) external view returns (uint256);
function version() external view returns (string memory);
function permitTypeHash() external view returns (bytes32);
function domainSeparator() external view returns (bytes32);
}
文件 14 的 29:ILiquityBase.sol
pragma solidity ^0.8.17;
import "./IPriceFeed.sol";
interface ILiquityBase {
function priceFeed() external view returns (IPriceFeed);
}
文件 15 的 29:IPCV.sol
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ITHUSDToken.sol";
interface IPCV {
event THUSDTokenAddressSet(address _thusdTokenAddress);
event BorrowerOperationsAddressSet(address _borrowerOperationsAddress);
event CollateralAddressSet(address _collateralAddress);
event BAMMAddressSet(address _bammAddress);
event RolesSet(address _council, address _treasury);
event BAMMDeposit(uint256 _thusdAmount);
event BAMMWithdraw(uint256 _numShares);
event THUSDWithdraw(address _recipient, uint256 _thusdAmount);
event CollateralWithdraw(address _recipient, uint256 _collateralAmount);
event PCVDebtPaid(uint256 _paidDebt);
event RecipientAdded(address _recipient);
event RecipientRemoved(address _recipient);
function debtToPay() external returns(uint256);
function payDebt(uint256 _thusdToBurn) external;
function setAddresses(
address _thusdTokenAddress,
address _borrowerOperations,
address payable _bammAddress,
address _collateralERC20
) external;
function initialize() external;
function depositToBAMM(uint256 _thusdAmount) external;
function withdrawFromBAMM(uint256 _numShares) external;
function withdrawTHUSD(address _recipient, uint256 _thusdAmount) external;
function withdrawCollateral(address _recipient, uint256 _collateralAmount) external;
function addRecipientToWhitelist(address _recipient) external;
function addRecipientsToWhitelist(address[] calldata _recipients) external;
function removeRecipientFromWhitelist(address _recipient) external;
function removeRecipientsFromWhitelist(address[] calldata _recipients) external;
function startChangingRoles(address _council, address _treasury) external;
function cancelChangingRoles() external;
function finalizeChangingRoles() external;
function collateralERC20() external view returns(IERC20);
function thusdToken() external view returns(ITHUSDToken);
}
文件 16 的 29:IPool.sol
pragma solidity ^0.8.17;
interface IPool {
event CollateralBalanceUpdated(uint256 _newBalance);
event THUSDBalanceUpdated(uint256 _newBalance);
event ActivePoolAddressChanged(address _newActivePoolAddress);
event DefaultPoolAddressChanged(address _newDefaultPoolAddress);
event StabilityPoolAddressChanged(address _newStabilityPoolAddress);
event CollateralSent(address _to, uint256 _amount);
function getCollateralBalance() external view returns (uint);
function getTHUSDDebt() external view returns (uint);
function increaseTHUSDDebt(uint256 _amount) external;
function decreaseTHUSDDebt(uint256 _amount) external;
}
文件 17 的 29:IPriceFeed.sol
pragma solidity ^0.8.17;
interface IPriceFeed {
event LastGoodPriceUpdated(uint256 _lastGoodPrice);
function fetchPrice() external returns (uint);
}
文件 18 的 29:ISortedTroves.sol
pragma solidity ^0.8.17;
interface ISortedTroves {
event SortedTrovesAddressChanged(address _sortedDoublyLLAddress);
event BorrowerOperationsAddressChanged(address _borrowerOperationsAddress);
event NodeAdded(address _id, uint256 _NICR);
event NodeRemoved(address _id);
function setParams(uint256 _size, address _TroveManagerAddress, address _borrowerOperationsAddress) external;
function insert(address _id, uint256 _ICR, address _prevId, address _nextId) external;
function remove(address _id) external;
function reInsert(address _id, uint256 _newICR, address _prevId, address _nextId) external;
function contains(address _id) external view returns (bool);
function isFull() external view returns (bool);
function isEmpty() external view returns (bool);
function getSize() external view returns (uint256);
function getMaxSize() external view returns (uint256);
function getFirst() external view returns (address);
function getLast() external view returns (address);
function getNext(address _id) external view returns (address);
function getPrev(address _id) external view returns (address);
function validInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (bool);
function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address);
}
文件 19 的 29:IStabilityPool.sol
pragma solidity ^0.8.17;
interface IStabilityPool {
event StabilityPoolCollateralBalanceUpdated(uint256 _newBalance);
event StabilityPoolTHUSDBalanceUpdated(uint256 _newBalance);
event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
event TroveManagerAddressChanged(address _newTroveManagerAddress);
event ActivePoolAddressChanged(address _newActivePoolAddress);
event DefaultPoolAddressChanged(address _newDefaultPoolAddress);
event THUSDTokenAddressChanged(address _newTHUSDTokenAddress);
event SortedTrovesAddressChanged(address _newSortedTrovesAddress);
event PriceFeedAddressChanged(address _newPriceFeedAddress);
event CollateralAddressChanged(address _newCollateralAddress);
event P_Updated(uint256 _P);
event S_Updated(uint256 _S, uint128 _epoch, uint128 _scale);
event EpochUpdated(uint128 _currentEpoch);
event ScaleUpdated(uint128 _currentScale);
event DepositSnapshotUpdated(address indexed _depositor, uint256 _P, uint256 _S);
event UserDepositChanged(address indexed _depositor, uint256 _newDeposit);
event CollateralGainWithdrawn(address indexed _depositor, uint256 _collateral, uint256 _THUSDLoss);
event CollateralSent(address _to, uint256 _amount);
function setAddresses(
address _borrowerOperationsAddress,
address _troveManagerAddress,
address _activePoolAddress,
address _thusdTokenAddress,
address _sortedTrovesAddress,
address _priceFeedAddress,
address _collateralAddress
) external;
function provideToSP(uint256 _amount) external;
function withdrawFromSP(uint256 _amount) external;
function withdrawCollateralGainToTrove(address _upperHint, address _lowerHint) external;
function offset(uint256 _debt, uint256 _coll) external;
function getCollateralBalance() external view returns (uint);
function getTotalTHUSDDeposits() external view returns (uint);
function getDepositorCollateralGain(address _depositor) external view returns (uint);
function getCompoundedTHUSDDeposit(address _depositor) external view returns (uint);
function updateCollateralBalance(uint256 _amount) external;
function collateralAddress() external view returns(address);
}
文件 20 的 29:ITHUSDToken.sol
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../Dependencies/IERC2612.sol";
interface ITHUSDToken is IERC20Metadata, IERC2612 {
event TroveManagerAddressAdded(address _troveManagerAddress);
event StabilityPoolAddressAdded(address _newStabilityPoolAddress);
event BorrowerOperationsAddressAdded(address _newBorrowerOperationsAddress);
event THUSDTokenBalanceUpdated(address _user, uint256 _amount);
function mintList(address contractAddress) external view returns (bool);
function burnList(address contractAddress) external view returns (bool);
function mint(address _account, uint256 _amount) external;
function burn(address _account, uint256 _amount) external;
function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
}
文件 21 的 29:ITroveManager.sol
pragma solidity ^0.8.17;
import "./ILiquityBase.sol";
import "./IStabilityPool.sol";
import "./ITHUSDToken.sol";
import "./IPCV.sol";
interface ITroveManager is ILiquityBase {
enum Status {
nonExistent,
active,
closedByOwner,
closedByLiquidation,
closedByRedemption
}
event BorrowerOperationsAddressChanged(address _newBorrowerOperationsAddress);
event PriceFeedAddressChanged(address _newPriceFeedAddress);
event THUSDTokenAddressChanged(address _newTHUSDTokenAddress);
event ActivePoolAddressChanged(address _activePoolAddress);
event DefaultPoolAddressChanged(address _defaultPoolAddress);
event StabilityPoolAddressChanged(address _stabilityPoolAddress);
event GasPoolAddressChanged(address _gasPoolAddress);
event CollSurplusPoolAddressChanged(address _collSurplusPoolAddress);
event SortedTrovesAddressChanged(address _sortedTrovesAddress);
event PCVAddressChanged(address _pcvAddress);
event Liquidation(uint256 _liquidatedDebt, uint256 _liquidatedColl, uint256 _collGasCompensation, uint256 _THUSDGasCompensation);
event Redemption(uint256 _attemptedTHUSDAmount, uint256 _actualTHUSDAmount, uint256 _collateralSent, uint256 _collateralFee);
event TroveUpdated(address indexed _borrower, uint256 _debt, uint256 _coll, uint256 stake, uint8 operation);
event TroveLiquidated(address indexed _borrower, uint256 _debt, uint256 _coll, uint8 operation);
event BaseRateUpdated(uint256 _baseRate);
event LastFeeOpTimeUpdated(uint256 _lastFeeOpTime);
event TotalStakesUpdated(uint256 _newTotalStakes);
event SystemSnapshotsUpdated(uint256 _totalStakesSnapshot, uint256 _totalCollateralSnapshot);
event LTermsUpdated(uint256 _L_Collateral, uint256 _L_THUSDDebt);
event TroveSnapshotsUpdated(uint256 _L_Collateral, uint256 _L_THUSDDebt);
event TroveIndexUpdated(address _borrower, uint256 _newIndex);
function setAddresses(
address _borrowerOperationsAddress,
address _activePoolAddress,
address _defaultPoolAddress,
address _stabilityPoolAddress,
address _gasPoolAddress,
address _collSurplusPoolAddress,
address _priceFeedAddress,
address _thusdTokenAddress,
address _sortedTrovesAddress,
address _pcvAddress
) external;
function stabilityPool() external view returns (IStabilityPool);
function thusdToken() external view returns (ITHUSDToken);
function pcv() external view returns (IPCV);
function getTroveOwnersCount() external view returns (uint);
function getTroveFromTroveOwnersArray(uint256 _index) external view returns (address);
function getNominalICR(address _borrower) external view returns (uint);
function getCurrentICR(address _borrower, uint256 _price) external view returns (uint);
function liquidate(address _borrower) external;
function liquidateTroves(uint256 _n) external;
function batchLiquidateTroves(address[] calldata _troveArray) external;
function redeemCollateral(
uint256 _THUSDAmount,
address _firstRedemptionHint,
address _upperPartialRedemptionHint,
address _lowerPartialRedemptionHint,
uint256 _partialRedemptionHintNICR,
uint256 _maxIterations,
uint256 _maxFee
) external;
function updateStakeAndTotalStakes(address _borrower) external returns (uint);
function updateTroveRewardSnapshots(address _borrower) external;
function addTroveOwnerToArray(address _borrower) external returns (uint256 index);
function applyPendingRewards(address _borrower) external;
function getPendingCollateralReward(address _borrower) external view returns (uint);
function getPendingTHUSDDebtReward(address _borrower) external view returns (uint);
function hasPendingRewards(address _borrower) external view returns (bool);
function getEntireDebtAndColl(address _borrower) external view returns (
uint256 debt,
uint256 coll,
uint256 pendingTHUSDDebtReward,
uint256 pendingCollateralReward
);
function closeTrove(address _borrower) external;
function removeStake(address _borrower) external;
function getRedemptionRate() external view returns (uint);
function getRedemptionRateWithDecay() external view returns (uint);
function getRedemptionFeeWithDecay(uint256 _collateralDrawn) external view returns (uint);
function getBorrowingRate() external view returns (uint);
function getBorrowingRateWithDecay() external view returns (uint);
function getBorrowingFee(uint256 THUSDDebt) external view returns (uint);
function getBorrowingFeeWithDecay(uint256 _THUSDDebt) external view returns (uint);
function decayBaseRateFromBorrowing() external;
function getTroveStatus(address _borrower) external view returns (Status);
function getTroveStake(address _borrower) external view returns (uint);
function getTroveDebt(address _borrower) external view returns (uint);
function getTroveColl(address _borrower) external view returns (uint);
function setTroveStatus(address _borrower, Status _status) external;
function increaseTroveColl(address _borrower, uint256 _collIncrease) external returns (uint);
function decreaseTroveColl(address _borrower, uint256 _collDecrease) external returns (uint);
function increaseTroveDebt(address _borrower, uint256 _debtIncrease) external returns (uint);
function decreaseTroveDebt(address _borrower, uint256 _collDecrease) external returns (uint);
function getTCR(uint256 _price) external view returns (uint);
function checkRecoveryMode(uint256 _price) external view returns (bool);
}
文件 22 的 29:LiquityBase.sol
pragma solidity ^0.8.17;
import "./BaseMath.sol";
import "./LiquityMath.sol";
import "../Interfaces/IActivePool.sol";
import "../Interfaces/IDefaultPool.sol";
import "../Interfaces/IPriceFeed.sol";
import "../Interfaces/ILiquityBase.sol";
contract LiquityBase is BaseMath, ILiquityBase {
uint256 constant public _100pct = 1e18;
uint256 constant public MCR = 1.1e18;
uint256 constant public CCR = 1.5e18;
uint256 constant public THUSD_GAS_COMPENSATION = 200e18;
uint256 constant public MIN_NET_DEBT = 1800e18;
uint256 constant public PERCENT_DIVISOR = 200;
uint256 constant public BORROWING_FEE_FLOOR = DECIMAL_PRECISION / 1000 * 5;
IActivePool public activePool;
IDefaultPool public defaultPool;
IPriceFeed public override priceFeed;
function _getCompositeDebt(uint256 _debt) internal pure returns (uint) {
return _debt + THUSD_GAS_COMPENSATION;
}
function _getNetDebt(uint256 _debt) internal pure returns (uint) {
return _debt - THUSD_GAS_COMPENSATION;
}
function _getCollGasCompensation(uint256 _entireColl) internal pure returns (uint) {
return _entireColl / PERCENT_DIVISOR;
}
function getEntireSystemColl() public view returns (uint256 entireSystemColl) {
uint256 activeColl = activePool.getCollateralBalance();
uint256 liquidatedColl = defaultPool.getCollateralBalance();
return activeColl + liquidatedColl;
}
function getEntireSystemDebt() public view returns (uint256 entireSystemDebt) {
uint256 activeDebt = activePool.getTHUSDDebt();
uint256 closedDebt = defaultPool.getTHUSDDebt();
return activeDebt + closedDebt;
}
function _getTCR(uint256 _price) internal view returns (uint256 TCR) {
uint256 entireSystemColl = getEntireSystemColl();
uint256 entireSystemDebt = getEntireSystemDebt();
TCR = LiquityMath._computeCR(entireSystemColl, entireSystemDebt, _price);
return TCR;
}
function _checkRecoveryMode(uint256 _price) internal view returns (bool) {
uint256 TCR = _getTCR(_price);
return TCR < CCR;
}
function _requireUserAcceptsFee(uint256 _fee, uint256 _amount, uint256 _maxFeePercentage) internal pure {
uint256 feePercentage = _fee * DECIMAL_PRECISION / _amount;
require(feePercentage <= _maxFeePercentage, "Fee exceeded provided maximum");
}
}
文件 23 的 29:LiquityMath.sol
pragma solidity ^0.8.17;
library LiquityMath {
uint256 internal constant DECIMAL_PRECISION = 1e18;
uint256 internal constant NICR_PRECISION = 1e20;
function _min(uint256 _a, uint256 _b) internal pure returns (uint) {
return (_a < _b) ? _a : _b;
}
function _max(uint256 _a, uint256 _b) internal pure returns (uint) {
return (_a >= _b) ? _a : _b;
}
function decMul(uint256 x, uint256 y) internal pure returns (uint256 decProd) {
uint256 prod_xy = x * y;
decProd = (prod_xy + (DECIMAL_PRECISION / 2)) / DECIMAL_PRECISION;
}
function _decPow(uint256 _base, uint256 _minutes) internal pure returns (uint) {
if (_minutes > 525600000) {_minutes = 525600000;}
if (_minutes == 0) {return DECIMAL_PRECISION;}
uint256 y = DECIMAL_PRECISION;
uint256 x = _base;
uint256 n = _minutes;
while (n > 1) {
if (n % 2 == 0) {
x = decMul(x, x);
n = n / 2;
} else {
y = decMul(x, y);
x = decMul(x, x);
n = (n - 1) / 2;
}
}
return decMul(x, y);
}
function _getAbsoluteDifference(uint256 _a, uint256 _b) internal pure returns (uint) {
return (_a >= _b) ? _a - _b : _b - _a;
}
function _computeNominalCR(uint256 _coll, uint256 _debt) internal pure returns (uint) {
if (_debt > 0) {
return _coll * NICR_PRECISION / _debt;
}
else {
return type(uint256).max;
}
}
function _computeCR(uint256 _coll, uint256 _debt, uint256 _price) internal pure returns (uint) {
if (_debt > 0) {
uint256 newCollRatio = _coll * _price / _debt;
return newCollRatio;
}
else {
return type(uint256).max;
}
}
}
文件 24 的 29:Ownable.sol
pragma solidity ^0.8.17;
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () {
_owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
function _renounceOwnership() internal {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 25 的 29:PriceFormula.sol
pragma solidity ^0.8.17;
contract PriceFormula {
function getSumFixedPoint(uint256 x, uint256 y, uint256 A) public pure returns(uint) {
if(x == 0 && y == 0) return 0;
uint256 sum = x + y;
for(uint256 i = 0 ; i < 255 ; i++) {
uint256 dP = sum;
dP = dP * sum / ((x * 2) + 1);
dP = dP * sum / ((y * 2) + 1);
uint256 prevSum = sum;
uint256 n = (A * 2 * (x + y) + (dP * 2)) * sum;
uint256 d = (A * 2 - 1) * sum;
sum = n / (d + dP * 3);
if(sum <= prevSum + 1 && prevSum <= sum + 1) break;
}
return sum;
}
function getReturn(uint256 xQty, uint256 xBalance, uint256 yBalance, uint256 A) public pure returns(uint256) {
uint256 sum = getSumFixedPoint(xBalance, yBalance, A);
uint256 c = sum * sum / ((xQty + xBalance) * 2);
c = c * sum / (A * 4);
uint256 b = (xQty + xBalance) + (sum / (A * 2));
uint256 yPrev = 0;
uint256 y = sum;
for(uint256 i = 0 ; i < 255 ; i++) {
yPrev = y;
uint256 n = y * y + c;
uint256 d = y * 2 + b - sum;
y = n / d;
if(y <= yPrev + 1 && yPrev <= y + 1) break;
}
return yBalance - y - 1;
}
}
文件 26 的 29:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
文件 27 的 29:SendCollateral.sol
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract SendCollateral {
using SafeERC20 for IERC20;
function sendCollateral(IERC20 _collateralERC20, address _recipient, uint256 _amount) internal {
if (address(_collateralERC20) == address(0)) {
(bool success, ) = _recipient.call{ value: _amount }("");
require(success, "Sending ETH failed");
} else {
_collateralERC20.safeTransfer(_recipient, _amount);
}
}
function sendCollateralFrom(IERC20 _collateralERC20, address _from, address _recipient, uint256 _amount) internal {
if (address(_collateralERC20) == address(0)) {
(bool success, ) = _recipient.call{ value: _amount }("");
require(success, "Sending ETH failed");
} else {
_collateralERC20.safeTransferFrom(_from, _recipient, _amount);
}
}
}
文件 28 的 29:StabilityPool.sol
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import './Interfaces/IBorrowerOperations.sol';
import './Interfaces/IStabilityPool.sol';
import './Interfaces/IBorrowerOperations.sol';
import './Interfaces/ITroveManager.sol';
import './Interfaces/ITHUSDToken.sol';
import './Interfaces/ISortedTroves.sol';
import "./Dependencies/LiquityBase.sol";
import "./Dependencies/Ownable.sol";
import "./Dependencies/CheckContract.sol";
import "./Dependencies/SendCollateral.sol";
contract StabilityPool is LiquityBase, Ownable, CheckContract, SendCollateral, IStabilityPool {
string constant public NAME = "StabilityPool";
address public collateralAddress;
IBorrowerOperations public borrowerOperations;
ITroveManager public troveManager;
ITHUSDToken public thusdToken;
ISortedTroves public sortedTroves;
uint256 internal collateral;
uint256 internal totalTHUSDDeposits;
struct Snapshots {
uint256 S;
uint256 P;
uint128 scale;
uint128 epoch;
}
mapping (address => uint256) public deposits;
mapping (address => Snapshots) public depositSnapshots;
uint256 public P = DECIMAL_PRECISION;
uint256 public constant SCALE_FACTOR = 1e9;
uint128 public currentScale;
uint128 public currentEpoch;
mapping (uint128 => mapping(uint128 => uint)) public epochToScaleToSum;
uint256 public lastCollateralError_Offset;
uint256 public lastTHUSDLossError_Offset;
function setAddresses(
address _borrowerOperationsAddress,
address _troveManagerAddress,
address _activePoolAddress,
address _thusdTokenAddress,
address _sortedTrovesAddress,
address _priceFeedAddress,
address _collateralAddress
)
external
override
onlyOwner
{
checkContract(_borrowerOperationsAddress);
checkContract(_troveManagerAddress);
checkContract(_activePoolAddress);
checkContract(_thusdTokenAddress);
checkContract(_sortedTrovesAddress);
checkContract(_priceFeedAddress);
if (_collateralAddress != address(0)) {
checkContract(_collateralAddress);
}
borrowerOperations = IBorrowerOperations(_borrowerOperationsAddress);
troveManager = ITroveManager(_troveManagerAddress);
activePool = IActivePool(_activePoolAddress);
thusdToken = ITHUSDToken(_thusdTokenAddress);
sortedTroves = ISortedTroves(_sortedTrovesAddress);
priceFeed = IPriceFeed(_priceFeedAddress);
collateralAddress = _collateralAddress;
require(
(Ownable(_borrowerOperationsAddress).owner() != address(0) ||
borrowerOperations.collateralAddress() == _collateralAddress) &&
(Ownable(_activePoolAddress).owner() != address(0) ||
activePool.collateralAddress() == _collateralAddress),
"The same collateral address must be used for the entire set of contracts"
);
emit BorrowerOperationsAddressChanged(_borrowerOperationsAddress);
emit TroveManagerAddressChanged(_troveManagerAddress);
emit ActivePoolAddressChanged(_activePoolAddress);
emit THUSDTokenAddressChanged(_thusdTokenAddress);
emit SortedTrovesAddressChanged(_sortedTrovesAddress);
emit PriceFeedAddressChanged(_priceFeedAddress);
emit CollateralAddressChanged(_collateralAddress);
_renounceOwnership();
}
function getCollateralBalance() external view override returns (uint) {
return collateral;
}
function getTotalTHUSDDeposits() external view override returns (uint) {
return totalTHUSDDeposits;
}
function provideToSP(uint256 _amount) external override {
_requireNonZeroAmount(_amount);
uint256 initialDeposit = deposits[msg.sender];
uint256 depositorCollateralGain = getDepositorCollateralGain(msg.sender);
uint256 compoundedTHUSDDeposit = getCompoundedTHUSDDeposit(msg.sender);
uint256 THUSDLoss = initialDeposit - compoundedTHUSDDeposit;
_sendTHUSDtoStabilityPool(msg.sender, _amount);
uint256 newDeposit = compoundedTHUSDDeposit + _amount;
_updateDepositAndSnapshots(msg.sender, newDeposit);
emit UserDepositChanged(msg.sender, newDeposit);
emit CollateralGainWithdrawn(msg.sender, depositorCollateralGain, THUSDLoss);
_sendCollateralGainToDepositor(depositorCollateralGain);
}
function withdrawFromSP(uint256 _amount) external override {
if (_amount !=0) {_requireNoUnderCollateralizedTroves();}
uint256 initialDeposit = deposits[msg.sender];
_requireUserHasDeposit(initialDeposit);
uint256 depositorCollateralGain = getDepositorCollateralGain(msg.sender);
uint256 compoundedTHUSDDeposit = getCompoundedTHUSDDeposit(msg.sender);
uint256 THUSDtoWithdraw = LiquityMath._min(_amount, compoundedTHUSDDeposit);
uint256 THUSDLoss = initialDeposit - compoundedTHUSDDeposit;
_sendTHUSDToDepositor(msg.sender, THUSDtoWithdraw);
uint256 newDeposit = compoundedTHUSDDeposit - THUSDtoWithdraw;
_updateDepositAndSnapshots(msg.sender, newDeposit);
emit UserDepositChanged(msg.sender, newDeposit);
emit CollateralGainWithdrawn(msg.sender, depositorCollateralGain, THUSDLoss);
_sendCollateralGainToDepositor(depositorCollateralGain);
}
function withdrawCollateralGainToTrove(address _upperHint, address _lowerHint) external override {
uint256 initialDeposit = deposits[msg.sender];
_requireUserHasDeposit(initialDeposit);
_requireUserHasTrove(msg.sender);
_requireUserHasCollateralGain(msg.sender);
uint256 depositorCollateralGain = getDepositorCollateralGain(msg.sender);
uint256 compoundedTHUSDDeposit = getCompoundedTHUSDDeposit(msg.sender);
uint256 THUSDLoss = initialDeposit - compoundedTHUSDDeposit;
_updateDepositAndSnapshots(msg.sender, compoundedTHUSDDeposit);
emit CollateralGainWithdrawn(msg.sender, depositorCollateralGain, THUSDLoss);
emit UserDepositChanged(msg.sender, compoundedTHUSDDeposit);
collateral -= depositorCollateralGain;
emit StabilityPoolCollateralBalanceUpdated(collateral);
emit CollateralSent(msg.sender, depositorCollateralGain);
if (collateralAddress == address(0)) {
borrowerOperations.moveCollateralGainToTrove{ value: depositorCollateralGain }(msg.sender, 0, _upperHint, _lowerHint);
} else {
borrowerOperations.moveCollateralGainToTrove{ value: 0 }(msg.sender, depositorCollateralGain, _upperHint, _lowerHint);
}
}
function offset(uint256 _debtToOffset, uint256 _collToAdd) external override {
_requireCallerIsTroveManager();
uint256 totalTHUSD = totalTHUSDDeposits;
if (totalTHUSD == 0 || _debtToOffset == 0) { return; }
(uint256 collateralGainPerUnitStaked,
uint256 THUSDLossPerUnitStaked) = _computeRewardsPerUnitStaked(_collToAdd, _debtToOffset, totalTHUSD);
_updateRewardSumAndProduct(collateralGainPerUnitStaked, THUSDLossPerUnitStaked);
_moveOffsetCollAndDebt(_collToAdd, _debtToOffset);
}
function _computeRewardsPerUnitStaked(
uint256 _collToAdd,
uint256 _debtToOffset,
uint256 _totalTHUSDDeposits
)
internal
returns (uint256 collateralGainPerUnitStaked, uint256 THUSDLossPerUnitStaked)
{
uint256 collateralNumerator = _collToAdd * DECIMAL_PRECISION + lastCollateralError_Offset;
assert(_debtToOffset <= _totalTHUSDDeposits);
if (_debtToOffset == _totalTHUSDDeposits) {
THUSDLossPerUnitStaked = DECIMAL_PRECISION;
lastTHUSDLossError_Offset = 0;
} else {
uint256 THUSDLossNumerator = _debtToOffset * DECIMAL_PRECISION - lastTHUSDLossError_Offset;
THUSDLossPerUnitStaked = THUSDLossNumerator / _totalTHUSDDeposits + 1;
lastTHUSDLossError_Offset = THUSDLossPerUnitStaked * _totalTHUSDDeposits - THUSDLossNumerator;
}
collateralGainPerUnitStaked = collateralNumerator / _totalTHUSDDeposits;
lastCollateralError_Offset = collateralNumerator - (collateralGainPerUnitStaked * _totalTHUSDDeposits);
return (collateralGainPerUnitStaked, THUSDLossPerUnitStaked);
}
function _updateRewardSumAndProduct(uint256 _collateralGainPerUnitStaked, uint256 _THUSDLossPerUnitStaked) internal {
uint256 currentP = P;
uint256 newP;
assert(_THUSDLossPerUnitStaked <= DECIMAL_PRECISION);
uint256 newProductFactor = DECIMAL_PRECISION - _THUSDLossPerUnitStaked;
uint128 currentScaleCached = currentScale;
uint128 currentEpochCached = currentEpoch;
uint256 currentS = epochToScaleToSum[currentEpochCached][currentScaleCached];
uint256 marginalCollateralGain = _collateralGainPerUnitStaked * currentP;
uint256 newS = currentS + marginalCollateralGain;
epochToScaleToSum[currentEpochCached][currentScaleCached] = newS;
emit S_Updated(newS, currentEpochCached, currentScaleCached);
if (newProductFactor == 0) {
currentEpoch = currentEpochCached + 1;
emit EpochUpdated(currentEpoch);
currentScale = 0;
emit ScaleUpdated(currentScale);
newP = DECIMAL_PRECISION;
} else if (currentP * newProductFactor / DECIMAL_PRECISION < SCALE_FACTOR) {
newP = currentP * newProductFactor * SCALE_FACTOR / DECIMAL_PRECISION;
currentScale = currentScaleCached + 1;
emit ScaleUpdated(currentScale);
} else {
newP = currentP * newProductFactor / DECIMAL_PRECISION;
}
assert(newP > 0);
P = newP;
emit P_Updated(newP);
}
function _moveOffsetCollAndDebt(uint256 _collToAdd, uint256 _debtToOffset) internal {
IActivePool activePoolCached = activePool;
activePoolCached.decreaseTHUSDDebt(_debtToOffset);
_decreaseTHUSD(_debtToOffset);
thusdToken.burn(address(this), _debtToOffset);
activePoolCached.sendCollateral(address(this), _collToAdd);
}
function _decreaseTHUSD(uint256 _amount) internal {
uint256 newTotalTHUSDDeposits = totalTHUSDDeposits - _amount;
totalTHUSDDeposits = newTotalTHUSDDeposits;
emit StabilityPoolTHUSDBalanceUpdated(newTotalTHUSDDeposits);
}
function getDepositorCollateralGain(address _depositor) public view override returns (uint) {
uint256 initialDeposit = deposits[_depositor];
if (initialDeposit == 0) { return 0; }
Snapshots memory snapshots = depositSnapshots[_depositor];
uint256 collateralGain = _getCollateralGainFromSnapshots(initialDeposit, snapshots);
return collateralGain;
}
function _getCollateralGainFromSnapshots(uint256 initialDeposit, Snapshots memory snapshots) internal view returns (uint) {
uint128 epochSnapshot = snapshots.epoch;
uint128 scaleSnapshot = snapshots.scale;
uint256 S_Snapshot = snapshots.S;
uint256 P_Snapshot = snapshots.P;
uint256 firstPortion = epochToScaleToSum[epochSnapshot][scaleSnapshot] - S_Snapshot;
uint256 secondPortion = epochToScaleToSum[epochSnapshot][scaleSnapshot + 1] / SCALE_FACTOR;
uint256 collateralGain = initialDeposit * (firstPortion + secondPortion) / P_Snapshot / DECIMAL_PRECISION;
return collateralGain;
}
function getCompoundedTHUSDDeposit(address _depositor) public view override returns (uint) {
uint256 initialDeposit = deposits[_depositor];
if (initialDeposit == 0) { return 0; }
Snapshots memory snapshots = depositSnapshots[_depositor];
uint256 compoundedDeposit = _getCompoundedStakeFromSnapshots(initialDeposit, snapshots);
return compoundedDeposit;
}
function _getCompoundedStakeFromSnapshots(
uint256 initialStake,
Snapshots memory snapshots
)
internal
view
returns (uint)
{
uint256 snapshot_P = snapshots.P;
uint128 scaleSnapshot = snapshots.scale;
uint128 epochSnapshot = snapshots.epoch;
if (epochSnapshot < currentEpoch) { return 0; }
uint256 compoundedStake;
uint128 scaleDiff = currentScale - scaleSnapshot;
if (scaleDiff == 0) {
compoundedStake = initialStake * P / snapshot_P;
} else if (scaleDiff == 1) {
compoundedStake = initialStake * P / snapshot_P / SCALE_FACTOR;
} else {
compoundedStake = 0;
}
if (compoundedStake < initialStake / 1e9) {return 0;}
return compoundedStake;
}
function _sendTHUSDtoStabilityPool(address _address, uint256 _amount) internal {
thusdToken.transferFrom(_address, address(this), _amount);
uint256 newTotalTHUSDDeposits = totalTHUSDDeposits + _amount;
totalTHUSDDeposits = newTotalTHUSDDeposits;
emit StabilityPoolTHUSDBalanceUpdated(newTotalTHUSDDeposits);
}
function _sendCollateralGainToDepositor(uint256 _amount) internal {
if (_amount == 0) {return;}
uint256 newCollateral = collateral - _amount;
collateral = newCollateral;
emit StabilityPoolCollateralBalanceUpdated(newCollateral);
emit CollateralSent(msg.sender, _amount);
sendCollateral(IERC20(collateralAddress), msg.sender, _amount);
}
function _sendTHUSDToDepositor(address _depositor, uint256 THUSDWithdrawal) internal {
if (THUSDWithdrawal == 0) {return;}
thusdToken.transfer(_depositor, THUSDWithdrawal);
_decreaseTHUSD(THUSDWithdrawal);
}
function _updateDepositAndSnapshots(address _depositor, uint256 _newValue) internal {
deposits[_depositor] = _newValue;
if (_newValue == 0) {
delete depositSnapshots[_depositor];
emit DepositSnapshotUpdated(_depositor, 0, 0);
return;
}
uint128 currentScaleCached = currentScale;
uint128 currentEpochCached = currentEpoch;
uint256 currentP = P;
uint256 currentS = epochToScaleToSum[currentEpochCached][currentScaleCached];
depositSnapshots[_depositor].P = currentP;
depositSnapshots[_depositor].S = currentS;
depositSnapshots[_depositor].scale = currentScaleCached;
depositSnapshots[_depositor].epoch = currentEpochCached;
emit DepositSnapshotUpdated(_depositor, currentP, currentS);
}
function _requireCallerIsActivePool() internal view {
require( msg.sender == address(activePool), "StabilityPool: Caller is not ActivePool");
}
function _requireCallerIsTroveManager() internal view {
require(msg.sender == address(troveManager), "StabilityPool: Caller is not TroveManager");
}
function _requireNoUnderCollateralizedTroves() internal {
uint256 price = priceFeed.fetchPrice();
address lowestTrove = sortedTroves.getLast();
uint256 ICR = troveManager.getCurrentICR(lowestTrove, price);
require(ICR >= MCR, "StabilityPool: Cannot withdraw while there are troves with ICR < MCR");
}
function _requireUserHasDeposit(uint256 _initialDeposit) internal pure {
require(_initialDeposit > 0, 'StabilityPool: User must have a non-zero deposit');
}
function _requireNonZeroAmount(uint256 _amount) internal pure {
require(_amount > 0, 'StabilityPool: Amount must be non-zero');
}
function _requireUserHasTrove(address _depositor) internal view {
require(
troveManager.getTroveStatus(_depositor) == ITroveManager.Status.active,
"StabilityPool: caller must have an active trove to withdraw collateralGain to"
);
}
function _requireUserHasCollateralGain(address _depositor) internal view {
uint256 collateralGain = getDepositorCollateralGain(_depositor);
require(collateralGain > 0, "StabilityPool: caller must have non-zero collateral Gain");
}
function updateCollateralBalance(uint256 _amount) external override {
_requireCallerIsActivePool();
collateral += _amount;
emit StabilityPoolCollateralBalanceUpdated(collateral);
}
receive() external payable {
_requireCallerIsActivePool();
collateral += msg.value;
emit StabilityPoolCollateralBalanceUpdated(collateral);
}
}
文件 29 的 29:YieldBoxRebase.sol
pragma solidity ^0.8.17;
library YieldBoxRebase {
function _toShares(
uint256 amount,
uint256 totalShares_,
uint256 totalAmount,
bool roundUp
) internal pure returns (uint256 share) {
totalAmount++;
totalShares_ += 1e8;
share = (amount * totalShares_) / totalAmount;
if (roundUp && (share * totalAmount) / totalShares_ < amount) {
share++;
}
}
function _toAmount(
uint256 share,
uint256 totalShares_,
uint256 totalAmount,
bool roundUp
) internal pure returns (uint256 amount) {
totalAmount++;
totalShares_ += 1e8;
amount = (share * totalAmount) / totalShares_;
if (roundUp && (amount * totalShares_) / totalAmount < share) {
amount++;
}
}
}
{
"compilationTarget": {
"contracts/B.Protocol/BAMM.sol": "BAMM"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 100
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_priceAggregator","type":"address"},{"internalType":"address payable","name":"_SP","type":"address"},{"internalType":"address","name":"_thusdToken","type":"address"},{"internalType":"address","name":"_collateralERC20","type":"address"},{"internalType":"uint256","name":"_maxDiscount","type":"uint256"},{"internalType":"address payable","name":"_feePool","type":"address"},{"internalType":"address","name":"_bProtocolOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"val","type":"uint256"}],"name":"Exit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"val","type":"uint256"}],"name":"Join","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"A","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"ParamsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"thusdAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"RebalanceSwap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"thusdAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"numShares","type":"uint256"}],"name":"UserDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"thusdAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"numShares","type":"uint256"}],"name":"UserWithdraw","type":"event"},{"inputs":[],"name":"A","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_A","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_A","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SP","outputs":[{"internalType":"contract StabilityPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bProtocolOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralERC20","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"compensateForTHUSDDeviation","outputs":[{"internalType":"uint256","name":"newCollateralAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"thusdAmount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feePool","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fetchPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCollateralBalance","outputs":[{"internalType":"uint256","name":"collateralValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"},{"internalType":"contract IERC20","name":"","type":"address"},{"internalType":"uint256","name":"srcQty","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"getConversionRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"xQty","type":"uint256"},{"internalType":"uint256","name":"xBalance","type":"uint256"},{"internalType":"uint256","name":"yBalance","type":"uint256"},{"internalType":"uint256","name":"A","type":"uint256"}],"name":"getReturn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"},{"internalType":"uint256","name":"A","type":"uint256"}],"name":"getSumFixedPoint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"thusdQty","type":"uint256"}],"name":"getSwapCollateralAmount","outputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"uint256","name":"feeTHUSDAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxDiscount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceAggregator","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_A","type":"uint256"},{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_thusd2UsdPriceAggregator","type":"address"}],"name":"setTHUSD2UsdPriceAggregator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"thusdAmount","type":"uint256"},{"internalType":"uint256","name":"minCollateralReturn","type":"uint256"},{"internalType":"address payable","name":"dest","type":"address"}],"name":"swap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"thusd2UsdPriceAggregator","outputs":[{"internalType":"contract AggregatorV3Interface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"thusdToken","outputs":[{"internalType":"contract ITHUSDToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"contract IERC20","name":"","type":"address"},{"internalType":"address payable","name":"destAddress","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"name":"trade","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferBProtocolOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numShares","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]