Latest 25 from a total of 4,264 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Cook | 30189192 | 2 days ago | IN | 0 ETH | 0.00001075 | ||||
| Cook | 29923232 | 8 days ago | IN | 0 ETH | 0.00000051 | ||||
| Cook | 29738376 | 12 days ago | IN | 0 ETH | 0.00000253 | ||||
| Cook | 29510304 | 18 days ago | IN | 0 ETH | 0.00000417 | ||||
| Cook | 29510222 | 18 days ago | IN | 0 ETH | 0.00000669 | ||||
| Cook | 29510152 | 18 days ago | IN | 0 ETH | 0.0000066 | ||||
| Cook | 29510100 | 18 days ago | IN | 0 ETH | 0.0000047 | ||||
| Cook | 29510022 | 18 days ago | IN | 0 ETH | 0.00000555 | ||||
| Cook | 29509797 | 18 days ago | IN | 0 ETH | 0.00000595 | ||||
| Cook | 29509535 | 18 days ago | IN | 0 ETH | 0.00000464 | ||||
| Cook | 29509529 | 18 days ago | IN | 0 ETH | 0.00000619 | ||||
| Cook | 29465959 | 19 days ago | IN | 0 ETH | 0.00000018 | ||||
| Cook | 29262988 | 23 days ago | IN | 0 ETH | 0.00000037 | ||||
| Cook | 29202271 | 25 days ago | IN | 0 ETH | 0.00000062 | ||||
| Cook | 29135096 | 26 days ago | IN | 0 ETH | 0.00000014 | ||||
| Cook | 29093535 | 27 days ago | IN | 0 ETH | 0.00000059 | ||||
| Cook | 29093517 | 27 days ago | IN | 0 ETH | 0.00000055 | ||||
| Cook | 27913603 | 54 days ago | IN | 0 ETH | 0 | ||||
| Cook | 27423611 | 66 days ago | IN | 0 ETH | 0.00000023 | ||||
| Cook | 26698807 | 83 days ago | IN | 0 ETH | 0 | ||||
| Cook | 25866323 | 102 days ago | IN | 0 ETH | 0.00000027 | ||||
| Cook | 25379349 | 113 days ago | IN | 0 ETH | 0 | ||||
| Cook | 25307270 | 115 days ago | IN | 0 ETH | 0.00000024 | ||||
| Cook | 25303976 | 115 days ago | IN | 0 ETH | 0.00000001 | ||||
| Cook | 25213091 | 117 days ago | IN | 0 ETH | 0 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 15865865 | 333 days ago | 0.001 ETH | ||||
| 15458130 | 343 days ago | 4 ETH | ||||
| 14684823 | 361 days ago | 0.33 ETH | ||||
| 14684718 | 361 days ago | 0.3887 ETH | ||||
| 14624702 | 362 days ago | 0.376 ETH | ||||
| 14085352 | 375 days ago | 0.292 ETH | ||||
| 14064749 | 375 days ago | 0.36653 ETH | ||||
| 13990540 | 377 days ago | 0.1673343 ETH | ||||
| 13989141 | 377 days ago | 0.31 ETH | ||||
| 13985064 | 377 days ago | 0.3 ETH | ||||
| 13782669 | 382 days ago | 0.18128331 ETH | ||||
| 13782588 | 382 days ago | 0.22947683 ETH | ||||
| 13782467 | 382 days ago | 0.29079452 ETH | ||||
| 13782369 | 382 days ago | 0.10777744 ETH | ||||
| 13776259 | 382 days ago | 0.21290667 ETH | ||||
| 13776172 | 382 days ago | 0.29824842 ETH | ||||
| 13775900 | 382 days ago | 0.29315437 ETH | ||||
| 13775779 | 382 days ago | 0.43167926 ETH | ||||
| 13775624 | 382 days ago | 0.318 ETH | ||||
| 13775496 | 382 days ago | 0.19 ETH | ||||
| 13775374 | 382 days ago | 0.21 ETH | ||||
| 13775273 | 382 days ago | 0.27 ETH | ||||
| 13775163 | 382 days ago | 0.09025925 ETH | ||||
| 13775036 | 382 days ago | 0.22263592 ETH | ||||
| 13774953 | 382 days ago | 0.30200837 ETH |
Cross-Chain Transactions
Loading...
Loading
Minimal Proxy Contract for 0x802762e604ce08a79da2ba809281d727a690fa0d
Contract Name:
BlastCauldronV4
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 400 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;
import {IERC20} from "BoringSolidity/interfaces/IERC20.sol";
import {BlastYields} from "/blast/libraries/BlastYields.sol";
import {Router} from "/mimswap/periphery/Router.sol";
import {Factory} from "/mimswap/periphery/Factory.sol";
import {IFeeRateModel} from "/mimswap/interfaces/IFeeRateModel.sol";
import {CauldronV4} from "cauldrons/CauldronV4.sol";
import {IWETH} from "interfaces/IWETH.sol";
import {IBentoBoxV1} from "interfaces/IBentoBoxV1.sol";
import {IFactory} from "/mimswap/interfaces/IFactory.sol";
/// @dev Collection of Blast wrapped contract that are succecptible to be used
/// enough to justify claiming gas yields.
error ErrZeroAddress();
contract BlastMIMSwapRouter is Router {
constructor(IWETH weth_, IFactory factory, address governor_) Router(weth_, factory) {
if (governor_ == address(0)) {
revert ErrZeroAddress();
}
BlastYields.configureDefaultClaimables(governor_);
}
}
contract BlastMIMSwapFactory is Factory {
constructor(
address implementation_,
IFeeRateModel maintainerFeeRateModel_,
address owner_,
address governor_
) Factory(implementation_, maintainerFeeRateModel_, owner_) {
if (governor_ == address(0)) {
revert ErrZeroAddress();
}
BlastYields.configureDefaultClaimables(governor_);
}
}
contract BlastCauldronV4 is CauldronV4 {
error ErrInvalidGovernorAddress();
address private immutable _governor;
constructor(address box_, address mim_, address governor_) CauldronV4(IBentoBoxV1(box_), IERC20(mim_)) {
if (governor_ == address(0)) {
revert ErrZeroAddress();
}
if (governor_ == address(this)) {
revert ErrInvalidGovernorAddress();
}
_governor = governor_;
_setupBlacklist();
}
function init(bytes calldata data) public payable override {
if (_governor == address(this)) {
revert ErrInvalidGovernorAddress();
}
_setupBlacklist();
super.init(data);
BlastYields.configureDefaultClaimables(_governor);
}
function _setupBlacklist() private {
blacklistedCallees[address(BlastYields.BLAST_YIELD_PRECOMPILE)] = true;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC20 {
// transfer and tranferFrom have been removed, because they don't work on all tokens (some aren't ERC20 complaint).
// By removing them you can't accidentally use them.
// name, symbol and decimals have been removed, because they are optional and sometimes wrongly implemented (MKR).
// Use BoringERC20 with `using BoringERC20 for IERC20` and call `safeTransfer`, `safeTransferFrom`, etc instead.
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
/// @notice EIP 2612
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
interface IStrictERC20 {
// This is the strict ERC20 interface. Don't use this, certainly not if you don't control the ERC20 token you're calling.
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address _owner) external view returns (uint256 balance);
function transfer(address _to, uint256 _value) external returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
/// @notice EIP 2612
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;
import {IBlast, IERC20Rebasing, YieldMode, GasMode} from "interfaces/IBlast.sol";
library BlastYields {
event LogBlastGasClaimed(address indexed recipient, uint256 amount);
event LogBlastETHClaimed(address indexed recipient, uint256 amount);
event LogBlastTokenClaimed(address indexed recipient, address indexed token, uint256 amount);
event LogBlastTokenClaimableEnabled(address indexed contractAddress, address indexed token);
event LogBlastNativeClaimableEnabled(address indexed contractAddress);
IBlast constant public BLAST_YIELD_PRECOMPILE = IBlast(0x4300000000000000000000000000000000000002);
function claimAllGasYields(address recipient) internal returns (uint256) {
return claimAllGasYields(address(this), recipient);
}
function claimAllNativeYields(address recipient) internal returns (uint256 amount) {
return claimAllNativeYields(address(this), recipient);
}
function claimAllGasYields(address contractAddress, address recipient) internal returns (uint256 amount) {
amount = BLAST_YIELD_PRECOMPILE.claimAllGas(contractAddress, recipient);
emit LogBlastGasClaimed(recipient, amount);
}
function claimAllNativeYields(address contractAddress, address recipient) internal returns (uint256 amount) {
amount = BLAST_YIELD_PRECOMPILE.claimAllYield(contractAddress, recipient);
emit LogBlastETHClaimed(recipient, amount);
}
function claimNativeYields(address recipient, uint256 amount) internal returns (uint256) {
return claimNativeYields(address(this), amount, recipient);
}
function claimNativeYields(address contractAddress, uint256 amount, address recipient) internal returns (uint256) {
amount = BLAST_YIELD_PRECOMPILE.claimYield(contractAddress, recipient, amount);
emit LogBlastETHClaimed(recipient, amount);
return amount;
}
function claimAllTokenYields(address token, address recipient) internal returns (uint256 amount) {
amount = IERC20Rebasing(token).claim(recipient, IERC20Rebasing(token).getClaimableAmount(address(this)));
emit LogBlastTokenClaimed(recipient, address(token), amount);
}
function claimTokenYields(address token, uint256 amount, address recipient) internal returns (uint256) {
amount = IERC20Rebasing(token).claim(recipient, amount);
emit LogBlastTokenClaimed(recipient, address(token), amount);
return amount;
}
function enableTokenClaimable(address token) internal {
if (IERC20Rebasing(token).getConfiguration(address(this)) == YieldMode.CLAIMABLE) {
return;
}
IERC20Rebasing(token).configure(YieldMode.CLAIMABLE);
emit LogBlastTokenClaimableEnabled(address(this), token);
}
function configureDefaultClaimables(address governor_) internal {
BLAST_YIELD_PRECOMPILE.configure(YieldMode.CLAIMABLE, GasMode.CLAIMABLE, governor_);
emit LogBlastNativeClaimableEnabled(address(this));
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {IERC20} from "openzeppelin-contracts/interfaces/IERC20.sol";
import {DecimalMath} from "/mimswap/libraries/DecimalMath.sol";
import {IWETH} from "interfaces/IWETH.sol";
import {IMagicLP} from "/mimswap/interfaces/IMagicLP.sol";
import {IFactory} from "/mimswap/interfaces/IFactory.sol";
contract Router {
using SafeTransferLib for address;
using SafeTransferLib for address payable;
error ErrNotETHLP();
error ErrExpired();
error ErrZeroAddress();
error ErrPathTooLong();
error ErrEmptyPath();
error ErrBadPath();
error ErrTooHighSlippage(uint256 amountOut);
error ErrInvalidBaseToken();
error ErrInvalidQuoteToken();
error ErrInTokenNotETH();
error ErrOutTokenNotETH();
IWETH public immutable weth;
IFactory public immutable factory;
receive() external payable {}
constructor(IWETH weth_, IFactory factory_) {
if (address(weth_) == address(0) || address(factory_) == address(0)) {
revert ErrZeroAddress();
}
weth = weth_;
factory = factory_;
}
modifier ensureDeadline(uint256 deadline) {
if (block.timestamp > deadline) {
revert ErrExpired();
}
_;
}
function createPool(
address baseToken,
address quoteToken,
uint256 lpFeeRate,
uint256 i,
uint256 k,
address to,
uint256 baseInAmount,
uint256 quoteInAmount
) external returns (address clone, uint256 shares) {
clone = IFactory(factory).create(baseToken, quoteToken, lpFeeRate, i, k);
baseToken.safeTransferFrom(msg.sender, clone, baseInAmount);
quoteToken.safeTransferFrom(msg.sender, clone, quoteInAmount);
(shares, , ) = IMagicLP(clone).buyShares(to);
}
function createPoolETH(
address token,
bool useTokenAsQuote,
uint256 lpFeeRate,
uint256 i,
uint256 k,
address to,
uint256 tokenInAmount
) external payable returns (address clone, uint256 shares) {
clone = IFactory(factory).create(useTokenAsQuote ? address(weth) : token, useTokenAsQuote ? token : address(weth), lpFeeRate, i, k);
weth.deposit{value: msg.value}();
token.safeTransferFrom(msg.sender, clone, tokenInAmount);
address(weth).safeTransferFrom(msg.sender, clone, msg.value);
(shares, , ) = IMagicLP(clone).buyShares(to);
}
function previewAddLiquidity(
address lp,
uint256 baseInAmount,
uint256 quoteInAmount
) external view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
(uint256 baseReserve, uint256 quoteReserve) = IMagicLP(lp).getReserves();
uint256 baseBalance = IMagicLP(lp)._BASE_TOKEN_().balanceOf(address(lp)) + baseInAmount;
uint256 quoteBalance = IMagicLP(lp)._QUOTE_TOKEN_().balanceOf(address(lp)) + baseInAmount;
baseInAmount = baseBalance - baseReserve;
quoteInAmount = quoteBalance - quoteReserve;
if (baseInAmount == 0) {
return (0, 0, 0);
}
uint256 totalSupply = IERC20(lp).totalSupply();
if (totalSupply == 0) {
if (quoteBalance == 0) {
return (0, 0, 0);
}
uint256 i = IMagicLP(lp)._I_();
shares = quoteBalance < DecimalMath.mulFloor(baseBalance, i) ? DecimalMath.divFloor(quoteBalance, i) : baseBalance;
baseAdjustedInAmount = shares;
quoteAdjustedInAmount = DecimalMath.mulFloor(shares, i);
if (shares <= 2001) {
return (0, 0, 0);
}
shares -= 1001;
} else if (baseReserve > 0 && quoteReserve > 0) {
uint256 baseInputRatio = DecimalMath.divFloor(baseInAmount, baseReserve);
uint256 quoteInputRatio = DecimalMath.divFloor(quoteInAmount, quoteReserve);
if (baseInputRatio <= quoteInputRatio) {
baseAdjustedInAmount = baseInAmount;
quoteAdjustedInAmount = DecimalMath.mulFloor(quoteReserve, baseInputRatio);
shares = DecimalMath.mulFloor(totalSupply, baseInputRatio);
} else {
quoteAdjustedInAmount = quoteInAmount;
baseAdjustedInAmount = DecimalMath.mulFloor(baseReserve, quoteInputRatio);
shares = DecimalMath.mulFloor(totalSupply, quoteInputRatio);
}
}
}
function addLiquidity(
address lp,
address to,
uint256 baseInAmount,
uint256 quoteInAmount,
uint256 minimumShares,
uint256 deadline
) external ensureDeadline(deadline) returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
(baseAdjustedInAmount, quoteAdjustedInAmount) = _adjustAddLiquidity(lp, baseInAmount, quoteInAmount);
IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, baseAdjustedInAmount);
IMagicLP(lp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, lp, quoteAdjustedInAmount);
shares = _addLiquidity(lp, to, minimumShares);
}
function addLiquidityUnsafe(
address lp,
address to,
uint256 baseInAmount,
uint256 quoteInAmount,
uint256 minimumShares,
uint256 deadline
) external ensureDeadline(deadline) returns (uint256 shares) {
IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, baseInAmount);
IMagicLP(lp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, lp, quoteInAmount);
return _addLiquidity(lp, to, minimumShares);
}
function addLiquidityETH(
address lp,
address to,
address payable refundTo,
uint256 tokenInAmount,
uint256 minimumShares,
uint256 deadline
) external payable ensureDeadline(deadline) returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount, uint256 shares) {
uint256 wethAdjustedAmount;
uint256 tokenAdjustedAmount;
address token = IMagicLP(lp)._BASE_TOKEN_();
if (token == address(weth)) {
token = IMagicLP(lp)._QUOTE_TOKEN_();
(baseAdjustedInAmount, quoteAdjustedInAmount) = _adjustAddLiquidity(lp, msg.value, tokenInAmount);
wethAdjustedAmount = baseAdjustedInAmount;
tokenAdjustedAmount = quoteAdjustedInAmount;
} else if (IMagicLP(lp)._QUOTE_TOKEN_() == address(weth)) {
(baseAdjustedInAmount, quoteAdjustedInAmount) = _adjustAddLiquidity(lp, tokenInAmount, msg.value);
wethAdjustedAmount = quoteAdjustedInAmount;
tokenAdjustedAmount = baseAdjustedInAmount;
} else {
revert ErrNotETHLP();
}
weth.deposit{value: wethAdjustedAmount}();
address(weth).safeTransfer(lp, wethAdjustedAmount);
// Refund unused ETH
if (msg.value > wethAdjustedAmount) {
refundTo.safeTransferETH(msg.value - wethAdjustedAmount);
}
token.safeTransferFrom(msg.sender, lp, tokenAdjustedAmount);
shares = _addLiquidity(lp, to, minimumShares);
}
function addLiquidityETHUnsafe(
address lp,
address to,
uint256 tokenInAmount,
uint256 minimumShares,
uint256 deadline
) external payable ensureDeadline(deadline) returns (uint256 shares) {
address token = IMagicLP(lp)._BASE_TOKEN_();
if (token == address(weth)) {
token = IMagicLP(lp)._QUOTE_TOKEN_();
} else if (IMagicLP(lp)._QUOTE_TOKEN_() != address(weth)) {
revert ErrNotETHLP();
}
weth.deposit{value: msg.value}();
address(weth).safeTransfer(lp, msg.value);
token.safeTransferFrom(msg.sender, lp, tokenInAmount);
return _addLiquidity(lp, to, minimumShares);
}
function previewRemoveLiquidity(address lp, uint256 sharesIn) external view returns (uint256 baseAmountOut, uint256 quoteAmountOut) {
uint256 baseBalance = IMagicLP(lp)._BASE_TOKEN_().balanceOf(address(lp));
uint256 quoteBalance = IMagicLP(lp)._QUOTE_TOKEN_().balanceOf(address(lp));
uint256 totalShares = IERC20(lp).totalSupply();
baseAmountOut = (baseBalance * sharesIn) / totalShares;
quoteAmountOut = (quoteBalance * sharesIn) / totalShares;
}
function removeLiquidity(
address lp,
address to,
uint256 sharesIn,
uint256 minimumBaseAmount,
uint256 minimumQuoteAmount,
uint256 deadline
) external returns (uint256 baseAmountOut, uint256 quoteAmountOut) {
lp.safeTransferFrom(msg.sender, address(this), sharesIn);
return IMagicLP(lp).sellShares(sharesIn, to, minimumBaseAmount, minimumQuoteAmount, "", deadline);
}
function removeLiquidityETH(
address lp,
address to,
uint256 sharesIn,
uint256 minimumETHAmount,
uint256 minimumTokenAmount,
uint256 deadline
) external returns (uint256 ethAmountOut, uint256 tokenAmountOut) {
lp.safeTransferFrom(msg.sender, address(this), sharesIn);
address token = IMagicLP(lp)._BASE_TOKEN_();
if (token == address(weth)) {
token = IMagicLP(lp)._QUOTE_TOKEN_();
(ethAmountOut, tokenAmountOut) = IMagicLP(lp).sellShares(
sharesIn,
address(this),
minimumETHAmount,
minimumTokenAmount,
"",
deadline
);
} else if (IMagicLP(lp)._QUOTE_TOKEN_() == address(weth)) {
(tokenAmountOut, ethAmountOut) = IMagicLP(lp).sellShares(
sharesIn,
address(this),
minimumTokenAmount,
minimumETHAmount,
"",
deadline
);
} else {
revert ErrNotETHLP();
}
weth.withdraw(ethAmountOut);
to.safeTransferETH(ethAmountOut);
token.safeTransfer(to, tokenAmountOut);
}
function swapTokensForTokens(
address to,
uint256 amountIn,
address[] calldata path,
uint256 directions,
uint256 minimumOut,
uint256 deadline
) external ensureDeadline(deadline) returns (uint256 amountOut) {
_validatePath(path);
address firstLp = path[0];
// Transfer to the first LP
if (directions & 1 == 0) {
IMagicLP(firstLp)._BASE_TOKEN_().safeTransferFrom(msg.sender, address(firstLp), amountIn);
} else {
IMagicLP(firstLp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, address(firstLp), amountIn);
}
return _swap(to, path, directions, minimumOut);
}
function swapETHForTokens(
address to,
address[] calldata path,
uint256 directions,
uint256 minimumOut,
uint256 deadline
) external payable ensureDeadline(deadline) returns (uint256 amountOut) {
_validatePath(path);
address firstLp = path[0];
address inToken;
if (directions & 1 == 0) {
inToken = IMagicLP(firstLp)._BASE_TOKEN_();
} else {
inToken = IMagicLP(firstLp)._QUOTE_TOKEN_();
}
// Transfer to the first LP
if (inToken != address(weth)) {
revert ErrInTokenNotETH();
}
weth.deposit{value: msg.value}();
inToken.safeTransfer(address(firstLp), msg.value);
return _swap(to, path, directions, minimumOut);
}
function swapTokensForETH(
address to,
uint256 amountIn,
address[] calldata path,
uint256 directions,
uint256 minimumOut,
uint256 deadline
) external ensureDeadline(deadline) returns (uint256 amountOut) {
_validatePath(path);
uint256 lastLpIndex = path.length - 1;
address lastLp = path[lastLpIndex];
address outToken;
if ((directions >> lastLpIndex) & 1 == 0) {
outToken = IMagicLP(lastLp)._QUOTE_TOKEN_();
} else {
outToken = IMagicLP(lastLp)._BASE_TOKEN_();
}
if (outToken != address(weth)) {
revert ErrOutTokenNotETH();
}
address firstLp = path[0];
// Transfer to the first LP
if (directions & 1 == 0) {
IMagicLP(firstLp)._BASE_TOKEN_().safeTransferFrom(msg.sender, firstLp, amountIn);
} else {
IMagicLP(firstLp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, firstLp, amountIn);
}
amountOut = _swap(address(this), path, directions, minimumOut);
weth.withdraw(amountOut);
to.safeTransferETH(amountOut);
}
function sellBaseTokensForTokens(
address lp,
address to,
uint256 amountIn,
uint256 minimumOut,
uint256 deadline
) external ensureDeadline(deadline) returns (uint256 amountOut) {
IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, amountIn);
return _sellBase(lp, to, minimumOut);
}
function sellBaseETHForTokens(
address lp,
address to,
uint256 minimumOut,
uint256 deadline
) external payable ensureDeadline(deadline) returns (uint256 amountOut) {
address baseToken = IMagicLP(lp)._BASE_TOKEN_();
if (baseToken != address(weth)) {
revert ErrInvalidBaseToken();
}
weth.deposit{value: msg.value}();
baseToken.safeTransfer(lp, msg.value);
return _sellBase(lp, to, minimumOut);
}
function sellBaseTokensForETH(
address lp,
address to,
uint256 amountIn,
uint256 minimumOut,
uint256 deadline
) external ensureDeadline(deadline) returns (uint256 amountOut) {
if (IMagicLP(lp)._QUOTE_TOKEN_() != address(weth)) {
revert ErrInvalidQuoteToken();
}
IMagicLP(lp)._BASE_TOKEN_().safeTransferFrom(msg.sender, lp, amountIn);
amountOut = _sellBase(lp, address(this), minimumOut);
weth.withdraw(amountOut);
to.safeTransferETH(amountOut);
}
function sellQuoteTokensForTokens(
address lp,
address to,
uint256 amountIn,
uint256 minimumOut,
uint256 deadline
) external ensureDeadline(deadline) returns (uint256 amountOut) {
IMagicLP(lp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, lp, amountIn);
return _sellQuote(lp, to, minimumOut);
}
function sellQuoteETHForTokens(
address lp,
address to,
uint256 minimumOut,
uint256 deadline
) external payable ensureDeadline(deadline) returns (uint256 amountOut) {
address quoteToken = IMagicLP(lp)._QUOTE_TOKEN_();
if (quoteToken != address(weth)) {
revert ErrInvalidQuoteToken();
}
weth.deposit{value: msg.value}();
quoteToken.safeTransfer(lp, msg.value);
return _sellQuote(lp, to, minimumOut);
}
function sellQuoteTokensForETH(
address lp,
address to,
uint256 amountIn,
uint256 minimumOut,
uint256 deadline
) external ensureDeadline(deadline) returns (uint256 amountOut) {
if (IMagicLP(lp)._BASE_TOKEN_() != address(weth)) {
revert ErrInvalidBaseToken();
}
IMagicLP(lp)._QUOTE_TOKEN_().safeTransferFrom(msg.sender, lp, amountIn);
amountOut = _sellQuote(lp, address(this), minimumOut);
weth.withdraw(amountOut);
to.safeTransferETH(amountOut);
}
//////////////////////////////////////////////////////////////////////////////////////
/// INTERNALS
//////////////////////////////////////////////////////////////////////////////////////
function _addLiquidity(address lp, address to, uint256 minimumShares) internal returns (uint256 shares) {
(shares, , ) = IMagicLP(lp).buyShares(to);
if (shares < minimumShares) {
revert ErrTooHighSlippage(shares);
}
}
/// Adapted from: https://github.com/DODOEX/contractV2/blob/main/contracts/SmartRoute/proxies/DODODspProxy.sol
/// Copyright 2020 DODO ZOO. Licensed under Apache-2.0.
function _adjustAddLiquidity(
address lp,
uint256 baseInAmount,
uint256 quoteInAmount
) internal view returns (uint256 baseAdjustedInAmount, uint256 quoteAdjustedInAmount) {
if (IERC20(lp).totalSupply() == 0) {
uint256 i = IMagicLP(lp)._I_();
uint256 shares = quoteInAmount < DecimalMath.mulFloor(baseInAmount, i) ? DecimalMath.divFloor(quoteInAmount, i) : baseInAmount;
baseAdjustedInAmount = shares;
quoteAdjustedInAmount = DecimalMath.mulFloor(shares, i);
} else {
(uint256 baseReserve, uint256 quoteReserve) = IMagicLP(lp).getReserves();
if (quoteReserve > 0 && baseReserve > 0) {
uint256 baseIncreaseRatio = DecimalMath.divFloor(baseInAmount, baseReserve);
uint256 quoteIncreaseRatio = DecimalMath.divFloor(quoteInAmount, quoteReserve);
if (baseIncreaseRatio <= quoteIncreaseRatio) {
baseAdjustedInAmount = baseInAmount;
quoteAdjustedInAmount = DecimalMath.mulFloor(quoteReserve, baseIncreaseRatio);
} else {
quoteAdjustedInAmount = quoteInAmount;
baseAdjustedInAmount = DecimalMath.mulFloor(baseReserve, quoteIncreaseRatio);
}
}
}
}
function _swap(address to, address[] calldata path, uint256 directions, uint256 minimumOut) internal returns (uint256 amountOut) {
uint256 iterations = path.length - 1; // Subtract by one as last swap is done separately
for (uint256 i = 0; i < iterations; ) {
if (directions & 1 == 0) {
// Sell base
IMagicLP(path[i]).sellBase(address(path[i + 1]));
} else {
// Sell quote
IMagicLP(path[i]).sellQuote(address(path[i + 1]));
}
directions >>= 1;
unchecked {
++i;
}
}
if ((directions & 1 == 0)) {
amountOut = IMagicLP(path[iterations]).sellBase(to);
} else {
amountOut = IMagicLP(path[iterations]).sellQuote(to);
}
if (amountOut < minimumOut) {
revert ErrTooHighSlippage(amountOut);
}
}
function _sellBase(address lp, address to, uint256 minimumOut) internal returns (uint256 amountOut) {
amountOut = IMagicLP(lp).sellBase(to);
if (amountOut < minimumOut) {
revert ErrTooHighSlippage(amountOut);
}
}
function _sellQuote(address lp, address to, uint256 minimumOut) internal returns (uint256 amountOut) {
amountOut = IMagicLP(lp).sellQuote(to);
if (amountOut < minimumOut) {
revert ErrTooHighSlippage(amountOut);
}
}
function _validatePath(address[] calldata path) internal pure {
uint256 pathLength = path.length;
// Max 256 because of bits in directions
if (pathLength > 256) {
revert ErrPathTooLong();
}
if (pathLength <= 0) {
revert ErrEmptyPath();
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;
import {Owned} from "solmate/auth/Owned.sol";
import {LibClone} from "solady/utils/LibClone.sol";
import {IFeeRateModel} from "/mimswap/interfaces/IFeeRateModel.sol";
import {IMagicLP} from "/mimswap/interfaces/IMagicLP.sol";
import {MagicLP} from "/mimswap/MagicLP.sol";
/// @notice Create and register MagicLP pools
contract Factory is Owned {
event LogCreated(
address clone_,
address indexed baseToken_,
address indexed quoteToken_,
address indexed creator_,
uint256 lpFeeRate_,
IFeeRateModel maintainerFeeRateModel,
uint256 i_,
uint256 k_
);
event LogPoolAdded(address baseToken, address quoteToken, address creator, address pool);
event LogPoolRemoved(address pool);
event LogSetImplementation(address indexed implementation);
event LogSetMaintainer(address indexed newMaintainer);
event LogSetMaintainerFeeRateModel(IFeeRateModel newMaintainerFeeRateModel);
error ErrInvalidUserPoolIndex();
error ErrZeroAddress();
address public implementation;
IFeeRateModel public maintainerFeeRateModel;
mapping(address base => mapping(address quote => address[] pools)) public pools;
mapping(address creator => address[] pools) public userPools;
constructor(address implementation_, IFeeRateModel maintainerFeeRateModel_, address owner_) Owned(owner_) {
if (implementation_ == address(0)) {
revert ErrZeroAddress();
}
if (address(maintainerFeeRateModel_) == address(0)) {
revert ErrZeroAddress();
}
implementation = implementation_;
maintainerFeeRateModel = maintainerFeeRateModel_;
}
//////////////////////////////////////////////////////////////////////////////////////
/// VIEWS
//////////////////////////////////////////////////////////////////////////////////////
function getPoolCount(address token0, address token1) external view returns (uint256) {
return pools[token0][token1].length;
}
function getUserPoolCount(address creator) external view returns (uint256) {
return userPools[creator].length;
}
//////////////////////////////////////////////////////////////////////////////////////
/// PUBLIC
//////////////////////////////////////////////////////////////////////////////////////
function predictDeterministicAddress(
address baseToken_,
address quoteToken_,
uint256 lpFeeRate_,
uint256 i_,
uint256 k_
) public view returns (address) {
return
LibClone.predictDeterministicAddress(implementation, _computeSalt(baseToken_, quoteToken_, lpFeeRate_, i_, k_), address(this));
}
function create(address baseToken_, address quoteToken_, uint256 lpFeeRate_, uint256 i_, uint256 k_) external returns (address clone) {
bytes32 salt = _computeSalt(baseToken_, quoteToken_, lpFeeRate_, i_, k_);
clone = LibClone.cloneDeterministic(address(implementation), salt);
IMagicLP(clone).init(address(baseToken_), address(quoteToken_), lpFeeRate_, address(maintainerFeeRateModel), i_, k_);
emit LogCreated(clone, baseToken_, quoteToken_, msg.sender, lpFeeRate_, maintainerFeeRateModel, i_, k_);
_addPool(msg.sender, baseToken_, quoteToken_, clone);
}
//////////////////////////////////////////////////////////////////////////////////////
/// ADMIN
//////////////////////////////////////////////////////////////////////////////////////
function setLpImplementation(address implementation_) external onlyOwner {
if (implementation_ == address(0)) {
revert ErrZeroAddress();
}
implementation = implementation_;
emit LogSetImplementation(implementation_);
}
function setMaintainerFeeRateModel(IFeeRateModel maintainerFeeRateModel_) external onlyOwner {
if (address(maintainerFeeRateModel_) == address(0)) {
revert ErrZeroAddress();
}
maintainerFeeRateModel = maintainerFeeRateModel_;
emit LogSetMaintainerFeeRateModel(maintainerFeeRateModel_);
}
/// @notice Register a pool to the list
/// Note this doesn't check if the pool is valid or if it's already registered.
function addPool(address creator, address baseToken, address quoteToken, address pool) external onlyOwner {
_addPool(creator, baseToken, quoteToken, pool);
}
function removePool(
address creator,
address baseToken,
address quoteToken,
uint256 poolIndex,
uint256 userPoolIndex
) external onlyOwner {
address[] storage _pools = pools[baseToken][quoteToken];
address pool = _pools[poolIndex];
address[] storage _userPools = userPools[creator];
_pools[poolIndex] = _pools[_pools.length - 1];
_pools.pop();
if (_userPools[userPoolIndex] != pool) {
revert ErrInvalidUserPoolIndex();
}
_userPools[userPoolIndex] = _userPools[_userPools.length - 1];
_userPools.pop();
emit LogPoolRemoved(pool);
}
//////////////////////////////////////////////////////////////////////////////////////
/// INTERNALS
//////////////////////////////////////////////////////////////////////////////////////
function _addPool(address creator, address baseToken, address quoteToken, address pool) internal {
pools[baseToken][quoteToken].push(pool);
userPools[creator].push(pool);
emit LogPoolAdded(baseToken, quoteToken, creator, pool);
}
function _computeSalt(
address baseToken_,
address quoteToken_,
uint256 lpFeeRate_,
uint256 i_,
uint256 k_
) internal view returns (bytes32) {
return keccak256(abi.encodePacked(implementation, baseToken_, quoteToken_, lpFeeRate_, i_, k_));
}
}/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity >=0.8.0;
interface IFeeRateImpl {
function getFeeRate(
address pool,
address trader,
uint256 lpFeeRate
) external view returns (uint256 adjustedLpFeeRate, uint256 mtFeeRate);
}
interface IFeeRateModel {
function maintainer() external view returns (address);
function getFeeRate(address trader, uint256 lpFeeRate) external view returns (uint256 adjustedLpFeeRate, uint256 mtFeeRate);
}// SPDX-License-Identifier: UNLICENSED
// Cauldron
// ( ( (
// )\ ) ( )\ )\ ) (
// (((_) ( /( ))\ ((_)(()/( )( ( (
// )\___ )(_)) /((_) _ ((_))(()\ )\ )\ )
// ((/ __|((_)_ (_))( | | _| | ((_) ((_) _(_/(
// | (__ / _` || || || |/ _` | | '_|/ _ \| ' \))
// \___|\__,_| \_,_||_|\__,_| |_| \___/|_||_|
pragma solidity >=0.8.0;
import {Owned} from "solmate/auth/Owned.sol";
import {IERC20} from "BoringSolidity/interfaces/IERC20.sol";
import {IOracle} from "interfaces/IOracle.sol";
import {ISwapperV2} from "interfaces/ISwapperV2.sol";
import {IBentoBoxV1} from "interfaces/IBentoBoxV1.sol";
import {IMasterContract} from "BoringSolidity/interfaces/IMasterContract.sol";
import {RebaseLibrary, Rebase} from "BoringSolidity/libraries/BoringRebase.sol";
import {BoringMath, BoringMath128} from "BoringSolidity/libraries/BoringMath.sol";
// solhint-disable avoid-low-level-calls
// solhint-disable no-inline-assembly
/// @title Cauldron
/// @dev This contract allows contract calls to any contract (except BentoBox)
/// from arbitrary callers thus, don't trust calls from this contract in any circumstances.
contract CauldronV4 is Owned, IMasterContract {
using BoringMath for uint256;
using BoringMath128 for uint128;
using RebaseLibrary for Rebase;
event LogExchangeRate(uint256 rate);
event LogAccrue(uint128 accruedAmount);
event LogAddCollateral(address indexed from, address indexed to, uint256 share);
event LogRemoveCollateral(address indexed from, address indexed to, uint256 share);
event LogBorrow(address indexed from, address indexed to, uint256 amount, uint256 part);
event LogRepay(address indexed from, address indexed to, uint256 amount, uint256 part);
event LogFeeTo(address indexed newFeeTo);
event LogWithdrawFees(address indexed feeTo, uint256 feesEarnedFraction);
event LogInterestChange(uint64 oldInterestRate, uint64 newInterestRate);
event LogChangeBorrowLimit(uint128 newLimit, uint128 perAddressPart);
event LogChangeBlacklistedCallee(address indexed account, bool blacklisted);
event LogLiquidationMultiplierChanged(uint256 previous, uint256 current);
event LogBorrowOpeningFeeChanged(uint256 previous, uint256 current);
event LogCollateralizationRateChanged(uint256 previous, uint256 current);
event LogLiquidation(
address indexed from,
address indexed user,
address indexed to,
uint256 collateralShare,
uint256 borrowAmount,
uint256 borrowPart
);
// Immutables (for MasterContract and all clones)
IBentoBoxV1 public immutable bentoBox;
CauldronV4 public immutable masterContract;
IERC20 public immutable magicInternetMoney;
// MasterContract variables
address public feeTo;
// Per clone variables
// Clone init settings
IERC20 public collateral;
IOracle public oracle;
bytes public oracleData;
struct BorrowCap {
uint128 total;
uint128 borrowPartPerAddress;
}
BorrowCap public borrowLimit;
// Total amounts
uint256 public totalCollateralShare; // Total collateral supplied
Rebase public totalBorrow; // elastic = Total token amount to be repayed by borrowers, base = Total parts of the debt held by borrowers
// User balances
mapping(address => uint256) public userCollateralShare;
mapping(address => uint256) public userBorrowPart;
// Callee restrictions
mapping(address => bool) public blacklistedCallees;
/// @notice Exchange and interest rate tracking.
/// This is 'cached' here because calls to Oracles can be very expensive.
uint256 public exchangeRate;
struct AccrueInfo {
uint64 lastAccrued;
uint128 feesEarned;
uint64 INTEREST_PER_SECOND;
}
AccrueInfo public accrueInfo;
uint64 internal constant ONE_PERCENT_RATE = 317097920;
// Settings
uint256 public COLLATERIZATION_RATE;
uint256 internal constant COLLATERIZATION_RATE_PRECISION = 1e5; // Must be less than EXCHANGE_RATE_PRECISION (due to optimization in math)
uint256 internal constant EXCHANGE_RATE_PRECISION = 1e18;
uint256 public LIQUIDATION_MULTIPLIER;
uint256 internal constant LIQUIDATION_MULTIPLIER_PRECISION = 1e5;
uint256 public BORROW_OPENING_FEE;
uint256 internal constant BORROW_OPENING_FEE_PRECISION = 1e5;
uint256 internal constant DISTRIBUTION_PART = 10;
uint256 internal constant DISTRIBUTION_PRECISION = 100;
modifier onlyMasterContractOwner() {
require(msg.sender == masterContract.owner(), "Caller is not the owner");
_;
}
/// @notice The constructor is only used for the initial master contract. Subsequent clones are initialised via `init`.
constructor(IBentoBoxV1 bentoBox_, IERC20 magicInternetMoney_) Owned(msg.sender) {
bentoBox = bentoBox_;
magicInternetMoney = magicInternetMoney_;
masterContract = this;
blacklistedCallees[address(bentoBox)] = true;
blacklistedCallees[address(this)] = true;
blacklistedCallees[Owned(address(bentoBox)).owner()] = true;
}
/// @notice Serves as the constructor for clones, as clones can't have a regular constructor
/// @dev `data` is abi encoded in the format: (IERC20 collateral, IERC20 asset, IOracle oracle, bytes oracleData)
function init(bytes calldata data) public virtual payable override {
require(address(collateral) == address(0), "Cauldron: already initialized");
(collateral, oracle, oracleData, accrueInfo.INTEREST_PER_SECOND, LIQUIDATION_MULTIPLIER, COLLATERIZATION_RATE, BORROW_OPENING_FEE) = abi.decode(data, (IERC20, IOracle, bytes, uint64, uint256, uint256, uint256));
borrowLimit = BorrowCap(type(uint128).max, type(uint128).max);
require(address(collateral) != address(0), "Cauldron: bad pair");
magicInternetMoney.approve(address(bentoBox), type(uint256).max);
blacklistedCallees[address(bentoBox)] = true;
blacklistedCallees[address(this)] = true;
blacklistedCallees[Owned(address(bentoBox)).owner()] = true;
(, exchangeRate) = oracle.get(oracleData);
accrue();
}
/// @notice Accrues the interest on the borrowed tokens and handles the accumulation of fees.
function accrue() public {
AccrueInfo memory _accrueInfo = accrueInfo;
// Number of seconds since accrue was called
uint256 elapsedTime = block.timestamp - _accrueInfo.lastAccrued;
if (elapsedTime == 0) {
return;
}
_accrueInfo.lastAccrued = uint64(block.timestamp);
Rebase memory _totalBorrow = totalBorrow;
if (_totalBorrow.base == 0) {
accrueInfo = _accrueInfo;
return;
}
// Accrue interest
uint128 extraAmount = (uint256(_totalBorrow.elastic).mul(_accrueInfo.INTEREST_PER_SECOND).mul(elapsedTime) / 1e18).to128();
_totalBorrow.elastic = _totalBorrow.elastic.add(extraAmount);
_accrueInfo.feesEarned = _accrueInfo.feesEarned.add(extraAmount);
totalBorrow = _totalBorrow;
accrueInfo = _accrueInfo;
emit LogAccrue(extraAmount);
}
/// @notice Concrete implementation of `isSolvent`. Includes a third parameter to allow caching `exchangeRate`.
/// @param _exchangeRate The exchange rate. Used to cache the `exchangeRate` between calls.
function _isSolvent(address user, uint256 _exchangeRate) virtual internal view returns (bool) {
// accrue must have already been called!
uint256 borrowPart = userBorrowPart[user];
if (borrowPart == 0) return true;
uint256 collateralShare = userCollateralShare[user];
if (collateralShare == 0) return false;
Rebase memory _totalBorrow = totalBorrow;
return
bentoBox.toAmount(
collateral,
collateralShare.mul(EXCHANGE_RATE_PRECISION / COLLATERIZATION_RATE_PRECISION).mul(COLLATERIZATION_RATE),
false
) >=
// Moved exchangeRate here instead of dividing the other side to preserve more precision
borrowPart.mul(_totalBorrow.elastic).mul(_exchangeRate) / _totalBorrow.base;
}
function isSolvent(address user) public view returns (bool) {
return _isSolvent(user, exchangeRate);
}
/// @dev Checks if the user is solvent in the closed liquidation case at the end of the function body.
modifier solvent() {
_;
(, uint256 _exchangeRate) = updateExchangeRate();
require(_isSolvent(msg.sender, _exchangeRate), "Cauldron: user insolvent");
}
/// @notice Gets the exchange rate. I.e how much collateral to buy 1e18 asset.
/// This function is supposed to be invoked if needed because Oracle queries can be expensive.
/// @return updated True if `exchangeRate` was updated.
/// @return rate The new exchange rate.
function updateExchangeRate() public returns (bool updated, uint256 rate) {
(updated, rate) = oracle.get(oracleData);
if (updated) {
exchangeRate = rate;
emit LogExchangeRate(rate);
} else {
// Return the old rate if fetching wasn't successful
rate = exchangeRate;
}
}
/// @dev Helper function to move tokens.
/// @param token The ERC-20 token.
/// @param share The amount in shares to add.
/// @param total Grand total amount to deduct from this contract's balance. Only applicable if `skim` is True.
/// Only used for accounting checks.
/// @param skim If True, only does a balance check on this contract.
/// False if tokens from msg.sender in `bentoBox` should be transferred.
function _addTokens(
IERC20 token,
uint256 share,
uint256 total,
bool skim
) internal {
if (skim) {
require(share <= bentoBox.balanceOf(token, address(this)).sub(total), "Cauldron: Skim too much");
} else {
bentoBox.transfer(token, msg.sender, address(this), share);
}
}
function _afterAddCollateral(address user, uint256 collateralShare) internal virtual {}
/// @notice Adds `collateral` from msg.sender to the account `to`.
/// @param to The receiver of the tokens.
/// @param skim True if the amount should be skimmed from the deposit balance of msg.sender.x
/// False if tokens from msg.sender in `bentoBox` should be transferred.
/// @param share The amount of shares to add for `to`.
function addCollateral(
address to,
bool skim,
uint256 share
) public virtual {
userCollateralShare[to] = userCollateralShare[to].add(share);
uint256 oldTotalCollateralShare = totalCollateralShare;
totalCollateralShare = oldTotalCollateralShare.add(share);
_addTokens(collateral, share, oldTotalCollateralShare, skim);
_afterAddCollateral(to, share);
emit LogAddCollateral(skim ? address(bentoBox) : msg.sender, to, share);
}
function _afterRemoveCollateral(address from, address to, uint256 collateralShare) internal virtual {}
/// @dev Concrete implementation of `removeCollateral`.
function _removeCollateral(address to, uint256 share) internal virtual {
userCollateralShare[msg.sender] = userCollateralShare[msg.sender].sub(share);
totalCollateralShare = totalCollateralShare.sub(share);
_afterRemoveCollateral(msg.sender, to, share);
emit LogRemoveCollateral(msg.sender, to, share);
bentoBox.transfer(collateral, address(this), to, share);
}
/// @notice Removes `share` amount of collateral and transfers it to `to`.
/// @param to The receiver of the shares.
/// @param share Amount of shares to remove.
function removeCollateral(address to, uint256 share) public solvent {
// accrue must be called because we check solvency
accrue();
_removeCollateral(to, share);
}
function _preBorrowAction(address to, uint256 amount, uint256 newBorrowPart, uint256 part) internal virtual {
}
/// @dev Concrete implementation of `borrow`.
function _borrow(address to, uint256 amount) internal returns (uint256 part, uint256 share) {
uint256 feeAmount = amount.mul(BORROW_OPENING_FEE) / BORROW_OPENING_FEE_PRECISION; // A flat % fee is charged for any borrow
(totalBorrow, part) = totalBorrow.add(amount.add(feeAmount), true);
BorrowCap memory cap = borrowLimit;
require(totalBorrow.elastic <= cap.total, "Borrow Limit reached");
accrueInfo.feesEarned = accrueInfo.feesEarned.add(uint128(feeAmount));
uint256 newBorrowPart = userBorrowPart[msg.sender].add(part);
require(newBorrowPart <= cap.borrowPartPerAddress, "Borrow Limit reached");
_preBorrowAction(to, amount, newBorrowPart, part);
userBorrowPart[msg.sender] = newBorrowPart;
// As long as there are tokens on this contract you can 'mint'... this enables limiting borrows
share = bentoBox.toShare(magicInternetMoney, amount, false);
bentoBox.transfer(magicInternetMoney, address(this), to, share);
emit LogBorrow(msg.sender, to, amount.add(feeAmount), part);
}
/// @notice Sender borrows `amount` and transfers it to `to`.
/// @return part Total part of the debt held by borrowers.
/// @return share Total amount in shares borrowed.
function borrow(address to, uint256 amount) public solvent returns (uint256 part, uint256 share) {
accrue();
(part, share) = _borrow(to, amount);
}
/// @dev Concrete implementation of `repay`.
function _repay(
address to,
bool skim,
uint256 part
) internal returns (uint256 amount) {
(totalBorrow, amount) = totalBorrow.sub(part, true);
userBorrowPart[to] = userBorrowPart[to].sub(part);
uint256 share = bentoBox.toShare(magicInternetMoney, amount, true);
bentoBox.transfer(magicInternetMoney, skim ? address(bentoBox) : msg.sender, address(this), share);
emit LogRepay(skim ? address(bentoBox) : msg.sender, to, amount, part);
}
/// @notice Repays a loan.
/// @param to Address of the user this payment should go.
/// @param skim True if the amount should be skimmed from the deposit balance of msg.sender.
/// False if tokens from msg.sender in `bentoBox` should be transferred.
/// @param part The amount to repay. See `userBorrowPart`.
/// @return amount The total amount repayed.
function repay(
address to,
bool skim,
uint256 part
) public returns (uint256 amount) {
accrue();
amount = _repay(to, skim, part);
}
// Functions that need accrue to be called
uint8 internal constant ACTION_REPAY = 2;
uint8 internal constant ACTION_REMOVE_COLLATERAL = 4;
uint8 internal constant ACTION_BORROW = 5;
uint8 internal constant ACTION_GET_REPAY_SHARE = 6;
uint8 internal constant ACTION_GET_REPAY_PART = 7;
uint8 internal constant ACTION_ACCRUE = 8;
// Functions that don't need accrue to be called
uint8 internal constant ACTION_ADD_COLLATERAL = 10;
uint8 internal constant ACTION_UPDATE_EXCHANGE_RATE = 11;
// Function on BentoBox
uint8 internal constant ACTION_BENTO_DEPOSIT = 20;
uint8 internal constant ACTION_BENTO_WITHDRAW = 21;
uint8 internal constant ACTION_BENTO_TRANSFER = 22;
uint8 internal constant ACTION_BENTO_TRANSFER_MULTIPLE = 23;
uint8 internal constant ACTION_BENTO_SETAPPROVAL = 24;
// Any external call (except to BentoBox)
uint8 internal constant ACTION_CALL = 30;
uint8 internal constant ACTION_LIQUIDATE = 31;
// Custom cook actions
uint8 internal constant ACTION_CUSTOM_START_INDEX = 100;
int256 internal constant USE_VALUE1 = -1;
int256 internal constant USE_VALUE2 = -2;
/// @dev Helper function for choosing the correct value (`value1` or `value2`) depending on `inNum`.
function _num(
int256 inNum,
uint256 value1,
uint256 value2
) internal pure returns (uint256 outNum) {
outNum = inNum >= 0 ? uint256(inNum) : (inNum == USE_VALUE1 ? value1 : value2);
}
/// @dev Helper function for depositing into `bentoBox`.
function _bentoDeposit(
bytes memory data,
uint256 value,
uint256 value1,
uint256 value2
) internal returns (uint256, uint256) {
(IERC20 token, address to, int256 amount, int256 share) = abi.decode(data, (IERC20, address, int256, int256));
amount = int256(_num(amount, value1, value2)); // Done this way to avoid stack too deep errors
share = int256(_num(share, value1, value2));
return bentoBox.deposit{value: value}(token, msg.sender, to, uint256(amount), uint256(share));
}
/// @dev Helper function to withdraw from the `bentoBox`.
function _bentoWithdraw(
bytes memory data,
uint256 value1,
uint256 value2
) internal returns (uint256, uint256) {
(IERC20 token, address to, int256 amount, int256 share) = abi.decode(data, (IERC20, address, int256, int256));
return bentoBox.withdraw(token, msg.sender, to, _num(amount, value1, value2), _num(share, value1, value2));
}
/// @dev Helper function to perform a contract call and eventually extracting revert messages on failure.
/// Calls to `bentoBox` are not allowed for obvious security reasons.
/// This also means that calls made from this contract shall *not* be trusted.
function _call(
uint256 value,
bytes memory data,
uint256 value1,
uint256 value2
) internal returns (bytes memory, uint8) {
(address callee, bytes memory callData, bool useValue1, bool useValue2, uint8 returnValues) =
abi.decode(data, (address, bytes, bool, bool, uint8));
if (useValue1 && !useValue2) {
callData = abi.encodePacked(callData, value1);
} else if (!useValue1 && useValue2) {
callData = abi.encodePacked(callData, value2);
} else if (useValue1 && useValue2) {
callData = abi.encodePacked(callData, value1, value2);
}
require(!blacklistedCallees[callee], "Cauldron: can't call");
(bool success, bytes memory returnData) = callee.call{value: value}(callData);
require(success, "Cauldron: call failed");
return (returnData, returnValues);
}
struct CookStatus {
bool needsSolvencyCheck;
bool hasAccrued;
}
function _additionalCookAction(uint8 action, CookStatus memory, uint256 value, bytes memory data, uint256 value1, uint256 value2) internal virtual returns (bytes memory, uint8, CookStatus memory) {}
/// @notice Executes a set of actions and allows composability (contract calls) to other contracts.
/// @param actions An array with a sequence of actions to execute (see ACTION_ declarations).
/// @param values A one-to-one mapped array to `actions`. ETH amounts to send along with the actions.
/// Only applicable to `ACTION_CALL`, `ACTION_BENTO_DEPOSIT`.
/// @param datas A one-to-one mapped array to `actions`. Contains abi encoded data of function arguments.
/// @return value1 May contain the first positioned return value of the last executed action (if applicable).
/// @return value2 May contain the second positioned return value of the last executed action which returns 2 values (if applicable).
function cook(
uint8[] calldata actions,
uint256[] calldata values,
bytes[] calldata datas
) external payable returns (uint256 value1, uint256 value2) {
CookStatus memory status;
for (uint256 i = 0; i < actions.length; i++) {
uint8 action = actions[i];
if (!status.hasAccrued && action < 10) {
accrue();
status.hasAccrued = true;
}
if (action == ACTION_ADD_COLLATERAL) {
(int256 share, address to, bool skim) = abi.decode(datas[i], (int256, address, bool));
addCollateral(to, skim, _num(share, value1, value2));
} else if (action == ACTION_REPAY) {
(int256 part, address to, bool skim) = abi.decode(datas[i], (int256, address, bool));
_repay(to, skim, _num(part, value1, value2));
} else if (action == ACTION_REMOVE_COLLATERAL) {
(int256 share, address to) = abi.decode(datas[i], (int256, address));
_removeCollateral(to, _num(share, value1, value2));
status.needsSolvencyCheck = true;
} else if (action == ACTION_BORROW) {
(int256 amount, address to) = abi.decode(datas[i], (int256, address));
(value1, value2) = _borrow(to, _num(amount, value1, value2));
status.needsSolvencyCheck = true;
} else if (action == ACTION_UPDATE_EXCHANGE_RATE) {
(bool must_update, uint256 minRate, uint256 maxRate) = abi.decode(datas[i], (bool, uint256, uint256));
(bool updated, uint256 rate) = updateExchangeRate();
require((!must_update || updated) && rate > minRate && (maxRate == 0 || rate < maxRate), "Cauldron: rate not ok");
} else if (action == ACTION_BENTO_SETAPPROVAL) {
(address user, address _masterContract, bool approved, uint8 v, bytes32 r, bytes32 s) =
abi.decode(datas[i], (address, address, bool, uint8, bytes32, bytes32));
bentoBox.setMasterContractApproval(user, _masterContract, approved, v, r, s);
} else if (action == ACTION_BENTO_DEPOSIT) {
(value1, value2) = _bentoDeposit(datas[i], values[i], value1, value2);
} else if (action == ACTION_BENTO_WITHDRAW) {
(value1, value2) = _bentoWithdraw(datas[i], value1, value2);
} else if (action == ACTION_BENTO_TRANSFER) {
(IERC20 token, address to, int256 share) = abi.decode(datas[i], (IERC20, address, int256));
bentoBox.transfer(token, msg.sender, to, _num(share, value1, value2));
} else if (action == ACTION_BENTO_TRANSFER_MULTIPLE) {
(IERC20 token, address[] memory tos, uint256[] memory shares) = abi.decode(datas[i], (IERC20, address[], uint256[]));
bentoBox.transferMultiple(token, msg.sender, tos, shares);
} else if (action == ACTION_CALL) {
(bytes memory returnData, uint8 returnValues) = _call(values[i], datas[i], value1, value2);
if (returnValues == 1) {
(value1) = abi.decode(returnData, (uint256));
} else if (returnValues == 2) {
(value1, value2) = abi.decode(returnData, (uint256, uint256));
}
} else if (action == ACTION_GET_REPAY_SHARE) {
int256 part = abi.decode(datas[i], (int256));
value1 = bentoBox.toShare(magicInternetMoney, totalBorrow.toElastic(_num(part, value1, value2), true), true);
} else if (action == ACTION_GET_REPAY_PART) {
int256 amount = abi.decode(datas[i], (int256));
value1 = totalBorrow.toBase(_num(amount, value1, value2), false);
} else if (action == ACTION_LIQUIDATE) {
_cookActionLiquidate(datas[i]);
} else {
(bytes memory returnData, uint8 returnValues, CookStatus memory returnStatus) = _additionalCookAction(action, status, values[i], datas[i], value1, value2);
status = returnStatus;
if (returnValues == 1) {
(value1) = abi.decode(returnData, (uint256));
} else if (returnValues == 2) {
(value1, value2) = abi.decode(returnData, (uint256, uint256));
}
}
}
if (status.needsSolvencyCheck) {
(, uint256 _exchangeRate) = updateExchangeRate();
require(_isSolvent(msg.sender, _exchangeRate), "Cauldron: user insolvent");
}
}
function _cookActionLiquidate(bytes calldata data) internal {
(address[] memory users, uint256[] memory maxBorrowParts, address to, ISwapperV2 swapper, bytes memory swapperData) = abi.decode(data, (address[], uint256[], address, ISwapperV2, bytes));
liquidate(users, maxBorrowParts, to, swapper, swapperData);
}
function _beforeUsersLiquidated(address[] memory users, uint256[] memory maxBorrowPart) internal virtual {}
function _beforeUserLiquidated(address user, uint256 borrowPart, uint256 borrowAmount, uint256 collateralShare) internal virtual {}
function _afterUserLiquidated(address user, uint256 collateralShare) internal virtual {}
/// @notice Handles the liquidation of users' balances, once the users' amount of collateral is too low.
/// @param users An array of user addresses.
/// @param maxBorrowParts A one-to-one mapping to `users`, contains maximum (partial) borrow amounts (to liquidate) of the respective user.
/// @param to Address of the receiver in open liquidations if `swapper` is zero.
function liquidate(
address[] memory users,
uint256[] memory maxBorrowParts,
address to,
ISwapperV2 swapper,
bytes memory swapperData
) public virtual {
// Oracle can fail but we still need to allow liquidations
(, uint256 _exchangeRate) = updateExchangeRate();
accrue();
uint256 allCollateralShare;
uint256 allBorrowAmount;
uint256 allBorrowPart;
Rebase memory bentoBoxTotals = bentoBox.totals(collateral);
_beforeUsersLiquidated(users, maxBorrowParts);
for (uint256 i = 0; i < users.length; i++) {
address user = users[i];
if (!_isSolvent(user, _exchangeRate)) {
uint256 borrowPart;
uint256 availableBorrowPart = userBorrowPart[user];
borrowPart = maxBorrowParts[i] > availableBorrowPart ? availableBorrowPart : maxBorrowParts[i];
uint256 borrowAmount = totalBorrow.toElastic(borrowPart, false);
uint256 collateralShare =
bentoBoxTotals.toBase(
borrowAmount.mul(LIQUIDATION_MULTIPLIER).mul(_exchangeRate) /
(LIQUIDATION_MULTIPLIER_PRECISION * EXCHANGE_RATE_PRECISION),
false
);
_beforeUserLiquidated(user, borrowPart, borrowAmount, collateralShare);
userBorrowPart[user] = availableBorrowPart.sub(borrowPart);
userCollateralShare[user] = userCollateralShare[user].sub(collateralShare);
_afterUserLiquidated(user, collateralShare);
emit LogRemoveCollateral(user, to, collateralShare);
emit LogRepay(msg.sender, user, borrowAmount, borrowPart);
emit LogLiquidation(msg.sender, user, to, collateralShare, borrowAmount, borrowPart);
// Keep totals
allCollateralShare = allCollateralShare.add(collateralShare);
allBorrowAmount = allBorrowAmount.add(borrowAmount);
allBorrowPart = allBorrowPart.add(borrowPart);
}
}
require(allBorrowAmount != 0, "Cauldron: all are solvent");
totalBorrow.elastic = totalBorrow.elastic.sub(allBorrowAmount.to128());
totalBorrow.base = totalBorrow.base.sub(allBorrowPart.to128());
totalCollateralShare = totalCollateralShare.sub(allCollateralShare);
// Apply a percentual fee share to sSpell holders
{
uint256 distributionAmount = (allBorrowAmount.mul(LIQUIDATION_MULTIPLIER) / LIQUIDATION_MULTIPLIER_PRECISION).sub(allBorrowAmount).mul(DISTRIBUTION_PART) / DISTRIBUTION_PRECISION; // Distribution Amount
allBorrowAmount = allBorrowAmount.add(distributionAmount);
accrueInfo.feesEarned = accrueInfo.feesEarned.add(distributionAmount.to128());
}
uint256 allBorrowShare = bentoBox.toShare(magicInternetMoney, allBorrowAmount, true);
// Swap using a swapper freely chosen by the caller
// Open (flash) liquidation: get proceeds first and provide the borrow after
bentoBox.transfer(collateral, address(this), to, allCollateralShare);
if (swapper != ISwapperV2(address(0))) {
swapper.swap(address(collateral), address(magicInternetMoney), msg.sender, allBorrowShare, allCollateralShare, swapperData);
}
allBorrowShare = bentoBox.toShare(magicInternetMoney, allBorrowAmount, true);
bentoBox.transfer(magicInternetMoney, msg.sender, address(this), allBorrowShare);
}
/// @notice Withdraws the fees accumulated.
function withdrawFees() public {
accrue();
address _feeTo = masterContract.feeTo();
uint256 _feesEarned = accrueInfo.feesEarned;
uint256 share = bentoBox.toShare(magicInternetMoney, _feesEarned, false);
bentoBox.transfer(magicInternetMoney, address(this), _feeTo, share);
accrueInfo.feesEarned = 0;
emit LogWithdrawFees(_feeTo, _feesEarned);
}
/// @notice Sets the beneficiary of interest accrued.
/// MasterContract Only Admin function.
/// @param newFeeTo The address of the receiver.
function setFeeTo(address newFeeTo) public onlyOwner {
feeTo = newFeeTo;
emit LogFeeTo(newFeeTo);
}
/// @notice reduces the supply of MIM
/// @param amount amount to reduce supply by
function reduceSupply(uint256 amount) public onlyMasterContractOwner {
uint256 maxAmount = bentoBox.toAmount(magicInternetMoney, bentoBox.balanceOf(magicInternetMoney, address(this)), false);
amount = maxAmount > amount ? amount : maxAmount;
bentoBox.withdraw(magicInternetMoney, address(this), msg.sender, amount, 0);
}
/// @notice allows to change the interest rate
/// @param newInterestRate new interest rate
function changeInterestRate(uint64 newInterestRate) public onlyMasterContractOwner {
accrue();
emit LogInterestChange(accrueInfo.INTEREST_PER_SECOND, newInterestRate);
accrueInfo.INTEREST_PER_SECOND = newInterestRate;
}
/// @notice allows to change the borrow limit
/// @param newBorrowLimit new borrow limit
/// @param perAddressPart new borrow limit per address
function changeBorrowLimit(uint128 newBorrowLimit, uint128 perAddressPart) public onlyMasterContractOwner {
borrowLimit = BorrowCap(newBorrowLimit, perAddressPart);
emit LogChangeBorrowLimit(newBorrowLimit, perAddressPart);
}
/// @notice allows to change blacklisted callees
/// @param callee callee to blacklist or not
/// @param blacklisted true when the callee cannot be used in call cook action
function setBlacklistedCallee(address callee, bool blacklisted) public onlyMasterContractOwner {
require(callee != address(bentoBox) && callee != address(this), "invalid callee");
blacklistedCallees[callee] = blacklisted;
emit LogChangeBlacklistedCallee(callee, blacklisted);
}
/// Allows to change the liquidation multiplier
/// @param _liquidationMultiplier new liquidation multiplier.
/// To convert from bips: liquidationFeeBips * 1e1 + 1e5
function setLiquidationMultiplier(uint256 _liquidationMultiplier) public onlyMasterContractOwner {
emit LogLiquidationMultiplierChanged(LIQUIDATION_MULTIPLIER, _liquidationMultiplier);
LIQUIDATION_MULTIPLIER = _liquidationMultiplier;
}
/// Allows to change the borrow opening fee
/// @param _borrowOpeningFee new borrow opening fee.
/// To convert from bips: borrowOpeningFeeBips * 1e1
function setBorrowOpeningFee(uint256 _borrowOpeningFee) public onlyMasterContractOwner {
emit LogBorrowOpeningFeeChanged(BORROW_OPENING_FEE, _borrowOpeningFee);
BORROW_OPENING_FEE = _borrowOpeningFee;
}
/// Allows to change the collateralization rate
/// @param _collateralizationRate new collateralization rate.
/// To convert from bips: collateralizationRateBips * 1e1
function setCollateralizationRate(uint256 _collateralizationRate) public onlyMasterContractOwner {
emit LogCollateralizationRateChanged(COLLATERIZATION_RATE, _collateralizationRate);
COLLATERIZATION_RATE = _collateralizationRate;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import {IERC20} from "BoringSolidity/interfaces/IERC20.sol";
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint256) external;
}
interface IWETHAlike is IWETH {}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import {IERC20} from "BoringSolidity/interfaces/IERC20.sol";
import {Rebase} from "BoringSolidity/libraries/BoringRebase.sol";
import {IStrategy} from "interfaces/IStrategy.sol";
interface IFlashBorrower {
/// @notice The flashloan callback. `amount` + `fee` needs to repayed to msg.sender before this call returns.
/// @param sender The address of the invoker of this flashloan.
/// @param token The address of the token that is loaned.
/// @param amount of the `token` that is loaned.
/// @param fee The fee that needs to be paid on top for this loan. Needs to be the same as `token`.
/// @param data Additional data that was passed to the flashloan function.
function onFlashLoan(
address sender,
IERC20 token,
uint256 amount,
uint256 fee,
bytes calldata data
) external;
}
interface IBatchFlashBorrower {
/// @notice The callback for batched flashloans. Every amount + fee needs to repayed to msg.sender before this call returns.
/// @param sender The address of the invoker of this flashloan.
/// @param tokens Array of addresses for ERC-20 tokens that is loaned.
/// @param amounts A one-to-one map to `tokens` that is loaned.
/// @param fees A one-to-one map to `tokens` that needs to be paid on top for each loan. Needs to be the same token.
/// @param data Additional data that was passed to the flashloan function.
function onBatchFlashLoan(
address sender,
IERC20[] calldata tokens,
uint256[] calldata amounts,
uint256[] calldata fees,
bytes calldata data
) external;
}
interface IBentoBoxV1 {
function balanceOf(IERC20, address) external view returns (uint256);
function batch(bytes[] calldata calls, bool revertOnFail) external payable returns (bool[] memory successes, bytes[] memory results);
function batchFlashLoan(
IBatchFlashBorrower borrower,
address[] calldata receivers,
IERC20[] calldata tokens,
uint256[] calldata amounts,
bytes calldata data
) external;
function claimOwnership() external;
function flashLoan(
IFlashBorrower borrower,
address receiver,
IERC20 token,
uint256 amount,
bytes calldata data
) external;
function deploy(
address masterContract,
bytes calldata data,
bool useCreate2
) external payable returns (address);
function deposit(
IERC20 token_,
address from,
address to,
uint256 amount,
uint256 share
) external payable returns (uint256 amountOut, uint256 shareOut);
function harvest(
IERC20 token,
bool balance,
uint256 maxChangeAmount
) external;
function masterContractApproved(address, address) external view returns (bool);
function masterContractOf(address) external view returns (address);
function nonces(address) external view returns (uint256);
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function pendingStrategy(IERC20) external view returns (IStrategy);
function permitToken(
IERC20 token,
address from,
address to,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function registerProtocol() external;
function setMasterContractApproval(
address user,
address masterContract,
bool approved,
uint8 v,
bytes32 r,
bytes32 s
) external;
function setStrategy(IERC20 token, IStrategy newStrategy) external;
function setStrategyTargetPercentage(IERC20 token, uint64 targetPercentage_) external;
function strategy(IERC20) external view returns (IStrategy);
function strategyData(IERC20)
external
view
returns (
uint64 strategyStartDate,
uint64 targetPercentage,
uint128 balance
);
function toAmount(
IERC20 token,
uint256 share,
bool roundUp
) external view returns (uint256 amount);
function toShare(
IERC20 token,
uint256 amount,
bool roundUp
) external view returns (uint256 share);
function totals(IERC20) external view returns (Rebase memory totals_);
function transfer(
IERC20 token,
address from,
address to,
uint256 share
) external;
function transferMultiple(
IERC20 token,
address from,
address[] calldata tos,
uint256[] calldata shares
) external;
function transferOwnership(
address newOwner,
bool direct,
bool renounce
) external;
function whitelistMasterContract(address masterContract, bool approved) external;
function whitelistedMasterContracts(address) external view returns (bool);
function withdraw(
IERC20 token_,
address from,
address to,
uint256 amount,
uint256 share
) external returns (uint256 amountOut, uint256 shareOut);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IFactory {
function create(address baseToken_, address quoteToken_, uint256 lpFeeRate_, uint256 i_, uint256 k_) external returns (address clone);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
enum YieldMode {
AUTOMATIC,
DISABLED,
CLAIMABLE
}
enum GasMode {
VOID,
CLAIMABLE
}
interface IBlast {
function governorMap(address) external view returns (address);
// configure
function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external;
function configure(YieldMode _yield, GasMode gasMode, address governor) external;
// base configuration options
function configureClaimableYield() external;
function configureClaimableYieldOnBehalf(address contractAddress) external;
function configureAutomaticYield() external;
function configureAutomaticYieldOnBehalf(address contractAddress) external;
function configureVoidYield() external;
function configureVoidYieldOnBehalf(address contractAddress) external;
function configureClaimableGas() external;
function configureClaimableGasOnBehalf(address contractAddress) external;
function configureVoidGas() external;
function configureVoidGasOnBehalf(address contractAddress) external;
function configureGovernor(address _governor) external;
function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external;
// claim yield
function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256);
function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256);
// claim gas
function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256);
function claimGasAtMinClaimRate(address contractAddress, address recipientOfGas, uint256 minClaimRateBips) external returns (uint256);
function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256);
function claimGas(
address contractAddress,
address recipientOfGas,
uint256 gasToClaim,
uint256 gasSecondsToConsume
) external returns (uint256);
// read functions
function readClaimableYield(address contractAddress) external view returns (uint256);
function readYieldConfiguration(address contractAddress) external view returns (uint8);
function readGasParams(
address contractAddress
) external view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode);
}
interface IERC20Rebasing {
function getConfiguration(address account) external view returns (YieldMode);
// changes the yield mode of the caller and update the balance
// to reflect the configuration
function configure(YieldMode) external returns (YieldMode);
// "claimable" yield mode accounts can call this this claim their yield
// to another address
function claim(address recipient, uint256 amount) external returns (uint256);
// read the claimable amount for an account
function getClaimableAmount(address account) external view returns (uint256);
}
interface IBlastPoints {
function configurePointsOperator(address operator) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
/// responsibility is delegated to the caller.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, amount) // Store the `amount` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have their entire balance approved for
/// the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x40, to) // Store the `to` argument.
mstore(0x2c, shl(96, from)) // Store the `from` argument.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
// Read the balance, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, to) // Store the `to` argument.
mstore(0x34, amount) // Store the `amount` argument.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
if iszero(
and(
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x14, account) // Store the `account` argument.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=
mul(
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
}// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol) pragma solidity ^0.8.0; import "../token/ERC20/IERC20.sol";
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity >=0.8.0;
import {Math} from "/mimswap/libraries/Math.sol";
/**
* @title DecimalMath
* @author DODO Breeder
*
* @notice Functions for fixed point number with 18 decimals
*/
library DecimalMath {
using Math for uint256;
uint256 internal constant ONE = 10 ** 18;
uint256 internal constant ONE2 = 10 ** 36;
function mulFloor(uint256 target, uint256 d) internal pure returns (uint256) {
return (target * d) / ONE;
}
function mulCeil(uint256 target, uint256 d) internal pure returns (uint256) {
return (target * d).divCeil(ONE);
}
function divFloor(uint256 target, uint256 d) internal pure returns (uint256) {
return (target * ONE) / d;
}
function divCeil(uint256 target, uint256 d) internal pure returns (uint256) {
return (target * ONE).divCeil(d);
}
function reciprocalFloor(uint256 target) internal pure returns (uint256) {
return ONE2 / target;
}
function reciprocalCeil(uint256 target) internal pure returns (uint256) {
return ONE2.divCeil(target);
}
function powFloor(uint256 target, uint256 e) internal pure returns (uint256) {
if (e == 0) {
return 10 ** 18;
} else if (e == 1) {
return target;
} else {
uint p = powFloor(target, e / 2);
p = (p * p) / ONE;
if (e % 2 == 1) {
p = (p * target) / ONE;
}
return p;
}
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;
interface IMagicLP {
function _BASE_TOKEN_() external view returns (address);
function _QUOTE_TOKEN_() external view returns (address);
function _I_() external view returns (uint256);
function getReserves() external view returns (uint256 baseReserve, uint256 quoteReserve);
function init(
address baseTokenAddress,
address quoteTokenAddress,
uint256 lpFeeRate,
address mtFeeRateModel,
uint256 i,
uint256 k
) external;
function sellBase(address to) external returns (uint256 receiveQuoteAmount);
function sellQuote(address to) external returns (uint256 receiveBaseAmount);
function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external;
function buyShares(address to) external returns (uint256 shares, uint256 baseInput, uint256 quoteInput);
function sellShares(
uint256 shareAmount,
address to,
uint256 baseMinAmount,
uint256 quoteMinAmount,
bytes calldata data,
uint256 deadline
) external returns (uint256 baseAmount, uint256 quoteAmount);
function MIN_LP_FEE_RATE() external view returns (uint256);
function MAX_LP_FEE_RATE() external view returns (uint256);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event OwnershipTransferred(address indexed user, address indexed newOwner);
/*//////////////////////////////////////////////////////////////
OWNERSHIP STORAGE
//////////////////////////////////////////////////////////////*/
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
/*//////////////////////////////////////////////////////////////
OWNERSHIP LOGIC
//////////////////////////////////////////////////////////////*/
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Minimal proxy library.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol)
/// @author Minimal proxy by 0age (https://github.com/0age)
/// @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie
/// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args)
/// @author Minimal ERC1967 proxy by jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy)
///
/// @dev Minimal proxy:
/// Although the sw0nt pattern saves 5 gas over the erc-1167 pattern during runtime,
/// it is not supported out-of-the-box on Etherscan. Hence, we choose to use the 0age pattern,
/// which saves 4 gas over the erc-1167 pattern during runtime, and has the smallest bytecode.
///
/// @dev Minimal proxy (PUSH0 variant):
/// This is a new minimal proxy that uses the PUSH0 opcode introduced during Shanghai.
/// It is optimized first for minimal runtime gas, then for minimal bytecode.
/// The PUSH0 clone functions are intentionally postfixed with a jarring "_PUSH0" as
/// many EVM chains may not support the PUSH0 opcode in the early months after Shanghai.
/// Please use with caution.
///
/// @dev Clones with immutable args (CWIA):
/// The implementation of CWIA here implements a `receive()` method that emits the
/// `ReceiveETH(uint256)` event. This skips the `DELEGATECALL` when there is no calldata,
/// enabling us to accept hard gas-capped `sends` & `transfers` for maximum backwards
/// composability. The minimal proxy implementation does not offer this feature.
///
/// @dev Minimal ERC1967 proxy:
/// An minimal ERC1967 proxy, intended to be upgraded with UUPS.
/// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic.
///
/// @dev ERC1967I proxy:
/// An variant of the minimal ERC1967 proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// `implementation` address. The returned implementation is guaranteed to be valid if the
/// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`.
library LibClone {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The keccak256 of the deployed code for the ERC1967 proxy.
bytes32 internal constant ERC1967_CODE_HASH =
0xaaa52c8cc8a0e3fd27ce756cc6b4e70c51423e9b597b11f32d3e49f8b1fc890d;
/// @dev The keccak256 of the deployed code for the ERC1967I proxy.
bytes32 internal constant ERC1967I_CODE_HASH =
0xce700223c0d4cea4583409accfc45adac4a093b3519998a9cbbe1504dadba6f7;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unable to deploy the clone.
error DeploymentFailed();
/// @dev The salt must start with either the zero address or `by`.
error SaltDoesNotStartWith();
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a clone of `implementation`.
function clone(address implementation) internal returns (address instance) {
instance = clone(0, implementation);
}
/// @dev Deploys a clone of `implementation`.
/// Deposits `value` ETH during deployment.
function clone(uint256 value, address implementation) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
/**
* --------------------------------------------------------------------------+
* CREATION (9 bytes) |
* --------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* --------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* --------------------------------------------------------------------------|
* RUNTIME (44 bytes) |
* --------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* --------------------------------------------------------------------------|
* |
* ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 3d | RETURNDATASIZE | 0 0 0 | |
* 3d | RETURNDATASIZE | 0 0 0 0 | |
* |
* ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds 0 0 0 0 | |
* 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | |
* 3d | RETURNDATASIZE | 0 0 cds 0 0 0 0 | |
* 37 | CALLDATACOPY | 0 0 0 0 | [0..cds): calldata |
* |
* ::: delegate call to the implementation contract :::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds 0 0 0 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | [0..cds): calldata |
* 73 addr | PUSH20 addr | addr 0 cds 0 0 0 0 | [0..cds): calldata |
* 5a | GAS | gas addr 0 cds 0 0 0 0 | [0..cds): calldata |
* f4 | DELEGATECALL | success 0 0 | [0..cds): calldata |
* |
* ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata |
* 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata |
* 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata |
* 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata |
* |
* 60 0x2a | PUSH1 0x2a | 0x2a success 0 rds | [0..rds): returndata |
* 57 | JUMPI | 0 rds | [0..rds): returndata |
* |
* ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* fd | REVERT | | [0..rds): returndata |
* |
* ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | 0 rds | [0..rds): returndata |
* f3 | RETURN | | [0..rds): returndata |
* --------------------------------------------------------------------------+
*/
mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
mstore(0x14, implementation)
mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
instance := create(value, 0x0c, 0x35)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Deploys a deterministic clone of `implementation` with `salt`.
function cloneDeterministic(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = cloneDeterministic(0, implementation, salt);
}
/// @dev Deploys a deterministic clone of `implementation` with `salt`.
/// Deposits `value` ETH during deployment.
function cloneDeterministic(uint256 value, address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
mstore(0x14, implementation)
mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
instance := create2(value, 0x0c, 0x35, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the clone of `implementation`.
function initCode(address implementation) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
mstore(add(result, 0x40), 0x5af43d3d93803e602a57fd5bf30000000000000000000000)
mstore(add(result, 0x28), implementation)
mstore(add(result, 0x14), 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
mstore(result, 0x35) // Store the length.
mstore(0x40, add(result, 0x60)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the clone of `implementation`.
/// Used for mining vanity addresses with create2crunch.
function initCodeHash(address implementation) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
mstore(0x14, implementation)
mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
hash := keccak256(0x0c, 0x35)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the address of the deterministic clone of `implementation`,
/// with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddress(address implementation, bytes32 salt, address deployer)
internal
pure
returns (address predicted)
{
bytes32 hash = initCodeHash(implementation);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL PROXY OPERATIONS (PUSH0 VARIANT) */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a PUSH0 clone of `implementation`.
function clone_PUSH0(address implementation) internal returns (address instance) {
instance = clone_PUSH0(0, implementation);
}
/// @dev Deploys a PUSH0 clone of `implementation`.
/// Deposits `value` ETH during deployment.
function clone_PUSH0(uint256 value, address implementation)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* --------------------------------------------------------------------------+
* CREATION (9 bytes) |
* --------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* --------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 5f | PUSH0 | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 5f | PUSH0 | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* --------------------------------------------------------------------------|
* RUNTIME (45 bytes) |
* --------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* --------------------------------------------------------------------------|
* |
* ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: |
* 5f | PUSH0 | 0 | |
* 5f | PUSH0 | 0 0 | |
* |
* ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds 0 0 | |
* 5f | PUSH0 | 0 cds 0 0 | |
* 5f | PUSH0 | 0 0 cds 0 0 | |
* 37 | CALLDATACOPY | 0 0 | [0..cds): calldata |
* |
* ::: delegate call to the implementation contract :::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds 0 0 | [0..cds): calldata |
* 5f | PUSH0 | 0 cds 0 0 | [0..cds): calldata |
* 73 addr | PUSH20 addr | addr 0 cds 0 0 | [0..cds): calldata |
* 5a | GAS | gas addr 0 cds 0 0 | [0..cds): calldata |
* f4 | DELEGATECALL | success | [0..cds): calldata |
* |
* ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds success | [0..cds): calldata |
* 5f | PUSH0 | 0 rds success | [0..cds): calldata |
* 5f | PUSH0 | 0 0 rds success | [0..cds): calldata |
* 3e | RETURNDATACOPY | success | [0..rds): returndata |
* |
* 60 0x29 | PUSH1 0x29 | 0x29 success | [0..rds): returndata |
* 57 | JUMPI | | [0..rds): returndata |
* |
* ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [0..rds): returndata |
* 5f | PUSH0 | 0 rds | [0..rds): returndata |
* fd | REVERT | | [0..rds): returndata |
* |
* ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [0..rds): returndata |
* 3d | RETURNDATASIZE | rds | [0..rds): returndata |
* 5f | PUSH0 | 0 rds | [0..rds): returndata |
* f3 | RETURN | | [0..rds): returndata |
* --------------------------------------------------------------------------+
*/
mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
mstore(0x14, implementation) // 20
mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
instance := create(value, 0x0e, 0x36)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`.
function cloneDeterministic_PUSH0(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = cloneDeterministic_PUSH0(0, implementation, salt);
}
/// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`.
/// Deposits `value` ETH during deployment.
function cloneDeterministic_PUSH0(uint256 value, address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
mstore(0x14, implementation) // 20
mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
instance := create2(value, 0x0e, 0x36, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the PUSH0 clone of `implementation`.
function initCode_PUSH0(address implementation) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
mstore(add(result, 0x40), 0x5af43d5f5f3e6029573d5ffd5b3d5ff300000000000000000000) // 16
mstore(add(result, 0x26), implementation) // 20
mstore(add(result, 0x12), 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
mstore(result, 0x36) // Store the length.
mstore(0x40, add(result, 0x60)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the PUSH0 clone of `implementation`.
/// Used for mining vanity addresses with create2crunch.
function initCodeHash_PUSH0(address implementation) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
mstore(0x14, implementation) // 20
mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
hash := keccak256(0x0e, 0x36)
mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the address of the deterministic PUSH0 clone of `implementation`,
/// with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddress_PUSH0(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHash_PUSH0(implementation);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CLONES WITH IMMUTABLE ARGS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: This implementation of CWIA differs from the original implementation.
// If the calldata is empty, it will emit a `ReceiveETH(uint256)` event and skip the `DELEGATECALL`.
/// @dev Deploys a clone of `implementation` with immutable arguments encoded in `data`.
function clone(address implementation, bytes memory data) internal returns (address instance) {
instance = clone(0, implementation, data);
}
/// @dev Deploys a clone of `implementation` with immutable arguments encoded in `data`.
/// Deposits `value` ETH during deployment.
function clone(uint256 value, address implementation, bytes memory data)
internal
returns (address instance)
{
assembly {
// Compute the boundaries of the data and cache the memory slots around it.
let mBefore3 := mload(sub(data, 0x60))
let mBefore2 := mload(sub(data, 0x40))
let mBefore1 := mload(sub(data, 0x20))
let dataLength := mload(data)
let dataEnd := add(add(data, 0x20), dataLength)
let mAfter1 := mload(dataEnd)
// +2 bytes for telling how much data there is appended to the call.
let extraLength := add(dataLength, 2)
// The `creationSize` is `extraLength + 108`
// The `runSize` is `creationSize - 10`.
/**
* ---------------------------------------------------------------------------------------------------+
* CREATION (10 bytes) |
* ---------------------------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------------------------|
* 61 runSize | PUSH2 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------------------------|
* RUNTIME (98 bytes + extraLength) |
* ---------------------------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------------------------|
* |
* ::: if no calldata, emit event & return w/o `DELEGATECALL` ::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 60 0x2c | PUSH1 0x2c | 0x2c cds | |
* 57 | JUMPI | | |
* 34 | CALLVALUE | cv | |
* 3d | RETURNDATASIZE | 0 cv | |
* 52 | MSTORE | | [0..0x20): callvalue |
* 7f sig | PUSH32 0x9e.. | sig | [0..0x20): callvalue |
* 59 | MSIZE | 0x20 sig | [0..0x20): callvalue |
* 3d | RETURNDATASIZE | 0 0x20 sig | [0..0x20): callvalue |
* a1 | LOG1 | | [0..0x20): callvalue |
* 00 | STOP | | [0..0x20): callvalue |
* 5b | JUMPDEST | | |
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..cds): calldata |
* |
* ::: keep some values in stack :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | 0 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | 0 0 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | 0 0 0 0 | [0..cds): calldata |
* 61 extra | PUSH2 extra | e 0 0 0 0 | [0..cds): calldata |
* |
* ::: copy extra data to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 80 | DUP1 | e e 0 0 0 0 | [0..cds): calldata |
* 60 0x62 | PUSH1 0x62 | 0x62 e e 0 0 0 0 | [0..cds): calldata |
* 36 | CALLDATASIZE | cds 0x62 e e 0 0 0 0 | [0..cds): calldata |
* 39 | CODECOPY | e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData |
* |
* ::: delegate call to the implementation contract ::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData |
* 01 | ADD | cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData |
* 3d | RETURNDATASIZE | 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData |
* 73 addr | PUSH20 addr | addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData |
* 5a | GAS | gas addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData |
* f4 | DELEGATECALL | success 0 0 | [0..cds): calldata, [cds..cds+e): extraData |
* |
* ::: copy return data to memory ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata, [cds..cds+e): extraData |
* 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata, [cds..cds+e): extraData |
* 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata, [cds..cds+e): extraData |
* 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata, [cds..cds+e): extraData |
* 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata |
* |
* 60 0x60 | PUSH1 0x60 | 0x60 success 0 rds | [0..rds): returndata |
* 57 | JUMPI | 0 rds | [0..rds): returndata |
* |
* ::: revert ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* fd | REVERT | | [0..rds): returndata |
* |
* ::: return ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | 0 rds | [0..rds): returndata |
* f3 | RETURN | | [0..rds): returndata |
* ---------------------------------------------------------------------------------------------------+
*/
mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data.
mstore(sub(data, 0x0d), implementation) // Write the address of the implementation.
// Write the rest of the bytecode.
mstore(
sub(data, 0x21),
or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73)
)
// `keccak256("ReceiveETH(uint256)")`
mstore(
sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff
)
mstore(
// Do a out-of-gas revert if `extraLength` is too big. 0xffff - 0x62 + 0x01 = 0xff9e.
// The actual EVM limit may be smaller and may change over time.
sub(data, add(0x59, lt(extraLength, 0xff9e))),
or(shl(0x78, add(extraLength, 0x62)), 0xfd6100003d81600a3d39f336602c57343d527f)
)
mstore(dataEnd, shl(0xf0, extraLength))
instance := create(value, sub(data, 0x4c), add(extraLength, 0x6c))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
// Restore the overwritten memory surrounding `data`.
mstore(dataEnd, mAfter1)
mstore(data, dataLength)
mstore(sub(data, 0x20), mBefore1)
mstore(sub(data, 0x40), mBefore2)
mstore(sub(data, 0x60), mBefore3)
}
}
/// @dev Deploys a deterministic clone of `implementation`
/// with immutable arguments encoded in `data` and `salt`.
function cloneDeterministic(address implementation, bytes memory data, bytes32 salt)
internal
returns (address instance)
{
instance = cloneDeterministic(0, implementation, data, salt);
}
/// @dev Deploys a deterministic clone of `implementation`
/// with immutable arguments encoded in `data` and `salt`.
function cloneDeterministic(
uint256 value,
address implementation,
bytes memory data,
bytes32 salt
) internal returns (address instance) {
assembly {
// Compute the boundaries of the data and cache the memory slots around it.
let mBefore3 := mload(sub(data, 0x60))
let mBefore2 := mload(sub(data, 0x40))
let mBefore1 := mload(sub(data, 0x20))
let dataLength := mload(data)
let dataEnd := add(add(data, 0x20), dataLength)
let mAfter1 := mload(dataEnd)
// +2 bytes for telling how much data there is appended to the call.
let extraLength := add(dataLength, 2)
mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data.
mstore(sub(data, 0x0d), implementation) // Write the address of the implementation.
// Write the rest of the bytecode.
mstore(
sub(data, 0x21),
or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73)
)
// `keccak256("ReceiveETH(uint256)")`
mstore(
sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff
)
mstore(
// Do a out-of-gas revert if `extraLength` is too big. 0xffff - 0x62 + 0x01 = 0xff9e.
// The actual EVM limit may be smaller and may change over time.
sub(data, add(0x59, lt(extraLength, 0xff9e))),
or(shl(0x78, add(extraLength, 0x62)), 0xfd6100003d81600a3d39f336602c57343d527f)
)
mstore(dataEnd, shl(0xf0, extraLength))
instance := create2(value, sub(data, 0x4c), add(extraLength, 0x6c), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
// Restore the overwritten memory surrounding `data`.
mstore(dataEnd, mAfter1)
mstore(data, dataLength)
mstore(sub(data, 0x20), mBefore1)
mstore(sub(data, 0x40), mBefore2)
mstore(sub(data, 0x60), mBefore3)
}
}
/// @dev Returns the initialization code hash of the clone of `implementation`
/// using immutable arguments encoded in `data`.
function initCode(address implementation, bytes memory data)
internal
pure
returns (bytes memory result)
{
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let dataLength := mload(data)
// Do a out-of-gas revert if `dataLength` is too big. 0xffff - 0x02 - 0x62 = 0xff9b.
// The actual EVM limit may be smaller and may change over time.
returndatacopy(returndatasize(), returndatasize(), gt(dataLength, 0xff9b))
let o := add(result, 0x8c)
let end := add(o, dataLength)
// Copy the `data` into `result`.
for { let d := sub(add(data, 0x20), o) } 1 {} {
mstore(o, mload(add(o, d)))
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
// +2 bytes for telling how much data there is appended to the call.
let extraLength := add(dataLength, 2)
mstore(add(result, 0x6c), 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data.
mstore(add(result, 0x5f), implementation) // Write the address of the implementation.
// Write the rest of the bytecode.
mstore(
add(result, 0x4b),
or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73)
)
// `keccak256("ReceiveETH(uint256)")`
mstore(
add(result, 0x32),
0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff
)
mstore(
add(result, 0x12),
or(shl(0x78, add(extraLength, 0x62)), 0x6100003d81600a3d39f336602c57343d527f)
)
mstore(end, shl(0xf0, extraLength))
mstore(add(end, 0x02), 0) // Zeroize the slot after the result.
mstore(result, add(extraLength, 0x6c)) // Store the length.
mstore(0x40, add(0x22, end)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the clone of `implementation`
/// using immutable arguments encoded in `data`.
/// Used for mining vanity addresses with create2crunch.
function initCodeHash(address implementation, bytes memory data)
internal
pure
returns (bytes32 hash)
{
assembly {
// Compute the boundaries of the data and cache the memory slots around it.
let mBefore3 := mload(sub(data, 0x60))
let mBefore2 := mload(sub(data, 0x40))
let mBefore1 := mload(sub(data, 0x20))
let dataLength := mload(data)
let dataEnd := add(add(data, 0x20), dataLength)
let mAfter1 := mload(dataEnd)
// Do a out-of-gas revert if `dataLength` is too big. 0xffff - 0x02 - 0x62 = 0xff9b.
// The actual EVM limit may be smaller and may change over time.
returndatacopy(returndatasize(), returndatasize(), gt(dataLength, 0xff9b))
// +2 bytes for telling how much data there is appended to the call.
let extraLength := add(dataLength, 2)
mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data.
mstore(sub(data, 0x0d), implementation) // Write the address of the implementation.
// Write the rest of the bytecode.
mstore(
sub(data, 0x21),
or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73)
)
// `keccak256("ReceiveETH(uint256)")`
mstore(
sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff
)
mstore(
sub(data, 0x5a),
or(shl(0x78, add(extraLength, 0x62)), 0x6100003d81600a3d39f336602c57343d527f)
)
mstore(dataEnd, shl(0xf0, extraLength))
hash := keccak256(sub(data, 0x4c), add(extraLength, 0x6c))
// Restore the overwritten memory surrounding `data`.
mstore(dataEnd, mAfter1)
mstore(data, dataLength)
mstore(sub(data, 0x20), mBefore1)
mstore(sub(data, 0x40), mBefore2)
mstore(sub(data, 0x60), mBefore3)
}
}
/// @dev Returns the address of the deterministic clone of
/// `implementation` using immutable arguments encoded in `data`, with `salt`, by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddress(
address implementation,
bytes memory data,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHash(implementation, data);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL ERC1967 PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: The ERC1967 proxy here is intended to be upgraded with UUPS.
// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic.
/// @dev Deploys a minimal ERC1967 proxy with `implementation`.
function deployERC1967(address implementation) internal returns (address instance) {
instance = deployERC1967(0, implementation);
}
/// @dev Deploys a minimal ERC1967 proxy with `implementation`.
/// Deposits `value` ETH during deployment.
function deployERC1967(uint256 value, address implementation)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------------+
* CREATION (34 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code |
* 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code |
* 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code |
* 55 | SSTORE | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------|
* RUNTIME (61 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..calldatasize): calldata |
* |
* ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata |
* 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata |
* 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata |
* 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata |
* f4 | DELEGATECALL | succ | [0..calldatasize): calldata |
* |
* ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata |
* 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata |
* 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata |
* 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata |
* |
* ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
* 60 0x38 | PUSH1 0x38 | dest succ | [0..returndatasize): returndata |
* 57 | JUMPI | | [0..returndatasize): returndata |
* |
* ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* fd | REVERT | | [0..returndatasize): returndata |
* |
* ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [0..returndatasize): returndata |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* f3 | RETURN | | [0..returndatasize): returndata |
* ---------------------------------------------------------------------------------+
*/
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
instance := create(value, 0x21, 0x5f)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
function deployDeterministicERC1967(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967(0, implementation, salt);
}
/// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967(uint256 value, address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
instance := create2(value, 0x21, 0x5f, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967(address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967(0, implementation, salt);
}
/// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967(uint256 value, address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
// Compute and store the bytecode hash.
mstore(add(m, 0x35), keccak256(0x21, 0x5f))
mstore(m, shl(88, address()))
mstore8(m, 0xff) // Write the prefix.
mstore(add(m, 0x15), salt)
instance := keccak256(m, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, 0x21, 0x5f, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation`.
function initCodeERC1967(address implementation) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
mstore(
add(result, 0x60),
0x3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f300
)
mstore(
add(result, 0x40),
0x55f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc
)
mstore(add(result, 0x20), or(shl(24, implementation), 0x600951))
mstore(add(result, 0x09), 0x603d3d8160223d3973)
mstore(result, 0x5f) // Store the length.
mstore(0x40, add(result, 0x80)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation`.
/// Used for mining vanity addresses with create2crunch.
function initCodeHashERC1967(address implementation) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
hash := keccak256(0x21, 0x5f)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the address of the deterministic ERC1967 proxy of `implementation`,
/// with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967(implementation);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967I PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: This proxy has a special code path that activates if `calldatasize() == 1`.
// This code path skips the delegatecall and directly returns the `implementation` address.
// The returned implementation is guaranteed to be valid if the keccak256 of the
// proxy's code is equal to `ERC1967I_CODE_HASH`.
/// @dev Deploys a minimal ERC1967I proxy with `implementation`.
function deployERC1967I(address implementation) internal returns (address instance) {
instance = deployERC1967I(0, implementation);
}
/// @dev Deploys a ERC1967I proxy with `implementation`.
/// Deposits `value` ETH during deployment.
function deployERC1967I(uint256 value, address implementation)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------------+
* CREATION (34 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* 60 runSize | PUSH1 runSize | r | |
* 3d | RETURNDATASIZE | 0 r | |
* 81 | DUP2 | r 0 r | |
* 60 offset | PUSH1 offset | o r 0 r | |
* 3d | RETURNDATASIZE | 0 o r 0 r | |
* 39 | CODECOPY | 0 r | [0..runSize): runtime code |
* 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code |
* 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code |
* 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code |
* 55 | SSTORE | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------|
* RUNTIME (82 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* |
* ::: check calldatasize ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 58 | PC | 1 cds | |
* 14 | EQ | eqs | |
* 60 0x43 | PUSH1 0x43 | dest eqs | |
* 57 | JUMPI | | |
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..calldatasize): calldata |
* |
* ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata |
* 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata |
* 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata |
* 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata |
* f4 | DELEGATECALL | succ | [0..calldatasize): calldata |
* |
* ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata |
* 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata |
* 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata |
* 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata |
* |
* ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
* 60 0x3E | PUSH1 0x3E | dest succ | [0..returndatasize): returndata |
* 57 | JUMPI | | [0..returndatasize): returndata |
* |
* ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* fd | REVERT | | [0..returndatasize): returndata |
* |
* ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [0..returndatasize): returndata |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* f3 | RETURN | | [0..returndatasize): returndata |
* |
* ::: implementation , return :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | |
* 60 0x20 | PUSH1 0x20 | 32 | |
* 60 0x0F | PUSH1 0x0F | o 32 | |
* 3d | RETURNDATASIZE | 0 o 32 | |
* 39 | CODECOPY | | [0..32): implementation slot |
* 3d | RETURNDATASIZE | 0 | [0..32): implementation slot |
* 51 | MLOAD | slot | [0..32): implementation slot |
* 54 | SLOAD | impl | [0..32): implementation slot |
* 3d | RETURNDATASIZE | 0 impl | [0..32): implementation slot |
* 52 | MSTORE | | [0..32): implementation address |
* 59 | MSIZE | 32 | [0..32): implementation address |
* 3d | RETURNDATASIZE | 0 32 | [0..32): implementation address |
* f3 | RETURN | | [0..32): implementation address |
* |
* ---------------------------------------------------------------------------------+
*/
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
instance := create(value, 0x0c, 0x74)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`.
function deployDeterministicERC1967I(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967I(0, implementation, salt);
}
/// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967I(uint256 value, address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
instance := create2(value, 0x0c, 0x74, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967I(address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967I(0, implementation, salt);
}
/// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967I(uint256 value, address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
// Compute and store the bytecode hash.
mstore(add(m, 0x35), keccak256(0x0c, 0x74))
mstore(m, shl(88, address()))
mstore8(m, 0xff) // Write the prefix.
mstore(add(m, 0x15), salt)
instance := keccak256(m, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, 0x0c, 0x74, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation`.
function initCodeERC1967I(address implementation) internal pure returns (bytes memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
mstore(
add(result, 0x74),
0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3
)
mstore(
add(result, 0x54),
0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4
)
mstore(add(result, 0x34), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(add(result, 0x1d), implementation)
mstore(add(result, 0x09), 0x60523d8160223d3973)
mstore(add(result, 0x94), 0)
mstore(result, 0x74) // Store the length.
mstore(0x40, add(result, 0xa0)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation`.
/// Used for mining vanity addresses with create2crunch.
function initCodeHashERC1967I(address implementation) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
hash := keccak256(0x0c, 0x74)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the address of the deterministic ERC1967I proxy of `implementation`,
/// with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967I(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967I(implementation);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OTHER OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the address when a contract with initialization code hash,
/// `hash`, is deployed with `salt`, by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddress(bytes32 hash, bytes32 salt, address deployer)
internal
pure
returns (address predicted)
{
/// @solidity memory-safe-assembly
assembly {
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, hash)
mstore(0x01, shl(96, deployer))
mstore(0x15, salt)
predicted := keccak256(0x00, 0x55)
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Requires that `salt` starts with either the zero address or `by`.
function checkStartsWith(bytes32 salt, address by) internal pure {
/// @solidity memory-safe-assembly
assembly {
// If the salt does not start with the zero address or `by`.
if iszero(or(iszero(shr(96, salt)), eq(shr(96, shl(96, by)), shr(96, salt)))) {
mstore(0x00, 0x0c4549ef) // `SaltDoesNotStartWith()`.
revert(0x1c, 0x04)
}
}
}
}/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity >=0.8.0;
import {Owned} from "solmate/auth/Owned.sol";
import {IERC20Metadata} from "openzeppelin-contracts/interfaces/IERC20Metadata.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {ReentrancyGuard} from "solady/utils/ReentrancyGuard.sol";
import {ERC20} from "solady/tokens/ERC20.sol";
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
import {DecimalMath} from "/mimswap/libraries/DecimalMath.sol";
import {Math} from "/mimswap/libraries/Math.sol";
import {PMMPricing} from "/mimswap/libraries/PMMPricing.sol";
import {ICallee} from "/mimswap/interfaces/ICallee.sol";
import {IFeeRateModel} from "/mimswap/interfaces/IFeeRateModel.sol";
import {IWETH} from "interfaces/IWETH.sol";
/// @title MIMSwap MagicLP
/// @author Adapted from DODOEX DSP https://github.com/DODOEX/contractV2/tree/main/contracts/DODOStablePool
contract MagicLP is ERC20, ReentrancyGuard, Owned {
using Math for uint256;
using SafeCastLib for uint256;
using SafeTransferLib for address;
event BuyShares(address to, uint256 increaseShares, uint256 totalShares);
event SellShares(address payer, address to, uint256 decreaseShares, uint256 totalShares);
event Swap(address fromToken, address toToken, uint256 fromAmount, uint256 toAmount, address trader, address receiver);
event FlashLoan(address borrower, address assetTo, uint256 baseAmount, uint256 quoteAmount);
event RChange(PMMPricing.RState newRState);
event TokenRescue(address indexed token, address to, uint256 amount);
error ErrInitialized();
error ErrBaseQuoteSame();
error ErrInvalidI();
error ErrInvalidK();
error ErrExpired();
error ErrInvalidSignature();
error ErrFlashLoanFailed();
error ErrNoBaseInput();
error ErrZeroAddress();
error ErrZeroQuoteAmount();
error ErrMintAmountNotEnough();
error ErrNotEnough();
error ErrWithdrawNotEnough();
error ErrSellBackNotAllowed();
error ErrInvalidLPFeeRate();
error ErrNotImplementationOwner();
error ErrNotImplementation();
error ErrNotClone();
error ErrNotAllowed();
MagicLP public immutable implementation;
uint256 public constant MAX_I = 10 ** 36;
uint256 public constant MAX_K = 10 ** 18;
uint256 public constant MIN_LP_FEE_RATE = 1e14; // 0.01%
uint256 public constant MAX_LP_FEE_RATE = 1e16; // 1%
bool internal _INITIALIZED_;
address public _BASE_TOKEN_;
address public _QUOTE_TOKEN_;
uint112 public _BASE_RESERVE_;
uint112 public _QUOTE_RESERVE_;
uint32 public _BLOCK_TIMESTAMP_LAST_;
uint256 public _BASE_PRICE_CUMULATIVE_LAST_;
uint112 public _BASE_TARGET_;
uint112 public _QUOTE_TARGET_;
uint32 public _RState_;
IFeeRateModel public _MT_FEE_RATE_MODEL_;
uint256 public _LP_FEE_RATE_;
uint256 public _K_;
uint256 public _I_;
constructor(address owner_) Owned(owner_) {
implementation = this;
// prevents the implementation contract initialization
_INITIALIZED_ = true;
}
function init(
address baseTokenAddress,
address quoteTokenAddress,
uint256 lpFeeRate,
address mtFeeRateModel,
uint256 i,
uint256 k
) external {
if (_INITIALIZED_) {
revert ErrInitialized();
}
if (mtFeeRateModel == address(0) || baseTokenAddress == address(0) || quoteTokenAddress == address(0)) {
revert ErrZeroAddress();
}
if (baseTokenAddress == quoteTokenAddress) {
revert ErrBaseQuoteSame();
}
if (i == 0 || i > MAX_I) {
revert ErrInvalidI();
}
if (k > MAX_K) {
revert ErrInvalidK();
}
if (lpFeeRate < MIN_LP_FEE_RATE || lpFeeRate > MAX_LP_FEE_RATE) {
revert ErrInvalidLPFeeRate();
}
_INITIALIZED_ = true;
_BASE_TOKEN_ = baseTokenAddress;
_QUOTE_TOKEN_ = quoteTokenAddress;
_I_ = i;
_K_ = k;
_LP_FEE_RATE_ = lpFeeRate;
_MT_FEE_RATE_MODEL_ = IFeeRateModel(mtFeeRateModel);
_BLOCK_TIMESTAMP_LAST_ = uint32(block.timestamp % 2 ** 32);
_afterInitialized();
}
//////////////////////////////////////////////////////////////////////////////////////
/// PUBLIC
//////////////////////////////////////////////////////////////////////////////////////
function sync() external nonReentrant {
_sync();
}
function correctRState() external {
if (_RState_ == uint32(PMMPricing.RState.BELOW_ONE) && _BASE_RESERVE_ < _BASE_TARGET_) {
_RState_ = uint32(PMMPricing.RState.ONE);
_BASE_TARGET_ = _BASE_RESERVE_;
_QUOTE_TARGET_ = _QUOTE_RESERVE_;
}
if (_RState_ == uint32(PMMPricing.RState.ABOVE_ONE) && _QUOTE_RESERVE_ < _QUOTE_TARGET_) {
_RState_ = uint32(PMMPricing.RState.ONE);
_BASE_TARGET_ = _BASE_RESERVE_;
_QUOTE_TARGET_ = _QUOTE_RESERVE_;
}
}
//////////////////////////////////////////////////////////////////////////////////////
/// VIEWS
//////////////////////////////////////////////////////////////////////////////////////
function name() public view override returns (string memory) {
return string(abi.encodePacked("MagicLP ", IERC20Metadata(_BASE_TOKEN_).symbol(), "/", IERC20Metadata(_QUOTE_TOKEN_).symbol()));
}
function symbol() public pure override returns (string memory) {
return "MagicLP";
}
function decimals() public view override returns (uint8) {
return IERC20Metadata(_BASE_TOKEN_).decimals();
}
function querySellBase(
address trader,
uint256 payBaseAmount
) public view returns (uint256 receiveQuoteAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newBaseTarget) {
PMMPricing.PMMState memory state = getPMMState();
(receiveQuoteAmount, newRState) = PMMPricing.sellBaseToken(state, payBaseAmount);
(uint256 lpFeeRate, uint256 mtFeeRate) = _MT_FEE_RATE_MODEL_.getFeeRate(trader, _LP_FEE_RATE_);
mtFee = DecimalMath.mulFloor(receiveQuoteAmount, mtFeeRate);
receiveQuoteAmount = receiveQuoteAmount - DecimalMath.mulFloor(receiveQuoteAmount, lpFeeRate) - mtFee;
newBaseTarget = state.B0;
}
function querySellQuote(
address trader,
uint256 payQuoteAmount
) public view returns (uint256 receiveBaseAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newQuoteTarget) {
PMMPricing.PMMState memory state = getPMMState();
(receiveBaseAmount, newRState) = PMMPricing.sellQuoteToken(state, payQuoteAmount);
(uint256 lpFeeRate, uint256 mtFeeRate) = _MT_FEE_RATE_MODEL_.getFeeRate(trader, _LP_FEE_RATE_);
mtFee = DecimalMath.mulFloor(receiveBaseAmount, mtFeeRate);
receiveBaseAmount = receiveBaseAmount - DecimalMath.mulFloor(receiveBaseAmount, lpFeeRate) - mtFee;
newQuoteTarget = state.Q0;
}
function getPMMState() public view returns (PMMPricing.PMMState memory state) {
state.i = _I_;
state.K = _K_;
state.B = _BASE_RESERVE_;
state.Q = _QUOTE_RESERVE_;
state.B0 = _BASE_TARGET_; // will be calculated in adjustedTarget
state.Q0 = _QUOTE_TARGET_;
state.R = PMMPricing.RState(_RState_);
PMMPricing.adjustedTarget(state);
}
function getPMMStateForCall() external view returns (uint256 i, uint256 K, uint256 B, uint256 Q, uint256 B0, uint256 Q0, uint256 R) {
PMMPricing.PMMState memory state = getPMMState();
i = state.i;
K = state.K;
B = state.B;
Q = state.Q;
B0 = state.B0;
Q0 = state.Q0;
R = uint256(state.R);
}
function getMidPrice() public view returns (uint256 midPrice) {
return PMMPricing.getMidPrice(getPMMState());
}
function getReserves() external view returns (uint256 baseReserve, uint256 quoteReserve) {
baseReserve = _BASE_RESERVE_;
quoteReserve = _QUOTE_RESERVE_;
}
function getUserFeeRate(address user) external view returns (uint256 lpFeeRate, uint256 mtFeeRate) {
return _MT_FEE_RATE_MODEL_.getFeeRate(user, _LP_FEE_RATE_);
}
function getBaseInput() public view returns (uint256 input) {
return _BASE_TOKEN_.balanceOf(address(this)) - uint256(_BASE_RESERVE_);
}
function getQuoteInput() public view returns (uint256 input) {
return _QUOTE_TOKEN_.balanceOf(address(this)) - uint256(_QUOTE_RESERVE_);
}
function version() external pure virtual returns (string memory) {
return "MagicLP 1.0.0";
}
//////////////////////////////////////////////////////////////////////////////////////
/// TRADE FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////
function sellBase(address to) external nonReentrant returns (uint256 receiveQuoteAmount) {
uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
uint256 baseInput = baseBalance - uint256(_BASE_RESERVE_);
uint256 mtFee;
uint256 newBaseTarget;
PMMPricing.RState newRState;
(receiveQuoteAmount, mtFee, newRState, newBaseTarget) = querySellBase(tx.origin, baseInput);
_transferQuoteOut(to, receiveQuoteAmount);
_transferQuoteOut(_MT_FEE_RATE_MODEL_.maintainer(), mtFee);
// update TARGET
if (_RState_ != uint32(newRState)) {
_BASE_TARGET_ = newBaseTarget.toUint112();
_RState_ = uint32(newRState);
emit RChange(newRState);
}
_setReserve(baseBalance, _QUOTE_TOKEN_.balanceOf(address(this)));
emit Swap(address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, to);
}
function sellQuote(address to) external nonReentrant returns (uint256 receiveBaseAmount) {
uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
uint256 quoteInput = quoteBalance - uint256(_QUOTE_RESERVE_);
uint256 mtFee;
uint256 newQuoteTarget;
PMMPricing.RState newRState;
(receiveBaseAmount, mtFee, newRState, newQuoteTarget) = querySellQuote(tx.origin, quoteInput);
_transferBaseOut(to, receiveBaseAmount);
_transferBaseOut(_MT_FEE_RATE_MODEL_.maintainer(), mtFee);
// update TARGET
if (_RState_ != uint32(newRState)) {
_QUOTE_TARGET_ = newQuoteTarget.toUint112();
_RState_ = uint32(newRState);
emit RChange(newRState);
}
_setReserve(_BASE_TOKEN_.balanceOf(address(this)), quoteBalance);
emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, to);
}
function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external nonReentrant {
_transferBaseOut(assetTo, baseAmount);
_transferQuoteOut(assetTo, quoteAmount);
if (data.length > 0) {
ICallee(assetTo).FlashLoanCall(msg.sender, baseAmount, quoteAmount, data);
}
uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
// no input -> pure loss
if (baseBalance < _BASE_RESERVE_ && quoteBalance < _QUOTE_RESERVE_) {
revert ErrFlashLoanFailed();
}
// sell quote case
// quote input + base output
if (baseBalance < _BASE_RESERVE_) {
uint256 quoteInput = quoteBalance - uint256(_QUOTE_RESERVE_);
(uint256 receiveBaseAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newQuoteTarget) = querySellQuote(
tx.origin,
quoteInput
);
if (uint256(_BASE_RESERVE_) - baseBalance > receiveBaseAmount) {
revert ErrFlashLoanFailed();
}
_transferBaseOut(_MT_FEE_RATE_MODEL_.maintainer(), mtFee);
if (_RState_ != uint32(newRState)) {
_QUOTE_TARGET_ = newQuoteTarget.toUint112();
_RState_ = uint32(newRState);
emit RChange(newRState);
}
emit Swap(address(_QUOTE_TOKEN_), address(_BASE_TOKEN_), quoteInput, receiveBaseAmount, msg.sender, assetTo);
}
// sell base case
// base input + quote output
if (quoteBalance < _QUOTE_RESERVE_) {
uint256 baseInput = baseBalance - uint256(_BASE_RESERVE_);
(uint256 receiveQuoteAmount, uint256 mtFee, PMMPricing.RState newRState, uint256 newBaseTarget) = querySellBase(
tx.origin,
baseInput
);
if (uint256(_QUOTE_RESERVE_) - quoteBalance > receiveQuoteAmount) {
revert ErrFlashLoanFailed();
}
_transferQuoteOut(_MT_FEE_RATE_MODEL_.maintainer(), mtFee);
if (_RState_ != uint32(newRState)) {
_BASE_TARGET_ = newBaseTarget.toUint112();
_RState_ = uint32(newRState);
emit RChange(newRState);
}
emit Swap(address(_BASE_TOKEN_), address(_QUOTE_TOKEN_), baseInput, receiveQuoteAmount, msg.sender, assetTo);
}
_sync();
emit FlashLoan(msg.sender, assetTo, baseAmount, quoteAmount);
}
//////////////////////////////////////////////////////////////////////////////////////
/// BUY & SELL SHARES
//////////////////////////////////////////////////////////////////////////////////////
// buy shares [round down]
function buyShares(address to) external nonReentrant returns (uint256 shares, uint256 baseInput, uint256 quoteInput) {
uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
uint256 baseReserve = _BASE_RESERVE_;
uint256 quoteReserve = _QUOTE_RESERVE_;
baseInput = baseBalance - baseReserve;
quoteInput = quoteBalance - quoteReserve;
if (baseInput == 0) {
revert ErrNoBaseInput();
}
// Round down when withdrawing. Therefore, never be a situation occuring balance is 0 but totalsupply is not 0
// But May Happen,reserve >0 But totalSupply = 0
if (totalSupply() == 0) {
// case 1. initial supply
if (quoteBalance == 0) {
revert ErrZeroQuoteAmount();
}
shares = quoteBalance < DecimalMath.mulFloor(baseBalance, _I_) ? DecimalMath.divFloor(quoteBalance, _I_) : baseBalance;
_BASE_TARGET_ = uint112(shares);
_QUOTE_TARGET_ = uint112(DecimalMath.mulFloor(shares, _I_));
if (shares <= 2001) {
revert ErrMintAmountNotEnough();
}
_mint(address(0), 1001);
shares -= 1001;
} else if (baseReserve > 0 && quoteReserve > 0) {
// case 2. normal case
uint256 baseInputRatio = DecimalMath.divFloor(baseInput, baseReserve);
uint256 quoteInputRatio = DecimalMath.divFloor(quoteInput, quoteReserve);
uint256 mintRatio = quoteInputRatio < baseInputRatio ? quoteInputRatio : baseInputRatio;
shares = DecimalMath.mulFloor(totalSupply(), mintRatio);
_BASE_TARGET_ = uint112(uint256(_BASE_TARGET_) + DecimalMath.mulFloor(uint256(_BASE_TARGET_), mintRatio));
_QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_) + DecimalMath.mulFloor(uint256(_QUOTE_TARGET_), mintRatio));
}
_mint(to, shares);
_setReserve(baseBalance, quoteBalance);
emit BuyShares(to, shares, balanceOf(to));
}
// sell shares [round down]
function sellShares(
uint256 shareAmount,
address to,
uint256 baseMinAmount,
uint256 quoteMinAmount,
bytes calldata data,
uint256 deadline
) external nonReentrant returns (uint256 baseAmount, uint256 quoteAmount) {
if (deadline < block.timestamp) {
revert ErrExpired();
}
if (shareAmount > balanceOf(msg.sender)) {
revert ErrNotEnough();
}
if (to == address(this)) {
revert ErrSellBackNotAllowed();
}
uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
uint256 totalShares = totalSupply();
baseAmount = (baseBalance * shareAmount) / totalShares;
quoteAmount = (quoteBalance * shareAmount) / totalShares;
_BASE_TARGET_ = uint112(uint256(_BASE_TARGET_) - (uint256(_BASE_TARGET_) * shareAmount).divCeil(totalShares));
_QUOTE_TARGET_ = uint112(uint256(_QUOTE_TARGET_) - (uint256(_QUOTE_TARGET_) * shareAmount).divCeil(totalShares));
if (baseAmount < baseMinAmount || quoteAmount < quoteMinAmount) {
revert ErrWithdrawNotEnough();
}
_burn(msg.sender, shareAmount);
_transferBaseOut(to, baseAmount);
_transferQuoteOut(to, quoteAmount);
_sync();
if (data.length > 0) {
ICallee(to).SellShareCall(msg.sender, shareAmount, baseAmount, quoteAmount, data);
}
emit SellShares(msg.sender, to, shareAmount, balanceOf(msg.sender));
}
//////////////////////////////////////////////////////////////////////////////////////
/// ADMIN
//////////////////////////////////////////////////////////////////////////////////////
function rescue(address token, address to, uint256 amount) external onlyImplementationOwner {
if (token == _BASE_TOKEN_ || token == _QUOTE_TOKEN_) {
revert ErrNotAllowed();
}
token.safeTransfer(to, amount);
emit TokenRescue(token, to, amount);
}
//////////////////////////////////////////////////////////////////////////////////////
/// INTERNALS
//////////////////////////////////////////////////////////////////////////////////////
function _twapUpdate() internal {
uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32);
uint32 timeElapsed = blockTimestamp - _BLOCK_TIMESTAMP_LAST_;
if (timeElapsed > 0 && _BASE_RESERVE_ != 0 && _QUOTE_RESERVE_ != 0) {
_BASE_PRICE_CUMULATIVE_LAST_ += getMidPrice() * timeElapsed;
}
_BLOCK_TIMESTAMP_LAST_ = blockTimestamp;
}
function _setReserve(uint256 baseReserve, uint256 quoteReserve) internal {
_BASE_RESERVE_ = baseReserve.toUint112();
_QUOTE_RESERVE_ = quoteReserve.toUint112();
_twapUpdate();
}
function _sync() internal {
uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this));
uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this));
if (baseBalance != _BASE_RESERVE_) {
_BASE_RESERVE_ = baseBalance.toUint112();
}
if (quoteBalance != _QUOTE_RESERVE_) {
_QUOTE_RESERVE_ = quoteBalance.toUint112();
}
_twapUpdate();
}
function _transferBaseOut(address to, uint256 amount) internal {
if (amount > 0) {
_BASE_TOKEN_.safeTransfer(to, amount);
}
}
function _transferQuoteOut(address to, uint256 amount) internal {
if (amount > 0) {
_QUOTE_TOKEN_.safeTransfer(to, amount);
}
}
function _mint(address to, uint256 amount) internal override {
if (amount <= 1000) {
revert ErrMintAmountNotEnough();
}
super._mint(to, amount);
}
function _afterInitialized() internal virtual {}
//////////////////////////////////////////////////////////////////////////////////////
/// MODIFIERS
//////////////////////////////////////////////////////////////////////////////////////
modifier onlyImplementationOwner() {
if (msg.sender != implementation.owner()) {
revert ErrNotImplementationOwner();
}
_;
}
modifier onlyClones() {
if (address(this) == address(implementation)) {
revert ErrNotClone();
}
_;
}
modifier onlyImplementation() {
if (address(this) != address(implementation)) {
revert ErrNotImplementation();
}
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IOracle {
function decimals() external view returns (uint8);
function get(bytes calldata data) external returns (bool success, uint256 rate);
function peek(bytes calldata data) external view returns (bool success, uint256 rate);
function peekSpot(bytes calldata data) external view returns (uint256 rate);
function symbol(bytes calldata data) external view returns (string memory);
function name(bytes calldata data) external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface ISwapperV2 {
function swap(
address fromToken,
address toToken,
address recipient,
uint256 shareToMin,
uint256 shareFrom,
bytes calldata data
) external returns (uint256 extraShare, uint256 shareReturned);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IMasterContract {
/// @notice Init function that gets called from `BoringFactory.deploy`.
/// Also kown as the constructor for cloned contracts.
/// Any ETH send to `BoringFactory.deploy` ends up here.
/// @param data Can be abi encoded arguments or anything else.
function init(bytes calldata data) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {BoringMath, BoringMath128} from "./BoringMath.sol";
struct Rebase {
uint128 elastic;
uint128 base;
}
/// @notice A rebasing library using overflow-/underflow-safe math.
library RebaseLibrary {
using BoringMath for uint256;
using BoringMath128 for uint128;
/// @notice Calculates the base value in relationship to `elastic` and `total`.
function toBase(
Rebase memory total,
uint256 elastic,
bool roundUp
) internal pure returns (uint256 base) {
if (total.elastic == 0) {
base = elastic;
} else {
base = (elastic * total.base) / total.elastic;
if (roundUp && (base * total.elastic) / total.base < elastic) {
base++;
}
}
}
/// @notice Calculates the elastic value in relationship to `base` and `total`.
function toElastic(
Rebase memory total,
uint256 base,
bool roundUp
) internal pure returns (uint256 elastic) {
if (total.base == 0) {
elastic = base;
} else {
elastic = (base * total.elastic) / total.base;
if (roundUp && (elastic * total.base) / total.elastic < base) {
elastic++;
}
}
}
/// @notice Add `elastic` to `total` and doubles `total.base`.
/// @return (Rebase) The new total.
/// @return base in relationship to `elastic`.
function add(
Rebase memory total,
uint256 elastic,
bool roundUp
) internal pure returns (Rebase memory, uint256 base) {
base = toBase(total, elastic, roundUp);
total.elastic += elastic.to128();
total.base += base.to128();
return (total, base);
}
/// @notice Sub `base` from `total` and update `total.elastic`.
/// @return (Rebase) The new total.
/// @return elastic in relationship to `base`.
function sub(
Rebase memory total,
uint256 base,
bool roundUp
) internal pure returns (Rebase memory, uint256 elastic) {
elastic = toElastic(total, base, roundUp);
total.elastic -= elastic.to128();
total.base -= base.to128();
return (total, elastic);
}
/// @notice Add `elastic` and `base` to `total`.
function add(
Rebase memory total,
uint256 elastic,
uint256 base
) internal pure returns (Rebase memory) {
total.elastic += elastic.to128();
total.base += base.to128();
return total;
}
/// @notice Subtract `elastic` and `base` to `total`.
function sub(
Rebase memory total,
uint256 elastic,
uint256 base
) internal pure returns (Rebase memory) {
total.elastic -= elastic.to128();
total.base -= base.to128();
return total;
}
/// @notice Add `elastic` to `total` and update storage.
/// @return newElastic Returns updated `elastic`.
function addElastic(Rebase storage total, uint256 elastic) internal returns (uint256 newElastic) {
newElastic = total.elastic += elastic.to128();
}
/// @notice Subtract `elastic` from `total` and update storage.
/// @return newElastic Returns updated `elastic`.
function subElastic(Rebase storage total, uint256 elastic) internal returns (uint256 newElastic) {
newElastic = total.elastic -= elastic.to128();
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
library BoringMath {
error ErrOverflow();
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
function to32(uint256 a) internal pure returns (uint32) {
if (a > type(uint32).max) {
revert ErrOverflow();
}
return uint32(a);
}
function to40(uint256 a) internal pure returns (uint40) {
if (a > type(uint40).max) {
revert ErrOverflow();
}
return uint40(a);
}
function to64(uint256 a) internal pure returns (uint64) {
if (a > type(uint64).max) {
revert ErrOverflow();
}
return uint64(a);
}
function to112(uint256 a) internal pure returns (uint112) {
if (a > type(uint112).max) {
revert ErrOverflow();
}
return uint112(a);
}
function to128(uint256 a) internal pure returns (uint128) {
if (a > type(uint128).max) {
revert ErrOverflow();
}
return uint128(a);
}
function to208(uint256 a) internal pure returns (uint208) {
if (a > type(uint208).max) {
revert ErrOverflow();
}
return uint208(a);
}
function to216(uint256 a) internal pure returns (uint216) {
if (a > type(uint216).max) {
revert ErrOverflow();
}
return uint216(a);
}
function to224(uint256 a) internal pure returns (uint224) {
if (a > type(uint224).max) {
revert ErrOverflow();
}
return uint224(a);
}
}
library BoringMath32 {
function add(uint32 a, uint32 b) internal pure returns (uint32) {
return a + b;
}
function sub(uint32 a, uint32 b) internal pure returns (uint32) {
return a - b;
}
function mul(uint32 a, uint32 b) internal pure returns (uint32) {
return a * b;
}
function div(uint32 a, uint32 b) internal pure returns (uint32) {
return a / b;
}
}
library BoringMath64 {
function add(uint64 a, uint64 b) internal pure returns (uint64) {
return a + b;
}
function sub(uint64 a, uint64 b) internal pure returns (uint64) {
return a - b;
}
function mul(uint64 a, uint64 b) internal pure returns (uint64) {
return a * b;
}
function div(uint64 a, uint64 b) internal pure returns (uint64) {
return a / b;
}
}
library BoringMath112 {
function add(uint112 a, uint112 b) internal pure returns (uint112) {
return a + b;
}
function sub(uint112 a, uint112 b) internal pure returns (uint112) {
return a - b;
}
function mul(uint112 a, uint112 b) internal pure returns (uint112) {
return a * b;
}
function div(uint112 a, uint112 b) internal pure returns (uint112) {
return a / b;
}
}
library BoringMath128 {
function add(uint128 a, uint128 b) internal pure returns (uint128) {
return a + b;
}
function sub(uint128 a, uint128 b) internal pure returns (uint128) {
return a - b;
}
function mul(uint128 a, uint128 b) internal pure returns (uint128) {
return a * b;
}
function div(uint128 a, uint128 b) internal pure returns (uint128) {
return a / b;
}
}
library BoringMath224 {
function add(uint224 a, uint224 b) internal pure returns (uint224) {
return a + b;
}
function sub(uint224 a, uint224 b) internal pure returns (uint224) {
return a - b;
}
function mul(uint224 a, uint224 b) internal pure returns (uint224) {
return a * b;
}
function div(uint224 a, uint224 b) internal pure returns (uint224) {
return a / b;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IStrategy {
function skim(uint256 amount) external;
function harvest(uint256 balance, address sender) external returns (int256 amountAdded);
function withdraw(uint256 amount) external returns (uint256 actualAmount);
function exit(uint256 balance) external returns (int256 amountAdded);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity >=0.8.0;
import {DecimalMath} from "/mimswap/libraries/DecimalMath.sol";
/**
* @author Adapted from https://github.com/DODOEX/contractV2/blob/main/contracts/lib/Math.sol
* @notice Functions for complex calculating. Including ONE Integration and TWO Quadratic solutions
*/
library Math {
error ErrIsZero();
function divCeil(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 quotient = a / b;
uint256 remainder = a - quotient * b;
if (remainder > 0) {
return quotient + 1;
} else {
return quotient;
}
}
function sqrt(uint256 x) internal pure returns (uint256 y) {
uint256 z = x / 2 + 1;
y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
}
/*
Integrate dodo curve from V1 to V2
require V0>=V1>=V2>0
res = (1-k)i(V1-V2)+ikV0*V0(1/V2-1/V1)
let V1-V2=delta
res = i*delta*(1-k+k(V0^2/V1/V2))
i is the price of V-res trading pair
support k=1 & k=0 case
[round down]
*/
function _GeneralIntegrate(uint256 V0, uint256 V1, uint256 V2, uint256 i, uint256 k) internal pure returns (uint256) {
if (V0 == 0) {
revert ErrIsZero();
}
uint256 fairAmount = i * (V1 - V2); // i*delta
if (k == 0) {
return fairAmount / DecimalMath.ONE;
}
uint256 V0V0V1V2 = DecimalMath.divFloor((V0 * V0) / V1, V2);
uint256 penalty = DecimalMath.mulFloor(k, V0V0V1V2); // k(V0^2/V1/V2)
return (((DecimalMath.ONE - k) + penalty) * fairAmount) / DecimalMath.ONE2;
}
/*
Follow the integration function above
i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
Assume Q2=Q0, Given Q1 and deltaB, solve Q0
i is the price of delta-V trading pair
give out target of V
support k=1 & k=0 case
[round down]
*/
function _SolveQuadraticFunctionForTarget(uint256 V1, uint256 delta, uint256 i, uint256 k) internal pure returns (uint256) {
if (k == 0) {
return V1 + DecimalMath.mulFloor(i, delta);
}
// V0 = V1*(1+(sqrt-1)/2k)
// sqrt = √(1+4kidelta/V1)
// premium = 1+(sqrt-1)/2k
// uint256 sqrt = (4 * k).mul(i).mul(delta).div(V1).add(DecimalMath.ONE2).sqrt();
if (V1 == 0) {
return 0;
}
uint256 _sqrt;
uint256 ki = (4 * k) * i;
if (ki == 0) {
_sqrt = DecimalMath.ONE;
} else if ((ki * delta) / ki == delta) {
_sqrt = sqrt(((ki * delta) / V1) + DecimalMath.ONE2);
} else {
_sqrt = sqrt(((ki / V1) * delta) + DecimalMath.ONE2);
}
uint256 premium = DecimalMath.divFloor(_sqrt - DecimalMath.ONE, k * 2) + DecimalMath.ONE;
// V0 is greater than or equal to V1 according to the solution
return DecimalMath.mulFloor(V1, premium);
}
/*
Follow the integration expression above, we have:
i*deltaB = (Q2-Q1)*(1-k+kQ0^2/Q1/Q2)
Given Q1 and deltaB, solve Q2
This is a quadratic function and the standard version is
aQ2^2 + bQ2 + c = 0, where
a=1-k
-b=(1-k)Q1-kQ0^2/Q1+i*deltaB
c=-kQ0^2
and Q2=(-b+sqrt(b^2+4(1-k)kQ0^2))/2(1-k)
note: another root is negative, abondan
if deltaBSig=true, then Q2>Q1, user sell Q and receive B
if deltaBSig=false, then Q2<Q1, user sell B and receive Q
return |Q1-Q2|
as we only support sell amount as delta, the deltaB is always negative
the input ideltaB is actually -ideltaB in the equation
i is the price of delta-V trading pair
support k=1 & k=0 case
[round down]
*/
function _SolveQuadraticFunctionForTrade(uint256 V0, uint256 V1, uint256 delta, uint256 i, uint256 k) internal pure returns (uint256) {
if (V0 == 0) {
revert ErrIsZero();
}
if (delta == 0) {
return 0;
}
if (k == 0) {
return DecimalMath.mulFloor(i, delta) > V1 ? V1 : DecimalMath.mulFloor(i, delta);
}
if (k == DecimalMath.ONE) {
// if k==1
// Q2=Q1/(1+ideltaBQ1/Q0/Q0)
// temp = ideltaBQ1/Q0/Q0
// Q2 = Q1/(1+temp)
// Q1-Q2 = Q1*(1-1/(1+temp)) = Q1*(temp/(1+temp))
// uint256 temp = i.mul(delta).mul(V1).div(V0.mul(V0));
uint256 temp;
uint256 idelta = i * delta;
if (idelta == 0) {
temp = 0;
} else if ((idelta * V1) / idelta == V1) {
temp = (idelta * V1) / (V0 * V0);
} else {
temp = (((delta * V1) / V0) * i) / V0;
}
return (V1 * temp) / (temp + DecimalMath.ONE);
}
// calculate -b value and sig
// b = kQ0^2/Q1-i*deltaB-(1-k)Q1
// part1 = (1-k)Q1 >=0
// part2 = kQ0^2/Q1-i*deltaB >=0
// bAbs = abs(part1-part2)
// if part1>part2 => b is negative => bSig is false
// if part2>part1 => b is positive => bSig is true
uint256 part2 = (((k * V0) / V1) * V0) + (i * delta); // kQ0^2/Q1-i*deltaB
uint256 bAbs = (DecimalMath.ONE - k) * V1; // (1-k)Q1
bool bSig;
if (bAbs >= part2) {
bAbs = bAbs - part2;
bSig = false;
} else {
bAbs = part2 - bAbs;
bSig = true;
}
bAbs = bAbs / DecimalMath.ONE;
// calculate sqrt
uint256 squareRoot = DecimalMath.mulFloor((DecimalMath.ONE - k) * 4, DecimalMath.mulFloor(k, V0) * V0); // 4(1-k)kQ0^2
squareRoot = sqrt((bAbs * bAbs) + squareRoot); // sqrt(b*b+4(1-k)kQ0*Q0)
// final res
uint256 denominator = (DecimalMath.ONE - k) * 2; // 2(1-k)
uint256 numerator;
if (bSig) {
numerator = squareRoot - bAbs;
if (numerator == 0) {
revert ErrIsZero();
}
} else {
numerator = bAbs + squareRoot;
}
uint256 V2 = DecimalMath.divCeil(numerator, denominator);
if (V2 > V1) {
return 0;
} else {
return V1 - V2;
}
}
}// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../token/ERC20/extensions/IERC20Metadata.sol";
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Reentrancy guard mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unauthorized reentrant call.
error Reentrancy();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
/// 9 bytes is large enough to avoid collisions with lower slots,
/// but not too large to result in excessive bytecode bloat.
uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* REENTRANCY GUARD */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Guards a function from reentrancy.
modifier nonReentrant() virtual {
/// @solidity memory-safe-assembly
assembly {
if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
mstore(0x00, 0xab143c06) // `Reentrancy()`.
revert(0x1c, 0x04)
}
sstore(_REENTRANCY_GUARD_SLOT, address())
}
_;
/// @solidity memory-safe-assembly
assembly {
sstore(_REENTRANCY_GUARD_SLOT, codesize())
}
}
/// @dev Guards a view function from read-only reentrancy.
modifier nonReadReentrant() virtual {
/// @solidity memory-safe-assembly
assembly {
if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
mstore(0x00, 0xab143c06) // `Reentrancy()`.
revert(0x1c, 0x04)
}
}
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
/// minting and transferring zero tokens, as well as self-approvals.
/// For performance, this implementation WILL NOT revert for such actions.
/// Please add any checks with overrides if desired.
/// - The `permit` function uses the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
/// the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
/// change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The total supply has overflowed.
error TotalSupplyOverflow();
/// @dev The allowance has overflowed.
error AllowanceOverflow();
/// @dev The allowance has underflowed.
error AllowanceUnderflow();
/// @dev Insufficient balance.
error InsufficientBalance();
/// @dev Insufficient allowance.
error InsufficientAllowance();
/// @dev The permit is invalid.
error InvalidPermit();
/// @dev The permit has expired.
error PermitExpired();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
event Transfer(address indexed from, address indexed to, uint256 amount);
/// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
event Approval(address indexed owner, address indexed spender, uint256 amount);
/// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
uint256 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
/// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
uint256 private constant _APPROVAL_EVENT_SIGNATURE =
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The storage slot for the total supply.
uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;
/// @dev The balance slot of `owner` is given by:
/// ```
/// mstore(0x0c, _BALANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let balanceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;
/// @dev The allowance slot of (`owner`, `spender`) is given by:
/// ```
/// mstore(0x20, spender)
/// mstore(0x0c, _ALLOWANCE_SLOT_SEED)
/// mstore(0x00, owner)
/// let allowanceSlot := keccak256(0x0c, 0x34)
/// ```
uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;
/// @dev The nonce slot of `owner` is given by:
/// ```
/// mstore(0x0c, _NONCES_SLOT_SEED)
/// mstore(0x00, owner)
/// let nonceSlot := keccak256(0x0c, 0x20)
/// ```
uint256 private constant _NONCES_SLOT_SEED = 0x38377508;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 private constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev `keccak256("1")`.
bytes32 private constant _VERSION_HASH =
0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
/// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
bytes32 private constant _PERMIT_TYPEHASH =
0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 METADATA */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the name of the token.
function name() public view virtual returns (string memory);
/// @dev Returns the symbol of the token.
function symbol() public view virtual returns (string memory);
/// @dev Returns the decimals places of the token.
function decimals() public view virtual returns (uint8) {
return 18;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the amount of tokens in existence.
function totalSupply() public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(_TOTAL_SUPPLY_SLOT)
}
}
/// @dev Returns the amount of tokens owned by `owner`.
function balanceOf(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
function allowance(address owner, address spender)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x34))
}
}
/// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
///
/// Emits a {Approval} event.
function approve(address spender, uint256 amount) public virtual returns (bool) {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
}
return true;
}
/// @dev Transfer `amount` tokens from the caller to `to`.
///
/// Requirements:
/// - `from` must at least have `amount`.
///
/// Emits a {Transfer} event.
function transfer(address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(msg.sender, to, amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, caller())
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
}
_afterTokenTransfer(msg.sender, to, amount);
return true;
}
/// @dev Transfers `amount` tokens from `from` to `to`.
///
/// Note: Does not update the allowance if it is the maximum uint256 value.
///
/// Requirements:
/// - `from` must at least have `amount`.
/// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
///
/// Emits a {Transfer} event.
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the allowance slot and load its value.
mstore(0x20, caller())
mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if add(allowance_, 1) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
return true;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-2612 */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev For more performance, override to return the constant value
/// of `keccak256(bytes(name()))` if `name()` will never change.
function _constantNameHash() internal view virtual returns (bytes32 result) {}
/// @dev Returns the current nonce for `owner`.
/// This value is used to compute the signature for EIP-2612 permit.
function nonces(address owner) public view virtual returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// Compute the nonce slot and load its value.
mstore(0x0c, _NONCES_SLOT_SEED)
mstore(0x00, owner)
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
/// authorized by a signed approval by `owner`.
///
/// Emits a {Approval} event.
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
bytes32 nameHash = _constantNameHash();
// We simply calculate it on-the-fly to allow for cases where the `name` may change.
if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
/// @solidity memory-safe-assembly
assembly {
// Revert if the block timestamp is greater than `deadline`.
if gt(timestamp(), deadline) {
mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
revert(0x1c, 0x04)
}
let m := mload(0x40) // Grab the free memory pointer.
// Clean the upper 96 bits.
owner := shr(96, shl(96, owner))
spender := shr(96, shl(96, spender))
// Compute the nonce slot and load its value.
mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
mstore(0x00, owner)
let nonceSlot := keccak256(0x0c, 0x20)
let nonceValue := sload(nonceSlot)
// Prepare the domain separator.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), _VERSION_HASH)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
mstore(0x2e, keccak256(m, 0xa0))
// Prepare the struct hash.
mstore(m, _PERMIT_TYPEHASH)
mstore(add(m, 0x20), owner)
mstore(add(m, 0x40), spender)
mstore(add(m, 0x60), value)
mstore(add(m, 0x80), nonceValue)
mstore(add(m, 0xa0), deadline)
mstore(0x4e, keccak256(m, 0xc0))
// Prepare the ecrecover calldata.
mstore(0x00, keccak256(0x2c, 0x42))
mstore(0x20, and(0xff, v))
mstore(0x40, r)
mstore(0x60, s)
let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
// If the ecrecover fails, the returndatasize will be 0x00,
// `owner` will be checked if it equals the hash at 0x00,
// which evaluates to false (i.e. 0), and we will revert.
// If the ecrecover succeeds, the returndatasize will be 0x20,
// `owner` will be compared against the returned address at 0x20.
if iszero(eq(mload(returndatasize()), owner)) {
mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
revert(0x1c, 0x04)
}
// Increment and store the updated nonce.
sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
// Compute the allowance slot and store the value.
// The `owner` is already at slot 0x20.
mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
sstore(keccak256(0x2c, 0x34), value)
// Emit the {Approval} event.
log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
bytes32 nameHash = _constantNameHash();
// We simply calculate it on-the-fly to allow for cases where the `name` may change.
if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Grab the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), _VERSION_HASH)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
result := keccak256(m, 0xa0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL MINT FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Mints `amount` tokens to `to`, increasing the total supply.
///
/// Emits a {Transfer} event.
function _mint(address to, uint256 amount) internal virtual {
_beforeTokenTransfer(address(0), to, amount);
/// @solidity memory-safe-assembly
assembly {
let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
let totalSupplyAfter := add(totalSupplyBefore, amount)
// Revert if the total supply overflows.
if lt(totalSupplyAfter, totalSupplyBefore) {
mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
revert(0x1c, 0x04)
}
// Store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
}
_afterTokenTransfer(address(0), to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL BURN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Burns `amount` tokens from `from`, reducing the total supply.
///
/// Emits a {Transfer} event.
function _burn(address from, uint256 amount) internal virtual {
_beforeTokenTransfer(from, address(0), amount);
/// @solidity memory-safe-assembly
assembly {
// Compute the balance slot and load its value.
mstore(0x0c, _BALANCE_SLOT_SEED)
mstore(0x00, from)
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Subtract and store the updated total supply.
sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
// Emit the {Transfer} event.
mstore(0x00, amount)
log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
}
_afterTokenTransfer(from, address(0), amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL TRANSFER FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Moves `amount` of tokens from `from` to `to`.
function _transfer(address from, address to, uint256 amount) internal virtual {
_beforeTokenTransfer(from, to, amount);
/// @solidity memory-safe-assembly
assembly {
let from_ := shl(96, from)
// Compute the balance slot and load its value.
mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
let fromBalanceSlot := keccak256(0x0c, 0x20)
let fromBalance := sload(fromBalanceSlot)
// Revert if insufficient balance.
if gt(amount, fromBalance) {
mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated balance.
sstore(fromBalanceSlot, sub(fromBalance, amount))
// Compute the balance slot of `to`.
mstore(0x00, to)
let toBalanceSlot := keccak256(0x0c, 0x20)
// Add and store the updated balance of `to`.
// Will not overflow because the sum of all user balances
// cannot exceed the maximum uint256 value.
sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
// Emit the {Transfer} event.
mstore(0x20, amount)
log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
}
_afterTokenTransfer(from, to, amount);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL ALLOWANCE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the allowance slot and load its value.
mstore(0x20, spender)
mstore(0x0c, _ALLOWANCE_SLOT_SEED)
mstore(0x00, owner)
let allowanceSlot := keccak256(0x0c, 0x34)
let allowance_ := sload(allowanceSlot)
// If the allowance is not the maximum uint256 value.
if add(allowance_, 1) {
// Revert if the amount to be transferred exceeds the allowance.
if gt(amount, allowance_) {
mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
revert(0x1c, 0x04)
}
// Subtract and store the updated allowance.
sstore(allowanceSlot, sub(allowance_, amount))
}
}
}
/// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
///
/// Emits a {Approval} event.
function _approve(address owner, address spender, uint256 amount) internal virtual {
/// @solidity memory-safe-assembly
assembly {
let owner_ := shl(96, owner)
// Compute the allowance slot and store the amount.
mstore(0x20, spender)
mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
sstore(keccak256(0x0c, 0x34), amount)
// Emit the {Approval} event.
mstore(0x00, amount)
log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HOOKS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Hook that is called before any transfer of tokens.
/// This includes minting and burning.
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/// @dev Hook that is called after any transfer of tokens.
/// This includes minting and burning.
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe integer casting library that reverts on overflow.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error Overflow();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UNSIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function toUint8(uint256 x) internal pure returns (uint8) {
if (x >= 1 << 8) _revertOverflow();
return uint8(x);
}
function toUint16(uint256 x) internal pure returns (uint16) {
if (x >= 1 << 16) _revertOverflow();
return uint16(x);
}
function toUint24(uint256 x) internal pure returns (uint24) {
if (x >= 1 << 24) _revertOverflow();
return uint24(x);
}
function toUint32(uint256 x) internal pure returns (uint32) {
if (x >= 1 << 32) _revertOverflow();
return uint32(x);
}
function toUint40(uint256 x) internal pure returns (uint40) {
if (x >= 1 << 40) _revertOverflow();
return uint40(x);
}
function toUint48(uint256 x) internal pure returns (uint48) {
if (x >= 1 << 48) _revertOverflow();
return uint48(x);
}
function toUint56(uint256 x) internal pure returns (uint56) {
if (x >= 1 << 56) _revertOverflow();
return uint56(x);
}
function toUint64(uint256 x) internal pure returns (uint64) {
if (x >= 1 << 64) _revertOverflow();
return uint64(x);
}
function toUint72(uint256 x) internal pure returns (uint72) {
if (x >= 1 << 72) _revertOverflow();
return uint72(x);
}
function toUint80(uint256 x) internal pure returns (uint80) {
if (x >= 1 << 80) _revertOverflow();
return uint80(x);
}
function toUint88(uint256 x) internal pure returns (uint88) {
if (x >= 1 << 88) _revertOverflow();
return uint88(x);
}
function toUint96(uint256 x) internal pure returns (uint96) {
if (x >= 1 << 96) _revertOverflow();
return uint96(x);
}
function toUint104(uint256 x) internal pure returns (uint104) {
if (x >= 1 << 104) _revertOverflow();
return uint104(x);
}
function toUint112(uint256 x) internal pure returns (uint112) {
if (x >= 1 << 112) _revertOverflow();
return uint112(x);
}
function toUint120(uint256 x) internal pure returns (uint120) {
if (x >= 1 << 120) _revertOverflow();
return uint120(x);
}
function toUint128(uint256 x) internal pure returns (uint128) {
if (x >= 1 << 128) _revertOverflow();
return uint128(x);
}
function toUint136(uint256 x) internal pure returns (uint136) {
if (x >= 1 << 136) _revertOverflow();
return uint136(x);
}
function toUint144(uint256 x) internal pure returns (uint144) {
if (x >= 1 << 144) _revertOverflow();
return uint144(x);
}
function toUint152(uint256 x) internal pure returns (uint152) {
if (x >= 1 << 152) _revertOverflow();
return uint152(x);
}
function toUint160(uint256 x) internal pure returns (uint160) {
if (x >= 1 << 160) _revertOverflow();
return uint160(x);
}
function toUint168(uint256 x) internal pure returns (uint168) {
if (x >= 1 << 168) _revertOverflow();
return uint168(x);
}
function toUint176(uint256 x) internal pure returns (uint176) {
if (x >= 1 << 176) _revertOverflow();
return uint176(x);
}
function toUint184(uint256 x) internal pure returns (uint184) {
if (x >= 1 << 184) _revertOverflow();
return uint184(x);
}
function toUint192(uint256 x) internal pure returns (uint192) {
if (x >= 1 << 192) _revertOverflow();
return uint192(x);
}
function toUint200(uint256 x) internal pure returns (uint200) {
if (x >= 1 << 200) _revertOverflow();
return uint200(x);
}
function toUint208(uint256 x) internal pure returns (uint208) {
if (x >= 1 << 208) _revertOverflow();
return uint208(x);
}
function toUint216(uint256 x) internal pure returns (uint216) {
if (x >= 1 << 216) _revertOverflow();
return uint216(x);
}
function toUint224(uint256 x) internal pure returns (uint224) {
if (x >= 1 << 224) _revertOverflow();
return uint224(x);
}
function toUint232(uint256 x) internal pure returns (uint232) {
if (x >= 1 << 232) _revertOverflow();
return uint232(x);
}
function toUint240(uint256 x) internal pure returns (uint240) {
if (x >= 1 << 240) _revertOverflow();
return uint240(x);
}
function toUint248(uint256 x) internal pure returns (uint248) {
if (x >= 1 << 248) _revertOverflow();
return uint248(x);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function toInt8(int256 x) internal pure returns (int8) {
int8 y = int8(x);
if (x != y) _revertOverflow();
return y;
}
function toInt16(int256 x) internal pure returns (int16) {
int16 y = int16(x);
if (x != y) _revertOverflow();
return y;
}
function toInt24(int256 x) internal pure returns (int24) {
int24 y = int24(x);
if (x != y) _revertOverflow();
return y;
}
function toInt32(int256 x) internal pure returns (int32) {
int32 y = int32(x);
if (x != y) _revertOverflow();
return y;
}
function toInt40(int256 x) internal pure returns (int40) {
int40 y = int40(x);
if (x != y) _revertOverflow();
return y;
}
function toInt48(int256 x) internal pure returns (int48) {
int48 y = int48(x);
if (x != y) _revertOverflow();
return y;
}
function toInt56(int256 x) internal pure returns (int56) {
int56 y = int56(x);
if (x != y) _revertOverflow();
return y;
}
function toInt64(int256 x) internal pure returns (int64) {
int64 y = int64(x);
if (x != y) _revertOverflow();
return y;
}
function toInt72(int256 x) internal pure returns (int72) {
int72 y = int72(x);
if (x != y) _revertOverflow();
return y;
}
function toInt80(int256 x) internal pure returns (int80) {
int80 y = int80(x);
if (x != y) _revertOverflow();
return y;
}
function toInt88(int256 x) internal pure returns (int88) {
int88 y = int88(x);
if (x != y) _revertOverflow();
return y;
}
function toInt96(int256 x) internal pure returns (int96) {
int96 y = int96(x);
if (x != y) _revertOverflow();
return y;
}
function toInt104(int256 x) internal pure returns (int104) {
int104 y = int104(x);
if (x != y) _revertOverflow();
return y;
}
function toInt112(int256 x) internal pure returns (int112) {
int112 y = int112(x);
if (x != y) _revertOverflow();
return y;
}
function toInt120(int256 x) internal pure returns (int120) {
int120 y = int120(x);
if (x != y) _revertOverflow();
return y;
}
function toInt128(int256 x) internal pure returns (int128) {
int128 y = int128(x);
if (x != y) _revertOverflow();
return y;
}
function toInt136(int256 x) internal pure returns (int136) {
int136 y = int136(x);
if (x != y) _revertOverflow();
return y;
}
function toInt144(int256 x) internal pure returns (int144) {
int144 y = int144(x);
if (x != y) _revertOverflow();
return y;
}
function toInt152(int256 x) internal pure returns (int152) {
int152 y = int152(x);
if (x != y) _revertOverflow();
return y;
}
function toInt160(int256 x) internal pure returns (int160) {
int160 y = int160(x);
if (x != y) _revertOverflow();
return y;
}
function toInt168(int256 x) internal pure returns (int168) {
int168 y = int168(x);
if (x != y) _revertOverflow();
return y;
}
function toInt176(int256 x) internal pure returns (int176) {
int176 y = int176(x);
if (x != y) _revertOverflow();
return y;
}
function toInt184(int256 x) internal pure returns (int184) {
int184 y = int184(x);
if (x != y) _revertOverflow();
return y;
}
function toInt192(int256 x) internal pure returns (int192) {
int192 y = int192(x);
if (x != y) _revertOverflow();
return y;
}
function toInt200(int256 x) internal pure returns (int200) {
int200 y = int200(x);
if (x != y) _revertOverflow();
return y;
}
function toInt208(int256 x) internal pure returns (int208) {
int208 y = int208(x);
if (x != y) _revertOverflow();
return y;
}
function toInt216(int256 x) internal pure returns (int216) {
int216 y = int216(x);
if (x != y) _revertOverflow();
return y;
}
function toInt224(int256 x) internal pure returns (int224) {
int224 y = int224(x);
if (x != y) _revertOverflow();
return y;
}
function toInt232(int256 x) internal pure returns (int232) {
int232 y = int232(x);
if (x != y) _revertOverflow();
return y;
}
function toInt240(int256 x) internal pure returns (int240) {
int240 y = int240(x);
if (x != y) _revertOverflow();
return y;
}
function toInt248(int256 x) internal pure returns (int248) {
int248 y = int248(x);
if (x != y) _revertOverflow();
return y;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OTHER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function toInt256(uint256 x) internal pure returns (int256) {
if (x >= 1 << 255) _revertOverflow();
return int256(x);
}
function toUint256(int256 x) internal pure returns (uint256) {
if (x < 0) _revertOverflow();
return uint256(x);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function _revertOverflow() private pure {
/// @solidity memory-safe-assembly
assembly {
// Store the function selector of `Overflow()`.
mstore(0x00, 0x35278d12)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity >=0.8.0;
import {DecimalMath} from "/mimswap/libraries/DecimalMath.sol";
import {Math} from "/mimswap/libraries/Math.sol";
/**
* @title Pricing
* @author DODO Breeder
*
* @notice DODO Pricing model
*/
library PMMPricing {
enum RState {
ONE,
ABOVE_ONE,
BELOW_ONE
}
struct PMMState {
uint256 i;
uint256 K;
uint256 B;
uint256 Q;
uint256 B0;
uint256 Q0;
RState R;
}
// ============ buy & sell ============
function sellBaseToken(PMMState memory state, uint256 payBaseAmount) internal pure returns (uint256 receiveQuoteAmount, RState newR) {
if (state.R == RState.ONE) {
// case 1: R=1
// R falls below one
receiveQuoteAmount = _ROneSellBaseToken(state, payBaseAmount);
newR = RState.BELOW_ONE;
} else if (state.R == RState.ABOVE_ONE) {
uint256 backToOnePayBase = state.B0 - state.B;
uint256 backToOneReceiveQuote = state.Q - state.Q0;
// case 2: R>1
// complex case, R status depends on trading amount
if (payBaseAmount < backToOnePayBase) {
// case 2.1: R status do not change
receiveQuoteAmount = _RAboveSellBaseToken(state, payBaseAmount);
newR = RState.ABOVE_ONE;
if (receiveQuoteAmount > backToOneReceiveQuote) {
// [Important corner case!] may enter this branch when some precision problem happens. And consequently contribute to negative spare quote amount
// to make sure spare quote>=0, mannually set receiveQuote=backToOneReceiveQuote
receiveQuoteAmount = backToOneReceiveQuote;
}
} else if (payBaseAmount == backToOnePayBase) {
// case 2.2: R status changes to ONE
receiveQuoteAmount = backToOneReceiveQuote;
newR = RState.ONE;
} else {
// case 2.3: R status changes to BELOW_ONE
receiveQuoteAmount = backToOneReceiveQuote + _ROneSellBaseToken(state, payBaseAmount - backToOnePayBase);
newR = RState.BELOW_ONE;
}
} else {
// state.R == RState.BELOW_ONE
// case 3: R<1
receiveQuoteAmount = _RBelowSellBaseToken(state, payBaseAmount);
newR = RState.BELOW_ONE;
}
}
function sellQuoteToken(PMMState memory state, uint256 payQuoteAmount) internal pure returns (uint256 receiveBaseAmount, RState newR) {
if (state.R == RState.ONE) {
receiveBaseAmount = _ROneSellQuoteToken(state, payQuoteAmount);
newR = RState.ABOVE_ONE;
} else if (state.R == RState.ABOVE_ONE) {
receiveBaseAmount = _RAboveSellQuoteToken(state, payQuoteAmount);
newR = RState.ABOVE_ONE;
} else {
uint256 backToOnePayQuote = state.Q0 - state.Q;
uint256 backToOneReceiveBase = state.B - state.B0;
if (payQuoteAmount < backToOnePayQuote) {
receiveBaseAmount = _RBelowSellQuoteToken(state, payQuoteAmount);
newR = RState.BELOW_ONE;
if (receiveBaseAmount > backToOneReceiveBase) {
receiveBaseAmount = backToOneReceiveBase;
}
} else if (payQuoteAmount == backToOnePayQuote) {
receiveBaseAmount = backToOneReceiveBase;
newR = RState.ONE;
} else {
receiveBaseAmount = backToOneReceiveBase + _ROneSellQuoteToken(state, payQuoteAmount - backToOnePayQuote);
newR = RState.ABOVE_ONE;
}
}
}
// ============ R = 1 cases ============
function _ROneSellBaseToken(
PMMState memory state,
uint256 payBaseAmount
)
internal
pure
returns (
uint256 // receiveQuoteToken
)
{
// in theory Q2 <= targetQuoteTokenAmount
// however when amount is close to 0, precision problems may cause Q2 > targetQuoteTokenAmount
return Math._SolveQuadraticFunctionForTrade(state.Q0, state.Q0, payBaseAmount, state.i, state.K);
}
function _ROneSellQuoteToken(
PMMState memory state,
uint256 payQuoteAmount
)
internal
pure
returns (
uint256 // receiveBaseToken
)
{
return Math._SolveQuadraticFunctionForTrade(state.B0, state.B0, payQuoteAmount, DecimalMath.reciprocalFloor(state.i), state.K);
}
// ============ R < 1 cases ============
function _RBelowSellQuoteToken(
PMMState memory state,
uint256 payQuoteAmount
)
internal
pure
returns (
uint256 // receiveBaseToken
)
{
return Math._GeneralIntegrate(state.Q0, state.Q + payQuoteAmount, state.Q, DecimalMath.reciprocalFloor(state.i), state.K);
}
function _RBelowSellBaseToken(
PMMState memory state,
uint256 payBaseAmount
)
internal
pure
returns (
uint256 // receiveQuoteToken
)
{
return Math._SolveQuadraticFunctionForTrade(state.Q0, state.Q, payBaseAmount, state.i, state.K);
}
// ============ R > 1 cases ============
function _RAboveSellBaseToken(
PMMState memory state,
uint256 payBaseAmount
)
internal
pure
returns (
uint256 // receiveQuoteToken
)
{
return Math._GeneralIntegrate(state.B0, state.B + payBaseAmount, state.B, state.i, state.K);
}
function _RAboveSellQuoteToken(
PMMState memory state,
uint256 payQuoteAmount
)
internal
pure
returns (
uint256 // receiveBaseToken
)
{
return Math._SolveQuadraticFunctionForTrade(state.B0, state.B, payQuoteAmount, DecimalMath.reciprocalFloor(state.i), state.K);
}
// ============ Helper functions ============
function adjustedTarget(PMMState memory state) internal pure {
if (state.R == RState.BELOW_ONE) {
state.Q0 = Math._SolveQuadraticFunctionForTarget(state.Q, state.B - state.B0, state.i, state.K);
} else if (state.R == RState.ABOVE_ONE) {
state.B0 = Math._SolveQuadraticFunctionForTarget(
state.B,
state.Q - state.Q0,
DecimalMath.reciprocalFloor(state.i),
state.K
);
}
}
function getMidPrice(PMMState memory state) internal pure returns (uint256) {
if (state.R == RState.BELOW_ONE) {
uint256 R = DecimalMath.divFloor((state.Q0 * state.Q0) / state.Q, state.Q);
R = (DecimalMath.ONE - state.K) + DecimalMath.mulFloor(state.K, R);
return DecimalMath.divFloor(state.i, R);
} else {
uint256 R = DecimalMath.divFloor((state.B0 * state.B0) / state.B, state.B);
R = (DecimalMath.ONE - state.K) + DecimalMath.mulFloor(state.K, R);
return DecimalMath.mulFloor(state.i, R);
}
}
}/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
pragma solidity >=0.8.0;
interface ICallee {
function SellShareCall(
address sender,
uint256 burnShareAmount,
uint256 baseAmount,
uint256 quoteAmount,
bytes calldata data
) external;
function FlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}{
"remappings": [
"/=src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
"BoringSolidity/=lib/BoringSolidity/contracts/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"solmate/=lib/solmate/src/",
"utils/=utils/",
"libraries/=src/libraries/",
"interfaces/=src/interfaces/",
"cauldrons/=src/cauldrons/",
"staking/=src/staking/",
"swappers/=src/swappers/",
"oracles/=src/oracles/",
"strategies/=src/strategies/",
"tokens/=src/tokens/",
"periphery/=src/periphery/",
"mixins/=src/mixins/",
"lenses/=src/lenses/",
"surl/=lib/surl/src/",
"solady/=lib/solady/src/",
"forge-deploy/=lib/forge-deploy/contracts/",
"ExcessivelySafeCall/=lib/ExcessivelySafeCall/src/",
"safe-contracts/=lib/safe-contracts/contracts/"
],
"optimizer": {
"enabled": true,
"runs": 400
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": false,
"libraries": {}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"box_","type":"address"},{"internalType":"address","name":"mim_","type":"address"},{"internalType":"address","name":"governor_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ErrInvalidGovernorAddress","type":"error"},{"inputs":[],"name":"ErrOverflow","type":"error"},{"inputs":[],"name":"ErrZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"accruedAmount","type":"uint128"}],"name":"LogAccrue","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":"share","type":"uint256"}],"name":"LogAddCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"}],"name":"LogBlastNativeClaimableEnabled","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":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"part","type":"uint256"}],"name":"LogBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previous","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"current","type":"uint256"}],"name":"LogBorrowOpeningFeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"blacklisted","type":"bool"}],"name":"LogChangeBlacklistedCallee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"newLimit","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"perAddressPart","type":"uint128"}],"name":"LogChangeBorrowLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previous","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"current","type":"uint256"}],"name":"LogCollateralizationRateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"}],"name":"LogExchangeRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newFeeTo","type":"address"}],"name":"LogFeeTo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"oldInterestRate","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"newInterestRate","type":"uint64"}],"name":"LogInterestChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralShare","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowPart","type":"uint256"}],"name":"LogLiquidation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previous","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"current","type":"uint256"}],"name":"LogLiquidationMultiplierChanged","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":"share","type":"uint256"}],"name":"LogRemoveCollateral","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":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"part","type":"uint256"}],"name":"LogRepay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeTo","type":"address"},{"indexed":false,"internalType":"uint256","name":"feesEarnedFraction","type":"uint256"}],"name":"LogWithdrawFees","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"},{"inputs":[],"name":"BORROW_OPENING_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COLLATERIZATION_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIQUIDATION_MULTIPLIER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accrueInfo","outputs":[{"internalType":"uint64","name":"lastAccrued","type":"uint64"},{"internalType":"uint128","name":"feesEarned","type":"uint128"},{"internalType":"uint64","name":"INTEREST_PER_SECOND","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"skim","type":"bool"},{"internalType":"uint256","name":"share","type":"uint256"}],"name":"addCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bentoBox","outputs":[{"internalType":"contract IBentoBoxV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"blacklistedCallees","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"part","type":"uint256"},{"internalType":"uint256","name":"share","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowLimit","outputs":[{"internalType":"uint128","name":"total","type":"uint128"},{"internalType":"uint128","name":"borrowPartPerAddress","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"newBorrowLimit","type":"uint128"},{"internalType":"uint128","name":"perAddressPart","type":"uint128"}],"name":"changeBorrowLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"newInterestRate","type":"uint64"}],"name":"changeInterestRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"actions","type":"uint8[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bytes[]","name":"datas","type":"bytes[]"}],"name":"cook","outputs":[{"internalType":"uint256","name":"value1","type":"uint256"},{"internalType":"uint256","name":"value2","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"exchangeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"init","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"isSolvent","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"uint256[]","name":"maxBorrowParts","type":"uint256[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"contract ISwapperV2","name":"swapper","type":"address"},{"internalType":"bytes","name":"swapperData","type":"bytes"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"magicInternetMoney","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"masterContract","outputs":[{"internalType":"contract CauldronV4","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleData","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"reduceSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"share","type":"uint256"}],"name":"removeCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"skim","type":"bool"},{"internalType":"uint256","name":"part","type":"uint256"}],"name":"repay","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"callee","type":"address"},{"internalType":"bool","name":"blacklisted","type":"bool"}],"name":"setBlacklistedCallee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_borrowOpeningFee","type":"uint256"}],"name":"setBorrowOpeningFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collateralizationRate","type":"uint256"}],"name":"setCollateralizationRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeTo","type":"address"}],"name":"setFeeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_liquidationMultiplier","type":"uint256"}],"name":"setLiquidationMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalBorrow","outputs":[{"internalType":"uint128","name":"elastic","type":"uint128"},{"internalType":"uint128","name":"base","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateralShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateExchangeRate","outputs":[{"internalType":"bool","name":"updated","type":"bool"},{"internalType":"uint256","name":"rate","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userBorrowPart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userCollateralShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.