编译器
0.8.21+commit.d9974bed
文件 1 的 21:AccountantWithRateProviders.sol
pragma solidity 0.8.21;
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {IRateProvider} from "src/interfaces/IRateProvider.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {BoringVault} from "src/base/BoringVault.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
import {IPausable} from "src/interfaces/IPausable.sol";
contract AccountantWithRateProviders is Auth, IRateProvider, IPausable {
using FixedPointMathLib for uint256;
using SafeTransferLib for ERC20;
struct AccountantState {
address payoutAddress;
uint96 highwaterMark;
uint128 feesOwedInBase;
uint128 totalSharesLastUpdate;
uint96 exchangeRate;
uint16 allowedExchangeRateChangeUpper;
uint16 allowedExchangeRateChangeLower;
uint64 lastUpdateTimestamp;
bool isPaused;
uint24 minimumUpdateDelayInSeconds;
uint16 platformFee;
uint16 performanceFee;
}
struct RateProviderData {
bool isPeggedToBase;
IRateProvider rateProvider;
}
AccountantState public accountantState;
mapping(ERC20 => RateProviderData) public rateProviderData;
error AccountantWithRateProviders__UpperBoundTooSmall();
error AccountantWithRateProviders__LowerBoundTooLarge();
error AccountantWithRateProviders__PlatformFeeTooLarge();
error AccountantWithRateProviders__PerformanceFeeTooLarge();
error AccountantWithRateProviders__Paused();
error AccountantWithRateProviders__ZeroFeesOwed();
error AccountantWithRateProviders__OnlyCallableByBoringVault();
error AccountantWithRateProviders__UpdateDelayTooLarge();
error AccountantWithRateProviders__ExchangeRateAboveHighwaterMark();
event Paused();
event Unpaused();
event DelayInSecondsUpdated(uint24 oldDelay, uint24 newDelay);
event UpperBoundUpdated(uint16 oldBound, uint16 newBound);
event LowerBoundUpdated(uint16 oldBound, uint16 newBound);
event PlatformFeeUpdated(uint16 oldFee, uint16 newFee);
event PerformanceFeeUpdated(uint16 oldFee, uint16 newFee);
event PayoutAddressUpdated(address oldPayout, address newPayout);
event RateProviderUpdated(address asset, bool isPegged, address rateProvider);
event ExchangeRateUpdated(uint96 oldRate, uint96 newRate, uint64 currentTime);
event FeesClaimed(address indexed feeAsset, uint256 amount);
event HighwaterMarkReset();
ERC20 public immutable base;
uint8 public immutable decimals;
BoringVault public immutable vault;
uint256 internal immutable ONE_SHARE;
constructor(
address _owner,
address _vault,
address payoutAddress,
uint96 startingExchangeRate,
address _base,
uint16 allowedExchangeRateChangeUpper,
uint16 allowedExchangeRateChangeLower,
uint24 minimumUpdateDelayInSeconds,
uint16 platformFee,
uint16 performanceFee
) Auth(_owner, Authority(address(0))) {
base = ERC20(_base);
decimals = ERC20(_base).decimals();
vault = BoringVault(payable(_vault));
ONE_SHARE = 10 ** vault.decimals();
accountantState = AccountantState({
payoutAddress: payoutAddress,
highwaterMark: startingExchangeRate,
feesOwedInBase: 0,
totalSharesLastUpdate: uint128(vault.totalSupply()),
exchangeRate: startingExchangeRate,
allowedExchangeRateChangeUpper: allowedExchangeRateChangeUpper,
allowedExchangeRateChangeLower: allowedExchangeRateChangeLower,
lastUpdateTimestamp: uint64(block.timestamp),
isPaused: false,
minimumUpdateDelayInSeconds: minimumUpdateDelayInSeconds,
platformFee: platformFee,
performanceFee: performanceFee
});
}
function pause() external requiresAuth {
accountantState.isPaused = true;
emit Paused();
}
function unpause() external requiresAuth {
accountantState.isPaused = false;
emit Unpaused();
}
function updateDelay(uint24 minimumUpdateDelayInSeconds) external requiresAuth {
if (minimumUpdateDelayInSeconds > 14 days) revert AccountantWithRateProviders__UpdateDelayTooLarge();
uint24 oldDelay = accountantState.minimumUpdateDelayInSeconds;
accountantState.minimumUpdateDelayInSeconds = minimumUpdateDelayInSeconds;
emit DelayInSecondsUpdated(oldDelay, minimumUpdateDelayInSeconds);
}
function updateUpper(uint16 allowedExchangeRateChangeUpper) external requiresAuth {
if (allowedExchangeRateChangeUpper < 1e4) revert AccountantWithRateProviders__UpperBoundTooSmall();
uint16 oldBound = accountantState.allowedExchangeRateChangeUpper;
accountantState.allowedExchangeRateChangeUpper = allowedExchangeRateChangeUpper;
emit UpperBoundUpdated(oldBound, allowedExchangeRateChangeUpper);
}
function updateLower(uint16 allowedExchangeRateChangeLower) external requiresAuth {
if (allowedExchangeRateChangeLower > 1e4) revert AccountantWithRateProviders__LowerBoundTooLarge();
uint16 oldBound = accountantState.allowedExchangeRateChangeLower;
accountantState.allowedExchangeRateChangeLower = allowedExchangeRateChangeLower;
emit LowerBoundUpdated(oldBound, allowedExchangeRateChangeLower);
}
function updatePlatformFee(uint16 platformFee) external requiresAuth {
if (platformFee > 0.2e4) revert AccountantWithRateProviders__PlatformFeeTooLarge();
uint16 oldFee = accountantState.platformFee;
accountantState.platformFee = platformFee;
emit PlatformFeeUpdated(oldFee, platformFee);
}
function updatePerformanceFee(uint16 performanceFee) external requiresAuth {
if (performanceFee > 0.5e4) revert AccountantWithRateProviders__PerformanceFeeTooLarge();
uint16 oldFee = accountantState.performanceFee;
accountantState.performanceFee = performanceFee;
emit PerformanceFeeUpdated(oldFee, performanceFee);
}
function updatePayoutAddress(address payoutAddress) external requiresAuth {
address oldPayout = accountantState.payoutAddress;
accountantState.payoutAddress = payoutAddress;
emit PayoutAddressUpdated(oldPayout, payoutAddress);
}
function setRateProviderData(ERC20 asset, bool isPeggedToBase, address rateProvider) external requiresAuth {
rateProviderData[asset] =
RateProviderData({isPeggedToBase: isPeggedToBase, rateProvider: IRateProvider(rateProvider)});
emit RateProviderUpdated(address(asset), isPeggedToBase, rateProvider);
}
function resetHighwaterMark() external virtual requiresAuth {
AccountantState storage state = accountantState;
if (state.exchangeRate > state.highwaterMark) {
revert AccountantWithRateProviders__ExchangeRateAboveHighwaterMark();
}
uint64 currentTime = uint64(block.timestamp);
uint256 currentTotalShares = vault.totalSupply();
_calculateFeesOwed(state, state.exchangeRate, state.exchangeRate, currentTotalShares, currentTime);
state.totalSharesLastUpdate = uint128(currentTotalShares);
state.highwaterMark = accountantState.exchangeRate;
state.lastUpdateTimestamp = currentTime;
emit HighwaterMarkReset();
}
function updateExchangeRate(uint96 newExchangeRate) external virtual requiresAuth {
(
bool shouldPause,
AccountantState storage state,
uint64 currentTime,
uint256 currentExchangeRate,
uint256 currentTotalShares
) = _beforeUpdateExchangeRate(newExchangeRate);
if (shouldPause) {
state.isPaused = true;
} else {
_calculateFeesOwed(state, newExchangeRate, currentExchangeRate, currentTotalShares, currentTime);
}
newExchangeRate = _setExchangeRate(newExchangeRate, state);
state.totalSharesLastUpdate = uint128(currentTotalShares);
state.lastUpdateTimestamp = currentTime;
emit ExchangeRateUpdated(uint96(currentExchangeRate), newExchangeRate, currentTime);
}
function claimFees(ERC20 feeAsset) external {
if (msg.sender != address(vault)) revert AccountantWithRateProviders__OnlyCallableByBoringVault();
AccountantState storage state = accountantState;
if (state.isPaused) revert AccountantWithRateProviders__Paused();
if (state.feesOwedInBase == 0) revert AccountantWithRateProviders__ZeroFeesOwed();
uint256 feesOwedInFeeAsset;
RateProviderData memory data = rateProviderData[feeAsset];
if (address(feeAsset) == address(base)) {
feesOwedInFeeAsset = state.feesOwedInBase;
} else {
uint8 feeAssetDecimals = ERC20(feeAsset).decimals();
uint256 feesOwedInBaseUsingFeeAssetDecimals =
_changeDecimals(state.feesOwedInBase, decimals, feeAssetDecimals);
if (data.isPeggedToBase) {
feesOwedInFeeAsset = feesOwedInBaseUsingFeeAssetDecimals;
} else {
uint256 rate = data.rateProvider.getRate();
feesOwedInFeeAsset = feesOwedInBaseUsingFeeAssetDecimals.mulDivDown(10 ** feeAssetDecimals, rate);
}
}
state.feesOwedInBase = 0;
feeAsset.safeTransferFrom(msg.sender, state.payoutAddress, feesOwedInFeeAsset);
emit FeesClaimed(address(feeAsset), feesOwedInFeeAsset);
}
function getRate() public view returns (uint256 rate) {
rate = accountantState.exchangeRate;
}
function getRateSafe() external view returns (uint256 rate) {
if (accountantState.isPaused) revert AccountantWithRateProviders__Paused();
rate = getRate();
}
function getRateInQuote(ERC20 quote) public view returns (uint256 rateInQuote) {
if (address(quote) == address(base)) {
rateInQuote = accountantState.exchangeRate;
} else {
RateProviderData memory data = rateProviderData[quote];
uint8 quoteDecimals = ERC20(quote).decimals();
uint256 exchangeRateInQuoteDecimals = _changeDecimals(accountantState.exchangeRate, decimals, quoteDecimals);
if (data.isPeggedToBase) {
rateInQuote = exchangeRateInQuoteDecimals;
} else {
uint256 quoteRate = data.rateProvider.getRate();
uint256 oneQuote = 10 ** quoteDecimals;
rateInQuote = oneQuote.mulDivDown(exchangeRateInQuoteDecimals, quoteRate);
}
}
}
function getRateInQuoteSafe(ERC20 quote) external view returns (uint256 rateInQuote) {
if (accountantState.isPaused) revert AccountantWithRateProviders__Paused();
rateInQuote = getRateInQuote(quote);
}
function previewUpdateExchangeRate(uint96 newExchangeRate)
external
view
virtual
returns (bool updateWillPause, uint256 newFeesOwedInBase, uint256 totalFeesOwedInBase)
{
(
bool shouldPause,
AccountantState storage state,
uint64 currentTime,
uint256 currentExchangeRate,
uint256 currentTotalShares
) = _beforeUpdateExchangeRate(newExchangeRate);
updateWillPause = shouldPause;
totalFeesOwedInBase = state.feesOwedInBase;
if (!shouldPause) {
(uint256 platformFeesOwedInBase, uint256 shareSupplyToUse) = _calculatePlatformFee(
state.totalSharesLastUpdate,
state.lastUpdateTimestamp,
state.platformFee,
newExchangeRate,
currentExchangeRate,
currentTotalShares,
currentTime
);
uint256 performanceFeesOwedInBase;
if (newExchangeRate > state.highwaterMark) {
(performanceFeesOwedInBase,) = _calculatePerformanceFee(
newExchangeRate, shareSupplyToUse, state.highwaterMark, state.performanceFee
);
}
newFeesOwedInBase = platformFeesOwedInBase + performanceFeesOwedInBase;
totalFeesOwedInBase += newFeesOwedInBase;
}
}
function _changeDecimals(uint256 amount, uint8 fromDecimals, uint8 toDecimals) internal pure returns (uint256) {
if (fromDecimals == toDecimals) {
return amount;
} else if (fromDecimals < toDecimals) {
return amount * 10 ** (toDecimals - fromDecimals);
} else {
return amount / 10 ** (fromDecimals - toDecimals);
}
}
function _beforeUpdateExchangeRate(uint96 newExchangeRate)
internal
view
returns (
bool shouldPause,
AccountantState storage state,
uint64 currentTime,
uint256 currentExchangeRate,
uint256 currentTotalShares
)
{
state = accountantState;
if (state.isPaused) revert AccountantWithRateProviders__Paused();
currentTime = uint64(block.timestamp);
currentExchangeRate = state.exchangeRate;
currentTotalShares = vault.totalSupply();
shouldPause = currentTime < state.lastUpdateTimestamp + state.minimumUpdateDelayInSeconds
|| newExchangeRate > currentExchangeRate.mulDivDown(state.allowedExchangeRateChangeUpper, 1e4)
|| newExchangeRate < currentExchangeRate.mulDivDown(state.allowedExchangeRateChangeLower, 1e4);
}
function _setExchangeRate(uint96 newExchangeRate, AccountantState storage state)
internal
virtual
returns (uint96)
{
state.exchangeRate = newExchangeRate;
return newExchangeRate;
}
function _calculatePlatformFee(
uint128 totalSharesLastUpdate,
uint64 lastUpdateTimestamp,
uint16 platformFee,
uint96 newExchangeRate,
uint256 currentExchangeRate,
uint256 currentTotalShares,
uint64 currentTime
) internal view returns (uint256 platformFeesOwedInBase, uint256 shareSupplyToUse) {
shareSupplyToUse = currentTotalShares;
if (totalSharesLastUpdate < shareSupplyToUse) {
shareSupplyToUse = totalSharesLastUpdate;
}
if (platformFee > 0) {
uint256 timeDelta = currentTime - lastUpdateTimestamp;
uint256 minimumAssets = newExchangeRate > currentExchangeRate
? shareSupplyToUse.mulDivDown(currentExchangeRate, ONE_SHARE)
: shareSupplyToUse.mulDivDown(newExchangeRate, ONE_SHARE);
uint256 platformFeesAnnual = minimumAssets.mulDivDown(platformFee, 1e4);
platformFeesOwedInBase = platformFeesAnnual.mulDivDown(timeDelta, 365 days);
}
}
function _calculatePerformanceFee(
uint96 newExchangeRate,
uint256 shareSupplyToUse,
uint96 datum,
uint16 performanceFee
) internal view returns (uint256 performanceFeesOwedInBase, uint256 yieldEarned) {
uint256 changeInExchangeRate = newExchangeRate - datum;
yieldEarned = changeInExchangeRate.mulDivDown(shareSupplyToUse, ONE_SHARE);
if (performanceFee > 0) {
performanceFeesOwedInBase = yieldEarned.mulDivDown(performanceFee, 1e4);
}
}
function _calculateFeesOwed(
AccountantState storage state,
uint96 newExchangeRate,
uint256 currentExchangeRate,
uint256 currentTotalShares,
uint64 currentTime
) internal virtual {
(uint256 newFeesOwedInBase, uint256 shareSupplyToUse) = _calculatePlatformFee(
state.totalSharesLastUpdate,
state.lastUpdateTimestamp,
state.platformFee,
newExchangeRate,
currentExchangeRate,
currentTotalShares,
currentTime
);
if (newExchangeRate > state.highwaterMark) {
(uint256 performanceFeesOwedInBase,) =
_calculatePerformanceFee(newExchangeRate, shareSupplyToUse, state.highwaterMark, state.performanceFee);
newFeesOwedInBase += performanceFeesOwedInBase;
state.highwaterMark = newExchangeRate;
}
state.feesOwedInBase += uint128(newFeesOwedInBase);
}
}
文件 2 的 21:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 3 的 21:Auth.sol
pragma solidity >=0.8.0;
abstract contract Auth {
event OwnershipTransferred(address indexed user, address indexed newOwner);
event AuthorityUpdated(address indexed user, Authority indexed newAuthority);
address public owner;
Authority public authority;
constructor(address _owner, Authority _authority) {
owner = _owner;
authority = _authority;
emit OwnershipTransferred(msg.sender, _owner);
emit AuthorityUpdated(msg.sender, _authority);
}
modifier requiresAuth() virtual {
require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
_;
}
function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
Authority auth = authority;
return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
}
function setAuthority(Authority newAuthority) public virtual {
require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));
authority = newAuthority;
emit AuthorityUpdated(msg.sender, newAuthority);
}
function transferOwnership(address newOwner) public virtual requiresAuth {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
interface Authority {
function canCall(
address user,
address target,
bytes4 functionSig
) external view returns (bool);
}
文件 4 的 21:BeforeTransferHook.sol
pragma solidity 0.8.21;
interface BeforeTransferHook {
function beforeTransfer(address from, address to, address operator) external view;
}
文件 5 的 21:BoringOnChainQueue.sol
pragma solidity 0.8.21;
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {WETH} from "@solmate/tokens/WETH.sol";
import {BoringVault} from "src/base/BoringVault.sol";
import {AccountantWithRateProviders} from "src/base/Roles/AccountantWithRateProviders.sol";
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
import {ReentrancyGuard} from "@solmate/utils/ReentrancyGuard.sol";
import {IPausable} from "src/interfaces/IPausable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IBoringSolver} from "src/base/Roles/BoringQueue/IBoringSolver.sol";
contract BoringOnChainQueue is Auth, ReentrancyGuard, IPausable {
using EnumerableSet for EnumerableSet.Bytes32Set;
using SafeTransferLib for BoringVault;
using SafeTransferLib for ERC20;
using FixedPointMathLib for uint256;
struct WithdrawAsset {
bool allowWithdraws;
uint24 secondsToMaturity;
uint24 minimumSecondsToDeadline;
uint16 minDiscount;
uint16 maxDiscount;
uint96 minimumShares;
}
struct OnChainWithdraw {
uint96 nonce;
address user;
address assetOut;
uint128 amountOfShares;
uint128 amountOfAssets;
uint40 creationTime;
uint24 secondsToMaturity;
uint24 secondsToDeadline;
}
uint16 internal constant MAX_DISCOUNT = 0.3e4;
uint24 internal constant MAXIMUM_SECONDS_TO_MATURITY = 30 days;
uint24 internal constant MAXIMUM_MINIMUM_SECONDS_TO_DEADLINE = 30 days;
modifier onlyRequestUser(address requestUser, address msgSender) {
if (requestUser != msgSender) revert BoringOnChainQueue__BadUser();
_;
}
EnumerableSet.Bytes32Set private _withdrawRequests;
mapping(address => WithdrawAsset) public withdrawAssets;
uint96 public nonce = 1;
bool public isPaused;
error BoringOnChainQueue__Paused();
error BoringOnChainQueue__WithdrawsNotAllowedForAsset();
error BoringOnChainQueue__BadDiscount();
error BoringOnChainQueue__BadShareAmount();
error BoringOnChainQueue__BadDeadline();
error BoringOnChainQueue__BadUser();
error BoringOnChainQueue__DeadlinePassed();
error BoringOnChainQueue__NotMatured();
error BoringOnChainQueue__Keccak256Collision();
error BoringOnChainQueue__RequestNotFound();
error BoringOnChainQueue__PermitFailedAndAllowanceTooLow();
error BoringOnChainQueue__MAX_DISCOUNT();
error BoringOnChainQueue__MAXIMUM_MINIMUM_SECONDS_TO_DEADLINE();
error BoringOnChainQueue__SolveAssetMismatch();
error BoringOnChainQueue__Overflow();
error BoringOnChainQueue__MAXIMUM_SECONDS_TO_MATURITY();
error BoringOnChainQueue__BadInput();
error BoringOnChainQueue__RescueCannotTakeSharesFromActiveRequests();
event OnChainWithdrawRequested(
bytes32 indexed requestId,
address indexed user,
address indexed assetOut,
uint96 nonce,
uint128 amountOfShares,
uint128 amountOfAssets,
uint40 creationTime,
uint24 secondsToMaturity,
uint24 secondsToDeadline
);
event OnChainWithdrawCancelled(bytes32 indexed requestId, address indexed user, uint256 timestamp);
event OnChainWithdrawSolved(bytes32 indexed requestId, address indexed user, uint256 timestamp);
event WithdrawAssetSetup(
address indexed assetOut,
uint24 secondsToMaturity,
uint24 minimumSecondsToDeadline,
uint16 minDiscount,
uint16 maxDiscount,
uint96 minimumShares
);
event WithdrawAssetStopped(address indexed assetOut);
event WithdrawAssetUpdated(
address indexed assetOut,
uint24 minimumSecondsToDeadline,
uint24 secondsToMaturity,
uint16 minDiscount,
uint16 maxDiscount,
uint96 minimumShares
);
event Paused();
event Unpaused();
BoringVault public immutable boringVault;
AccountantWithRateProviders public immutable accountant;
uint256 public immutable ONE_SHARE;
constructor(address _owner, address _auth, address payable _boringVault, address _accountant)
Auth(_owner, Authority(_auth))
{
boringVault = BoringVault(_boringVault);
ONE_SHARE = 10 ** boringVault.decimals();
accountant = AccountantWithRateProviders(_accountant);
}
function rescueTokens(ERC20 token, uint256 amount, address to, OnChainWithdraw[] calldata activeRequests)
external
requiresAuth
{
if (address(token) == address(boringVault)) {
bytes32[] memory requestIds = _withdrawRequests.values();
uint256 requestIdsLength = requestIds.length;
if (activeRequests.length != requestIdsLength) revert BoringOnChainQueue__BadInput();
uint256 activeRequestShareSum;
for (uint256 i = 0; i < requestIdsLength; ++i) {
if (keccak256(abi.encode(activeRequests[i])) != requestIds[i]) revert BoringOnChainQueue__BadInput();
activeRequestShareSum += activeRequests[i].amountOfShares;
}
uint256 freeShares = boringVault.balanceOf(address(this)) - activeRequestShareSum;
if (amount == type(uint256).max) amount = freeShares;
else if (amount > freeShares) revert BoringOnChainQueue__RescueCannotTakeSharesFromActiveRequests();
} else {
if (amount == type(uint256).max) amount = token.balanceOf(address(this));
}
token.safeTransfer(to, amount);
}
function pause() external requiresAuth {
isPaused = true;
emit Paused();
}
function unpause() external requiresAuth {
isPaused = false;
emit Unpaused();
}
function updateWithdrawAsset(
address assetOut,
uint24 secondsToMaturity,
uint24 minimumSecondsToDeadline,
uint16 minDiscount,
uint16 maxDiscount,
uint96 minimumShares
) external requiresAuth {
if (maxDiscount > MAX_DISCOUNT) revert BoringOnChainQueue__MAX_DISCOUNT();
if (secondsToMaturity > MAXIMUM_SECONDS_TO_MATURITY) {
revert BoringOnChainQueue__MAXIMUM_SECONDS_TO_MATURITY();
}
if (minimumSecondsToDeadline > MAXIMUM_MINIMUM_SECONDS_TO_DEADLINE) {
revert BoringOnChainQueue__MAXIMUM_MINIMUM_SECONDS_TO_DEADLINE();
}
if (minDiscount > maxDiscount) revert BoringOnChainQueue__BadDiscount();
accountant.getRateInQuoteSafe(ERC20(assetOut));
withdrawAssets[assetOut] = WithdrawAsset({
allowWithdraws: true,
secondsToMaturity: secondsToMaturity,
minimumSecondsToDeadline: minimumSecondsToDeadline,
minDiscount: minDiscount,
maxDiscount: maxDiscount,
minimumShares: minimumShares
});
emit WithdrawAssetUpdated(
assetOut, secondsToMaturity, minimumSecondsToDeadline, minDiscount, maxDiscount, minimumShares
);
}
function stopWithdrawsInAsset(address assetOut) external requiresAuth {
withdrawAssets[assetOut].allowWithdraws = false;
emit WithdrawAssetStopped(assetOut);
}
function cancelUserWithdraws(OnChainWithdraw[] calldata requests)
external
requiresAuth
returns (bytes32[] memory canceledRequestIds)
{
uint256 requestsLength = requests.length;
canceledRequestIds = new bytes32[](requestsLength);
for (uint256 i = 0; i < requestsLength; ++i) {
canceledRequestIds[i] = _cancelOnChainWithdraw(requests[i]);
}
}
function requestOnChainWithdraw(address assetOut, uint128 amountOfShares, uint16 discount, uint24 secondsToDeadline)
external
virtual
requiresAuth
returns (bytes32 requestId)
{
WithdrawAsset memory withdrawAsset = withdrawAssets[assetOut];
_beforeNewRequest(withdrawAsset, amountOfShares, discount, secondsToDeadline);
boringVault.safeTransferFrom(msg.sender, address(this), amountOfShares);
(requestId,) = _queueOnChainWithdraw(
msg.sender, assetOut, amountOfShares, discount, withdrawAsset.secondsToMaturity, secondsToDeadline
);
}
function requestOnChainWithdrawWithPermit(
address assetOut,
uint128 amountOfShares,
uint16 discount,
uint24 secondsToDeadline,
uint256 permitDeadline,
uint8 v,
bytes32 r,
bytes32 s
) external virtual requiresAuth returns (bytes32 requestId) {
WithdrawAsset memory withdrawAsset = withdrawAssets[assetOut];
_beforeNewRequest(withdrawAsset, amountOfShares, discount, secondsToDeadline);
try boringVault.permit(msg.sender, address(this), amountOfShares, permitDeadline, v, r, s) {}
catch {
if (boringVault.allowance(msg.sender, address(this)) < amountOfShares) {
revert BoringOnChainQueue__PermitFailedAndAllowanceTooLow();
}
}
boringVault.safeTransferFrom(msg.sender, address(this), amountOfShares);
(requestId,) = _queueOnChainWithdraw(
msg.sender, assetOut, amountOfShares, discount, withdrawAsset.secondsToMaturity, secondsToDeadline
);
}
function cancelOnChainWithdraw(OnChainWithdraw memory request)
external
virtual
requiresAuth
returns (bytes32 requestId)
{
requestId = _cancelOnChainWithdrawWithUserCheck(request);
}
function replaceOnChainWithdraw(OnChainWithdraw memory oldRequest, uint16 discount, uint24 secondsToDeadline)
external
virtual
requiresAuth
returns (bytes32 oldRequestId, bytes32 newRequestId)
{
(oldRequestId, newRequestId) = _replaceOnChainWithdrawWithUserCheck(oldRequest, discount, secondsToDeadline);
}
function solveOnChainWithdraws(OnChainWithdraw[] calldata requests, bytes calldata solveData, address solver)
external
requiresAuth
{
if (isPaused) revert BoringOnChainQueue__Paused();
ERC20 solveAsset = ERC20(requests[0].assetOut);
uint256 requiredAssets;
uint256 totalShares;
uint256 requestsLength = requests.length;
for (uint256 i = 0; i < requestsLength; ++i) {
if (address(solveAsset) != requests[i].assetOut) revert BoringOnChainQueue__SolveAssetMismatch();
uint256 maturity = requests[i].creationTime + requests[i].secondsToMaturity;
if (block.timestamp < maturity) revert BoringOnChainQueue__NotMatured();
uint256 deadline = maturity + requests[i].secondsToDeadline;
if (block.timestamp > deadline) revert BoringOnChainQueue__DeadlinePassed();
requiredAssets += requests[i].amountOfAssets;
totalShares += requests[i].amountOfShares;
bytes32 requestId = _dequeueOnChainWithdraw(requests[i]);
emit OnChainWithdrawSolved(requestId, requests[i].user, block.timestamp);
}
boringVault.safeTransfer(solver, totalShares);
if (solveData.length > 0) {
IBoringSolver(solver).boringSolve(
msg.sender, address(boringVault), address(solveAsset), totalShares, requiredAssets, solveData
);
}
for (uint256 i = 0; i < requestsLength; ++i) {
solveAsset.safeTransferFrom(solver, requests[i].user, requests[i].amountOfAssets);
}
}
function getRequestIds() public view returns (bytes32[] memory) {
return _withdrawRequests.values();
}
function getRequestId(OnChainWithdraw calldata request) external pure returns (bytes32 requestId) {
return keccak256(abi.encode(request));
}
function previewAssetsOut(address assetOut, uint128 amountOfShares, uint16 discount)
public
view
returns (uint128 amountOfAssets128)
{
uint256 price = accountant.getRateInQuoteSafe(ERC20(assetOut));
price = price.mulDivDown(1e4 - discount, 1e4);
uint256 amountOfAssets = uint256(amountOfShares).mulDivDown(price, ONE_SHARE);
if (amountOfAssets > type(uint128).max) revert BoringOnChainQueue__Overflow();
amountOfAssets128 = uint128(amountOfAssets);
}
function _beforeNewRequest(
WithdrawAsset memory withdrawAsset,
uint128 amountOfShares,
uint16 discount,
uint24 secondsToDeadline
) internal view virtual {
if (isPaused) revert BoringOnChainQueue__Paused();
if (!withdrawAsset.allowWithdraws) revert BoringOnChainQueue__WithdrawsNotAllowedForAsset();
if (discount < withdrawAsset.minDiscount || discount > withdrawAsset.maxDiscount) {
revert BoringOnChainQueue__BadDiscount();
}
if (amountOfShares < withdrawAsset.minimumShares) revert BoringOnChainQueue__BadShareAmount();
if (secondsToDeadline < withdrawAsset.minimumSecondsToDeadline) revert BoringOnChainQueue__BadDeadline();
}
function _cancelOnChainWithdrawWithUserCheck(OnChainWithdraw memory request)
internal
virtual
onlyRequestUser(request.user, msg.sender)
returns (bytes32 requestId)
{
requestId = _cancelOnChainWithdraw(request);
}
function _cancelOnChainWithdraw(OnChainWithdraw memory request) internal virtual returns (bytes32 requestId) {
requestId = _dequeueOnChainWithdraw(request);
boringVault.safeTransfer(request.user, request.amountOfShares);
emit OnChainWithdrawCancelled(requestId, request.user, block.timestamp);
}
function _replaceOnChainWithdrawWithUserCheck(
OnChainWithdraw memory oldRequest,
uint16 discount,
uint24 secondsToDeadline
)
internal
virtual
onlyRequestUser(oldRequest.user, msg.sender)
returns (bytes32 oldRequestId, bytes32 newRequestId)
{
(oldRequestId, newRequestId) = _replaceOnChainWithdraw(oldRequest, discount, secondsToDeadline);
}
function _replaceOnChainWithdraw(OnChainWithdraw memory oldRequest, uint16 discount, uint24 secondsToDeadline)
internal
virtual
onlyRequestUser(oldRequest.user, msg.sender)
returns (bytes32 oldRequestId, bytes32 newRequestId)
{
WithdrawAsset memory withdrawAsset = withdrawAssets[oldRequest.assetOut];
_beforeNewRequest(withdrawAsset, oldRequest.amountOfShares, discount, secondsToDeadline);
oldRequestId = _dequeueOnChainWithdraw(oldRequest);
emit OnChainWithdrawCancelled(oldRequestId, oldRequest.user, block.timestamp);
(newRequestId,) = _queueOnChainWithdraw(
oldRequest.user,
oldRequest.assetOut,
oldRequest.amountOfShares,
discount,
withdrawAsset.secondsToMaturity,
secondsToDeadline
);
}
function _queueOnChainWithdraw(
address user,
address assetOut,
uint128 amountOfShares,
uint16 discount,
uint24 secondsToMaturity,
uint24 secondsToDeadline
) internal virtual returns (bytes32 requestId, OnChainWithdraw memory req) {
uint96 requestNonce;
unchecked {
requestNonce = nonce++;
}
uint128 amountOfAssets128 = previewAssetsOut(assetOut, amountOfShares, discount);
uint40 timeNow = uint40(block.timestamp);
req = OnChainWithdraw({
nonce: requestNonce,
user: user,
assetOut: assetOut,
amountOfShares: amountOfShares,
amountOfAssets: amountOfAssets128,
creationTime: timeNow,
secondsToMaturity: secondsToMaturity,
secondsToDeadline: secondsToDeadline
});
requestId = keccak256(abi.encode(req));
bool addedToSet = _withdrawRequests.add(requestId);
if (!addedToSet) revert BoringOnChainQueue__Keccak256Collision();
emit OnChainWithdrawRequested(
requestId,
user,
assetOut,
requestNonce,
amountOfShares,
amountOfAssets128,
timeNow,
secondsToMaturity,
secondsToDeadline
);
}
function _dequeueOnChainWithdraw(OnChainWithdraw memory request) internal virtual returns (bytes32 requestId) {
requestId = keccak256(abi.encode(request));
bool removedFromSet = _withdrawRequests.remove(requestId);
if (!removedFromSet) revert BoringOnChainQueue__RequestNotFound();
}
}
文件 6 的 21:BoringVault.sol
pragma solidity 0.8.21;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol";
import {SafeTransferLib} from "@solmate/utils/SafeTransferLib.sol";
import {ERC20} from "@solmate/tokens/ERC20.sol";
import {BeforeTransferHook} from "src/interfaces/BeforeTransferHook.sol";
import {Auth, Authority} from "@solmate/auth/Auth.sol";
contract BoringVault is ERC20, Auth, ERC721Holder, ERC1155Holder {
using Address for address;
using SafeTransferLib for ERC20;
using FixedPointMathLib for uint256;
BeforeTransferHook public hook;
event Enter(address indexed from, address indexed asset, uint256 amount, address indexed to, uint256 shares);
event Exit(address indexed to, address indexed asset, uint256 amount, address indexed from, uint256 shares);
constructor(address _owner, string memory _name, string memory _symbol, uint8 _decimals)
ERC20(_name, _symbol, _decimals)
Auth(_owner, Authority(address(0)))
{}
function manage(address target, bytes calldata data, uint256 value)
external
requiresAuth
returns (bytes memory result)
{
result = target.functionCallWithValue(data, value);
}
function manage(address[] calldata targets, bytes[] calldata data, uint256[] calldata values)
external
requiresAuth
returns (bytes[] memory results)
{
uint256 targetsLength = targets.length;
results = new bytes[](targetsLength);
for (uint256 i; i < targetsLength; ++i) {
results[i] = targets[i].functionCallWithValue(data[i], values[i]);
}
}
function enter(address from, ERC20 asset, uint256 assetAmount, address to, uint256 shareAmount)
external
requiresAuth
{
if (assetAmount > 0) asset.safeTransferFrom(from, address(this), assetAmount);
_mint(to, shareAmount);
emit Enter(from, address(asset), assetAmount, to, shareAmount);
}
function exit(address to, ERC20 asset, uint256 assetAmount, address from, uint256 shareAmount)
external
requiresAuth
{
_burn(from, shareAmount);
if (assetAmount > 0) asset.safeTransfer(to, assetAmount);
emit Exit(to, address(asset), assetAmount, from, shareAmount);
}
function setBeforeTransferHook(address _hook) external requiresAuth {
hook = BeforeTransferHook(_hook);
}
function _callBeforeTransfer(address from, address to) internal view {
if (address(hook) != address(0)) hook.beforeTransfer(from, to, msg.sender);
}
function transfer(address to, uint256 amount) public override returns (bool) {
_callBeforeTransfer(msg.sender, to);
return super.transfer(to, amount);
}
function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
_callBeforeTransfer(from, to);
return super.transferFrom(from, to, amount);
}
receive() external payable {}
}
文件 7 的 21:ERC1155Holder.sol
pragma solidity ^0.8.20;
import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol";
import {IERC1155Receiver} from "../IERC1155Receiver.sol";
abstract contract ERC1155Holder is ERC165, IERC1155Receiver {
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
}
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}
文件 8 的 21:ERC165.sol
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 9 的 21:ERC20.sol
pragma solidity >=0.8.0;
abstract contract ERC20 {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
文件 10 的 21:ERC721Holder.sol
pragma solidity ^0.8.20;
import {IERC721Receiver} from "../IERC721Receiver.sol";
abstract contract ERC721Holder is IERC721Receiver {
function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
return this.onERC721Received.selector;
}
}
文件 11 的 21:EnumerableSet.sol
pragma solidity ^0.8.20;
library EnumerableSet {
struct Set {
bytes32[] _values;
mapping(bytes32 value => uint256) _positions;
}
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
function _remove(Set storage set, bytes32 value) private returns (bool) {
uint256 position = set._positions[value];
if (position != 0) {
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
set._values[valueIndex] = lastValue;
set._positions[lastValue] = position;
}
set._values.pop();
delete set._positions[value];
return true;
} else {
return false;
}
}
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
struct Bytes32Set {
Set _inner;
}
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
assembly {
result := store
}
return result;
}
struct AddressSet {
Set _inner;
}
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
struct UintSet {
Set _inner;
}
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
}
文件 12 的 21:FixedPointMathLib.sol
pragma solidity >=0.8.0;
library FixedPointMathLib {
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18;
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD);
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD);
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y);
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y);
}
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
assembly {
switch x
case 0 {
switch n
case 0 {
z := scalar
}
default {
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
z := scalar
}
default {
z := x
}
let half := shr(1, scalar)
for {
n := shr(1, n)
} n {
n := shr(1, n)
} {
if shr(128, x) {
revert(0, 0)
}
let xx := mul(x, x)
let xxRound := add(xx, half)
if lt(xxRound, xx) {
revert(0, 0)
}
x := div(xxRound, scalar)
if mod(n, 2) {
let zx := mul(z, x)
if iszero(eq(div(zx, x), z)) {
if iszero(iszero(x)) {
revert(0, 0)
}
}
let zxRound := add(zx, half)
if lt(zxRound, zx) {
revert(0, 0)
}
z := div(zxRound, scalar)
}
}
}
}
}
function sqrt(uint256 x) internal pure returns (uint256 z) {
assembly {
let y := x
z := 181
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
z := shr(18, mul(z, add(y, 65536)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
assembly {
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}
文件 13 的 21:IBoringSolver.sol
pragma solidity 0.8.21;
interface IBoringSolver {
function boringSolve(
address initiator,
address boringVault,
address solveAsset,
uint256 totalShares,
uint256 requiredAssets,
bytes calldata solveData
) external;
}
文件 14 的 21:IERC1155Receiver.sol
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
interface IERC1155Receiver is IERC165 {
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
文件 15 的 21:IERC165.sol
pragma solidity ^0.8.20;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 16 的 21:IERC721Receiver.sol
pragma solidity ^0.8.20;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 17 的 21:IPausable.sol
pragma solidity 0.8.21;
interface IPausable {
function pause() external;
function unpause() external;
}
文件 18 的 21:IRateProvider.sol
pragma solidity ^0.8.0;
interface IRateProvider {
function getRate() external view returns (uint256);
}
文件 19 的 21:ReentrancyGuard.sol
pragma solidity >=0.8.0;
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() virtual {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}
文件 20 的 21:SafeTransferLib.sol
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
library SafeTransferLib {
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 68), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
文件 21 的 21:WETH.sol
pragma solidity >=0.8.0;
import {ERC20} from "./ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
contract WETH is ERC20("Wrapped Ether", "WETH", 18) {
using SafeTransferLib for address;
event Deposit(address indexed from, uint256 amount);
event Withdrawal(address indexed to, uint256 amount);
function deposit() public payable virtual {
_mint(msg.sender, msg.value);
emit Deposit(msg.sender, msg.value);
}
function withdraw(uint256 amount) public virtual {
_burn(msg.sender, amount);
emit Withdrawal(msg.sender, amount);
msg.sender.safeTransferETH(amount);
}
receive() external payable virtual {
deposit();
}
}
{
"compilationTarget": {
"src/base/Roles/BoringQueue/BoringOnChainQueue.sol": "BoringOnChainQueue"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@ccip/=lib/ccip/",
":@devtools-oapp-evm/=lib/OAppAuth/lib/devtools/packages/oapp-evm/contracts/oapp/",
":@ds-test/=lib/forge-std/lib/ds-test/src/",
":@forge-std/=lib/forge-std/src/",
":@layerzerolabs/lz-evm-messagelib-v2/=lib/OAppAuth/node_modules/@layerzerolabs/lz-evm-messagelib-v2/",
":@layerzerolabs/lz-evm-protocol-v2/=lib/OAppAuth/lib/LayerZero-V2/packages/layerzero-v2/evm/protocol/",
":@layerzerolabs/oapp-evm/=lib/OAppAuth/lib/devtools/packages/oapp-evm/",
":@lz-oapp-evm/=lib/OAppAuth/lib/LayerZero-V2/packages/layerzero-v2/evm/oapp/contracts/oapp/",
":@oapp-auth/=lib/OAppAuth/src/",
":@openzeppelin/=lib/openzeppelin-contracts/",
":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
":@solmate/=lib/solmate/src/",
":LayerZero-V2/=lib/OAppAuth/lib/",
":OAppAuth/=lib/OAppAuth/",
":ccip/=lib/ccip/contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":halmos-cheatcodes/=lib/OAppAuth/lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":solidity-bytes-utils/=lib/OAppAuth/node_modules/solidity-bytes-utils/",
":solmate/=lib/solmate/src/"
]
}
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_auth","type":"address"},{"internalType":"address payable","name":"_boringVault","type":"address"},{"internalType":"address","name":"_accountant","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BoringOnChainQueue__BadDeadline","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__BadDiscount","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__BadInput","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__BadShareAmount","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__BadUser","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__DeadlinePassed","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__Keccak256Collision","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__MAXIMUM_MINIMUM_SECONDS_TO_DEADLINE","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__MAXIMUM_SECONDS_TO_MATURITY","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__MAX_DISCOUNT","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__NotMatured","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__Overflow","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__Paused","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__PermitFailedAndAllowanceTooLow","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__RequestNotFound","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__RescueCannotTakeSharesFromActiveRequests","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__SolveAssetMismatch","type":"error"},{"inputs":[],"name":"BoringOnChainQueue__WithdrawsNotAllowedForAsset","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"OnChainWithdrawCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"assetOut","type":"address"},{"indexed":false,"internalType":"uint96","name":"nonce","type":"uint96"},{"indexed":false,"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amountOfAssets","type":"uint128"},{"indexed":false,"internalType":"uint40","name":"creationTime","type":"uint40"},{"indexed":false,"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"indexed":false,"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"name":"OnChainWithdrawRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"OnChainWithdrawSolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"assetOut","type":"address"},{"indexed":false,"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"indexed":false,"internalType":"uint24","name":"minimumSecondsToDeadline","type":"uint24"},{"indexed":false,"internalType":"uint16","name":"minDiscount","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"maxDiscount","type":"uint16"},{"indexed":false,"internalType":"uint96","name":"minimumShares","type":"uint96"}],"name":"WithdrawAssetSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"assetOut","type":"address"}],"name":"WithdrawAssetStopped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"assetOut","type":"address"},{"indexed":false,"internalType":"uint24","name":"minimumSecondsToDeadline","type":"uint24"},{"indexed":false,"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"indexed":false,"internalType":"uint16","name":"minDiscount","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"maxDiscount","type":"uint16"},{"indexed":false,"internalType":"uint96","name":"minimumShares","type":"uint96"}],"name":"WithdrawAssetUpdated","type":"event"},{"inputs":[],"name":"ONE_SHARE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accountant","outputs":[{"internalType":"contract AccountantWithRateProviders","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boringVault","outputs":[{"internalType":"contract BoringVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint128","name":"amountOfAssets","type":"uint128"},{"internalType":"uint40","name":"creationTime","type":"uint40"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"internalType":"struct BoringOnChainQueue.OnChainWithdraw","name":"request","type":"tuple"}],"name":"cancelOnChainWithdraw","outputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint128","name":"amountOfAssets","type":"uint128"},{"internalType":"uint40","name":"creationTime","type":"uint40"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"internalType":"struct BoringOnChainQueue.OnChainWithdraw[]","name":"requests","type":"tuple[]"}],"name":"cancelUserWithdraws","outputs":[{"internalType":"bytes32[]","name":"canceledRequestIds","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint128","name":"amountOfAssets","type":"uint128"},{"internalType":"uint40","name":"creationTime","type":"uint40"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"internalType":"struct BoringOnChainQueue.OnChainWithdraw","name":"request","type":"tuple"}],"name":"getRequestId","outputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getRequestIds","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint16","name":"discount","type":"uint16"}],"name":"previewAssetsOut","outputs":[{"internalType":"uint128","name":"amountOfAssets128","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint128","name":"amountOfAssets","type":"uint128"},{"internalType":"uint40","name":"creationTime","type":"uint40"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"internalType":"struct BoringOnChainQueue.OnChainWithdraw","name":"oldRequest","type":"tuple"},{"internalType":"uint16","name":"discount","type":"uint16"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"name":"replaceOnChainWithdraw","outputs":[{"internalType":"bytes32","name":"oldRequestId","type":"bytes32"},{"internalType":"bytes32","name":"newRequestId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint16","name":"discount","type":"uint16"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"name":"requestOnChainWithdraw","outputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint16","name":"discount","type":"uint16"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"},{"internalType":"uint256","name":"permitDeadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"requestOnChainWithdrawWithPermit","outputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint128","name":"amountOfAssets","type":"uint128"},{"internalType":"uint40","name":"creationTime","type":"uint40"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"internalType":"struct BoringOnChainQueue.OnChainWithdraw[]","name":"activeRequests","type":"tuple[]"}],"name":"rescueTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint96","name":"nonce","type":"uint96"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint128","name":"amountOfShares","type":"uint128"},{"internalType":"uint128","name":"amountOfAssets","type":"uint128"},{"internalType":"uint40","name":"creationTime","type":"uint40"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"secondsToDeadline","type":"uint24"}],"internalType":"struct BoringOnChainQueue.OnChainWithdraw[]","name":"requests","type":"tuple[]"},{"internalType":"bytes","name":"solveData","type":"bytes"},{"internalType":"address","name":"solver","type":"address"}],"name":"solveOnChainWithdraws","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetOut","type":"address"}],"name":"stopWithdrawsInAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetOut","type":"address"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"minimumSecondsToDeadline","type":"uint24"},{"internalType":"uint16","name":"minDiscount","type":"uint16"},{"internalType":"uint16","name":"maxDiscount","type":"uint16"},{"internalType":"uint96","name":"minimumShares","type":"uint96"}],"name":"updateWithdrawAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawAssets","outputs":[{"internalType":"bool","name":"allowWithdraws","type":"bool"},{"internalType":"uint24","name":"secondsToMaturity","type":"uint24"},{"internalType":"uint24","name":"minimumSecondsToDeadline","type":"uint24"},{"internalType":"uint16","name":"minDiscount","type":"uint16"},{"internalType":"uint16","name":"maxDiscount","type":"uint16"},{"internalType":"uint96","name":"minimumShares","type":"uint96"}],"stateMutability":"view","type":"function"}]