More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 17,796 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Extend Loan | 4078483 | 23 mins ago | IN | 0.01814952 BERA | 0 | ||||
Approve | 4078380 | 26 mins ago | IN | 0 BERA | 0.00002307 | ||||
Increase Borrow | 4078302 | 29 mins ago | IN | 0 BERA | 0.00003341 | ||||
Approve | 4078282 | 29 mins ago | IN | 0 BERA | 0.00000005 | ||||
Approve | 4077368 | 1 hr ago | IN | 0 BERA | 0.00000005 | ||||
Approve | 4076871 | 1 hr ago | IN | 0 BERA | 0.00000006 | ||||
Approve | 4076821 | 1 hr ago | IN | 0 BERA | 0.00000692 | ||||
Approve | 4076089 | 1 hr ago | IN | 0 BERA | 0.00000095 | ||||
Burn | 4076077 | 1 hr ago | IN | 0 BERA | 0 | ||||
Approve | 4076075 | 1 hr ago | IN | 0 BERA | 0.00000005 | ||||
Increase Borrow | 4076004 | 1 hr ago | IN | 0 BERA | 0.00000015 | ||||
Approve | 4075955 | 1 hr ago | IN | 0 BERA | 0 | ||||
Approve | 4075107 | 2 hrs ago | IN | 0 BERA | 0.00000005 | ||||
Burn | 4073935 | 2 hrs ago | IN | 0 BERA | 0 | ||||
Burn | 4073925 | 2 hrs ago | IN | 0 BERA | 0 | ||||
Approve | 4073722 | 3 hrs ago | IN | 0 BERA | 0 | ||||
Approve | 4073625 | 3 hrs ago | IN | 0 BERA | 0 | ||||
Burn | 4073609 | 3 hrs ago | IN | 0 BERA | 0 | ||||
Approve | 4073423 | 3 hrs ago | IN | 0 BERA | 0.00000005 | ||||
Burn | 4072705 | 3 hrs ago | IN | 0 BERA | 0 | ||||
Burn | 4072543 | 3 hrs ago | IN | 0 BERA | 0 | ||||
Approve | 4072466 | 3 hrs ago | IN | 0 BERA | 0.00000042 | ||||
Burn | 4072410 | 3 hrs ago | IN | 0 BERA | 0 | ||||
Burn | 4072401 | 3 hrs ago | IN | 0 BERA | 0 | ||||
Burn | 4072391 | 3 hrs ago | IN | 0 BERA | 0 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
4079049 | 4 mins ago | 0.00005291 BERA | ||||
4079049 | 4 mins ago | 0.0151176 BERA | ||||
4078989 | 6 mins ago | 0.23463643 BERA | ||||
4078989 | 6 mins ago | 24.25116493 BERA | ||||
4078989 | 6 mins ago | 0.23516653 BERA | ||||
4078989 | 6 mins ago | 24.30595406 BERA | ||||
4078483 | 23 mins ago | 0.00635233 BERA | ||||
4078183 | 33 mins ago | 0 BERA | ||||
4078183 | 33 mins ago | 0.00000007 BERA | ||||
4078157 | 34 mins ago | 0 BERA | ||||
4078157 | 34 mins ago | 0.00000162 BERA | ||||
4076607 | 1 hr ago | 3.54719319 BERA | ||||
4076607 | 1 hr ago | 366.62492769 BERA | ||||
4076607 | 1 hr ago | 0.14505179 BERA | ||||
4076607 | 1 hr ago | 14.99202344 BERA | ||||
4076607 | 1 hr ago | 0.14506179 BERA | ||||
4076607 | 1 hr ago | 14.9930567 BERA | ||||
4076607 | 1 hr ago | 0.14507178 BERA | ||||
4076607 | 1 hr ago | 14.99409007 BERA | ||||
4076607 | 1 hr ago | 0.14508178 BERA | ||||
4076607 | 1 hr ago | 14.99512355 BERA | ||||
4076607 | 1 hr ago | 0.14509178 BERA | ||||
4076607 | 1 hr ago | 14.99615713 BERA | ||||
4076607 | 1 hr ago | 0.14510178 BERA | ||||
4076607 | 1 hr ago | 14.99719082 BERA |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Source Code Verified (Exact Match)
Contract Name:
Bread
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 100 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
//SPDX-License-Identifier: MIT //Baking Bread with the BakerDao pragma solidity ^0.8.26; import {ERC20} from "lib/solady/src/tokens/ERC20.sol"; import {Ownable} from "lib/solady/src/auth/Ownable.sol"; import {ReentrancyGuard} from "lib/solady/src/utils/ReentrancyGuard.sol"; import {SafeTransferLib} from "lib/solady/src/utils/SafeTransferLib.sol"; import {Multicallable} from "lib/solady/src/utils/Multicallable.sol"; import "lib/openzeppelin-contracts/contracts/utils/math/Math.sol"; import {Loan} from "src/interfaces/IBread.sol"; /// @title Bread /// @author BakerDao /// @notice Bera-native DeFi protocol with a bonding curve mechanism, built-in lending, staking, and gameFi elements /// @dev BREAD can be baked with BERA /// @dev Launchpad: Authorized baker contracts can pool deposits to bake BREAD /// @dev Auto TWAP: Add any ERC20 token, will twap into BERA and add to BREAD backing /// @dev Looping: 100x loop BREAD/BERA in single-click /// @dev Token Locker: lock any ERC20 token with BREAD and contribute to growing BREAD /// @dev Multicallable: all actions within BREAD can be batched in single click /// @dev Liquid BGT: Mint BreadGT by claiming BGT through Bread, redeem BreadGT for BREAD /// @dev Proof-of-Liquidity: Earn BGT by staking iBREAD, or BREAD-OHM LP /// @dev Staking: Stake BREAD into iBREAD. iBREAD has elastic supply, controlled by authorized contracts /// @dev GameFi: Participate in games of chance with iBREAD liquidity //RewardVault interface IRewardVault { function getReward(address account, address recipient) external returns (uint256); } //BreadGT contract BreadGT is ERC20 { Bread public immutable bread; constructor() { bread = Bread(payable(msg.sender)); } modifier onlyBread() { require(msg.sender == address(bread), "Only Bread"); _; } /// @notice Only BREAD contract is authorized minter function mint(address user, uint256 amount) external onlyBread { _mint(user, amount); } /// @notice No authorized burners, withdrawal contract will handle burn function burn(uint256 amount) external { _burn(msg.sender, amount); } /// @notice Returns the name of the token function name() public pure override returns (string memory) { return "Bread BGT"; } /// @notice Returns the symbol of the token function symbol() public pure override returns (string memory) { return "BreadGT"; } } // iBread contract iBread is ERC20 { Bread public immutable bread; constructor() { bread = Bread(payable(msg.sender)); } mapping(address => bool) public minters; event MinterSet(address _contract, bool _allowed); modifier onlyMinters() { require(msg.sender == address(bread) || minters[msg.sender], "Only Bread or Minters"); _; } function setMinter(address _contract, bool _allowed) external { require(msg.sender == address(bread), "Only bread"); minters[_contract] = _allowed; emit MinterSet(_contract, _allowed); } /// @notice Mints iBREAD to a user (only callable by Bread contract) function mint(address user, uint256 amount) external onlyMinters { _mint(user, amount); } /// @notice Burns iBREAD from a user (only callable by Bread contract) function burn(address user, uint256 amount) external onlyMinters { _burn(user, amount); } /// @notice Burn your own tokens :) function burn(uint256 amount) external { _burn(msg.sender, amount); } /// @notice Returns the name of the token function name() public pure override returns (string memory) { return "Inflation BREAD"; } /// @notice Returns the symbol of the token function symbol() public pure override returns (string memory) { return "iBREAD"; } } // Bread contract Bread is ERC20, Ownable, ReentrancyGuard, Multicallable { using SafeTransferLib for address; //Token locker and POL integration uint256 public polFeeBps = 1000; uint256 public bribeBounty = 10_000 ether; uint256 public tokenLockerFeeBps = 100; struct LockedToken { uint256 amount; uint256 unlockTime; } mapping(address => mapping(address => LockedToken)) public lockedTokens; //user -> token -> LockTokens mapping(address => uint256) public totalLockedTokens; //Total locked balance per token // Bread staking / iBREAD iBread public stakedBread; uint256 public totalStakedBread; mapping(address => uint256) public unstakeRequestTime; mapping(address => uint256) public unstakeRequestAmount; bool public stakingEnabled; // BreadGT address public constant BGT = 0x656b95E550C07a9ffe548bd4085c72418Ceb1dba; // mapping(address => uint256) public breadGTRequested; BreadGT public breadGT; bool public breadGTEnabled; // Borrowing uint256 public constant COLLATERAL_RATIO = 9900; uint256 public constant INTEREST_APR_BPS = 690; //6.9% bool public borrowingEnabled; // Constants uint256 public constant DUST = 1000; uint256 public constant BPS_DENOMINATOR = 10_000; uint256 public constant PROTOCOL_FEE_SHARE_BPS = 3500; address public constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD; //Mutable, owner-only setting variables address payable public breadTreasury; uint256 public bakeFeeBps = 269; uint256 public burnFeeBps = 269; uint256 public leverageFeeBps = 142; address public baker; //Global state variables bool public started; uint256 public totalLoans; uint256 public totalCollateral; uint256 public maxBake; uint256 public totalBaked; uint256 public prevPrice; //User state variables mapping(address => Loan) public activeLoans; //Global by date state variables mapping(uint256 => uint256) public loansByDate; mapping(uint256 => uint256) public collateralByDate; uint256 public lastToastDate; //*************************************************** // Constructor /// @notice Initializes the Bread contract /// @dev Sets the initial lastToastDate and breadTreasury, deploy iBread and breadGT constructor() { _initializeOwner(msg.sender); lastToastDate = getDayStart(block.timestamp); breadTreasury = payable(msg.sender); stakedBread = new iBread(); breadGT = new BreadGT(); } //*************************************************** // ERC20 settings function name() public view override returns (string memory) { return "Bread"; } function symbol() public view override returns (string memory) { return "BREAD"; } //*************************************************** // Owner settings /// @notice Starts baking and burning for the BREAD token /// @dev Requires that fee address is set and must send 6900 BERA /// @dev Requires that the maxSupply is 0 and trading hasn't already started /// Mints initial BREAD to the owner and burns 0.1 BREAD function setStart() public payable onlyOwner { require(!started && maxBake == 0, "Trading already initialized"); require(msg.value == 6900 ether, "Must send 6900 BERA to start trading"); uint256 masterBakerBake = msg.value; // sets initial price to 1 BERA maxBake = masterBakerBake; emit MaxBakeUpdated(masterBakerBake); _bake(msg.sender, masterBakerBake); _transfer(msg.sender, DEAD_ADDRESS, 0.1 ether); started = true; borrowingEnabled = true; emit Started(true); } /// @notice Sets the baker address /// @param _baker The address of the baker contract /// @dev The baker contract can mint and increase total supply atomically function setBaker(address _baker) external onlyOwner { baker = _baker; emit BakerSet(_baker); } /// @notice Increase the maxBake /// @param _maxBake The new maximum supply of BREAD /// @dev Requires its increasing and higher than total baked function increaseMaxSupply(uint256 _maxBake) external onlyOwner { require(_maxBake > totalBaked, "Max supply must be greater than total baked"); require(_maxBake > maxBake, "Increase only"); maxBake = _maxBake; emit MaxBakeUpdated(_maxBake); } /// @notice Sets the fee recipient address /// @param _address The new fee address /// @dev Cannot be the zero address function setBreadTreasury(address _address) external onlyOwner { require(_address != address(0), "Can't set fee address to 0 address"); breadTreasury = payable(_address); emit BreadTreasuryUpdated(_address); } /// @notice Sets the bake fee /// @param amount The new bake fee /// @dev Fee is in basis points, must be between 1-5% function setBakeFee(uint256 amount) external onlyOwner { require(amount >= 100, "bake fee must be greater than 1%"); require(amount <= 500, "bake fee must be less than 5%"); require(amount >= leverageFeeBps, "bake fee must be greater than leverage fee"); bakeFeeBps = amount; emit BakeFeeUpdated(amount); } /// @notice Sets the leverage fee /// @param amount The new leverage fee /// @dev Fee is in basis points, must be between 0.5% and 2.5% function setLeverageFee(uint256 amount) external onlyOwner { require(amount >= 50, "leverage fee must be greater than 0.5%"); require(amount <= 250, "leverage fee must be less than 2.5%"); require(amount <= bakeFeeBps, "leverage fee must be less than bake fee"); leverageFeeBps = amount; emit LeverageFeeUpdated(amount); } /// @notice Sets the burn fee /// @param amount The new burn fee /// @dev Fee is in basis points, must be between 1-5% function setBurnFee(uint256 amount) external onlyOwner { require(amount >= 100, "burn fee must be greater than 1%"); require(amount <= 500, "burn fee must be less than 5%"); burnFeeBps = amount; emit BurnFeeUpdated(amount); } /// @notice Enable borrowing /// @param _enabled Whether borrowing is enabled function enableBorrowing(bool _enabled) external onlyOwner { borrowingEnabled = _enabled; emit BorrowingEnabled(_enabled); } /// @notice Set fee for POL rewards. /// @dev Applies to bribe bounty and BreadGT /// @param _polFeeBps Fee in basis points function setPolFee(uint256 _polFeeBps) external onlyOwner { require(_polFeeBps <= PROTOCOL_FEE_SHARE_BPS, "POL fee must be less than overall fee"); polFeeBps = _polFeeBps; emit PolFeeUpdated(_polFeeBps); } /// @notice Set fee for token locker /// @dev Note 100% of the fee is added to BREAD contract and adds to backing via bribe bounty /// @param _tokenLockerFeeBps Fee in basis points function setTokenLockerFee(uint256 _tokenLockerFeeBps) external onlyOwner { require(_tokenLockerFeeBps <= 1000, "Token locker fee must be less than or equal to 10%"); tokenLockerFeeBps = _tokenLockerFeeBps; emit TokenLockerFeeUpdated(_tokenLockerFeeBps); } /// @notice Sets contracts allowed to mint/redeem iBread /// @dev This affects all BREAD stakers, so must be used cautiously /// @dev No event here as this is just a wrapper for the method on iBread contract /// @param _contract Contract to whitelist / blacklist /// @param _allowed Whether to enable or disable function setIBreadMinter(address _contract, bool _allowed) external onlyOwner { stakedBread.setMinter(_contract, _allowed); } /// @notice Enable staking - i.e. deploy iBread and enable its functionality function enableStaking(bool _enabled) external onlyOwner { stakingEnabled = _enabled; emit StakingEnabled(_enabled); } /// @notice Enable BreadGT - i.e. deploy BreadGT and enable its functionality function enableBreadGT(bool _enabled) external onlyOwner { breadGTEnabled = _enabled; emit BreadGTEnabled(_enabled); } //*************************************************** // ERC20 token compatibility (for Berachain POL bribe collection) /// @notice Recover ERC20 tokens from the contract after bread is baked /// @param _token Address of the token to recover /// @dev Unruggable because it can only recover erc20s that are not bread (mistakenly donated or farmed tokens) function recoverERC20(address _token) external onlyOwner { require(_token != address(this), "Bread: can only recover erc20s that are not bread"); _token.safeTransfer(msg.sender, _token.balanceOf(address(this))); } /// @notice Set the bribe bounty amount /// @param _amount Amount of bribe bounty to set function setBribeBounty(uint256 _amount) external onlyOwner { require(_amount >= 0, "Bounty must be greater than 0"); bribeBounty = _amount; emit BribeBountyUpdated(_amount); } /// @notice Allows anyone to claim all ERC-20 fees in the contract by paying a fixed bounty. /// @dev Cannot claim BREAD tokens. /// @dev Built-in fee collection allows BREAD to add *any* ERC20 token to its backing /// @param tokens The list of ERC-20 token addresses to collect. function claimBribeBounty(address[] calldata tokens) external payable nonReentrant { require(msg.value == bribeBounty, "Incorrect bounty amount"); uint256 length = tokens.length; require(length > 0, "Must claim at least one token"); uint256[] memory amounts = new uint256[](length); for (uint256 i = 0; i < length; i++) { address token = tokens[i]; require(token != address(this), "Cannot claim BREAD tokens"); // Prevent collecting BREAD uint256 balance = token.balanceOf(address(this)); uint256 locked = totalLockedTokens[token]; balance -= locked; if (balance > 0) { amounts[i] = balance; token.safeTransfer(msg.sender, balance); } } uint256 treasuryAmount = msg.value * polFeeBps / BPS_DENOMINATOR; require(treasuryAmount >= DUST, "Fee must be greater than dust"); _sendBera(breadTreasury, treasuryAmount); emit BountyCollected(msg.sender, bribeBounty, tokens, amounts); } /// @notice Lock tokens to keep them unruggable while improving BREAD backing /// @dev We only support one lock per user / token, and it must be extended or added to /// @param token ERC20 token to lock /// @param amount Amount of token to lock /// @param unlockTime Time to unlock token function lockTokens(address token, uint256 amount, uint256 unlockTime) external nonReentrant { require(token != address(this), "Cannot lock BREAD tokens"); require(unlockTime > block.timestamp, "Unlock time must be in the future"); require(amount > 0, "Amount must be greater than 0"); LockedToken storage userLock = lockedTokens[msg.sender][token]; if(userLock.amount > 0) { require(unlockTime >= userLock.unlockTime, "Cannot shorten lock time"); } uint256 breadFee = amount * tokenLockerFeeBps / BPS_DENOMINATOR; //100% to backing uint256 lockAmount = amount - breadFee; token.safeTransferFrom(msg.sender, address(this), amount); userLock.amount += lockAmount; userLock.unlockTime = unlockTime; totalLockedTokens[token] += lockAmount; emit TokenLocked(msg.sender, token, lockAmount, unlockTime); } /// @notice Unlock tokens previously locked with token locker /// @param token ERC20 token to unlock function unlockTokens(address token) external nonReentrant { LockedToken storage userLock = lockedTokens[msg.sender][token]; require(userLock.amount > 0, "No tokens locked"); require(block.timestamp >= userLock.unlockTime, "Unlock time not reached"); uint256 amount = userLock.amount; delete lockedTokens[msg.sender][token]; totalLockedTokens[token] -= amount; token.safeTransfer(msg.sender, amount); emit TokenUnlocked(msg.sender, token, amount); } //*************************************************** // BreadGT /// @notice Claim BGT rewards from POL through the BREAD protocol, and get BreadGT receipt token /// @dev Requires the reward vault to be set up and the user to have rewards /// @dev Requires Bread contract to have been set as the operator for the user in this reward vault /// @param rewardVault address of the BGT whitelisted reward vault that the user has already staked to function claimBreadGT(address rewardVault) external nonReentrant { require(breadGTEnabled, "BreadGT not enabled"); uint256 bgtBalanceBefore = BGT.balanceOf(breadTreasury); //First, claim the BGT into the breadTreasury uint256 bgtClaimed = IRewardVault(rewardVault).getReward(msg.sender, breadTreasury); require(bgtClaimed > 0, "No reward to claim"); uint256 bgtBalanceAfter = BGT.balanceOf(breadTreasury); //Confirm that the amount claimed per rewardVault contract matches the BGT balance change (to avoid fake reward vaults) require(bgtClaimed == bgtBalanceAfter - bgtBalanceBefore, "Incorrect reward amount, something went wrong"); //Mint BreadGT uint256 breadGTFee = bgtClaimed * polFeeBps / BPS_DENOMINATOR; breadGT.mint(msg.sender, bgtClaimed - breadGTFee); if(breadGTFee > 0) { breadGT.mint(breadTreasury, breadGTFee); } emit BreadGTMinted(msg.sender, bgtClaimed - breadGTFee); } //TODO: staking and withdrawals to be enabled later, no need to overengineer // /// @notice Request redemption of BreadGT for BREAD // /// @dev You must transfer in the BreadGT tokens first, and the requests can be fulfilled // /// @param amount Amount of BreadGT to redeem // function requestRedeemBreadGT(uint256 amount) external nonReentrant { // require(breadGTEnabled, "BreadGT not enabled"); // require(amount > 0, "Amount must be greater than 0"); // breadGT.approve(address(this), amount); // breadGT.transferFrom(msg.sender, address(this), amount); // breadGTRequested[msg.sender] += amount; // emit BreadGTRequested(msg.sender, amount); // } // // /// @notice Fulfill redemption request of breadGT by sending 1 BERA worth of BREAD to the user // /// @dev breadTreasury holds the BGT and can fulfill the request // /// @param user Address of user // function fulfillRedeemBreadGT(address user) external nonReentrant { // require(msg.sender == breadTreasury, "Only breadTreasury"); // require(breadGTEnabled, "BreadGT not enabled"); // uint256 redemptionRequested = breadGTRequested[user]; // require(redemptionRequested > 0, "No redemption requested"); // breadGTRequested[user] = 0; // uint256 breadAmount = BERAtoBREADNoTradeCeil(redemptionRequested); // _transfer(address(msg.sender), user, breadAmount); // breadGT.burn(address(this), redemptionRequested); // emit BreadGTFulfilled(user, redemptionRequested, breadAmount); // } //*************************************************** // iBREAD / Inflation bread /// @notice Stakes BREAD to mint iBREAD /// @dev The rate is NOT 1:1, but floating. Based on total staked BREAD and total iBREAD supply /// @dev iBREAD supply is elastic, and can be minted or burned by authorized contracts /// @dev For example, iBREAD staking could be used to facilitate liquidity for GameFi (games of chance) /// @param breadAmount Amount of BREAD to stake function stakeBread(uint256 breadAmount) external nonReentrant { require(stakingEnabled, "Staking is disabled"); require(breadAmount > 0, "Must stake more than 0"); _transfer(msg.sender, address(this), breadAmount); uint256 iBREADToMint = totalStakedBread == 0 ? breadAmount : Math.mulDiv(breadAmount, stakedBread.totalSupply(), totalStakedBread, Math.Rounding.Floor); require(iBREADToMint > 0, "Stake amount too small, rounded to 0"); totalStakedBread += breadAmount; stakedBread.mint(msg.sender, iBREADToMint); emit Staked(msg.sender, breadAmount, iBREADToMint); } /// @notice Requests to unstake iBREAD into BREAD /// @dev Must wait 24 hours after request before unstaking /// @param iBreadAmount Amount of iBread to unstake function requestUnstakeBread(uint256 iBreadAmount) external nonReentrant { // require(stakingEnabled, "Staking is disabled"); require(iBreadAmount > 0, "Must request unstake more than 0"); require(iBreadAmount <= stakedBread.balanceOf(msg.sender), "Invalid iBREAD amount"); unstakeRequestTime[msg.sender] = block.timestamp; unstakeRequestAmount[msg.sender] = iBreadAmount; emit UnstakeRequested(msg.sender, iBreadAmount); } /// @notice Unstakes iBREAD into BREAD /// @param iBreadAmount Amount of iBread to unstake function unstakeBread(uint256 iBreadAmount) external nonReentrant { // require(stakingEnabled, "Staking is disabled"); require(unstakeRequestTime[msg.sender] > 0, "Must request unstake first"); require(block.timestamp >= unstakeRequestTime[msg.sender] + 24 hours, "Must wait 24 hours after request"); require(iBreadAmount > 0, "Must unstake more than 0"); require(unstakeRequestAmount[msg.sender] == iBreadAmount, "Must unstake requested amount"); require(stakedBread.balanceOf(msg.sender) >= iBreadAmount, "Not enough iBREAD"); uint256 breadAmount = Math.mulDiv(iBreadAmount, totalStakedBread, stakedBread.totalSupply(), Math.Rounding.Floor); require(breadAmount > 0, "Unstake amount too small, rounded to 0"); totalStakedBread -= breadAmount; stakedBread.burn(msg.sender, iBreadAmount); //Burn iBREAD 1:1 _transfer(address(this), msg.sender, breadAmount); unstakeRequestTime[msg.sender] = 0; unstakeRequestAmount[msg.sender] = 0; emit Unstaked(msg.sender, breadAmount, iBreadAmount); } //*************************************************** // External functions /// alias for bake function buy(address receiver) external payable nonReentrant { _bakeBread(receiver); } /// @notice Buys BREAD tokens with BERA /// @param receiver The address to receive the BREAD tokens /// @dev Requires trading to be started /// Mints BREAD to the receiver based on the current price function bake(address receiver) external payable nonReentrant { _bakeBread(receiver); } function _bakeBread(address receiver) internal { toast(); require(started, "Trading must be initialized"); require(receiver != address(0), "Receiver cannot be 0 address"); // Mint Bread to receiver uint256 bread = BERAtoBREADFloor(msg.value); uint256 breadToBake = bread * (BPS_DENOMINATOR - bakeFeeBps) / BPS_DENOMINATOR; if (msg.sender == baker) { // check if we need to increase max supply if (totalBaked + breadToBake > maxBake) { uint256 _maxBake = maxBake + breadToBake; maxBake = _maxBake; emit MaxBakeUpdated(_maxBake); } } // Mint BREAD to receiver _bake(receiver, breadToBake); // Treasury fee uint256 treasuryAmount = msg.value * bakeFeeBps * PROTOCOL_FEE_SHARE_BPS / BPS_DENOMINATOR / BPS_DENOMINATOR; require(treasuryAmount > DUST, "must trade over min"); _sendBera(breadTreasury, treasuryAmount); _riseOnly(msg.value); emit Bake(receiver, msg.value, breadToBake); } /// @notice Sells BREAD tokens for BERA /// @param bread The amount of BREAD to sell /// @dev Burns BREAD and sends BERA to the sender based on the current price function burn(uint256 bread) external nonReentrant { toast(); // Total Bera to be sent uint256 bera = BREADtoBERAFloor(bread); //Rounds down user amount (in favor of protocol) // Burn of Bread _burn(msg.sender, bread); // Payment to sender uint256 beraToPay = bera * (BPS_DENOMINATOR - burnFeeBps) / BPS_DENOMINATOR; _sendBera(msg.sender, beraToPay); // Treasury fee uint256 treasuryAmount = bera * burnFeeBps * PROTOCOL_FEE_SHARE_BPS / BPS_DENOMINATOR / BPS_DENOMINATOR; require(treasuryAmount > DUST, "must trade over min"); _sendBera(breadTreasury, treasuryAmount); _riseOnly(bera); emit Burn(msg.sender, beraToPay, bread); } /// @notice Creates a leveraged position /// @param bera The amount of BERA to loop /// @param numberOfDays The duration of the loan in days /// @dev Requires trading to be started /// Creates a loan with collateral and borrowed amount function loop(uint256 bera, uint256 numberOfDays) public payable nonReentrant { require(started, "Trading must be initialized"); require(borrowingEnabled, "Borrowing is disabled"); require(numberOfDays < 366, "Max borrow/extension must be 365 days or less"); Loan memory userLoan = activeLoans[msg.sender]; if (userLoan.borrowed != 0) { if (isLoanExpired(msg.sender)) { delete activeLoans[msg.sender]; } require(activeLoans[msg.sender].borrowed == 0, "Use account with no loans"); } toast(); uint256 endDate = getDayStart((numberOfDays * 1 days) + block.timestamp); (uint256 bakeFee, uint256 userBorrow, uint256 overCollateralizationAmount, uint256 interestFee) = loopCalcs(bera, numberOfDays); uint256 totalBeraRequired = overCollateralizationAmount + bakeFee + interestFee; uint256 feeOverage; if (msg.value > totalBeraRequired) { feeOverage = msg.value - totalBeraRequired; _sendBera(msg.sender, feeOverage); } require(msg.value - feeOverage == totalBeraRequired, "Insufficient bera fee sent"); uint256 userBera = bera - bakeFee; uint256 userBread = BERAtoBREADLev(userBera, totalBeraRequired); _bake(address(this), userBread); uint256 treasuryAmount = (bakeFee + interestFee) * PROTOCOL_FEE_SHARE_BPS / BPS_DENOMINATOR; require(treasuryAmount > DUST, "Fees must be higher than dust"); _sendBera(breadTreasury, treasuryAmount); _addLoansOnDate(userBorrow, userBread, endDate); activeLoans[msg.sender] = Loan({collateral: userBread, borrowed: userBorrow, endDate: endDate, numberOfDays: numberOfDays, lastTimeCreated: block.timestamp}); _riseOnly(bera); emit Loop(msg.sender, bera, numberOfDays, userBread, userBorrow, totalBeraRequired); } /// @notice Creates a loan by borrowing BERA against BREAD collateral /// @param bera The amount of BERA to borrow /// @param numberOfDays The duration of the loan in days /// @dev Requires no existing loan /// @dev Use increaseBorrow with existing loan function borrow(uint256 bera, uint256 numberOfDays) public nonReentrant { require(borrowingEnabled, "Borrowing is disabled"); require(numberOfDays < 366, "Max borrow/extension must be 365 days or less"); require(bera != 0, "Must borrow more than 0"); if (isLoanExpired(msg.sender)) { delete activeLoans[msg.sender]; } require(activeLoans[msg.sender].borrowed == 0, "Use increaseBorrow to borrow more"); toast(); uint256 endDate = getDayStart((numberOfDays * 1 days) + block.timestamp); uint256 newUserBorrow = bera * COLLATERAL_RATIO / BPS_DENOMINATOR; uint256 beraFee = getInterestFee(newUserBorrow, numberOfDays); uint256 treasuryFee = beraFee * PROTOCOL_FEE_SHARE_BPS / BPS_DENOMINATOR; uint256 userBread = BERAtoBREADNoTradeCeil(bera); //Rounds up borrow amount (in favor of protocol) activeLoans[msg.sender] = Loan({collateral: userBread, borrowed: newUserBorrow, endDate: endDate, numberOfDays: numberOfDays, lastTimeCreated: block.timestamp}); _transfer(msg.sender, address(this), userBread); require(treasuryFee > DUST, "Fees must be higher than dust"); _sendBera(msg.sender, newUserBorrow - beraFee); _sendBera(breadTreasury, treasuryFee); _addLoansOnDate(newUserBorrow, userBread, endDate); _riseOnly(beraFee); emit Borrow(msg.sender, bera, numberOfDays, userBread, newUserBorrow, beraFee); } /// @notice Increases an existing loan by borrowing more BERA /// @param bera The additional amount of BERA to borrow /// @dev Requires an active non-expired loan function increaseBorrow(uint256 bera) public nonReentrant { require(borrowingEnabled, "Borrowing is disabled"); require(!isLoanExpired(msg.sender), "Loan expired use borrow"); require(bera != 0, "Must borrow more than 0"); toast(); uint256 userBorrowed = activeLoans[msg.sender].borrowed; uint256 userCollateral = activeLoans[msg.sender].collateral; uint256 userEndDate = activeLoans[msg.sender].endDate; uint256 todayMidnight = getDayStart(block.timestamp); uint256 newBorrowLength = (userEndDate - todayMidnight) / 1 days; uint256 newUserBorrow = (bera * COLLATERAL_RATIO) / BPS_DENOMINATOR; uint256 beraFee = getInterestFee(newUserBorrow, newBorrowLength); uint256 userBorrowedInBread = BERAtoBREADNoTradeCeil(userBorrowed); //Rounds up borrow amount (in favor of protocol) uint256 userExcessInBread = userCollateral - Math.mulDiv(userBorrowedInBread, BPS_DENOMINATOR, COLLATERAL_RATIO, Math.Rounding.Ceil); //Rounds up (in favor of protocol) uint256 userBread = BERAtoBREADNoTradeCeil(bera); //Rounds up borrow amount (in favor of protocol) uint256 requireCollateralFromUser = userExcessInBread >= userBread ? 0 : userBread - userExcessInBread; { uint256 newUserBorrowTotal = userBorrowed + newUserBorrow; uint256 newUserCollateralTotal = userCollateral + requireCollateralFromUser; activeLoans[msg.sender] = Loan({collateral: newUserCollateralTotal, borrowed: newUserBorrowTotal, endDate: userEndDate, numberOfDays: newBorrowLength, lastTimeCreated: block.timestamp}); } if (requireCollateralFromUser != 0) { _transfer(msg.sender, address(this), requireCollateralFromUser); } { uint256 treasuryFee = beraFee * PROTOCOL_FEE_SHARE_BPS / BPS_DENOMINATOR; require(treasuryFee > DUST, "Fees must be higher than dust"); _sendBera(breadTreasury, treasuryFee); } _sendBera(msg.sender, newUserBorrow - beraFee); _addLoansOnDate(newUserBorrow, requireCollateralFromUser, userEndDate); _riseOnly(beraFee); emit Borrow(msg.sender, bera, newBorrowLength, userBread, newUserBorrow, beraFee); } /// @notice Removes collateral from an active loan /// @param amount The amount of BREAD collateral to remove /// @dev Requires an active non-expired loan and maintains collateralization ratio function removeCollateral(uint256 amount) public nonReentrant { require(!isLoanExpired(msg.sender), "No active loans"); toast(); uint256 collateral = activeLoans[msg.sender].collateral; uint256 remainingCollateralInBera = BREADtoBERAFloor(collateral - amount); //Rounds down user amount (in favor of protocol) require(activeLoans[msg.sender].borrowed <= (remainingCollateralInBera * COLLATERAL_RATIO) / BPS_DENOMINATOR, "Require 99% collateralization rate"); activeLoans[msg.sender].collateral = activeLoans[msg.sender].collateral - amount; _transfer(address(this), msg.sender, amount); _subtractLoansOnDate(0, amount, activeLoans[msg.sender].endDate); _riseOnly(0); emit RemoveCollateral(msg.sender, amount); } /// @notice Partially repays an active loan /// @dev Requires an active non-expired loan and repayment amount less than borrowed function repay() public payable nonReentrant { uint256 borrowed = activeLoans[msg.sender].borrowed; require(borrowed > msg.value, "Must repay less than borrowed amount"); require(msg.value != 0, "Must repay something"); toast(); require(!isLoanExpired(msg.sender), "Your loan has been liquidated, cannot repay"); uint256 newBorrow = borrowed - msg.value; activeLoans[msg.sender].borrowed = newBorrow; _subtractLoansOnDate(msg.value, 0, activeLoans[msg.sender].endDate); _riseOnly(0); emit Repay(msg.sender, msg.value, newBorrow); } /// @notice Fully repays a loan and returns collateral /// @dev Requires an active non-expired loan and exact repayment amount /// @dev Applies a 1% fee on the collateral value function closePosition() public payable nonReentrant { uint256 borrowed = activeLoans[msg.sender].borrowed; uint256 collateral = activeLoans[msg.sender].collateral; require(!isLoanExpired(msg.sender), "No active loans"); require(borrowed == msg.value, "Must return entire borrowed amount"); toast(); _transfer(address(this), msg.sender, collateral); _subtractLoansOnDate(borrowed, collateral, activeLoans[msg.sender].endDate); delete activeLoans[msg.sender]; _riseOnly(0); emit Repay(msg.sender, msg.value, 0); } /// @notice Allows users to close their loan positions by using their BREAD collateral directly /// @dev Requires an active non-expired loan with sufficient collateral value function flashBurn() public nonReentrant { require(!isLoanExpired(msg.sender), "No active loan"); toast(); uint256 borrowed = activeLoans[msg.sender].borrowed; uint256 collateral = activeLoans[msg.sender].collateral; uint256 collateralInBera = BREADtoBERAFloor(collateral); //Rounds down user amount (in favor of protocol) _burn(address(this), collateral); uint256 burnFee = collateralInBera * burnFeeBps / BPS_DENOMINATOR; uint256 collateralInBeraAfterFee = collateralInBera - burnFee; require(collateralInBeraAfterFee >= borrowed, "Not enough collateral to close position"); //This seems redundant, but fine to keep // Explanation of how it works: // Assume BREAD/BERA = 1.10 // I own 90.90 BREAD = 100 BERA worth of BREAD // I max borrow against it for 1 year // Max borrow possible is 99 BERA // Interest fee = 99 * 6.9% APR // Thus, borrow amount = 99 BERA and bera received = 92.169 BERA // Collateral = 100 BERA (worth of) BREAD = 90.90 BREAD // Let's say BREAD/BERA rises to 1.20, and now I want to close my position // Option 1: close the loan normally. // I flash borrow (elsewhere, assume for 0 cost) and pay back 99 BERA // I receive 90.90 BREAD // I burn the BREAD for 90.90 * 1.20 = 109.08 BERA - 2.69% burn fee // I receive 106.145 BERA and pay back by flash borrow, I netted 106.145 - 99 BERA = 7.145 BERA // Option 2: I call flashBurn() // My entire BREAD collateral (90.90 BREAD) is burned // My collateral was worth 90.90 * 120 = 109.08 BERA // The fee is 109.08 * burnFee = 109.08 * 2.69% = 2.93 BERA // Remaining collateral in BERA after fee = 109.08 - 2.934 = 106.145 BERA // 106.145 BERA must be worth more than the borrowed amount (99 BERA) -> which it is // I get back collateral value - borrowed - fee = 106.145 BERA - 99 BERA = 7.145 BERA // Note this is the exact same outcome as Option 1 uint256 toUser = collateralInBeraAfterFee - borrowed; uint256 treasuryFee = burnFee * PROTOCOL_FEE_SHARE_BPS / BPS_DENOMINATOR; if (toUser > 0) { _sendBera(msg.sender, toUser); } require(treasuryFee > DUST, "Fees must be higher than dust"); _sendBera(breadTreasury, treasuryFee); _subtractLoansOnDate(borrowed, collateral, activeLoans[msg.sender].endDate); delete activeLoans[msg.sender]; _riseOnly(borrowed); emit FlashBurn(msg.sender, borrowed, collateral, toUser, burnFee); } /// @notice Extends the duration of an existing loan /// @param numberOfDays Additional days to extend the loan /// @return The fee paid for the extension /// @dev Requires an active non-expired loan and payment of extension fee function extendLoan(uint256 numberOfDays) public payable nonReentrant returns (uint256) { require(borrowingEnabled, "Borrowing is disabled"); uint256 oldEndDate = activeLoans[msg.sender].endDate; uint256 borrowed = activeLoans[msg.sender].borrowed; uint256 collateral = activeLoans[msg.sender].collateral; uint256 _numberOfDays = activeLoans[msg.sender].numberOfDays; uint256 newEndDate = oldEndDate + (numberOfDays * 1 days); uint256 loanFee = getInterestFee(borrowed, numberOfDays); require(!isLoanExpired(msg.sender), "No active loans"); require(loanFee == msg.value, "Loan extension fee incorrect"); uint256 treasuryFee = loanFee * PROTOCOL_FEE_SHARE_BPS / BPS_DENOMINATOR; require(treasuryFee > DUST, "Fees must be higher than dust"); toast(); _sendBera(breadTreasury, treasuryFee); _subtractLoansOnDate(borrowed, collateral, oldEndDate); _addLoansOnDate(borrowed, collateral, newEndDate); activeLoans[msg.sender].endDate = newEndDate; activeLoans[msg.sender].numberOfDays = numberOfDays + _numberOfDays; require((newEndDate - block.timestamp) / 1 days < 366, "Loan must be under 365 days"); _riseOnly(msg.value); emit LoanExtended(msg.sender, numberOfDays, collateral, borrowed, loanFee); return loanFee; } /// @notice Liquidates expired loans /// @dev Processes all expired loans up to the current timestamp /// @dev Burns collateral and updates total borrowed/collateral amounts /// @dev The Loan mapping is not updated for the users whose loan are liquidated function toast() public { uint256 borrowed; uint256 collateral; while (lastToastDate < block.timestamp) { collateral = collateral + collateralByDate[lastToastDate]; borrowed = borrowed + loansByDate[lastToastDate]; lastToastDate = lastToastDate + 1 days; } if (collateral != 0) { totalCollateral = totalCollateral - collateral; _burn(address(this), collateral); } if (borrowed != 0) { totalLoans = totalLoans - borrowed; emit Toast(lastToastDate - 1 days, borrowed); } } //*************************************************** // Internal functions /// @notice Mints BREAD tokens to a specified address /// @param to Address to receive the minted tokens /// @param value Amount of tokens to mint /// @dev Updates totalMinted, enforces max supply, and prevents minting to zero address function _bake(address to, uint256 value) private { require(to != address(0), "Can't mint to 0 address"); totalBaked = totalBaked + value; require(totalBaked <= maxBake, "NO MORE BREAD"); _mint(to, value); } /// @notice Adds loan data to the tracking by expiration date /// @param borrowed Amount borrowed in the loan /// @param collateral Amount of collateral in the loan /// @param date Expiration date of the loan /// @dev Updates global tracking variables and emits LoanDataUpdate event function _addLoansOnDate(uint256 borrowed, uint256 collateral, uint256 date) private { collateralByDate[date] = collateralByDate[date] + collateral; loansByDate[date] = loansByDate[date] + borrowed; totalLoans = totalLoans + borrowed; totalCollateral = totalCollateral + collateral; emit LoanDataUpdate(collateralByDate[date], loansByDate[date], totalLoans, totalCollateral); } /// @notice Subtracts loan data from the tracking by expiration date /// @param borrowed Amount borrowed to subtract /// @param collateral Amount of collateral to subtract /// @param date Expiration date of the loan /// @dev Updates global tracking variables and emits LoanDataUpdate event function _subtractLoansOnDate(uint256 borrowed, uint256 collateral, uint256 date) private { collateralByDate[date] = collateralByDate[date] - collateral; loansByDate[date] = loansByDate[date] - borrowed; totalLoans = totalLoans - borrowed; totalCollateral = totalCollateral - collateral; emit LoanDataUpdate(collateralByDate[date], loansByDate[date], totalLoans, totalCollateral); } /// @notice Performs safety checks after operations that affect the protocol's state /// @param bera Amount of BERA involved in the operation /// @dev Ensures contract balance covers all collateral, price only increases, and emits Price event function _riseOnly(uint256 bera) private { uint256 newPrice = (getBacking() * 1 ether) / totalSupply(); uint256 _totalCollateral = balanceOf(address(this)); require(_totalCollateral >= totalCollateral, "The bread balance of the contract must be greater than or equal to the collateral"); require(prevPrice <= newPrice, "The price of bread cannot decrease"); prevPrice = newPrice; emit PriceUpdated(block.timestamp, newPrice, bera); } /// @notice Sends BERA to a specified address /// @param _address Recipient address /// @param _value Amount of BERA to send /// @dev Emits SendBera event on successful transfer function _sendBera(address _address, uint256 _value) internal { require(_address != address(0), "Can't send to 0 address"); _address.safeTransferETH(_value); emit SendBera(_address, _value); } //*************************************************** // Utility functions /// @notice Calculates the start timestamp for a given date /// @param date Timestamp to convert /// @return Start timestamp of the current day function getDayStart(uint256 date) public pure returns (uint256) { uint256 dayBoundary = date - (date % 86400); // Subtracting the remainder when divided by the number of seconds in a day (86400) return dayBoundary + 1 days; } /// @notice Gets the total borrowed and collateral amounts expiring on a specific date /// @param date Date to check /// @return Borrowed amount and collateral amount expiring on the date function getLoansExpiringByDate(uint256 date) public view returns (uint256, uint256) { return (loansByDate[getDayStart(date)], collateralByDate[getDayStart(date)]); } /// @notice Gets loan details for a specific address /// @param _address Address to check /// @return Collateral amount, borrowed amount, and end date of the loan /// @dev Returns zeros if the loan has expired function getLoanByAddress(address _address) public view returns (uint256, uint256, uint256) { if (activeLoans[_address].endDate >= block.timestamp) { return (activeLoans[_address].collateral, activeLoans[_address].borrowed, activeLoans[_address].endDate); } else { return (0, 0, 0); } } /// @notice Calculates the leverage fee for a loan /// @param bera Amount of BERA to borrow /// @param numberOfDays Duration of the loan in days function loopCalcs(uint256 bera, uint256 numberOfDays) public view returns (uint256 bakeFee, uint256 userBorrow, uint256 overCollateralizationAmount, uint256 interest) { bakeFee = bera * leverageFeeBps / BPS_DENOMINATOR; uint256 userBera = bera - bakeFee; userBorrow = userBera * COLLATERAL_RATIO / BPS_DENOMINATOR; overCollateralizationAmount = userBera - userBorrow; interest = getInterestFee(userBorrow, numberOfDays); } /// @notice Calculates the interest fee for a loan /// @param borrowed Amount borrowed /// @param numberOfDays Duration of the loan in days /// @return Interest fee amount function getInterestFee(uint256 borrowed, uint256 numberOfDays) public view returns (uint256) { uint256 interestFee = borrowed * INTEREST_APR_BPS * numberOfDays * 1e18 / 365 / BPS_DENOMINATOR / 1e18; uint256 overCollateralizationAmount = borrowed * (BPS_DENOMINATOR - COLLATERAL_RATIO) / COLLATERAL_RATIO; uint256 burnFee = (borrowed + overCollateralizationAmount) * burnFeeBps / BPS_DENOMINATOR; // Assume you have 100 BERA worth of BREAD // Borrow 99 BERA // If I sold my BREAD, I would get 100 BERA - 2.69% burn fee = 97.31 BERA // If I borrow BERA against that same BREAD, I should receive no more than 97.31 BERA // If I borrow 99 BERA, that means there is 1 BERA overCollateralizationAmount // Interest Fee should be at least (99 - 97.31) = 1.69 BERA // Ensure that borrowing + letting yourself get liquidated isn't cheaper than burning if(burnFee <= overCollateralizationAmount) { return interestFee; } else if(interestFee >= burnFee - overCollateralizationAmount) { return interestFee; } else { return burnFee - overCollateralizationAmount; } } /// @notice Checks if a loan for a specific address has expired /// @param _address Address to check /// @return True if the loan has expired, false otherwise function isLoanExpired(address _address) public view returns (bool) { return activeLoans[_address].endDate < block.timestamp; } /// @notice Calculates the total backing value of the protocol /// @return Sum of contract balance and total borrowed amount function getBacking() public view returns (uint256) { return address(this).balance + totalLoans; } /// @notice Converts BREAD tokens to BERA /// @dev Round down user amount (in favor of protocol) /// @param value Amount of BREAD to convert /// @return Equivalent amount in BERA function BREADtoBERAFloor(uint256 value) public view returns (uint256) { return Math.mulDiv(value, getBacking(), totalSupply(), Math.Rounding.Floor); } /// @notice Converts BERA to BREAD tokens, To be used when Bera is already received from the user. /// @dev Rounds down user amount (in favor of protocol). /// @param value Amount of BERA to convert /// @return Equivalent amount in BREAD function BERAtoBREADFloor(uint256 value) public view returns (uint256) { return Math.mulDiv(value, totalSupply(), getBacking() - value, Math.Rounding.Floor); } /// @notice Converts BERA to BREAD tokens with leverage fee consideration. /// @dev Rounds down user amount (in favor of protocol). /// @param value Amount of BERA to convert /// @param totalBeraRequired Net fee + overcollaterization amount received from the user /// @return Equivalent amount in BREAD function BERAtoBREADLev(uint256 value, uint256 totalBeraRequired) public view returns (uint256) { uint256 backing = getBacking() - totalBeraRequired; return Math.mulDiv(value, totalSupply(), backing, Math.Rounding.Floor); } /// @notice Converts BERA to BREAD without receiving BERA, Rounds up. /// @param value Amount of BERA to convert /// @return Equivalent amount in BREAD (rounded up) function BERAtoBREADNoTradeCeil(uint256 value) public view returns (uint256) { uint256 backing = getBacking(); return Math.mulDiv(value, totalSupply(), backing, Math.Rounding.Ceil); } /// @notice Converts BERA to BREAD without receiving BERA. Rounds down. /// @param value Amount of BERA to convert /// @return Equivalent amount in BREAD function BERAtoBREADNoTradeFloor(uint256 value) public view returns (uint256) { uint256 backing = getBacking(); return Math.mulDiv(value, totalSupply(), backing, Math.Rounding.Floor); } //*************************************************** // Frontend View functions /// @notice Calculates the maximum borrowable amount and borrow details /// @param _user Address of the user /// @param _numberOfDays Duration of the loan in days function getMaxBorrow(address _user, uint256 _numberOfDays) external view returns (uint256 userBera, uint256 userBorrow, uint256 interestFee) { uint256 userBreadBalance = balanceOf(_user) + getFreeCollateral(_user); userBera = BREADtoBERAFloor(userBreadBalance); userBorrow = userBera * COLLATERAL_RATIO / BPS_DENOMINATOR; interestFee = getInterestFee(userBorrow, _numberOfDays); } /// @notice Return the free collateral for a user that can be withdrawn via removeCollateral() /// @param user The address of the user /// @return The amount of free collateral in BREAD function getFreeCollateral(address user) public view returns (uint256) { if (isLoanExpired(user)) { return 0; } uint256 userCollateral = activeLoans[user].collateral; uint256 userBorrowed = activeLoans[user].borrowed; // Note this is the same calculation as in increaseBorrow uint256 userBorrowedInBread = BERAtoBREADNoTradeCeil(userBorrowed); //Rounds up borrow amount (in favor of protocol) return userCollateral - Math.mulDiv(userBorrowedInBread, BPS_DENOMINATOR, COLLATERAL_RATIO, Math.Rounding.Ceil); //Rounds up (in favor of protocol) } /// @notice Calculates the amount of BREAD you get by buying with BERA /// @param beraAmount Amount of BERA to spend /// @return Amount of BREAD user would receive function getAmountOutBuy(uint256 beraAmount) external view returns (uint256) { uint256 breadAmount = BERAtoBREADNoTradeFloor(beraAmount); return breadAmount * (BPS_DENOMINATOR - bakeFeeBps) / BPS_DENOMINATOR; } /// @notice Calculates the amount of BERA you get by selling BREAD /// @param breadAmount Amount of BREAD to sell /// @return Amount of BERA user would receive function getAmountOutSell(uint256 breadAmount) external view returns (uint256) { uint256 beraAmount = BREADtoBERAFloor(breadAmount); return beraAmount * (BPS_DENOMINATOR - burnFeeBps) / BPS_DENOMINATOR; } /// @notice Calculates the input Bera to call in loopCalcs given the totalBeraRequired /// @param totalBeraRequired = bakeFee + interest + overcollateralizationAmount /// @param numberOfDays Duration of the loan in days function inverseLoopCalc(uint256 totalBeraRequired, uint256 numberOfDays) public view returns (uint256 bera) { uint256 low = totalBeraRequired * 5; //initial guess (5x - 100x leverage, it should always be within these bounds) uint256 high = totalBeraRequired * 100; uint256 mid; while (low < high) { mid = (low + high + 1) / 2; // Bias towards upper range to avoid infinite loops (uint256 bakeFee, , uint256 overCollateralizationAmount, uint256 interest) = loopCalcs(mid, numberOfDays); uint256 calculatedTotal = interest + overCollateralizationAmount + bakeFee; if (calculatedTotal < totalBeraRequired) { low = mid; // Move upwards } else { high = mid - 1; // Move downwards } } return low; } //*************************************************** /// @notice Fallback function to receive BERA receive() external payable {} //*************************************************** // Events /// @notice Emitted when the price of BREAD changes /// @param time Timestamp of the price change /// @param price New price of BREAD in BERA /// @param volumeInBera Volume of the transaction in BERA event PriceUpdated(uint256 time, uint256 price, uint256 volumeInBera); /// @notice Emitted when a user bakes BREAD /// @param receiver Address of the buyer /// @param amount Amount of BERA spent /// @param bread Amount of BREAD received event Bake(address indexed receiver, uint256 amount, uint256 bread); /// @notice Emitted when a user burns BREAD /// @param seller Address of the seller /// @param bera Amount of BERA received /// @param bread Amount of BREAD sold event Burn(address indexed seller, uint256 bera, uint256 bread); /// @notice Emitted when a user takes a leveraged position in BREAD /// @param user Address of the user /// @param bera Amount of BERA used for leverage /// @param numberOfDays Duration of leverage in days /// @param userBread Amount of BREAD held by the user before leverage /// @param userBorrow Total borrowed BERA after leverage /// @param fee Fee charged for leverage event Loop(address indexed user, uint256 bera, uint256 numberOfDays, uint256 userBread, uint256 userBorrow, uint256 fee); /// @notice Emitted when a user borrows BERA against BREAD collateral /// @param user Address of the borrower /// @param bera Amount of BERA borrowed /// @param numberOfDays Duration of the loan in days /// @param userBread Amount of BREAD held as collateral /// @param newUserBorrow Total outstanding debt after borrowing /// @param fee Fee charged for borrowing event Borrow(address indexed user, uint256 bera, uint256 numberOfDays, uint256 userBread, uint256 newUserBorrow, uint256 fee); /// @notice Emitted when a user removes collateral /// @param user Address of the user /// @param amount Amount of collateral removed event RemoveCollateral(address indexed user, uint256 amount); /// @notice Emitted when a user repays their loan /// @param user Address of the borrower /// @param amount Amount of BERA repaid /// @param newBorrow Remaining outstanding debt after repayment event Repay(address indexed user, uint256 amount, uint256 newBorrow); /// @notice Emitted when a user closes a leveraged position using a flash loan /// @param user Address of the user /// @param borrowed Amount of BERA borrowed via flash loan /// @param collateral Amount of BREAD collateral liquidated /// @param toUser Amount returned to the user after closing position /// @param fee Fee charged for using the flash loan event FlashBurn(address indexed user, uint256 borrowed, uint256 collateral, uint256 toUser, uint256 fee); /// @notice Emitted when a loan is extended /// @param user Address of the borrower /// @param numberOfDays Additional loan duration in days /// @param collateral Amount of BREAD held as collateral /// @param borrowed Amount of BERA borrowed /// @param fee Fee charged for the extension event LoanExtended(address indexed user, uint256 numberOfDays, uint256 collateral, uint256 borrowed, uint256 fee); /// @notice Emitted when the max supply is updated /// @param max New maximum supply event MaxBakeUpdated(uint256 max); /// @notice Emitted when baker contract is updated /// @param baker New baker address event BakerSet(address baker); /// @notice Emitted when the sell fee is updated /// @param sellFee New sell fee event BurnFeeUpdated(uint256 sellFee); /// @notice Emitted when the fee address is updated /// @param _address New fee address event BreadTreasuryUpdated(address _address); /// @notice Emitted when the bake fee is updated /// @param bakeFee New bake fee event BakeFeeUpdated(uint256 bakeFee); /// @notice Emitted when the leverage fee is updated /// @param leverageFee New leverage fee event LeverageFeeUpdated(uint256 leverageFee); /// @notice Emitted when trading is started /// @param started Whether trading has started event Started(bool started); /// @notice Triggered when borrowing is enabled /// @param enabled Whether borrowing is enabled event BorrowingEnabled(bool enabled); /// @notice Emitted when loans are toasted /// @param time Timestamp of toast /// @param amount Amount of BERA toasted event Toast(uint256 indexed time, uint256 amount); /// @notice Emitted when loan data is updated /// @param collateralByDate Total collateral amount for a specific date /// @param borrowedByDate Total borrowed amount for a specific date /// @param totalBorrowed Total borrowed amount /// @param totalCollateral Total collateral amount event LoanDataUpdate(uint256 collateralByDate, uint256 borrowedByDate, uint256 totalBorrowed, uint256 totalCollateral); /// @notice Emitted when BERA is sent /// @param to Recipient address /// @param amount Amount of BERA sent event SendBera(address to, uint256 amount); /// @notice Emitted when fees are collected /// @param sender Address of the sender /// @param bounty Amount of BERA paid as bounty /// @param tokens List of ERC-20 tokens collected /// @param amounts Amounts of ERC-20 tokens collected event BountyCollected(address indexed sender, uint256 indexed bounty, address[] tokens, uint256[] amounts); /// @notice Emitted when the bribe bounty is updated /// @param amount New bribe bounty amount event BribeBountyUpdated(uint256 amount); /// @notice Emitted when the token locker fee is updated /// @param tokenLockerFee New token locker fee event TokenLockerFeeUpdated(uint256 tokenLockerFee); /// @notice Emitted when a token is locked /// @param sender Address of the user /// @param token Address of the token /// @param lockAmount Amount of tokens locked /// @param unlockTime Time when the tokens can be unlocked event TokenLocked(address indexed sender, address indexed token, uint256 lockAmount, uint256 unlockTime); /// @notice Emitted when a token is unlocked /// @param sender Address of the user /// @param token Address of the token /// @param amount Amount of tokens unlocked event TokenUnlocked(address indexed sender, address indexed token, uint256 amount); /// @notice Emitted when BREAD is staked for iBREAD /// @param sender Address of the user /// @param breadAmount Amount of BREAD to stake /// @param iBreadAmount Amount of iBread received event Staked(address indexed sender, uint256 breadAmount, uint256 iBreadAmount); /// @notice Emitted when iBread is requested to be unstaked /// @param user Address of the user /// @param iBreadAmount Amount of iBread to unstake event UnstakeRequested(address indexed user, uint256 iBreadAmount); /// @notice Emitted when iBread is unstaked for BREAD /// @param sender Address of the user /// @param breadAmount Amount of BREAD received /// @param iBreadAmount Amount of iBread unstaked event Unstaked(address indexed sender, uint256 breadAmount, uint256 iBreadAmount); /// @notice Triggered when BREAD staking is enabled /// @param enabled Whether staking is enabled event StakingEnabled(bool enabled); /// @notice Triggered when BreadGT is enabled /// @param enabled Whether BreadGT is enabled event BreadGTEnabled(bool enabled); /// @notice Emitted when the POL fee is updated /// @param polFee New POL fee event PolFeeUpdated(uint256 polFee); /// @notice Emitted when BreadGT is minted /// @param user Address of the user /// @param amount Amount of BreadGT minted event BreadGTMinted(address indexed user, uint256 amount); // // /// @notice Emitted when BreadGT is requested to be redeemed // /// @param user Address of the user // /// @param amount Amount of BreadGT redemption requested // event BreadGTRequested(address indexed user, uint256 amount); // // /// @notice Emitted when BreadGT is redeemed // /// @param user Address of the user // /// @param amount Amount of BreadGT redeemed // event BreadGTFulfilled(address indexed user, uint256 amount, uint256 breadAmount); }
// 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(); /// @dev The allowance of Permit2 is fixed at infinity. error Permit2AllowanceIsFixedAtInfinity(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* 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")`. /// If you need to use a different version, override `_versionHash`. bytes32 private constant _DEFAULT_VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6; /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`. bytes32 private constant _PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; /// @dev The canonical Permit2 address. /// For signature-based allowance granting for single transaction ERC20 `transferFrom`. /// To enable, override `_givePermit2InfiniteAllowance()`. /// [Github](https://github.com/Uniswap/permit2) /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) address internal constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* 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) { if (_givePermit2InfiniteAllowance()) { if (spender == _PERMIT2) return type(uint256).max; } /// @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) { if (_givePermit2InfiniteAllowance()) { /// @solidity memory-safe-assembly assembly { // If `spender == _PERMIT2 && amount != type(uint256).max`. if iszero(or(xor(shr(96, shl(96, spender)), _PERMIT2), iszero(not(amount)))) { mstore(0x00, 0x3f68539a) // `Permit2AllowanceIsFixedAtInfinity()`. revert(0x1c, 0x04) } } } /// @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); // Code duplication is for zero-cost abstraction if possible. if (_givePermit2InfiniteAllowance()) { /// @solidity memory-safe-assembly assembly { let from_ := shl(96, from) if iszero(eq(caller(), _PERMIT2)) { // 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 not(allowance_) { // 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))) } } else { /// @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 not(allowance_) { // 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 If you need a different value, override this function. function _versionHash() internal view virtual returns (bytes32 result) { result = _DEFAULT_VERSION_HASH; } /// @dev For inheriting contracts to increment the nonce. function _incrementNonce(address owner) internal virtual { /// @solidity memory-safe-assembly assembly { mstore(0x0c, _NONCES_SLOT_SEED) mstore(0x00, owner) let nonceSlot := keccak256(0x0c, 0x20) sstore(nonceSlot, add(1, sload(nonceSlot))) } } /// @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 { if (_givePermit2InfiniteAllowance()) { /// @solidity memory-safe-assembly assembly { // If `spender == _PERMIT2 && value != type(uint256).max`. if iszero(or(xor(shr(96, shl(96, spender)), _PERMIT2), iszero(not(value)))) { mstore(0x00, 0x3f68539a) // `Permit2AllowanceIsFixedAtInfinity()`. revert(0x1c, 0x04) } } } 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())); bytes32 versionHash = _versionHash(); /// @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), versionHash) 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, 0x00, 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())); bytes32 versionHash = _versionHash(); /// @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), versionHash) 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 { if (_givePermit2InfiniteAllowance()) { if (spender == _PERMIT2) return; // Do nothing, as allowance is infinite. } /// @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 not(allowance_) { // 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 { if (_givePermit2InfiniteAllowance()) { /// @solidity memory-safe-assembly assembly { // If `spender == _PERMIT2 && amount != type(uint256).max`. if iszero(or(xor(shr(96, shl(96, spender)), _PERMIT2), iszero(not(amount)))) { mstore(0x00, 0x3f68539a) // `Permit2AllowanceIsFixedAtInfinity()`. revert(0x1c, 0x04) } } } /// @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 {} /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PERMIT2 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns whether to fix the Permit2 contract's allowance at infinity. /// /// This value should be kept constant after contract initialization, /// or else the actual allowance values may not match with the {Approval} events. /// For best performance, return a compile-time constant for zero-cost abstraction. function _givePermit2InfiniteAllowance() internal view virtual returns (bool) { return true; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple single owner authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// /// @dev Note: /// This implementation does NOT auto-initialize the owner to `msg.sender`. /// You MUST call the `_initializeOwner` in the constructor / initializer. /// /// While the ownable portion follows /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility, /// the nomenclature for the 2-step ownership handover may be unique to this codebase. abstract contract Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The caller is not authorized to call the function. error Unauthorized(); /// @dev The `newOwner` cannot be the zero address. error NewOwnerIsZeroAddress(); /// @dev The `pendingOwner` does not have a valid handover request. error NoHandoverRequest(); /// @dev Cannot double-initialize. error AlreadyInitialized(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership is transferred from `oldOwner` to `newOwner`. /// This event is intentionally kept the same as OpenZeppelin's Ownable to be /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), /// despite it not being as lightweight as a single argument event. event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @dev An ownership handover to `pendingOwner` has been requested. event OwnershipHandoverRequested(address indexed pendingOwner); /// @dev The ownership handover to `pendingOwner` has been canceled. event OwnershipHandoverCanceled(address indexed pendingOwner); /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The owner slot is given by: /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`. /// It is intentionally chosen to be a high value /// to avoid collision with lower slots. /// The choice of manual storage layout is to enable compatibility /// with both regular and upgradeable contracts. bytes32 internal constant _OWNER_SLOT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927; /// The ownership handover slot of `newOwner` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) /// let handoverSlot := keccak256(0x00, 0x20) /// ``` /// It stores the expiry timestamp of the two-step ownership handover. uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Override to return true to make `_initializeOwner` prevent double-initialization. function _guardInitializeOwner() internal pure virtual returns (bool guard) {} /// @dev Initializes the owner directly without authorization guard. /// This function must be called upon initialization, /// regardless of whether the contract is upgradeable or not. /// This is to enable generalization to both regular and upgradeable contracts, /// and to save gas in case the initial owner is not the caller. /// For performance reasons, this function will not check if there /// is an existing owner. function _initializeOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT if sload(ownerSlot) { mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`. revert(0x1c, 0x04) } // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } else { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(_OWNER_SLOT, newOwner) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } } /// @dev Sets the owner directly without authorization guard. function _setOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) } } else { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, newOwner) } } } /// @dev Throws if the sender is not the owner. function _checkOwner() internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner, revert. if iszero(eq(caller(), sload(_OWNER_SLOT))) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } /// @dev Returns how long a two-step ownership handover is valid for in seconds. /// Override to return a different value if needed. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _ownershipHandoverValidFor() internal view virtual returns (uint64) { return 48 * 3600; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to transfer the ownership to `newOwner`. function transferOwnership(address newOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { if iszero(shl(96, newOwner)) { mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`. revert(0x1c, 0x04) } } _setOwner(newOwner); } /// @dev Allows the owner to renounce their ownership. function renounceOwnership() public payable virtual onlyOwner { _setOwner(address(0)); } /// @dev Request a two-step ownership handover to the caller. /// The request will automatically expire in 48 hours (172800 seconds) by default. function requestOwnershipHandover() public payable virtual { unchecked { uint256 expires = block.timestamp + _ownershipHandoverValidFor(); /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to `expires`. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), expires) // Emit the {OwnershipHandoverRequested} event. log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) } } } /// @dev Cancels the two-step ownership handover to the caller, if any. function cancelOwnershipHandover() public payable virtual { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), 0) // Emit the {OwnershipHandoverCanceled} event. log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) } } /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. /// Reverts if there is no existing ownership handover requested by `pendingOwner`. function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) let handoverSlot := keccak256(0x0c, 0x20) // If the handover does not exist, or has expired. if gt(timestamp(), sload(handoverSlot)) { mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`. revert(0x1c, 0x04) } // Set the handover slot to 0. sstore(handoverSlot, 0) } _setOwner(pendingOwner); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of the contract. function owner() public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { result := sload(_OWNER_SLOT) } } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the handover slot. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) // Load the handover slot. result := sload(keccak256(0x0c, 0x20)) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by the owner. modifier onlyOwner() virtual { _checkOwner(); _; } }
// 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 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) /// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol) /// /// @dev Note: /// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection. 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(); /// @dev The ERC20 `totalSupply` query has failed. error TotalSupplyQueryFailed(); /// @dev The Permit2 operation has failed. error Permit2Failed(); /// @dev The Permit2 amount must be less than `2**160 - 1`. error Permit2AmountOverflow(); /// @dev The Permit2 approve operation has failed. error Permit2ApproveFailed(); /// @dev The Permit2 lockdown operation has failed. error Permit2LockdownFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* 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; /// @dev The unique EIP-712 domain domain separator for the DAI token contract. bytes32 internal constant DAI_DOMAIN_SEPARATOR = 0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7; /// @dev The address for the WETH9 contract on Ethereum mainnet. address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; /// @dev The canonical Permit2 address. /// [Github](https://github.com/Uniswap/permit2) /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* 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)`. let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { 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 `from` to `to`. /// /// The `from` account must have at least `amount` approved for the current contract to manage. function trySafeTransferFrom(address token, address from, address to, uint256 amount) internal returns (bool success) { /// @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)`. success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { success := lt(or(iszero(extcodesize(token)), returndatasize()), success) } 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. let success := call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { 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. let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { 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. let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { 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)`. let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { 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. let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { 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. success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) if iszero(and(eq(mload(0x00), 1), success)) { // Check the `extcodesize` again just in case the token selfdestructs lol. if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) { 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( // The arguments of `mul` are evaluated from right to left. 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) ) ) } } /// @dev Returns the total supply of the `token`. /// Reverts if the token does not exist or does not implement `totalSupply()`. function totalSupply(address token) internal view returns (uint256 result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x18160ddd) // `totalSupply()`. if iszero( and(gt(returndatasize(), 0x1f), staticcall(gas(), token, 0x1c, 0x04, 0x00, 0x20)) ) { mstore(0x00, 0x54cd9435) // `TotalSupplyQueryFailed()`. revert(0x1c, 0x04) } result := mload(0x00) } } /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// If the initial attempt fails, try to use Permit2 to transfer the token. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for the current contract to manage. function safeTransferFrom2(address token, address from, address to, uint256 amount) internal { if (!trySafeTransferFrom(token, from, to, amount)) { permit2TransferFrom(token, from, to, amount); } } /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2. /// Reverts upon failure. function permit2TransferFrom(address token, address from, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) mstore(add(m, 0x74), shr(96, shl(96, token))) mstore(add(m, 0x54), amount) mstore(add(m, 0x34), to) mstore(add(m, 0x20), shl(96, from)) // `transferFrom(address,address,uint160,address)`. mstore(m, 0x36c78516000000000000000000000000) let p := PERMIT2 let exists := eq(chainid(), 1) if iszero(exists) { exists := iszero(iszero(extcodesize(p))) } if iszero( and( call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00), lt(iszero(extcodesize(token)), exists) // Token has code and Permit2 exists. ) ) { mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`. revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04) } } } /// @dev Permit a user to spend a given amount of /// another user's tokens via native EIP-2612 permit if possible, falling /// back to Permit2 if native permit fails or is not implemented on the token. function permit2( address token, address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { bool success; /// @solidity memory-safe-assembly assembly { for {} shl(96, xor(token, WETH9)) {} { mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`. if iszero( and( // The arguments of `and` are evaluated from right to left. lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word. // Gas stipend to limit gas burn for tokens that don't refund gas when // an non-existing function is called. 5K should be enough for a SLOAD. staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20) ) ) { break } // After here, we can be sure that token is a contract. let m := mload(0x40) mstore(add(m, 0x34), spender) mstore(add(m, 0x20), shl(96, owner)) mstore(add(m, 0x74), deadline) if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) { mstore(0x14, owner) mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`. mstore( add(m, 0x94), lt(iszero(amount), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20)) ) mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`. // `nonces` is already at `add(m, 0x54)`. // `amount != 0` is already stored at `add(m, 0x94)`. mstore(add(m, 0xb4), and(0xff, v)) mstore(add(m, 0xd4), r) mstore(add(m, 0xf4), s) success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00) break } mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`. mstore(add(m, 0x54), amount) mstore(add(m, 0x94), and(0xff, v)) mstore(add(m, 0xb4), r) mstore(add(m, 0xd4), s) success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00) break } } if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s); } /// @dev Simple permit on the Permit2 contract. function simplePermit2( address token, address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) mstore(m, 0x927da105) // `allowance(address,address,address)`. { let addressMask := shr(96, not(0)) mstore(add(m, 0x20), and(addressMask, owner)) mstore(add(m, 0x40), and(addressMask, token)) mstore(add(m, 0x60), and(addressMask, spender)) mstore(add(m, 0xc0), and(addressMask, spender)) } let p := mul(PERMIT2, iszero(shr(160, amount))) if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`. staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60) ) ) { mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`. revert(add(0x18, shl(2, iszero(p))), 0x04) } mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant). // `owner` is already `add(m, 0x20)`. // `token` is already at `add(m, 0x40)`. mstore(add(m, 0x60), amount) mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`. // `nonce` is already at `add(m, 0xa0)`. // `spender` is already at `add(m, 0xc0)`. mstore(add(m, 0xe0), deadline) mstore(add(m, 0x100), 0x100) // `signature` offset. mstore(add(m, 0x120), 0x41) // `signature` length. mstore(add(m, 0x140), r) mstore(add(m, 0x160), s) mstore(add(m, 0x180), shl(248, v)) if iszero( // Revert if token does not have code, or if the call fails. mul(extcodesize(token), call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00))) { mstore(0x00, 0x6b836e6b) // `Permit2Failed()`. revert(0x1c, 0x04) } } } /// @dev Approves `spender` to spend `amount` of `token` for `address(this)`. function permit2Approve(address token, address spender, uint160 amount, uint48 expiration) internal { /// @solidity memory-safe-assembly assembly { let addressMask := shr(96, not(0)) let m := mload(0x40) mstore(m, 0x87517c45) // `approve(address,address,uint160,uint48)`. mstore(add(m, 0x20), and(addressMask, token)) mstore(add(m, 0x40), and(addressMask, spender)) mstore(add(m, 0x60), and(addressMask, amount)) mstore(add(m, 0x80), and(0xffffffffffff, expiration)) if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) { mstore(0x00, 0x324f14ae) // `Permit2ApproveFailed()`. revert(0x1c, 0x04) } } } /// @dev Revokes an approval for `token` and `spender` for `address(this)`. function permit2Lockdown(address token, address spender) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) mstore(m, 0xcc53287f) // `Permit2.lockdown`. mstore(add(m, 0x20), 0x20) // Offset of the `approvals`. mstore(add(m, 0x40), 1) // `approvals.length`. mstore(add(m, 0x60), shr(96, shl(96, token))) mstore(add(m, 0x80), shr(96, shl(96, spender))) if iszero(call(gas(), PERMIT2, 0, add(m, 0x1c), 0xa0, codesize(), 0x00)) { mstore(0x00, 0x96b3de23) // `Permit2LockdownFailed()`. revert(0x1c, 0x04) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Contract that enables a single call to call multiple methods on itself. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Multicallable.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Multicallable.sol) /// /// WARNING: /// This implementation is NOT to be used with ERC2771 out-of-the-box. /// https://blog.openzeppelin.com/arbitrary-address-spoofing-vulnerability-erc2771context-multicall-public-disclosure /// This also applies to potentially other ERCs / patterns appending to the back of calldata. /// /// We do NOT have a check for ERC2771, as we do not inherit from OpenZeppelin's context. /// Moreover, it is infeasible and inefficient for us to add checks and mitigations /// for all possible ERC / patterns appending to the back of calldata. /// /// We would highly recommend using an alternative pattern such as /// https://github.com/Vectorized/multicaller /// which is more flexible, futureproof, and safer by default. abstract contract Multicallable { /// @dev Apply `delegatecall` with the current contract to each calldata in `data`, /// and store the `abi.encode` formatted results of each `delegatecall` into `results`. /// If any of the `delegatecall`s reverts, the entire context is reverted, /// and the error is bubbled up. /// /// By default, this function directly returns the results and terminates the call context. /// If you need to add before and after actions to the multicall, please override this function. function multicall(bytes[] calldata data) public payable virtual returns (bytes[] memory) { // Revert if `msg.value` is non-zero by default to guard against double-spending. // (See: https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong) // // If you really need to pass in a `msg.value`, then you will have to // override this function and add in any relevant before and after checks. if (msg.value != 0) revert(); // `_multicallDirectReturn` returns the results directly and terminates the call context. _multicallDirectReturn(_multicall(data)); } /// @dev The inner logic of `multicall`. /// This function is included so that you can override `multicall` /// to add before and after actions, and use the `_multicallDirectReturn` function. function _multicall(bytes[] calldata data) internal virtual returns (bytes32 results) { /// @solidity memory-safe-assembly assembly { results := mload(0x40) mstore(results, 0x20) mstore(add(0x20, results), data.length) let c := add(0x40, results) let s := c let end := shl(5, data.length) calldatacopy(c, data.offset, end) end := add(c, end) let m := end if data.length { for {} 1 {} { let o := add(data.offset, mload(c)) calldatacopy(m, add(o, 0x20), calldataload(o)) // forgefmt: disable-next-item if iszero(delegatecall(gas(), address(), m, calldataload(o), codesize(), 0x00)) { // Bubble up the revert if the delegatecall reverts. returndatacopy(results, 0x00, returndatasize()) revert(results, returndatasize()) } mstore(c, sub(m, s)) c := add(0x20, c) // Append the `returndatasize()`, and the return data. mstore(m, returndatasize()) let b := add(m, 0x20) returndatacopy(b, 0x00, returndatasize()) // Advance `m` by `returndatasize() + 0x20`, // rounded up to the next multiple of 32. m := and(add(add(b, returndatasize()), 0x1f), 0xffffffffffffffe0) mstore(add(b, returndatasize()), 0) // Zeroize the slot after the returndata. if iszero(lt(c, end)) { break } } } mstore(0x40, m) // Allocate memory. results := or(shl(64, sub(m, results)), results) // Pack the bytes length into `results`. } } /// @dev Decodes the `results` into an array of bytes. /// This can be useful if you need to access the results or re-encode it. function _multicallResultsToBytesArray(bytes32 results) internal pure virtual returns (bytes[] memory decoded) { /// @solidity memory-safe-assembly assembly { decoded := mload(0x40) let c := and(0xffffffffffffffff, results) // Extract the offset. mstore(decoded, mload(add(c, 0x20))) // Store the length. let o := add(decoded, 0x20) // Start of elements in `decoded`. let end := add(o, shl(5, mload(decoded))) mstore(0x40, end) // Allocate memory. let s := add(c, 0x40) // Start of elements in `results`. let d := sub(s, o) // Difference between input and output pointers. for {} iszero(eq(o, end)) { o := add(o, 0x20) } { mstore(o, add(mload(add(d, o)), s)) } } } /// @dev Directly returns the `results` and terminates the current call context. /// `results` must be from `_multicall`, else behavior is undefined. function _multicallDirectReturn(bytes32 results) internal pure virtual { /// @solidity memory-safe-assembly assembly { return(and(0xffffffffffffffff, results), shr(64, results)) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol) pragma solidity ^0.8.20; import {Panic} from "../Panic.sol"; import {SafeCast} from "./SafeCast.sol"; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an success flag (no overflow). */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow). */ function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow). */ function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a success flag (no division by zero). */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero). */ function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant. * * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone. * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute * one branch when needed, making this function more expensive. */ function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) { unchecked { // branchless ternary works because: // b ^ (a ^ b) == a // b ^ 0 == b return b ^ ((a ^ b) * SafeCast.toUint(condition)); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return ternary(a > b, a, b); } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return ternary(a < b, a, b); } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. Panic.panic(Panic.DIVISION_BY_ZERO); } // The following calculation ensures accurate ceiling division without overflow. // Since a is non-zero, (a - 1) / b will not overflow. // The largest possible result occurs when (a - 1) / b is type(uint256).max, // but the largest value we can obtain is type(uint256).max - 1, which happens // when a = type(uint256).max and b = 1. unchecked { return SafeCast.toUint(a > 0) * ((a - 1) / b + 1); } } /** * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2²⁵⁶ + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0. if (denominator <= prod1) { Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW)); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv ≡ 1 mod 2⁴. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2⁸ inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶ inverse *= 2 - denominator * inverse; // inverse mod 2³² inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴ inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸ inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶ // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @dev Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0); } /** * @dev Calculate the modular multiplicative inverse of a number in Z/nZ. * * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0. * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible. * * If the input value is not inversible, 0 is returned. * * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}. */ function invMod(uint256 a, uint256 n) internal pure returns (uint256) { unchecked { if (n == 0) return 0; // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version) // Used to compute integers x and y such that: ax + ny = gcd(a, n). // When the gcd is 1, then the inverse of a modulo n exists and it's x. // ax + ny = 1 // ax = 1 + (-y)n // ax ≡ 1 (mod n) # x is the inverse of a modulo n // If the remainder is 0 the gcd is n right away. uint256 remainder = a % n; uint256 gcd = n; // Therefore the initial coefficients are: // ax + ny = gcd(a, n) = n // 0a + 1n = n int256 x = 0; int256 y = 1; while (remainder != 0) { uint256 quotient = gcd / remainder; (gcd, remainder) = ( // The old remainder is the next gcd to try. remainder, // Compute the next remainder. // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd // where gcd is at most n (capped to type(uint256).max) gcd - remainder * quotient ); (x, y) = ( // Increment the coefficient of a. y, // Decrement the coefficient of n. // Can overflow, but the result is casted to uint256 so that the // next value of y is "wrapped around" to a value between 0 and n - 1. x - y * int256(quotient) ); } if (gcd != 1) return 0; // No inverse exists. return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative. } } /** * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`. * * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that * `a**(p-2)` is the modular multiplicative inverse of a in Fp. * * NOTE: this function does NOT check that `p` is a prime greater than `2`. */ function invModPrime(uint256 a, uint256 p) internal view returns (uint256) { unchecked { return Math.modExp(a, p - 2, p); } } /** * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m) * * Requirements: * - modulus can't be zero * - underlying staticcall to precompile must succeed * * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make * sure the chain you're using it on supports the precompiled contract for modular exponentiation * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, * the underlying function will succeed given the lack of a revert, but the result may be incorrectly * interpreted as 0. */ function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) { (bool success, uint256 result) = tryModExp(b, e, m); if (!success) { Panic.panic(Panic.DIVISION_BY_ZERO); } return result; } /** * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m). * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying * to operate modulo 0 or if the underlying precompile reverted. * * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack * of a revert, but the result may be incorrectly interpreted as 0. */ function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) { if (m == 0) return (false, 0); assembly ("memory-safe") { let ptr := mload(0x40) // | Offset | Content | Content (Hex) | // |-----------|------------|--------------------------------------------------------------------| // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 | // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 | // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 | // | 0x60:0x7f | value of b | 0x<.............................................................b> | // | 0x80:0x9f | value of e | 0x<.............................................................e> | // | 0xa0:0xbf | value of m | 0x<.............................................................m> | mstore(ptr, 0x20) mstore(add(ptr, 0x20), 0x20) mstore(add(ptr, 0x40), 0x20) mstore(add(ptr, 0x60), b) mstore(add(ptr, 0x80), e) mstore(add(ptr, 0xa0), m) // Given the result < m, it's guaranteed to fit in 32 bytes, // so we can use the memory scratch space located at offset 0. success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20) result := mload(0x00) } } /** * @dev Variant of {modExp} that supports inputs of arbitrary length. */ function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) { (bool success, bytes memory result) = tryModExp(b, e, m); if (!success) { Panic.panic(Panic.DIVISION_BY_ZERO); } return result; } /** * @dev Variant of {tryModExp} that supports inputs of arbitrary length. */ function tryModExp( bytes memory b, bytes memory e, bytes memory m ) internal view returns (bool success, bytes memory result) { if (_zeroBytes(m)) return (false, new bytes(0)); uint256 mLen = m.length; // Encode call args in result and move the free memory pointer result = abi.encodePacked(b.length, e.length, mLen, b, e, m); assembly ("memory-safe") { let dataPtr := add(result, 0x20) // Write result on top of args to avoid allocating extra memory. success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen) // Overwrite the length. // result.length > returndatasize() is guaranteed because returndatasize() == m.length mstore(result, mLen) // Set the memory pointer after the returned data. mstore(0x40, add(dataPtr, mLen)) } } /** * @dev Returns whether the provided byte array is zero. */ function _zeroBytes(bytes memory byteArray) private pure returns (bool) { for (uint256 i = 0; i < byteArray.length; ++i) { if (byteArray[i] != 0) { return false; } } return true; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * This method is based on Newton's method for computing square roots; the algorithm is restricted to only * using integer operations. */ function sqrt(uint256 a) internal pure returns (uint256) { unchecked { // Take care of easy edge cases when a == 0 or a == 1 if (a <= 1) { return a; } // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between // the current value as `ε_n = | x_n - sqrt(a) |`. // // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is // bigger than any uint256. // // By noticing that // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)` // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar // to the msb function. uint256 aa = a; uint256 xn = 1; if (aa >= (1 << 128)) { aa >>= 128; xn <<= 64; } if (aa >= (1 << 64)) { aa >>= 64; xn <<= 32; } if (aa >= (1 << 32)) { aa >>= 32; xn <<= 16; } if (aa >= (1 << 16)) { aa >>= 16; xn <<= 8; } if (aa >= (1 << 8)) { aa >>= 8; xn <<= 4; } if (aa >= (1 << 4)) { aa >>= 4; xn <<= 2; } if (aa >= (1 << 2)) { xn <<= 1; } // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1). // // We can refine our estimation by noticing that the middle of that interval minimizes the error. // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2). // This is going to be our x_0 (and ε_0) xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2) // From here, Newton's method give us: // x_{n+1} = (x_n + a / x_n) / 2 // // One should note that: // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a // = ((x_n² + a) / (2 * x_n))² - a // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²) // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²) // = (x_n² - a)² / (2 * x_n)² // = ((x_n² - a) / (2 * x_n))² // ≥ 0 // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n // // This gives us the proof of quadratic convergence of the sequence: // ε_{n+1} = | x_{n+1} - sqrt(a) | // = | (x_n + a / x_n) / 2 - sqrt(a) | // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) | // = | (x_n - sqrt(a))² / (2 * x_n) | // = | ε_n² / (2 * x_n) | // = ε_n² / | (2 * x_n) | // // For the first iteration, we have a special case where x_0 is known: // ε_1 = ε_0² / | (2 * x_0) | // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2))) // ≤ 2**(2*e-4) / (3 * 2**(e-1)) // ≤ 2**(e-3) / 3 // ≤ 2**(e-3-log2(3)) // ≤ 2**(e-4.5) // // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n: // ε_{n+1} = ε_n² / | (2 * x_n) | // ≤ (2**(e-k))² / (2 * 2**(e-1)) // ≤ 2**(2*e-2*k) / 2**e // ≤ 2**(e-2*k) xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5 xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9 xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18 xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36 xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72 // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either // sqrt(a) or sqrt(a) + 1. return xn - SafeCast.toUint(xn > a / xn); } } /** * @dev Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 x) internal pure returns (uint256 r) { // If value has upper 128 bits set, log2 result is at least 128 r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7; // If upper 64 bits of 128-bit half set, add 64 to result r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6; // If upper 32 bits of 64-bit half set, add 32 to result r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5; // If upper 16 bits of 32-bit half set, add 16 to result r |= SafeCast.toUint((x >> r) > 0xffff) << 4; // If upper 8 bits of 16-bit half set, add 8 to result r |= SafeCast.toUint((x >> r) > 0xff) << 3; // If upper 4 bits of 8-bit half set, add 4 to result r |= SafeCast.toUint((x >> r) > 0xf) << 2; // Shifts value right by the current result and use it as an index into this lookup table: // // | x (4 bits) | index | table[index] = MSB position | // |------------|---------|-----------------------------| // | 0000 | 0 | table[0] = 0 | // | 0001 | 1 | table[1] = 0 | // | 0010 | 2 | table[2] = 1 | // | 0011 | 3 | table[3] = 1 | // | 0100 | 4 | table[4] = 2 | // | 0101 | 5 | table[5] = 2 | // | 0110 | 6 | table[6] = 2 | // | 0111 | 7 | table[7] = 2 | // | 1000 | 8 | table[8] = 3 | // | 1001 | 9 | table[9] = 3 | // | 1010 | 10 | table[10] = 3 | // | 1011 | 11 | table[11] = 3 | // | 1100 | 12 | table[12] = 3 | // | 1101 | 13 | table[13] = 3 | // | 1110 | 14 | table[14] = 3 | // | 1111 | 15 | table[15] = 3 | // // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes. assembly ("memory-safe") { r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000)) } } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 x) internal pure returns (uint256 r) { // If value has upper 128 bits set, log2 result is at least 128 r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7; // If upper 64 bits of 128-bit half set, add 64 to result r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6; // If upper 32 bits of 64-bit half set, add 32 to result r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5; // If upper 16 bits of 32-bit half set, add 16 to result r |= SafeCast.toUint((x >> r) > 0xffff) << 4; // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8 return (r >> 3) | SafeCast.toUint((x >> r) > 0xff); } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {Ownable} from "lib/solady/src/auth/Ownable.sol"; interface IERC20Burnable { function burn(uint256 amount) external; function burnFrom(address account, uint256 amount) external; } /// @notice Struct representing a user's loan /// @param collateral Amount of BREAD tokens staked as collateral /// @param borrowed Amount of BERA borrowed against the collateral /// @param endDate Timestamp when the loan expires /// @param numberOfDays Duration of the loan in days struct Loan { uint256 collateral; // shares of token staked uint256 borrowed; // user reward per token paid uint256 endDate; uint256 numberOfDays; uint256 lastTimeCreated; } /** * @title IBread * @dev Interface for the Bread contract */ interface IBread is IERC20, IERC20Burnable { // Events event PriceUpdated(uint256 time, uint256 price, uint256 volumeInBera); event MaxUpdated(uint256 max); event SellFeeUpdated(uint256 sellFee); event BreadTreasuryUpdated(address _address); event BuyFeeUpdated(uint256 buyFee); event LeverageFeeUpdated(uint256 leverageFee); event Started(bool started); event Toast(uint256 time, uint256 amount); event LoanDataUpdate(uint256 collateralByDate, uint256 borrowedByDate, uint256 totalBorrowed, uint256 totalCollateral); event SendBera(address to, uint256 amount); // Constants function DUST() external view returns(uint256); function COLLATERAL_RATIO() external view returns(uint256); function BPS_DENOMINATOR() external view returns(uint256); function PROTOCOL_FEE_SHARE_BPS() external view returns(uint256); function INTEREST_APR_1e18() external view returns(uint256); function ORIGINATION_FEE_1e18() external view returns(uint256); // State Variables function breadTreasury() external view returns (address payable); function burnFeeBps() external view returns (uint256); function bakeFeeBps() external view returns (uint256); function leverageFeeBps() external view returns (uint256); function started() external view returns (bool); function whitelist() external view returns (bool); function maxSupply() external view returns (uint256); function totalBaked() external view returns (uint256); function lastPrice() external view returns (uint256); function activeLoans(address user) external view returns (uint256, uint256, uint256, uint256, uint256); function loansByDate(uint256 date) external view returns (uint256); function collateralByDate(uint256 date) external view returns (uint256); function lastToastDate() external view returns (uint256); function tokenLockerFeeBps() external view returns (uint256); function totalLockedTokens(address token) external view returns (uint256); function lockedTokens(address user, address token) external view returns (uint256, uint256); // Owner settings function getWhitelistAllowance(address _address) external view returns (uint256); function setStart() external payable; function setBreadTreasury(address _address) external; function setBakeFee(uint256 amount) external; function setLeverageFee(uint256 amount) external; function setBurnFee(uint256 amount) external; function increaseMaxSupply(uint256 amount) external; function setTokenLockerFee(uint256 amount) external; function setBribeBounty(uint256 amount) external; // External functions function buy(address receiver) external payable; function bake(address receiver) external payable; function burn(uint256 bread) external; function loop(uint256 bera, uint256 numberOfDays) external payable; function borrow(uint256 bera, uint256 numberOfDays) external; function increaseBorrow(uint256 bera) external; function removeCollateral(uint256 amount) external; function repay() external payable; function closePosition() external payable; function flashBurn() external; function extendLoan(uint256 numberOfDays) external payable returns (uint256); function toast() external; function lockTokens(address token, uint256 amount, uint256 unlockTime) external; function unlockTokens(address token) external; function claimBribeBounty(address[] calldata tokens) external payable; // Utility functions function getDayStart(uint256 date) external pure returns (uint256); function getLoansExpiringByDate(uint256 date) external view returns (uint256, uint256); function getLoanByAddress(address _address) external view returns (uint256, uint256, uint256); function leverageFee(uint256 bera, uint256 numberOfDays) external view returns (uint256); function getInterestFee(uint256 amount, uint256 numberOfDays) external view returns (uint256); function isLoanExpired(address _address) external view returns (bool); function getBacking() external view returns (uint256); function BREADtoBERAFloor(uint256 value) external view returns (uint256); function BERAtoBREADFloor(uint256 value) external view returns (uint256); function BERAtoBREADLev(uint256 value, uint256 fee) external view returns (uint256); function BERAtoBREADNoTradeCeil(uint256 value) external view returns (uint256); function BERAtoBREADNoTradeFloor(uint256 value) external view returns (uint256); function getAmountOutBuy(uint256 beraAmount) external view returns (uint256); function getAmountOutSell(uint256 breadAmount) external view returns (uint256); function totalLoans() external view returns (uint256); function totalCollateral() external view returns (uint256); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol) pragma solidity ^0.8.20; /** * @dev Helper library for emitting standardized panic codes. * * ```solidity * contract Example { * using Panic for uint256; * * // Use any of the declared internal constants * function foo() { Panic.GENERIC.panic(); } * * // Alternatively * function foo() { Panic.panic(Panic.GENERIC); } * } * ``` * * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil]. * * _Available since v5.1._ */ // slither-disable-next-line unused-state library Panic { /// @dev generic / unspecified error uint256 internal constant GENERIC = 0x00; /// @dev used by the assert() builtin uint256 internal constant ASSERT = 0x01; /// @dev arithmetic underflow or overflow uint256 internal constant UNDER_OVERFLOW = 0x11; /// @dev division or modulo by zero uint256 internal constant DIVISION_BY_ZERO = 0x12; /// @dev enum conversion error uint256 internal constant ENUM_CONVERSION_ERROR = 0x21; /// @dev invalid encoding in storage uint256 internal constant STORAGE_ENCODING_ERROR = 0x22; /// @dev empty array pop uint256 internal constant EMPTY_ARRAY_POP = 0x31; /// @dev array out of bounds access uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32; /// @dev resource error (too large allocation or too large array) uint256 internal constant RESOURCE_ERROR = 0x41; /// @dev calling invalid internal function uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51; /// @dev Reverts with a panic code. Recommended to use with /// the internal constants with predefined codes. function panic(uint256 code) internal pure { assembly ("memory-safe") { mstore(0x00, 0x4e487b71) mstore(0x20, code) revert(0x1c, 0x24) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.20; /** * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeCast { /** * @dev Value doesn't fit in an uint of `bits` size. */ error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value); /** * @dev An int value doesn't fit in an uint of `bits` size. */ error SafeCastOverflowedIntToUint(int256 value); /** * @dev Value doesn't fit in an int of `bits` size. */ error SafeCastOverflowedIntDowncast(uint8 bits, int256 value); /** * @dev An uint value doesn't fit in an int of `bits` size. */ error SafeCastOverflowedUintToInt(uint256 value); /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits */ function toUint248(uint256 value) internal pure returns (uint248) { if (value > type(uint248).max) { revert SafeCastOverflowedUintDowncast(248, value); } return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits */ function toUint240(uint256 value) internal pure returns (uint240) { if (value > type(uint240).max) { revert SafeCastOverflowedUintDowncast(240, value); } return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits */ function toUint232(uint256 value) internal pure returns (uint232) { if (value > type(uint232).max) { revert SafeCastOverflowedUintDowncast(232, value); } return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits */ function toUint224(uint256 value) internal pure returns (uint224) { if (value > type(uint224).max) { revert SafeCastOverflowedUintDowncast(224, value); } return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits */ function toUint216(uint256 value) internal pure returns (uint216) { if (value > type(uint216).max) { revert SafeCastOverflowedUintDowncast(216, value); } return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits */ function toUint208(uint256 value) internal pure returns (uint208) { if (value > type(uint208).max) { revert SafeCastOverflowedUintDowncast(208, value); } return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits */ function toUint200(uint256 value) internal pure returns (uint200) { if (value > type(uint200).max) { revert SafeCastOverflowedUintDowncast(200, value); } return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits */ function toUint192(uint256 value) internal pure returns (uint192) { if (value > type(uint192).max) { revert SafeCastOverflowedUintDowncast(192, value); } return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits */ function toUint184(uint256 value) internal pure returns (uint184) { if (value > type(uint184).max) { revert SafeCastOverflowedUintDowncast(184, value); } return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits */ function toUint176(uint256 value) internal pure returns (uint176) { if (value > type(uint176).max) { revert SafeCastOverflowedUintDowncast(176, value); } return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits */ function toUint168(uint256 value) internal pure returns (uint168) { if (value > type(uint168).max) { revert SafeCastOverflowedUintDowncast(168, value); } return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits */ function toUint160(uint256 value) internal pure returns (uint160) { if (value > type(uint160).max) { revert SafeCastOverflowedUintDowncast(160, value); } return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits */ function toUint152(uint256 value) internal pure returns (uint152) { if (value > type(uint152).max) { revert SafeCastOverflowedUintDowncast(152, value); } return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits */ function toUint144(uint256 value) internal pure returns (uint144) { if (value > type(uint144).max) { revert SafeCastOverflowedUintDowncast(144, value); } return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits */ function toUint136(uint256 value) internal pure returns (uint136) { if (value > type(uint136).max) { revert SafeCastOverflowedUintDowncast(136, value); } return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { if (value > type(uint128).max) { revert SafeCastOverflowedUintDowncast(128, value); } return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits */ function toUint120(uint256 value) internal pure returns (uint120) { if (value > type(uint120).max) { revert SafeCastOverflowedUintDowncast(120, value); } return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits */ function toUint112(uint256 value) internal pure returns (uint112) { if (value > type(uint112).max) { revert SafeCastOverflowedUintDowncast(112, value); } return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits */ function toUint104(uint256 value) internal pure returns (uint104) { if (value > type(uint104).max) { revert SafeCastOverflowedUintDowncast(104, value); } return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits */ function toUint96(uint256 value) internal pure returns (uint96) { if (value > type(uint96).max) { revert SafeCastOverflowedUintDowncast(96, value); } return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits */ function toUint88(uint256 value) internal pure returns (uint88) { if (value > type(uint88).max) { revert SafeCastOverflowedUintDowncast(88, value); } return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits */ function toUint80(uint256 value) internal pure returns (uint80) { if (value > type(uint80).max) { revert SafeCastOverflowedUintDowncast(80, value); } return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits */ function toUint72(uint256 value) internal pure returns (uint72) { if (value > type(uint72).max) { revert SafeCastOverflowedUintDowncast(72, value); } return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { if (value > type(uint64).max) { revert SafeCastOverflowedUintDowncast(64, value); } return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits */ function toUint56(uint256 value) internal pure returns (uint56) { if (value > type(uint56).max) { revert SafeCastOverflowedUintDowncast(56, value); } return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits */ function toUint48(uint256 value) internal pure returns (uint48) { if (value > type(uint48).max) { revert SafeCastOverflowedUintDowncast(48, value); } return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits */ function toUint40(uint256 value) internal pure returns (uint40) { if (value > type(uint40).max) { revert SafeCastOverflowedUintDowncast(40, value); } return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { if (value > type(uint32).max) { revert SafeCastOverflowedUintDowncast(32, value); } return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits */ function toUint24(uint256 value) internal pure returns (uint24) { if (value > type(uint24).max) { revert SafeCastOverflowedUintDowncast(24, value); } return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { if (value > type(uint16).max) { revert SafeCastOverflowedUintDowncast(16, value); } return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits */ function toUint8(uint256 value) internal pure returns (uint8) { if (value > type(uint8).max) { revert SafeCastOverflowedUintDowncast(8, value); } return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { if (value < 0) { revert SafeCastOverflowedIntToUint(value); } return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(248, value); } } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(240, value); } } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(232, value); } } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(224, value); } } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(216, value); } } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(208, value); } } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(200, value); } } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(192, value); } } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(184, value); } } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(176, value); } } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(168, value); } } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(160, value); } } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(152, value); } } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(144, value); } } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(136, value); } } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(128, value); } } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(120, value); } } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(112, value); } } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(104, value); } } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(96, value); } } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(88, value); } } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(80, value); } } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(72, value); } } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(64, value); } } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(56, value); } } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(48, value); } } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(40, value); } } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(32, value); } } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(24, value); } } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(16, value); } } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(8, value); } } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive if (value > uint256(type(int256).max)) { revert SafeCastOverflowedUintToInt(value); } return int256(value); } /** * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump. */ function toUint(bool b) internal pure returns (uint256 u) { assembly ("memory-safe") { u := iszero(iszero(b)) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ 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 value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` 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 value) external returns (bool); }
{ "remappings": [ "forge-std/=lib/forge-std/src/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/" ], "optimizer": { "enabled": true, "runs": 100 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "shanghai", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AllowanceOverflow","type":"error"},{"inputs":[],"name":"AllowanceUnderflow","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidPermit","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Permit2AllowanceIsFixedAtInfinity","type":"error"},{"inputs":[],"name":"PermitExpired","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"inputs":[],"name":"Unauthorized","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":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bread","type":"uint256"}],"name":"Bake","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"bakeFee","type":"uint256"}],"name":"BakeFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"baker","type":"address"}],"name":"BakerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"bera","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"numberOfDays","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"userBread","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newUserBorrow","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"BorrowingEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"uint256","name":"bounty","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"BountyCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"BreadGTEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BreadGTMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"BreadTreasuryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BribeBountyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"seller","type":"address"},{"indexed":false,"internalType":"uint256","name":"bera","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bread","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"sellFee","type":"uint256"}],"name":"BurnFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toUser","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FlashBurn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"leverageFee","type":"uint256"}],"name":"LeverageFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"collateralByDate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowedByDate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrowed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalCollateral","type":"uint256"}],"name":"LoanDataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"numberOfDays","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LoanExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"bera","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"numberOfDays","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"userBread","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"userBorrow","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"Loop","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"max","type":"uint256"}],"name":"MaxBakeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"polFee","type":"uint256"}],"name":"PolFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"time","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"volumeInBera","type":"uint256"}],"name":"PriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RemoveCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBorrow","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SendBera","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"breadAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"iBreadAmount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"StakingEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"started","type":"bool"}],"name":"Started","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"time","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Toast","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"lockAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unlockTime","type":"uint256"}],"name":"TokenLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokenLockerFee","type":"uint256"}],"name":"TokenLockerFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenUnlocked","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"iBreadAmount","type":"uint256"}],"name":"UnstakeRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"breadAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"iBreadAmount","type":"uint256"}],"name":"Unstaked","type":"event"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"BERAtoBREADFloor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"totalBeraRequired","type":"uint256"}],"name":"BERAtoBREADLev","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"BERAtoBREADNoTradeCeil","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"BERAtoBREADNoTradeFloor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BGT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BPS_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"BREADtoBERAFloor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COLLATERAL_RATIO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEAD_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"result","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DUST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INTEREST_APR_BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROTOCOL_FEE_SHARE_BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"activeLoans","outputs":[{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"borrowed","type":"uint256"},{"internalType":"uint256","name":"endDate","type":"uint256"},{"internalType":"uint256","name":"numberOfDays","type":"uint256"},{"internalType":"uint256","name":"lastTimeCreated","type":"uint256"}],"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":"receiver","type":"address"}],"name":"bake","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"bakeFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baker","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bera","type":"uint256"},{"internalType":"uint256","name":"numberOfDays","type":"uint256"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowingEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"breadGT","outputs":[{"internalType":"contract BreadGT","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"breadGTEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"breadTreasury","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bribeBounty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bread","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"burnFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"buy","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"rewardVault","type":"address"}],"name":"claimBreadGT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"claimBribeBounty","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"closePosition","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"collateralByDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"enableBorrowing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"enableBreadGT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"enableStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numberOfDays","type":"uint256"}],"name":"extendLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"flashBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"beraAmount","type":"uint256"}],"name":"getAmountOutBuy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"breadAmount","type":"uint256"}],"name":"getAmountOutSell","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBacking","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"date","type":"uint256"}],"name":"getDayStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getFreeCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"borrowed","type":"uint256"},{"internalType":"uint256","name":"numberOfDays","type":"uint256"}],"name":"getInterestFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"getLoanByAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"date","type":"uint256"}],"name":"getLoansExpiringByDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_numberOfDays","type":"uint256"}],"name":"getMaxBorrow","outputs":[{"internalType":"uint256","name":"userBera","type":"uint256"},{"internalType":"uint256","name":"userBorrow","type":"uint256"},{"internalType":"uint256","name":"interestFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bera","type":"uint256"}],"name":"increaseBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxBake","type":"uint256"}],"name":"increaseMaxSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"totalBeraRequired","type":"uint256"},{"internalType":"uint256","name":"numberOfDays","type":"uint256"}],"name":"inverseLoopCalc","outputs":[{"internalType":"uint256","name":"bera","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"isLoanExpired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastToastDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"leverageFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"loansByDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"unlockTime","type":"uint256"}],"name":"lockTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"lockedTokens","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"unlockTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bera","type":"uint256"},{"internalType":"uint256","name":"numberOfDays","type":"uint256"}],"name":"loop","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"bera","type":"uint256"},{"internalType":"uint256","name":"numberOfDays","type":"uint256"}],"name":"loopCalcs","outputs":[{"internalType":"uint256","name":"bakeFee","type":"uint256"},{"internalType":"uint256","name":"userBorrow","type":"uint256"},{"internalType":"uint256","name":"overCollateralizationAmount","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxBake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"payable","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":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"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":[],"name":"polFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"prevPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"removeCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"repay","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"iBreadAmount","type":"uint256"}],"name":"requestUnstakeBread","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setBakeFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_baker","type":"address"}],"name":"setBaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setBreadTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setBribeBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setBurnFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setIBreadMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setLeverageFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_polFeeBps","type":"uint256"}],"name":"setPolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setStart","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenLockerFeeBps","type":"uint256"}],"name":"setTokenLockerFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"breadAmount","type":"uint256"}],"name":"stakeBread","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakedBread","outputs":[{"internalType":"contract iBread","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"started","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"toast","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenLockerFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLoans","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalLockedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakedBread","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"unlockTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"iBreadAmount","type":"uint256"}],"name":"unstakeBread","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unstakeRequestAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"unstakeRequestTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60806040526103e85f5569021e19e0c9bab2400000600155606460025561010d600b5561010d600c55608e600d55348015610038575f5ffd5b50610042336100f7565b61004b42610132565b601755600a80546001600160a01b0319163317905560405161006c90610161565b604051809103905ff080158015610085573d5f5f3e3d5ffd5b50600580546001600160a01b0319166001600160a01b03929092169190911790556040516100b29061016e565b604051809103905ff0801580156100cb573d5f5f3e3d5ffd5b50600960016101000a8154816001600160a01b0302191690836001600160a01b031602179055506101da565b6001600160a01b0316638b78c6d819819055805f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b5f80610141620151808461017b565b61014b90846101ae565b905061015a81620151806101c7565b9392505050565b610c278061614e83390190565b610a1280616d7583390190565b5f8261019557634e487b7160e01b5f52601260045260245ffd5b500690565b634e487b7160e01b5f52601160045260245ffd5b818103818111156101c1576101c161019a565b92915050565b808201808211156101c1576101c161019a565b615f67806101e75f395ff3fe608060405260043610610584575f3560e01c806390b6bfc9116102d0578063baeff8a311610181578063d9e0da64116100df578063f04e283e1161008e578063f04e283e1461112e578063f088d54714610bed578063f2fde38b14611141578063f361ed6614611154578063f940c26814611173578063fab7c40914611192578063fee81cf4146111b1575f5ffd5b8063d9e0da6414611049578063d9e69a0514611088578063dd62ed3e1461109d578063e1a45218146110bc578063e753dcdf146110d1578063e783f8af146110f0578063efaf69681461110f575f5ffd5b8063cb67f9481161013b578063cb67f94814610f7a578063cc591db814610f99578063cc952b5714610fb8578063d505accf14610fcd578063d56413ba14610fec578063d65e608d1461100b578063d9b687ad1461102a575f5ffd5b8063baeff8a314610eed578063bff9d78c14610f0c578063c2b941a814610f20578063c393d0e314610f3f578063c399944414610f47578063c94220ab14610f66575f5ffd5b8063a49414ae1161022e578063ac9650d8116101e8578063ac9650d814610de1578063acc7268114610e01578063ae4b77a914610e16578063b1b3b7fb14610e35578063b2a6773514610e60578063ba64983914610e75578063bae6d50614610ece575f5ffd5b8063a49414ae14610d39578063a494f29014610d4e578063a518981014610d63578063a599efd214610d78578063a797385614610da3578063a9059cbb14610dc2575f5ffd5b80639c381b011161028a5780639c381b0114610c6b5780639cefc1da14610c7e5780639e8c708e14610c9d578063a1d0f26c14610cbc578063a25983e514610cdb578063a35d130014610cfa578063a48a9cd314610d1a575f5ffd5b806390b6bfc914610ba357806390edbfee14610bce57806394d5d8e814610bed57806395ced06f14610c0057806395d89b4114610c1f578063997b226914610c4c575f5ffd5b8063402d8883116104355780636271b2e811610393578063715018a611610342578063715018a614610ac95780637896034314610ad15780637ace2ac914610b0b5780637ecebe0014610b1e57806389bba14214610b4f5780638da5cb5b14610b64578063905d907514610b7c575f5ffd5b80636271b2e8146109e5578063647fbcfd146109fa57806369c4916514610a0f5780636b2e6cde14610a2e5780636eabc43214610a4e57806370a0823114610a7957806370f84ba914610aaa575f5ffd5b80634bf2c7c9116103ef5780634bf2c7c91461094c5780634e0cd7991461096b5780634e6fd6c41461098057806350badcb514610995578063548e762f146109aa57806354d1f13d146109c95780635751b361146109d1575f5ffd5b8063402d8883146108bd57806342966c68146108c55780634508ba33146108e457806346051f341461090357806349717a72146109185780634ac8eb5f14610937575f5ffd5b806318160ddd116104e2578063256929621161049c5780632569296214610821578063313ce567146108295780633237c1581461084457806335975a37146108635780633644e5151461086b5780633890b1521461087f578063402b7423146108aa575f5ffd5b806318160ddd146107795780631cfff51b146107955780631e62cc44146107ae5780631f2698ab146107c357806323b872dd146107e35780632567f73114610802575f5ffd5b8063081ce0381161053e578063081ce038146106a0578063095ea7b3146106bf5780630a67ba65146106ee5780630b2b2915146107025780630ecbcdab1461072657806312686f511461074557806317e79db01461075a575f5ffd5b8063024cad3b1461058f578063035b7c4b146105c8578063050ac5fb146105f557806305b7c1c91461061657806306fdde03146106425780630774e00314610663575f5ffd5b3661058b57005b5f5ffd5b34801561059a575f5ffd5b506105ae6105a9366004615880565b6111e2565b604080519283526020830191909152015b60405180910390f35b3480156105d3575f5ffd5b506105e76105e2366004615897565b611221565b6040519081526020016105bf565b348015610600575f5ffd5b5061061461060f366004615880565b61130d565b005b348015610621575f5ffd5b50600e54610635906001600160a01b031681565b6040516105bf91906158b7565b34801561064d575f5ffd5b5061065661149f565b6040516105bf919061590e565b34801561066e575f5ffd5b506105ae61067d36600461593b565b600360209081525f92835260408084209091529082529020805460019091015482565b3480156106ab575f5ffd5b506105e76106ba366004615880565b6114be565b3480156106ca575f5ffd5b506106de6106d936600461596c565b6114ed565b60405190151581526020016105bf565b3480156106f9575f5ffd5b506105e75f5481565b34801561070d575f5ffd5b506009546106359061010090046001600160a01b031681565b348015610731575f5ffd5b50610614610740366004615897565b61156c565b348015610750575f5ffd5b506105e760175481565b348015610765575f5ffd5b506105e7610774366004615880565b611836565b348015610784575f5ffd5b506805345cdf77eb68f44c546105e7565b3480156107a0575f5ffd5b506009546106de9060ff1681565b3480156107b9575f5ffd5b506105e760065481565b3480156107ce575f5ffd5b50600e546106de90600160a01b900460ff1681565b3480156107ee575f5ffd5b506106de6107fd366004615994565b61186a565b34801561080d575f5ffd5b5061061461081c366004615880565b611926565b6106146119c9565b348015610834575f5ffd5b50604051601281526020016105bf565b34801561084f575f5ffd5b5061061461085e366004615880565b611a16565b610614611bb6565b348015610876575f5ffd5b506105e7611d29565b34801561088a575f5ffd5b506105e76108993660046159ce565b60076020525f908152604090205481565b6106146108b8366004615897565b611da5565b61061461215d565b3480156108d0575f5ffd5b506106146108df366004615880565b612334565b3480156108ef575f5ffd5b506106146108fe3660046159f6565b612473565b34801561090e575f5ffd5b506105e760135481565b348015610923575f5ffd5b506105e7610932366004615880565b6124c8565b348015610942575f5ffd5b506105e760105481565b348015610957575f5ffd5b50610614610966366004615880565b6124e8565b348015610976575f5ffd5b506105e76103e881565b34801561098b575f5ffd5b5061063561dead81565b3480156109a0575f5ffd5b506105e7600d5481565b3480156109b5575f5ffd5b506106146109c4366004615880565b6125c8565b6106146129d8565b3480156109dc575f5ffd5b50610614612a11565b3480156109f0575f5ffd5b506105e760015481565b348015610a05575f5ffd5b506105e760125481565b348015610a1a575f5ffd5b50600554610635906001600160a01b031681565b348015610a39575f5ffd5b506009546106de90600160a81b900460ff1681565b348015610a59575f5ffd5b506105e7610a68366004615880565b60156020525f908152604090205481565b348015610a84575f5ffd5b506105e7610a933660046159ce565b6387a211a2600c9081525f91909152602090205490565b348015610ab5575f5ffd5b506106de610ac43660046159ce565b612c62565b610614612c81565b348015610adc575f5ffd5b50610af0610aeb36600461596c565b612c94565b604080519384526020840192909252908201526060016105bf565b6105e7610b19366004615880565b612cfa565b348015610b29575f5ffd5b506105e7610b383660046159ce565b6338377508600c9081525f91909152602090205490565b348015610b5a575f5ffd5b506105e7610dac81565b348015610b6f575f5ffd5b50638b78c6d81954610635565b348015610b87575f5ffd5b5061063573656b95e550c07a9ffe548bd4085c72418ceb1dba81565b348015610bae575f5ffd5b506105e7610bbd366004615880565b60166020525f908152604090205481565b348015610bd9575f5ffd5b50610614610be8366004615880565b612f83565b610614610bfb3660046159ce565b613053565b348015610c0b575f5ffd5b50610af0610c1a3660046159ce565b613095565b348015610c2a575f5ffd5b50604080518082019091526005815264109491505160da1b6020820152610656565b348015610c57575f5ffd5b506105e7610c66366004615880565b6130f9565b610614610c79366004615a57565b613122565b348015610c89575f5ffd5b50610614610c983660046159f6565b613412565b348015610ca8575f5ffd5b50610614610cb73660046159ce565b61345b565b348015610cc7575f5ffd5b506105e7610cd6366004615897565b613500565b348015610ce6575f5ffd5b50610614610cf5366004615a96565b6135a9565b348015610d05575f5ffd5b506009546106de90600160b01b900460ff1681565b348015610d25575f5ffd5b50600a54610635906001600160a01b031681565b348015610d44575f5ffd5b506105e76102b281565b348015610d59575f5ffd5b506105e760025481565b348015610d6e575f5ffd5b506105e7600c5481565b348015610d83575f5ffd5b506105e7610d923660046159ce565b60086020525f908152604090205481565b348015610dae575f5ffd5b50610614610dbd366004615880565b61382b565b348015610dcd575f5ffd5b506106de610ddc36600461596c565b613b12565b610df4610def366004615a57565b613b76565b6040516105bf9190615ac6565b348015610e0c575f5ffd5b506105e7600b5481565b348015610e21575f5ffd5b50610614610e30366004615880565b613b94565b348015610e40575f5ffd5b506105e7610e4f3660046159ce565b60046020525f908152604090205481565b348015610e6b575f5ffd5b506105e760115481565b348015610e80575f5ffd5b50610ebd610e8f3660046159ce565b60146020525f9081526040902080546001820154600283015460038401546004909401549293919290919085565b6040516105bf959493929190615b29565b348015610ed9575f5ffd5b506105e7610ee8366004615880565b613c3e565b348015610ef8575f5ffd5b50610614610f07366004615880565b613c5b565b348015610f17575f5ffd5b50610614613da0565b348015610f2b575f5ffd5b50610614610f3a366004615880565b613e82565b610614613ebf565b348015610f52575f5ffd5b506105e7610f613660046159ce565b61401e565b348015610f71575f5ffd5b506105e7614084565b348015610f85575f5ffd5b50610614610f943660046159ce565b614098565b348015610fa4575f5ffd5b506105e7610fb3366004615880565b614222565b348015610fc3575f5ffd5b506105e7600f5481565b348015610fd8575f5ffd5b50610614610fe7366004615b4c565b61424a565b348015610ff7575f5ffd5b506105e7611006366004615897565b6143fe565b348015611016575f5ffd5b50610614611025366004615880565b614432565b348015611035575f5ffd5b506106146110443660046159ce565b614693565b348015611054575f5ffd5b50611068611063366004615897565b6149de565b6040805194855260208501939093529183015260608201526080016105bf565b348015611093575f5ffd5b506105e76126ac81565b3480156110a8575f5ffd5b506105e76110b736600461593b565b614a49565b3480156110c7575f5ffd5b506105e761271081565b3480156110dc575f5ffd5b506105e76110eb366004615880565b614a8d565b3480156110fb575f5ffd5b5061061461110a3660046159f6565b614ab6565b34801561111a575f5ffd5b506106146111293660046159ce565b614b0b565b61061461113c3660046159ce565b614bbf565b61061461114f3660046159ce565b614bf9565b34801561115f575f5ffd5b5061061461116e3660046159ce565b614c1f565b34801561117e575f5ffd5b5061061461118d366004615bb9565b614c72565b34801561119d575f5ffd5b506106146111ac366004615880565b614ce0565b3480156111bc575f5ffd5b506105e76111cb3660046159ce565b63389a75e1600c9081525f91909152602090205490565b5f5f60155f6111f085614222565b81526020019081526020015f205460165f61120a86614222565b81526020019081526020015f205491509150915091565b5f80670de0b6b3a764000061271061016d8561123f6102b289615bf5565b6112499190615bf5565b61125b90670de0b6b3a7640000615bf5565b6112659190615c20565b61126f9190615c20565b6112799190615c20565b90505f6126ac61128b81612710615c33565b6112959087615bf5565b61129f9190615c20565b90505f612710600c5483886112b49190615c46565b6112be9190615bf5565b6112c89190615c20565b90508181116112dc57829350505050611307565b6112e68282615c33565b83106112f757829350505050611307565b6113018282615c33565b93505050505b92915050565b3068929eee149b4bd21268540361132b5763ab143c065f526004601cfd5b3068929eee149b4bd21268555f811161138b5760405162461bcd60e51b815260206004820181905260248201527f4d757374207265717565737420756e7374616b65206d6f7265207468616e203060448201526064015b60405180910390fd5b6005546040516370a0823160e01b81526001600160a01b03909116906370a08231906113bb9033906004016158b7565b602060405180830381865afa1580156113d6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113fa9190615c59565b8111156114415760405162461bcd60e51b8152602060048201526015602482015274125b9d985b1a59081a509491505108185b5bdd5b9d605a1b6044820152606401611382565b335f818152600760209081526040808320429055600882529182902084905590518381527f828764c21e74c28710e19919735825aba966621c95cbd913f8ed65a2d298f48c910160405180910390a23868929eee149b4bd212685550565b604080518082019091526005815264109c99585960da1b602082015290565b5f5f6114c8614084565b90506114e6836114df6805345cdf77eb68f44c5490565b835f614e3c565b9392505050565b5f6001600160a01b0383166e22d473030f116ddee9f6b43ac78ba3188219151761151e57633f68539a5f526004601cfd5b82602052637f5e9f20600c52335f52816034600c2055815f52602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa350600192915050565b3068929eee149b4bd21268540361158a5763ab143c065f526004601cfd5b3068929eee149b4bd2126855600954600160b01b900460ff166115bf5760405162461bcd60e51b815260040161138290615c70565b61016e81106115e05760405162461bcd60e51b815260040161138290615c9f565b815f036115ff5760405162461bcd60e51b815260040161138290615cec565b61160833612c62565b1561163857335f908152601460205260408120818155600181018290556002810182905560038101829055600401555b335f90815260146020526040902060010154156116a15760405162461bcd60e51b815260206004820152602160248201527f55736520696e637265617365426f72726f7720746f20626f72726f77206d6f726044820152606560f81b6064820152608401611382565b6116a9613da0565b5f6116c5426116bb8462015180615bf5565b610fb39190615c46565b90505f6127106116d76126ac86615bf5565b6116e19190615c20565b90505f6116ee8285611221565b90505f612710611700610dac84615bf5565b61170a9190615c20565b90505f611716876130f9565b6040805160a08101825282815260208082018881528284018a8152606084018c81524260808601908152335f81815260149096529690942094518555915160018501555160028401555160038301555160049091015590915061177a903083614e7e565b6103e8821161179b5760405162461bcd60e51b815260040161138290615d1d565b6117ae336117a98587615c33565b614ee4565b600a546117c4906001600160a01b031683614ee4565b6117cf848287614f84565b6117d883615058565b336001600160a01b03167f212a25b7e6d62ca82a704bf5f0513e2d0840e89914c62639dceabfb40a34491a8888848888604051611819959493929190615b29565b60405180910390a250505050503868929eee149b4bd21268555050565b5f5f61184183613c3e565b9050612710600c546127106118569190615c33565b6118609083615bf5565b6114e69190615c20565b5f8360601b6e22d473030f116ddee9f6b43ac78ba333146118bf5733602052637f5e9f208117600c526034600c2080548019156118bc57808511156118b6576313be252b5f526004601cfd5b84810382555b50505b6387a211a28117600c526020600c208054808511156118e55763f4d678b85f526004601cfd5b84810382555050835f526020600c208381540181555082602052600c5160601c8160601c5f516020615f125f395f51905f52602080a3505060019392505050565b61192e6151e0565b610dac81111561198e5760405162461bcd60e51b815260206004820152602560248201527f504f4c20666565206d757374206265206c657373207468616e206f766572616c6044820152646c2066656560d81b6064820152608401611382565b5f8190556040518181527fe0c2437c53b6eb36e7e7380f81445b2ad7834b91623057dbb49b620ef11a6b64906020015b60405180910390a150565b5f6202a30067ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f5fa250565b3068929eee149b4bd212685403611a345763ab143c065f526004601cfd5b3068929eee149b4bd2126855611a4933612c62565b15611a665760405162461bcd60e51b815260040161138290615d54565b611a6e613da0565b335f9081526014602052604081205490611a8b610ee88484615c33565b9050612710611a9c6126ac83615bf5565b611aa69190615c20565b335f908152601460205260409020600101541115611b115760405162461bcd60e51b815260206004820152602260248201527f526571756972652039392520636f6c6c61746572616c697a6174696f6e207261604482015261746560f01b6064820152608401611382565b335f90815260146020526040902054611b2b908490615c33565b335f81815260146020526040902091909155611b4990309085614e7e565b335f90815260146020526040812060020154611b67919085906151fa565b611b705f615058565b60405183815233907fe25410a4059619c9594dc6f022fe231b02aaea733f689e7ab0cd21b3d4d0eb549060200160405180910390a250503868929eee149b4bd212685550565b611bbe6151e0565b600e54600160a01b900460ff16158015611bd85750601154155b611c245760405162461bcd60e51b815260206004820152601b60248201527f54726164696e6720616c726561647920696e697469616c697a656400000000006044820152606401611382565b346901760cbc623bb350000014611c895760405162461bcd60e51b8152602060048201526024808201527f4d7573742073656e642036393030204245524120746f2073746172742074726160448201526364696e6760e01b6064820152608401611382565b3460118190556040518181525f516020615ef25f395f51905f529060200160405180910390a1611cb93382615265565b611cce3361dead67016345785d8a0000614e7e565b600e805460ff60a01b1916600160a01b1790556009805460ff60b01b1916600160b01b1790556040517f4f366f0dc5cd876e456f089309e0c62fc2bc0e116c6f6ae308c392b4ad45b5b9906119be9060011515815260200190565b5f80611d3361149f565b805190602001209050604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815260208101929092527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc69082015246606082015230608082015260a09020919050565b3068929eee149b4bd212685403611dc35763ab143c065f526004601cfd5b3068929eee149b4bd2126855600e54600160a01b900460ff16611df85760405162461bcd60e51b815260040161138290615d7d565b600954600160b01b900460ff16611e215760405162461bcd60e51b815260040161138290615c70565b61016e8110611e425760405162461bcd60e51b815260040161138290615c9f565b335f90815260146020908152604091829020825160a0810184528154815260018201549281018390526002820154938101939093526003810154606084015260040154608083015215611f2357611e9833612c62565b15611ec857335f908152601460205260408120818155600181018290556002810182905560038101829055600401555b335f9081526014602052604090206001015415611f235760405162461bcd60e51b8152602060048201526019602482015278557365206163636f756e742077697468206e6f206c6f616e7360381b6044820152606401611382565b611f2b613da0565b5f611f3d426116bb8562015180615bf5565b90505f5f5f5f611f4d88886149de565b93509350935093505f818584611f639190615c46565b611f6d9190615c46565b90505f81341115611f8e57611f828234615c33565b9050611f8e3382614ee4565b81611f998234615c33565b14611fe65760405162461bcd60e51b815260206004820152601a60248201527f496e73756666696369656e742062657261206665652073656e740000000000006044820152606401611382565b5f611ff1878c615c33565b90505f611ffe82856143fe565b905061200a3082615265565b5f612710610dac61201b888c615c46565b6120259190615bf5565b61202f9190615c20565b90506103e881116120525760405162461bcd60e51b815260040161138290615d1d565b600a54612068906001600160a01b031682614ee4565b61207388838c614f84565b6040518060a001604052808381526020018981526020018b81526020018d81526020014281525060145f336001600160a01b03166001600160a01b031681526020019081526020015f205f820151815f0155602082015181600101556040820151816002015560608201518160030155608082015181600401559050506120f98d615058565b336001600160a01b03167feb367e9746d13b4c1364e134d4e5b495b973f2598bcf1c1e477345f66251df7d8e8e858c8a60405161213a959493929190615b29565b60405180910390a250505050505050505050503868929eee149b4bd21268555050565b3068929eee149b4bd21268540361217b5763ab143c065f526004601cfd5b3068929eee149b4bd2126855335f908152601460205260409020600101543481116121f45760405162461bcd60e51b8152602060048201526024808201527f4d757374207265706179206c657373207468616e20626f72726f77656420616d6044820152631bdd5b9d60e21b6064820152608401611382565b345f0361223a5760405162461bcd60e51b81526020600482015260146024820152734d75737420726570617920736f6d657468696e6760601b6044820152606401611382565b612242613da0565b61224b33612c62565b156122ac5760405162461bcd60e51b815260206004820152602b60248201527f596f7572206c6f616e20686173206265656e206c6971756964617465642c206360448201526a616e6e6f7420726570617960a81b6064820152608401611382565b5f6122b73483615c33565b335f90815260146020526040812060018101839055600201549192506122df913491906151fa565b6122e85f615058565b604080513481526020810183905233917f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d091015b60405180910390a250503868929eee149b4bd2126855565b3068929eee149b4bd2126854036123525763ab143c065f526004601cfd5b3068929eee149b4bd2126855612366613da0565b5f61237082613c3e565b905061237c3383615313565b5f612710600c546127106123909190615c33565b61239a9084615bf5565b6123a49190615c20565b90506123b03382614ee4565b5f61271080610dac600c54866123c69190615bf5565b6123d09190615bf5565b6123da9190615c20565b6123e49190615c20565b90506103e881116124075760405162461bcd60e51b815260040161138290615db4565b600a5461241d906001600160a01b031682614ee4565b61242683615058565b604080518381526020810186905233917f49995e5dd6158cf69ad3e9777c46755a1a826a446c6416992167462dad033b2a910160405180910390a25050503868929eee149b4bd212685550565b61247b6151e0565b60098054821515600160b01b0260ff60b01b199091161790556040517fbb614ecd1ed5d1454c15f856743fff55fa975d6257e7262b092a86067ceda764906119be90831515815260200190565b5f5f6124d3836114be565b9050612710600b546127106118569190615c33565b6124f06151e0565b60648110156125415760405162461bcd60e51b815260206004820181905260248201527f6275726e20666565206d7573742062652067726561746572207468616e2031256044820152606401611382565b6101f48111156125935760405162461bcd60e51b815260206004820152601d60248201527f6275726e20666565206d757374206265206c657373207468616e2035250000006044820152606401611382565b600c8190556040518181527fd54296e32811a7f2da2c1f6e8b39cb815ce208595da09bb98b034ccca6dffc69906020016119be565b3068929eee149b4bd2126854036125e65763ab143c065f526004601cfd5b3068929eee149b4bd2126855335f9081526007602052604090205461264d5760405162461bcd60e51b815260206004820152601a60248201527f4d757374207265717565737420756e7374616b652066697273740000000000006044820152606401611382565b335f908152600760205260409020546126699062015180615c46565b4210156126b85760405162461bcd60e51b815260206004820181905260248201527f4d757374207761697420323420686f75727320616674657220726571756573746044820152606401611382565b5f81116127025760405162461bcd60e51b815260206004820152601860248201527704d75737420756e7374616b65206d6f7265207468616e20360441b6044820152606401611382565b335f90815260086020526040902054811461275f5760405162461bcd60e51b815260206004820152601d60248201527f4d75737420756e7374616b652072657175657374656420616d6f756e740000006044820152606401611382565b6005546040516370a0823160e01b815282916001600160a01b0316906370a082319061278f9033906004016158b7565b602060405180830381865afa1580156127aa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127ce9190615c59565b10156128105760405162461bcd60e51b8152602060048201526011602482015270139bdd08195b9bdd59da081a5094915051607a1b6044820152606401611382565b5f6128928260065460055f9054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612868573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061288c9190615c59565b5f614e3c565b90505f81116128f25760405162461bcd60e51b815260206004820152602660248201527f556e7374616b6520616d6f756e7420746f6f20736d616c6c2c20726f756e646560448201526506420746f20360d41b6064820152608401611382565b8060065f8282546129039190615c33565b9091555050600554604051632770a7eb60e21b81526001600160a01b0390911690639dc29fac9061293a9033908690600401615de1565b5f604051808303815f87803b158015612951575f5ffd5b505af1158015612963573d5f5f3e3d5ffd5b50505050612972303383614e7e565b335f818152600760209081526040808320839055600882528083209290925581518481529081018590527f7fc4727e062e336010f2c282598ef5f14facb3de68cf8195c2f23e1454b2b74e91015b60405180910390a2503868929eee149b4bd212685550565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f5fa2565b3068929eee149b4bd212685403612a2f5763ab143c065f526004601cfd5b3068929eee149b4bd2126855612a4433612c62565b15612a825760405162461bcd60e51b815260206004820152600e60248201526d27379030b1ba34bb32903637b0b760911b6044820152606401611382565b612a8a613da0565b335f908152601460205260408120600181015490549091612aaa82613c3e565b9050612ab63083615313565b5f612710600c5483612ac89190615bf5565b612ad29190615c20565b90505f612adf8284615c33565b905084811015612b415760405162461bcd60e51b815260206004820152602760248201527f4e6f7420656e6f75676820636f6c6c61746572616c20746f20636c6f7365207060448201526637b9b4ba34b7b760c91b6064820152608401611382565b5f612b4c8683615c33565b90505f612710612b5e610dac86615bf5565b612b689190615c20565b90508115612b7a57612b7a3383614ee4565b6103e88111612b9b5760405162461bcd60e51b815260040161138290615d1d565b600a54612bb1906001600160a01b031682614ee4565b335f90815260146020526040902060020154612bd090889088906151fa565b335f90815260146020526040812081815560018101829055600281018290556003810182905560040155612c0387615058565b60408051888152602081018890529081018390526060810185905233907fc822db49fe0e413f18fe47901b8b3aaf08de512bdfdea4648f48988032810ffa9060800160405180910390a2505050505050503868929eee149b4bd2126855565b6001600160a01b03165f90815260146020526040902060020154421190565b612c896151e0565b612c925f615374565b565b5f5f5f5f612ca18661401e565b6387a211a2600c9081525f8890526020902054612cbe9190615c46565b9050612cc981613c3e565b9350612710612cda6126ac86615bf5565b612ce49190615c20565b9250612cf08386611221565b9150509250925092565b5f3068929eee149b4bd212685403612d195763ab143c065f526004601cfd5b3068929eee149b4bd2126855600954600160b01b900460ff16612d4e5760405162461bcd60e51b815260040161138290615c70565b335f908152601460205260408120600281015460018201548254600390930154919390929190612d818762015180615bf5565b612d8b9086615c46565b90505f612d988589611221565b9050612da333612c62565b15612dc05760405162461bcd60e51b815260040161138290615d54565b348114612e0f5760405162461bcd60e51b815260206004820152601c60248201527f4c6f616e20657874656e73696f6e2066656520696e636f7272656374000000006044820152606401611382565b5f612710612e1f610dac84615bf5565b612e299190615c20565b90506103e88111612e4c5760405162461bcd60e51b815260040161138290615d1d565b612e54613da0565b600a54612e6a906001600160a01b031682614ee4565b612e758686896151fa565b612e80868685614f84565b335f908152601460205260409020600201839055612e9e848a615c46565b335f9081526014602052604090206003015561016e62015180612ec14286615c33565b612ecb9190615c20565b10612f185760405162461bcd60e51b815260206004820152601b60248201527f4c6f616e206d75737420626520756e64657220333635206461797300000000006044820152606401611382565b612f2134615058565b604080518a8152602081018790529081018790526060810183905233907f7514cf35c34b8a13661f827353b5d69b4c72f13a08e166632a7fcd84d0bbb4069060800160405180910390a2503868929eee149b4bd2126855979650505050505050565b612f8b6151e0565b6012548111612ff05760405162461bcd60e51b815260206004820152602b60248201527f4d617820737570706c79206d7573742062652067726561746572207468616e2060448201526a1d1bdd185b0818985ad95960aa1b6064820152608401611382565b60115481116130315760405162461bcd60e51b815260206004820152600d60248201526c496e637265617365206f6e6c7960981b6044820152606401611382565b60118190556040518181525f516020615ef25f395f51905f52906020016119be565b3068929eee149b4bd2126854036130715763ab143c065f526004601cfd5b3068929eee149b4bd2126855613086816153b1565b3868929eee149b4bd212685550565b6001600160a01b0381165f908152601460205260408120600201548190819042116130e9575050506001600160a01b0381165f908152601460205260409020805460018201546002909201549091906130f2565b505f9150819050805b9193909250565b5f5f613103614084565b90506114e68361311a6805345cdf77eb68f44c5490565b836001614e3c565b3068929eee149b4bd2126854036131405763ab143c065f526004601cfd5b3068929eee149b4bd212685560015434146131975760405162461bcd60e51b8152602060048201526017602482015276125b98dbdc9c9958dd08189bdd5b9d1e48185b5bdd5b9d604a1b6044820152606401611382565b80806131e55760405162461bcd60e51b815260206004820152601d60248201527f4d75737420636c61696d206174206c65617374206f6e6520746f6b656e0000006044820152606401611382565b5f8167ffffffffffffffff8111156131ff576131ff615dfa565b604051908082528060200260200182016040528015613228578160200160208202803683370190505b5090505f5b82811015613331575f85858381811061324857613248615e0e565b905060200201602081019061325d91906159ce565b9050306001600160a01b038216036132b35760405162461bcd60e51b815260206004820152601960248201527843616e6e6f7420636c61696d20425245414420746f6b656e7360381b6044820152606401611382565b5f6132c76001600160a01b0383163061559b565b6001600160a01b0383165f908152600460205260409020549091506132ec8183615c33565b91508115613326578185858151811061330757613307615e0e565b60209081029190910101526133266001600160a01b03841633846155c5565b50505060010161322d565b505f6127105f54346133439190615bf5565b61334d9190615c20565b90506103e88110156133a15760405162461bcd60e51b815260206004820152601d60248201527f466565206d7573742062652067726561746572207468616e20647573740000006044820152606401611382565b600a546133b7906001600160a01b031682614ee4565b600154336001600160a01b03167fbf139079d1f4f0053ac82d22cc6e5769f37a0e5a276efdd4f1c2bb0b979c1d848787866040516133f793929190615e22565b60405180910390a35050503868929eee149b4bd21268555050565b61341a6151e0565b6009805460ff19168215159081179091556040519081527f280cb161002275204369941f9802ee155e35a137f91f719acae275cbed8a5092906020016119be565b6134636151e0565b306001600160a01b038216036134d55760405162461bcd60e51b815260206004820152603160248201527f42726561643a2063616e206f6e6c79207265636f7665722065726332307320746044820152701a185d08185c99481b9bdd08189c995859607a1b6064820152608401611382565b6134fd336134ec6001600160a01b0384163061559b565b6001600160a01b03841691906155c5565b50565b5f8061350d846005615bf5565b90505f61351b856064615bf5565b90505f5b8183101561359f5760026135338385615c46565b61353e906001615c46565b6135489190615c20565b90505f5f5f61355784896149de565b935093505092505f83838361356c9190615c46565b6135769190615c46565b90508981101561358857849650613596565b613593600186615c33565b95505b5050505061351f565b5090949350505050565b3068929eee149b4bd2126854036135c75763ab143c065f526004601cfd5b3068929eee149b4bd212688190556001600160a01b038416036136275760405162461bcd60e51b815260206004820152601860248201527743616e6e6f74206c6f636b20425245414420746f6b656e7360401b6044820152606401611382565b4281116136805760405162461bcd60e51b815260206004820152602160248201527f556e6c6f636b2074696d65206d75737420626520696e207468652066757475726044820152606560f81b6064820152608401611382565b5f82116136cf5760405162461bcd60e51b815260206004820152601d60248201527f416d6f756e74206d7573742062652067726561746572207468616e20300000006044820152606401611382565b335f9081526003602090815260408083206001600160a01b038716845290915290208054156137475780600101548210156137475760405162461bcd60e51b815260206004820152601860248201527743616e6e6f742073686f7274656e206c6f636b2074696d6560401b6044820152606401611382565b5f612710600254856137599190615bf5565b6137639190615c20565b90505f6137708286615c33565b90506137876001600160a01b03871633308861560f565b80835f015f8282546137999190615c46565b9091555050600183018490556001600160a01b0386165f90815260046020526040812080548392906137cc908490615c46565b909155505060408051828152602081018690526001600160a01b0388169133917fd8a1c0afefbfb5f6da58761715434130bcc1b9da12dba1ab5b21e44f5ae4d27e910160405180910390a35050503868929eee149b4bd2126855505050565b3068929eee149b4bd2126854036138495763ab143c065f526004601cfd5b3068929eee149b4bd2126855600954600160b01b900460ff1661387e5760405162461bcd60e51b815260040161138290615c70565b61388733612c62565b156138ce5760405162461bcd60e51b81526020600482015260176024820152764c6f616e20657870697265642075736520626f72726f7760481b6044820152606401611382565b805f036138ed5760405162461bcd60e51b815260040161138290615cec565b6138f5613da0565b335f90815260146020526040812060018101548154600290920154909261391b42614222565b90505f6201518061392c8385615c33565b6139369190615c20565b90505f6127106139486126ac89615bf5565b6139529190615c20565b90505f61395f8284611221565b90505f61396b886130f9565b90505f61397f826127106126ac6001614e3c565b6139899089615c33565b90505f6139958b6130f9565b90505f818310156139af576139aa8383615c33565b6139b1565b5f5b90505f6139be878d615c46565b90505f6139cb838d615c46565b6040805160a08101825291825260208083019485528282018e8152606084018d81524260808601908152335f908152601490945293909220935184559451600184015593516002830155925160038201559151600490920191909155508015613a3957613a39333083614e7e565b5f612710613a49610dac88615bf5565b613a539190615c20565b90506103e88111613a765760405162461bcd60e51b815260040161138290615d1d565b600a54613a8c906001600160a01b031682614ee4565b50613a9b336117a98789615c33565b613aa686828b614f84565b613aaf85615058565b336001600160a01b03167f212a25b7e6d62ca82a704bf5f0513e2d0840e89914c62639dceabfb40a34491a8d89858a8a604051613af0959493929190615b29565b60405180910390a250505050505050505050503868929eee149b4bd212685550565b5f6387a211a2600c52335f526020600c20805480841115613b3a5763f4d678b85f526004601cfd5b83810382555050825f526020600c208281540181555081602052600c5160601c335f516020615f125f395f51905f52602080a350600192915050565b60603415613b82575f5ffd5b611307613b8f8484615668565b6156fa565b613b9c6151e0565b6103e8811115613c095760405162461bcd60e51b815260206004820152603260248201527f546f6b656e206c6f636b657220666565206d757374206265206c657373207468604482015271616e206f7220657175616c20746f2031302560701b6064820152608401611382565b60028190556040518181527f95b18a9a21555a972999713c5cbda80d3b9d0ac264c9165671d99af6b10281a9906020016119be565b5f61130782613c4b614084565b6805345cdf77eb68f44c5461288c565b613c636151e0565b6064811015613cb45760405162461bcd60e51b815260206004820181905260248201527f62616b6520666565206d7573742062652067726561746572207468616e2031256044820152606401611382565b6101f4811115613d065760405162461bcd60e51b815260206004820152601d60248201527f62616b6520666565206d757374206265206c657373207468616e2035250000006044820152606401611382565b600d54811015613d6b5760405162461bcd60e51b815260206004820152602a60248201527f62616b6520666565206d7573742062652067726561746572207468616e206c656044820152697665726167652066656560b01b6064820152608401611382565b600b8190556040518181527f3697c9a523da6c9f5f906cf187ec9eea0872c710b6992b7ddfd76042a658f9d6906020016119be565b5f5f5b426017541015613e01576017545f90815260166020526040902054613dc89082615c46565b6017545f90815260156020526040902054909150613de69083615c46565b915060175462015180613df99190615c46565b601755613da3565b8015613e225780601054613e159190615c33565b601055613e223082615313565b8115613e7e5781600f54613e369190615c33565b600f55601754613e4a906201518090615c33565b6040518381527fb40747442264847104813679bf15dc7a9df6e2b499cfbd3cad9134d6a8261a149060200160405180910390a25b5050565b613e8a6151e0565b60018190556040518181527f5964e4694d5d7de1567d5bb0ca59699503fec37ed4eafc36acbca2afb9a337e1906020016119be565b3068929eee149b4bd212685403613edd5763ab143c065f526004601cfd5b3068929eee149b4bd2126855335f818152601460205260409020600181015490549091613f0990612c62565b15613f265760405162461bcd60e51b815260040161138290615d54565b348214613f805760405162461bcd60e51b815260206004820152602260248201527f4d7573742072657475726e20656e7469726520626f72726f77656420616d6f756044820152611b9d60f21b6064820152608401611382565b613f88613da0565b613f93303383614e7e565b335f90815260146020526040902060020154613fb290839083906151fa565b335f908152601460205260408120818155600181018290556002810182905560038101829055600401819055613fe790615058565b604080513481525f602082015233917f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d0910161231c565b5f61402882612c62565b1561403457505f919050565b6001600160a01b0382165f9081526014602052604081208054600190910154909161405e826130f9565b9050614071816127106126ac6001614e3c565b61407b9084615c33565b95945050505050565b5f600f54476140939190615c46565b905090565b3068929eee149b4bd2126854036140b65763ab143c065f526004601cfd5b3068929eee149b4bd2126855335f9081526003602090815260408083206001600160a01b0385168452909152902080546141255760405162461bcd60e51b815260206004820152601060248201526f139bc81d1bdad95b9cc81b1bd8dad95960821b6044820152606401611382565b80600101544210156141735760405162461bcd60e51b8152602060048201526017602482015276155b9b1bd8dac81d1a5b59481b9bdd081c995858da1959604a1b6044820152606401611382565b8054335f9081526003602090815260408083206001600160a01b038716845282528083208381556001018390556004909152812080548392906141b7908490615c33565b909155506141d190506001600160a01b03841633836155c5565b6040518181526001600160a01b0384169033907f549f3836aa79a43ac740f9814586c8b7ab5e0d299ea11ac017c6d889704962ae9060200160405180910390a350503868929eee149b4bd212685550565b5f806142316201518084615ea9565b61423b9084615c33565b90506114e68162015180615c46565b6001600160a01b0386166e22d473030f116ddee9f6b43ac78ba3188519151761427a57633f68539a5f526004601cfd5b5f61428361149f565b8051906020012090507fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6428610156142c257631a15a3cc5f526004601cfd5b6040518960601b60601c99508860601b60601c985065383775081901600e52895f526020600c2080547f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835284602084015283604084015246606084015230608084015260a08320602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983528b60208401528a60408401528960608401528060808401528860a084015260c08320604e526042602c205f528760ff16602052866040528560605260208060805f60015afa8c3d51146143aa5763ddafbaef5f526004601cfd5b0190556303faf4f960a51b89176040526034602c20889055888a7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925602060608501a360405250505f60605250505050505050565b5f5f82614409614084565b6144139190615c33565b905061442a846114df6805345cdf77eb68f44c5490565b949350505050565b3068929eee149b4bd2126854036144505763ab143c065f526004601cfd5b3068929eee149b4bd212685560095460ff166144a45760405162461bcd60e51b815260206004820152601360248201527214dd185ada5b99c81a5cc8191a5cd8589b1959606a1b6044820152606401611382565b5f81116144ec5760405162461bcd60e51b815260206004820152601660248201527504d757374207374616b65206d6f7265207468616e20360541b6044820152606401611382565b6144f7333083614e7e565b5f6006545f14614587576145828260055f9054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614555573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906145799190615c59565b6006545f614e3c565b614589565b815b90505f81116145e65760405162461bcd60e51b8152602060048201526024808201527f5374616b6520616d6f756e7420746f6f20736d616c6c2c20726f756e646564206044820152630746f20360e41b6064820152608401611382565b8160065f8282546145f79190615c46565b90915550506005546040516340c10f1960e01b81526001600160a01b03909116906340c10f199061462e9033908590600401615de1565b5f604051808303815f87803b158015614645575f5ffd5b505af1158015614657573d5f5f3e3d5ffd5b505060408051858152602081018590523393507f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee909250016129c0565b3068929eee149b4bd2126854036146b15763ab143c065f526004601cfd5b3068929eee149b4bd2126855600954600160a81b900460ff1661470c5760405162461bcd60e51b8152602060048201526013602482015272109c99585911d5081b9bdd08195b98589b1959606a1b6044820152606401611382565b600a545f906147399073656b95e550c07a9ffe548bd4085c72418ceb1dba906001600160a01b031661559b565b600a54604051636b09169560e01b81523360048201526001600160a01b0391821660248201529192505f9190841690636b091695906044016020604051808303815f875af115801561478d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906147b19190615c59565b90505f81116147f75760405162461bcd60e51b81526020600482015260126024820152714e6f2072657761726420746f20636c61696d60701b6044820152606401611382565b600a545f906148249073656b95e550c07a9ffe548bd4085c72418ceb1dba906001600160a01b031661559b565b90506148308382615c33565b82146148945760405162461bcd60e51b815260206004820152602d60248201527f496e636f72726563742072657761726420616d6f756e742c20736f6d6574686960448201526c6e672077656e742077726f6e6760981b6064820152608401611382565b5f6127105f54846148a59190615bf5565b6148af9190615c20565b60095490915061010090046001600160a01b03166340c10f19336148d38487615c33565b6040518363ffffffff1660e01b81526004016148f0929190615de1565b5f604051808303815f87803b158015614907575f5ffd5b505af1158015614919573d5f5f3e3d5ffd5b505050505f81111561498e57600954600a546040516340c10f1960e01b81526001600160a01b036101009093048316926340c10f1992614960929116908590600401615de1565b5f604051808303815f87803b158015614977575f5ffd5b505af1158015614989573d5f5f3e3d5ffd5b505050505b337f4bed93727626c2e5cdf79a4c7fe79a32aa12a558d20f90499442329be658f2066149ba8386615c33565b60405190815260200160405180910390a2505050503868929eee149b4bd212685550565b5f5f5f5f612710600d54876149f39190615bf5565b6149fd9190615c20565b93505f614a0a8588615c33565b9050612710614a1b6126ac83615bf5565b614a259190615c20565b9350614a318482615c33565b9250614a3d8487611221565b91505092959194509250565b5f6e22d473030f116ddee9f6b43ac78ba2196001600160a01b03831601614a7257505f19611307565b50602052637f5e9f20600c9081525f91909152603490205490565b5f61130782614aa36805345cdf77eb68f44c5490565b84614aac614084565b61288c9190615c33565b614abe6151e0565b60098054821515600160a81b0260ff60a81b199091161790556040517f0933b1cd1180593a9418d9d5d8a1b4449821a5b897c0ab543c7803bc6ef8d953906119be90831515815260200190565b614b136151e0565b6001600160a01b038116614b745760405162461bcd60e51b815260206004820152602260248201527f43616e27742073657420666565206164647265737320746f2030206164647265604482015261737360f01b6064820152608401611382565b600a80546001600160a01b0319166001600160a01b0383161790556040517f6cfe0497f5f7529f70577cf6199ec6c0b7485396857cd7b90d301b3728d10ca3906119be9083906158b7565b614bc76151e0565b63389a75e1600c52805f526020600c208054421115614bed57636f5e88185f526004601cfd5b5f90556134fd81615374565b614c016151e0565b8060601b614c1657637448fbae5f526004601cfd5b6134fd81615374565b614c276151e0565b600e80546001600160a01b0319166001600160a01b0383161790556040517f7de48a8d85b718057dc36cdcb027221944b0682f5ffeb60bf84644d038455ced906119be9083906158b7565b614c7a6151e0565b60055460405163cf456ae760e01b81526001600160a01b03848116600483015283151560248301529091169063cf456ae7906044015f604051808303815f87803b158015614cc6575f5ffd5b505af1158015614cd8573d5f5f3e3d5ffd5b505050505050565b614ce86151e0565b6032811015614d485760405162461bcd60e51b815260206004820152602660248201527f6c6576657261676520666565206d7573742062652067726561746572207468616044820152656e20302e352560d01b6064820152608401611382565b60fa811115614da55760405162461bcd60e51b815260206004820152602360248201527f6c6576657261676520666565206d757374206265206c657373207468616e20326044820152622e352560e81b6064820152608401611382565b600b54811115614e075760405162461bcd60e51b815260206004820152602760248201527f6c6576657261676520666565206d757374206265206c657373207468616e2062604482015266616b652066656560c81b6064820152608401611382565b600d8190556040518181527fd4e469371f09a592c9c4fde36ed11fd123a8cc55d93bd2920ea6ad544dff4395906020016119be565b5f614e69614e498361570b565b8015614e6457505f8480614e5f57614e5f615c0c565b868809115b151590565b614e74868686615737565b61407b9190615c46565b8260601b6387a211a28117600c526020600c20805480841115614ea85763f4d678b85f526004601cfd5b83810382555050825f526020600c208281540181555081602052600c5160601c8160601c5f516020615f125f395f51905f52602080a350505050565b6001600160a01b038216614f345760405162461bcd60e51b815260206004820152601760248201527643616e27742073656e6420746f2030206164647265737360481b6044820152606401611382565b614f476001600160a01b038316826157ed565b7ff6858a8e34dd017aae7934951c7fbd3129bde39de42369d53b17ff174e3ff10d8282604051614f78929190615de1565b60405180910390a15050565b5f81815260166020526040902054614f9d908390615c46565b5f82815260166020908152604080832093909355601590522054614fc2908490615c46565b5f82815260156020526040902055600f54614fde908490615c46565b600f55601054614fef908390615c46565b60108190555f82815260166020908152604080832054601583529281902054600f5482519485529284015282015260608101919091527faec00f5213a37254bc68a26b0685d1a5b2bf513e1e587c94c7df7f4a62b56c9c906080015b60405180910390a1505050565b5f61506a6805345cdf77eb68f44c5490565b615072614084565b61508490670de0b6b3a7640000615bf5565b61508e9190615c20565b90505f6150ac306387a211a2600c9081525f91909152602090205490565b90506010548110156151405760405162461bcd60e51b815260206004820152605160248201527f5468652062726561642062616c616e6365206f662074686520636f6e7472616360448201527f74206d7573742062652067726561746572207468616e206f7220657175616c206064820152701d1bc81d1a194818dbdb1b185d195c985b607a1b608482015260a401611382565b81601354111561519d5760405162461bcd60e51b815260206004820152602260248201527f546865207072696365206f662062726561642063616e6e6f7420646563726561604482015261736560f01b6064820152608401611382565b601382905560408051428152602081018490529081018490527f15819dd2fd9f6418b142e798d08a18d0bf06ea368f4480b7b0d3f75bd966bc489060600161504b565b638b78c6d819543314612c92576382b429005f526004601cfd5b5f81815260166020526040902054615213908390615c33565b5f82815260166020908152604080832093909355601590522054615238908490615c33565b5f82815260156020526040902055600f54615254908490615c33565b600f55601054614fef908390615c33565b6001600160a01b0382166152b55760405162461bcd60e51b815260206004820152601760248201527643616e2774206d696e7420746f2030206164647265737360481b6044820152606401611382565b806012546152c39190615c46565b601281905560115410156153095760405162461bcd60e51b815260206004820152600d60248201526c1393c81353d491481094915051609a1b6044820152606401611382565b613e7e8282615806565b6387a211a2600c52815f526020600c2080548083111561533a5763f4d678b85f526004601cfd5b82900390556805345cdf77eb68f44c805482900390555f8181526001600160a01b0383165f516020615f125f395f51905f52602083a35050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b6153b9613da0565b600e54600160a01b900460ff166153e25760405162461bcd60e51b815260040161138290615d7d565b6001600160a01b0381166154385760405162461bcd60e51b815260206004820152601c60248201527f52656365697665722063616e6e6f7420626520302061646472657373000000006044820152606401611382565b5f61544234614a8d565b90505f612710600b546127106154589190615c33565b6154629084615bf5565b61546c9190615c20565b600e549091506001600160a01b031633036154d157601154816012546154929190615c46565b11156154d1575f816011546154a79190615c46565b60118190556040518181529091505f516020615ef25f395f51905f529060200160405180910390a1505b6154db8382615265565b5f61271080610dac600b54346154f19190615bf5565b6154fb9190615bf5565b6155059190615c20565b61550f9190615c20565b90506103e881116155325760405162461bcd60e51b815260040161138290615db4565b600a54615548906001600160a01b031682614ee4565b61555134615058565b60408051348152602081018490526001600160a01b038616917f96c59bac7680c77217510ed14bc725bbca570081c194f20c9fad3fa3b349ca43910160405180910390a250505050565b5f816014526370a0823160601b5f5260208060246010865afa601f3d111660205102905092915050565b816014528060345263a9059cbb60601b5f5260205f604460105f875af18060015f51141661560557803d853b151710615605576390b8ec185f526004601cfd5b505f603452505050565b60405181606052826040528360601b602c526323b872dd60601b600c5260205f6064601c5f895af18060015f51141661565a57803d873b15171061565a57637939f4245f526004601cfd5b505f60605260405250505050565b5f60405190506020815281816020015280604001808360051b8086843782018085156156e2575b8351870180356020820183375f38823584305af46156af573d5f873e3d86fd5b5082810384523d815260209384019381013d5f823e5f3d8201523d01601f0167ffffffffffffffe016905081841061568f575b806040528485820360401b1794505050505092915050565b8060401c8167ffffffffffffffff16f35b5f600282600381111561572057615720615ebc565b61572a9190615ed0565b60ff166001149050919050565b5f838302815f1985870982811083820303915050805f0361576b5783828161576157615761615c0c565b04925050506114e6565b80841161578257615782600385150260111861586f565b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f385f3884865af1613e7e5763b12d13eb5f526004601cfd5b6805345cdf77eb68f44c54818101818110156158295763e5cfe9575f526004601cfd5b806805345cdf77eb68f44c5550506387a211a2600c52815f526020600c208181540181555080602052600c5160601c5f5f516020615f125f395f51905f52602080a35050565b634e487b715f52806020526024601cfd5b5f60208284031215615890575f5ffd5b5035919050565b5f5f604083850312156158a8575f5ffd5b50508035926020909101359150565b6001600160a01b0391909116815260200190565b5f81518084525f5b818110156158ef576020818501810151868301820152016158d3565b505f602082860101526020601f19601f83011685010191505092915050565b602081525f6114e660208301846158cb565b80356001600160a01b0381168114615936575f5ffd5b919050565b5f5f6040838503121561594c575f5ffd5b61595583615920565b915061596360208401615920565b90509250929050565b5f5f6040838503121561597d575f5ffd5b61598683615920565b946020939093013593505050565b5f5f5f606084860312156159a6575f5ffd5b6159af84615920565b92506159bd60208501615920565b929592945050506040919091013590565b5f602082840312156159de575f5ffd5b6114e682615920565b80358015158114615936575f5ffd5b5f60208284031215615a06575f5ffd5b6114e6826159e7565b5f5f83601f840112615a1f575f5ffd5b50813567ffffffffffffffff811115615a36575f5ffd5b6020830191508360208260051b8501011115615a50575f5ffd5b9250929050565b5f5f60208385031215615a68575f5ffd5b823567ffffffffffffffff811115615a7e575f5ffd5b615a8a85828601615a0f565b90969095509350505050565b5f5f5f60608486031215615aa8575f5ffd5b615ab184615920565b95602085013595506040909401359392505050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b82811015615b1d57603f19878603018452615b088583516158cb565b94506020938401939190910190600101615aec565b50929695505050505050565b948552602085019390935260408401919091526060830152608082015260a00190565b5f5f5f5f5f5f5f60e0888a031215615b62575f5ffd5b615b6b88615920565b9650615b7960208901615920565b95506040880135945060608801359350608088013560ff81168114615b9c575f5ffd5b9699959850939692959460a0840135945060c09093013592915050565b5f5f60408385031215615bca575f5ffd5b615bd383615920565b9150615963602084016159e7565b634e487b7160e01b5f52601160045260245ffd5b808202811582820484141761130757611307615be1565b634e487b7160e01b5f52601260045260245ffd5b5f82615c2e57615c2e615c0c565b500490565b8181038181111561130757611307615be1565b8082018082111561130757611307615be1565b5f60208284031215615c69575f5ffd5b5051919050565b602080825260159082015274109bdc9c9bddda5b99c81a5cc8191a5cd8589b1959605a1b604082015260600190565b6020808252602d908201527f4d617820626f72726f772f657874656e73696f6e206d7573742062652033363560408201526c2064617973206f72206c65737360981b606082015260800190565b60208082526017908201527604d75737420626f72726f77206d6f7265207468616e203604c1b604082015260600190565b6020808252601d908201527f46656573206d75737420626520686967686572207468616e2064757374000000604082015260600190565b6020808252600f908201526e4e6f20616374697665206c6f616e7360881b604082015260600190565b6020808252601b908201527f54726164696e67206d75737420626520696e697469616c697a65640000000000604082015260600190565b60208082526013908201527236bab9ba103a3930b2329037bb32b91036b4b760691b604082015260600190565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b604080825281018390525f8460608301825b86811015615e62576001600160a01b03615e4d84615920565b16825260209283019290910190600101615e34565b50838103602080860191909152855180835291810192508501905f5b81811015615e9c578251845260209384019390920191600101615e7e565b5091979650505050505050565b5f82615eb757615eb7615c0c565b500690565b634e487b7160e01b5f52602160045260245ffd5b5f60ff831680615ee257615ee2615c0c565b8060ff8416069150509291505056febaea970d90a83e1a788fb13b7d7d5dd6acdc45f67728ba650b5b69a496015344ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220b476cf6d62f153cc72a1a00274ed8c8965a01137f48476fe684a779d688ea4ef64736f6c634300081c003360a0604052348015600e575f5ffd5b5033608052608051610be76100405f395f818161023b015281816104bc0152818161054101526106100152610be75ff3fe608060405234801561000f575f5ffd5b50600436106100f3575f3560e01c80637ecebe001161008f5780637ecebe00146101c957806395d89b41146101ee5780639dc29fac14610210578063a9059cbb14610223578063a9e31a6e14610236578063cf456ae714610275578063d505accf14610288578063dd62ed3e1461029b578063f46eccc4146102ae575f5ffd5b806306fdde03146100f7578063095ea7b31461011557806318160ddd1461013857806323b872dd14610152578063313ce567146101655780633644e5151461017457806340c10f191461017c57806342966c681461019157806370a08231146101a4575b5f5ffd5b6100ff6102d0565b60405161010c919061098c565b60405180910390f35b6101286101233660046109f2565b6102f9565b604051901515815260200161010c565b6805345cdf77eb68f44c545b60405190815260200161010c565b610128610160366004610a1a565b610379565b6040516012815260200161010c565b610144610435565b61018f61018a3660046109f2565b6104b1565b005b61018f61019f366004610a54565b610529565b6101446101b2366004610a6b565b6387a211a2600c9081525f91909152602090205490565b6101446101d7366004610a6b565b6338377508600c9081525f91909152602090205490565b6040805180820190915260068152651a509491505160d21b60208201526100ff565b61018f61021e3660046109f2565b610536565b6101286102313660046109f2565b6105a1565b61025d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161010c565b61018f610283366004610a8b565b610605565b61018f610296366004610ac4565b6106ca565b6101446102a9366004610b31565b61087e565b6101286102bc366004610a6b565b5f6020819052908152604090205460ff1681565b60408051808201909152600f81526e125b999b185d1a5bdb881094915051608a1b602082015290565b5f6001600160a01b0383166e22d473030f116ddee9f6b43ac78ba3188219151761032a57633f68539a5f526004601cfd5b82602052637f5e9f20600c52335f52816034600c2055815f52602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa35060015b92915050565b5f8360601b6e22d473030f116ddee9f6b43ac78ba333146103ce5733602052637f5e9f208117600c526034600c2080548019156103cb57808511156103c5576313be252b5f526004601cfd5b84810382555b50505b6387a211a28117600c526020600c208054808511156103f45763f4d678b85f526004601cfd5b84810382555050835f526020600c208381540181555082602052600c5160601c8160601c5f516020610b925f395f51905f52602080a3505060019392505050565b5f8061043f6102d0565b805190602001209050604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815260208101929092527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc69082015246606082015230608082015260a09020919050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614806104f65750335f9081526020819052604090205460ff165b61051b5760405162461bcd60e51b815260040161051290610b62565b60405180910390fd5b61052582826108c2565b5050565b610533338261092b565b50565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148061057b5750335f9081526020819052604090205460ff165b6105975760405162461bcd60e51b815260040161051290610b62565b610525828261092b565b5f6387a211a2600c52335f526020600c208054808411156105c95763f4d678b85f526004601cfd5b83810382555050825f526020600c208281540181555081602052600c5160601c335f516020610b925f395f51905f52602080a350600192915050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461066a5760405162461bcd60e51b815260206004820152600a60248201526913db9b1e48189c99585960b21b6044820152606401610512565b6001600160a01b0382165f8181526020818152604091829020805460ff19168515159081179091558251938452908301527f583b0aa0e528532caf4b907c11d7a8158a122fe2a6fb80cd9b09776ebea8d92d910160405180910390a15050565b6001600160a01b0386166e22d473030f116ddee9f6b43ac78ba318851915176106fa57633f68539a5f526004601cfd5b5f6107036102d0565b8051906020012090507fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc64286101561074257631a15a3cc5f526004601cfd5b6040518960601b60601c99508860601b60601c985065383775081901600e52895f526020600c2080547f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835284602084015283604084015246606084015230608084015260a08320602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983528b60208401528a60408401528960608401528060808401528860a084015260c08320604e526042602c205f528760ff16602052866040528560605260208060805f60015afa8c3d511461082a5763ddafbaef5f526004601cfd5b0190556303faf4f960a51b89176040526034602c20889055888a7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925602060608501a360405250505f60605250505050505050565b5f6e22d473030f116ddee9f6b43ac78ba2196001600160a01b038316016108a757505f19610373565b50602052637f5e9f20600c9081525f91909152603490205490565b6805345cdf77eb68f44c54818101818110156108e55763e5cfe9575f526004601cfd5b806805345cdf77eb68f44c5550506387a211a2600c52815f526020600c208181540181555080602052600c5160601c5f5f516020610b925f395f51905f52602080a35050565b6387a211a2600c52815f526020600c208054808311156109525763f4d678b85f526004601cfd5b82900390556805345cdf77eb68f44c805482900390555f8181526001600160a01b0383165f516020610b925f395f51905f52602083a35050565b602081525f82518060208401525f5b818110156109b8576020818601810151604086840101520161099b565b505f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b03811681146109ed575f5ffd5b919050565b5f5f60408385031215610a03575f5ffd5b610a0c836109d7565b946020939093013593505050565b5f5f5f60608486031215610a2c575f5ffd5b610a35846109d7565b9250610a43602085016109d7565b929592945050506040919091013590565b5f60208284031215610a64575f5ffd5b5035919050565b5f60208284031215610a7b575f5ffd5b610a84826109d7565b9392505050565b5f5f60408385031215610a9c575f5ffd5b610aa5836109d7565b915060208301358015158114610ab9575f5ffd5b809150509250929050565b5f5f5f5f5f5f5f60e0888a031215610ada575f5ffd5b610ae3886109d7565b9650610af1602089016109d7565b95506040880135945060608801359350608088013560ff81168114610b14575f5ffd5b9699959850939692959460a0840135945060c09093013592915050565b5f5f60408385031215610b42575f5ffd5b610b4b836109d7565b9150610b59602084016109d7565b90509250929050565b6020808252601590820152744f6e6c79204272656164206f72204d696e7465727360581b60408201526060019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122010d0cd4d7d5e240f83c3e7271cddd998accb2cde034853e4263509c012ac6ae964736f6c634300081c003360a0604052348015600e575f5ffd5b50336080526080516109e06100325f395f8181610208015261044e01526109e05ff3fe608060405234801561000f575f5ffd5b50600436106100d2575f3560e01c806342966c681161008457806342966c681461017057806370a08231146101835780637ecebe00146101a857806395d89b41146101cd578063a9059cbb146101f0578063a9e31a6e14610203578063d505accf14610242578063dd62ed3e14610255575f5ffd5b806306fdde03146100d6578063095ea7b3146100f457806318160ddd1461011757806323b872dd14610131578063313ce567146101445780633644e5151461015357806340c10f191461015b575b5f5ffd5b6100de610268565b6040516100eb91906107ed565b60405180910390f35b610107610102366004610853565b61028b565b60405190151581526020016100eb565b6805345cdf77eb68f44c545b6040519081526020016100eb565b61010761013f36600461087b565b61030b565b604051601281526020016100eb565b6101236103c7565b61016e610169366004610853565b610443565b005b61016e61017e3660046108b5565b6104ba565b6101236101913660046108cc565b6387a211a2600c9081525f91909152602090205490565b6101236101b63660046108cc565b6338377508600c9081525f91909152602090205490565b604080518082019091526007815266109c99585911d560ca1b60208201526100de565b6101076101fe366004610853565b6104c7565b61022a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100eb565b61016e6102503660046108ec565b61052b565b610123610263366004610959565b6106df565b604080518082019091526009815268109c995859081091d560ba1b602082015290565b5f6001600160a01b0383166e22d473030f116ddee9f6b43ac78ba318821915176102bc57633f68539a5f526004601cfd5b82602052637f5e9f20600c52335f52816034600c2055815f52602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa35060015b92915050565b5f8360601b6e22d473030f116ddee9f6b43ac78ba333146103605733602052637f5e9f208117600c526034600c20805480191561035d5780851115610357576313be252b5f526004601cfd5b84810382555b50505b6387a211a28117600c526020600c208054808511156103865763f4d678b85f526004601cfd5b84810382555050835f526020600c208381540181555082602052600c5160601c8160601c5f51602061098b5f395f51905f52602080a3505060019392505050565b5f806103d1610268565b805190602001209050604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815260208101929092527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc69082015246606082015230608082015260a09020919050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104ac5760405162461bcd60e51b815260206004820152600a60248201526913db9b1e48109c99585960b21b604482015260640160405180910390fd5b6104b68282610723565b5050565b6104c4338261078c565b50565b5f6387a211a2600c52335f526020600c208054808411156104ef5763f4d678b85f526004601cfd5b83810382555050825f526020600c208281540181555081602052600c5160601c335f51602061098b5f395f51905f52602080a350600192915050565b6001600160a01b0386166e22d473030f116ddee9f6b43ac78ba3188519151761055b57633f68539a5f526004601cfd5b5f610564610268565b8051906020012090507fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6428610156105a357631a15a3cc5f526004601cfd5b6040518960601b60601c99508860601b60601c985065383775081901600e52895f526020600c2080547f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835284602084015283604084015246606084015230608084015260a08320602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983528b60208401528a60408401528960608401528060808401528860a084015260c08320604e526042602c205f528760ff16602052866040528560605260208060805f60015afa8c3d511461068b5763ddafbaef5f526004601cfd5b0190556303faf4f960a51b89176040526034602c20889055888a7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925602060608501a360405250505f60605250505050505050565b5f6e22d473030f116ddee9f6b43ac78ba2196001600160a01b0383160161070857505f19610305565b50602052637f5e9f20600c9081525f91909152603490205490565b6805345cdf77eb68f44c54818101818110156107465763e5cfe9575f526004601cfd5b806805345cdf77eb68f44c5550506387a211a2600c52815f526020600c208181540181555080602052600c5160601c5f5f51602061098b5f395f51905f52602080a35050565b6387a211a2600c52815f526020600c208054808311156107b35763f4d678b85f526004601cfd5b82900390556805345cdf77eb68f44c805482900390555f8181526001600160a01b0383165f51602061098b5f395f51905f52602083a35050565b602081525f82518060208401525f5b8181101561081957602081860181015160408684010152016107fc565b505f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b038116811461084e575f5ffd5b919050565b5f5f60408385031215610864575f5ffd5b61086d83610838565b946020939093013593505050565b5f5f5f6060848603121561088d575f5ffd5b61089684610838565b92506108a460208501610838565b929592945050506040919091013590565b5f602082840312156108c5575f5ffd5b5035919050565b5f602082840312156108dc575f5ffd5b6108e582610838565b9392505050565b5f5f5f5f5f5f5f60e0888a031215610902575f5ffd5b61090b88610838565b965061091960208901610838565b95506040880135945060608801359350608088013560ff8116811461093c575f5ffd5b9699959850939692959460a0840135945060c09093013592915050565b5f5f6040838503121561096a575f5ffd5b61097383610838565b915061098160208401610838565b9050925092905056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220b565e9b7cf2f891ef66781443f54da80c1e52fa08c82fb36b7135040f7efdd5c64736f6c634300081c0033
Deployed Bytecode
0x608060405260043610610584575f3560e01c806390b6bfc9116102d0578063baeff8a311610181578063d9e0da64116100df578063f04e283e1161008e578063f04e283e1461112e578063f088d54714610bed578063f2fde38b14611141578063f361ed6614611154578063f940c26814611173578063fab7c40914611192578063fee81cf4146111b1575f5ffd5b8063d9e0da6414611049578063d9e69a0514611088578063dd62ed3e1461109d578063e1a45218146110bc578063e753dcdf146110d1578063e783f8af146110f0578063efaf69681461110f575f5ffd5b8063cb67f9481161013b578063cb67f94814610f7a578063cc591db814610f99578063cc952b5714610fb8578063d505accf14610fcd578063d56413ba14610fec578063d65e608d1461100b578063d9b687ad1461102a575f5ffd5b8063baeff8a314610eed578063bff9d78c14610f0c578063c2b941a814610f20578063c393d0e314610f3f578063c399944414610f47578063c94220ab14610f66575f5ffd5b8063a49414ae1161022e578063ac9650d8116101e8578063ac9650d814610de1578063acc7268114610e01578063ae4b77a914610e16578063b1b3b7fb14610e35578063b2a6773514610e60578063ba64983914610e75578063bae6d50614610ece575f5ffd5b8063a49414ae14610d39578063a494f29014610d4e578063a518981014610d63578063a599efd214610d78578063a797385614610da3578063a9059cbb14610dc2575f5ffd5b80639c381b011161028a5780639c381b0114610c6b5780639cefc1da14610c7e5780639e8c708e14610c9d578063a1d0f26c14610cbc578063a25983e514610cdb578063a35d130014610cfa578063a48a9cd314610d1a575f5ffd5b806390b6bfc914610ba357806390edbfee14610bce57806394d5d8e814610bed57806395ced06f14610c0057806395d89b4114610c1f578063997b226914610c4c575f5ffd5b8063402d8883116104355780636271b2e811610393578063715018a611610342578063715018a614610ac95780637896034314610ad15780637ace2ac914610b0b5780637ecebe0014610b1e57806389bba14214610b4f5780638da5cb5b14610b64578063905d907514610b7c575f5ffd5b80636271b2e8146109e5578063647fbcfd146109fa57806369c4916514610a0f5780636b2e6cde14610a2e5780636eabc43214610a4e57806370a0823114610a7957806370f84ba914610aaa575f5ffd5b80634bf2c7c9116103ef5780634bf2c7c91461094c5780634e0cd7991461096b5780634e6fd6c41461098057806350badcb514610995578063548e762f146109aa57806354d1f13d146109c95780635751b361146109d1575f5ffd5b8063402d8883146108bd57806342966c68146108c55780634508ba33146108e457806346051f341461090357806349717a72146109185780634ac8eb5f14610937575f5ffd5b806318160ddd116104e2578063256929621161049c5780632569296214610821578063313ce567146108295780633237c1581461084457806335975a37146108635780633644e5151461086b5780633890b1521461087f578063402b7423146108aa575f5ffd5b806318160ddd146107795780631cfff51b146107955780631e62cc44146107ae5780631f2698ab146107c357806323b872dd146107e35780632567f73114610802575f5ffd5b8063081ce0381161053e578063081ce038146106a0578063095ea7b3146106bf5780630a67ba65146106ee5780630b2b2915146107025780630ecbcdab1461072657806312686f511461074557806317e79db01461075a575f5ffd5b8063024cad3b1461058f578063035b7c4b146105c8578063050ac5fb146105f557806305b7c1c91461061657806306fdde03146106425780630774e00314610663575f5ffd5b3661058b57005b5f5ffd5b34801561059a575f5ffd5b506105ae6105a9366004615880565b6111e2565b604080519283526020830191909152015b60405180910390f35b3480156105d3575f5ffd5b506105e76105e2366004615897565b611221565b6040519081526020016105bf565b348015610600575f5ffd5b5061061461060f366004615880565b61130d565b005b348015610621575f5ffd5b50600e54610635906001600160a01b031681565b6040516105bf91906158b7565b34801561064d575f5ffd5b5061065661149f565b6040516105bf919061590e565b34801561066e575f5ffd5b506105ae61067d36600461593b565b600360209081525f92835260408084209091529082529020805460019091015482565b3480156106ab575f5ffd5b506105e76106ba366004615880565b6114be565b3480156106ca575f5ffd5b506106de6106d936600461596c565b6114ed565b60405190151581526020016105bf565b3480156106f9575f5ffd5b506105e75f5481565b34801561070d575f5ffd5b506009546106359061010090046001600160a01b031681565b348015610731575f5ffd5b50610614610740366004615897565b61156c565b348015610750575f5ffd5b506105e760175481565b348015610765575f5ffd5b506105e7610774366004615880565b611836565b348015610784575f5ffd5b506805345cdf77eb68f44c546105e7565b3480156107a0575f5ffd5b506009546106de9060ff1681565b3480156107b9575f5ffd5b506105e760065481565b3480156107ce575f5ffd5b50600e546106de90600160a01b900460ff1681565b3480156107ee575f5ffd5b506106de6107fd366004615994565b61186a565b34801561080d575f5ffd5b5061061461081c366004615880565b611926565b6106146119c9565b348015610834575f5ffd5b50604051601281526020016105bf565b34801561084f575f5ffd5b5061061461085e366004615880565b611a16565b610614611bb6565b348015610876575f5ffd5b506105e7611d29565b34801561088a575f5ffd5b506105e76108993660046159ce565b60076020525f908152604090205481565b6106146108b8366004615897565b611da5565b61061461215d565b3480156108d0575f5ffd5b506106146108df366004615880565b612334565b3480156108ef575f5ffd5b506106146108fe3660046159f6565b612473565b34801561090e575f5ffd5b506105e760135481565b348015610923575f5ffd5b506105e7610932366004615880565b6124c8565b348015610942575f5ffd5b506105e760105481565b348015610957575f5ffd5b50610614610966366004615880565b6124e8565b348015610976575f5ffd5b506105e76103e881565b34801561098b575f5ffd5b5061063561dead81565b3480156109a0575f5ffd5b506105e7600d5481565b3480156109b5575f5ffd5b506106146109c4366004615880565b6125c8565b6106146129d8565b3480156109dc575f5ffd5b50610614612a11565b3480156109f0575f5ffd5b506105e760015481565b348015610a05575f5ffd5b506105e760125481565b348015610a1a575f5ffd5b50600554610635906001600160a01b031681565b348015610a39575f5ffd5b506009546106de90600160a81b900460ff1681565b348015610a59575f5ffd5b506105e7610a68366004615880565b60156020525f908152604090205481565b348015610a84575f5ffd5b506105e7610a933660046159ce565b6387a211a2600c9081525f91909152602090205490565b348015610ab5575f5ffd5b506106de610ac43660046159ce565b612c62565b610614612c81565b348015610adc575f5ffd5b50610af0610aeb36600461596c565b612c94565b604080519384526020840192909252908201526060016105bf565b6105e7610b19366004615880565b612cfa565b348015610b29575f5ffd5b506105e7610b383660046159ce565b6338377508600c9081525f91909152602090205490565b348015610b5a575f5ffd5b506105e7610dac81565b348015610b6f575f5ffd5b50638b78c6d81954610635565b348015610b87575f5ffd5b5061063573656b95e550c07a9ffe548bd4085c72418ceb1dba81565b348015610bae575f5ffd5b506105e7610bbd366004615880565b60166020525f908152604090205481565b348015610bd9575f5ffd5b50610614610be8366004615880565b612f83565b610614610bfb3660046159ce565b613053565b348015610c0b575f5ffd5b50610af0610c1a3660046159ce565b613095565b348015610c2a575f5ffd5b50604080518082019091526005815264109491505160da1b6020820152610656565b348015610c57575f5ffd5b506105e7610c66366004615880565b6130f9565b610614610c79366004615a57565b613122565b348015610c89575f5ffd5b50610614610c983660046159f6565b613412565b348015610ca8575f5ffd5b50610614610cb73660046159ce565b61345b565b348015610cc7575f5ffd5b506105e7610cd6366004615897565b613500565b348015610ce6575f5ffd5b50610614610cf5366004615a96565b6135a9565b348015610d05575f5ffd5b506009546106de90600160b01b900460ff1681565b348015610d25575f5ffd5b50600a54610635906001600160a01b031681565b348015610d44575f5ffd5b506105e76102b281565b348015610d59575f5ffd5b506105e760025481565b348015610d6e575f5ffd5b506105e7600c5481565b348015610d83575f5ffd5b506105e7610d923660046159ce565b60086020525f908152604090205481565b348015610dae575f5ffd5b50610614610dbd366004615880565b61382b565b348015610dcd575f5ffd5b506106de610ddc36600461596c565b613b12565b610df4610def366004615a57565b613b76565b6040516105bf9190615ac6565b348015610e0c575f5ffd5b506105e7600b5481565b348015610e21575f5ffd5b50610614610e30366004615880565b613b94565b348015610e40575f5ffd5b506105e7610e4f3660046159ce565b60046020525f908152604090205481565b348015610e6b575f5ffd5b506105e760115481565b348015610e80575f5ffd5b50610ebd610e8f3660046159ce565b60146020525f9081526040902080546001820154600283015460038401546004909401549293919290919085565b6040516105bf959493929190615b29565b348015610ed9575f5ffd5b506105e7610ee8366004615880565b613c3e565b348015610ef8575f5ffd5b50610614610f07366004615880565b613c5b565b348015610f17575f5ffd5b50610614613da0565b348015610f2b575f5ffd5b50610614610f3a366004615880565b613e82565b610614613ebf565b348015610f52575f5ffd5b506105e7610f613660046159ce565b61401e565b348015610f71575f5ffd5b506105e7614084565b348015610f85575f5ffd5b50610614610f943660046159ce565b614098565b348015610fa4575f5ffd5b506105e7610fb3366004615880565b614222565b348015610fc3575f5ffd5b506105e7600f5481565b348015610fd8575f5ffd5b50610614610fe7366004615b4c565b61424a565b348015610ff7575f5ffd5b506105e7611006366004615897565b6143fe565b348015611016575f5ffd5b50610614611025366004615880565b614432565b348015611035575f5ffd5b506106146110443660046159ce565b614693565b348015611054575f5ffd5b50611068611063366004615897565b6149de565b6040805194855260208501939093529183015260608201526080016105bf565b348015611093575f5ffd5b506105e76126ac81565b3480156110a8575f5ffd5b506105e76110b736600461593b565b614a49565b3480156110c7575f5ffd5b506105e761271081565b3480156110dc575f5ffd5b506105e76110eb366004615880565b614a8d565b3480156110fb575f5ffd5b5061061461110a3660046159f6565b614ab6565b34801561111a575f5ffd5b506106146111293660046159ce565b614b0b565b61061461113c3660046159ce565b614bbf565b61061461114f3660046159ce565b614bf9565b34801561115f575f5ffd5b5061061461116e3660046159ce565b614c1f565b34801561117e575f5ffd5b5061061461118d366004615bb9565b614c72565b34801561119d575f5ffd5b506106146111ac366004615880565b614ce0565b3480156111bc575f5ffd5b506105e76111cb3660046159ce565b63389a75e1600c9081525f91909152602090205490565b5f5f60155f6111f085614222565b81526020019081526020015f205460165f61120a86614222565b81526020019081526020015f205491509150915091565b5f80670de0b6b3a764000061271061016d8561123f6102b289615bf5565b6112499190615bf5565b61125b90670de0b6b3a7640000615bf5565b6112659190615c20565b61126f9190615c20565b6112799190615c20565b90505f6126ac61128b81612710615c33565b6112959087615bf5565b61129f9190615c20565b90505f612710600c5483886112b49190615c46565b6112be9190615bf5565b6112c89190615c20565b90508181116112dc57829350505050611307565b6112e68282615c33565b83106112f757829350505050611307565b6113018282615c33565b93505050505b92915050565b3068929eee149b4bd21268540361132b5763ab143c065f526004601cfd5b3068929eee149b4bd21268555f811161138b5760405162461bcd60e51b815260206004820181905260248201527f4d757374207265717565737420756e7374616b65206d6f7265207468616e203060448201526064015b60405180910390fd5b6005546040516370a0823160e01b81526001600160a01b03909116906370a08231906113bb9033906004016158b7565b602060405180830381865afa1580156113d6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113fa9190615c59565b8111156114415760405162461bcd60e51b8152602060048201526015602482015274125b9d985b1a59081a509491505108185b5bdd5b9d605a1b6044820152606401611382565b335f818152600760209081526040808320429055600882529182902084905590518381527f828764c21e74c28710e19919735825aba966621c95cbd913f8ed65a2d298f48c910160405180910390a23868929eee149b4bd212685550565b604080518082019091526005815264109c99585960da1b602082015290565b5f5f6114c8614084565b90506114e6836114df6805345cdf77eb68f44c5490565b835f614e3c565b9392505050565b5f6001600160a01b0383166e22d473030f116ddee9f6b43ac78ba3188219151761151e57633f68539a5f526004601cfd5b82602052637f5e9f20600c52335f52816034600c2055815f52602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa350600192915050565b3068929eee149b4bd21268540361158a5763ab143c065f526004601cfd5b3068929eee149b4bd2126855600954600160b01b900460ff166115bf5760405162461bcd60e51b815260040161138290615c70565b61016e81106115e05760405162461bcd60e51b815260040161138290615c9f565b815f036115ff5760405162461bcd60e51b815260040161138290615cec565b61160833612c62565b1561163857335f908152601460205260408120818155600181018290556002810182905560038101829055600401555b335f90815260146020526040902060010154156116a15760405162461bcd60e51b815260206004820152602160248201527f55736520696e637265617365426f72726f7720746f20626f72726f77206d6f726044820152606560f81b6064820152608401611382565b6116a9613da0565b5f6116c5426116bb8462015180615bf5565b610fb39190615c46565b90505f6127106116d76126ac86615bf5565b6116e19190615c20565b90505f6116ee8285611221565b90505f612710611700610dac84615bf5565b61170a9190615c20565b90505f611716876130f9565b6040805160a08101825282815260208082018881528284018a8152606084018c81524260808601908152335f81815260149096529690942094518555915160018501555160028401555160038301555160049091015590915061177a903083614e7e565b6103e8821161179b5760405162461bcd60e51b815260040161138290615d1d565b6117ae336117a98587615c33565b614ee4565b600a546117c4906001600160a01b031683614ee4565b6117cf848287614f84565b6117d883615058565b336001600160a01b03167f212a25b7e6d62ca82a704bf5f0513e2d0840e89914c62639dceabfb40a34491a8888848888604051611819959493929190615b29565b60405180910390a250505050503868929eee149b4bd21268555050565b5f5f61184183613c3e565b9050612710600c546127106118569190615c33565b6118609083615bf5565b6114e69190615c20565b5f8360601b6e22d473030f116ddee9f6b43ac78ba333146118bf5733602052637f5e9f208117600c526034600c2080548019156118bc57808511156118b6576313be252b5f526004601cfd5b84810382555b50505b6387a211a28117600c526020600c208054808511156118e55763f4d678b85f526004601cfd5b84810382555050835f526020600c208381540181555082602052600c5160601c8160601c5f516020615f125f395f51905f52602080a3505060019392505050565b61192e6151e0565b610dac81111561198e5760405162461bcd60e51b815260206004820152602560248201527f504f4c20666565206d757374206265206c657373207468616e206f766572616c6044820152646c2066656560d81b6064820152608401611382565b5f8190556040518181527fe0c2437c53b6eb36e7e7380f81445b2ad7834b91623057dbb49b620ef11a6b64906020015b60405180910390a150565b5f6202a30067ffffffffffffffff164201905063389a75e1600c52335f52806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f5fa250565b3068929eee149b4bd212685403611a345763ab143c065f526004601cfd5b3068929eee149b4bd2126855611a4933612c62565b15611a665760405162461bcd60e51b815260040161138290615d54565b611a6e613da0565b335f9081526014602052604081205490611a8b610ee88484615c33565b9050612710611a9c6126ac83615bf5565b611aa69190615c20565b335f908152601460205260409020600101541115611b115760405162461bcd60e51b815260206004820152602260248201527f526571756972652039392520636f6c6c61746572616c697a6174696f6e207261604482015261746560f01b6064820152608401611382565b335f90815260146020526040902054611b2b908490615c33565b335f81815260146020526040902091909155611b4990309085614e7e565b335f90815260146020526040812060020154611b67919085906151fa565b611b705f615058565b60405183815233907fe25410a4059619c9594dc6f022fe231b02aaea733f689e7ab0cd21b3d4d0eb549060200160405180910390a250503868929eee149b4bd212685550565b611bbe6151e0565b600e54600160a01b900460ff16158015611bd85750601154155b611c245760405162461bcd60e51b815260206004820152601b60248201527f54726164696e6720616c726561647920696e697469616c697a656400000000006044820152606401611382565b346901760cbc623bb350000014611c895760405162461bcd60e51b8152602060048201526024808201527f4d7573742073656e642036393030204245524120746f2073746172742074726160448201526364696e6760e01b6064820152608401611382565b3460118190556040518181525f516020615ef25f395f51905f529060200160405180910390a1611cb93382615265565b611cce3361dead67016345785d8a0000614e7e565b600e805460ff60a01b1916600160a01b1790556009805460ff60b01b1916600160b01b1790556040517f4f366f0dc5cd876e456f089309e0c62fc2bc0e116c6f6ae308c392b4ad45b5b9906119be9060011515815260200190565b5f80611d3361149f565b805190602001209050604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f815260208101929092527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc69082015246606082015230608082015260a09020919050565b3068929eee149b4bd212685403611dc35763ab143c065f526004601cfd5b3068929eee149b4bd2126855600e54600160a01b900460ff16611df85760405162461bcd60e51b815260040161138290615d7d565b600954600160b01b900460ff16611e215760405162461bcd60e51b815260040161138290615c70565b61016e8110611e425760405162461bcd60e51b815260040161138290615c9f565b335f90815260146020908152604091829020825160a0810184528154815260018201549281018390526002820154938101939093526003810154606084015260040154608083015215611f2357611e9833612c62565b15611ec857335f908152601460205260408120818155600181018290556002810182905560038101829055600401555b335f9081526014602052604090206001015415611f235760405162461bcd60e51b8152602060048201526019602482015278557365206163636f756e742077697468206e6f206c6f616e7360381b6044820152606401611382565b611f2b613da0565b5f611f3d426116bb8562015180615bf5565b90505f5f5f5f611f4d88886149de565b93509350935093505f818584611f639190615c46565b611f6d9190615c46565b90505f81341115611f8e57611f828234615c33565b9050611f8e3382614ee4565b81611f998234615c33565b14611fe65760405162461bcd60e51b815260206004820152601a60248201527f496e73756666696369656e742062657261206665652073656e740000000000006044820152606401611382565b5f611ff1878c615c33565b90505f611ffe82856143fe565b905061200a3082615265565b5f612710610dac61201b888c615c46565b6120259190615bf5565b61202f9190615c20565b90506103e881116120525760405162461bcd60e51b815260040161138290615d1d565b600a54612068906001600160a01b031682614ee4565b61207388838c614f84565b6040518060a001604052808381526020018981526020018b81526020018d81526020014281525060145f336001600160a01b03166001600160a01b031681526020019081526020015f205f820151815f0155602082015181600101556040820151816002015560608201518160030155608082015181600401559050506120f98d615058565b336001600160a01b03167feb367e9746d13b4c1364e134d4e5b495b973f2598bcf1c1e477345f66251df7d8e8e858c8a60405161213a959493929190615b29565b60405180910390a250505050505050505050503868929eee149b4bd21268555050565b3068929eee149b4bd21268540361217b5763ab143c065f526004601cfd5b3068929eee149b4bd2126855335f908152601460205260409020600101543481116121f45760405162461bcd60e51b8152602060048201526024808201527f4d757374207265706179206c657373207468616e20626f72726f77656420616d6044820152631bdd5b9d60e21b6064820152608401611382565b345f0361223a5760405162461bcd60e51b81526020600482015260146024820152734d75737420726570617920736f6d657468696e6760601b6044820152606401611382565b612242613da0565b61224b33612c62565b156122ac5760405162461bcd60e51b815260206004820152602b60248201527f596f7572206c6f616e20686173206265656e206c6971756964617465642c206360448201526a616e6e6f7420726570617960a81b6064820152608401611382565b5f6122b73483615c33565b335f90815260146020526040812060018101839055600201549192506122df913491906151fa565b6122e85f615058565b604080513481526020810183905233917f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d091015b60405180910390a250503868929eee149b4bd2126855565b3068929eee149b4bd2126854036123525763ab143c065f526004601cfd5b3068929eee149b4bd2126855612366613da0565b5f61237082613c3e565b905061237c3383615313565b5f612710600c546127106123909190615c33565b61239a9084615bf5565b6123a49190615c20565b90506123b03382614ee4565b5f61271080610dac600c54866123c69190615bf5565b6123d09190615bf5565b6123da9190615c20565b6123e49190615c20565b90506103e881116124075760405162461bcd60e51b815260040161138290615db4565b600a5461241d906001600160a01b031682614ee4565b61242683615058565b604080518381526020810186905233917f49995e5dd6158cf69ad3e9777c46755a1a826a446c6416992167462dad033b2a910160405180910390a25050503868929eee149b4bd212685550565b61247b6151e0565b60098054821515600160b01b0260ff60b01b199091161790556040517fbb614ecd1ed5d1454c15f856743fff55fa975d6257e7262b092a86067ceda764906119be90831515815260200190565b5f5f6124d3836114be565b9050612710600b546127106118569190615c33565b6124f06151e0565b60648110156125415760405162461bcd60e51b815260206004820181905260248201527f6275726e20666565206d7573742062652067726561746572207468616e2031256044820152606401611382565b6101f48111156125935760405162461bcd60e51b815260206004820152601d60248201527f6275726e20666565206d757374206265206c657373207468616e2035250000006044820152606401611382565b600c8190556040518181527fd54296e32811a7f2da2c1f6e8b39cb815ce208595da09bb98b034ccca6dffc69906020016119be565b3068929eee149b4bd2126854036125e65763ab143c065f526004601cfd5b3068929eee149b4bd2126855335f9081526007602052604090205461264d5760405162461bcd60e51b815260206004820152601a60248201527f4d757374207265717565737420756e7374616b652066697273740000000000006044820152606401611382565b335f908152600760205260409020546126699062015180615c46565b4210156126b85760405162461bcd60e51b815260206004820181905260248201527f4d757374207761697420323420686f75727320616674657220726571756573746044820152606401611382565b5f81116127025760405162461bcd60e51b815260206004820152601860248201527704d75737420756e7374616b65206d6f7265207468616e20360441b6044820152606401611382565b335f90815260086020526040902054811461275f5760405162461bcd60e51b815260206004820152601d60248201527f4d75737420756e7374616b652072657175657374656420616d6f756e740000006044820152606401611382565b6005546040516370a0823160e01b815282916001600160a01b0316906370a082319061278f9033906004016158b7565b602060405180830381865afa1580156127aa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127ce9190615c59565b10156128105760405162461bcd60e51b8152602060048201526011602482015270139bdd08195b9bdd59da081a5094915051607a1b6044820152606401611382565b5f6128928260065460055f9054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612868573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061288c9190615c59565b5f614e3c565b90505f81116128f25760405162461bcd60e51b815260206004820152602660248201527f556e7374616b6520616d6f756e7420746f6f20736d616c6c2c20726f756e646560448201526506420746f20360d41b6064820152608401611382565b8060065f8282546129039190615c33565b9091555050600554604051632770a7eb60e21b81526001600160a01b0390911690639dc29fac9061293a9033908690600401615de1565b5f604051808303815f87803b158015612951575f5ffd5b505af1158015612963573d5f5f3e3d5ffd5b50505050612972303383614e7e565b335f818152600760209081526040808320839055600882528083209290925581518481529081018590527f7fc4727e062e336010f2c282598ef5f14facb3de68cf8195c2f23e1454b2b74e91015b60405180910390a2503868929eee149b4bd212685550565b63389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f5fa2565b3068929eee149b4bd212685403612a2f5763ab143c065f526004601cfd5b3068929eee149b4bd2126855612a4433612c62565b15612a825760405162461bcd60e51b815260206004820152600e60248201526d27379030b1ba34bb32903637b0b760911b6044820152606401611382565b612a8a613da0565b335f908152601460205260408120600181015490549091612aaa82613c3e565b9050612ab63083615313565b5f612710600c5483612ac89190615bf5565b612ad29190615c20565b90505f612adf8284615c33565b905084811015612b415760405162461bcd60e51b815260206004820152602760248201527f4e6f7420656e6f75676820636f6c6c61746572616c20746f20636c6f7365207060448201526637b9b4ba34b7b760c91b6064820152608401611382565b5f612b4c8683615c33565b90505f612710612b5e610dac86615bf5565b612b689190615c20565b90508115612b7a57612b7a3383614ee4565b6103e88111612b9b5760405162461bcd60e51b815260040161138290615d1d565b600a54612bb1906001600160a01b031682614ee4565b335f90815260146020526040902060020154612bd090889088906151fa565b335f90815260146020526040812081815560018101829055600281018290556003810182905560040155612c0387615058565b60408051888152602081018890529081018390526060810185905233907fc822db49fe0e413f18fe47901b8b3aaf08de512bdfdea4648f48988032810ffa9060800160405180910390a2505050505050503868929eee149b4bd2126855565b6001600160a01b03165f90815260146020526040902060020154421190565b612c896151e0565b612c925f615374565b565b5f5f5f5f612ca18661401e565b6387a211a2600c9081525f8890526020902054612cbe9190615c46565b9050612cc981613c3e565b9350612710612cda6126ac86615bf5565b612ce49190615c20565b9250612cf08386611221565b9150509250925092565b5f3068929eee149b4bd212685403612d195763ab143c065f526004601cfd5b3068929eee149b4bd2126855600954600160b01b900460ff16612d4e5760405162461bcd60e51b815260040161138290615c70565b335f908152601460205260408120600281015460018201548254600390930154919390929190612d818762015180615bf5565b612d8b9086615c46565b90505f612d988589611221565b9050612da333612c62565b15612dc05760405162461bcd60e51b815260040161138290615d54565b348114612e0f5760405162461bcd60e51b815260206004820152601c60248201527f4c6f616e20657874656e73696f6e2066656520696e636f7272656374000000006044820152606401611382565b5f612710612e1f610dac84615bf5565b612e299190615c20565b90506103e88111612e4c5760405162461bcd60e51b815260040161138290615d1d565b612e54613da0565b600a54612e6a906001600160a01b031682614ee4565b612e758686896151fa565b612e80868685614f84565b335f908152601460205260409020600201839055612e9e848a615c46565b335f9081526014602052604090206003015561016e62015180612ec14286615c33565b612ecb9190615c20565b10612f185760405162461bcd60e51b815260206004820152601b60248201527f4c6f616e206d75737420626520756e64657220333635206461797300000000006044820152606401611382565b612f2134615058565b604080518a8152602081018790529081018790526060810183905233907f7514cf35c34b8a13661f827353b5d69b4c72f13a08e166632a7fcd84d0bbb4069060800160405180910390a2503868929eee149b4bd2126855979650505050505050565b612f8b6151e0565b6012548111612ff05760405162461bcd60e51b815260206004820152602b60248201527f4d617820737570706c79206d7573742062652067726561746572207468616e2060448201526a1d1bdd185b0818985ad95960aa1b6064820152608401611382565b60115481116130315760405162461bcd60e51b815260206004820152600d60248201526c496e637265617365206f6e6c7960981b6044820152606401611382565b60118190556040518181525f516020615ef25f395f51905f52906020016119be565b3068929eee149b4bd2126854036130715763ab143c065f526004601cfd5b3068929eee149b4bd2126855613086816153b1565b3868929eee149b4bd212685550565b6001600160a01b0381165f908152601460205260408120600201548190819042116130e9575050506001600160a01b0381165f908152601460205260409020805460018201546002909201549091906130f2565b505f9150819050805b9193909250565b5f5f613103614084565b90506114e68361311a6805345cdf77eb68f44c5490565b836001614e3c565b3068929eee149b4bd2126854036131405763ab143c065f526004601cfd5b3068929eee149b4bd212685560015434146131975760405162461bcd60e51b8152602060048201526017602482015276125b98dbdc9c9958dd08189bdd5b9d1e48185b5bdd5b9d604a1b6044820152606401611382565b80806131e55760405162461bcd60e51b815260206004820152601d60248201527f4d75737420636c61696d206174206c65617374206f6e6520746f6b656e0000006044820152606401611382565b5f8167ffffffffffffffff8111156131ff576131ff615dfa565b604051908082528060200260200182016040528015613228578160200160208202803683370190505b5090505f5b82811015613331575f85858381811061324857613248615e0e565b905060200201602081019061325d91906159ce565b9050306001600160a01b038216036132b35760405162461bcd60e51b815260206004820152601960248201527843616e6e6f7420636c61696d20425245414420746f6b656e7360381b6044820152606401611382565b5f6132c76001600160a01b0383163061559b565b6001600160a01b0383165f908152600460205260409020549091506132ec8183615c33565b91508115613326578185858151811061330757613307615e0e565b60209081029190910101526133266001600160a01b03841633846155c5565b50505060010161322d565b505f6127105f54346133439190615bf5565b61334d9190615c20565b90506103e88110156133a15760405162461bcd60e51b815260206004820152601d60248201527f466565206d7573742062652067726561746572207468616e20647573740000006044820152606401611382565b600a546133b7906001600160a01b031682614ee4565b600154336001600160a01b03167fbf139079d1f4f0053ac82d22cc6e5769f37a0e5a276efdd4f1c2bb0b979c1d848787866040516133f793929190615e22565b60405180910390a35050503868929eee149b4bd21268555050565b61341a6151e0565b6009805460ff19168215159081179091556040519081527f280cb161002275204369941f9802ee155e35a137f91f719acae275cbed8a5092906020016119be565b6134636151e0565b306001600160a01b038216036134d55760405162461bcd60e51b815260206004820152603160248201527f42726561643a2063616e206f6e6c79207265636f7665722065726332307320746044820152701a185d08185c99481b9bdd08189c995859607a1b6064820152608401611382565b6134fd336134ec6001600160a01b0384163061559b565b6001600160a01b03841691906155c5565b50565b5f8061350d846005615bf5565b90505f61351b856064615bf5565b90505f5b8183101561359f5760026135338385615c46565b61353e906001615c46565b6135489190615c20565b90505f5f5f61355784896149de565b935093505092505f83838361356c9190615c46565b6135769190615c46565b90508981101561358857849650613596565b613593600186615c33565b95505b5050505061351f565b5090949350505050565b3068929eee149b4bd2126854036135c75763ab143c065f526004601cfd5b3068929eee149b4bd212688190556001600160a01b038416036136275760405162461bcd60e51b815260206004820152601860248201527743616e6e6f74206c6f636b20425245414420746f6b656e7360401b6044820152606401611382565b4281116136805760405162461bcd60e51b815260206004820152602160248201527f556e6c6f636b2074696d65206d75737420626520696e207468652066757475726044820152606560f81b6064820152608401611382565b5f82116136cf5760405162461bcd60e51b815260206004820152601d60248201527f416d6f756e74206d7573742062652067726561746572207468616e20300000006044820152606401611382565b335f9081526003602090815260408083206001600160a01b038716845290915290208054156137475780600101548210156137475760405162461bcd60e51b815260206004820152601860248201527743616e6e6f742073686f7274656e206c6f636b2074696d6560401b6044820152606401611382565b5f612710600254856137599190615bf5565b6137639190615c20565b90505f6137708286615c33565b90506137876001600160a01b03871633308861560f565b80835f015f8282546137999190615c46565b9091555050600183018490556001600160a01b0386165f90815260046020526040812080548392906137cc908490615c46565b909155505060408051828152602081018690526001600160a01b0388169133917fd8a1c0afefbfb5f6da58761715434130bcc1b9da12dba1ab5b21e44f5ae4d27e910160405180910390a35050503868929eee149b4bd2126855505050565b3068929eee149b4bd2126854036138495763ab143c065f526004601cfd5b3068929eee149b4bd2126855600954600160b01b900460ff1661387e5760405162461bcd60e51b815260040161138290615c70565b61388733612c62565b156138ce5760405162461bcd60e51b81526020600482015260176024820152764c6f616e20657870697265642075736520626f72726f7760481b6044820152606401611382565b805f036138ed5760405162461bcd60e51b815260040161138290615cec565b6138f5613da0565b335f90815260146020526040812060018101548154600290920154909261391b42614222565b90505f6201518061392c8385615c33565b6139369190615c20565b90505f6127106139486126ac89615bf5565b6139529190615c20565b90505f61395f8284611221565b90505f61396b886130f9565b90505f61397f826127106126ac6001614e3c565b6139899089615c33565b90505f6139958b6130f9565b90505f818310156139af576139aa8383615c33565b6139b1565b5f5b90505f6139be878d615c46565b90505f6139cb838d615c46565b6040805160a08101825291825260208083019485528282018e8152606084018d81524260808601908152335f908152601490945293909220935184559451600184015593516002830155925160038201559151600490920191909155508015613a3957613a39333083614e7e565b5f612710613a49610dac88615bf5565b613a539190615c20565b90506103e88111613a765760405162461bcd60e51b815260040161138290615d1d565b600a54613a8c906001600160a01b031682614ee4565b50613a9b336117a98789615c33565b613aa686828b614f84565b613aaf85615058565b336001600160a01b03167f212a25b7e6d62ca82a704bf5f0513e2d0840e89914c62639dceabfb40a34491a8d89858a8a604051613af0959493929190615b29565b60405180910390a250505050505050505050503868929eee149b4bd212685550565b5f6387a211a2600c52335f526020600c20805480841115613b3a5763f4d678b85f526004601cfd5b83810382555050825f526020600c208281540181555081602052600c5160601c335f516020615f125f395f51905f52602080a350600192915050565b60603415613b82575f5ffd5b611307613b8f8484615668565b6156fa565b613b9c6151e0565b6103e8811115613c095760405162461bcd60e51b815260206004820152603260248201527f546f6b656e206c6f636b657220666565206d757374206265206c657373207468604482015271616e206f7220657175616c20746f2031302560701b6064820152608401611382565b60028190556040518181527f95b18a9a21555a972999713c5cbda80d3b9d0ac264c9165671d99af6b10281a9906020016119be565b5f61130782613c4b614084565b6805345cdf77eb68f44c5461288c565b613c636151e0565b6064811015613cb45760405162461bcd60e51b815260206004820181905260248201527f62616b6520666565206d7573742062652067726561746572207468616e2031256044820152606401611382565b6101f4811115613d065760405162461bcd60e51b815260206004820152601d60248201527f62616b6520666565206d757374206265206c657373207468616e2035250000006044820152606401611382565b600d54811015613d6b5760405162461bcd60e51b815260206004820152602a60248201527f62616b6520666565206d7573742062652067726561746572207468616e206c656044820152697665726167652066656560b01b6064820152608401611382565b600b8190556040518181527f3697c9a523da6c9f5f906cf187ec9eea0872c710b6992b7ddfd76042a658f9d6906020016119be565b5f5f5b426017541015613e01576017545f90815260166020526040902054613dc89082615c46565b6017545f90815260156020526040902054909150613de69083615c46565b915060175462015180613df99190615c46565b601755613da3565b8015613e225780601054613e159190615c33565b601055613e223082615313565b8115613e7e5781600f54613e369190615c33565b600f55601754613e4a906201518090615c33565b6040518381527fb40747442264847104813679bf15dc7a9df6e2b499cfbd3cad9134d6a8261a149060200160405180910390a25b5050565b613e8a6151e0565b60018190556040518181527f5964e4694d5d7de1567d5bb0ca59699503fec37ed4eafc36acbca2afb9a337e1906020016119be565b3068929eee149b4bd212685403613edd5763ab143c065f526004601cfd5b3068929eee149b4bd2126855335f818152601460205260409020600181015490549091613f0990612c62565b15613f265760405162461bcd60e51b815260040161138290615d54565b348214613f805760405162461bcd60e51b815260206004820152602260248201527f4d7573742072657475726e20656e7469726520626f72726f77656420616d6f756044820152611b9d60f21b6064820152608401611382565b613f88613da0565b613f93303383614e7e565b335f90815260146020526040902060020154613fb290839083906151fa565b335f908152601460205260408120818155600181018290556002810182905560038101829055600401819055613fe790615058565b604080513481525f602082015233917f77c6871227e5d2dec8dadd5354f78453203e22e669cd0ec4c19d9a8c5edb31d0910161231c565b5f61402882612c62565b1561403457505f919050565b6001600160a01b0382165f9081526014602052604081208054600190910154909161405e826130f9565b9050614071816127106126ac6001614e3c565b61407b9084615c33565b95945050505050565b5f600f54476140939190615c46565b905090565b3068929eee149b4bd2126854036140b65763ab143c065f526004601cfd5b3068929eee149b4bd2126855335f9081526003602090815260408083206001600160a01b0385168452909152902080546141255760405162461bcd60e51b815260206004820152601060248201526f139bc81d1bdad95b9cc81b1bd8dad95960821b6044820152606401611382565b80600101544210156141735760405162461bcd60e51b8152602060048201526017602482015276155b9b1bd8dac81d1a5b59481b9bdd081c995858da1959604a1b6044820152606401611382565b8054335f9081526003602090815260408083206001600160a01b038716845282528083208381556001018390556004909152812080548392906141b7908490615c33565b909155506141d190506001600160a01b03841633836155c5565b6040518181526001600160a01b0384169033907f549f3836aa79a43ac740f9814586c8b7ab5e0d299ea11ac017c6d889704962ae9060200160405180910390a350503868929eee149b4bd212685550565b5f806142316201518084615ea9565b61423b9084615c33565b90506114e68162015180615c46565b6001600160a01b0386166e22d473030f116ddee9f6b43ac78ba3188519151761427a57633f68539a5f526004601cfd5b5f61428361149f565b8051906020012090507fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6428610156142c257631a15a3cc5f526004601cfd5b6040518960601b60601c99508860601b60601c985065383775081901600e52895f526020600c2080547f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835284602084015283604084015246606084015230608084015260a08320602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983528b60208401528a60408401528960608401528060808401528860a084015260c08320604e526042602c205f528760ff16602052866040528560605260208060805f60015afa8c3d51146143aa5763ddafbaef5f526004601cfd5b0190556303faf4f960a51b89176040526034602c20889055888a7f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925602060608501a360405250505f60605250505050505050565b5f5f82614409614084565b6144139190615c33565b905061442a846114df6805345cdf77eb68f44c5490565b949350505050565b3068929eee149b4bd2126854036144505763ab143c065f526004601cfd5b3068929eee149b4bd212685560095460ff166144a45760405162461bcd60e51b815260206004820152601360248201527214dd185ada5b99c81a5cc8191a5cd8589b1959606a1b6044820152606401611382565b5f81116144ec5760405162461bcd60e51b815260206004820152601660248201527504d757374207374616b65206d6f7265207468616e20360541b6044820152606401611382565b6144f7333083614e7e565b5f6006545f14614587576145828260055f9054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614555573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906145799190615c59565b6006545f614e3c565b614589565b815b90505f81116145e65760405162461bcd60e51b8152602060048201526024808201527f5374616b6520616d6f756e7420746f6f20736d616c6c2c20726f756e646564206044820152630746f20360e41b6064820152608401611382565b8160065f8282546145f79190615c46565b90915550506005546040516340c10f1960e01b81526001600160a01b03909116906340c10f199061462e9033908590600401615de1565b5f604051808303815f87803b158015614645575f5ffd5b505af1158015614657573d5f5f3e3d5ffd5b505060408051858152602081018590523393507f1449c6dd7851abc30abf37f57715f492010519147cc2652fbc38202c18a6ee909250016129c0565b3068929eee149b4bd2126854036146b15763ab143c065f526004601cfd5b3068929eee149b4bd2126855600954600160a81b900460ff1661470c5760405162461bcd60e51b8152602060048201526013602482015272109c99585911d5081b9bdd08195b98589b1959606a1b6044820152606401611382565b600a545f906147399073656b95e550c07a9ffe548bd4085c72418ceb1dba906001600160a01b031661559b565b600a54604051636b09169560e01b81523360048201526001600160a01b0391821660248201529192505f9190841690636b091695906044016020604051808303815f875af115801561478d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906147b19190615c59565b90505f81116147f75760405162461bcd60e51b81526020600482015260126024820152714e6f2072657761726420746f20636c61696d60701b6044820152606401611382565b600a545f906148249073656b95e550c07a9ffe548bd4085c72418ceb1dba906001600160a01b031661559b565b90506148308382615c33565b82146148945760405162461bcd60e51b815260206004820152602d60248201527f496e636f72726563742072657761726420616d6f756e742c20736f6d6574686960448201526c6e672077656e742077726f6e6760981b6064820152608401611382565b5f6127105f54846148a59190615bf5565b6148af9190615c20565b60095490915061010090046001600160a01b03166340c10f19336148d38487615c33565b6040518363ffffffff1660e01b81526004016148f0929190615de1565b5f604051808303815f87803b158015614907575f5ffd5b505af1158015614919573d5f5f3e3d5ffd5b505050505f81111561498e57600954600a546040516340c10f1960e01b81526001600160a01b036101009093048316926340c10f1992614960929116908590600401615de1565b5f604051808303815f87803b158015614977575f5ffd5b505af1158015614989573d5f5f3e3d5ffd5b505050505b337f4bed93727626c2e5cdf79a4c7fe79a32aa12a558d20f90499442329be658f2066149ba8386615c33565b60405190815260200160405180910390a2505050503868929eee149b4bd212685550565b5f5f5f5f612710600d54876149f39190615bf5565b6149fd9190615c20565b93505f614a0a8588615c33565b9050612710614a1b6126ac83615bf5565b614a259190615c20565b9350614a318482615c33565b9250614a3d8487611221565b91505092959194509250565b5f6e22d473030f116ddee9f6b43ac78ba2196001600160a01b03831601614a7257505f19611307565b50602052637f5e9f20600c9081525f91909152603490205490565b5f61130782614aa36805345cdf77eb68f44c5490565b84614aac614084565b61288c9190615c33565b614abe6151e0565b60098054821515600160a81b0260ff60a81b199091161790556040517f0933b1cd1180593a9418d9d5d8a1b4449821a5b897c0ab543c7803bc6ef8d953906119be90831515815260200190565b614b136151e0565b6001600160a01b038116614b745760405162461bcd60e51b815260206004820152602260248201527f43616e27742073657420666565206164647265737320746f2030206164647265604482015261737360f01b6064820152608401611382565b600a80546001600160a01b0319166001600160a01b0383161790556040517f6cfe0497f5f7529f70577cf6199ec6c0b7485396857cd7b90d301b3728d10ca3906119be9083906158b7565b614bc76151e0565b63389a75e1600c52805f526020600c208054421115614bed57636f5e88185f526004601cfd5b5f90556134fd81615374565b614c016151e0565b8060601b614c1657637448fbae5f526004601cfd5b6134fd81615374565b614c276151e0565b600e80546001600160a01b0319166001600160a01b0383161790556040517f7de48a8d85b718057dc36cdcb027221944b0682f5ffeb60bf84644d038455ced906119be9083906158b7565b614c7a6151e0565b60055460405163cf456ae760e01b81526001600160a01b03848116600483015283151560248301529091169063cf456ae7906044015f604051808303815f87803b158015614cc6575f5ffd5b505af1158015614cd8573d5f5f3e3d5ffd5b505050505050565b614ce86151e0565b6032811015614d485760405162461bcd60e51b815260206004820152602660248201527f6c6576657261676520666565206d7573742062652067726561746572207468616044820152656e20302e352560d01b6064820152608401611382565b60fa811115614da55760405162461bcd60e51b815260206004820152602360248201527f6c6576657261676520666565206d757374206265206c657373207468616e20326044820152622e352560e81b6064820152608401611382565b600b54811115614e075760405162461bcd60e51b815260206004820152602760248201527f6c6576657261676520666565206d757374206265206c657373207468616e2062604482015266616b652066656560c81b6064820152608401611382565b600d8190556040518181527fd4e469371f09a592c9c4fde36ed11fd123a8cc55d93bd2920ea6ad544dff4395906020016119be565b5f614e69614e498361570b565b8015614e6457505f8480614e5f57614e5f615c0c565b868809115b151590565b614e74868686615737565b61407b9190615c46565b8260601b6387a211a28117600c526020600c20805480841115614ea85763f4d678b85f526004601cfd5b83810382555050825f526020600c208281540181555081602052600c5160601c8160601c5f516020615f125f395f51905f52602080a350505050565b6001600160a01b038216614f345760405162461bcd60e51b815260206004820152601760248201527643616e27742073656e6420746f2030206164647265737360481b6044820152606401611382565b614f476001600160a01b038316826157ed565b7ff6858a8e34dd017aae7934951c7fbd3129bde39de42369d53b17ff174e3ff10d8282604051614f78929190615de1565b60405180910390a15050565b5f81815260166020526040902054614f9d908390615c46565b5f82815260166020908152604080832093909355601590522054614fc2908490615c46565b5f82815260156020526040902055600f54614fde908490615c46565b600f55601054614fef908390615c46565b60108190555f82815260166020908152604080832054601583529281902054600f5482519485529284015282015260608101919091527faec00f5213a37254bc68a26b0685d1a5b2bf513e1e587c94c7df7f4a62b56c9c906080015b60405180910390a1505050565b5f61506a6805345cdf77eb68f44c5490565b615072614084565b61508490670de0b6b3a7640000615bf5565b61508e9190615c20565b90505f6150ac306387a211a2600c9081525f91909152602090205490565b90506010548110156151405760405162461bcd60e51b815260206004820152605160248201527f5468652062726561642062616c616e6365206f662074686520636f6e7472616360448201527f74206d7573742062652067726561746572207468616e206f7220657175616c206064820152701d1bc81d1a194818dbdb1b185d195c985b607a1b608482015260a401611382565b81601354111561519d5760405162461bcd60e51b815260206004820152602260248201527f546865207072696365206f662062726561642063616e6e6f7420646563726561604482015261736560f01b6064820152608401611382565b601382905560408051428152602081018490529081018490527f15819dd2fd9f6418b142e798d08a18d0bf06ea368f4480b7b0d3f75bd966bc489060600161504b565b638b78c6d819543314612c92576382b429005f526004601cfd5b5f81815260166020526040902054615213908390615c33565b5f82815260166020908152604080832093909355601590522054615238908490615c33565b5f82815260156020526040902055600f54615254908490615c33565b600f55601054614fef908390615c33565b6001600160a01b0382166152b55760405162461bcd60e51b815260206004820152601760248201527643616e2774206d696e7420746f2030206164647265737360481b6044820152606401611382565b806012546152c39190615c46565b601281905560115410156153095760405162461bcd60e51b815260206004820152600d60248201526c1393c81353d491481094915051609a1b6044820152606401611382565b613e7e8282615806565b6387a211a2600c52815f526020600c2080548083111561533a5763f4d678b85f526004601cfd5b82900390556805345cdf77eb68f44c805482900390555f8181526001600160a01b0383165f516020615f125f395f51905f52602083a35050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a355565b6153b9613da0565b600e54600160a01b900460ff166153e25760405162461bcd60e51b815260040161138290615d7d565b6001600160a01b0381166154385760405162461bcd60e51b815260206004820152601c60248201527f52656365697665722063616e6e6f7420626520302061646472657373000000006044820152606401611382565b5f61544234614a8d565b90505f612710600b546127106154589190615c33565b6154629084615bf5565b61546c9190615c20565b600e549091506001600160a01b031633036154d157601154816012546154929190615c46565b11156154d1575f816011546154a79190615c46565b60118190556040518181529091505f516020615ef25f395f51905f529060200160405180910390a1505b6154db8382615265565b5f61271080610dac600b54346154f19190615bf5565b6154fb9190615bf5565b6155059190615c20565b61550f9190615c20565b90506103e881116155325760405162461bcd60e51b815260040161138290615db4565b600a54615548906001600160a01b031682614ee4565b61555134615058565b60408051348152602081018490526001600160a01b038616917f96c59bac7680c77217510ed14bc725bbca570081c194f20c9fad3fa3b349ca43910160405180910390a250505050565b5f816014526370a0823160601b5f5260208060246010865afa601f3d111660205102905092915050565b816014528060345263a9059cbb60601b5f5260205f604460105f875af18060015f51141661560557803d853b151710615605576390b8ec185f526004601cfd5b505f603452505050565b60405181606052826040528360601b602c526323b872dd60601b600c5260205f6064601c5f895af18060015f51141661565a57803d873b15171061565a57637939f4245f526004601cfd5b505f60605260405250505050565b5f60405190506020815281816020015280604001808360051b8086843782018085156156e2575b8351870180356020820183375f38823584305af46156af573d5f873e3d86fd5b5082810384523d815260209384019381013d5f823e5f3d8201523d01601f0167ffffffffffffffe016905081841061568f575b806040528485820360401b1794505050505092915050565b8060401c8167ffffffffffffffff16f35b5f600282600381111561572057615720615ebc565b61572a9190615ed0565b60ff166001149050919050565b5f838302815f1985870982811083820303915050805f0361576b5783828161576157615761615c0c565b04925050506114e6565b80841161578257615782600385150260111861586f565b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f385f3884865af1613e7e5763b12d13eb5f526004601cfd5b6805345cdf77eb68f44c54818101818110156158295763e5cfe9575f526004601cfd5b806805345cdf77eb68f44c5550506387a211a2600c52815f526020600c208181540181555080602052600c5160601c5f5f516020615f125f395f51905f52602080a35050565b634e487b715f52806020526024601cfd5b5f60208284031215615890575f5ffd5b5035919050565b5f5f604083850312156158a8575f5ffd5b50508035926020909101359150565b6001600160a01b0391909116815260200190565b5f81518084525f5b818110156158ef576020818501810151868301820152016158d3565b505f602082860101526020601f19601f83011685010191505092915050565b602081525f6114e660208301846158cb565b80356001600160a01b0381168114615936575f5ffd5b919050565b5f5f6040838503121561594c575f5ffd5b61595583615920565b915061596360208401615920565b90509250929050565b5f5f6040838503121561597d575f5ffd5b61598683615920565b946020939093013593505050565b5f5f5f606084860312156159a6575f5ffd5b6159af84615920565b92506159bd60208501615920565b929592945050506040919091013590565b5f602082840312156159de575f5ffd5b6114e682615920565b80358015158114615936575f5ffd5b5f60208284031215615a06575f5ffd5b6114e6826159e7565b5f5f83601f840112615a1f575f5ffd5b50813567ffffffffffffffff811115615a36575f5ffd5b6020830191508360208260051b8501011115615a50575f5ffd5b9250929050565b5f5f60208385031215615a68575f5ffd5b823567ffffffffffffffff811115615a7e575f5ffd5b615a8a85828601615a0f565b90969095509350505050565b5f5f5f60608486031215615aa8575f5ffd5b615ab184615920565b95602085013595506040909401359392505050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b82811015615b1d57603f19878603018452615b088583516158cb565b94506020938401939190910190600101615aec565b50929695505050505050565b948552602085019390935260408401919091526060830152608082015260a00190565b5f5f5f5f5f5f5f60e0888a031215615b62575f5ffd5b615b6b88615920565b9650615b7960208901615920565b95506040880135945060608801359350608088013560ff81168114615b9c575f5ffd5b9699959850939692959460a0840135945060c09093013592915050565b5f5f60408385031215615bca575f5ffd5b615bd383615920565b9150615963602084016159e7565b634e487b7160e01b5f52601160045260245ffd5b808202811582820484141761130757611307615be1565b634e487b7160e01b5f52601260045260245ffd5b5f82615c2e57615c2e615c0c565b500490565b8181038181111561130757611307615be1565b8082018082111561130757611307615be1565b5f60208284031215615c69575f5ffd5b5051919050565b602080825260159082015274109bdc9c9bddda5b99c81a5cc8191a5cd8589b1959605a1b604082015260600190565b6020808252602d908201527f4d617820626f72726f772f657874656e73696f6e206d7573742062652033363560408201526c2064617973206f72206c65737360981b606082015260800190565b60208082526017908201527604d75737420626f72726f77206d6f7265207468616e203604c1b604082015260600190565b6020808252601d908201527f46656573206d75737420626520686967686572207468616e2064757374000000604082015260600190565b6020808252600f908201526e4e6f20616374697665206c6f616e7360881b604082015260600190565b6020808252601b908201527f54726164696e67206d75737420626520696e697469616c697a65640000000000604082015260600190565b60208082526013908201527236bab9ba103a3930b2329037bb32b91036b4b760691b604082015260600190565b6001600160a01b03929092168252602082015260400190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b604080825281018390525f8460608301825b86811015615e62576001600160a01b03615e4d84615920565b16825260209283019290910190600101615e34565b50838103602080860191909152855180835291810192508501905f5b81811015615e9c578251845260209384019390920191600101615e7e565b5091979650505050505050565b5f82615eb757615eb7615c0c565b500690565b634e487b7160e01b5f52602160045260245ffd5b5f60ff831680615ee257615ee2615c0c565b8060ff8416069150509291505056febaea970d90a83e1a788fb13b7d7d5dd6acdc45f67728ba650b5b69a496015344ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220b476cf6d62f153cc72a1a00274ed8c8965a01137f48476fe684a779d688ea4ef64736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
BERA | 100.00% | $3.72 | 428,126.4698 | $1,590,502.96 |
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.