More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 2,618 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Approve | 329382709 | 1 hr ago | IN | 0 ETH | 0.00000316 | ||||
Approve | 329373876 | 1 hr ago | IN | 0 ETH | 0.00000304 | ||||
Approve | 329329344 | 4 hrs ago | IN | 0 ETH | 0.00000271 | ||||
Approve | 329307428 | 6 hrs ago | IN | 0 ETH | 0.00000194 | ||||
Approve | 329257868 | 9 hrs ago | IN | 0 ETH | 0.00000154 | ||||
Approve | 329204899 | 13 hrs ago | IN | 0 ETH | 0.00000077 | ||||
Approve | 329181393 | 15 hrs ago | IN | 0 ETH | 0.00000176 | ||||
Approve | 329153716 | 16 hrs ago | IN | 0 ETH | 0.00000099 | ||||
Approve | 329135327 | 18 hrs ago | IN | 0 ETH | 0.00000058 | ||||
Approve | 329135271 | 18 hrs ago | IN | 0 ETH | 0.00000078 | ||||
Approve | 329134640 | 18 hrs ago | IN | 0 ETH | 0.00000069 | ||||
Approve | 329120712 | 19 hrs ago | IN | 0 ETH | 0.00000239 | ||||
Approve | 329049688 | 24 hrs ago | IN | 0 ETH | 0.00003021 | ||||
Approve | 329023625 | 25 hrs ago | IN | 0 ETH | 0.00000055 | ||||
Approve | 328943248 | 31 hrs ago | IN | 0 ETH | 0.00000034 | ||||
Approve | 328887374 | 35 hrs ago | IN | 0 ETH | 0.00000076 | ||||
Approve | 328855581 | 37 hrs ago | IN | 0 ETH | 0.0000005 | ||||
Approve | 328796770 | 41 hrs ago | IN | 0 ETH | 0.00000049 | ||||
Approve | 328792644 | 42 hrs ago | IN | 0 ETH | 0.00000048 | ||||
Approve | 328709729 | 47 hrs ago | IN | 0 ETH | 0.00000062 | ||||
Approve | 328699049 | 2 days ago | IN | 0 ETH | 0.00000054 | ||||
Approve | 328693047 | 2 days ago | IN | 0 ETH | 0.00000055 | ||||
Approve | 328632607 | 2 days ago | IN | 0 ETH | 0.00000035 | ||||
Approve | 328632379 | 2 days ago | IN | 0 ETH | 0.00000032 | ||||
Approve | 328632200 | 2 days ago | IN | 0 ETH | 0.00000052 |
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
230062626 | 289 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Minimal Proxy Contract for 0x2958db359d22411cdf9825f8b27f17350a6fc6bb
Contract Name:
MagicLP
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)
/* 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); event ParametersChanged(uint256 newLpFeeRate, uint256 newI, uint256 newK); event TargetChanged(uint112 newBaseTarget, uint112 newQuoteTarget); event PausedChanged(bool paused); event OperatorChanged(address indexed operator, bool status); error ErrInitialized(); error ErrBaseQuoteSame(); error ErrInvalidI(); error ErrInvalidK(); error ErrExpired(); error ErrInvalidSignature(); error ErrFlashLoanFailed(); error ErrNoBaseInput(); error ErrZeroAddress(); error ErrZeroQuoteAmount(); error ErrZeroQuoteTarget(); error ErrMintAmountNotEnough(); error ErrNotEnough(); error ErrWithdrawNotEnough(); error ErrSellBackNotAllowed(); error ErrInvalidLPFeeRate(); error ErrNotImplementationOwner(); error ErrNotImplementation(); error ErrNotClone(); error ErrNotAllowed(); error ErrReserveAmountNotEnough(); error ErrOverflow(); error ErrNotPaused(); error ErrNotAllowedImplementationOperator(); error ErrInvalidTargets(); 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_; bool public _PAUSED_; bool public _PROTOCOL_OWNED_POOL_; address public _BASE_TOKEN_; address public _QUOTE_TOKEN_; uint112 public _BASE_RESERVE_; uint112 public _QUOTE_RESERVE_; 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_; mapping(address => bool) public operators; 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, bool protocolOwnedPool ) 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); _PROTOCOL_OWNED_POOL_ = protocolOwnedPool; _afterInitialized(); } ////////////////////////////////////////////////////////////////////////////////////// /// PUBLIC - CLONES ONLY ////////////////////////////////////////////////////////////////////////////////////// function sync() external nonReentrant onlyClones { _sync(); } function correctRState() external onlyClones { 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 isImplementationOperator(address _operator) public view returns (bool) { return implementation.operators(_operator) || _operator == implementation.owner(); } 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 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 nonReadReentrant returns (uint256 input) { return _BASE_TOKEN_.balanceOf(address(this)) - uint256(_BASE_RESERVE_); } function getQuoteInput() public view nonReadReentrant 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 onlyClones whenNotPaused 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 onlyClones whenNotPaused 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 onlyClones whenNotPaused { _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 onlyClones whenNotPaused 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 occurring 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_ = shares.toUint112(); _QUOTE_TARGET_ = DecimalMath.mulFloor(shares, _I_).toUint112(); if (_QUOTE_TARGET_ == 0) { revert ErrZeroQuoteTarget(); } 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_ = (uint256(_BASE_TARGET_) + DecimalMath.mulFloor(uint256(_BASE_TARGET_), mintRatio)).toUint112(); _QUOTE_TARGET_ = (uint256(_QUOTE_TARGET_) + DecimalMath.mulFloor(uint256(_QUOTE_TARGET_), mintRatio)).toUint112(); } _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 onlyClones whenNotPaused 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 - IMPLEMENTATION ONLY ////////////////////////////////////////////////////////////////////////////////////// function setOperator(address _operator, bool _status) external onlyImplementation onlyImplementationOwner { operators[_operator] = _status; emit OperatorChanged(_operator, _status); } ////////////////////////////////////////////////////////////////////////////////////// /// OPERATORS / ADMIN - PROTOCOL OWNED POOL AND CLONES ONLY ////////////////////////////////////////////////////////////////////////////////////// function setPaused(bool paused) external onlyClones onlyProtocolOwnedPool onlyImplementationOperators { _PAUSED_ = paused; emit PausedChanged(paused); } function rescue(address token, address to, uint256 amount) external onlyClones onlyProtocolOwnedPool onlyImplementationOperators { if (token == _BASE_TOKEN_ || token == _QUOTE_TOKEN_) { revert ErrNotAllowed(); } token.safeTransfer(to, amount); emit TokenRescue(token, to, amount); } /// @notice Set new parameters for the pool /// The pool must be paused, preferably blocks earlier to avoid sandwiching function setParameters( address assetTo, uint256 newLpFeeRate, uint256 newI, uint256 newK, uint256 baseOutAmount, uint256 quoteOutAmount, uint256 minBaseReserve, uint256 minQuoteReserve ) public nonReentrant onlyClones whenPaused onlyProtocolOwnedPool onlyImplementationOperators { if (_BASE_RESERVE_ < minBaseReserve || _QUOTE_RESERVE_ < minQuoteReserve) { revert ErrReserveAmountNotEnough(); } if (newI == 0 || newI > MAX_I) { revert ErrInvalidI(); } if (newK > MAX_K) { revert ErrInvalidK(); } if (newLpFeeRate < MIN_LP_FEE_RATE || newLpFeeRate > MAX_LP_FEE_RATE) { revert ErrInvalidLPFeeRate(); } _LP_FEE_RATE_ = newLpFeeRate; _K_ = newK; _I_ = newI; _transferBaseOut(assetTo, baseOutAmount); _transferQuoteOut(assetTo, quoteOutAmount); emit ParametersChanged(newLpFeeRate, newI, newK); } function resetTargetAndReserve() public nonReentrant onlyClones whenPaused onlyProtocolOwnedPool onlyImplementationOperators returns (uint256 baseBalance, uint256 quoteBalance) { (baseBalance, quoteBalance) = _resetTargetAndReserve(); emit TargetChanged(uint112(baseBalance), uint112(quoteBalance)); } function setTargets( uint112 baseTarget, uint112 quoteTarget ) public nonReentrant onlyClones whenPaused onlyProtocolOwnedPool onlyImplementationOperators { _BASE_TARGET_ = baseTarget; _QUOTE_TARGET_ = quoteTarget; emit TargetChanged(baseTarget, quoteTarget); } function setRState( PMMPricing.RState newState ) public nonReentrant onlyClones whenPaused onlyProtocolOwnedPool onlyImplementationOperators { _RState_ = uint32(newState); emit RChange(newState); } function ratioSync() external nonReentrant onlyClones onlyProtocolOwnedPool onlyImplementationOperators { uint256 baseBalance = _BASE_TOKEN_.balanceOf(address(this)); uint256 quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); if (baseBalance > type(uint112).max || quoteBalance > type(uint112).max) { revert ErrOverflow(); } if (baseBalance != _BASE_RESERVE_) { _BASE_TARGET_ = uint112((uint256(_BASE_TARGET_) * baseBalance) / uint256(_BASE_RESERVE_)); _BASE_RESERVE_ = uint112(baseBalance); } if (quoteBalance != _QUOTE_RESERVE_) { _QUOTE_TARGET_ = uint112((uint256(_QUOTE_TARGET_) * quoteBalance) / uint256(_QUOTE_RESERVE_)); _QUOTE_RESERVE_ = uint112(quoteBalance); } if (_BASE_TARGET_ == 0 || _QUOTE_TARGET_ == 0) { revert ErrInvalidTargets(); } } ////////////////////////////////////////////////////////////////////////////////////// /// INTERNALS ////////////////////////////////////////////////////////////////////////////////////// function _resetTargetAndReserve() internal returns (uint256 baseBalance, uint256 quoteBalance) { baseBalance = _BASE_TOKEN_.balanceOf(address(this)); quoteBalance = _QUOTE_TOKEN_.balanceOf(address(this)); if (baseBalance > type(uint112).max || quoteBalance > type(uint112).max) { revert ErrOverflow(); } _BASE_RESERVE_ = uint112(baseBalance); _QUOTE_RESERVE_ = uint112(quoteBalance); _BASE_TARGET_ = uint112(baseBalance); _QUOTE_TARGET_ = uint112(quoteBalance); _RState_ = uint32(PMMPricing.RState.ONE); } function _setReserve(uint256 baseReserve, uint256 quoteReserve) internal { _BASE_RESERVE_ = baseReserve.toUint112(); _QUOTE_RESERVE_ = quoteReserve.toUint112(); } 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(); } } 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(); } _; } /// @dev owner is always considered an operator modifier onlyImplementationOperators() { if (!isImplementationOperator(msg.sender)) { revert ErrNotAllowedImplementationOperator(); } _; } /// @dev can only be called on a clone contract modifier onlyClones() { if (address(this) == address(implementation)) { revert ErrNotClone(); } _; } /// @dev can only be called on the implementation contract modifier onlyImplementation() { if (address(this) != address(implementation)) { revert ErrNotImplementation(); } _; } modifier onlyProtocolOwnedPool() { if (!_PROTOCOL_OWNED_POOL_) { revert ErrNotAllowed(); } _; } modifier whenPaused() { if (!_PAUSED_) { revert ErrNotPaused(); } _; } /// @dev When it's a paused protocol owned pool, /// only the implementation operators can call the function /// A normal pool can never be paused. modifier whenNotPaused() { if (_PROTOCOL_OWNED_POOL_ && _PAUSED_ && !isImplementationOperator(msg.sender)) { revert ErrNotAllowedImplementationOperator(); } _; } }
// 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 // 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 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 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 {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; } } }
/* 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; } } // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) // from UniswapV2 https://github.com/Uniswap/v2-core/blob/master/contracts/libraries/Math.sol function sqrt(uint y) internal pure returns (uint z) { if (y > 3) { z = y; uint x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } /* 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; } } }
/* 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 ); } } }
/* 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; }
/* 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: 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 // 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); }
// 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: 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); }
{ "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/", "fuzzlib/=lib/fuzzlib/src/" ], "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":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AllowanceOverflow","type":"error"},{"inputs":[],"name":"AllowanceUnderflow","type":"error"},{"inputs":[],"name":"ErrBaseQuoteSame","type":"error"},{"inputs":[],"name":"ErrExpired","type":"error"},{"inputs":[],"name":"ErrFlashLoanFailed","type":"error"},{"inputs":[],"name":"ErrInitialized","type":"error"},{"inputs":[],"name":"ErrInvalidI","type":"error"},{"inputs":[],"name":"ErrInvalidK","type":"error"},{"inputs":[],"name":"ErrInvalidLPFeeRate","type":"error"},{"inputs":[],"name":"ErrInvalidSignature","type":"error"},{"inputs":[],"name":"ErrInvalidTargets","type":"error"},{"inputs":[],"name":"ErrIsZero","type":"error"},{"inputs":[],"name":"ErrMintAmountNotEnough","type":"error"},{"inputs":[],"name":"ErrNoBaseInput","type":"error"},{"inputs":[],"name":"ErrNotAllowed","type":"error"},{"inputs":[],"name":"ErrNotAllowedImplementationOperator","type":"error"},{"inputs":[],"name":"ErrNotClone","type":"error"},{"inputs":[],"name":"ErrNotEnough","type":"error"},{"inputs":[],"name":"ErrNotImplementation","type":"error"},{"inputs":[],"name":"ErrNotImplementationOwner","type":"error"},{"inputs":[],"name":"ErrNotPaused","type":"error"},{"inputs":[],"name":"ErrOverflow","type":"error"},{"inputs":[],"name":"ErrReserveAmountNotEnough","type":"error"},{"inputs":[],"name":"ErrSellBackNotAllowed","type":"error"},{"inputs":[],"name":"ErrWithdrawNotEnough","type":"error"},{"inputs":[],"name":"ErrZeroAddress","type":"error"},{"inputs":[],"name":"ErrZeroQuoteAmount","type":"error"},{"inputs":[],"name":"ErrZeroQuoteTarget","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidPermit","type":"error"},{"inputs":[],"name":"PermitExpired","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"increaseShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalShares","type":"uint256"}],"name":"BuyShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"address","name":"assetTo","type":"address"},{"indexed":false,"internalType":"uint256","name":"baseAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quoteAmount","type":"uint256"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"OperatorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newLpFeeRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newI","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newK","type":"uint256"}],"name":"ParametersChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"PausedChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum PMMPricing.RState","name":"newRState","type":"uint8"}],"name":"RChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"decreaseShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalShares","type":"uint256"}],"name":"SellShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"fromToken","type":"address"},{"indexed":false,"internalType":"address","name":"toToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint112","name":"newBaseTarget","type":"uint112"},{"indexed":false,"internalType":"uint112","name":"newQuoteTarget","type":"uint112"}],"name":"TargetChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenRescue","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"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"result","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_I","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_K","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_LP_FEE_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_LP_FEE_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BASE_RESERVE_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BASE_TARGET_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_BASE_TOKEN_","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_I_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_K_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_LP_FEE_RATE_","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_MT_FEE_RATE_MODEL_","outputs":[{"internalType":"contract IFeeRateModel","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_PAUSED_","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_PROTOCOL_OWNED_POOL_","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_QUOTE_RESERVE_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_QUOTE_TARGET_","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_QUOTE_TOKEN_","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_RState_","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"buyShares","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"baseInput","type":"uint256"},{"internalType":"uint256","name":"quoteInput","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"correctRState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseAmount","type":"uint256"},{"internalType":"uint256","name":"quoteAmount","type":"uint256"},{"internalType":"address","name":"assetTo","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBaseInput","outputs":[{"internalType":"uint256","name":"input","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPMMState","outputs":[{"components":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"B","type":"uint256"},{"internalType":"uint256","name":"Q","type":"uint256"},{"internalType":"uint256","name":"B0","type":"uint256"},{"internalType":"uint256","name":"Q0","type":"uint256"},{"internalType":"enum PMMPricing.RState","name":"R","type":"uint8"}],"internalType":"struct PMMPricing.PMMState","name":"state","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPMMStateForCall","outputs":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"K","type":"uint256"},{"internalType":"uint256","name":"B","type":"uint256"},{"internalType":"uint256","name":"Q","type":"uint256"},{"internalType":"uint256","name":"B0","type":"uint256"},{"internalType":"uint256","name":"Q0","type":"uint256"},{"internalType":"uint256","name":"R","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getQuoteInput","outputs":[{"internalType":"uint256","name":"input","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint256","name":"baseReserve","type":"uint256"},{"internalType":"uint256","name":"quoteReserve","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserFeeRate","outputs":[{"internalType":"uint256","name":"lpFeeRate","type":"uint256"},{"internalType":"uint256","name":"mtFeeRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"contract MagicLP","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"baseTokenAddress","type":"address"},{"internalType":"address","name":"quoteTokenAddress","type":"address"},{"internalType":"uint256","name":"lpFeeRate","type":"uint256"},{"internalType":"address","name":"mtFeeRateModel","type":"address"},{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"k","type":"uint256"},{"internalType":"bool","name":"protocolOwnedPool","type":"bool"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"isImplementationOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"operators","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"payBaseAmount","type":"uint256"}],"name":"querySellBase","outputs":[{"internalType":"uint256","name":"receiveQuoteAmount","type":"uint256"},{"internalType":"uint256","name":"mtFee","type":"uint256"},{"internalType":"enum PMMPricing.RState","name":"newRState","type":"uint8"},{"internalType":"uint256","name":"newBaseTarget","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"trader","type":"address"},{"internalType":"uint256","name":"payQuoteAmount","type":"uint256"}],"name":"querySellQuote","outputs":[{"internalType":"uint256","name":"receiveBaseAmount","type":"uint256"},{"internalType":"uint256","name":"mtFee","type":"uint256"},{"internalType":"enum PMMPricing.RState","name":"newRState","type":"uint8"},{"internalType":"uint256","name":"newQuoteTarget","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ratioSync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resetTargetAndReserve","outputs":[{"internalType":"uint256","name":"baseBalance","type":"uint256"},{"internalType":"uint256","name":"quoteBalance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"sellBase","outputs":[{"internalType":"uint256","name":"receiveQuoteAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"sellQuote","outputs":[{"internalType":"uint256","name":"receiveBaseAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shareAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"baseMinAmount","type":"uint256"},{"internalType":"uint256","name":"quoteMinAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"sellShares","outputs":[{"internalType":"uint256","name":"baseAmount","type":"uint256"},{"internalType":"uint256","name":"quoteAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"assetTo","type":"address"},{"internalType":"uint256","name":"newLpFeeRate","type":"uint256"},{"internalType":"uint256","name":"newI","type":"uint256"},{"internalType":"uint256","name":"newK","type":"uint256"},{"internalType":"uint256","name":"baseOutAmount","type":"uint256"},{"internalType":"uint256","name":"quoteOutAmount","type":"uint256"},{"internalType":"uint256","name":"minBaseReserve","type":"uint256"},{"internalType":"uint256","name":"minQuoteReserve","type":"uint256"}],"name":"setParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"paused","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum PMMPricing.RState","name":"newState","type":"uint8"}],"name":"setRState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint112","name":"baseTarget","type":"uint112"},{"internalType":"uint112","name":"quoteTarget","type":"uint112"}],"name":"setTargets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]
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.