Source Code
More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 1,375 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Withdraw Fees | 24336794 | 20 days ago | IN | 0 ETH | 0.00002162 | ||||
| Withdraw Fees | 24286611 | 27 days ago | IN | 0 ETH | 0.0000194 | ||||
| Remove Collatera... | 24188877 | 40 days ago | IN | 0 ETH | 0.00050527 | ||||
| Repay | 24188873 | 40 days ago | IN | 0 ETH | 0.00022084 | ||||
| Withdraw Fees | 24186245 | 41 days ago | IN | 0 ETH | 0.00001424 | ||||
| Withdraw Fees | 24136056 | 48 days ago | IN | 0 ETH | 0.00001298 | ||||
| Withdraw Fees | 24085925 | 55 days ago | IN | 0 ETH | 0.00001091 | ||||
| Remove Collatera... | 24056168 | 59 days ago | IN | 0 ETH | 0.00002205 | ||||
| Repay With Colla... | 24056003 | 59 days ago | IN | 0 ETH | 0.00003639 | ||||
| Withdraw Fees | 24035777 | 62 days ago | IN | 0 ETH | 0.0000127 | ||||
| Get Reward | 24002391 | 66 days ago | IN | 0 ETH | 0.00001628 | ||||
| Repay With Colla... | 23993224 | 67 days ago | IN | 0 ETH | 0.00014072 | ||||
| Repay With Colla... | 23985777 | 68 days ago | IN | 0 ETH | 0.00023946 | ||||
| Withdraw Fees | 23985734 | 69 days ago | IN | 0 ETH | 0.00006126 | ||||
| Remove Collatera... | 23953468 | 73 days ago | IN | 0 ETH | 0.00002344 | ||||
| Remove Collatera... | 23946783 | 74 days ago | IN | 0 ETH | 0.000028 | ||||
| Withdraw Fees | 23936311 | 76 days ago | IN | 0 ETH | 0.00001028 | ||||
| Remove Collatera... | 23930003 | 76 days ago | IN | 0 ETH | 0.00003113 | ||||
| Remove Collatera... | 23925560 | 77 days ago | IN | 0 ETH | 0.00197426 | ||||
| Remove Collatera... | 23911837 | 79 days ago | IN | 0 ETH | 0.00003852 | ||||
| Remove Collatera... | 23888333 | 82 days ago | IN | 0 ETH | 0.00005156 | ||||
| Remove Collatera... | 23887514 | 82 days ago | IN | 0 ETH | 0.00005153 | ||||
| Withdraw Fees | 23786582 | 97 days ago | IN | 0 ETH | 0.0000336 | ||||
| Remove Collatera... | 23772546 | 98 days ago | IN | 0 ETH | 0.00013591 | ||||
| Remove Collatera... | 23758494 | 100 days ago | IN | 0 ETH | 0.00006493 |
Latest 2 internal transactions
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60a03460 | 22034916 | 341 days ago | Contract Creation | 0 ETH | |||
| 0x6101a080 | 22034916 | 341 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xD210Bc75...598e1E42b The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
ResupplyPair
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
/**
* @title ResupplyPair
* @notice Based on code from Drake Evans and Frax Finance's lending pair contract (https://github.com/FraxFinance/fraxlend), adapted for Resupply Finance
*/
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { ResupplyPairConstants } from "./pair/ResupplyPairConstants.sol";
import { ResupplyPairCore } from "./pair/ResupplyPairCore.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { VaultAccount, VaultAccountingLibrary } from "../libraries/VaultAccount.sol";
import { IRateCalculator } from "../interfaces/IRateCalculator.sol";
import { ISwapper } from "../interfaces/ISwapper.sol";
import { IFeeDeposit } from "../interfaces/IFeeDeposit.sol";
import { IResupplyRegistry } from "../interfaces/IResupplyRegistry.sol";
import { IConvexStaking } from "../interfaces/IConvexStaking.sol";
import { EpochTracker } from "../dependencies/EpochTracker.sol";
contract ResupplyPair is ResupplyPairCore, EpochTracker {
using VaultAccountingLibrary for VaultAccount;
using SafeERC20 for IERC20;
using SafeCast for uint256;
uint256 public lastFeeEpoch;
address public constant CRV = 0xD533a949740bb3306d119CC777fa900bA034cd52;
address public constant CVX = 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B;
// Staking Info
address public immutable convexBooster;
uint256 public convexPid;
error FeesAlreadyDistributed();
error IncorrectStakeBalance();
/// @param _core Core contract address
/// @param _configData config data
/// @param _immutables immutable data
/// @param _customConfigData extras
constructor(
address _core,
bytes memory _configData,
bytes memory _immutables,
bytes memory _customConfigData
) ResupplyPairCore(_core, _configData, _immutables, _customConfigData) EpochTracker(_core) {
(, address _govToken, address _convexBooster, uint256 _convexpid) = abi.decode(
_customConfigData,
(string, address, address, uint256)
);
//add gov token rewards
_insertRewardToken(_govToken);
//convex info
if(_convexBooster != address(0)){
convexBooster = _convexBooster;
convexPid = _convexpid;
//approve
collateral.forceApprove(convexBooster, type(uint256).max);
//add rewards for curve staking
_insertRewardToken(CRV);
_insertRewardToken(CVX);
emit SetConvexPool(_convexpid);
}
}
// ============================================================================================
// Functions: Helpers
// ============================================================================================
function getConstants()
external
pure
returns (
uint256 _LTV_PRECISION,
uint256 _LIQ_PRECISION,
uint256 _EXCHANGE_PRECISION,
uint256 _RATE_PRECISION
)
{
_LTV_PRECISION = LTV_PRECISION;
_LIQ_PRECISION = LIQ_PRECISION;
_EXCHANGE_PRECISION = EXCHANGE_PRECISION;
_RATE_PRECISION = RATE_PRECISION;
}
/// @notice The ```getUserSnapshot``` function gets user level accounting data
/// @param _address The user address
/// @return _borrowShares The user borrow shares
/// @return _collateralBalance The user collateral balance
function getUserSnapshot(
address _address
) external returns (uint256 _borrowShares, uint256 _collateralBalance) {
_collateralBalance = userCollateralBalance(_address);
_borrowShares = userBorrowShares(_address);
}
/// @notice The ```getPairAccounting``` function gets all pair level accounting numbers
/// @return _claimableFees Total claimable fees
/// @return _totalBorrowAmount Total borrows
/// @return _totalBorrowShares Total borrow shares
/// @return _totalCollateral Total collateral
function getPairAccounting()
external
view
returns (
uint256 _claimableFees,
uint128 _totalBorrowAmount,
uint128 _totalBorrowShares,
uint256 _totalCollateral
)
{
VaultAccount memory _totalBorrow;
(, , _claimableFees, _totalBorrow) = previewAddInterest();
_totalBorrowAmount = _totalBorrow.amount;
_totalBorrowShares = _totalBorrow.shares;
_totalCollateral = totalCollateral();
}
/// @notice The ```toBorrowShares``` function converts a given amount of borrow debt into the number of shares
/// @param _amount Amount of borrow
/// @param _roundUp Whether to roundup during division
/// @param _previewInterest Whether to simulate interest accrual
/// @return _shares The number of shares
function toBorrowShares(
uint256 _amount,
bool _roundUp,
bool _previewInterest
) external view returns (uint256 _shares) {
if (_previewInterest) {
(, , , VaultAccount memory _totalBorrow) = previewAddInterest();
_shares = _totalBorrow.toShares(_amount, _roundUp);
} else {
_shares = totalBorrow.toShares(_amount, _roundUp);
}
}
/// @notice The ```toBorrowAmount``` function converts a given amount of borrow debt into the number of shares
/// @param _shares Shares of borrow
/// @param _roundUp Whether to roundup during division
/// @param _previewInterest Whether to simulate interest accrual
/// @return _amount The amount of asset
function toBorrowAmount(
uint256 _shares,
bool _roundUp,
bool _previewInterest
) external view returns (uint256 _amount) {
if (_previewInterest) {
(, , , VaultAccount memory _totalBorrow) = previewAddInterest();
_amount = _totalBorrow.toAmount(_shares, _roundUp);
} else {
_amount = totalBorrow.toAmount(_shares, _roundUp);
}
}
// ============================================================================================
// Functions: Configuration
// ============================================================================================
/// @notice The ```SetOracleInfo``` event is emitted when the oracle info is set
/// @param oldOracle The old oracle address
/// @param newOracle The new oracle address
event SetOracleInfo(
address oldOracle,
address newOracle
);
/// @notice The ```setOracleInfo``` function sets the oracle data
/// @param _newOracle The new oracle address
function setOracle(address _newOracle) external onlyOwner{
ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;
emit SetOracleInfo(
_exchangeRateInfo.oracle,
_newOracle
);
_exchangeRateInfo.oracle = _newOracle;
exchangeRateInfo = _exchangeRateInfo;
}
/// @notice The ```SetMaxLTV``` event is emitted when the max LTV is set
/// @param oldMaxLTV The old max LTV
/// @param newMaxLTV The new max LTV
event SetMaxLTV(uint256 oldMaxLTV, uint256 newMaxLTV);
/// @notice The ```setMaxLTV``` function sets the max LTV
/// @param _newMaxLTV The new max LTV
function setMaxLTV(uint256 _newMaxLTV) external onlyOwner{
if (_newMaxLTV > LTV_PRECISION) revert InvalidParameter();
emit SetMaxLTV(maxLTV, _newMaxLTV);
maxLTV = _newMaxLTV;
}
/// @notice The ```SetRateCalculator``` event is emitted when the rate contract is set
/// @param oldRateCalculator The old rate contract
/// @param newRateCalculator The new rate contract
event SetRateCalculator(address oldRateCalculator, address newRateCalculator);
/// @notice The ```setRateCalculator``` function sets the rate contract address
/// @param _newRateCalculator The new rate contract address
/// @param _updateInterest Whether to update interest before setting new rate calculator
function setRateCalculator(address _newRateCalculator, bool _updateInterest) external onlyOwner{
//should add interest before changing rate calculator
//however if there is an intrinsic problem with the current rate calculate, need to be able
//to update without calling addInterest
if(_updateInterest){
_addInterest();
}
emit SetRateCalculator(address(rateCalculator), _newRateCalculator);
rateCalculator = IRateCalculator(_newRateCalculator);
}
/// @notice The ```SetLiquidationFees``` event is emitted when the liquidation fees are set
/// @param oldLiquidationFee The old clean liquidation fee
/// @param newLiquidationFee The new clean liquidation fee
event SetLiquidationFees(
uint256 oldLiquidationFee,
uint256 newLiquidationFee
);
/// @notice The ```setLiquidationFees``` function sets the liquidation fees
/// @param _newLiquidationFee The new clean liquidation fee
function setLiquidationFees(
uint256 _newLiquidationFee
) external onlyOwner{
if (_newLiquidationFee > LIQ_PRECISION) revert InvalidParameter();
emit SetLiquidationFees(
liquidationFee,
_newLiquidationFee
);
liquidationFee = _newLiquidationFee;
}
/// @notice The ```SetMintFees``` event is emitted when the liquidation fees are set
/// @param oldMintFee The old mint fee
/// @param newMintFee The new mint fee
event SetMintFees(
uint256 oldMintFee,
uint256 newMintFee
);
/// @notice The ```setMintFees``` function sets the mint
/// @param _newMintFee The new mint fee
function setMintFees(
uint256 _newMintFee
) external onlyOwner{
emit SetMintFees(
mintFee,
_newMintFee
);
mintFee = _newMintFee;
}
function setBorrowLimit(uint256 _limit) external onlyOwner{
_setBorrowLimit(_limit);
}
/// @notice The ```SetBorrowLimit``` event is emitted when the borrow limit is set
/// @param limit The new borrow limit
event SetBorrowLimit(uint256 limit);
function _setBorrowLimit(uint256 _limit) internal {
if(_limit > type(uint128).max){
revert InvalidParameter();
}
borrowLimit = _limit;
emit SetBorrowLimit(_limit);
}
event SetMinimumRedemption(uint256 min);
function setMinimumRedemption(uint256 _min) external onlyOwner{
if(_min < 100 * PAIR_DECIMALS ){
revert InvalidParameter();
}
minimumRedemption = _min;
emit SetMinimumRedemption(_min);
}
event SetMinimumLeftover(uint256 min);
function setMinimumLeftoverDebt(uint256 _min) external onlyOwner{
minimumLeftoverDebt = _min;
emit SetMinimumLeftover(_min);
}
event SetMinimumBorrowAmount(uint256 min);
function setMinimumBorrowAmount(uint256 _min) external onlyOwner{
minimumBorrowAmount = _min;
emit SetMinimumBorrowAmount(_min);
}
event SetProtocolRedemptionFee(uint256 fee);
/// @notice Sets the redemption fee percentage for this specific pair
/// @dev The fee is 1e18 precision (1e16 = 1%) and taken from redemptions and sent to the protocol.
/// @param _fee The new redemption fee percentage. Must be less than or equal to 1e18 (100%)
function setProtocolRedemptionFee(uint256 _fee) external onlyOwner{
if(_fee > EXCHANGE_PRECISION) revert InvalidParameter();
protocolRedemptionFee = _fee;
emit SetProtocolRedemptionFee(_fee);
}
/// @notice The ```WithdrawFees``` event fires when the fees are withdrawn
/// @param recipient To whom the assets were sent
/// @param interestFees the amount of interest based fees claimed
/// @param otherFees the amount of other fees claimed(mint/redemption)
event WithdrawFees(address recipient, uint256 interestFees, uint256 otherFees);
/// @notice The ```withdrawFees``` function withdraws fees accumulated
/// @return _fees the amount of interest based fees claimed
/// @return _otherFees the amount of other fees claimed(mint/redemption)
function withdrawFees() external nonReentrant returns (uint256 _fees, uint256 _otherFees) {
// Accrue interest if necessary
_addInterest();
//get deposit contract
address feeDeposit = IResupplyRegistry(registry).feeDeposit();
uint256 lastDistributedEpoch = IFeeDeposit(feeDeposit).lastDistributedEpoch();
uint256 currentEpoch = getEpoch();
//current epoch must be greater than last claimed epoch
//current epoch must be equal to the FeeDeposit prev distributed epoch (FeeDeposit must distribute first)
if(currentEpoch <= lastFeeEpoch || currentEpoch != lastDistributedEpoch){
revert FeesAlreadyDistributed();
}
lastFeeEpoch = currentEpoch;
//get fees and clear
_fees = claimableFees;
_otherFees = claimableOtherFees;
claimableFees = 0;
claimableOtherFees = 0;
//mint new stables to the receiver
IResupplyRegistry(registry).mint(feeDeposit,_fees+_otherFees);
//inform deposit contract of this pair's contribution
IFeeDeposit(feeDeposit).incrementPairRevenue(_fees,_otherFees);
emit WithdrawFees(feeDeposit, _fees, _otherFees);
}
/// @notice The ```SetSwapper``` event fires whenever a swapper is black or whitelisted
/// @param swapper The swapper address
/// @param approval The approval
event SetSwapper(address swapper, bool approval);
/// @notice The ```setSwapper``` function is called to black or whitelist a given swapper address
/// @dev
/// @param _swapper The swapper address
/// @param _approval The approval
function setSwapper(address _swapper, bool _approval) external{
if(msg.sender == owner() || msg.sender == registry){
swappers[_swapper] = _approval;
emit SetSwapper(_swapper, _approval);
}else{
revert OnlyProtocolOrOwner();
}
}
/// @notice The ```SetConvexPool``` event fires when convex pool id is updated
/// @param pid the convex pool id
event SetConvexPool(uint256 pid);
/// @notice The ```setConvexPool``` function is called update the underlying convex pool
/// @dev
/// @param pid the convex pool id
function setConvexPool(uint256 pid) external onlyOwner{
_updateConvexPool(pid);
emit SetConvexPool(pid);
}
function _updateConvexPool(uint256 _pid) internal{
uint256 currentPid = convexPid;
if(currentPid != _pid){
//get previous staking
(,,,address _rewards,,) = IConvexStaking(convexBooster).poolInfo(currentPid);
//get balance
uint256 stakedBalance = IConvexStaking(_rewards).balanceOf(address(this));
if(stakedBalance > 0){
//withdraw
IConvexStaking(_rewards).withdrawAndUnwrap(stakedBalance,false);
if(collateral.balanceOf(address(this)) < stakedBalance){
revert IncorrectStakeBalance();
}
}
//stake in new pool
IConvexStaking(convexBooster).deposit(_pid, stakedBalance, true);
//update pid
convexPid = _pid;
}
}
function _stakeUnderlying(uint256 _amount) internal override{
uint256 currentPid = convexPid;
if(currentPid != 0){
IConvexStaking(convexBooster).deposit(currentPid, _amount, true);
}
}
function _unstakeUnderlying(uint256 _amount) internal override{
uint256 currentPid = convexPid;
if(currentPid != 0){
(,,,address _rewards,,) = IConvexStaking(convexBooster).poolInfo(currentPid);
IConvexStaking(_rewards).withdrawAndUnwrap(_amount, false);
}
}
function totalCollateral() public view override returns(uint256 _totalCollateralBalance){
uint256 currentPid = convexPid;
if(currentPid != 0){
//get staking
(,,,address _rewards,,) = IConvexStaking(convexBooster).poolInfo(currentPid);
//get balance
_totalCollateralBalance = IConvexStaking(_rewards).balanceOf(address(this));
}else{
_totalCollateralBalance = collateral.balanceOf(address(this));
}
}
// ============================================================================================
// Functions: Access Control
// ============================================================================================
uint256 previousBorrowLimit;
/// @notice The ```pause``` function is called to pause all contract functionality
function pause() external onlyOwner{
if (borrowLimit > 0) {
previousBorrowLimit = borrowLimit;
_setBorrowLimit(0);
}
}
/// @notice The ```unpause``` function is called to unpause all contract functionality
function unpause() external onlyOwner{
if (borrowLimit == 0) _setBorrowLimit(previousBorrowLimit);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// 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
pragma solidity 0.8.28;
/**
* @title ResupplyPairConstants
* @notice Based on code from Drake Evans and Frax Finance's pair constants (https://github.com/FraxFinance/fraxlend), adapted for Resupply Finance
*/
abstract contract ResupplyPairConstants {
// Precision settings
uint256 public constant LTV_PRECISION = 1e5; // 5 decimals
uint256 public constant LIQ_PRECISION = 1e5;
uint256 public constant EXCHANGE_PRECISION = 1e18;
uint256 public constant RATE_PRECISION = 1e18;
uint256 public constant SHARE_REFACTOR_PRECISION = 1e12;
uint256 public constant PAIR_DECIMALS = 1e18;
error Insolvent(uint256 _borrow, uint256 _collateral, uint256 _exchangeRate);
error BorrowerSolvent();
error InsufficientDebtAvailable(uint256 _assets, uint256 _request);
error SlippageTooHigh(uint256 _minOut, uint256 _actual);
error BadSwapper();
error InvalidPath(address _expected, address _actual);
error InvalidReceiver();
error InvalidLiquidator();
error InvalidRedemptionHandler();
error InvalidParameter();
error InsufficientDebtToRedeem();
error MinimumRedemption();
error InsufficientBorrowAmount();
error OnlyProtocolOrOwner();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
/**
* @title ResupplyPairCore
* @notice Based on code from Drake Evans and Frax Finance's lending pair core contract (https://github.com/FraxFinance/fraxlend), adapted for Resupply Finance
*/
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { ResupplyPairConstants } from "./ResupplyPairConstants.sol";
import { VaultAccount, VaultAccountingLibrary } from "../../libraries/VaultAccount.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IOracle } from "../../interfaces/IOracle.sol";
import { IRateCalculator } from "../../interfaces/IRateCalculator.sol";
import { ISwapper } from "../../interfaces/ISwapper.sol";
import { IResupplyRegistry } from "../../interfaces/IResupplyRegistry.sol";
import { ILiquidationHandler } from "../../interfaces/ILiquidationHandler.sol";
import { RewardDistributorMultiEpoch } from "../RewardDistributorMultiEpoch.sol";
import { WriteOffToken } from "../WriteOffToken.sol";
import { IERC4626 } from "../../interfaces/IERC4626.sol";
import { CoreOwnable } from "../../dependencies/CoreOwnable.sol";
import { IMintable } from "../../interfaces/IMintable.sol";
abstract contract ResupplyPairCore is CoreOwnable, ResupplyPairConstants, RewardDistributorMultiEpoch {
using VaultAccountingLibrary for VaultAccount;
using SafeERC20 for IERC20;
using SafeCast for uint256;
//forked fraxlend at version 3,0,0
function version() external pure returns (uint256 _major, uint256 _minor, uint256 _patch) {
_major = 3;
_minor = 0;
_patch = 0;
}
// ============================================================================================
// Settings set by constructor()
// ============================================================================================
// Asset and collateral contracts
address public immutable registry;
IERC20 internal immutable debtToken;
IERC20 public immutable collateral;
IERC20 public immutable underlying;
// LTV Settings
/// @notice The maximum LTV allowed for this pair
/// @dev 1e5 precision
uint256 public maxLTV;
//max borrow
uint256 public borrowLimit;
//Fees
/// @notice The liquidation fee, given as a % of repayment amount
/// @dev 1e5 precision
uint256 public mintFee;
uint256 public liquidationFee;
/// @dev 1e18 precision
uint256 public protocolRedemptionFee;
uint256 public minimumRedemption = 100 * PAIR_DECIMALS; //minimum amount of debt to redeem
uint256 public minimumLeftoverDebt = 10000 * PAIR_DECIMALS; //minimum amount of assets left over via redemptions
uint256 public minimumBorrowAmount = 1000 * PAIR_DECIMALS; //minimum amount of assets to borrow
// Interest Rate Calculator Contract
IRateCalculator public rateCalculator; // For complex rate calculations
// Swapper
mapping(address => bool) public swappers; // approved swapper addresses
// Metadata
string public name;
// ============================================================================================
// Storage
// ============================================================================================
/// @notice Stores information about the current interest rate
CurrentRateInfo public currentRateInfo;
struct CurrentRateInfo {
uint64 lastTimestamp;
uint64 ratePerSec;
uint128 lastShares;
}
/// @notice Stores information about the current exchange rate. Collateral:Asset ratio
/// @dev Struct packed to save SLOADs. Amount of Collateral Token to buy 1e18 Asset Token
ExchangeRateInfo public exchangeRateInfo;
struct ExchangeRateInfo {
address oracle;
uint96 lastTimestamp;
uint256 exchangeRate;
}
// Contract Level Accounting
VaultAccount public totalBorrow; // amount = total borrow amount with interest accrued, shares = total shares outstanding
uint256 public claimableFees; //amount of interest gained that is claimable as fees
uint256 public claimableOtherFees; //amount of redemption/mint fees claimable by protocol
WriteOffToken immutable public redemptionWriteOff; //token to keep track of redemption write offs
// User Level Accounting
/// @notice Stores the balance of collateral for each user
mapping(address => uint256) internal _userCollateralBalance; // amount of collateral each user is backed
/// @notice Stores the balance of borrow shares for each user
mapping(address => uint256) internal _userBorrowShares; // represents the shares held by individuals
// ============================================================================================
// Constructor
// ============================================================================================
/// @notice The ```constructor``` function is called on deployment
/// @param _core Core contract address
/// @param _configData abi.encode(address _collateral, address _oracle, address _rateCalculator, uint256 _maxLTV, uint256 _borrowLimit, uint256 _liquidationFee, uint256 _mintFee, uint256 _protocolRedemptionFee)
/// @param _immutables abi.encode(address _registry)
/// @param _customConfigData abi.encode(address _name, address _govToken, address _underlyingStaking, uint256 _stakingId)
constructor(
address _core,
bytes memory _configData,
bytes memory _immutables,
bytes memory _customConfigData
) CoreOwnable(_core){
(address _registry) = abi.decode(
_immutables,
(address)
);
registry = _registry;
debtToken = IERC20(IResupplyRegistry(registry).token());
{
(
address _collateral,
address _oracle,
address _rateCalculator,
uint256 _maxLTV,
uint256 _initialBorrowLimit,
uint256 _liquidationFee,
uint256 _mintFee,
uint256 _protocolRedemptionFee
) = abi.decode(
_configData,
(address, address, address, uint256, uint256, uint256, uint256, uint256)
);
// Pair Settings
collateral = IERC20(_collateral);
if(IERC20Metadata(_collateral).decimals() != 18){
revert InvalidParameter();
}
underlying = IERC20(IERC4626(_collateral).asset());
if(IERC20Metadata(address(underlying)).decimals() != 18){
revert InvalidParameter();
}
// approve so this contract can deposit
underlying.forceApprove(_collateral, type(uint256).max);
currentRateInfo.lastShares = uint128(IERC4626(_collateral).convertToShares(PAIR_DECIMALS));
exchangeRateInfo.oracle = _oracle;
rateCalculator = IRateCalculator(_rateCalculator);
borrowLimit = _initialBorrowLimit;
//Liquidation Fee Settings
liquidationFee = _liquidationFee;
mintFee = _mintFee;
protocolRedemptionFee = _protocolRedemptionFee;
// set maxLTV
maxLTV = _maxLTV;
}
//starting reward types
redemptionWriteOff = new WriteOffToken(address(this));
_insertRewardToken(address(redemptionWriteOff));//add redemption token as a reward
//set the redemption token as non claimable via getReward
rewards[0].is_non_claimable = true;
{
(string memory _name,,,) = abi.decode(
_customConfigData,
(string, address, address, uint256)
);
// Metadata
name = _name;
// Instantiate Interest
_addInterest();
// Instantiate Exchange Rate
_updateExchangeRate();
}
}
// ============================================================================================
// Helpers
// ============================================================================================
//get total collateral, either parked here or staked
function totalCollateral() public view virtual returns(uint256 _totalCollateralBalance);
function userBorrowShares(address _account) public view returns(uint256 borrowShares){
borrowShares = _userBorrowShares[_account];
uint256 globalEpoch = currentRewardEpoch;
uint256 userEpoch = userRewardEpoch[_account];
if(userEpoch < globalEpoch){
//need to calculate shares while keeping this as a view function
for(;;){
//reduce shares by refactoring amount
borrowShares /= SHARE_REFACTOR_PRECISION;
unchecked {
userEpoch += 1;
}
if(userEpoch == globalEpoch){
break;
}
}
}
}
//get _userCollateralBalance minus redemption tokens
function userCollateralBalance(address _account) public nonReentrant returns(uint256 _collateralAmount){
_syncUserRedemptions(_account);
_collateralAmount = _userCollateralBalance[_account];
//since there are some very small dust during distribution there could be a few wei
//in user collateral that is over total collateral. clamp to total
uint256 total = totalCollateral();
_collateralAmount = _collateralAmount > total ? total : _collateralAmount;
}
/// @notice The ```totalDebtAvailable``` function returns the total balance of debt tokens in the contract
/// @return The balance of debt tokens held by contract
function totalDebtAvailable() external view returns (uint256) {
(,,, VaultAccount memory _totalBorrow) = previewAddInterest();
return _totalDebtAvailable(_totalBorrow);
}
/// @notice The ```_totalDebtAvailable``` function returns the total amount of debt that can be issued on this pair
/// @param _totalBorrow Total borrowed amount, inclusive of interest
/// @return The amount of debt that can be issued
function _totalDebtAvailable(VaultAccount memory _totalBorrow) internal view returns (uint256) {
uint256 _borrowLimit = borrowLimit;
uint256 borrowable = _borrowLimit > _totalBorrow.amount ? _borrowLimit - _totalBorrow.amount : 0;
return borrowable > type(uint128).max ? type(uint128).max : borrowable;
}
function currentUtilization() external view returns (uint256) {
uint256 _borrowLimit = borrowLimit;
if(_borrowLimit == 0){
return PAIR_DECIMALS;
}
(,,, VaultAccount memory _totalBorrow) = previewAddInterest();
return _totalBorrow.amount * PAIR_DECIMALS / _borrowLimit;
}
/// @notice The ```_isSolvent``` function determines if a given borrower is solvent given an exchange rate
/// @param _borrower The borrower address to check
/// @param _exchangeRate The exchange rate, i.e. the amount of collateral to buy 1e18 asset
/// @return Whether borrower is solvent
function _isSolvent(address _borrower, uint256 _exchangeRate) internal view returns (bool) {
uint256 _maxLTV = maxLTV;
if (_maxLTV == 0) return true;
//must look at borrow shares of current epoch so user helper function
//user borrow shares should be synced before _isSolvent is called
uint256 _borrowerAmount = totalBorrow.toAmount(_userBorrowShares[_borrower], true);
if (_borrowerAmount == 0) return true;
//anything that calls _isSolvent will call _syncUserRedemptions beforehand
uint256 _collateralAmount = _userCollateralBalance[_borrower];
if (_collateralAmount == 0) return false;
uint256 _ltv = ((_borrowerAmount * _exchangeRate * LTV_PRECISION) / EXCHANGE_PRECISION) / _collateralAmount;
return _ltv <= _maxLTV;
}
function _isSolventSync(address _borrower, uint256 _exchangeRate) internal returns (bool){
//checkpoint rewards and sync _userCollateralBalance
_syncUserRedemptions(_borrower);
return _isSolvent(_borrower, _exchangeRate);
}
// ============================================================================================
// Modifiers
// ============================================================================================
/// @notice Checks for solvency AFTER executing contract code
/// @param _borrower The borrower whose solvency we will check
modifier isSolvent(address _borrower) {
//checkpoint rewards and sync _userCollateralBalance before doing other actions
_syncUserRedemptions(_borrower);
_;
ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;
if (!_isSolvent(_borrower, _exchangeRateInfo.exchangeRate)) {
revert Insolvent(
totalBorrow.toAmount(_userBorrowShares[_borrower], true),
_userCollateralBalance[_borrower], //_issolvent sync'd so take base _userCollateral
_exchangeRateInfo.exchangeRate
);
}
}
// ============================================================================================
// Reward Implementation
// ============================================================================================
function _isRewardManager() internal view override returns(bool){
return msg.sender == address(core) || msg.sender == IResupplyRegistry(registry).rewardHandler();
}
function _fetchIncentives() internal override{
IResupplyRegistry(registry).claimRewards(address(this));
}
function _totalRewardShares() internal view override returns(uint256){
return totalBorrow.shares;
}
function _userRewardShares(address _account) internal view override returns(uint256){
return _userBorrowShares[_account];
}
function _increaseUserRewardEpoch(address _account, uint256 _currentUserEpoch) internal override{
//convert shares to next epoch shares
//share refactoring will never be 0
_userBorrowShares[_account] = _userBorrowShares[_account] / SHARE_REFACTOR_PRECISION;
//update user reward epoch
userRewardEpoch[_account] = _currentUserEpoch + 1;
}
function earned(address _account) public override returns(EarnedData[] memory claimable){
EarnedData[] memory earneddata = super.earned(_account);
uint256 rewardCount = earneddata.length - 1;
claimable = new EarnedData[](rewardCount);
//remove index 0 as we dont need to report the write off tokens
for (uint256 i = 1; i <= rewardCount; ) {
claimable[i-1].amount = earneddata[i].amount;
claimable[i-1].token = earneddata[i].token;
unchecked{ i += 1; }
}
}
function _checkAddToken(address _address) internal view virtual override returns(bool){
if(_address == address(collateral)) return false;
if(_address == address(debtToken)) return false;
return true;
}
// ============================================================================================
// Underlying Staking
// ============================================================================================
function _stakeUnderlying(uint256 _amount) internal virtual;
function _unstakeUnderlying(uint256 _amount) internal virtual;
// ============================================================================================
// Functions: Interest Accumulation and Adjustment
// ============================================================================================
/// @notice The ```AddInterest``` event is emitted when interest is accrued by borrowers
/// @param interestEarned The total interest accrued by all borrowers
/// @param rate The interest rate used to calculate accrued interest
event AddInterest(uint256 interestEarned, uint256 rate);
/// @notice The ```UpdateRate``` event is emitted when the interest rate is updated
/// @param oldRatePerSec The old interest rate (per second)
/// @param oldShares previous used shares
/// @param newRatePerSec The new interest rate (per second)
/// @param newShares new shares
event UpdateRate(
uint256 oldRatePerSec,
uint128 oldShares,
uint256 newRatePerSec,
uint128 newShares
);
/// @notice The ```addInterest``` function is a public implementation of _addInterest and allows 3rd parties to trigger interest accrual
/// @param _returnAccounting Whether to return additional accounting data
/// @return _interestEarned The amount of interest accrued by all borrowers
/// @return _currentRateInfo The new rate info struct
/// @return _claimableFees The new total of fees that are claimable
/// @return _totalBorrow The new total borrow struct
function addInterest(
bool _returnAccounting
)
external
nonReentrant
returns (
uint256 _interestEarned,
CurrentRateInfo memory _currentRateInfo,
uint256 _claimableFees,
VaultAccount memory _totalBorrow
)
{
(, _interestEarned, _currentRateInfo) = _addInterest();
if (_returnAccounting) {
_claimableFees = claimableFees;
_totalBorrow = totalBorrow;
}
}
/// @notice The ```previewAddInterest``` function
/// @return _interestEarned The amount of interest accrued by all borrowers
/// @return _newCurrentRateInfo The new rate info struct
/// @return _claimableFees The new total of fees that are claimable
/// @return _totalBorrow The new total borrow struct
function previewAddInterest()
public
view
returns (
uint256 _interestEarned,
CurrentRateInfo memory _newCurrentRateInfo,
uint256 _claimableFees,
VaultAccount memory _totalBorrow
)
{
_newCurrentRateInfo = currentRateInfo;
_newCurrentRateInfo.lastTimestamp = uint64(block.timestamp);
// Write return values
InterestCalculationResults memory _results = _calculateInterest(_newCurrentRateInfo);
if (_results.isInterestUpdated) {
_interestEarned = _results.interestEarned;
_newCurrentRateInfo.ratePerSec = _results.newRate;
_newCurrentRateInfo.lastShares = _results.newShares;
_claimableFees = claimableFees + uint128(_interestEarned);
_totalBorrow = _results.totalBorrow;
} else {
_claimableFees = claimableFees;
_totalBorrow = totalBorrow;
}
}
struct InterestCalculationResults {
bool isInterestUpdated;
uint64 newRate;
uint128 newShares;
uint256 interestEarned;
VaultAccount totalBorrow;
}
/// @notice The ```_calculateInterest``` function calculates the interest to be accrued and the new interest rate info
/// @param _currentRateInfo The current rate info
/// @return _results The results of the interest calculation
function _calculateInterest(
CurrentRateInfo memory _currentRateInfo
) internal view returns (InterestCalculationResults memory _results) {
// Short circuit if interest already calculated this block
if (_currentRateInfo.lastTimestamp < block.timestamp) {
// Indicate that interest is updated and calculated
_results.isInterestUpdated = true;
// Write return values and use these to save gas
_results.totalBorrow = totalBorrow;
// Time elapsed since last interest update
uint256 _deltaTime = block.timestamp - _currentRateInfo.lastTimestamp;
// Request new interest rate and full utilization rate from the rate calculator
(_results.newRate, _results.newShares) = IRateCalculator(rateCalculator).getNewRate(
address(collateral),
_deltaTime,
_currentRateInfo.lastShares
);
// Calculate interest accrued
_results.interestEarned = (_deltaTime * _results.totalBorrow.amount * _results.newRate) / RATE_PRECISION;
// Accrue interest (if any) and fees if no overflow
if (
_results.interestEarned > 0 &&
_results.interestEarned + _results.totalBorrow.amount <= type(uint128).max
) {
// Increment totalBorrow by interestEarned
_results.totalBorrow.amount += uint128(_results.interestEarned);
}else{
//reset interest earned
_results.interestEarned = 0;
}
}
}
/// @notice The ```_addInterest``` function is invoked prior to every external function and is used to accrue interest and update interest rate
/// @dev Can only called once per block
/// @return _isInterestUpdated True if interest was calculated
/// @return _interestEarned The amount of interest accrued by all borrowers
/// @return _currentRateInfo The new rate info struct
function _addInterest()
internal
returns (
bool _isInterestUpdated,
uint256 _interestEarned,
CurrentRateInfo memory _currentRateInfo
)
{
// Pull from storage and set default return values
_currentRateInfo = currentRateInfo;
// Calc interest
InterestCalculationResults memory _results = _calculateInterest(_currentRateInfo);
// Write return values only if interest was updated and calculated
if (_results.isInterestUpdated) {
_isInterestUpdated = _results.isInterestUpdated;
_interestEarned = _results.interestEarned;
// emit here so that we have access to the old values
emit UpdateRate(
_currentRateInfo.ratePerSec,
_currentRateInfo.lastShares,
_results.newRate,
_results.newShares
);
emit AddInterest(_interestEarned, _results.newRate);
// overwrite original values
_currentRateInfo.ratePerSec = _results.newRate;
_currentRateInfo.lastShares = _results.newShares;
_currentRateInfo.lastTimestamp = uint64(block.timestamp);
// Effects: write to state
currentRateInfo = _currentRateInfo;
claimableFees += _interestEarned; //increase claimable fees by interest earned
totalBorrow = _results.totalBorrow;
}
}
// ============================================================================================
// Functions: ExchangeRate
// ============================================================================================
/// @notice The ```UpdateExchangeRate``` event is emitted when the Collateral:Asset exchange rate is updated
/// @param exchangeRate The exchange rate
event UpdateExchangeRate(uint256 exchangeRate);
/// @notice The ```updateExchangeRate``` function is the external implementation of _updateExchangeRate.
/// @dev This function is invoked at most once per block as these queries can be expensive
/// @return _exchangeRate The exchange rate
function updateExchangeRate()
external
nonReentrant
returns (uint256 _exchangeRate)
{
return _updateExchangeRate();
}
/// @notice The ```_updateExchangeRate``` function retrieves the latest exchange rate. i.e how much collateral to buy 1e18 asset.
/// @dev This function is invoked at most once per block as these queries can be expensive
/// @return _exchangeRate The exchange rate
function _updateExchangeRate()
internal
returns (uint256 _exchangeRate)
{
// Pull from storage to save gas and set default return values
ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo;
// Short circuit if already updated this block
if (_exchangeRateInfo.lastTimestamp != block.timestamp) {
// Get the latest exchange rate from the oracle
_exchangeRate = IOracle(_exchangeRateInfo.oracle).getPrices(address(collateral));
//convert price of collateral as debt is priced in terms of collateral amount (inverse)
_exchangeRate = 1e36 / _exchangeRate;
// Effects: Bookkeeping and write to storage
_exchangeRateInfo.lastTimestamp = uint96(block.timestamp);
_exchangeRateInfo.exchangeRate = _exchangeRate;
exchangeRateInfo = _exchangeRateInfo;
emit UpdateExchangeRate(_exchangeRate);
} else {
// Use default return values if already updated this block
_exchangeRate = _exchangeRateInfo.exchangeRate;
}
}
// ============================================================================================
// Functions: Lending
// ============================================================================================
// ONLY Protocol can lend
// ============================================================================================
// Functions: Borrowing
// ============================================================================================
//sync user collateral by removing account of userCollateralBalance based on
//how many "claimable" redemption tokens are available to the user
//should be called before anything with userCollateralBalance is used
function _syncUserRedemptions(address _account) internal{
//sync rewards first
_checkpoint(_account);
//get token count (divide by LTV_PRECISION as precision is padded)
uint256 rTokens = claimable_reward[address(redemptionWriteOff)][_account] / LTV_PRECISION;
//reset claimables
claimable_reward[address(redemptionWriteOff)][_account] = 0;
//remove from collateral balance the number of rtokens the user has
uint256 currentUserBalance = _userCollateralBalance[_account];
_userCollateralBalance[_account] = currentUserBalance >= rTokens ? currentUserBalance - rTokens : 0;
}
/// @notice The ```Borrow``` event is emitted when a borrower increases their position
/// @param _borrower The borrower whose account was debited
/// @param _receiver The address to which the Asset Tokens were transferred
/// @param _borrowAmount The amount of Asset Tokens transferred
/// @param _sharesAdded The number of Borrow Shares the borrower was debited
/// @param _mintFees The amount of mint fees incurred
event Borrow(
address indexed _borrower,
address indexed _receiver,
uint256 _borrowAmount,
uint256 _sharesAdded,
uint256 _mintFees
);
/// @notice The ```_borrow``` function is the internal implementation for borrowing assets
/// @param _borrowAmount The amount of the Asset Token to borrow
/// @param _receiver The address to receive the Asset Tokens
/// @return _sharesAdded The amount of borrow shares the msg.sender will be debited
function _borrow(uint128 _borrowAmount, address _receiver) internal returns (uint256 _sharesAdded) {
// Get borrow accounting from storage to save gas
VaultAccount memory _totalBorrow = totalBorrow;
if(_borrowAmount < minimumBorrowAmount){
revert InsufficientBorrowAmount();
}
//mint fees
uint256 debtForMint = (_borrowAmount * (LIQ_PRECISION + mintFee) / LIQ_PRECISION);
// Check available capital
uint256 _assetsAvailable = _totalDebtAvailable(_totalBorrow);
if (_assetsAvailable < debtForMint) {
revert InsufficientDebtAvailable(_assetsAvailable, debtForMint);
}
// Calculate the number of shares to add based on the amount to borrow
_sharesAdded = _totalBorrow.toShares(debtForMint, true);
//combine current shares and new shares
uint256 newTotalShares = _totalBorrow.shares + _sharesAdded;
// Effects: Bookkeeping to add shares & amounts to total Borrow accounting
_totalBorrow.amount += debtForMint.toUint128();
_totalBorrow.shares = newTotalShares.toUint128();
// Effects: write back to storage
totalBorrow = _totalBorrow;
_userBorrowShares[msg.sender] += _sharesAdded;
uint256 otherFees = debtForMint > _borrowAmount ? debtForMint - _borrowAmount : 0;
if (otherFees > 0) claimableOtherFees += otherFees;
// Interactions
IResupplyRegistry(registry).mint(_receiver, _borrowAmount);
emit Borrow(msg.sender, _receiver, _borrowAmount, _sharesAdded, otherFees);
}
/// @notice The ```borrow``` function allows a user to open/increase a borrow position
/// @dev Borrower must call ```ERC20.approve``` on the Collateral Token contract if applicable
/// @param _borrowAmount The amount to borrow
/// @param _underlyingAmount The amount of underlying tokens to transfer to Pair
/// @param _receiver The address which will receive the Asset Tokens
/// @return _shares The number of borrow Shares the msg.sender will be debited
function borrow(
uint256 _borrowAmount,
uint256 _underlyingAmount,
address _receiver
) external nonReentrant isSolvent(msg.sender) returns (uint256 _shares) {
if (_receiver == address(0)) revert InvalidReceiver();
// Accrue interest if necessary
_addInterest();
// Update _exchangeRate
_updateExchangeRate();
// Only add collateral if necessary
if (_underlyingAmount > 0) {
//pull underlying and deposit in vault
underlying.safeTransferFrom(msg.sender, address(this), _underlyingAmount);
uint256 collateralShares = IERC4626(address(collateral)).deposit(_underlyingAmount, address(this));
//add collateral to msg.sender
_addCollateral(address(this), collateralShares, msg.sender);
}
// Effects: Call internal borrow function
_shares = _borrow(_borrowAmount.toUint128(), _receiver);
}
/// @notice The ```AddCollateral``` event is emitted when a borrower adds collateral to their position
/// @param borrower The borrower account for which the collateral should be credited
/// @param collateralAmount The amount of Collateral Token to be transferred
event AddCollateral(address indexed borrower, uint256 collateralAmount);
/// @notice The ```_addCollateral``` function is an internal implementation for adding collateral to a borrowers position
/// @param _sender The source of funds for the new collateral
/// @param _collateralAmount The amount of Collateral Token to be transferred
/// @param _borrower The borrower account for which the collateral should be credited
function _addCollateral(address _sender, uint256 _collateralAmount, address _borrower) internal {
_userCollateralBalance[_borrower] += _collateralAmount;
if (_sender != address(this)) {
collateral.safeTransferFrom(_sender, address(this), _collateralAmount);
}
//stake underlying
_stakeUnderlying(_collateralAmount);
emit AddCollateral(_borrower, _collateralAmount);
}
/// @notice The ```addCollateral``` function allows the caller to add Collateral Token to a borrowers position
/// @dev msg.sender must call ERC20.approve() on the Collateral Token contract prior to invocation
/// @param _collateralAmount The amount of Collateral Token to be added to borrower's position
/// @param _borrower The account to be credited
function addCollateralVault(uint256 _collateralAmount, address _borrower) external nonReentrant {
if (_borrower == address(0)) revert InvalidReceiver();
_addInterest();
_addCollateral(msg.sender, _collateralAmount, _borrower);
}
/// @notice Allows depositing in terms of underlying asset, and have it converted to collateral shares to the borrower's position.
/// @param _amount The amount of the underlying asset to deposit.
/// @param _borrower The address of the borrower whose collateral balance will be credited.
function addCollateral(uint256 _amount, address _borrower) external nonReentrant {
if (_borrower == address(0)) revert InvalidReceiver();
_addInterest();
underlying.safeTransferFrom(msg.sender, address(this), _amount);
uint256 collateralShares = IERC4626(address(collateral)).deposit(_amount, address(this));
_addCollateral(address(this), collateralShares, _borrower);
}
/// @notice The ```RemoveCollateral``` event is emitted when collateral is removed from a borrower's position
/// @param _collateralAmount The amount of Collateral Token to be transferred
/// @param _receiver The address to which Collateral Tokens will be transferred
/// @param _borrower The address of the account in which collateral is being removed
event RemoveCollateral(
uint256 _collateralAmount,
address indexed _receiver,
address indexed _borrower
);
/// @notice The ```_removeCollateral``` function is the internal implementation for removing collateral from a borrower's position
/// @param _collateralAmount The amount of Collateral Token to remove from the borrower's position
/// @param _receiver The address to receive the Collateral Token transferred
/// @param _borrower The borrower whose account will be debited the Collateral amount
function _removeCollateral(uint256 _collateralAmount, address _receiver, address _borrower) internal {
// Effects: write to state
// NOTE: Following line will revert on underflow if _collateralAmount > userCollateralBalance
_userCollateralBalance[_borrower] -= _collateralAmount;
//unstake underlying
//NOTE: following will revert on underflow if total collateral < _collateralAmount
_unstakeUnderlying(_collateralAmount);
// Interactions
if (_receiver != address(this)) {
collateral.safeTransfer(_receiver, _collateralAmount);
}
emit RemoveCollateral(_collateralAmount, _receiver, _borrower);
}
/// @notice The ```removeCollateralVault``` function is used to remove collateral from msg.sender's borrow position
/// @dev msg.sender must be solvent after invocation or transaction will revert
/// @param _collateralAmount The amount of Collateral Token to transfer
/// @param _receiver The address to receive the transferred funds
function removeCollateralVault(
uint256 _collateralAmount,
address _receiver
) external nonReentrant isSolvent(msg.sender) {
//note: isSolvent checkpoints msg.sender via _syncUserRedemptions
if (_receiver == address(0)) revert InvalidReceiver();
_addInterest();
// Note: exchange rate is irrelevant when borrower has no debt shares
if (_userBorrowShares[msg.sender] > 0) {
_updateExchangeRate();
}
_removeCollateral(_collateralAmount, _receiver, msg.sender);
}
/// @notice The ```removeCollateral``` function is used to remove collateral from msg.sender's borrow position and redeem it for underlying tokens
/// @dev msg.sender must be solvent after invocation or transaction will revert
/// @param _collateralAmount The amount of Collateral Token to redeem
/// @param _receiver The address to receive the redeemed underlying tokens
function removeCollateral(
uint256 _collateralAmount,
address _receiver
) external nonReentrant isSolvent(msg.sender) {
//note: isSolvent checkpoints msg.sender via _syncUserRedemptions
if (_receiver == address(0)) revert InvalidReceiver();
_addInterest();
// Note: exchange rate is irrelevant when borrower has no debt shares
if (_userBorrowShares[msg.sender] > 0) {
_updateExchangeRate();
}
_removeCollateral(_collateralAmount, address(this), msg.sender);
IERC4626(address(collateral)).redeem(_collateralAmount, _receiver, address(this));
}
/// @notice The ```Repay``` event is emitted whenever a debt position is repaid
/// @param payer The address paying for the repayment
/// @param borrower The borrower whose account will be credited
/// @param amountToRepay The amount of Asset token to be transferred
/// @param shares The amount of Borrow Shares which will be debited from the borrower after repayment
event Repay(address indexed payer, address indexed borrower, uint256 amountToRepay, uint256 shares);
/// @notice The ```_repay``` function is the internal implementation for repaying a borrow position
/// @dev The payer must have called ERC20.approve() on the Asset Token contract prior to invocation
/// @param _totalBorrow An in memory copy of the totalBorrow VaultAccount struct
/// @param _amountToRepay The amount of Asset Token to transfer
/// @param _shares The number of Borrow Shares the sender is repaying
/// @param _payer The address from which funds will be transferred
/// @param _borrower The borrower account which will be credited
function _repay(
VaultAccount memory _totalBorrow,
uint128 _amountToRepay,
uint128 _shares,
address _payer,
address _borrower
) internal {
//checkpoint rewards for borrower before adjusting borrow shares
_checkpoint(_borrower);
// Effects: Bookkeeping
_totalBorrow.amount -= _amountToRepay;
_totalBorrow.shares -= _shares;
// Effects: write user state
uint256 usershares = _userBorrowShares[_borrower] - _shares;
_userBorrowShares[_borrower] = usershares;
//check that any remaining user amount is greater than minimumBorrowAmount
if(usershares > 0 && _totalBorrow.toAmount(usershares, true) < minimumBorrowAmount){
revert InsufficientBorrowAmount();
}
// Effects: write global state
totalBorrow = _totalBorrow;
// Interactions
// burn from non-zero address. zero address is only supplied during liquidations
// for liqudations the handler will do the burning
if (_payer != address(0)) {
IMintable(address(debtToken)).burn(_payer, _amountToRepay);
}
emit Repay(_payer, _borrower, _amountToRepay, _shares);
}
/// @notice The ```repay``` function allows the caller to pay down the debt for a given borrower.
/// @dev Caller must first invoke ```ERC20.approve()``` for the Asset Token contract
/// @param _shares The number of Borrow Shares which will be repaid by the call
/// @param _borrower The account for which the debt will be reduced
/// @return _amountToRepay The amount of Asset Tokens which were burned to repay the Borrow Shares
function repay(uint256 _shares, address _borrower) external nonReentrant returns (uint256 _amountToRepay) {
if (_borrower == address(0)) revert InvalidReceiver();
// Accrue interest if necessary
_addInterest();
// Calculate amount to repay based on shares
VaultAccount memory _totalBorrow = totalBorrow;
_amountToRepay = _totalBorrow.toAmount(_shares, true);
// Execute repayment effects
_repay(_totalBorrow, _amountToRepay.toUint128(), _shares.toUint128(), msg.sender, _borrower);
}
// ============================================================================================
// Functions: Redemptions
// ============================================================================================
event Redeemed(
address indexed _caller,
uint256 _amount,
uint256 _collateralFreed,
uint256 _protocolFee,
uint256 _debtReduction
);
/// @notice Allows redemption of the debt tokens for collateral
/// @dev Only callable by the registry's redeemer contract
/// @param _caller The address of the caller
/// @param _amount The amount of debt tokens to redeem
/// @param _totalFeePct Total fee to charge, expressed as a percentage of the stablecoin input; to be subdivided between protocol and borrowers.
/// @param _receiver The address to receive the collateral tokens
/// @return _collateralToken The address of the collateral token
/// @return _collateralFreed The amount of collateral tokens returned to receiver
function redeemCollateral(
address _caller,
uint256 _amount,
uint256 _totalFeePct,
address _receiver
) external nonReentrant returns(address _collateralToken, uint256 _collateralFreed){
//check sender. must go through the registry's redemptionHandler
if(msg.sender != IResupplyRegistry(registry).redemptionHandler()) revert InvalidRedemptionHandler();
if (_receiver == address(0) || _receiver == address(this)) revert InvalidReceiver();
if(_amount < minimumRedemption){
revert MinimumRedemption();
}
// accrue interest if necessary
_addInterest();
//redemption fees
//assuming 1% redemption fee(0.5% to protocol, 0.5% to borrowers) and a redemption of $100
// reduce totalBorrow.amount by 99.5$
// add 0.5$ to protocol earned fees
// return 99$ of collateral
// burn $100 of stables
uint256 valueToRedeem = _amount * (EXCHANGE_PRECISION - _totalFeePct) / EXCHANGE_PRECISION;
uint256 protocolFee = (_amount - valueToRedeem) * protocolRedemptionFee / EXCHANGE_PRECISION;
uint256 debtReduction = _amount - protocolFee; // protocol fee portion is not burned
//check if theres enough debt to write off
VaultAccount memory _totalBorrow = totalBorrow;
if(debtReduction > _totalBorrow.amount || _totalBorrow.amount - debtReduction < minimumLeftoverDebt ){
revert InsufficientDebtToRedeem(); // size of request exceeeds total pair debt
}
_totalBorrow.amount -= uint128(debtReduction);
//if after many redemptions the amount to shares ratio has deteriorated too far, then refactor
//cast to uint256 to reduce chance of overflow
if(uint256(_totalBorrow.amount) * SHARE_REFACTOR_PRECISION < _totalBorrow.shares){
_increaseRewardEpoch(); //will do final checkpoint on previous total supply
_totalBorrow.shares /= uint128(SHARE_REFACTOR_PRECISION);
}
// Effects: write to state
totalBorrow = _totalBorrow;
claimableOtherFees += protocolFee; //increase claimable fees
// Update exchange rate
uint256 _exchangeRate = _updateExchangeRate();
//calc collateral units
_collateralFreed = ((valueToRedeem * _exchangeRate) / EXCHANGE_PRECISION);
_unstakeUnderlying(_collateralFreed);
_collateralToken = address(collateral);
IERC20(_collateralToken).safeTransfer(_receiver, _collateralFreed);
//distribute write off tokens to adjust userCollateralbalances
//padded with LTV_PRECISION for extra precision
redemptionWriteOff.mint(_collateralFreed * LTV_PRECISION);
emit Redeemed(_caller, _amount, _collateralFreed, protocolFee, debtReduction);
}
// ============================================================================================
// Functions: Liquidations
// ============================================================================================
/// @notice The ```Liquidate``` event is emitted when a liquidation occurs
/// @param _borrower The borrower account for which the liquidation occurred
/// @param _collateralForLiquidator The amount of collateral token transferred to the liquidator
/// @param _sharesLiquidated The number of borrow shares liquidated
/// @param _amountLiquidatorToRepay The amount of asset tokens to be repaid by the liquidator
event Liquidate(
address indexed _borrower,
uint256 _collateralForLiquidator,
uint256 _sharesLiquidated,
uint256 _amountLiquidatorToRepay
);
/// @notice The ```liquidate``` function allows a third party to repay a borrower's debt if they have become insolvent
/// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling ```Liquidate()```
/// @param _borrower The account for which the repayment is credited and from whom collateral will be taken
/// @return _collateralForLiquidator The amount of Collateral Token transferred to the liquidator
function liquidate(
address _borrower
) external nonReentrant returns (uint256 _collateralForLiquidator) {
address liquidationHandler = IResupplyRegistry(registry).liquidationHandler();
if(msg.sender != liquidationHandler) revert InvalidLiquidator();
if (_borrower == address(0)) revert InvalidReceiver();
// accrue interest if necessary
_addInterest();
// Update exchange rate and use the lower rate for liquidations
uint256 _exchangeRate = _updateExchangeRate();
// Check if borrower is solvent, revert if they are
//_isSolventSync calls _syncUserRedemptions which checkpoints rewards and userCollateral
if (_isSolventSync(_borrower, _exchangeRate)) {
revert BorrowerSolvent();
}
// Read from state
VaultAccount memory _totalBorrow = totalBorrow;
uint256 _collateralBalance = _userCollateralBalance[_borrower];
uint128 _borrowerShares = _userBorrowShares[_borrower].toUint128();
// Checks & Calculations
// Determine the liquidation amount in collateral units (i.e. how much debt liquidator is going to repay)
uint256 _liquidationAmountInCollateralUnits = ((_totalBorrow.toAmount(_borrowerShares, false) *
_exchangeRate) / EXCHANGE_PRECISION);
// add fee for liquidation
_collateralForLiquidator = (_liquidationAmountInCollateralUnits *
(LIQ_PRECISION + liquidationFee)) / LIQ_PRECISION;
// clamp to user collateral balance as we cant take more than that
_collateralForLiquidator = _collateralForLiquidator > _collateralBalance ? _collateralBalance : _collateralForLiquidator;
// Calculated here for use during repayment, grouped with other calcs before effects start
uint128 _amountLiquidatorToRepay = (_totalBorrow.toAmount(_borrowerShares, true)).toUint128();
emit Liquidate(
_borrower,
_collateralForLiquidator,
_borrowerShares,
_amountLiquidatorToRepay
);
// Effects & Interactions
// repay using address(0) to skip burning (liquidationHandler will burn from insurance pool)
_repay(
_totalBorrow,
_amountLiquidatorToRepay,
_borrowerShares,
address(0),
_borrower
);
// Collateral is removed on behalf of borrower and sent to liquidationHandler
// NOTE: isSolvent above checkpoints user with _syncUserRedemptions before removing collateral
_removeCollateral(_collateralForLiquidator, liquidationHandler, _borrower);
//call liquidation handler to distribute and burn debt
ILiquidationHandler(liquidationHandler).processLiquidationDebt(address(collateral), _collateralForLiquidator, _amountLiquidatorToRepay);
}
// ============================================================================================
// Functions: Leverage
// ============================================================================================
/// @notice The ```LeveragedPosition``` event is emitted when a borrower takes out a new leveraged position
/// @param _borrower The account for which the debt is debited
/// @param _swapperAddress The address of the swapper which conforms the FraxSwap interface
/// @param _borrowAmount The amount of Asset Token to be borrowed to be borrowed
/// @param _borrowShares The number of Borrow Shares the borrower is credited
/// @param _initialUnderlyingAmount The amount of initial underlying Tokens supplied by the borrower
/// @param _amountCollateralOut The amount of Collateral Token which was received for the Asset Tokens
event LeveragedPosition(
address indexed _borrower,
address _swapperAddress,
uint256 _borrowAmount,
uint256 _borrowShares,
uint256 _initialUnderlyingAmount,
uint256 _amountCollateralOut
);
/// @notice The ```leveragedPosition``` function allows a user to enter a leveraged borrow position with minimal upfront Underlying tokens
/// @dev Caller must invoke ```ERC20.approve()``` on the Underlying Token contract prior to calling function
/// @param _swapperAddress The address of the whitelisted swapper to use to swap borrowed Asset Tokens for Collateral Tokens
/// @param _borrowAmount The amount of Asset Tokens borrowed
/// @param _initialUnderlyingAmount The initial amount of underlying Tokens supplied by the borrower
/// @param _amountCollateralOutMin The minimum amount of Collateral Tokens to be received in exchange for the borrowed Asset Tokens
/// @param _path An array containing the addresses of ERC20 tokens to swap. Adheres to UniV2 style path params.
/// @return _totalCollateralBalance The total amount of Collateral Tokens added to a users account (initial + swap)
function leveragedPosition(
address _swapperAddress,
uint256 _borrowAmount,
uint256 _initialUnderlyingAmount,
uint256 _amountCollateralOutMin,
address[] memory _path
) external nonReentrant isSolvent(msg.sender) returns (uint256 _totalCollateralBalance) {
// Accrue interest if necessary
_addInterest();
// Update exchange rate
_updateExchangeRate();
IERC20 _debtToken = debtToken;
IERC20 _collateral = collateral;
if (!swappers[_swapperAddress]) {
revert BadSwapper();
}
if (_path[0] != address(_debtToken)) {
revert InvalidPath(address(_debtToken), _path[0]);
}
if (_path[_path.length - 1] != address(_collateral)) {
revert InvalidPath(address(_collateral), _path[_path.length - 1]);
}
// Add initial underlying
if (_initialUnderlyingAmount > 0) {
underlying.safeTransferFrom(msg.sender, address(this), _initialUnderlyingAmount);
uint256 collateralShares = IERC4626(address(collateral)).deposit(_initialUnderlyingAmount, address(this));
_addCollateral(address(this), collateralShares, msg.sender);
_totalCollateralBalance = collateralShares;
}
// Debit borrowers account
// setting recipient to _swapperAddress allows us to skip a transfer (debt still goes to msg.sender)
uint256 _borrowShares = _borrow(_borrowAmount.toUint128(), _swapperAddress);
// Even though swappers are trusted, we verify the balance before and after swap
uint256 _initialCollateralBalance = _collateral.balanceOf(address(this));
ISwapper(_swapperAddress).swap(
msg.sender,
_borrowAmount,
_path,
address(this)
);
uint256 _finalCollateralBalance = _collateral.balanceOf(address(this));
// Note: VIOLATES CHECKS-EFFECTS-INTERACTION pattern, make sure function is NONREENTRANT
// Effects: bookkeeping & write to state
uint256 _amountCollateralOut = _finalCollateralBalance - _initialCollateralBalance;
if (_amountCollateralOut < _amountCollateralOutMin) {
revert SlippageTooHigh(_amountCollateralOutMin, _amountCollateralOut);
}
// address(this) as _sender means no transfer occurs as the pair has already received the collateral during swap
_addCollateral(address(this), _amountCollateralOut, msg.sender);
_totalCollateralBalance += _amountCollateralOut;
emit LeveragedPosition(
msg.sender,
_swapperAddress,
_borrowAmount,
_borrowShares,
_initialUnderlyingAmount,
_amountCollateralOut
);
}
/// @notice The ```RepayWithCollateral``` event is emitted whenever ```repayWithCollateral()``` is invoked
/// @param _borrower The borrower account for which the repayment is taking place
/// @param _swapperAddress The address of the whitelisted swapper to use for token swaps
/// @param _collateralToSwap The amount of Collateral Token to swap and use for repayment
/// @param _amountAssetOut The amount of Asset Token which was repaid
/// @param _sharesRepaid The number of Borrow Shares which were repaid
event RepayWithCollateral(
address indexed _borrower,
address _swapperAddress,
uint256 _collateralToSwap,
uint256 _amountAssetOut,
uint256 _sharesRepaid
);
/// @notice The ```repayWithCollateral``` function allows a borrower to repay their debt using existing collateral in contract
/// @param _swapperAddress The address of the whitelisted swapper to use for token swaps
/// @param _collateralToSwap The amount of Collateral Tokens to swap for Asset Tokens
/// @param _amountOutMin The minimum amount of Asset Tokens to receive during the swap
/// @param _path An array containing the addresses of ERC20 tokens to swap. Adheres to UniV2 style path params.
/// @return _amountOut The amount of Asset Tokens received for the Collateral Tokens, the amount the borrowers account was credited
function repayWithCollateral(
address _swapperAddress,
uint256 _collateralToSwap,
uint256 _amountOutMin,
address[] calldata _path
) external nonReentrant isSolvent(msg.sender) returns (uint256 _amountOut) {
// Accrue interest if necessary
_addInterest();
// Update exchange rate
_updateExchangeRate();
IERC20 _debtToken = debtToken;
IERC20 _collateral = collateral;
VaultAccount memory _totalBorrow = totalBorrow;
if (!swappers[_swapperAddress]) {
revert BadSwapper();
}
if (_path[0] != address(_collateral)) {
revert InvalidPath(address(_collateral), _path[0]);
}
if (_path[_path.length - 1] != address(_debtToken)) {
revert InvalidPath(address(_debtToken), _path[_path.length - 1]);
}
//in case of a full redemption/shutdown via protocol,
//all user debt should be 0 and thus swapping to repay is unnecessary.
//toShares below will also return an incorrect value.
//in case of a full redemption, users can use the normal repayAsset with 0 cost
//or just withdraw collateral via removeCollateral
if(_totalBorrow.amount == 0){
revert InsufficientBorrowAmount();
}
// Effects: bookkeeping & write to state
// Debit users collateral balance and sends directly to the swapper
// NOTE: isSolvent checkpoints msg.sender with _syncUserRedemptions
_removeCollateral(_collateralToSwap, _swapperAddress, msg.sender);
// Even though swappers are trusted, we verify the balance before and after swap
uint256 _initialBalance = _debtToken.balanceOf(address(this));
ISwapper(_swapperAddress).swap(
msg.sender,
_collateralToSwap,
_path,
address(this)
);
// Note: VIOLATES CHECKS-EFFECTS-INTERACTION pattern, make sure function is NONREENTRANT
// Effects: bookkeeping
_amountOut = _debtToken.balanceOf(address(this)) - _initialBalance;
if (_amountOut < _amountOutMin) {
revert SlippageTooHigh(_amountOutMin, _amountOut);
}
uint256 _sharesToRepay = _totalBorrow.toShares(_amountOut, false);
//check if over user borrow shares or will revert
uint256 currentUserBorrowShares = _userBorrowShares[msg.sender];
if(_sharesToRepay > currentUserBorrowShares){
//clamp
_sharesToRepay = currentUserBorrowShares;
//readjust token amount since shares changed
_amountOut = _totalBorrow.toAmount(_sharesToRepay, true);
}
// Effects: write to state
_repay(_totalBorrow, _amountOut.toUint128(), _sharesToRepay.toUint128(), address(this), msg.sender);
//check for leftover stables that didnt go toward repaying debt
uint256 leftover = debtToken.balanceOf(address(this)) - _initialBalance;
if(leftover > 0){
//send change back to user
debtToken.transfer(msg.sender, leftover);
}
emit RepayWithCollateral(msg.sender, _swapperAddress, _collateralToSwap, _amountOut, _sharesToRepay);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: ISC
pragma solidity ^0.8.19;
struct VaultAccount {
uint128 amount; // Total amount, analogous to market cap
uint128 shares; // Total shares, analogous to shares outstanding
}
/// @title VaultAccount Library
/// @author Drake Evans (Frax Finance) github.com/drakeevans, modified from work by @Boring_Crypto github.com/boring_crypto
/// @notice Provides a library for use with the VaultAccount struct, provides convenient math implementations
/// @dev Uses uint128 to save on storage
library VaultAccountingLibrary {
/// @notice Calculates the shares value in relationship to `amount` and `total`
/// @dev Given an amount, return the appropriate number of shares
function toShares(VaultAccount memory total, uint256 amount, bool roundUp) internal pure returns (uint256 shares) {
if (total.amount == 0) {
shares = amount;
} else {
shares = (amount * total.shares) / total.amount;
if (roundUp && (shares * total.amount) / total.shares < amount) {
shares = shares + 1;
}
}
}
/// @notice Calculates the amount value in relationship to `shares` and `total`
/// @dev Given a number of shares, returns the appropriate amount
function toAmount(VaultAccount memory total, uint256 shares, bool roundUp) internal pure returns (uint256 amount) {
if (total.shares == 0) {
amount = shares;
} else {
amount = (shares * total.amount) / total.shares;
if (roundUp && total.amount > 0 && (amount * total.shares) / total.amount < shares) {
amount = amount + 1;
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IRateCalculator {
function name() external view returns (string memory);
function version() external view returns (uint256, uint256, uint256);
function getNewRate(
address _vault,
uint256 _deltaTime,
uint256 _previousShares
) external view returns (uint64 _newRatePerSec, uint128 _newShares);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface ISwapper {
function swap(
address account,
uint256 amountIn,
address[] calldata path,
address to
) external;
function swapPools(address tokenIn, address tokenOut) external view returns(address swappool, int32 tokenInIndex, int32 tokenOutIndex, uint32 swaptype);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IFeeDeposit {
function operator() external view returns(address);
function lastDistributedEpoch() external view returns(uint256);
function setOperator(address _newAddress) external;
function distributeFees() external;
function incrementPairRevenue(uint256 _fees, uint256 _otherFees) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IResupplyRegistry {
event AddPair(address pairAddress);
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event SetDeployer(address deployer, bool _bool);
function acceptOwnership() external;
function addPair(address _pairAddress) external;
function registeredPairs(uint256) external view returns (address);
function pairsByName(string memory) external view returns (address);
function defaultSwappersLength() external view returns (uint256);
function registeredPairsLength() external view returns (uint256);
function getAllPairAddresses() external view returns (address[] memory _deployedPairsArray);
function getAllDefaultSwappers() external view returns (address[] memory _defaultSwappers);
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function renounceOwnership() external;
function transferOwnership(address newOwner) external;
function claimFees(address _pair) external;
function claimRewards(address _pair) external;
function claimInsuranceRewards() external;
function withdrawTo(address _asset, uint256 _amount, address _to) external;
function mint( address receiver, uint256 amount) external;
function burn( address target, uint256 amount) external;
function liquidationHandler() external view returns(address);
function feeDeposit() external view returns(address);
function redemptionHandler() external view returns(address);
function rewardHandler() external view returns(address);
function insurancePool() external view returns(address);
function setRewardClaimer(address _newAddress) external;
function setRedemptionHandler(address _newAddress) external;
function setFeeDeposit(address _newAddress) external;
function setLiquidationHandler(address _newAddress) external;
function setInsurancePool(address _newAddress) external;
function setStaker(address _newAddress) external;
function setTreasury(address _newAddress) external;
function staker() external view returns(address);
function token() external view returns(address);
function treasury() external view returns(address);
function govToken() external view returns(address);
function l2manager() external view returns(address);
function setRewardHandler(address _newAddress) external;
function setVestManager(address _newAddress) external;
function setDefaultSwappers(address[] memory _swappers) external;
function collateralId(address _collateral) external view returns(uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IConvexStaking {
function poolInfo(uint256 _pid) external view returns(
address lptoken,
address token,
address gauge,
address crvRewards,
address stash,
bool shutdown
);
function deposit(uint256 _pid, uint256 _amount, bool _stake) external returns(bool);
function depositAll(uint256 _pid, bool _stake) external returns(bool);
function withdrawAndUnwrap(uint256 amount, bool claim) external returns(bool);
function withdrawAllAndUnwrap(bool claim) external;
function getReward() external returns(bool);
function getReward(address _account, bool _claimExtras) external returns(bool);
function totalSupply() external view returns (uint256);
function extraRewardsLength() external view returns (uint256);
function extraRewards(uint256 _rid) external view returns (address _rewardContract);
function rewardToken() external view returns (address _rewardToken);
function token() external view returns (address _token);
function balanceOf(address account) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import "../interfaces/ICore.sol";
/**
@title EpochTracker
@dev Provides a unified `startTime` and `getEpoch`, used for tracking epochs.
*/
contract EpochTracker {
uint256 public immutable startTime;
/// @notice Length of an epoch, in seconds
uint256 public immutable epochLength;
constructor(address _core) {
startTime = ICore(_core).startTime();
epochLength = ICore(_core).epochLength();
}
function getEpoch() public view returns (uint256 epoch) {
return (block.timestamp - startTime) / epochLength;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IOracle {
function decimals() external view returns (uint8);
function getPrices(address _vault) external view returns (uint256 _price);
function name() external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface ILiquidationHandler {
function operator() external view returns(address);
function setOperator(address _newAddress) external;
function processLiquidationDebt(address _collateral, uint256 _collateralAmount, uint256 _debtAmount) external;
function processCollateral(address _collateral) external;
function liquidate(
address _pair,
address _borrower
) external returns (uint256 _collateralForLiquidator);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
//abstract reward handling to attach to another contract
//supports an epoch system for supply changes
abstract contract RewardDistributorMultiEpoch is ReentrancyGuard{
using SafeERC20 for IERC20;
struct EarnedData {
address token;
uint256 amount;
}
struct RewardType {
address reward_token;
bool is_non_claimable; //a bit unothrodox setting but need to block claims on our redemption tokens as they will be processed differently
uint256 reward_remaining;
}
//rewards
RewardType[] public rewards;
uint256 public currentRewardEpoch;
mapping(address => uint256) public userRewardEpoch; //account -> epoch
mapping(uint256 => mapping(address => uint256)) public global_reward_integral; //epoch -> token -> integral
mapping(uint256 => mapping(address => mapping(address => uint256))) public reward_integral_for;// epoch -> token -> account -> integral
mapping(address => mapping(address => uint256)) public claimable_reward;//token -> account -> claimable
mapping(address => uint256) public rewardMap;
mapping(address => address) public rewardRedirect;
uint256 constant private PRECISION = 1e22;
//events
event RewardPaid(address indexed _user, address indexed _rewardToken, address indexed _receiver, uint256 _rewardAmount);
event RewardAdded(address indexed _rewardToken);
event RewardInvalidated(address indexed _rewardToken);
event RewardRedirected(address indexed _account, address _forward);
event NewEpoch(uint256 indexed _epoch);
constructor() {
}
modifier onlyRewardManager() {
require(_isRewardManager(), "!rewardManager");
_;
}
/////////
// Abstract functions
////////
function _isRewardManager() internal view virtual returns(bool);
function _fetchIncentives() internal virtual;
function _totalRewardShares() internal view virtual returns(uint256);
function _userRewardShares(address _account) internal view virtual returns(uint256);
function _increaseUserRewardEpoch(address _account, uint256 _currentUserEpoch) internal virtual;
function _checkAddToken(address _address) internal view virtual returns(bool);
//////////
function maxRewards() public pure virtual returns(uint256){
return 15;
}
//register an extra reward token to be handled
function addExtraReward(address _token) external onlyRewardManager nonReentrant{
//add to reward list
_insertRewardToken(_token);
}
//insert a new reward, ignore if already registered or invalid
function _insertRewardToken(address _token) internal{
if(_token == address(this) || _token == address(0) || !_checkAddToken(_token)){
//dont allow reward tracking of the staking token or invalid address
return;
}
//add to reward list if new
if(rewardMap[_token] == 0){
//check reward count for new additions
require(rewards.length < maxRewards(), "max rewards");
//set token
RewardType storage r = rewards.push();
r.reward_token = _token;
//set map index after push (mapped value is +1 of real index)
rewardMap[_token] = rewards.length;
emit RewardAdded(_token);
//workaround: transfer 0 to self so that earned() reports correctly
//with new tokens
if(_token.code.length > 0){
IERC20(_token).safeTransfer(address(this), 0);
}else{
//non contract address added? invalidate
_invalidateReward(_token);
}
}else{
//get previous used index of given token
//this ensures that reviving can only be done on the previous used slot
uint256 index = rewardMap[_token];
//index is rewardMap minus one
RewardType storage reward = rewards[index-1];
//check if it was invalidated
if(reward.reward_token == address(0)){
//revive
reward.reward_token = _token;
emit RewardAdded(_token);
}
}
}
//allow invalidating a reward if the token causes trouble in calcRewardIntegral
function invalidateReward(address _token) external onlyRewardManager nonReentrant{
_invalidateReward(_token);
}
function _invalidateReward(address _token) internal{
uint256 index = rewardMap[_token];
if(index > 0){
//index is registered rewards minus one
RewardType storage reward = rewards[index-1];
require(reward.reward_token == _token, "!mismatch");
//set reward token address to 0, integral calc will now skip
reward.reward_token = address(0);
emit RewardInvalidated(_token);
}
}
//get reward count
function rewardLength() external view returns(uint256) {
return rewards.length;
}
//calculate and record an account's earnings of the given reward. if _claimTo is given it will also claim.
function _calcRewardIntegral(uint256 _epoch, uint256 _currentEpoch, uint256 _index, address _account, address _claimTo) internal{
RewardType storage reward = rewards[_index];
address rewardToken = reward.reward_token;
//skip invalidated rewards
//if a reward token starts throwing an error, calcRewardIntegral needs a way to exit
if(rewardToken == address(0)){
return;
}
//get difference in balance and remaining rewards
//getReward is unguarded so we use reward_remaining to keep track of how much was actually claimed since last checkpoint
uint256 bal = IERC20(rewardToken).balanceOf(address(this));
uint256 remainingRewards = reward.reward_remaining;
//update the global integral but only for the current epoch
if (_epoch == _currentEpoch && _totalRewardShares() > 0 && bal > remainingRewards) {
uint256 rewardPerToken = ((bal - remainingRewards) * PRECISION / _totalRewardShares());
if(rewardPerToken > 0){
//increase integral
global_reward_integral[_epoch][rewardToken] += rewardPerToken;
}else{
//set balance as current reward_remaining to let dust grow
bal = remainingRewards;
}
}
uint256 reward_global = global_reward_integral[_epoch][rewardToken];
if(_account != address(0)){
//update user integrals
uint userI = reward_integral_for[_epoch][rewardToken][_account];
if(_claimTo != address(0) || userI < reward_global){
//_claimTo address non-zero means its a claim
// only allow claims if current epoch and if the reward allows it
if(_epoch == _currentEpoch && _claimTo != address(0) && !reward.is_non_claimable){
uint256 receiveable = claimable_reward[rewardToken][_account] + (_userRewardShares(_account) * (reward_global - userI) / PRECISION);
if(receiveable > 0){
claimable_reward[rewardToken][_account] = 0;
IERC20(rewardToken).safeTransfer(_claimTo, receiveable);
emit RewardPaid(_account, rewardToken, _claimTo, receiveable);
//remove what was claimed from balance
bal -= receiveable;
}
}else{
claimable_reward[rewardToken][_account] = claimable_reward[rewardToken][_account] + ( _userRewardShares(_account) * (reward_global - userI) / PRECISION);
}
reward_integral_for[_epoch][rewardToken][_account] = reward_global;
}
}
//update remaining reward so that next claim can properly calculate the balance change
//claims and tracking new rewards should only happen on current epoch
if(_epoch == _currentEpoch && bal != remainingRewards){
reward.reward_remaining = bal;
}
}
function _increaseRewardEpoch() internal{
//final checkpoint for this epoch
_checkpoint(address(0), address(0), type(uint256).max);
//move epoch up
uint256 newEpoch = currentRewardEpoch + 1;
currentRewardEpoch = newEpoch;
emit NewEpoch(newEpoch);
}
//checkpoint without claiming
function _checkpoint(address _account) internal {
//checkpoint without claiming by passing address(0)
//default to max as most operations such as deposit/withdraw etc needs to fully sync beforehand
_checkpoint(_account, address(0), type(uint256).max);
}
//checkpoint with claim
function _checkpoint(address _account, address _claimTo, uint256 _maxloops) internal {
//claim rewards first
_fetchIncentives();
uint256 globalEpoch = currentRewardEpoch;
uint256 rewardCount = rewards.length;
for (uint256 loops = 0; loops < _maxloops;) {
uint256 userEpoch = globalEpoch;
if(_account != address(0)){
//take user epoch
userEpoch = userRewardEpoch[_account];
//if no shares then jump to current epoch
if(userEpoch != globalEpoch && _userRewardShares(_account) == 0){
userEpoch = globalEpoch;
userRewardEpoch[_account] = userEpoch;
}
}
//calc reward integrals
for(uint256 i = 0; i < rewardCount;){
_calcRewardIntegral(userEpoch, globalEpoch, i,_account,_claimTo);
unchecked { i += 1; }
}
if(userEpoch < globalEpoch){
_increaseUserRewardEpoch(_account, userEpoch);
}else{
return;
}
unchecked { loops += 1; }
}
}
//manually checkpoint a user account
function user_checkpoint(address _account, uint256 _epochloops) external nonReentrant returns(bool) {
_checkpoint(_account, address(0), _epochloops);
return true;
}
//get earned token info
//change ABI to view to use this off chain
function earned(address _account) public nonReentrant virtual returns(EarnedData[] memory claimable) {
//because this is a state mutative function
//we can simplify the earned() logic of all rewards (internal and external)
//and allow this contract to be agnostic to outside reward contract design
//by just claiming everything and updating state via _checkpoint()
_checkpoint(_account);
uint256 rewardCount = rewards.length;
claimable = new EarnedData[](rewardCount);
for (uint256 i = 0; i < rewardCount;) {
RewardType storage reward = rewards[i];
//skip invalidated and non claimable rewards
if(reward.reward_token == address(0) || reward.is_non_claimable){
unchecked{ i += 1; }
continue;
}
claimable[i].amount = claimable_reward[reward.reward_token][_account];
claimable[i].token = reward.reward_token;
unchecked{ i += 1; }
}
return claimable;
}
//set any claimed rewards to automatically go to a different address
//set address to zero to disable
function setRewardRedirect(address _to) external nonReentrant{
rewardRedirect[msg.sender] = _to;
emit RewardRedirected(msg.sender, _to);
}
//claim reward for given account (unguarded)
function getReward(address _account) public virtual nonReentrant {
//check if there is a redirect address
address redirect = rewardRedirect[_account];
if(redirect != address(0)){
_checkpoint(_account, redirect, type(uint256).max);
}else{
//claim directly in checkpoint logic to save a bit of gas
_checkpoint(_account, _account, type(uint256).max);
}
}
//claim reward for given account and forward (guarded)
function getReward(address _account, address _forwardTo) public virtual nonReentrant{
//in order to forward, must be called by the account itself
require(msg.sender == _account, "!self");
require(_forwardTo != address(0), "fwd address cannot be 0");
//use _forwardTo address instead of _account
_checkpoint(_account, _forwardTo, type(uint256).max);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
/*
A token-like contract to track write offs via erc20 interfaces to be used
in reward distribution logic
interfaces needed:
- balanceOf
- transfer
- mint
*/
contract WriteOffToken {
address public immutable owner;
uint256 public totalSupply;
constructor(address _owner)
{
owner = _owner;
}
function name() external pure returns (string memory){
return "WriteOffToken";
}
function symbol() external pure returns (string memory){
return "WOT";
}
function decimals() external pure returns (uint8){
return 18;
}
function mint(uint256 _amount) external{
if(msg.sender == owner){
totalSupply += _amount;
}
}
function transfer(address to, uint256 amount) external returns (bool){
return true;
}
function balanceOf(address) external view returns (uint256){
//just return total supply
return totalSupply;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed caller,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
function asset() external view returns (address);
function convertToAssets(uint256 shares) external view returns (uint256);
function convertToShares(uint256 assets) external view returns (uint256);
function maxDeposit(address) external view returns (uint256);
function maxMint(address) external view returns (uint256);
function maxRedeem(address owner) external view returns (uint256);
function maxWithdraw(address owner) external view returns (uint256);
function previewDeposit(uint256 assets) external view returns (uint256);
function previewMint(uint256 shares) external view returns (uint256);
function previewRedeem(uint256 shares) external view returns (uint256);
function previewWithdraw(uint256 assets) external view returns (uint256);
function totalAssets() external view returns (uint256);
function mint(uint256 shares, address receiver) external returns (uint256 assets);
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {ICore} from "../interfaces/ICore.sol";
/**
@title Core Ownable
@author Prisma Finance (with edits by Resupply Finance)
@notice Contracts inheriting `CoreOwnable` have the same owner as `Core`.
The ownership cannot be independently modified or renounced.
*/
contract CoreOwnable {
ICore public immutable core;
constructor(address _core) {
core = ICore(_core);
}
modifier onlyOwner() {
require(msg.sender == address(core), "!core");
_;
}
function owner() public view returns (address) {
return address(core);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IMintable{
function mint(address _to, uint256 _amount) external;
function burn(address _from, uint256 _amount) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import { IAuthHook } from './IAuthHook.sol';
interface ICore {
struct OperatorAuth {
bool authorized;
IAuthHook hook;
}
event VoterSet(address indexed newVoter);
event OperatorExecuted(address indexed caller, address indexed target, bytes data);
event OperatorSet(address indexed caller, address indexed target, bool authorized, bytes4 selector, IAuthHook authHook);
function execute(address target, bytes calldata data) external returns (bytes memory);
function epochLength() external view returns (uint256);
function startTime() external view returns (uint256);
function voter() external view returns (address);
function ownershipTransferDeadline() external view returns (uint256);
function pendingOwner() external view returns (address);
function setOperatorPermissions(
address caller,
address target,
bytes4 selector,
bool authorized,
IAuthHook authHook
) external;
function setVoter(address newVoter) external;
function operatorPermissions(address caller, address target, bytes4 selector) external view returns (bool authorized, IAuthHook hook);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IAuthHook {
function preHook(address operator, address target, bytes calldata data) external returns (bool);
function postHook(bytes memory result, address operator, address target, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}{
"remappings": [
"ds-test/=node_modules/ds-test/src/",
"forge-std/=node_modules/forge-std/src/",
"frax-std/=node_modules/frax-standard-solidity/src/",
"@axelar-network/=node_modules/@axelar-network/",
"@chainlink/=node_modules/@chainlink/",
"@eth-optimism/=node_modules/@eth-optimism/",
"@layerzerolabs/=node_modules/@layerzerolabs/",
"@mean-finance/=node_modules/@mean-finance/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@rari-capital/=node_modules/@rari-capital/",
"@uniswap/=node_modules/@uniswap/",
"base64-sol/=node_modules/base64-sol/",
"frax-standard-solidity/=node_modules/frax-standard-solidity/",
"hardhat-deploy/=node_modules/hardhat-deploy/",
"hardhat/=node_modules/hardhat/",
"solidity-bytes-utils/=node_modules/solidity-bytes-utils/",
"solidity-stringutils/=lib/surl/lib/solidity-stringutils/",
"surl/=lib/surl/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_core","type":"address"},{"internalType":"bytes","name":"_configData","type":"bytes"},{"internalType":"bytes","name":"_immutables","type":"bytes"},{"internalType":"bytes","name":"_customConfigData","type":"bytes"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BadSwapper","type":"error"},{"inputs":[],"name":"BorrowerSolvent","type":"error"},{"inputs":[],"name":"FeesAlreadyDistributed","type":"error"},{"inputs":[],"name":"IncorrectStakeBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"_borrow","type":"uint256"},{"internalType":"uint256","name":"_collateral","type":"uint256"},{"internalType":"uint256","name":"_exchangeRate","type":"uint256"}],"name":"Insolvent","type":"error"},{"inputs":[],"name":"InsufficientBorrowAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"},{"internalType":"uint256","name":"_request","type":"uint256"}],"name":"InsufficientDebtAvailable","type":"error"},{"inputs":[],"name":"InsufficientDebtToRedeem","type":"error"},{"inputs":[],"name":"InvalidLiquidator","type":"error"},{"inputs":[],"name":"InvalidParameter","type":"error"},{"inputs":[{"internalType":"address","name":"_expected","type":"address"},{"internalType":"address","name":"_actual","type":"address"}],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidReceiver","type":"error"},{"inputs":[],"name":"InvalidRedemptionHandler","type":"error"},{"inputs":[],"name":"MinimumRedemption","type":"error"},{"inputs":[],"name":"OnlyProtocolOrOwner","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintDowncast","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"_minOut","type":"uint256"},{"internalType":"uint256","name":"_actual","type":"uint256"}],"name":"SlippageTooHigh","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"AddCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"interestEarned","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"}],"name":"AddInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":true,"internalType":"address","name":"_receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"_borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_sharesAdded","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_mintFees","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"address","name":"_swapperAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_borrowShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_initialUnderlyingAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountCollateralOut","type":"uint256"}],"name":"LeveragedPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"_collateralForLiquidator","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_sharesLiquidated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountLiquidatorToRepay","type":"uint256"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"NewEpoch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_collateralFreed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_protocolFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_debtReduction","type":"uint256"}],"name":"Redeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"_receiver","type":"address"},{"indexed":true,"internalType":"address","name":"_borrower","type":"address"}],"name":"RemoveCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"payer","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountToRepay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":false,"internalType":"address","name":"_swapperAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_collateralToSwap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amountAssetOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_sharesRepaid","type":"uint256"}],"name":"RepayWithCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_rewardToken","type":"address"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_rewardToken","type":"address"}],"name":"RewardInvalidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":true,"internalType":"address","name":"_rewardToken","type":"address"},{"indexed":true,"internalType":"address","name":"_receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"_rewardAmount","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_account","type":"address"},{"indexed":false,"internalType":"address","name":"_forward","type":"address"}],"name":"RewardRedirected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"limit","type":"uint256"}],"name":"SetBorrowLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"pid","type":"uint256"}],"name":"SetConvexPool","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldLiquidationFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLiquidationFee","type":"uint256"}],"name":"SetLiquidationFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldMaxLTV","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMaxLTV","type":"uint256"}],"name":"SetMaxLTV","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"min","type":"uint256"}],"name":"SetMinimumBorrowAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"min","type":"uint256"}],"name":"SetMinimumLeftover","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"min","type":"uint256"}],"name":"SetMinimumRedemption","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldMintFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMintFee","type":"uint256"}],"name":"SetMintFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOracle","type":"address"},{"indexed":false,"internalType":"address","name":"newOracle","type":"address"}],"name":"SetOracleInfo","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"SetProtocolRedemptionFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldRateCalculator","type":"address"},{"indexed":false,"internalType":"address","name":"newRateCalculator","type":"address"}],"name":"SetRateCalculator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"swapper","type":"address"},{"indexed":false,"internalType":"bool","name":"approval","type":"bool"}],"name":"SetSwapper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"exchangeRate","type":"uint256"}],"name":"UpdateExchangeRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldRatePerSec","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"oldShares","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"newRatePerSec","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"newShares","type":"uint128"}],"name":"UpdateRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"interestFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"otherFees","type":"uint256"}],"name":"WithdrawFees","type":"event"},{"inputs":[],"name":"CRV","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CVX","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXCHANGE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIQ_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LTV_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAIR_DECIMALS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RATE_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SHARE_REFACTOR_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_borrower","type":"address"}],"name":"addCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"internalType":"address","name":"_borrower","type":"address"}],"name":"addCollateralVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"addExtraReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_returnAccounting","type":"bool"}],"name":"addInterest","outputs":[{"internalType":"uint256","name":"_interestEarned","type":"uint256"},{"components":[{"internalType":"uint64","name":"lastTimestamp","type":"uint64"},{"internalType":"uint64","name":"ratePerSec","type":"uint64"},{"internalType":"uint128","name":"lastShares","type":"uint128"}],"internalType":"struct ResupplyPairCore.CurrentRateInfo","name":"_currentRateInfo","type":"tuple"},{"internalType":"uint256","name":"_claimableFees","type":"uint256"},{"components":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"shares","type":"uint128"}],"internalType":"struct VaultAccount","name":"_totalBorrow","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_borrowAmount","type":"uint256"},{"internalType":"uint256","name":"_underlyingAmount","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimableFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimableOtherFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"claimable_reward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"convexBooster","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"convexPid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"core","outputs":[{"internalType":"contract ICore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentRateInfo","outputs":[{"internalType":"uint64","name":"lastTimestamp","type":"uint64"},{"internalType":"uint64","name":"ratePerSec","type":"uint64"},{"internalType":"uint128","name":"lastShares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentRewardEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentUtilization","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"earned","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct RewardDistributorMultiEpoch.EarnedData[]","name":"claimable","type":"tuple[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epochLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exchangeRateInfo","outputs":[{"internalType":"address","name":"oracle","type":"address"},{"internalType":"uint96","name":"lastTimestamp","type":"uint96"},{"internalType":"uint256","name":"exchangeRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getConstants","outputs":[{"internalType":"uint256","name":"_LTV_PRECISION","type":"uint256"},{"internalType":"uint256","name":"_LIQ_PRECISION","type":"uint256"},{"internalType":"uint256","name":"_EXCHANGE_PRECISION","type":"uint256"},{"internalType":"uint256","name":"_RATE_PRECISION","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getEpoch","outputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPairAccounting","outputs":[{"internalType":"uint256","name":"_claimableFees","type":"uint256"},{"internalType":"uint128","name":"_totalBorrowAmount","type":"uint128"},{"internalType":"uint128","name":"_totalBorrowShares","type":"uint128"},{"internalType":"uint256","name":"_totalCollateral","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_forwardTo","type":"address"}],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"getUserSnapshot","outputs":[{"internalType":"uint256","name":"_borrowShares","type":"uint256"},{"internalType":"uint256","name":"_collateralBalance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"global_reward_integral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"invalidateReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastFeeEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_swapperAddress","type":"address"},{"internalType":"uint256","name":"_borrowAmount","type":"uint256"},{"internalType":"uint256","name":"_initialUnderlyingAmount","type":"uint256"},{"internalType":"uint256","name":"_amountCollateralOutMin","type":"uint256"},{"internalType":"address[]","name":"_path","type":"address[]"}],"name":"leveragedPosition","outputs":[{"internalType":"uint256","name":"_totalCollateralBalance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"}],"name":"liquidate","outputs":[{"internalType":"uint256","name":"_collateralForLiquidator","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidationFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLTV","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"minimumBorrowAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumLeftoverDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumRedemption","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"previewAddInterest","outputs":[{"internalType":"uint256","name":"_interestEarned","type":"uint256"},{"components":[{"internalType":"uint64","name":"lastTimestamp","type":"uint64"},{"internalType":"uint64","name":"ratePerSec","type":"uint64"},{"internalType":"uint128","name":"lastShares","type":"uint128"}],"internalType":"struct ResupplyPairCore.CurrentRateInfo","name":"_newCurrentRateInfo","type":"tuple"},{"internalType":"uint256","name":"_claimableFees","type":"uint256"},{"components":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"shares","type":"uint128"}],"internalType":"struct VaultAccount","name":"_totalBorrow","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolRedemptionFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rateCalculator","outputs":[{"internalType":"contract IRateCalculator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_totalFeePct","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"redeemCollateral","outputs":[{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"uint256","name":"_collateralFreed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redemptionWriteOff","outputs":[{"internalType":"contract WriteOffToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"removeCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_collateralAmount","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"removeCollateralVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"address","name":"_borrower","type":"address"}],"name":"repay","outputs":[{"internalType":"uint256","name":"_amountToRepay","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapperAddress","type":"address"},{"internalType":"uint256","name":"_collateralToSwap","type":"uint256"},{"internalType":"uint256","name":"_amountOutMin","type":"uint256"},{"internalType":"address[]","name":"_path","type":"address[]"}],"name":"repayWithCollateral","outputs":[{"internalType":"uint256","name":"_amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardMap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardRedirect","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"reward_integral_for","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewards","outputs":[{"internalType":"address","name":"reward_token","type":"address"},{"internalType":"bool","name":"is_non_claimable","type":"bool"},{"internalType":"uint256","name":"reward_remaining","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"name":"setBorrowLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pid","type":"uint256"}],"name":"setConvexPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newLiquidationFee","type":"uint256"}],"name":"setLiquidationFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newMaxLTV","type":"uint256"}],"name":"setMaxLTV","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_min","type":"uint256"}],"name":"setMinimumBorrowAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_min","type":"uint256"}],"name":"setMinimumLeftoverDebt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_min","type":"uint256"}],"name":"setMinimumRedemption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newMintFee","type":"uint256"}],"name":"setMintFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOracle","type":"address"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setProtocolRedemptionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newRateCalculator","type":"address"},{"internalType":"bool","name":"_updateInterest","type":"bool"}],"name":"setRateCalculator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"setRewardRedirect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapper","type":"address"},{"internalType":"bool","name":"_approval","type":"bool"}],"name":"setSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"swappers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"},{"internalType":"bool","name":"_roundUp","type":"bool"},{"internalType":"bool","name":"_previewInterest","type":"bool"}],"name":"toBorrowAmount","outputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_roundUp","type":"bool"},{"internalType":"bool","name":"_previewInterest","type":"bool"}],"name":"toBorrowShares","outputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrow","outputs":[{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"shares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateral","outputs":[{"internalType":"uint256","name":"_totalCollateralBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDebtAvailable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateExchangeRate","outputs":[{"internalType":"uint256","name":"_exchangeRate","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"userBorrowShares","outputs":[{"internalType":"uint256","name":"borrowShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"userCollateralBalance","outputs":[{"internalType":"uint256","name":"_collateralAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userRewardEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_epochloops","type":"uint256"}],"name":"user_checkpoint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"_major","type":"uint256"},{"internalType":"uint256","name":"_minor","type":"uint256"},{"internalType":"uint256","name":"_patch","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"withdrawFees","outputs":[{"internalType":"uint256","name":"_fees","type":"uint256"},{"internalType":"uint256","name":"_otherFees","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
0x6101a0806040523461088057617916803803809161001d8285610e6f565b83398101906080818303126108805761003581610e92565b60208201519091906001600160401b0381116108805783610057918301610eeb565b60408201519091906001600160401b0381116108805784610079918301610eeb565b60608201519094906001600160401b038111610880576100999201610eeb565b9160018060a01b0316928360805260015f5568056bc75e2d63100000600e5569021e19e0c9bab2400000600f55683635c9adc5dea00000601055602081805181010312610880576004906020906001600160a01b03906100fa908301610e92565b168060a05260405192838092637e062a3560e11b82525afa90811561088c575f91610dff575b506001600160a01b031660c0528051610100908201829003126108805761014960208201610e92565b61015560408301610e92565b9161016260608201610e92565b9260808201519360a08301519060c08401519261010060e08601519501519560018060a01b03168060e05260405163313ce56760e01b8152602081600481855afa90811561088c5760129160ff915f91610de0575b501603610d63576040516338d52e0f60e01b8152602081600481855afa90811561088c575f91610da1575b506001600160a01b031661010081905260405163313ce56760e01b815290602090829060049082905afa90811561088c5760129160ff915f91610d72575b501603610d6357610100516024916020916102459082906001600160a01b031661166b565b6040516363737ac960e11b8152670de0b6b3a7640000600482015292839182905afa90811561088c575f91610d31575b50601480546001600160801b031660809290921b6001600160801b031916919091179055601580546001600160a01b03199081166001600160a01b03938416179091556011805490911692909116919091179055600a55600c55600b55600d55600955604051610330808201906001600160401b03821183831017610c1a57602091839161750683393081520301905ff0801561088c57610120819052610324906001600160a01b03166114cb565b60015415610d1d5760015f525f5160206178b65f395f51905f52805460ff60a01b1916600160a01b179055805160208281019291610366919083010183610f08565b5050815191949190506001600160401b038111610c1a57601354600181811c91168015610d13575b6020821014610cff57601f8111610c9c575b50602094601f8211600114610c39579481929394955f92610c2e575b50508160011b915f199060031b1c1916176013555b5f604080516103df81610e39565b82815282602082015201526040516103f681610e39565b6014546001600160401b038082168352604082811c821660208501908152608084901c858301908152915190949093909290919060a0850190811185821017610c1a576040525f845260208401915f835260408501935f855260608601965f8852608087019660405161046881610e54565b5f8082526020820152885282516001600160401b03164211610a56575b511515958661091d575b5050505050505050506040516104a481610e39565b6015546001600160a01b03811680835260a09190911c60208301818152601654604085018181529194919242146109125750602060018060a01b0360e051166024604051809681936315caaba160e21b835260048301525afa92831561088c575f936108de575b5082156108ca57426001600160601b0381169094526ec097ce7bc90715b34b9f10000000009290920490819052905160a09290921b6001600160a01b0319166001600160a01b03929092169190911760155560168190556040519081527f4fc1b45960547ee95894b08a284c3c066cf5aca706a7420639c42c3ec2e118a490602090a15b6040516378e9792560e01b8152602081600481855afa90811561088c575f91610897575b5061014052604051630afaeebf60e31b815290602090829060049082905afa90811561088c575f91610853575b509160206105fa9261060f94610160528051010190610f08565b936001600160a01b03918216935091166114cb565b806107f4575b604051615d4d90816117b982396080518181816109ee01528181610a3501528181610b75015281816119ad01528181611ac101528181611bd601528181611f85015281816121bd0152818161235101528181612522015281816125a1015281816129130152818161297d015281816135db015281816137900152614972015260a05181818161048201528181611f340152818161267501528181612a1d01528181612ad6015281816149ba01528181614d4801528181614ead015281816150170152818161516301526153b0015260c05181818161139101528181613158015281816148680152615955015260e05181818161061501528181610b2b01528181610d3101528181610e7c01528181610ffe015281816113e601528181611de401528181612d2e0152818161310401528181613ed50152818161419e015281816146cd01528181614d19015281816155ba015261592d015261010051818181610ce101528181610fd5015281816116480152612243015261012051818181610654015281816128cd01526142b80152610140518181816120690152818161215601526126ef01526101605181818161217d0152612456015261018051818181611c5601528181612f0301528181613df201528181614c7e015261580b0152f35b7fa83ced135f4f6135be6fb52ae9183bf190452765f5b75b36caa332537c3ca7e591610835826020936101805282601d5560018060a01b0360e0511661166b565b61083d610fb3565b610845611277565b604051908152a15f80610615565b9290506020833d602011610884575b8161086f60209383610e6f565b8101031261088057915160206105e0565b5f80fd5b3d9150610862565b6040513d5f823e3d90fd5b90506020813d6020116108c2575b816108b260209383610e6f565b81010312610880575160046105b3565b3d91506108a5565b634e487b7160e01b5f52601260045260245ffd5b9092506020813d60201161090a575b816108fa60209383610e6f565b810103126108805751915f61050b565b3d91506108ed565b93505050505061058f565b97518151855187518b51604080516001600160401b0395861681526001600160801b039485166020828101919091529390951690850152909116606083015299610a2298509196907f6b9ef8676ff86d806b7a7bd7a9b0266910c9fce560c26289d7ed7cd7743127c790608090a18051604080518981526001600160401b039092168c8301527f939dcec711228d083924a0db6cecbb66bb8403ea7cdbfe2f928901cbac2cdfc191a1516001600160401b0380821690935290516001600160801b038116909552429091169182905260401b6fffffffffffffffff0000000000000000161760809290921b6001600160801b031916919091176014555060185461171e565b60185551805191015160801b6001600160801b0319166001600160801b0391909116176017555f808080808080808061048f565b60018152604051610a6681610e54565b6017546001600160801b038116825260801c6020820152885282516001600160401b031642908103908111610b795760115460e0518751604080516356fb8f2d60e01b81526001600160a01b039384166004820152602481018690526001600160801b0390921660448301529092909183916064918391165afa801561088c575f915f91610bb7575b506001600160801b039081168a526001600160401b039091168852895151670de0b6b3a764000092610b3792610b26921690610f62565b88516001600160401b031690610f62565b04808a528015159081610b95575b5015610b8d5788518851805190916001600160801b0391821690821601908111610b79576001600160801b03169052610485565b634e487b7160e01b5f52601160045260245ffd5b5f8952610485565b8951516001600160801b039250610baf919083169061171e565b11155f610b45565b9150506040813d604011610c12575b81610bd360409383610e6f565b81010312610880578051906001600160401b03821682036108805760200151906001600160801b03821682036108805790670de0b6b3a7640000610aef565b3d9150610bc6565b634e487b7160e01b5f52604160045260245ffd5b015190505f806103bc565b601f1982169560135f52805f20915f5b888110610c8457508360019596979810610c6c575b505050811b016013556103d1565b01515f1960f88460031b161c191690555f8080610c5e565b91926020600181928685015181550194019201610c49565b60135f527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090601f830160051c81019160208410610cf5575b601f0160051c01905b818110610cea57506103a0565b5f8155600101610cdd565b9091508190610cd4565b634e487b7160e01b5f52602260045260245ffd5b90607f169061038e565b634e487b7160e01b5f52603260045260245ffd5b90506020813d602011610d5b575b81610d4c60209383610e6f565b8101031261088057515f610275565b3d9150610d3f565b630309cb8760e51b5f5260045ffd5b610d94915060203d602011610d9a575b610d8c8183610e6f565b810190610f75565b5f610220565b503d610d82565b90506020813d602011610dd8575b81610dbc60209383610e6f565b81010312610880576020610dd1600492610e92565b91506101e2565b3d9150610daf565b610df9915060203d602011610d9a57610d8c8183610e6f565b5f6101b7565b90506020813d602011610e31575b81610e1a60209383610e6f565b8101031261088057610e2b90610e92565b5f610120565b3d9150610e0d565b606081019081106001600160401b03821117610c1a57604052565b604081019081106001600160401b03821117610c1a57604052565b601f909101601f19168101906001600160401b03821190821017610c1a57604052565b51906001600160a01b038216820361088057565b9192916001600160401b038211610c1a5760405191610ecf601f8201601f191660200184610e6f565b829481845281830111610880578281602093845f96015e010152565b9080601f83011215610880578151610f0592602001610ea6565b90565b91906080838203126108805782516001600160401b0381116108805783019080601f83011215610880578151610f4092602001610ea6565b91610f4d60208201610e92565b916060610f5c60408401610e92565b92015190565b81810292918115918404141715610b7957565b90816020910312610880575160ff811681036108805790565b600154811015610d1d5760015f81815291901b5f5160206178b65f395f51905f520191565b305f5160206178765f395f51905f52148015611270575b8015611253575b6110d8575f5160206178765f395f51905f525f5260076020525f5160206178d65f395f51905f52546111d457600154600f8110156111a15768010000000000000000811015610c1a5780600161102a9201600155610f8e565b5080546001600160a01b0319165f5160206178765f395f51905f529081179091556001545f82815260076020525f5160206178d65f395f51905f52919091555f5160206178565f395f51905f529080a25f5160206178765f395f51905f523b156110da5760405163a9059cbb60e01b60208201523060248201525f60448201526110d8906110c581606481015b03601f198101835282610e6f565b5f5160206178765f395f51905f52611760565b565b5f5160206178765f395f51905f525f5260076020525f5160206178d65f395f51905f5254806111065750565b5f198101908111610b795761111a90610f8e565b5080546001600160a01b031673d533a949740bb3306d119cc777fa900ba034cd5119016111705780546001600160a01b03191690555f5160206178765f395f51905f525f5160206178965f395f51905f525f80a2565b60405162461bcd60e51b8152602060048201526009602482015268042dad2e6dac2e8c6d60bb1b6044820152606490fd5b60405162461bcd60e51b815260206004820152600b60248201526a6d6178207265776172647360a81b6044820152606490fd5b5f5160206178765f395f51905f525f5260076020525f5160206178d65f395f51905f52545f198101908111610b795761120c90610f8e565b5080546001600160a01b0316156112205750565b80546001600160a01b0319165f5160206178765f395f51905f529081179091555f5160206178565f395f51905f525f80a2565b5061126a5f5160206178765f395f51905f5261172b565b15610fd1565b505f610fca565b305f5160206178f65f395f51905f521480156114c4575b80156114a7575b6110d8575f5160206178f65f395f51905f525f5260076020525f5160206178365f395f51905f525461142857600154600f8110156111a15768010000000000000000811015610c1a578060016112ee9201600155610f8e565b5080546001600160a01b0319165f5160206178f65f395f51905f529081179091556001545f82815260076020525f5160206178365f395f51905f52919091555f5160206178565f395f51905f529080a25f5160206178f65f395f51905f523b156113925760405163a9059cbb60e01b60208201523060248201525f60448201526110d89061137f81606481016110b7565b5f5160206178f65f395f51905f52611760565b5f5160206178f65f395f51905f525f5260076020525f5160206178365f395f51905f5254806113be5750565b5f198101908111610b79576113d290610f8e565b5080546001600160a01b0316734e3fbd56cd56c3e72c1403e103b45db9da5b9d2a19016111705780546001600160a01b03191690555f5160206178f65f395f51905f525f5160206178965f395f51905f525f80a2565b5f5160206178f65f395f51905f525f5260076020525f5160206178365f395f51905f52545f198101908111610b795761146090610f8e565b5080546001600160a01b0316156114745750565b80546001600160a01b0319165f5160206178f65f395f51905f529081179091555f5160206178565f395f51905f525f80a2565b506114be5f5160206178f65f395f51905f5261172b565b15611295565b505f61128e565b6001600160a01b038116903082148015611663575b8015611653575b6115ad57815f52600760205260405f2054155f146115f957600154600f8110156111a15768010000000000000000811015610c1a5780600161152c9201600155610f8e565b508260018060a01b0319825416179055600154825f52600760205260405f2055815f5160206178565f395f51905f525f80a23b1561159a5760405163a9059cbb60e01b60208201523060248201525f60448083019190915281526110d891611595606483610e6f565b611760565b805f52600760205260405f2054806115b1575b5050565b5f198101908111610b79576115c590610f8e565b5080546001600160a01b03168290036111705780546001600160a01b03191690555f5160206178965f395f51905f525f80a2565b50805f52600760205260405f20545f198101908111610b795761161b90610f8e565b5080546001600160a01b031615611630575050565b80546001600160a01b031916821790555f5160206178565f395f51905f525f80a2565b5061165d8161172b565b156114e7565b5081156114e0565b6040519060205f8184019463095ea7b360e01b865260018060a01b03169485602486015281196044860152604485526116a5606486610e6f565b84519082855af15f513d826116f9575b5050156116c157505050565b6115956110d8936040519063095ea7b360e01b602083015260248201525f6044820152604481526116f3606482610e6f565b82611760565b90915061171657506001600160a01b0381163b15155b5f806116b5565b60011461170f565b91908201809211610b7957565b60e0516001600160a01b039182169116811461175b5760c0516001600160a01b03161461175757600190565b5f90565b505f90565b905f602091828151910182855af11561088c575f513d6117af57506001600160a01b0381163b155b61178f5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b6001141561178856fe60806040526004361015610011575f80fd5b5f5f3560e01c80628cc2621461382357806302ce728f146137f557806306b6f7e91461377657806306fdde031461365957806308a0c375146135c157806308c7e366146135a45780630cab937d146130385780630df8dfac1461300f57806313966db514612ff1578063146a29eb14612fd15780631c6c959714612f505780632164e85b14612f325780632b3ba681146110be5780632cdacb5014612eed5780632f86556814612a9e57806333fd6f7414612a4b5780633f2617cb146129575780633f4ba83a146128fc57806342f43d0b146128b7578063476343ee1461264357806348741376146125795780634ac8eb5f1461255e5780634cae6513146124e45780634fd422df146124c057806354fd4d501461249757806356ecf28b1461247957806357d775f81461243e5780635e43c47b1461240957806360c52d05146123c05780636551f16c146123365780636b091695146122725780636f307dc31461222d578063730a7514146121a2578063757991a81461213b578063759cb53b1461210c57806375a410141461208c57806378e97925146120515780637adbf97314611f635780637b10399914611f1e5780637e92968414611bbb5780637ec4b57114611b7a5780638049d97114611afd5780638214ba4814611a835780638285ef4014611a5457806383d4433914611a1b5780638456cb591461199657806386993bef146119785780638cad7fbe146119395780638da5cb5b146109d85780638f50ea921461191b57806391ebebbd146118bc57806392bbcaed1461189e57806393ae0df91461187757806393f46f6414611831578063945c91421461180257806395d14ca8146117c05780639a295e731461177a5780639fe34bdc14611158578063a053db68146112af578063a36a363014611291578063ab7cfaf914611273578063acb70815146111f5578063b5af3062146111d1578063b5b5454714611198578063b68d0a091461115d578063b78294dd14611158578063b95c57461461113a578063c00007b0146110e1578063c0a7e892146110be578063c3192f14146110c3578063c49bb2d1146110be578063ca51bb59146110a0578063cacf3b5814611074578063cadac47914610f86578063cdd72d5214610f2c578063d41ddc9614610dee578063d619658b14610dd2578063d6bda0c014610bf4578063d870ce6614610b5a578063d8dfeb4514610b15578063e509b9d914610ad4578063e551d11d14610ab6578063e69bc27114610a71578063e7a3317414610a1d578063f2f4eb26146109d8578063f301af4214610982578063f384bd0514610964578063f5c7f89914610927578063f8112eed146108e6578063fbbbf94c146108ac578063fd6d0526146104325763fff5d9da1461040d575f80fd5b3461042f578060031936011261042f5760206104276140ee565b604051908152f35b80fd5b503461042f57608036600319011261042f5761044c6139b7565b90606435906024356001600160a01b0383168084036108a85761046d614145565b60405163e8673ef560e01b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561089d57849161085f575b506001600160a01b03163303610850578015908115610846575b5061083757600e548110610828576104ea6144d9565b505050604435670de0b6b3a764000003670de0b6b3a764000081116108145761051c670de0b6b3a76400009183613d36565b0493670de0b6b3a764000061053d6105348785613b9b565b600d5490613d36565b04946105498684613b9b565b90610552613c9e565b6001600160801b03815116838181119182156107ff575b50506107f0576001600160801b03610586818516828451166140ce565b169081815264e8d4a5100082029180830464e8d4a5100014901517156107525760208101916001600160801b0383511611610766575b51905160801b6001600160801b0319166001600160801b039190911617601755670de0b6b3a764000090610608905b6105f789601954613d67565b601955610602614163565b90613d36565b0495610613876157e2565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169561064a908890886158e3565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016620186a088810290898204148915171561075257813b1561074e57869160248392604051948593849263140e25ad60e31b845260048401525af180156107435761072a575b5060408051948552602085018890528401526060830152600192916001600160a01b03909116907f76cd0cedf979345ca241ce6de696a520a8efc860c6c10d9db2a7953307c237fc90608090a255604080516001600160a01b039092168252602082019290925290819081015b0390f35b610735868092613a57565b61073f575f6106b9565b8480fd5b6040513d88823e3d90fd5b8680fd5b634e487b7160e01b87526011600452602487fd5b61076e615160565b600254600181018091116107dc576001600160801b036106089381670de0b6b3a76400009694846105eb956002557febad8099c467528a56c98b63c8d476d251cf1ffb4c75db94b4d23fa2b6a1e3358d80a28164e8d4a51000818551160416835294965050935050506105bc565b634e487b7160e01b88526011600452602488fd5b6316c6823f60e01b8652600486fd5b6108099250613b9b565b600f5411835f610569565b634e487b7160e01b83526011600452602483fd5b6362171e8f60e01b8252600482fd5b631e4ec46b60e01b8252600482fd5b905030145f6104d4565b63d520e5ad60e01b8352600483fd5b90506020813d602011610895575b8161087a60209383613a57565b810103126108915761088b90613d22565b5f6104ba565b8380fd5b3d915061086d565b6040513d86823e3d90fd5b8280fd5b503461042f578060031936011261042f57601554601654604080516001600160a01b038416815260a09390931c6020840152820152606090f35b503461042f57602036600319011261042f576109206109036139b7565b61091361090e61496f565b613f6e565b61091b614145565b615736565b6001815580f35b503461042f57604036600319011261042f576001906109586109476139b7565b61094f614145565b60243590615012565b55602060405160018152f35b503461042f578060031936011261042f576020600954604051908152f35b503461042f57602036600319011261042f576004359060015482101561042f5760606109ad83613b63565b506001815491015460ff6040519260018060a01b038116845260a01c16151560208301526040820152f35b503461042f578060031936011261042f576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461042f57602036600319011261042f57610a63337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b610a6e600435614930565b80f35b503461042f57604036600319011261042f576040610a8d6139cd565b9160043581526004602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461042f578060031936011261042f576020600a54604051908152f35b503461042f57602036600319011261042f576020906001600160a01b03610af96139b7565b16815260088252604060018060a01b0391205416604051908152f35b503461042f578060031936011261042f576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461042f57602036600319011261042f57600435610ba3337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b68056bc75e2d631000008110610be5576020817febc3aac103344d02f2e1b3fb50ca740929143c84082dd6e7698963e902aa636992600e55604051908152a180f35b630309cb8760e51b8252600482fd5b503461042f57606036600319011261042f57602435610c116139e3565b610c19614145565b610c22336142a5565b6001600160a01b03811615610dc357610c396144d9565b505050610c44614163565b5081610cd3575b610c609150610c5b60043561473e565b6152af565b906040610c6b613c70565b01610c77815133614335565b15610c8b5760208360018455604051908152f35b60649250610cad610c9a613c9e565b338452601b6020526040842054906143d7565b90338352601a6020526040832054905191633b49de0f60e21b8452600452602452604452fd5b602082610d05610d2c9430337f000000000000000000000000000000000000000000000000000000000000000061524b565b604051636e553f6560e01b8152600481019190915230602482015292839081906044820190565b0381867f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af1918215610db8578392610d7e575b50610d79610c6092339030614be4565b610c4b565b91506020823d602011610db0575b81610d9960209383613a57565b81010312610dac57905190610d79610d69565b5f80fd5b3d9150610d8c565b6040513d85823e3d90fd5b631e4ec46b60e01b8352600483fd5b503461042f578060031936011261042f576020604051600f8152f35b503461042f57604036600319011261042f57600435610e0b6139cd565b610e13614145565b610e1c336142a5565b6001600160a01b03168015610dc357610e336144d9565b505050338352601b6020526040832054610f1e575b610e53333084614659565b604051635d043b2960e11b815260048101929092526024820152306044820152602081606481857f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015610f1357610ee4575b506040610ebc613c70565b01610ec8815133614335565b15610ed557506001815580f35b90606491610cad610c9a613c9e565b610f059060203d602011610f0c575b610efd8183613a57565b810190613ce8565b505f610eb1565b503d610ef3565b6040513d84823e3d90fd5b610f26614163565b50610e48565b503461042f578060031936011261042f57610f45613ba8565b506080610f5061402c565b915091506001600160801b0360208183511692015116610f6e613dcc565b91604051938452602084015260408301526060820152f35b503461042f57604036600319011261042f57600435610fa36139cd565b90610fac614145565b6001600160a01b03821615610dc357906020610ff992610fca6144d9565b505050610d058130337f000000000000000000000000000000000000000000000000000000000000000061524b565b0381867f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015610db8578390611040575b610920925030614be4565b506020823d60201161106c575b8161105a60209383613a57565b81010312610dac576109209151611035565b3d915061104d565b503461042f578060031936011261042f5761072661109061402c565b9060409492945194859485613a87565b503461042f578060031936011261042f576020601c54604051908152f35b613ae1565b503461042f578060031936011261042f576020601854604051908152f35b503461042f57602036600319011261042f576110fb6139b7565b611103614145565b6001600160a01b0381811683526008602052604083205416801561112a5761092091614eab565b508061113591614eab565b610920565b503461042f578060031936011261042f576020600154604051908152f35b613b2f565b503461042f57602036600319011261042f5760406111796139b7565b61118b61118582613fab565b91613f10565b9082519182526020820152f35b503461042f57602036600319011261042f576020906040906001600160a01b036111c06139b7565b168152600383522054604051908152f35b503461042f57602036600319011261042f5760206104276111f06139b7565b613fab565b503461042f57604036600319011261042f576004356112126139cd565b61121a614145565b6001600160a01b03811615610dc35760209261126a60019261123a6144d9565b505050611245613c9e565b61124f86826143d7565b9561126261125c8861473e565b9161473e565b90339261476f565b55604051908152f35b503461042f578060031936011261042f576020600d54604051908152f35b503461042f578060031936011261042f576020600c54604051908152f35b503461042f5760a036600319011261042f576112c96139b7565b60243591906064356044356084356001600160401b03811161073f573660238201121561073f5780600401356112fe81613b4c565b9161130c6040519384613a57565b818352602083016024819360051b8301019136831161177657602401905b82821061175e5750505061133c614145565b611345336142a5565b859061134f6144d9565b50505061135a614163565b506001600160a01b0386168088526012602052604088205490969060ff161561174f576001600160a01b0361138e85613c1b565b517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169116819003611739575083515f198101908111611725576001600160a01b03906113e39086613c28565b517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169291168290036116e9578561163e575b61142b90610c5b8b61473e565b93604051926370a0823160e01b8452306004850152602084602481865afa938415611633578a946115ff575b50883b156115fb57604051634b4ecc5560e11b8152336004820152602481018c9052608060448201529151608483018190528a90839060a4820190835b8181106115d6575050819293503060648301520381838c5af180156115cb579089916115b2575b5050906020602492604051938480926370a0823160e01b82523060048301525afa80156115a7578890611573575b6114f39250613b9b565b9380851061155c5750836115119161150c338330614be4565b613d67565b9560405194855260208501526040840152606083015260808201527fb19ca0df3f3a01af950d8e6ad62aeff167cf14c73e98af6c52afef1add5c97ed60a03392a26040610c6b613c70565b633b5d56ed60e11b87526004526024849052604486fd5b506020823d60201161159f575b8161158d60209383613a57565b81010312610dac576114f391516114e9565b3d9150611580565b6040513d8a823e3d90fd5b816115bc91613a57565b6115c757875f6114bb565b8780fd5b6040513d8b823e3d90fd5b85516001600160a01b031683526020958601958f955087945090920191600101611494565b8980fd5b9093506020813d60201161162b575b8161161b60209383613a57565b81010312610dac5751925f611457565b3d915061160e565b6040513d8c823e3d90fd5b925061166c8530337f000000000000000000000000000000000000000000000000000000000000000061524b565b604051636e553f6560e01b8152600481018690523060248201526020816044818c865af180156115cb5789906116b5575b61142b91506116ad338230614be4565b93905061141e565b506020813d6020116116e1575b816116cf60209383613a57565b81010312610dac5761142b905161169d565b3d91506116c2565b8489838251925f198401938411610814576044936001600160a01b03916117109190613c28565b51169063b0b3262d60e01b8352600452602452fd5b634e487b7160e01b89526011600452602489fd5b60449089906001600160a01b0361171088613c1b565b631311dc6d60e01b8852600488fd5b6020809161176b846139f9565b81520191019061132a565b8880fd5b503461042f578060031936011261042f576080906040519050620186a08152620186a06020820152670de0b6b3a76400006040820152670de0b6b3a76400006060820152f35b503461042f578060031936011261042f576060601454604051906001600160401b03811682526001600160401b038160401c16602083015260801c6040820152f35b503461042f578060031936011261042f57602060405173d533a949740bb3306d119cc777fa900ba034cd528152f35b503461042f5760209061184336613b03565b9192509015611861576104279161185861402c565b925050506146f8565b6118729161186d613c9e565b6146f8565b610427565b503461042f578060031936011261042f57602061042761189561402c565b92505050615209565b503461042f578060031936011261042f576020600f54604051908152f35b503461042f57606036600319011261042f576118d66139cd565b60406118e06139e3565b9260043581526005602052209060018060a01b03165f5260205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b503461042f578060031936011261042f576020600e54604051908152f35b503461042f57602036600319011261042f5760209060ff906040906001600160a01b036119646139b7565b168152601284522054166040519015158152f35b503461042f578060031936011261042f576020601d54604051908152f35b503461042f578060031936011261042f576119db337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b600a54806119e7575080f35b601e5580600a557fbf1ce7fb3a8e648b70ea830f99b52f7ea31554186d29763280751f42e77f63866020604051838152a180f35b503461042f57602036600319011261042f576020906040906001600160a01b03611a436139b7565b168152600783522054604051908152f35b503461042f578060031936011261042f5760406017548151906001600160801b038116825260801c6020820152f35b503461042f57602036600319011261042f577fc98711414b05c67ac27ffb415026d97ad5094c5490747891a834cb44f64940d96020600435611aef337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b80600f55604051908152a180f35b503461042f57604036600319011261042f57611b176139cd565b611b1f614145565b611b28336142a5565b6001600160a01b0381161561083757611b6290611b436144d9565b505050338352601b6020526040832054611b6c575b3390600435614659565b6040610ebc613c70565b611b74614163565b50611b58565b503461042f57602090611b8c36613b03565b9192509015611baa5761042791611ba161402c565b92505050614475565b61187291611bb6613c9e565b614475565b503461042f57602036600319011261042f57600435611c04337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b81601d54828103611c3f575b507fa83ced135f4f6135be6fb52ae9183bf190452765f5b75b36caa332537c3ca7e5602083604051908152a180f35b604051631526fe2760e01b815260048101919091527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169060c081602481855afa908115610db8578391611ee9575b506040516370a0823160e01b8152306004820152906001600160a01b0316602082602481845afa91821561089d578492611eb2575b5081611d95575b5090606460209260405194859384926321d0683360e11b84528860048501526024840152600160448401525af18015610db857611d37575b50601d819055817fa83ced135f4f6135be6fb52ae9183bf190452765f5b75b36caa332537c3ca7e5611c10565b6020813d602011611d8d575b81611d5060209383613a57565b810103126108a8577fa83ced135f4f6135be6fb52ae9183bf190452765f5b75b36caa332537c3ca7e591611d85602092613cf7565b509150611d0a565b3d9150611d43565b60209060446040959394955180948193636197390160e11b83528760048401528160248401525af18015611e7057611e7b575b506040516370a0823160e01b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015611e705782918691611e3b575b5010611e2c579083915f611cd2565b6346bc68ed60e01b8452600484fd5b9150506020813d602011611e68575b81611e5760209383613a57565b81010312610dac578190515f611e1d565b3d9150611e4a565b6040513d87823e3d90fd5b6020813d602011611eaa575b81611e9460209383613a57565b8101031261073f57611ea590613cf7565b611dc8565b3d9150611e87565b935090506020833d602011611ee1575b81611ecf60209383613a57565b81010312610dac57849251905f611ccb565b3d9150611ec2565b611f0b915060c03d60c011611f17575b611f038183613a57565b810190613d74565b5050925050505f611c96565b503d611ef9565b503461042f578060031936011261042f576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461042f57602036600319011261042f576040611f7f6139b7565b611fb3337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b612021611fbe613c70565b805184516001600160a01b03918216815290841660208201529092907fb9d23ad01dc54c1fad84c770708fbe314a369b505c073949334dad786229805e90604090a16001600160a01b0316808352601580546001600160a01b0319169091179055565b6020810151601580546001600160a01b031660a09290921b6001600160a01b031916919091179055015160165580f35b503461042f578060031936011261042f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461042f57602036600319011261042f576120a66139b7565b6120ae614145565b3380835260086020908152604080852080546001600160a01b0319166001600160a01b039590951694851790555192835290917ff4239ad0860f93469699dd4be8040b8838c5e25bb6cf24a1dfb381b937ff078c9190a26001815580f35b503461042f578060031936011261042f576020604051734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b8152f35b503461042f578060031936011261042f57602061042761217b7f000000000000000000000000000000000000000000000000000000000000000042613b9b565b7f000000000000000000000000000000000000000000000000000000000000000090613d49565b503461042f57602036600319011261042f576004356121eb337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b620186a08111610be5577f1f93e1cc6afaef2ddc4d7ef7b0f45ac3278d548338c839e9be18695732a662746040600c548151908152836020820152a1600c5580f35b503461042f578060031936011261042f576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461042f57604036600319011261042f5761228c6139b7565b6122946139cd565b61229c614145565b6001600160a01b0382163303612309576001600160a01b038116156122c45761092091614eab565b60405162461bcd60e51b815260206004820152601760248201527f66776420616464726573732063616e6e6f7420626520300000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b6044820152606490fd5b503461042f57602036600319011261042f5760043561237f337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b670de0b6b3a76400008111610be5576020817f3e0c428758b3ad6ab4fd85e8257e4eee404fb36e80c0251143635b549cd70b8892600d55604051908152a180f35b503461042f57604036600319011261042f576123da6139cd565b6123e2614145565b6001600160a01b0381161561083757610920906123fd6144d9565b50505060043533614be4565b503461042f57602036600319011261042f576109206124266139b7565b61243161090e61496f565b612439614145565b614a3b565b503461042f578060031936011261042f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461042f578060031936011261042f576020600254604051908152f35b503461042f578060031936011261042f5760609060405190600382528060208301526040820152f35b503461042f57602036600319011261042f5760206104276124df6139b7565b613f10565b503461042f57602036600319011261042f577f18e1a8f58cc03bc99f69c27336072db255c3f01827f2923f654ddc209e2b8db56020600435612550337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b80601055604051908152a180f35b503461042f578060031936011261042f576020610427613dcc565b503461042f57604036600319011261042f576125936139b7565b61259b613a78565b6125cf337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b612633575b601154604080516001600160a01b038084168252841660208201529192917f74cd8ef76f78382ae0f3ee4e21117be974af0041a24dc98f7aa8ddabb0b2960c9190a16001600160a01b03166001600160a01b0319919091161760115580f35b61263b6144d9565b5050506125d4565b503461042f578060031936011261042f5761265c614145565b6126646144d9565b5050604051630abca72960e41b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169150602081600481855afa908115610db857839161287d575b506040516379bd9e4160e01b81526001600160a01b03821690602081600481855afa908115611e7057859161284b575b5061271461217b7f000000000000000000000000000000000000000000000000000000000000000042613b9b565b90601c54821190811591612840575b5061283157601c5583601854936019549382601855826019556127468587613d67565b823b15610891576040516340c10f1960e01b81526001600160a01b0392909216600483015260248201529082908290604490829084905af18015610f135761281c575b5050803b15610891576040516362f2221960e01b8152836004820152826024820152848160448183865af18015611e7057612807575b506040937f9cc800ba322ea82ab3e1e911dd4ccd84129687c4952f4ee2f937e3ac68755131606060019387519081528660208201528588820152a15582519182526020820152f35b612812858092613a57565b610891575f6127bf565b8161282691613a57565b61089157835f612789565b630e1b248d60e01b8552600485fd5b90508114155f612723565b90506020813d602011612875575b8161286660209383613a57565b81010312610dac57515f6126e6565b3d9150612859565b90506020813d6020116128af575b8161289860209383613a57565b810103126108a8576128a990613d22565b5f6126b6565b3d915061288b565b503461042f578060031936011261042f576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461042f578060031936011261042f57612941337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b600a541561294c5780f35b610a6e601e54614930565b503461042f57604036600319011261042f576129716139b7565b612979613a78565b90337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316148015612a19575b15612a0a577fea1eefb4fd58778d7b274fe54045a9feeec8f2847899c2e71126d3a74d486da59160409160018060a01b03169081855260126020528285209015159060ff1981541660ff831617905582519182526020820152a180f35b631d1e647b60e01b8352600483fd5b50337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316146129ad565b503461042f57604036600319011261042f576040612a676139b7565b91612a706139cd565b9260018060a01b031681526006602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461042f57602036600319011261042f57612ab86139b7565b612ac0614145565b60405163d25adeb360e01b8152906020826004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa918215610db8578392612eb1575b506001600160a01b0382169133839003612ea2576001600160a01b0382168015612e9357612b3a6144d9565b505050612b45614163565b612b4e846142a5565b612b588185614335565b612e8457612b64613c9e565b90828752601a602052604087205490838852601b602052612b88604089205461473e565b926001600160801b0384169260208201926001600160801b03845116155f14612ded57612bbe670de0b6b3a76400009186613d36565b04600c54620186a0019081620186a011612dd9576001600160801b0398969492620186a0612bf5612c739d9c9a9896948c94613d36565b0481811115612dcf57509a8b955b877f158ba9ab7bbbd08eeffa4753bad41f4d450e24831d293427308badf3eadd8c766060612c39612c348a896143d7565b61473e565b9d8e6040519b8c528a60208d015216998a6040820152a2612c598a614d44565b82612c678c828751166140ce565b168452828551166140ce565b168252848b52601b602052612c8c8360408d2054613b9b565b858c52601b6020528060408d20558015159081612dba575b50612dab5751905160801b6001600160801b0319166001600160801b03919091161760175588969594612d109490939092909188917fe4a1ae657f49cb1fb1c7d3a94ae6093565c4c8c0e03de488f79c377c3c3a24e0916040919082519182526020820152a386614659565b813b156108a857604051632be71c5f60e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166004820152602481018590526001600160801b0391909116604482015291908290606490829084905af18015610db857612d92575b50600160209255604051908152f35b612d9d838092613a57565b612da7575f612d83565b5080fd5b631abfe8a760e01b8b5260048bfd5b612dc59150826143d7565b601054115f612ca4565b90509a8b95612c03565b634e487b7160e01b8c52601160045260248cfd5b612e15612e046001600160801b0385511687613d36565b6001600160801b0386511690613d49565b8b80612e54575b612e34575b670de0b6b3a764000091612bbe91613d36565b6001810180911115612e2157634e487b7160e01b8c52601160045260248cfd5b5085612e7e612e6d6001600160801b0388511684613d36565b6001600160801b0387511690613d49565b10612e1c565b633af2cafd60e11b8652600486fd5b631e4ec46b60e01b8552600485fd5b6387e9041360e01b8452600484fd5b9091506020813d602011612ee5575b81612ecd60209383613a57565b810103126108a857612ede90613d22565b905f612b0e565b3d9150612ec0565b503461042f578060031936011261042f576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461042f578060031936011261042f576020601954604051908152f35b503461042f57602036600319011261042f576004358015158103612da757612f76613d04565b50610726604051612f8681613a0d565b5f81525f6020820152612f97614145565b83612fa06144d9565b96915094612fb9575b6001905560405194859485613a87565b9150506018546001612fc9613c9e565b929050612fa9565b503461042f578060031936011261042f57602060405164e8d4a510008152f35b503461042f578060031936011261042f576020600b54604051908152f35b503461042f578060031936011261042f576011546040516001600160a01b039091168152602090f35b5034610dac576080366003190112610dac576130526139b7565b60443591906064356024356001600160401b038211610dac5736602383011215610dac578160040135926001600160401b038411610dac576024830192602436918660051b010111610dac576130a6614145565b6130af336142a5565b6130b76144d9565b5050506130c2614163565b506130cb613c9e565b9260018060a01b03821694855f52601260205260ff60405f20541615613595578015613581576130fa82613cd4565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911681900361357857505f198101928184116135645761314e613149858486613cc4565b613cd4565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811695911685900361353257506001600160801b0386511615613523576131a090339086614659565b6040516370a0823160e01b815230600482015291602083602481875afa9283156134b6575f936134ef575b50863b15610dac57604051634b4ecc5560e11b815233600482015260248101869052608060448201526084810183905291829160a48301915f5b8181106134c15750505090805f923060648301520381838a5af180156134b6576134a1575b506040516370a0823160e01b8152306004820152602081602481865afa80156133ed578290889061346b575b6132609250613b9b565b968088106134545750856001600160801b03855116155f14613410575086935b338752601b60205260408720548086116133f8575b506132b6906132a38961473e565b339130916132b08961473e565b9161476f565b6040516370a0823160e01b8152306004820152602081602481865afa9081156133ed57908793929184916133b4575b50906132f091613b9b565b8061333f575b505050604051928352602083015283604083015260608201527f6b2b212085c14b2dc48ec56d19c05084e5d8ef233539cdd3dc0d70a4ec7f4f9e60803392a26040610c6b613c70565b60405163a9059cbb60e01b815233600482015260248101919091529160209183916044918391905af18015611e705761337b575b8085916132f6565b6020813d6020116133ac575b8161339460209383613a57565b8101031261073f576133a590613cf7565b505f613373565b3d9150613387565b919350506020813d6020116133e5575b816133d160209383613a57565b81010312610dac57518692906132f06132e5565b3d91506133c4565b6040513d89823e3d90fd5b945096506132b661340985896143d7565b9790613295565b9361343c61342b6001600160801b036020840151168a613d36565b6001600160801b0383511690613d49565b94156132805793600181018091116107525793613280565b633b5d56ed60e11b87526004526024879052604486fd5b50506020813d602011613499575b8161348660209383613a57565b81010312610dac57816132609151613256565b3d9150613479565b6134ae9196505f90613a57565b5f945f61322a565b6040513d5f823e3d90fd5b9193509160019060209081906001600160a01b036134de886139f9565b168152019401910191849392613205565b9092506020813d60201161351b575b8161350b60209383613a57565b81010312610dac5751915f6131cb565b3d91506134fe565b631abfe8a760e01b5f5260045ffd5b90506131499161354193613cc4565b63b0b3262d60e01b5f9081526004929092526001600160a01b0316602452604490fd5b634e487b7160e01b5f52601160045260245ffd5b61354183613cd4565b634e487b7160e01b5f52603260045260245ffd5b631311dc6d60e01b5f5260045ffd5b34610dac575f366003190112610dac576020601054604051908152f35b34610dac576020366003190112610dac57600435613609337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b620186a0811161364a577fe796e9ae748449310fcd1cc6718aab236c9b8d2e0e04dacb232ba564d5b338cc60406009548151908152836020820152a1600955005b630309cb8760e51b5f5260045ffd5b34610dac575f366003190112610dac576040515f90601354918260011c6001841693841561376c575b60208210851461375857818452602084019490811561373d57506001146136df575b50906136b4816040930382613a57565b8151928391602083525180918160208501528484015e5f828201840152601f01601f19168101030190f35b91905060135f527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090915f905b808210613723575090915081016020016136b46136a4565b91926001816020925483858801015201910190929161370b565b60ff1916855250151560051b820160200190506136b46136a4565b634e487b7160e01b5f52602260045260245ffd5b90607f1690613682565b34610dac576020366003190112610dac576004356137be337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614613c3c565b7f388cba11a61d4b31467a533c88eef75abfb6097ff2ab4652d086560fffcc2f836040600b548151908152836020820152a1600b55005b34610dac575f366003190112610dac5761380d614145565b6020613817614163565b60015f55604051908152f35b34610dac576020366003190112610dac5761383c6139b7565b613844614145565b61384d81614d44565b60015461385981613bc0565b916001600160a01b03165f5b8281106139405760015f5583515f19810190859082116135645761388882613bc0565b9160015b818111156138ee57836040518091602082016020835281518091526020604084019201905f5b8181106138c0575050500390f35b825180516001600160a01b0316855260209081015181860152869550604090940193909201916001016138b2565b60206138fa8285613c28565b510151905f1981018181116135645760019260206139188389613c28565b510152613938838060a01b0361392e8488613c28565b5151169187613c28565b51520161388c565b8061394c600192613b63565b5054828060a01b0381169081159081156139a9575b506139a157805f52600660205260405f20845f5260205260405f205460206139898489613c28565b5101526139968287613c28565b515201915b91613865565b50019161399b565b60ff915060a01c1687613961565b600435906001600160a01b0382168203610dac57565b602435906001600160a01b0382168203610dac57565b604435906001600160a01b0382168203610dac57565b35906001600160a01b0382168203610dac57565b604081019081106001600160401b03821117613a2857604052565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b03821117613a2857604052565b90601f801991011681019081106001600160401b03821117613a2857604052565b602435908115158203610dac57565b926001600160801b0391959460209183604060c09660e089019a89526001600160401b03815116868a01526001600160401b038682015116828a0152015116606087015260808601528281511660a0860152015116910152565b34610dac575f366003190112610dac576020604051670de0b6b3a76400008152f35b6060906003190112610dac57600435906024358015158103610dac57906044358015158103610dac5790565b34610dac575f366003190112610dac576020604051620186a08152f35b6001600160401b038111613a285760051b60200190565b6001548110156135815760015f81815291901b7fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60191565b9190820391821161356457565b60405190613bb582613a0d565b5f6020838281520152565b90613bca82613b4c565b613bd76040519182613a57565b8281528092613be8601f1991613b4c565b01905f5b828110613bf857505050565b602090604051613c0781613a0d565b5f81525f8382015282828501015201613bec565b8051156135815760200190565b80518210156135815760209160051b010190565b15613c4357565b60405162461bcd60e51b815260206004820152600560248201526421636f726560d81b6044820152606490fd5b60405190613c7d82613a3c565b6015546001600160a01b038116835260a01c60208301526016546040830152565b60405190613cab82613a0d565b6017546001600160801b038116835260801c6020830152565b91908110156135815760051b0190565b356001600160a01b0381168103610dac5790565b90816020910312610dac575190565b51908115158203610dac57565b60405190613d1182613a3c565b5f6040838281528260208201520152565b51906001600160a01b0382168203610dac57565b8181029291811591840414171561356457565b8115613d53570490565b634e487b7160e01b5f52601260045260245ffd5b9190820180921161356457565b91908260c0910312610dac57613d8982613d22565b91613d9660208201613d22565b91613da360408301613d22565b91613db060608201613d22565b91613dc960a0613dc260808501613d22565b9301613cf7565b90565b601d548015613eb957604051631526fe2760e01b8152600481019190915260c0816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156134b6576024916020915f91613e94575b506040516370a0823160e01b815230600482015292839182906001600160a01b03165afa9081156134b6575f91613e65575090565b90506020813d602011613e8c575b81613e8060209383613a57565b81010312610dac575190565b3d9150613e73565b613ead915060c03d60c011611f1757611f038183613a57565b5050925050505f613e30565b506040516370a0823160e01b81523060048201526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156134b6575f91613e65575090565b6001600160a01b03165f908152601b6020908152604080832054600254600390935292205491929190808210613f44575050565b909264e8d4a510006001915b04930192818414613f695764e8d4a51000600191613f50565b925050565b15613f7557565b60405162461bcd60e51b815260206004820152600e60248201526d10b932bbb0b93226b0b730b3b2b960911b6044820152606490fd5b613fb3614145565b613fbc816142a5565b6001600160a01b03165f908152601a6020526040902054613fdb613dcc565b9081811115613fed57505b9060015f55565b9050613fe6565b6040519061400182613a3c565b8160406014546001600160401b03811683526001600160401b0381831c16602084015260801c910152565b5f614035613d04565b5060405161404281613a0d565b5f81525f602082015250614054613ff4565b906001600160401b034216825261406a82615507565b8051909190156140bf57506060810151926001600160401b0360208301511660208401526001600160801b03604083015116604084015260806140b96018546001600160801b03871690613d67565b92015190565b92905060185490613dc9613c9e565b906001600160801b03809116911603906001600160801b03821161356457565b600a548015614138576001600160801b0361410761402c565b92505050511690670de0b6b3a7640000820291808304670de0b6b3a7640000149015171561356457613dc991613d49565b50670de0b6b3a764000090565b60025f54146141545760025f55565b633ee5aeb560e01b5f5260045ffd5b61416b613c70565b9060208201916001600160601b038351164214155f1461429d5780516040516315caaba160e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015290929160209184916024918391165afa9182156134b6575f92614269575b508115613d53576001600160601b03429081169094526ec097ce7bc90715b34b9f1000000000919091046040808301829052915160a09490941b6001600160a01b0319166001600160a01b03909416939093176015556016839055518281527f4fc1b45960547ee95894b08a284c3c066cf5aca706a7420639c42c3ec2e118a490602090a1565b9091506020813d602011614295575b8161428560209383613a57565b81010312610dac5751905f6141e2565b3d9150614278565b604001519150565b6142ae81614d44565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081165f90815260066020908152604080832094909316808352938152828220805490839055601a9091529190205490620186a0900480821061432c5761431c91613b9b565b905b5f52601a60205260405f2055565b50505f9061431e565b906009549182156143cf5761436b61434b613c9e565b6001600160a01b039092165f818152601b602052604090205490926143d7565b9081156143c6575f52601a60205260405f20549182156143be5761438e91613d36565b90620186a0820291808304620186a0149015171561356457670de0b6b3a76400006143b99204613d49565b111590565b505050505f90565b50505050600190565b505050600190565b6020810180516001600160801b03166143f0575050905b565b90929161441b61440a6001600160801b0386511683613d36565b6001600160801b0384511690613d49565b936001600160801b0381511615159283614448575b50505061443957565b90600181018091116135645790565b61446c9293506144636001600160801b038092511687613d36565b91511690613d49565b105f8080614430565b909160208201916001600160801b03835116155f146144945750505090565b6144b0612e6d6001600160801b03839796949597511684613d36565b94836144c5575b836144485750505061443957565b81516001600160801b0316151593506144b7565b5f905f906144e5613d04565b506144ee613ff4565b906144f882615507565b8051151580614505575050565b9194509250926143ee6001600160801b03602060806060870151968287016001600160401b0381511690604089019186835116907f6b9ef8676ff86d806b7a7bd7a9b0266910c9fce560c26289d7ed7cd7743127c786888701926001600160401b038451169460408901958c875116916040519384528c84015260408301526060820152a17f939dcec711228d083924a0db6cecbb66bb8403ea7cdbfe2f928901cbac2cdfc160408d6001600160401b0384511682519182528a820152a151916001600160401b0383169052519186831690526001600160401b034216808a526fffffffffffffffff00000000000000006014549260401b169187191617176014558460145491811990821990861b161691161760145561462888601854613d67565b601855015182815116831960175416176017550151166001600160801b036017549181199060801b16911617601755565b9160207fba68c7a8c8efbddb7e938ee32ecc28a68930c18105a3a0ab9563eac7f051cf5a9160018060a01b031693845f52601a825260405f2061469d828254613b9b565b90556146a8816157e2565b6001600160a01b03841693813086036146c7575b5050604051908152a3565b6146f1917f00000000000000000000000000000000000000000000000000000000000000006158e3565b5f816146bc565b90916001600160801b03825116155f1461471157505090565b9092916020840161472f612e6d6001600160801b0383511684613d36565b94836144485750505061443957565b6001600160801b038111614758576001600160801b031690565b6306dfcc6560e41b5f52608060045260245260445ffd5b9391909161477c81614d44565b6001600160801b0361479184828851166140ce565b16855260208501906001600160801b036147ae84828551166140ce565b16825260018060a01b031694855f52601b6020526147db6001600160801b0360405f205494168094613b9b565b865f52601b6020528060405f2055801515908161491b575b506135235751905160801b6001600160801b0319166001600160801b0391909116176017556001600160a01b0383169283614865575b507fe4a1ae657f49cb1fb1c7d3a94ae6093565c4c8c0e03de488f79c377c3c3a24e0916040916001600160801b038351921682526020820152a3565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610dac57604051632770a7eb60e21b81526001600160a01b039490941660048501526001600160801b03821660248501525f908490604490829084905af19182156134b6577fe4a1ae657f49cb1fb1c7d3a94ae6093565c4c8c0e03de488f79c377c3c3a24e09360409361490b575b50915091614829565b5f61491591613a57565b5f614902565b6149269150826143d7565b601054115f6147f3565b6001600160801b03811161364a576020817fbf1ce7fb3a8e648b70ea830f99b52f7ea31554186d29763280751f42e77f638692600a55604051908152a1565b337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480156149a45790565b5060405163670fb82160e01b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156134b6575f91614a01575b506001600160a01b0316331490565b90506020813d602011614a33575b81614a1c60209383613a57565b81010312610dac57614a2d90613d22565b5f6149f2565b3d9150614a0f565b6001600160a01b038116903082148015614bdc575b8015614bcc575b614b9257815f52600760205260405f2054155f14614b5e57600154600f811015614b2b5768010000000000000000811015613a2857806001614a9c9201600155613b63565b50826001600160601b0360a01b825416179055600154825f52600760205260405f2055817fb13fd610fe4e1b384966826794a9b2f6100ad031f352cc5ec6f22667f60749805f80a2803b15614b2157506143ee906040519063a9059cbb60e01b60208301523060248301525f604483015260448252614b1c606483613a57565b615ce8565b6143ee9150615736565b60405162461bcd60e51b815260206004820152600b60248201526a6d6178207265776172647360a81b6044820152606490fd5b50805f52600760205260405f20545f19810190811161356457614b8090613b63565b5080546001600160a01b031615614b96575b5050565b80546001600160a01b031916821790557fb13fd610fe4e1b384966826794a9b2f6100ad031f352cc5ec6f22667f60749805f80a2565b50614bd68161591f565b15614a57565b508115614a50565b9160018060a01b031691825f52601a60205260405f20614c05838254613d67565b905581306001600160a01b03831603614d11575b5050601d5480614c52575b5060207f2ac90482c3b6bea30a2c085cf093016bad7f970d91c5fd233e6b848d3e231dc391604051908152a2565b6040516321d0683360e11b8152600481019190915260248101829052600160448201526020816064815f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af180156134b65715614c24576020813d602011614d09575b81614ccc60209383613a57565b81010312610dac577f2ac90482c3b6bea30a2c085cf093016bad7f970d91c5fd233e6b848d3e231dc391614d01602092613cf7565b509150614c24565b3d9150614cbf565b614d3d9130907f000000000000000000000000000000000000000000000000000000000000000061524b565b5f81614c19565b5f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610dac575f8091602460405180948193633bd73ee360e21b83523060048401525af180156134b657614e96575b506002549060015490835b5f198110614dba575050505050565b836001600160a01b03831680614e4d575b865b858110614e26575085821015614e1d57808752601b60205264e8d4a51000604088205404818852601b60205260408820556001820180921161075257865260036020526040862055600101614dab565b50505050505050565b80614e3e89876001948b889c999d9e9a979b98615990565b01919490939796929591614dcd565b8087526003602052604087205491508582141580614e82575b15614dcb57905084908087526003602052856040882055614dcb565b50808752601b602052604087205415614e66565b614ea39192505f90613a57565b5f905f614da0565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691823b15610dac57604051633bd73ee360e21b81525f816024818380983060048401525af180156134b657614ffd575b506002549160015492845b5f198110614f2057505050505050565b816001600160a01b03841680614fb4575b875b878110614f8d575083821015614f8357808852601b60205264e8d4a51000604089205404818952601b6020526040892055600182018092116107dc57875260036020526040872055600101614f10565b5050505050505050565b80614fa5888860019489889d999e9f9b979c98615990565b01929691959094989792614f33565b8088526003602052604088205491508382141580614fe9575b15614f3157905082908088526003602052836040892055614f31565b50808852601b602052604088205415614fcd565b61500a9193505f90613a57565b5f915f614f05565b5f91907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610dac575f8091602460405180948193633bd73ee360e21b83523060048401525af180156134b65761514b575b506002549060015491845b84811061508957505050505050565b816001600160a01b03841680615102575b875b8681106150ec575083821015614f8357808852601b60205264e8d4a51000604089205404818952601b6020526040892055600182018092116107dc5787526003602052604087205560010161507a565b806150fc8a886001948988615990565b0161509c565b8088526003602052604088205491508382141580615137575b1561509a5790508290808852600360205283604089205561509a565b50808852601b60205260408820541561511b565b6151589193505f90613a57565b5f915f61506f565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610dac575f8091602460405180948193633bd73ee360e21b83523060048401525af180156134b6576151f6575b5060025460015491905f1982106151d157505050565b815b8381106151e05750505050565b806151f084806001948680615990565b016151d3565b61520291505f90613a57565b5f5f6151bb565b600a5490516001600160801b0316808211156152435761522891613b9b565b6001600160801b03811115613dc957506001600160801b0390565b50505f615228565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526143ee91614b1c608483613a57565b906001600160801b03809116911601906001600160801b03821161356457565b9190916152ba613c9e565b92601054916001600160801b03811692831061352357600b54620186a00180620186a011613564576152f0620186a09185613d36565b046152fa86615209565b8181106154f2575085516001600160801b031661549357805b61537b82976001600160801b036020820191816153546153368883875116613d67565b8261534c6153438a61473e565b8287511661528f565b16845261473e565b1692839052516001600160801b0391161660809190911b6001600160801b03191617601755565b335f52601b60205260405f20615392838254613d67565b9055848082111561548a576153a691613b9b565b915b82615476575b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610dac576040516340c10f1960e01b81526001600160a01b03861660048201526001600160801b039290921660248301525f908290604490829084905af180156134b657615466575b506040519384526020840152604083015260018060a01b0316907f10a0132d3bf8c82a7fb93a86160f3074ca5c3e5706fa2bcdf0e2b5fd495af09b60603392a3565b5f61547091613a57565b5f615424565b61548283601954613d67565b6019556153ae565b50505f916153a8565b6020860190806154da6154c46154b36001600160801b0386511684613d36565b6001600160801b038b511690613d49565b936001600160801b03614463818c511687613d36565b10156153135790600181018091116135645790615313565b63586ce21f60e11b5f5260045260245260445ffd5b9060405160a081018181106001600160401b03821117613a28576040525f8152602081015f815260408201935f855260608301915f8352608084019360405161554f81613a0d565b5f81525f6020820152855280966001600160401b038451164211615576575b505050505050565b600161560d9252615585613c9e565b8652604061559d6001600160401b0386511642613b9b565b6011549582015182516356fb8f2d60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018490526001600160801b0390921660448201529196919485929190911690829081906064820190565b03915afa80156134b6575f925f916156cf575b50670de0b6b3a7640000946001600160401b03615667959481946001600160801b0361565e951690521684526001600160801b038851511690613d36565b91511690613d36565b0480825280151590816156ae575b50156156a5576156966001600160801b03809251169251928284511661528f565b1690525b5f808080808061556e565b5f91505261569a565b6001600160801b0391506156c790828551511690613d67565b11155f615675565b939250506040833d60401161572e575b816156ec60409383613a57565b81010312610dac578251926001600160401b0384168403610dac5760200151906001600160801b0382168203610dac5791929190670de0b6b3a7640000615620565b3d91506156df565b6001600160a01b03165f8181526007602052604090205480615756575050565b5f1981019081116135645761576a90613b63565b5080546001600160a01b03168290036157b15780546001600160a01b03191690557f646cfe9445aed85f4853d501d1924d2bdabb1bbf12531df29f929f07ba4169e05f80a2565b60405162461bcd60e51b8152602060048201526009602482015268042dad2e6dac2e8c6d60bb1b6044820152606490fd5b601d54806157ee575050565b604051631526fe2760e01b8152600481019190915260c0816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa80156134b6575f9260209284926158b9575b50604051636197390160e11b815260048101919091526024810184905292839160449183916001600160a01b03165af180156134b6576158845750565b6020813d6020116158b1575b8161589d60209383613a57565b81010312610dac576158ae90613cf7565b50565b3d9150615890565b60449192506158d69060c03d60c011611f1757611f038183613a57565b5050925050509190615847565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526143ee91614b1c606483613a57565b6001600160a01b03908116907f000000000000000000000000000000000000000000000000000000000000000016811461598b577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161461598757600190565b5f90565b505f90565b93929161599c90613b63565b5080546001600160a01b03169290831561556e576040516370a0823160e01b815230600482015293602085602481845afa9485156134b6575f95615cb4575b508496600183019687549582149686888099615ca6575b80615c9d575b615c1c575b50505f8281526004602090815260408083206001600160a01b03878116855292529091205495169384615a4e575b50505050505081615a43575b50615a40575050565b55565b90508214155f615a37565b825f52600560205260405f2060018060a01b0385165f5260205260405f20855f5260205260405f20548860018060a01b03841692831580158091615c13575b615a9b575b50505050615a2b565b82615c0b575b5081615bfb575b5015615b9b57615af690855f52600660205260405f20875f5260205269021e19e0c9bab2400000615aef60405f205492895f52601b60205261060260405f2054918c613b9b565b0490613d67565b9182615b38575b5050505b5f52600560205260405f209060018060a01b03165f5260205260405f20905f5260205260405f20555f808080808080888180615a92565b90615b6183615b92959c93875f52600660205260405f20895f526020525f6040812055876158e3565b84867fce405e67b4d6e56e438257e15f160ae28b450e6e7659bbc4c1f4e09a1ac846cb6020604051878152a4613b9b565b965f8080615afd565b9050615bdf9150835f52600660205260405f20855f5260205269021e19e0c9bab2400000615aef60405f205492875f52601b60205261060260405f2054918a613b9b565b825f52600660205260405f20845f5260205260405f2055615b01565b60ff91505460a01c16155f615aa8565b91505f615aa1565b50898410615a8d565b615c2591613b9b565b69021e19e0c9bab240000081029080820469021e19e0c9bab2400000149015171561356457615c5a9060175460801c90613d49565b8015615c9357825f52600460205260405f2060018060a01b0385165f52602052615c8960405f20918254613d67565b90555b5f866159fd565b5097508497615c8c565b508082116159f8565b5060175460801c15156159f2565b9094506020813d602011615ce0575b81615cd060209383613a57565b81010312610dac5751935f6159db565b3d9150615cc3565b905f602091828151910182855af1156134b6575f513d615d3757506001600160a01b0381163b155b615d175750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415615d1056fea164736f6c634300081c000a60a034606c57601f61033038819003918201601f19168301916001600160401b03831184841017607057808492602094604052833981010312606c57516001600160a01b0381168103606c576080526040516102ab9081610085823960805181818160ae015261017e0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f3560e01c806306fdde031461020a57806318160ddd146101ee578063313ce567146101d357806370a08231146101ad5780638da5cb5b1461016957806395d89b4114610103578063a0712d681461009a5763a9059cbb14610071575f80fd5b346100965760403660031901126100965761008a610288565b50602060405160018152f35b5f80fd5b3461009657602036600319011261009657337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316146100dd57005b5f5460043581018091116100ef575f55005b634e487b7160e01b5f52601160045260245ffd5b34610096575f36600319011261009657604051604081019080821067ffffffffffffffff8311176101555761015191604052600381526215d3d560ea1b60208201526040519182918261025e565b0390f35b634e487b7160e01b5f52604160045260245ffd5b34610096575f366003190112610096576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610096576020366003190112610096576101c6610288565b5060205f54604051908152f35b34610096575f36600319011261009657602060405160128152f35b34610096575f3660031901126100965760205f54604051908152f35b34610096575f36600319011261009657604051604081019080821067ffffffffffffffff8311176101555761015191604052600d81526c2bb934ba32a7b3332a37b5b2b760991b6020820152604051918291825b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b600435906001600160a01b03821682036100965756fea164736f6c634300081c000af3af3b9da6ac33569932110b27324f490cadff82974ce0ccd88561c89e289b4ab13fd610fe4e1b384966826794a9b2f6100ad031f352cc5ec6f22667f6074980000000000000000000000000d533a949740bb3306d119cc777fa900ba034cd52646cfe9445aed85f4853d501d1924d2bdabb1bbf12531df29f929f07ba4169e0b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6ed4c9b3ad13d2bf45175455fec325ef1713cf4b0f709952687da6d7332a081ba0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000014361c243174794e2207296a6ad59bb0dec1d388000000000000000000000000cb7e25fbbd8afe4ce73d7dac647dbc3d847f3c8200000000000000000000000077777777729c405efb6ac823493e6111f0070d67000000000000000000000000000000000000000000000000000000000001731800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001388000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f05b59d3b20000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000010101010e0c3171d894b71b3400668af311e7d9400000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000419905009e4656fdc02418c7df35b1e61ed5f726000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae310000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002b5265737570706c792050616972202843757276654c656e643a206372765553442f73444f4c4129202d2031000000000000000000000000000000000000000000
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f5f3560e01c80628cc2621461382357806302ce728f146137f557806306b6f7e91461377657806306fdde031461365957806308a0c375146135c157806308c7e366146135a45780630cab937d146130385780630df8dfac1461300f57806313966db514612ff1578063146a29eb14612fd15780631c6c959714612f505780632164e85b14612f325780632b3ba681146110be5780632cdacb5014612eed5780632f86556814612a9e57806333fd6f7414612a4b5780633f2617cb146129575780633f4ba83a146128fc57806342f43d0b146128b7578063476343ee1461264357806348741376146125795780634ac8eb5f1461255e5780634cae6513146124e45780634fd422df146124c057806354fd4d501461249757806356ecf28b1461247957806357d775f81461243e5780635e43c47b1461240957806360c52d05146123c05780636551f16c146123365780636b091695146122725780636f307dc31461222d578063730a7514146121a2578063757991a81461213b578063759cb53b1461210c57806375a410141461208c57806378e97925146120515780637adbf97314611f635780637b10399914611f1e5780637e92968414611bbb5780637ec4b57114611b7a5780638049d97114611afd5780638214ba4814611a835780638285ef4014611a5457806383d4433914611a1b5780638456cb591461199657806386993bef146119785780638cad7fbe146119395780638da5cb5b146109d85780638f50ea921461191b57806391ebebbd146118bc57806392bbcaed1461189e57806393ae0df91461187757806393f46f6414611831578063945c91421461180257806395d14ca8146117c05780639a295e731461177a5780639fe34bdc14611158578063a053db68146112af578063a36a363014611291578063ab7cfaf914611273578063acb70815146111f5578063b5af3062146111d1578063b5b5454714611198578063b68d0a091461115d578063b78294dd14611158578063b95c57461461113a578063c00007b0146110e1578063c0a7e892146110be578063c3192f14146110c3578063c49bb2d1146110be578063ca51bb59146110a0578063cacf3b5814611074578063cadac47914610f86578063cdd72d5214610f2c578063d41ddc9614610dee578063d619658b14610dd2578063d6bda0c014610bf4578063d870ce6614610b5a578063d8dfeb4514610b15578063e509b9d914610ad4578063e551d11d14610ab6578063e69bc27114610a71578063e7a3317414610a1d578063f2f4eb26146109d8578063f301af4214610982578063f384bd0514610964578063f5c7f89914610927578063f8112eed146108e6578063fbbbf94c146108ac578063fd6d0526146104325763fff5d9da1461040d575f80fd5b3461042f578060031936011261042f5760206104276140ee565b604051908152f35b80fd5b503461042f57608036600319011261042f5761044c6139b7565b90606435906024356001600160a01b0383168084036108a85761046d614145565b60405163e8673ef560e01b81526020816004817f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03165afa90811561089d57849161085f575b506001600160a01b03163303610850578015908115610846575b5061083757600e548110610828576104ea6144d9565b505050604435670de0b6b3a764000003670de0b6b3a764000081116108145761051c670de0b6b3a76400009183613d36565b0493670de0b6b3a764000061053d6105348785613b9b565b600d5490613d36565b04946105498684613b9b565b90610552613c9e565b6001600160801b03815116838181119182156107ff575b50506107f0576001600160801b03610586818516828451166140ce565b169081815264e8d4a5100082029180830464e8d4a5100014901517156107525760208101916001600160801b0383511611610766575b51905160801b6001600160801b0319166001600160801b039190911617601755670de0b6b3a764000090610608905b6105f789601954613d67565b601955610602614163565b90613d36565b0495610613876157e2565b7f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d3886001600160a01b03169561064a908890886158e3565b6001600160a01b037f0000000000000000000000007041d2c85887fcdc2a697e5ed272695f25d71dab16620186a088810290898204148915171561075257813b1561074e57869160248392604051948593849263140e25ad60e31b845260048401525af180156107435761072a575b5060408051948552602085018890528401526060830152600192916001600160a01b03909116907f76cd0cedf979345ca241ce6de696a520a8efc860c6c10d9db2a7953307c237fc90608090a255604080516001600160a01b039092168252602082019290925290819081015b0390f35b610735868092613a57565b61073f575f6106b9565b8480fd5b6040513d88823e3d90fd5b8680fd5b634e487b7160e01b87526011600452602487fd5b61076e615160565b600254600181018091116107dc576001600160801b036106089381670de0b6b3a76400009694846105eb956002557febad8099c467528a56c98b63c8d476d251cf1ffb4c75db94b4d23fa2b6a1e3358d80a28164e8d4a51000818551160416835294965050935050506105bc565b634e487b7160e01b88526011600452602488fd5b6316c6823f60e01b8652600486fd5b6108099250613b9b565b600f5411835f610569565b634e487b7160e01b83526011600452602483fd5b6362171e8f60e01b8252600482fd5b631e4ec46b60e01b8252600482fd5b905030145f6104d4565b63d520e5ad60e01b8352600483fd5b90506020813d602011610895575b8161087a60209383613a57565b810103126108915761088b90613d22565b5f6104ba565b8380fd5b3d915061086d565b6040513d86823e3d90fd5b8280fd5b503461042f578060031936011261042f57601554601654604080516001600160a01b038416815260a09390931c6020840152820152606090f35b503461042f57602036600319011261042f576109206109036139b7565b61091361090e61496f565b613f6e565b61091b614145565b615736565b6001815580f35b503461042f57604036600319011261042f576001906109586109476139b7565b61094f614145565b60243590615012565b55602060405160018152f35b503461042f578060031936011261042f576020600954604051908152f35b503461042f57602036600319011261042f576004359060015482101561042f5760606109ad83613b63565b506001815491015460ff6040519260018060a01b038116845260a01c16151560208301526040820152f35b503461042f578060031936011261042f576040517f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b03168152602090f35b503461042f57602036600319011261042f57610a63337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b610a6e600435614930565b80f35b503461042f57604036600319011261042f576040610a8d6139cd565b9160043581526004602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461042f578060031936011261042f576020600a54604051908152f35b503461042f57602036600319011261042f576020906001600160a01b03610af96139b7565b16815260088252604060018060a01b0391205416604051908152f35b503461042f578060031936011261042f576040517f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d3886001600160a01b03168152602090f35b503461042f57602036600319011261042f57600435610ba3337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b68056bc75e2d631000008110610be5576020817febc3aac103344d02f2e1b3fb50ca740929143c84082dd6e7698963e902aa636992600e55604051908152a180f35b630309cb8760e51b8252600482fd5b503461042f57606036600319011261042f57602435610c116139e3565b610c19614145565b610c22336142a5565b6001600160a01b03811615610dc357610c396144d9565b505050610c44614163565b5081610cd3575b610c609150610c5b60043561473e565b6152af565b906040610c6b613c70565b01610c77815133614335565b15610c8b5760208360018455604051908152f35b60649250610cad610c9a613c9e565b338452601b6020526040842054906143d7565b90338352601a6020526040832054905191633b49de0f60e21b8452600452602452604452fd5b602082610d05610d2c9430337f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e61524b565b604051636e553f6560e01b8152600481019190915230602482015292839081906044820190565b0381867f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d3886001600160a01b03165af1918215610db8578392610d7e575b50610d79610c6092339030614be4565b610c4b565b91506020823d602011610db0575b81610d9960209383613a57565b81010312610dac57905190610d79610d69565b5f80fd5b3d9150610d8c565b6040513d85823e3d90fd5b631e4ec46b60e01b8352600483fd5b503461042f578060031936011261042f576020604051600f8152f35b503461042f57604036600319011261042f57600435610e0b6139cd565b610e13614145565b610e1c336142a5565b6001600160a01b03168015610dc357610e336144d9565b505050338352601b6020526040832054610f1e575b610e53333084614659565b604051635d043b2960e11b815260048101929092526024820152306044820152602081606481857f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d3886001600160a01b03165af18015610f1357610ee4575b506040610ebc613c70565b01610ec8815133614335565b15610ed557506001815580f35b90606491610cad610c9a613c9e565b610f059060203d602011610f0c575b610efd8183613a57565b810190613ce8565b505f610eb1565b503d610ef3565b6040513d84823e3d90fd5b610f26614163565b50610e48565b503461042f578060031936011261042f57610f45613ba8565b506080610f5061402c565b915091506001600160801b0360208183511692015116610f6e613dcc565b91604051938452602084015260408301526060820152f35b503461042f57604036600319011261042f57600435610fa36139cd565b90610fac614145565b6001600160a01b03821615610dc357906020610ff992610fca6144d9565b505050610d058130337f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e61524b565b0381867f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d3886001600160a01b03165af18015610db8578390611040575b610920925030614be4565b506020823d60201161106c575b8161105a60209383613a57565b81010312610dac576109209151611035565b3d915061104d565b503461042f578060031936011261042f5761072661109061402c565b9060409492945194859485613a87565b503461042f578060031936011261042f576020601c54604051908152f35b613ae1565b503461042f578060031936011261042f576020601854604051908152f35b503461042f57602036600319011261042f576110fb6139b7565b611103614145565b6001600160a01b0381811683526008602052604083205416801561112a5761092091614eab565b508061113591614eab565b610920565b503461042f578060031936011261042f576020600154604051908152f35b613b2f565b503461042f57602036600319011261042f5760406111796139b7565b61118b61118582613fab565b91613f10565b9082519182526020820152f35b503461042f57602036600319011261042f576020906040906001600160a01b036111c06139b7565b168152600383522054604051908152f35b503461042f57602036600319011261042f5760206104276111f06139b7565b613fab565b503461042f57604036600319011261042f576004356112126139cd565b61121a614145565b6001600160a01b03811615610dc35760209261126a60019261123a6144d9565b505050611245613c9e565b61124f86826143d7565b9561126261125c8861473e565b9161473e565b90339261476f565b55604051908152f35b503461042f578060031936011261042f576020600d54604051908152f35b503461042f578060031936011261042f576020600c54604051908152f35b503461042f5760a036600319011261042f576112c96139b7565b60243591906064356044356084356001600160401b03811161073f573660238201121561073f5780600401356112fe81613b4c565b9161130c6040519384613a57565b818352602083016024819360051b8301019136831161177657602401905b82821061175e5750505061133c614145565b611345336142a5565b859061134f6144d9565b50505061135a614163565b506001600160a01b0386168088526012602052604088205490969060ff161561174f576001600160a01b0361138e85613c1b565b517f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b03169116819003611739575083515f198101908111611725576001600160a01b03906113e39086613c28565b517f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d3886001600160a01b03169291168290036116e9578561163e575b61142b90610c5b8b61473e565b93604051926370a0823160e01b8452306004850152602084602481865afa938415611633578a946115ff575b50883b156115fb57604051634b4ecc5560e11b8152336004820152602481018c9052608060448201529151608483018190528a90839060a4820190835b8181106115d6575050819293503060648301520381838c5af180156115cb579089916115b2575b5050906020602492604051938480926370a0823160e01b82523060048301525afa80156115a7578890611573575b6114f39250613b9b565b9380851061155c5750836115119161150c338330614be4565b613d67565b9560405194855260208501526040840152606083015260808201527fb19ca0df3f3a01af950d8e6ad62aeff167cf14c73e98af6c52afef1add5c97ed60a03392a26040610c6b613c70565b633b5d56ed60e11b87526004526024849052604486fd5b506020823d60201161159f575b8161158d60209383613a57565b81010312610dac576114f391516114e9565b3d9150611580565b6040513d8a823e3d90fd5b816115bc91613a57565b6115c757875f6114bb565b8780fd5b6040513d8b823e3d90fd5b85516001600160a01b031683526020958601958f955087945090920191600101611494565b8980fd5b9093506020813d60201161162b575b8161161b60209383613a57565b81010312610dac5751925f611457565b3d915061160e565b6040513d8c823e3d90fd5b925061166c8530337f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e61524b565b604051636e553f6560e01b8152600481018690523060248201526020816044818c865af180156115cb5789906116b5575b61142b91506116ad338230614be4565b93905061141e565b506020813d6020116116e1575b816116cf60209383613a57565b81010312610dac5761142b905161169d565b3d91506116c2565b8489838251925f198401938411610814576044936001600160a01b03916117109190613c28565b51169063b0b3262d60e01b8352600452602452fd5b634e487b7160e01b89526011600452602489fd5b60449089906001600160a01b0361171088613c1b565b631311dc6d60e01b8852600488fd5b6020809161176b846139f9565b81520191019061132a565b8880fd5b503461042f578060031936011261042f576080906040519050620186a08152620186a06020820152670de0b6b3a76400006040820152670de0b6b3a76400006060820152f35b503461042f578060031936011261042f576060601454604051906001600160401b03811682526001600160401b038160401c16602083015260801c6040820152f35b503461042f578060031936011261042f57602060405173d533a949740bb3306d119cc777fa900ba034cd528152f35b503461042f5760209061184336613b03565b9192509015611861576104279161185861402c565b925050506146f8565b6118729161186d613c9e565b6146f8565b610427565b503461042f578060031936011261042f57602061042761189561402c565b92505050615209565b503461042f578060031936011261042f576020600f54604051908152f35b503461042f57606036600319011261042f576118d66139cd565b60406118e06139e3565b9260043581526005602052209060018060a01b03165f5260205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b503461042f578060031936011261042f576020600e54604051908152f35b503461042f57602036600319011261042f5760209060ff906040906001600160a01b036119646139b7565b168152601284522054166040519015158152f35b503461042f578060031936011261042f576020601d54604051908152f35b503461042f578060031936011261042f576119db337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b600a54806119e7575080f35b601e5580600a557fbf1ce7fb3a8e648b70ea830f99b52f7ea31554186d29763280751f42e77f63866020604051838152a180f35b503461042f57602036600319011261042f576020906040906001600160a01b03611a436139b7565b168152600783522054604051908152f35b503461042f578060031936011261042f5760406017548151906001600160801b038116825260801c6020820152f35b503461042f57602036600319011261042f577fc98711414b05c67ac27ffb415026d97ad5094c5490747891a834cb44f64940d96020600435611aef337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b80600f55604051908152a180f35b503461042f57604036600319011261042f57611b176139cd565b611b1f614145565b611b28336142a5565b6001600160a01b0381161561083757611b6290611b436144d9565b505050338352601b6020526040832054611b6c575b3390600435614659565b6040610ebc613c70565b611b74614163565b50611b58565b503461042f57602090611b8c36613b03565b9192509015611baa5761042791611ba161402c565b92505050614475565b61187291611bb6613c9e565b614475565b503461042f57602036600319011261042f57600435611c04337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b81601d54828103611c3f575b507fa83ced135f4f6135be6fb52ae9183bf190452765f5b75b36caa332537c3ca7e5602083604051908152a180f35b604051631526fe2760e01b815260048101919091527f000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae316001600160a01b03169060c081602481855afa908115610db8578391611ee9575b506040516370a0823160e01b8152306004820152906001600160a01b0316602082602481845afa91821561089d578492611eb2575b5081611d95575b5090606460209260405194859384926321d0683360e11b84528860048501526024840152600160448401525af18015610db857611d37575b50601d819055817fa83ced135f4f6135be6fb52ae9183bf190452765f5b75b36caa332537c3ca7e5611c10565b6020813d602011611d8d575b81611d5060209383613a57565b810103126108a8577fa83ced135f4f6135be6fb52ae9183bf190452765f5b75b36caa332537c3ca7e591611d85602092613cf7565b509150611d0a565b3d9150611d43565b60209060446040959394955180948193636197390160e11b83528760048401528160248401525af18015611e7057611e7b575b506040516370a0823160e01b81523060048201526020816024817f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d3886001600160a01b03165afa8015611e705782918691611e3b575b5010611e2c579083915f611cd2565b6346bc68ed60e01b8452600484fd5b9150506020813d602011611e68575b81611e5760209383613a57565b81010312610dac578190515f611e1d565b3d9150611e4a565b6040513d87823e3d90fd5b6020813d602011611eaa575b81611e9460209383613a57565b8101031261073f57611ea590613cf7565b611dc8565b3d9150611e87565b935090506020833d602011611ee1575b81611ecf60209383613a57565b81010312610dac57849251905f611ccb565b3d9150611ec2565b611f0b915060c03d60c011611f17575b611f038183613a57565b810190613d74565b5050925050505f611c96565b503d611ef9565b503461042f578060031936011261042f576040517f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03168152602090f35b503461042f57602036600319011261042f576040611f7f6139b7565b611fb3337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b612021611fbe613c70565b805184516001600160a01b03918216815290841660208201529092907fb9d23ad01dc54c1fad84c770708fbe314a369b505c073949334dad786229805e90604090a16001600160a01b0316808352601580546001600160a01b0319169091179055565b6020810151601580546001600160a01b031660a09290921b6001600160a01b031916919091179055015160165580f35b503461042f578060031936011261042f5760206040517f0000000000000000000000000000000000000000000000000000000067d220008152f35b503461042f57602036600319011261042f576120a66139b7565b6120ae614145565b3380835260086020908152604080852080546001600160a01b0319166001600160a01b039590951694851790555192835290917ff4239ad0860f93469699dd4be8040b8838c5e25bb6cf24a1dfb381b937ff078c9190a26001815580f35b503461042f578060031936011261042f576020604051734e3fbd56cd56c3e72c1403e103b45db9da5b9d2b8152f35b503461042f578060031936011261042f57602061042761217b7f0000000000000000000000000000000000000000000000000000000067d2200042613b9b565b7f0000000000000000000000000000000000000000000000000000000000093a8090613d49565b503461042f57602036600319011261042f576004356121eb337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b620186a08111610be5577f1f93e1cc6afaef2ddc4d7ef7b0f45ac3278d548338c839e9be18695732a662746040600c548151908152836020820152a1600c5580f35b503461042f578060031936011261042f576040517f000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e6001600160a01b03168152602090f35b503461042f57604036600319011261042f5761228c6139b7565b6122946139cd565b61229c614145565b6001600160a01b0382163303612309576001600160a01b038116156122c45761092091614eab565b60405162461bcd60e51b815260206004820152601760248201527f66776420616464726573732063616e6e6f7420626520300000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b6044820152606490fd5b503461042f57602036600319011261042f5760043561237f337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b670de0b6b3a76400008111610be5576020817f3e0c428758b3ad6ab4fd85e8257e4eee404fb36e80c0251143635b549cd70b8892600d55604051908152a180f35b503461042f57604036600319011261042f576123da6139cd565b6123e2614145565b6001600160a01b0381161561083757610920906123fd6144d9565b50505060043533614be4565b503461042f57602036600319011261042f576109206124266139b7565b61243161090e61496f565b612439614145565b614a3b565b503461042f578060031936011261042f5760206040517f0000000000000000000000000000000000000000000000000000000000093a808152f35b503461042f578060031936011261042f576020600254604051908152f35b503461042f578060031936011261042f5760609060405190600382528060208301526040820152f35b503461042f57602036600319011261042f5760206104276124df6139b7565b613f10565b503461042f57602036600319011261042f577f18e1a8f58cc03bc99f69c27336072db255c3f01827f2923f654ddc209e2b8db56020600435612550337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b80601055604051908152a180f35b503461042f578060031936011261042f576020610427613dcc565b503461042f57604036600319011261042f576125936139b7565b61259b613a78565b6125cf337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b612633575b601154604080516001600160a01b038084168252841660208201529192917f74cd8ef76f78382ae0f3ee4e21117be974af0041a24dc98f7aa8ddabb0b2960c9190a16001600160a01b03166001600160a01b0319919091161760115580f35b61263b6144d9565b5050506125d4565b503461042f578060031936011261042f5761265c614145565b6126646144d9565b5050604051630abca72960e41b81527f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03169150602081600481855afa908115610db857839161287d575b506040516379bd9e4160e01b81526001600160a01b03821690602081600481855afa908115611e7057859161284b575b5061271461217b7f0000000000000000000000000000000000000000000000000000000067d2200042613b9b565b90601c54821190811591612840575b5061283157601c5583601854936019549382601855826019556127468587613d67565b823b15610891576040516340c10f1960e01b81526001600160a01b0392909216600483015260248201529082908290604490829084905af18015610f135761281c575b5050803b15610891576040516362f2221960e01b8152836004820152826024820152848160448183865af18015611e7057612807575b506040937f9cc800ba322ea82ab3e1e911dd4ccd84129687c4952f4ee2f937e3ac68755131606060019387519081528660208201528588820152a15582519182526020820152f35b612812858092613a57565b610891575f6127bf565b8161282691613a57565b61089157835f612789565b630e1b248d60e01b8552600485fd5b90508114155f612723565b90506020813d602011612875575b8161286660209383613a57565b81010312610dac57515f6126e6565b3d9150612859565b90506020813d6020116128af575b8161289860209383613a57565b810103126108a8576128a990613d22565b5f6126b6565b3d915061288b565b503461042f578060031936011261042f576040517f0000000000000000000000007041d2c85887fcdc2a697e5ed272695f25d71dab6001600160a01b03168152602090f35b503461042f578060031936011261042f57612941337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b600a541561294c5780f35b610a6e601e54614930565b503461042f57604036600319011261042f576129716139b7565b612979613a78565b90337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b0316148015612a19575b15612a0a577fea1eefb4fd58778d7b274fe54045a9feeec8f2847899c2e71126d3a74d486da59160409160018060a01b03169081855260126020528285209015159060ff1981541660ff831617905582519182526020820152a180f35b631d1e647b60e01b8352600483fd5b50337f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316146129ad565b503461042f57604036600319011261042f576040612a676139b7565b91612a706139cd565b9260018060a01b031681526006602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461042f57602036600319011261042f57612ab86139b7565b612ac0614145565b60405163d25adeb360e01b8152906020826004817f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03165afa918215610db8578392612eb1575b506001600160a01b0382169133839003612ea2576001600160a01b0382168015612e9357612b3a6144d9565b505050612b45614163565b612b4e846142a5565b612b588185614335565b612e8457612b64613c9e565b90828752601a602052604087205490838852601b602052612b88604089205461473e565b926001600160801b0384169260208201926001600160801b03845116155f14612ded57612bbe670de0b6b3a76400009186613d36565b04600c54620186a0019081620186a011612dd9576001600160801b0398969492620186a0612bf5612c739d9c9a9896948c94613d36565b0481811115612dcf57509a8b955b877f158ba9ab7bbbd08eeffa4753bad41f4d450e24831d293427308badf3eadd8c766060612c39612c348a896143d7565b61473e565b9d8e6040519b8c528a60208d015216998a6040820152a2612c598a614d44565b82612c678c828751166140ce565b168452828551166140ce565b168252848b52601b602052612c8c8360408d2054613b9b565b858c52601b6020528060408d20558015159081612dba575b50612dab5751905160801b6001600160801b0319166001600160801b03919091161760175588969594612d109490939092909188917fe4a1ae657f49cb1fb1c7d3a94ae6093565c4c8c0e03de488f79c377c3c3a24e0916040919082519182526020820152a386614659565b813b156108a857604051632be71c5f60e11b81526001600160a01b037f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d388166004820152602481018590526001600160801b0391909116604482015291908290606490829084905af18015610db857612d92575b50600160209255604051908152f35b612d9d838092613a57565b612da7575f612d83565b5080fd5b631abfe8a760e01b8b5260048bfd5b612dc59150826143d7565b601054115f612ca4565b90509a8b95612c03565b634e487b7160e01b8c52601160045260248cfd5b612e15612e046001600160801b0385511687613d36565b6001600160801b0386511690613d49565b8b80612e54575b612e34575b670de0b6b3a764000091612bbe91613d36565b6001810180911115612e2157634e487b7160e01b8c52601160045260248cfd5b5085612e7e612e6d6001600160801b0388511684613d36565b6001600160801b0387511690613d49565b10612e1c565b633af2cafd60e11b8652600486fd5b631e4ec46b60e01b8552600485fd5b6387e9041360e01b8452600484fd5b9091506020813d602011612ee5575b81612ecd60209383613a57565b810103126108a857612ede90613d22565b905f612b0e565b3d9150612ec0565b503461042f578060031936011261042f576040517f000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae316001600160a01b03168152602090f35b503461042f578060031936011261042f576020601954604051908152f35b503461042f57602036600319011261042f576004358015158103612da757612f76613d04565b50610726604051612f8681613a0d565b5f81525f6020820152612f97614145565b83612fa06144d9565b96915094612fb9575b6001905560405194859485613a87565b9150506018546001612fc9613c9e565b929050612fa9565b503461042f578060031936011261042f57602060405164e8d4a510008152f35b503461042f578060031936011261042f576020600b54604051908152f35b503461042f578060031936011261042f576011546040516001600160a01b039091168152602090f35b5034610dac576080366003190112610dac576130526139b7565b60443591906064356024356001600160401b038211610dac5736602383011215610dac578160040135926001600160401b038411610dac576024830192602436918660051b010111610dac576130a6614145565b6130af336142a5565b6130b76144d9565b5050506130c2614163565b506130cb613c9e565b9260018060a01b03821694855f52601260205260ff60405f20541615613595578015613581576130fa82613cd4565b6001600160a01b037f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d3888116911681900361357857505f198101928184116135645761314e613149858486613cc4565b613cd4565b6001600160a01b037f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec811695911685900361353257506001600160801b0386511615613523576131a090339086614659565b6040516370a0823160e01b815230600482015291602083602481875afa9283156134b6575f936134ef575b50863b15610dac57604051634b4ecc5560e11b815233600482015260248101869052608060448201526084810183905291829160a48301915f5b8181106134c15750505090805f923060648301520381838a5af180156134b6576134a1575b506040516370a0823160e01b8152306004820152602081602481865afa80156133ed578290889061346b575b6132609250613b9b565b968088106134545750856001600160801b03855116155f14613410575086935b338752601b60205260408720548086116133f8575b506132b6906132a38961473e565b339130916132b08961473e565b9161476f565b6040516370a0823160e01b8152306004820152602081602481865afa9081156133ed57908793929184916133b4575b50906132f091613b9b565b8061333f575b505050604051928352602083015283604083015260608201527f6b2b212085c14b2dc48ec56d19c05084e5d8ef233539cdd3dc0d70a4ec7f4f9e60803392a26040610c6b613c70565b60405163a9059cbb60e01b815233600482015260248101919091529160209183916044918391905af18015611e705761337b575b8085916132f6565b6020813d6020116133ac575b8161339460209383613a57565b8101031261073f576133a590613cf7565b505f613373565b3d9150613387565b919350506020813d6020116133e5575b816133d160209383613a57565b81010312610dac57518692906132f06132e5565b3d91506133c4565b6040513d89823e3d90fd5b945096506132b661340985896143d7565b9790613295565b9361343c61342b6001600160801b036020840151168a613d36565b6001600160801b0383511690613d49565b94156132805793600181018091116107525793613280565b633b5d56ed60e11b87526004526024879052604486fd5b50506020813d602011613499575b8161348660209383613a57565b81010312610dac57816132609151613256565b3d9150613479565b6134ae9196505f90613a57565b5f945f61322a565b6040513d5f823e3d90fd5b9193509160019060209081906001600160a01b036134de886139f9565b168152019401910191849392613205565b9092506020813d60201161351b575b8161350b60209383613a57565b81010312610dac5751915f6131cb565b3d91506134fe565b631abfe8a760e01b5f5260045ffd5b90506131499161354193613cc4565b63b0b3262d60e01b5f9081526004929092526001600160a01b0316602452604490fd5b634e487b7160e01b5f52601160045260245ffd5b61354183613cd4565b634e487b7160e01b5f52603260045260245ffd5b631311dc6d60e01b5f5260045ffd5b34610dac575f366003190112610dac576020601054604051908152f35b34610dac576020366003190112610dac57600435613609337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b620186a0811161364a577fe796e9ae748449310fcd1cc6718aab236c9b8d2e0e04dacb232ba564d5b338cc60406009548151908152836020820152a1600955005b630309cb8760e51b5f5260045ffd5b34610dac575f366003190112610dac576040515f90601354918260011c6001841693841561376c575b60208210851461375857818452602084019490811561373d57506001146136df575b50906136b4816040930382613a57565b8151928391602083525180918160208501528484015e5f828201840152601f01601f19168101030190f35b91905060135f527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090915f905b808210613723575090915081016020016136b46136a4565b91926001816020925483858801015201910190929161370b565b60ff1916855250151560051b820160200190506136b46136a4565b634e487b7160e01b5f52602260045260245ffd5b90607f1690613682565b34610dac576020366003190112610dac576004356137be337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614613c3c565b7f388cba11a61d4b31467a533c88eef75abfb6097ff2ab4652d086560fffcc2f836040600b548151908152836020820152a1600b55005b34610dac575f366003190112610dac5761380d614145565b6020613817614163565b60015f55604051908152f35b34610dac576020366003190112610dac5761383c6139b7565b613844614145565b61384d81614d44565b60015461385981613bc0565b916001600160a01b03165f5b8281106139405760015f5583515f19810190859082116135645761388882613bc0565b9160015b818111156138ee57836040518091602082016020835281518091526020604084019201905f5b8181106138c0575050500390f35b825180516001600160a01b0316855260209081015181860152869550604090940193909201916001016138b2565b60206138fa8285613c28565b510151905f1981018181116135645760019260206139188389613c28565b510152613938838060a01b0361392e8488613c28565b5151169187613c28565b51520161388c565b8061394c600192613b63565b5054828060a01b0381169081159081156139a9575b506139a157805f52600660205260405f20845f5260205260405f205460206139898489613c28565b5101526139968287613c28565b515201915b91613865565b50019161399b565b60ff915060a01c1687613961565b600435906001600160a01b0382168203610dac57565b602435906001600160a01b0382168203610dac57565b604435906001600160a01b0382168203610dac57565b35906001600160a01b0382168203610dac57565b604081019081106001600160401b03821117613a2857604052565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b03821117613a2857604052565b90601f801991011681019081106001600160401b03821117613a2857604052565b602435908115158203610dac57565b926001600160801b0391959460209183604060c09660e089019a89526001600160401b03815116868a01526001600160401b038682015116828a0152015116606087015260808601528281511660a0860152015116910152565b34610dac575f366003190112610dac576020604051670de0b6b3a76400008152f35b6060906003190112610dac57600435906024358015158103610dac57906044358015158103610dac5790565b34610dac575f366003190112610dac576020604051620186a08152f35b6001600160401b038111613a285760051b60200190565b6001548110156135815760015f81815291901b7fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60191565b9190820391821161356457565b60405190613bb582613a0d565b5f6020838281520152565b90613bca82613b4c565b613bd76040519182613a57565b8281528092613be8601f1991613b4c565b01905f5b828110613bf857505050565b602090604051613c0781613a0d565b5f81525f8382015282828501015201613bec565b8051156135815760200190565b80518210156135815760209160051b010190565b15613c4357565b60405162461bcd60e51b815260206004820152600560248201526421636f726560d81b6044820152606490fd5b60405190613c7d82613a3c565b6015546001600160a01b038116835260a01c60208301526016546040830152565b60405190613cab82613a0d565b6017546001600160801b038116835260801c6020830152565b91908110156135815760051b0190565b356001600160a01b0381168103610dac5790565b90816020910312610dac575190565b51908115158203610dac57565b60405190613d1182613a3c565b5f6040838281528260208201520152565b51906001600160a01b0382168203610dac57565b8181029291811591840414171561356457565b8115613d53570490565b634e487b7160e01b5f52601260045260245ffd5b9190820180921161356457565b91908260c0910312610dac57613d8982613d22565b91613d9660208201613d22565b91613da360408301613d22565b91613db060608201613d22565b91613dc960a0613dc260808501613d22565b9301613cf7565b90565b601d548015613eb957604051631526fe2760e01b8152600481019190915260c0816024817f000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae316001600160a01b03165afa9081156134b6576024916020915f91613e94575b506040516370a0823160e01b815230600482015292839182906001600160a01b03165afa9081156134b6575f91613e65575090565b90506020813d602011613e8c575b81613e8060209383613a57565b81010312610dac575190565b3d9150613e73565b613ead915060c03d60c011611f1757611f038183613a57565b5050925050505f613e30565b506040516370a0823160e01b81523060048201526020816024817f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d3886001600160a01b03165afa9081156134b6575f91613e65575090565b6001600160a01b03165f908152601b6020908152604080832054600254600390935292205491929190808210613f44575050565b909264e8d4a510006001915b04930192818414613f695764e8d4a51000600191613f50565b925050565b15613f7557565b60405162461bcd60e51b815260206004820152600e60248201526d10b932bbb0b93226b0b730b3b2b960911b6044820152606490fd5b613fb3614145565b613fbc816142a5565b6001600160a01b03165f908152601a6020526040902054613fdb613dcc565b9081811115613fed57505b9060015f55565b9050613fe6565b6040519061400182613a3c565b8160406014546001600160401b03811683526001600160401b0381831c16602084015260801c910152565b5f614035613d04565b5060405161404281613a0d565b5f81525f602082015250614054613ff4565b906001600160401b034216825261406a82615507565b8051909190156140bf57506060810151926001600160401b0360208301511660208401526001600160801b03604083015116604084015260806140b96018546001600160801b03871690613d67565b92015190565b92905060185490613dc9613c9e565b906001600160801b03809116911603906001600160801b03821161356457565b600a548015614138576001600160801b0361410761402c565b92505050511690670de0b6b3a7640000820291808304670de0b6b3a7640000149015171561356457613dc991613d49565b50670de0b6b3a764000090565b60025f54146141545760025f55565b633ee5aeb560e01b5f5260045ffd5b61416b613c70565b9060208201916001600160601b038351164214155f1461429d5780516040516315caaba160e21b81526001600160a01b037f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d3888116600483015290929160209184916024918391165afa9182156134b6575f92614269575b508115613d53576001600160601b03429081169094526ec097ce7bc90715b34b9f1000000000919091046040808301829052915160a09490941b6001600160a01b0319166001600160a01b03909416939093176015556016839055518281527f4fc1b45960547ee95894b08a284c3c066cf5aca706a7420639c42c3ec2e118a490602090a1565b9091506020813d602011614295575b8161428560209383613a57565b81010312610dac5751905f6141e2565b3d9150614278565b604001519150565b6142ae81614d44565b6001600160a01b037f0000000000000000000000007041d2c85887fcdc2a697e5ed272695f25d71dab81165f90815260066020908152604080832094909316808352938152828220805490839055601a9091529190205490620186a0900480821061432c5761431c91613b9b565b905b5f52601a60205260405f2055565b50505f9061431e565b906009549182156143cf5761436b61434b613c9e565b6001600160a01b039092165f818152601b602052604090205490926143d7565b9081156143c6575f52601a60205260405f20549182156143be5761438e91613d36565b90620186a0820291808304620186a0149015171561356457670de0b6b3a76400006143b99204613d49565b111590565b505050505f90565b50505050600190565b505050600190565b6020810180516001600160801b03166143f0575050905b565b90929161441b61440a6001600160801b0386511683613d36565b6001600160801b0384511690613d49565b936001600160801b0381511615159283614448575b50505061443957565b90600181018091116135645790565b61446c9293506144636001600160801b038092511687613d36565b91511690613d49565b105f8080614430565b909160208201916001600160801b03835116155f146144945750505090565b6144b0612e6d6001600160801b03839796949597511684613d36565b94836144c5575b836144485750505061443957565b81516001600160801b0316151593506144b7565b5f905f906144e5613d04565b506144ee613ff4565b906144f882615507565b8051151580614505575050565b9194509250926143ee6001600160801b03602060806060870151968287016001600160401b0381511690604089019186835116907f6b9ef8676ff86d806b7a7bd7a9b0266910c9fce560c26289d7ed7cd7743127c786888701926001600160401b038451169460408901958c875116916040519384528c84015260408301526060820152a17f939dcec711228d083924a0db6cecbb66bb8403ea7cdbfe2f928901cbac2cdfc160408d6001600160401b0384511682519182528a820152a151916001600160401b0383169052519186831690526001600160401b034216808a526fffffffffffffffff00000000000000006014549260401b169187191617176014558460145491811990821990861b161691161760145561462888601854613d67565b601855015182815116831960175416176017550151166001600160801b036017549181199060801b16911617601755565b9160207fba68c7a8c8efbddb7e938ee32ecc28a68930c18105a3a0ab9563eac7f051cf5a9160018060a01b031693845f52601a825260405f2061469d828254613b9b565b90556146a8816157e2565b6001600160a01b03841693813086036146c7575b5050604051908152a3565b6146f1917f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d3886158e3565b5f816146bc565b90916001600160801b03825116155f1461471157505090565b9092916020840161472f612e6d6001600160801b0383511684613d36565b94836144485750505061443957565b6001600160801b038111614758576001600160801b031690565b6306dfcc6560e41b5f52608060045260245260445ffd5b9391909161477c81614d44565b6001600160801b0361479184828851166140ce565b16855260208501906001600160801b036147ae84828551166140ce565b16825260018060a01b031694855f52601b6020526147db6001600160801b0360405f205494168094613b9b565b865f52601b6020528060405f2055801515908161491b575b506135235751905160801b6001600160801b0319166001600160801b0391909116176017556001600160a01b0383169283614865575b507fe4a1ae657f49cb1fb1c7d3a94ae6093565c4c8c0e03de488f79c377c3c3a24e0916040916001600160801b038351921682526020820152a3565b917f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b0316803b15610dac57604051632770a7eb60e21b81526001600160a01b039490941660048501526001600160801b03821660248501525f908490604490829084905af19182156134b6577fe4a1ae657f49cb1fb1c7d3a94ae6093565c4c8c0e03de488f79c377c3c3a24e09360409361490b575b50915091614829565b5f61491591613a57565b5f614902565b6149269150826143d7565b601054115f6147f3565b6001600160801b03811161364a576020817fbf1ce7fb3a8e648b70ea830f99b52f7ea31554186d29763280751f42e77f638692600a55604051908152a1565b337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b03161480156149a45790565b5060405163670fb82160e01b81526020816004817f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03165afa9081156134b6575f91614a01575b506001600160a01b0316331490565b90506020813d602011614a33575b81614a1c60209383613a57565b81010312610dac57614a2d90613d22565b5f6149f2565b3d9150614a0f565b6001600160a01b038116903082148015614bdc575b8015614bcc575b614b9257815f52600760205260405f2054155f14614b5e57600154600f811015614b2b5768010000000000000000811015613a2857806001614a9c9201600155613b63565b50826001600160601b0360a01b825416179055600154825f52600760205260405f2055817fb13fd610fe4e1b384966826794a9b2f6100ad031f352cc5ec6f22667f60749805f80a2803b15614b2157506143ee906040519063a9059cbb60e01b60208301523060248301525f604483015260448252614b1c606483613a57565b615ce8565b6143ee9150615736565b60405162461bcd60e51b815260206004820152600b60248201526a6d6178207265776172647360a81b6044820152606490fd5b50805f52600760205260405f20545f19810190811161356457614b8090613b63565b5080546001600160a01b031615614b96575b5050565b80546001600160a01b031916821790557fb13fd610fe4e1b384966826794a9b2f6100ad031f352cc5ec6f22667f60749805f80a2565b50614bd68161591f565b15614a57565b508115614a50565b9160018060a01b031691825f52601a60205260405f20614c05838254613d67565b905581306001600160a01b03831603614d11575b5050601d5480614c52575b5060207f2ac90482c3b6bea30a2c085cf093016bad7f970d91c5fd233e6b848d3e231dc391604051908152a2565b6040516321d0683360e11b8152600481019190915260248101829052600160448201526020816064815f7f000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae316001600160a01b03165af180156134b65715614c24576020813d602011614d09575b81614ccc60209383613a57565b81010312610dac577f2ac90482c3b6bea30a2c085cf093016bad7f970d91c5fd233e6b848d3e231dc391614d01602092613cf7565b509150614c24565b3d9150614cbf565b614d3d9130907f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d38861524b565b5f81614c19565b5f907f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316803b15610dac575f8091602460405180948193633bd73ee360e21b83523060048401525af180156134b657614e96575b506002549060015490835b5f198110614dba575050505050565b836001600160a01b03831680614e4d575b865b858110614e26575085821015614e1d57808752601b60205264e8d4a51000604088205404818852601b60205260408820556001820180921161075257865260036020526040862055600101614dab565b50505050505050565b80614e3e89876001948b889c999d9e9a979b98615990565b01919490939796929591614dcd565b8087526003602052604087205491508582141580614e82575b15614dcb57905084908087526003602052856040882055614dcb565b50808752601b602052604087205415614e66565b614ea39192505f90613a57565b5f905f614da0565b7f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b031691823b15610dac57604051633bd73ee360e21b81525f816024818380983060048401525af180156134b657614ffd575b506002549160015492845b5f198110614f2057505050505050565b816001600160a01b03841680614fb4575b875b878110614f8d575083821015614f8357808852601b60205264e8d4a51000604089205404818952601b6020526040892055600182018092116107dc57875260036020526040872055600101614f10565b5050505050505050565b80614fa5888860019489889d999e9f9b979c98615990565b01929691959094989792614f33565b8088526003602052604088205491508382141580614fe9575b15614f3157905082908088526003602052836040892055614f31565b50808852601b602052604088205415614fcd565b61500a9193505f90613a57565b5f915f614f05565b5f91907f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316803b15610dac575f8091602460405180948193633bd73ee360e21b83523060048401525af180156134b65761514b575b506002549060015491845b84811061508957505050505050565b816001600160a01b03841680615102575b875b8681106150ec575083821015614f8357808852601b60205264e8d4a51000604089205404818952601b6020526040892055600182018092116107dc5787526003602052604087205560010161507a565b806150fc8a886001948988615990565b0161509c565b8088526003602052604088205491508382141580615137575b1561509a5790508290808852600360205283604089205561509a565b50808852601b60205260408820541561511b565b6151589193505f90613a57565b5f915f61506f565b5f7f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316803b15610dac575f8091602460405180948193633bd73ee360e21b83523060048401525af180156134b6576151f6575b5060025460015491905f1982106151d157505050565b815b8381106151e05750505050565b806151f084806001948680615990565b016151d3565b61520291505f90613a57565b5f5f6151bb565b600a5490516001600160801b0316808211156152435761522891613b9b565b6001600160801b03811115613dc957506001600160801b0390565b50505f615228565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526143ee91614b1c608483613a57565b906001600160801b03809116911601906001600160801b03821161356457565b9190916152ba613c9e565b92601054916001600160801b03811692831061352357600b54620186a00180620186a011613564576152f0620186a09185613d36565b046152fa86615209565b8181106154f2575085516001600160801b031661549357805b61537b82976001600160801b036020820191816153546153368883875116613d67565b8261534c6153438a61473e565b8287511661528f565b16845261473e565b1692839052516001600160801b0391161660809190911b6001600160801b03191617601755565b335f52601b60205260405f20615392838254613d67565b9055848082111561548a576153a691613b9b565b915b82615476575b7f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b0316803b15610dac576040516340c10f1960e01b81526001600160a01b03861660048201526001600160801b039290921660248301525f908290604490829084905af180156134b657615466575b506040519384526020840152604083015260018060a01b0316907f10a0132d3bf8c82a7fb93a86160f3074ca5c3e5706fa2bcdf0e2b5fd495af09b60603392a3565b5f61547091613a57565b5f615424565b61548283601954613d67565b6019556153ae565b50505f916153a8565b6020860190806154da6154c46154b36001600160801b0386511684613d36565b6001600160801b038b511690613d49565b936001600160801b03614463818c511687613d36565b10156153135790600181018091116135645790615313565b63586ce21f60e11b5f5260045260245260445ffd5b9060405160a081018181106001600160401b03821117613a28576040525f8152602081015f815260408201935f855260608301915f8352608084019360405161554f81613a0d565b5f81525f6020820152855280966001600160401b038451164211615576575b505050505050565b600161560d9252615585613c9e565b8652604061559d6001600160401b0386511642613b9b565b6011549582015182516356fb8f2d60e01b81526001600160a01b037f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d38881166004830152602482018490526001600160801b0390921660448201529196919485929190911690829081906064820190565b03915afa80156134b6575f925f916156cf575b50670de0b6b3a7640000946001600160401b03615667959481946001600160801b0361565e951690521684526001600160801b038851511690613d36565b91511690613d36565b0480825280151590816156ae575b50156156a5576156966001600160801b03809251169251928284511661528f565b1690525b5f808080808061556e565b5f91505261569a565b6001600160801b0391506156c790828551511690613d67565b11155f615675565b939250506040833d60401161572e575b816156ec60409383613a57565b81010312610dac578251926001600160401b0384168403610dac5760200151906001600160801b0382168203610dac5791929190670de0b6b3a7640000615620565b3d91506156df565b6001600160a01b03165f8181526007602052604090205480615756575050565b5f1981019081116135645761576a90613b63565b5080546001600160a01b03168290036157b15780546001600160a01b03191690557f646cfe9445aed85f4853d501d1924d2bdabb1bbf12531df29f929f07ba4169e05f80a2565b60405162461bcd60e51b8152602060048201526009602482015268042dad2e6dac2e8c6d60bb1b6044820152606490fd5b601d54806157ee575050565b604051631526fe2760e01b8152600481019190915260c0816024817f000000000000000000000000f403c135812408bfbe8713b5a23a04b3d48aae316001600160a01b03165afa80156134b6575f9260209284926158b9575b50604051636197390160e11b815260048101919091526024810184905292839160449183916001600160a01b03165af180156134b6576158845750565b6020813d6020116158b1575b8161589d60209383613a57565b81010312610dac576158ae90613cf7565b50565b3d9150615890565b60449192506158d69060c03d60c011611f1757611f038183613a57565b5050925050509190615847565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526143ee91614b1c606483613a57565b6001600160a01b03908116907f00000000000000000000000014361c243174794e2207296a6ad59bb0dec1d38816811461598b577f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b03161461598757600190565b5f90565b505f90565b93929161599c90613b63565b5080546001600160a01b03169290831561556e576040516370a0823160e01b815230600482015293602085602481845afa9485156134b6575f95615cb4575b508496600183019687549582149686888099615ca6575b80615c9d575b615c1c575b50505f8281526004602090815260408083206001600160a01b03878116855292529091205495169384615a4e575b50505050505081615a43575b50615a40575050565b55565b90508214155f615a37565b825f52600560205260405f2060018060a01b0385165f5260205260405f20855f5260205260405f20548860018060a01b03841692831580158091615c13575b615a9b575b50505050615a2b565b82615c0b575b5081615bfb575b5015615b9b57615af690855f52600660205260405f20875f5260205269021e19e0c9bab2400000615aef60405f205492895f52601b60205261060260405f2054918c613b9b565b0490613d67565b9182615b38575b5050505b5f52600560205260405f209060018060a01b03165f5260205260405f20905f5260205260405f20555f808080808080888180615a92565b90615b6183615b92959c93875f52600660205260405f20895f526020525f6040812055876158e3565b84867fce405e67b4d6e56e438257e15f160ae28b450e6e7659bbc4c1f4e09a1ac846cb6020604051878152a4613b9b565b965f8080615afd565b9050615bdf9150835f52600660205260405f20855f5260205269021e19e0c9bab2400000615aef60405f205492875f52601b60205261060260405f2054918a613b9b565b825f52600660205260405f20845f5260205260405f2055615b01565b60ff91505460a01c16155f615aa8565b91505f615aa1565b50898410615a8d565b615c2591613b9b565b69021e19e0c9bab240000081029080820469021e19e0c9bab2400000149015171561356457615c5a9060175460801c90613d49565b8015615c9357825f52600460205260405f2060018060a01b0385165f52602052615c8960405f20918254613d67565b90555b5f866159fd565b5097508497615c8c565b508082116159f8565b5060175460801c15156159f2565b9094506020813d602011615ce0575b81615cd060209383613a57565b81010312610dac5751935f6159db565b3d9150615cc3565b905f602091828151910182855af1156134b6575f513d615d3757506001600160a01b0381163b155b615d175750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415615d1056fea164736f6c634300081c000a
Loading...
Loading
Loading...
Loading
Net Worth in USD
$88.41
Net Worth in ETH
0.044336
Token Allocations
RSUP
80.16%
CRV
19.52%
CVX
0.32%
Multichain Portfolio | 34 Chains
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.