Contract Address Details

0x7De7d7165B33D4292840dAf7CB2629459e796CB5

Contract Name
Vault
Creator
0x598ca9–731e18 at 0xaec8d6–33cd14
Balance
0 TT ( )
Tokens
Fetching tokens...
Transactions
Transfers
Gas Used
Last Balance Update
3954829
Contract name:
Vault




Optimization enabled
true
Compiler version
v0.8.19+commit.7dd6d404




Optimization runs
200
Verified at
2024-03-24 03:29:17.282003Z

Constructor Arguments

000000000000000000000000598ca9ca53d5bb84fd56fe41c3f40ed15d731e18000000000000000000000000f97b79ece2f95e7b63a05f1fd73a59a1ef3e4fd70000000000000000000000000000000000000000000000000000048c2739500000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000002b7eeaf01d706b466b8f1af9c4f9d56ffbf00f100000000000000000000000005922f957c2df08fe3dd5f6dd8f2f7fe14ff9c43f00000000000000000000000000000000000000000000000000000000000000066379555344430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000743595f5553444300000000000000000000000000000000000000000000000000

Arg [0] (address) : 0x598ca9ca53d5bb84fd56fe41c3f40ed15d731e18
Arg [1] (address) : 0xf97b79ece2f95e7b63a05f1fd73a59a1ef3e4fd7
Arg [2] (uint256) : 5000000000000
Arg [3] (string) : cyUSDC
Arg [4] (string) : CY_USDC
Arg [5] (address) : 0x2b7eeaf01d706b466b8f1af9c4f9d56ffbf00f10
Arg [6] (address) : 0x5922f957c2df08fe3dd5f6dd8f2f7fe14ff9c43f

              

src/Vault.sol

//SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
//=============================================================================
//IMPORTS
//=============================================================================
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../lib/solmate/src/tokens/ERC4626.sol";
import "./interface/ICYDyson.sol";
import "./interface/IPair.sol";
import "./interface/IFarm.sol";
import "./interface/IERC20.sol";
import "./lib/TransferHelper.sol";
import "./lib/SqrtMath.sol";
/**
@title Vault
@notice This contract is used to store the yield strategies of the protocol
*/
contract Vault is ERC4626, ReentrancyGuard {
using TransferHelper for address;
using SqrtMath for *;
//=============================================================================
//STATE VARIABLES
//=============================================================================
struct VaultUser {
uint points;
uint debt;
uint credit;
uint collateral;
uint lastRedistributed;
}
mapping(address => VaultUser) public vaultUsers;
address[] public users;
/// @dev 50/40/10 ratio for debt paydown, credit reward, and protocol reward
uint public constant DEBT_PAYDOWN_RATIO = 5000;
uint public constant CREDIT_REWARD_RATIO = 4000;
uint public constant PROTOCOL_REWARD_RATIO = 1000;
/// @dev Basis points for percentages
uint public constant BPS = 10_000;
/// @dev Maximum tokens that can be heled by the vault at a time
uint public immutable MAX_VAULT_CAPACITY;
/// @dev Addresses to track synthetic asset and treasury
address public immutable TREASURY_ADDRESS;
ICYDyson public immutable CYDYSON_ADDRESS;
uint public vaultPoints;
uint public vaultTVL;
/// @dev Maximum loan-to-value ratio
uint public constant MAX_LTV = 50;
//======================== DYSON/USDC POOL INTEGRATION ========================
/// @dev Address to store the DYSON/USDC pool on Sepolia
address public constant DYSON_USDC_POOL =
0xd0f3c7d3d02909014303d13223302eFB80A29Ff3;
/// @dev Address to store the farm on Sepolia
address public constant FARM = 0x09E1c1C6b273d7fB7F56066D191B7A15205aFcBc;
/// @dev Address to store the DYSON token on Sepolia
address public constant DYSON = 0xeDC2B3Bebbb4351a391363578c4248D672Ba7F9B;
/// @dev Address to store the USDC token on Sepolia
address public constant USDC = 0xFA0bd2B4d6D629AdF683e4DCA310c562bCD98E4E;
struct Position {
uint index;
uint spAmount;
bool hasDepositedAsset;
}
struct Note {
uint token0Amt;
uint token1Amt;
uint due;
}
mapping(address => mapping(address => uint)) public positionsCount;
mapping(address => mapping(address => mapping(uint => Position)))
public positions;
/// @notice Notes created by user, indexed by note number
mapping(address => mapping(uint => Note)) public notes;
uint public lastUpdateTime;
uint public spSnapshot; //sp in farm at last update
uint public spPending; //sp to be added to pool
uint public updatePeriod = 1 minutes;
uint public spPool;
address public owner;
uint public dysonPool;
uint public adminFeeRatio;
uint private constant MAX_ADMIN_FEE_RATIO = 1e18;
uint constant MAX_FEE_RATIO = 2 ** 64;
uint private constant MAX_FEE_RATIO_SQRT = 2 ** 32;
//=============================================================================
//EVENTS
//=============================================================================
/**
@dev Emitted when a user deposits assets into the vault
@param pair The address of the pair
@param user The address of the user
@param noteId The ID of the note
@param positionId The ID of the position
@param spAmount The amount of SP added to the pool
*/
event Deposited(
address indexed pair,
address indexed user,
uint noteId,
uint positionId,
uint spAmount
);
event Deposit();
/**
@dev Emitted when a user borrows synthetic assets
@param user The address of the user
@param amount The amount of synthetic assets borrowed
*/
event Borrowed(address indexed user, uint256 amount);
/**
@dev Emitted when the yield is redistributed
@param user The address of the user
@param paydown The amount of the user's share of the paydown
@param creditReward The amount of the user's share of the credit reward
@param protocolFee The amount of the user's share of the protocol fee
*/
event YieldRedistributed(
address indexed user,
uint paydown,
uint creditReward,
uint protocolFee,
uint spBefore
);
/**
@dev Emitted when the vault receives DYSON tokens
@param ownerAmount The amount of DYSON tokens sent to the owner
@param poolAmount The amount of DYSON tokens added to the pool
*/
event DYSONReceived(uint ownerAmount, uint poolAmount);
//=============================================================================
//ERRORS
//=============================================================================
/**
@dev Error when the deposit amount is less than zero
@param _assets The amount of assets deposited
*/
error DepositLessThanZero(uint _assets);
/**
@dev Error when the deposit limit is reached
*/
error DepositLimitReached();
/**
@dev Error when the withdraw receiver address is zero
*/
error ZeroAddress();
/**
@dev Error when the withdraw caller has not made a deposit
@param _caller The address of the caller
*/
error NotADepositor(address _caller);
/**
@dev Error when the borrow amount is less than zero
@param _amount The amount of synthetic assets borrowed
*/
error BorrowLessThanZero(uint _amount);
/**
@dev Error when the borrow amount is more than the user's credit
@param _amount The amount of synthetic assets borrowed
@param _credit The amount of synthetic assets the user can borrow
*/
error BorrowExceedsCredit(uint _amount, uint _credit);
/**
* @dev Error when the user's yield has been redistributed in the last 24 hours
*/
error YieldRedistributedRecently();
/**
@dev Error when the user has insufficient synthetic assets to repay
@param _amount The amount of synthetic assets repaid
@param _balance The amount of synthetic assets the user has
*/
error InsufficientSyntheticAssets(uint _amount, uint _balance);
/**
@dev Error when the user has not deposited assets to Dyson Finance
@param _user The address of the user
*/
error NoDepositedAssets(address _user);
//=============================================================================
//CONSTRUCTOR
//=============================================================================
/**
@dev Initializes the vault with the underlying asset, name, symbol, and synthetic assets.
@param _asset Either DYSN or USDC address based on the DYSN/USDC pool in Dyson Finance
@param _maxVaultCapacity The maximum capacity of the vault, to control price impact and prevent an imbalance in the pool
@param _name The name of the points that represent the user's share of the vault
@param _symbol The symbol of the points that represent the user's share of the vault
@param _cyDysonAddress The address of the synthetic asset that can be borrowed against the vault
*/
constructor(
address _owner,
address _asset, // DYSN or USDC
uint256 _maxVaultCapacity,
string memory _name, //cyDYSN or cyUSDC
string memory _symbol, //cyDYSN or cyUSDC
ICYDyson _cyDysonAddress, //cyDyson
address _treasury
) ERC4626(ERC20(_asset), _name, _symbol) {
CYDYSON_ADDRESS = _cyDysonAddress;
MAX_VAULT_CAPACITY = _maxVaultCapacity;
TREASURY_ADDRESS = _treasury;
owner = _owner;
}
//=============================================================================
//EXTERNAL FUNCTIONS
//=============================================================================
/// @dev integration from dyson finance
function depositToVault(
address tokenIn, //DYSN
address tokenOut, //USDC
uint256 input //USDC is 8 decimals
) external nonReentrant returns (uint output) {
uint spBefore = _update();
//check if this contract has the allowance to spend the input
if (IERC20(tokenIn).allowance(address(this), DYSON_USDC_POOL) < input) {
IERC20(tokenIn).approve(DYSON_USDC_POOL, input);
}
if (IERC20(tokenIn).allowance(msg.sender, address(this)) < input) {
IERC20(tokenIn).approve(address(this), input);
}
tokenIn.safeTransferFrom(msg.sender, address(this), input);
//find out if tokenIn is DYSN or USDC
bool tokenIsDyson = tokenIn == DYSON ? true : false;
uint minOutput;
//get the min output from the Router contract
if (tokenIsDyson) {
(uint reserve0, uint reserve1) = IPair(DYSON_USDC_POOL)
.getReserves();
(uint64 _feeRatio0, ) = IPair(DYSON_USDC_POOL).getFeeRatio();
uint fee = (uint(_feeRatio0) * input) / MAX_FEE_RATIO;
uint inputLessFee = input - fee;
minOutput = (inputLessFee * reserve1) / (reserve0 + inputLessFee);
} else {
(uint reserve0, uint reserve1) = IPair(DYSON_USDC_POOL)
.getReserves();
(, uint64 _feeRatio1) = IPair(DYSON_USDC_POOL).getFeeRatio();
uint fee = (uint(_feeRatio1) * input) / MAX_FEE_RATIO;
uint inputLessFee = input - fee;
minOutput = (inputLessFee * reserve0) / (reserve1 + inputLessFee);
}
return
_deposit(tokenIn, tokenOut, 1, input, minOutput, 1 days, spBefore);
}
/**
* @dev Borrow function that allows a user to borrow a synthetic asset against the underlying vault asset deposit.
* @param _amount The amount of synthetic assets to borrow.
* @notice Check that the borrow amount is at a maximum of 50% of the collateral
* @notice Check that the user has enough collateral to borrow
* @notice Mint the synthetic asset to the user
* @notice Borrow amount must be greater than zero
* @notice Increase the user's debt, decrease the user's credit
* @notice WIP: MAKE SURE THAT THE BORROW AMOUNT IS LESS THAN OR EQUAL TO THE LTV
*/
function borrow(uint256 _amount) external nonReentrant {
if (!(_amount > 0)) {
revert BorrowLessThanZero(_amount);
}
if (!(_amount <= vaultUsers[msg.sender].credit)) {
revert BorrowExceedsCredit(_amount, vaultUsers[msg.sender].credit);
}
vaultUsers[msg.sender].debt += _amount;
vaultUsers[msg.sender].credit -= _amount;
IERC20(address(this)).approve(address(this), MAX_VAULT_CAPACITY);
ICYDyson(CYDYSON_ADDRESS).mint(msg.sender, _amount * 1e12);
emit Borrowed(msg.sender, _amount * 1e12);
}
/**
@dev Mechanism to redistribute yield
@dev this will be called automatically in the background when the user connects to the vault
@notice the yield will be redistributed based on some conditions:
- Yield will be collected by the vault and redistributed back to users based on their points (representing their share of the vault)
- 50% of the yield will be used to pay down user's debt (if no debt, then it will be used to increase user's credit)
- 40% of the yield will be used to increase user's credit
- 10% of the yield will be used to be sent to the treasury
@dev formula to calculate the yield by the user
- first, calculate the user's share of the vault (user's points / total points)
- then, calculate the user's share of the yield (user's share of the vault * total yield)
- then, calculate the user's share of the paydown (user's share of the yield * 50%)
- then, calculate the user's share of the credit reward (user's share of the yield * 40%)
- then, calculate the user's share of the protocol fee (user's share of the yield * 10%)
@dev if yield in dy
*/
function redistributeYield(
uint index // 1
) public returns (uint token0Amt, uint token1Amt) {
uint spBefore = _update();
Position storage position = positions[DYSON_USDC_POOL][msg.sender][
index
];
if (!(position.hasDepositedAsset)) {
revert NoDepositedAssets(msg.sender);
}
if (vaultUsers[msg.sender].points == 0) {
revert NotADepositor(msg.sender);
}
if (
block.timestamp - vaultUsers[msg.sender].lastRedistributed < 86400
) {
revert YieldRedistributedRecently();
}
position.hasDepositedAsset = false;
//will need to hardcode the below temporarily cause of time constraints
// (token0Amt, token1Amt) = IPair(DYSON_USDC_POOL).withdraw(
// position.index,
// address(this)
// );
Note storage note = notes[msg.sender][index];
token0Amt = note.token0Amt;
token1Amt = note.token1Amt;
uint due = note.due;
note.token0Amt = 0;
note.token1Amt = 0;
note.due = 0;
(uint reserve0, uint reserve1) = IPair(DYSON_USDC_POOL).getReserves();
(uint64 _feeRatio0, uint64 _feeRatio1) = IPair(DYSON_USDC_POOL)
.getFeeRatio();
if (
(((MAX_FEE_RATIO * (MAX_FEE_RATIO - uint(_feeRatio0))) /
(MAX_FEE_RATIO - uint(_feeRatio1))).sqrt() * token0Amt) /
reserve0 <
(MAX_FEE_RATIO_SQRT * token1Amt) / reserve1
) {
token1Amt = 0;
// IPair(DYSON_USDC_POOL).swap0in(
// address(this),
// token0Amt,
// (token0Amt * 90) / 100
// );
// uint64 feeRatioAdded = uint64(
// (token0Amt * MAX_FEE_RATIO) / reserve0
// );
// _updateFeeRatio0(_feeRatio0, feeRatioAdded);
// emit Withdraw(address(this), true, index, token0Amt);
//provide a fixed return for now
USDC.safeTransferFrom(msg.sender, address(this), 100_000_000);
} else {
token0Amt = 0;
USDC.safeTransferFrom(msg.sender, address(this), 100_000_000);
// uint64 feeRatioAdded = uint64(
// (token1Amt * MAX_FEE_RATIO) / reserve1
// );
// _updateFeeRatio1(_feeRatio1, feeRatioAdded);
// emit Withdraw(address(this), false, index, token1Amt);
}
//token0 is Dyson, token1 is USDC
// (address token0, address token1) = DYSON < USDC
// ? (DYSON, USDC)
// : (USDC, DYSON);
uint totalYield = token0Amt == 0 ? token1Amt : token0Amt;
//get the user's share of the vault
uint256 userShareOfVault = (vaultUsers[msg.sender].points * BPS) /
vaultPoints;
//get the user's share of the yield
uint256 userShareOfYield = (userShareOfVault * totalYield) / BPS;
//get the user's share of the paydown
uint256 userShareOfPaydown = (userShareOfYield * DEBT_PAYDOWN_RATIO) /
BPS;
//get the user's share of the credit reward
uint256 userShareOfCreditReward = (userShareOfYield *
CREDIT_REWARD_RATIO) / BPS;
//get the user's share of the protocol fee
uint256 userShareOfProtocolFee = (userShareOfYield *
PROTOCOL_REWARD_RATIO) / BPS;
//decrease the user's debt.
//if the user's debt is less than the user's share of the paydown, then the user's debt will be set to zero.
//if there is no debt, then the user's credit will be increased by the user's share of the paydown
if (vaultUsers[msg.sender].debt < userShareOfPaydown) {
vaultUsers[msg.sender].debt = 0;
vaultUsers[msg.sender].credit += userShareOfPaydown;
USDC.safeTransferFrom(
address(this),
address(0),
userShareOfPaydown
);
} else {
vaultUsers[msg.sender].debt -= userShareOfPaydown;
USDC.safeTransferFrom(
address(this),
address(0),
userShareOfPaydown
);
}
//increase the user's credit by the user's share of the credit reward
vaultUsers[msg.sender].credit += userShareOfCreditReward;
USDC.safeTransferFrom(
address(this),
msg.sender,
userShareOfCreditReward
);
//send the user's share of the protocol fee to the treasury
USDC.safeTransferFrom(
address(this),
TREASURY_ADDRESS,
userShareOfProtocolFee
);
//update the user's last redistributed time
vaultUsers[msg.sender].lastRedistributed = block.timestamp;
//emit the YieldRedistributed event
emit YieldRedistributed(
msg.sender,
userShareOfPaydown,
userShareOfCreditReward,
userShareOfProtocolFee,
spBefore
);
}
//=============================================================================
//INTERNAL FUNCTIONS
//=============================================================================
function _update() internal returns (uint spInFarm) {
if (lastUpdateTime + updatePeriod < block.timestamp) {
try IFarm(FARM).swap(address(this)) {} catch {}
lastUpdateTime = block.timestamp;
}
spInFarm = IFarm(FARM).balanceOf(address(this));
if (spInFarm < spSnapshot) {
spPool += spPending;
spPending = 0;
uint newBalance = IERC20(DYSON).balanceOf(address(this));
if (newBalance > dysonPool) {
uint dysonAdded = newBalance - dysonPool;
uint adminFee = (dysonAdded * adminFeeRatio) /
MAX_ADMIN_FEE_RATIO;
uint poolIncome = dysonAdded - adminFee;
dysonPool += poolIncome;
IERC20(DYSON).transfer(owner, adminFee);
emit DYSONReceived(adminFee, poolIncome);
}
}
}
/**
* @dev Deposits the underlying asset into the Dyson Finance vault. Either DYSN, or USDC.
* @param input The amount of the asset to deposit
*/
function _deposit(
address tokenIn,
address tokenOut,
uint index,
uint input,
uint minOutput,
uint time,
uint spBefore
) internal returns (uint output) {
//checks
if (!(input > 0)) {
revert DepositLessThanZero(input);
}
if (!(vaultTVL + input <= MAX_VAULT_CAPACITY)) {
revert DepositLimitReached();
}
//effects
vaultUsers[msg.sender].collateral += input;
vaultUsers[msg.sender].credit =
(vaultUsers[msg.sender].collateral * MAX_LTV) /
100;
vaultTVL += input;
//interactions
(address token0, ) = sortTokens(tokenIn, tokenOut);
uint noteCount = IPair(DYSON_USDC_POOL).noteCount(address(this));
if (tokenIn == token0)
output = IPair(DYSON_USDC_POOL).deposit0(
address(this),
input,
minOutput,
time
);
else
output = IPair(DYSON_USDC_POOL).deposit1(
address(this),
input,
minOutput,
time
);
uint spAfter = IFarm(FARM).balanceOf(address(this)); //get a hardcoded address for the farm
uint spAdded = spAfter - spBefore;
spPending += spAdded;
spSnapshot = spAfter;
uint positionId = positionsCount[DYSON_USDC_POOL][msg.sender];
Position storage position = positions[DYSON_USDC_POOL][msg.sender][
positionId
];
position.index = noteCount;
position.spAmount = spAdded;
position.hasDepositedAsset = true;
positionsCount[DYSON_USDC_POOL][msg.sender]++;
uint256 receivedPoints = deposit(input, msg.sender);
vaultUsers[msg.sender].points += receivedPoints;
vaultPoints += receivedPoints;
users.push(msg.sender);
emit Deposited(
DYSON_USDC_POOL,
msg.sender,
noteCount,
positionId,
spAdded
);
}
//=============================================================================
//VIEW FUNCTIONS
//=============================================================================
function getMaxVaultCapacity()
public
view
returns (uint256 _maxVaultCapacity)
{
return MAX_VAULT_CAPACITY;
}
/**
@dev Returns the current user data
@return VaultUser The current user data
*/
function getUserData() public view returns (VaultUser memory) {
return vaultUsers[msg.sender];
}
/**
@dev Returns the user details
@param _userAddress The address of the user
@return VaultUser The user details
*/
function getUserDetails(
address _userAddress
) public view returns (VaultUser memory) {
return vaultUsers[_userAddress];
}
/**
@dev Returns the cyDyson address
@return _cyDysonAddress The cyDyson address
*/
function getCyDysonAddress() public view returns (address _cyDysonAddress) {
return address(CYDYSON_ADDRESS);
}
/**
@dev Returns the treasury address
@return _treasuryAddress The treasury address
*/
function getTreasuryAddress()
public
view
returns (address _treasuryAddress)
{
return TREASURY_ADDRESS;
}
function getTotalBorrowed() public view returns (uint256) {
uint256 totalBorrowed;
for (uint256 i = 0; i < users.length; i++) {
totalBorrowed += vaultUsers[users[i]].debt;
}
return totalBorrowed;
}
function getTotalCredit() public view returns (uint256) {
uint256 totalCredit;
for (uint256 i = 0; i < users.length; i++) {
totalCredit += vaultUsers[users[i]].credit;
}
return totalCredit;
}
/**
@dev Returns the total value locked in the vault
@return _vaultTVL The total value locked in the vault
*/
function getVaultTVL() public view returns (uint256 _vaultTVL) {
return vaultTVL;
}
//=============================================================================
//PURE FUNCTIONS
//=============================================================================
/// @dev returns sorted token addresses, used to handle return values from pairs sorted in this order
function sortTokens(
address tokenA,
address tokenB
) internal pure returns (address token0, address token1) {
require(tokenA != tokenB, "identical addresses");
(token0, token1) = tokenA < tokenB
? (tokenA, tokenB)
: (tokenB, tokenA);
require(token0 != address(0), "zero address");
}
function totalAssets() public view override returns (uint256) {
return vaultTVL;
}
/**
@dev Returns the current LTV
@return _LTV The current LTV
*/
function getLTV() public pure returns (uint256 _LTV) {
return MAX_LTV;
}
/**
@dev Returns the current protocol reward ratio
@return _protocolRewardRatio The current protocol reward ratio
*/
function getProtocolRewardRatio() public pure returns (uint256) {
return PROTOCOL_REWARD_RATIO;
}
/**
@dev Returns the current debt paydown ratio
@return _debtPaydownRatio The current debt paydown ratio
*/
function getDebtPaydownRatio() public pure returns (uint256) {
return DEBT_PAYDOWN_RATIO;
}
/**
@dev Returns the current credit reward ratio
@return _creditRewardRatio The current credit reward ratio
*/
function getCreditRewardRatio() public pure returns (uint256) {
return CREDIT_REWARD_RATIO;
}
}

lib/OpenZeppelin-contracts/contracts/utils/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity 0.8.19;
/**
* @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 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;
}
}

lib/solmate/src/tokens/ERC20.sol

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}

lib/solmate/src/tokens/ERC4626.sol

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol";
/// @notice Minimal ERC4626 tokenized Vault implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC4626.sol)
abstract contract ERC4626 is ERC20 {
using SafeTransferLib for ERC20;
using FixedPointMathLib for uint256;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
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
);
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
ERC20 public immutable asset;
constructor(
ERC20 _asset,
string memory _name,
string memory _symbol
) ERC20(_name, _symbol, _asset.decimals()) {
asset = _asset;
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LOGIC
//////////////////////////////////////////////////////////////*/
function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
// Check for rounding error since we round down in previewDeposit.
require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");
// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {
assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.
// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
function withdraw(
uint256 assets,
address receiver,
address owner
) public virtual returns (uint256 shares) {
shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.
if (msg.sender != owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
function redeem(
uint256 shares,
address receiver,
address owner
) public virtual returns (uint256 assets) {
if (msg.sender != owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
// Check for rounding error since we round down in previewRedeem.
require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
/*//////////////////////////////////////////////////////////////
ACCOUNTING LOGIC
//////////////////////////////////////////////////////////////*/
function totalAssets() public view virtual returns (uint256);
function convertToShares(uint256 assets) public view virtual returns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
}
function convertToAssets(uint256 shares) public view virtual returns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);
}
function previewDeposit(uint256 assets) public view virtual returns (uint256) {
return convertToShares(assets);
}
function previewMint(uint256 shares) public view virtual returns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
}
function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
}
function previewRedeem(uint256 shares) public view virtual returns (uint256) {
return convertToAssets(shares);
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LIMIT LOGIC
//////////////////////////////////////////////////////////////*/
function maxDeposit(address) public view virtual returns (uint256) {
return type(uint256).max;
}
function maxMint(address) public view virtual returns (uint256) {
return type(uint256).max;
}
function maxWithdraw(address owner) public view virtual returns (uint256) {
return convertToAssets(balanceOf[owner]);
}
function maxRedeem(address owner) public view virtual returns (uint256) {
return balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
INTERNAL HOOKS LOGIC
//////////////////////////////////////////////////////////////*/
function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {}
function afterDeposit(uint256 assets, uint256 shares) internal virtual {}
}

lib/solmate/src/utils/FixedPointMathLib.sol

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,
// 1 is added to round up the division of x * y by the denominator.
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Add 1 to x * y if x % y > 0. Note this will
// return 0 instead of reverting if y is zero.
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}

lib/solmate/src/utils/SafeTransferLib.sol

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}

src/interface/ICYDyson.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
interface ICYDyson {
// Error declarations can be included if they will be used externally.
error VaultNotWhitelisted();
// Function signatures
function mint(address _to, uint256 _amount) external;
function burn(address _from, uint256 _amount) external;
function addVault(address _vault) external;
function removeVault(address _vault) external;
// Admin role identifier function for external access
function ADMIN_ROLE() external view returns (bytes32);
}

src/interface/IERC20.sol

pragma solidity >=0.8.0;
// SPDX-License-Identifier: MIT
interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(
address owner,
address spender
) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(
address from,
address to,
uint value
) external returns (bool);
}

src/interface/IFarm.sol

pragma solidity >=0.8.0;
// SPDX-License-Identifier: MIT
interface IFarm {
event TransferOwnership(address newOwner);
event RateUpdated(address indexed poolId, uint rewardRate, uint weight);
event GrantSP(
address indexed user,
address indexed poolId,
uint amountIn,
uint amountOut
);
event Swap(
address indexed user,
address indexed parent,
uint amountIn,
uint amountOut
);
struct Pool {
uint weight;
uint rewardRate;
uint lastUpdateTime;
uint lastReserve;
address gauge;
}
function agency() external view returns (address);
function gov() external view returns (address);
function owner() external view returns (address);
function globalPool() external view returns (address);
function pools(
address poolId
)
external
view
returns (
uint weight,
uint rewardRate,
uint lastUpdateTime,
uint lastReserve,
address gauge
);
function balanceOf(address user) external view returns (uint);
function cooldown(address user) external view returns (uint);
function transferOwnership(address _owner) external;
function rescueERC20(
address tokenAddress,
address to,
uint256 amount
) external;
function setPool(address poolId, address gauge) external;
function setPoolRewardRate(
address poolId,
uint rewardRate,
uint weight
) external;
function setGlobalRewardRate(uint rewardRate, uint weight) external;
function getCurrentPoolReserve(
address poolId
) external view returns (uint reserve);
function getCurrentGlobalReserve() external view returns (uint reserve);
function grantSP(address to, uint amount) external;
function swap(address user) external returns (uint amountOut);
}

src/interface/IPair.sol

pragma solidity >=0.8.0;
// SPDX-License-Identifier: MIT
interface IPair {
event Swap(
address indexed sender,
bool indexed isSwap0,
uint amountIn,
uint amountOut,
address indexed to
);
event FeeCollected(uint token0Amt, uint token1Amt);
event Deposit(
address indexed user,
bool indexed isToken0,
uint index,
uint amountIn,
uint token0Amt,
uint token1Amt,
uint due
);
event Withdraw(
address indexed user,
bool indexed isToken0,
uint index,
uint amountOut
);
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
struct Note {
uint token0Amt;
uint token1Amt;
uint due;
}
function token0() external view returns (address);
function token1() external view returns (address);
function getFeeRatio()
external
view
returns (uint64 _feeRatio0, uint64 _feeRatio1);
function getReserves() external view returns (uint reserve0, uint reserve1);
function deposit0(
address to,
uint input,
uint minOutput,
uint time
) external returns (uint output);
function deposit1(
address to,
uint input,
uint minOutput,
uint time
) external returns (uint output);
function swap0in(
address to,
uint input,
uint minOutput
) external returns (uint output);
function swap1in(
address to,
uint input,
uint minOutput
) external returns (uint output);
function withdraw(
uint index,
address to
) external returns (uint token0Amt, uint token1Amt);
function halfLife() external view returns (uint64);
function calcNewFeeRatio(
uint64 _oldFeeRatio,
uint _elapsedTime
) external view returns (uint64 _newFeeRatio);
function feeTo() external view returns (address);
function initialize(address _token0, address _token1) external;
function collectFee() external;
function DOMAIN_SEPARATOR() external view returns (bytes32);
function WITHDRAW_TYPEHASH() external pure returns (bytes32);
function factory() external view returns (address);
function farm() external view returns (address);
function basis() external view returns (uint);
function noteCount(address user) external view returns (uint);
function notes(
address user,
uint index
) external view returns (Note memory);
function getPremium(uint time) external view returns (uint premium);
function setBasis(uint _basis) external;
function setHalfLife(uint64 _halfLife) external;
function setFarm(address _farm) external;
function setFeeTo(address _feeTo) external;
function rescueERC20(
address tokenAddress,
address to,
uint256 amount
) external;
function setApprovalForAllWithSig(
address owner,
address operator,
bool approved,
uint deadline,
bytes calldata sig
) external;
function setApprovalForAll(address operator, bool approved) external;
function operatorApprovals(
address owner,
address operator
) external view returns (bool);
function withdrawFrom(
address from,
uint index,
address to
) external returns (uint token0Amts, uint token1Amts);
}

src/lib/SqrtMath.sol

pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-2.0
//https://github.com/Gaussian-Process/solidity-sqrt/blob/main/src/FixedPointMathLib.sol
library SqrtMath {
function sqrt(uint256 x) internal pure returns (uint256 z) {
assembly {
// This segment is to get a reasonable initial estimate for the Babylonian method.
// If the initial estimate is bad, the number of correct bits increases ~linearly
// each iteration instead of ~quadratically.
// The idea is to get z*z*y within a small factor of x.
// More iterations here gets y in a tighter range. Currently, we will have
// y in [256, 256*2^16). We ensure y>= 256 so that the relative difference
// between y and y+1 is small. If x < 256 this is not possible, but those cases
// are easy enough to verify exhaustively.
z := 181 // The 'correct' value is 1, but this saves a multiply later
let y := x
// Note that we check y>= 2^(k + 8) but shift right by k bits each branch,
// this is to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8),
// and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of x, or about 20bps.
// The estimate sqrt(x) = (181/1024) * (x+1) is off by a factor of ~2.83 both when x=1
// and when x = 256 or 1/256. In the worst case, this needs seven Babylonian iterations.
z := shr(18, mul(z, add(y, 65536))) // A multiply is saved from the initial z := 181
// Run the Babylonian method seven times. This should be enough given initial estimate.
// Possibly with a quadratic/cubic polynomial above we could get 4-6.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// See https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division.
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This check ensures we return floor.
// The solmate implementation assigns zRoundDown := div(x, z) first, but
// since this case is rare, we choose to save gas on the assignment and
// repeat division in the rare case.
// If you don't care whether floor or ceil is returned, you can skip this.
if lt(div(x, z), z) {
z := div(x, z)
}
}
}
}

src/lib/TransferHelper.sol

pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-2.0
library TransferHelper {
function safeApprove(address token, address to, uint value) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(0x095ea7b3, to, value)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"transferHelper: approve failed"
);
}
function safeTransfer(address token, address to, uint value) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(0xa9059cbb, to, value)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"transferHelper: transfer failed"
);
}
function safeTransferFrom(
address token,
address from,
address to,
uint value
) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(0x23b872dd, from, to, value)
);
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"transferHelper: transferFrom failed"
);
}
function safeTransferETH(address to, uint value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, "transferHelper: ETH transfer failed");
}
}

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_owner","internalType":"address"},{"type":"address","name":"_asset","internalType":"address"},{"type":"uint256","name":"_maxVaultCapacity","internalType":"uint256"},{"type":"string","name":"_name","internalType":"string"},{"type":"string","name":"_symbol","internalType":"string"},{"type":"address","name":"_cyDysonAddress","internalType":"contract ICYDyson"},{"type":"address","name":"_treasury","internalType":"address"}]},{"type":"error","name":"BorrowExceedsCredit","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"},{"type":"uint256","name":"_credit","internalType":"uint256"}]},{"type":"error","name":"BorrowLessThanZero","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"error","name":"DepositLessThanZero","inputs":[{"type":"uint256","name":"_assets","internalType":"uint256"}]},{"type":"error","name":"DepositLimitReached","inputs":[]},{"type":"error","name":"InsufficientSyntheticAssets","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"},{"type":"uint256","name":"_balance","internalType":"uint256"}]},{"type":"error","name":"NoDepositedAssets","inputs":[{"type":"address","name":"_user","internalType":"address"}]},{"type":"error","name":"NotADepositor","inputs":[{"type":"address","name":"_caller","internalType":"address"}]},{"type":"error","name":"ReentrancyGuardReentrantCall","inputs":[]},{"type":"error","name":"YieldRedistributedRecently","inputs":[]},{"type":"error","name":"ZeroAddress","inputs":[]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Borrowed","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DYSONReceived","inputs":[{"type":"uint256","name":"ownerAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"poolAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Deposit","inputs":[],"anonymous":false},{"type":"event","name":"Deposit","inputs":[{"type":"address","name":"caller","internalType":"address","indexed":true},{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"uint256","name":"assets","internalType":"uint256","indexed":false},{"type":"uint256","name":"shares","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Deposited","inputs":[{"type":"address","name":"pair","internalType":"address","indexed":true},{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"noteId","internalType":"uint256","indexed":false},{"type":"uint256","name":"positionId","internalType":"uint256","indexed":false},{"type":"uint256","name":"spAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Withdraw","inputs":[{"type":"address","name":"caller","internalType":"address","indexed":true},{"type":"address","name":"receiver","internalType":"address","indexed":true},{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"uint256","name":"assets","internalType":"uint256","indexed":false},{"type":"uint256","name":"shares","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"YieldRedistributed","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"paydown","internalType":"uint256","indexed":false},{"type":"uint256","name":"creditReward","internalType":"uint256","indexed":false},{"type":"uint256","name":"protocolFee","internalType":"uint256","indexed":false},{"type":"uint256","name":"spBefore","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BPS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"CREDIT_REWARD_RATIO","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ICYDyson"}],"name":"CYDYSON_ADDRESS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"DEBT_PAYDOWN_RATIO","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"DOMAIN_SEPARATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"DYSON","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"DYSON_USDC_POOL","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"FARM","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_LTV","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_VAULT_CAPACITY","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"PROTOCOL_REWARD_RATIO","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"TREASURY_ADDRESS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"USDC","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"adminFeeRatio","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ERC20"}],"name":"asset","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"borrow","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"convertToAssets","inputs":[{"type":"uint256","name":"shares","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"convertToShares","inputs":[{"type":"uint256","name":"assets","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"shares","internalType":"uint256"}],"name":"deposit","inputs":[{"type":"uint256","name":"assets","internalType":"uint256"},{"type":"address","name":"receiver","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"output","internalType":"uint256"}],"name":"depositToVault","inputs":[{"type":"address","name":"tokenIn","internalType":"address"},{"type":"address","name":"tokenOut","internalType":"address"},{"type":"uint256","name":"input","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"dysonPool","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCreditRewardRatio","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"_cyDysonAddress","internalType":"address"}],"name":"getCyDysonAddress","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getDebtPaydownRatio","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"_LTV","internalType":"uint256"}],"name":"getLTV","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_maxVaultCapacity","internalType":"uint256"}],"name":"getMaxVaultCapacity","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getProtocolRewardRatio","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getTotalBorrowed","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getTotalCredit","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"_treasuryAddress","internalType":"address"}],"name":"getTreasuryAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct Vault.VaultUser","components":[{"type":"uint256","name":"points","internalType":"uint256"},{"type":"uint256","name":"debt","internalType":"uint256"},{"type":"uint256","name":"credit","internalType":"uint256"},{"type":"uint256","name":"collateral","internalType":"uint256"},{"type":"uint256","name":"lastRedistributed","internalType":"uint256"}]}],"name":"getUserData","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"","internalType":"struct Vault.VaultUser","components":[{"type":"uint256","name":"points","internalType":"uint256"},{"type":"uint256","name":"debt","internalType":"uint256"},{"type":"uint256","name":"credit","internalType":"uint256"},{"type":"uint256","name":"collateral","internalType":"uint256"},{"type":"uint256","name":"lastRedistributed","internalType":"uint256"}]}],"name":"getUserDetails","inputs":[{"type":"address","name":"_userAddress","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"_vaultTVL","internalType":"uint256"}],"name":"getVaultTVL","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastUpdateTime","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxDeposit","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxMint","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxRedeem","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxWithdraw","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"assets","internalType":"uint256"}],"name":"mint","inputs":[{"type":"uint256","name":"shares","internalType":"uint256"},{"type":"address","name":"receiver","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"nonces","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"token0Amt","internalType":"uint256"},{"type":"uint256","name":"token1Amt","internalType":"uint256"},{"type":"uint256","name":"due","internalType":"uint256"}],"name":"notes","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"permit","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"index","internalType":"uint256"},{"type":"uint256","name":"spAmount","internalType":"uint256"},{"type":"bool","name":"hasDepositedAsset","internalType":"bool"}],"name":"positions","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"positionsCount","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"previewDeposit","inputs":[{"type":"uint256","name":"assets","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"previewMint","inputs":[{"type":"uint256","name":"shares","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"previewRedeem","inputs":[{"type":"uint256","name":"shares","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"previewWithdraw","inputs":[{"type":"uint256","name":"assets","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"assets","internalType":"uint256"}],"name":"redeem","inputs":[{"type":"uint256","name":"shares","internalType":"uint256"},{"type":"address","name":"receiver","internalType":"address"},{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"token0Amt","internalType":"uint256"},{"type":"uint256","name":"token1Amt","internalType":"uint256"}],"name":"redistributeYield","inputs":[{"type":"uint256","name":"index","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"spPending","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"spPool","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"spSnapshot","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalAssets","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"updatePeriod","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"users","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"vaultPoints","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"vaultTVL","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"points","internalType":"uint256"},{"type":"uint256","name":"debt","internalType":"uint256"},{"type":"uint256","name":"credit","internalType":"uint256"},{"type":"uint256","name":"collateral","internalType":"uint256"},{"type":"uint256","name":"lastRedistributed","internalType":"uint256"}],"name":"vaultUsers","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"shares","internalType":"uint256"}],"name":"withdraw","inputs":[{"type":"uint256","name":"assets","internalType":"uint256"},{"type":"address","name":"receiver","internalType":"address"},{"type":"address","name":"owner","internalType":"address"}]}]
            

Deployed ByteCode

0x6040608081526004908136101561001557600080fd5b600091823560e01c9081624c2b90146124c857816301e1d114146112185781630307c4a11461246057816306fdde03146123b857816307604b1a1461047657816307a2d13a146111f8578163095ea7b3146123495781630a28a477146123295781631355c6ab146122fa57816317536c061461195657816318160ddd146119375781631bb35fdc1461193257816322e8c87d146118fb57816323b872dd1461184d578163249d39e91461183057816327354de71461131c578163313ce567146112de5781633644e515146112c1578163365b98b2146112805781633760205014610af857816337d976181461126157816338d52e0f1461121d578163402d267d146108ca57816346c34b35146112185781634cdad506146111f85781634e637ba9146111d95781634ed2b8ac146110d65781634fd37f1b146107a1578163576a5bf1146111ba5781635e5a67a8146108865781636e553f651461119257816370a08231146104cc57816374ede36a146111735781637ecebe001461113b5781637fedafad146110f257816381bf8d3d146110d6578163849efb711461107357816389a30271146110445781638da5cb5b1461101b5781638f92b02814610ffc57816392097c3414610e0757816394bf804d14610f5d57816395c0009414610eff57816395d89b4114610e2457816399e3833214610e07578163a83627de14610de8578163a9059cbb14610d76578163b3a9992e14610d47578163b3d7f6b914610d27578163b460af9414610c43578163ba08765214610b15578163baedafad14610af8578163c5ebeaec146108cf578163c63d75b6146108ca578163c6e066e214610886578163c6e6f59214610394578163c8f33c9114610867578163cc3d967b146107be578163cd368003146107a1578163ce4cfeb114610782578163ce96cb7714610748578163d505accf14610504578163d905777e146104cc578163dd62ed3e1461047b578163e002460414610476578163e0b117ff1461040c578163e4021087146103dd578163ee8dfccf146103be578163ef8b30f714610394575063f30576081461032457600080fd5b346103905781600319360112610390578190826008545b80821061034c576020848451908152f35b909261038461038a9161035e866126a3565b905460039190911b1c6001600160a01b03168752600760205284872060020154906129cd565b93612f5b565b9061033b565b5080fd5b8284346103bb5760203660031901126103bb57506103b460209235612a67565b9051908152f35b80fd5b5050346103905781600319360112610390576020906009549051908152f35b5050346103905781600319360112610390576020905173edc2b3bebbb4351a391363578c4248d672ba7f9b8152f35b5050346103905780600319360112610390579081906001600160a01b03610431612642565b168152600d60205281812060243582526020522090610472825491600260018501549401549051938493846040919493926060820195825260208201520152565b0390f35b6125fd565b9050346104c857816003193601126104c85760209282610499612642565b916104a2612658565b6001600160a01b0393841682529386522091166000908152908352819020549051908152f35b8280fd5b5050346103905760203660031901126103905760209181906001600160a01b036104f4612642565b1681526003845220549051908152f35b8383346103905760e03660031901126103905761051f612642565b90610528612658565b91604435606435926084359260ff8416809403610744574285106107015761054e61276e565b9560018060a01b038092169586895260209560058752848a209889549960018b01905585519285898501957f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c987528b89870152169a8b606086015288608086015260a085015260c084015260c0835260e0830167ffffffffffffffff94848210868311176106ec578188528451902061010085019261190160f01b845261010286015261012285015260428152610160840194818610908611176106d757848752519020835261018082015260a4356101a082015260c4356101c0909101528780528490889060809060015afa156106cd5786511696871515806106c4575b156106925786977f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92595969752835280872086600052835281816000205551908152a380f35b83606492519162461bcd60e51b8352820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152fd5b5084881461064d565b81513d88823e3d90fd5b60418d634e487b7160e01b6000525260246000fd5b60418e634e487b7160e01b6000525260246000fd5b815162461bcd60e51b81526020818a0152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606490fd5b8680fd5b505034610390576020366003190112610390576020916103b49082906001600160a01b03610774612642565b168152600385522054612a84565b5050346103905781600319360112610390576020906015549051908152f35b505034610390578160031936011261039057602090516103e88152f35b919050346104c85760203660031901126104c85780610472936107df612642565b6107e7612f6a565b506001600160a01b031681526007602052208151929061080684612560565b80548452600181015460208501526002810154838501526003810154606085015201546080830152519182918291909160808060a0830194805184526020810151602085015260408101516040850152606081015160608501520151910152565b505034610390578160031936011261039057602090600e549051908152f35b505034610390578160031936011261039057517f0000000000000000000000002b7eeaf01d706b466b8f1af9c4f9d56ffbf00f106001600160a01b03168152602090f35b6126f0565b838334610390576020806003193601126104c8578335916108ee612c9f565b8215610ae2573384526007825260028185200154808411610ac65750338452600782526001818520016109228482546129cd565b90553384526007825260028185200161093c84825461274b565b9055805163095ea7b360e01b8152308682019081527f0000000000000000000000000000000000000000000000000000048c273950006020820152839082908190604001038188305af18015610a7857610a99575b5064e8d4a510008302926001600160a01b037f0000000000000000000000002b7eeaf01d706b466b8f1af9c4f9d56ffbf00f101690840464e8d4a50fff1901610a8657803b15610a825781516340c10f1960e01b815233878201908152602081018690529091869183919082908490829060400103925af18015610a7857610a47575b50907fac59582e5396aca512fa873a2047e7f4c80f8f55d4a06cb34a78a0187f62719f91519283523392a2600160065580f35b67ffffffffffffffff8195929511610a655784529293508385610a14565b634e487b7160e01b825260418652602482fd5b82513d87823e3d90fd5b8480fd5b634e487b7160e01b855260118652602485fd5b610ab890833d8511610abf575b610ab08183612592565b810190612c08565b5085610991565b503d610aa6565b8590846044935192636ee8822d60e11b84528301526024820152fd5b5163731258e960e11b8152808501839052602490fd5b50503461039057816003193601126103905760209051610fa08152f35b905082346103bb57610b2636612716565b90926001600160a01b038281169392909133859003610bf6575b50610b4a83612a84565b958615610bc5575092826020979592610b678895610bbf97612a20565b865191858352898301528316907ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db873392a47f000000000000000000000000f97b79ece2f95e7b63a05f1fd73a59a1ef3e4fd7612b89565b51908152f35b606490602089519162461bcd60e51b8352820152600b60248201526a5a45524f5f41535345545360a81b6044820152fd5b848152866020528781203382526020528780822054856000198203610c1e575b505050610b40565b610c279161274b565b9186815288602052818120338252602052205587878185610c16565b83833461039057610bbf602093610c5936612716565b95909192610c88610c6985612ab4565b809860018060a01b03938985831696873303610ce1575b505050612a20565b85519084825287898301528316907ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db873392a47f000000000000000000000000f97b79ece2f95e7b63a05f1fd73a59a1ef3e4fd7612b89565b878152828e528181203382528e52818120548560018201610d04575b5050610c80565b610d0d9161274b565b928882528e528181203382528e5220558b89818085610cfd565b8284346103bb5760203660031901126103bb57506103b460209235612a9c565b505034610390578160031936011261039057602090517309e1c1c6b273d7fb7f56066d191b7a15205afcbc8152f35b505034610390578060031936011261039057602091610d93612642565b826024359133845260038652818420610dad84825461274b565b90556001600160a01b0316808452600386529220805482019055825190815233906000805160206131a5833981519152908590a35160018152f35b5050346103905781600319360112610390576020906011549051908152f35b505034610390578160031936011261039057602090516113888152f35b505034610390578160031936011261039057805190826001805491610e4883612526565b80865292828116908115610ed75750600114610e7b575b505050610e7182610472940383612592565b51918291826125b4565b94508085527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b828610610ebf57505050610e718260206104729582010194610e5f565b80546020878701810191909152909501948101610ea2565b610472975086935060209250610e7194915060ff191682840152151560051b82010194610e5f565b5050346103905780606092610f133661266e565b9160018060a01b038091168452600c60205284842091168352602052828220908252602052209081549160ff60026001830154920154169082519384526020840152151590820152f35b9050346104c857816003193601126104c857602092503590610f7d612658565b91610f8781612a9c565b92610fb48430337f000000000000000000000000f97b79ece2f95e7b63a05f1fd73a59a1ef3e4fd7612b02565b610fbe82826129da565b8251918483528583015260018060a01b0316907fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7833392a351908152f35b5050346103905781600319360112610390576020906012549051908152f35b50503461039057816003193601126103905760135490516001600160a01b039091168152602090f35b5050346103905781600319360112610390576020905173fa0bd2b4d6d629adf683e4dca310c562bcd98e4e8152f35b919050346104c85760203660031901126104c85760a09281906001600160a01b0361109c612642565b1681526007602052208054926001820154926002830154916003840154930154938151958652602086015284015260608301526080820152f35b5050346103905781600319360112610390576020905160328152f35b50503461039057806003193601126103905780602092611110612642565b611118612658565b6001600160a01b039182168352600b865283832091168252845220549051908152f35b5050346103905760203660031901126103905760209181906001600160a01b03611163612642565b1681526005845220549051908152f35b5050346103905781600319360112610390576020906010549051908152f35b8284346103bb57816003193601126103bb57506103b46020926111b3612658565b9035612914565b5050346103905781600319360112610390576020906014549051908152f35b505034610390578160031936011261039057602090600a549051908152f35b8284346103bb5760203660031901126103bb57506103b460209235612a84565b612508565b505034610390578160031936011261039057517f000000000000000000000000f97b79ece2f95e7b63a05f1fd73a59a1ef3e4fd76001600160a01b03168152602090f35b505034610390578160031936011261039057602090600f549051908152f35b9050346104c85760203660031901126104c85735916008548310156103bb57506112ab6020926126a3565b905491519160018060a01b039160031b1c168152f35b5050346103905781600319360112610390576020906103b461276e565b5050346103905781600319360112610390576020905160ff7f0000000000000000000000000000000000000000000000000000000000000012168152f35b83915034610390576020806003193601126104c85781359261133c612cc2565b73d0f3c7d3d02909014303d13223302efb80a29ff394858352600c845286832033845284528683208184528452600287842001805460ff81161561181a573385526007865288852054156118045733855260078652620151806113a4888b882001544261274b565b106117f45760ff19169055338352600d845286832090835283528582208054958360026001840193828554958183555501558751630240bc6b60e21b815288818881855afa9182156117ea57898891879388956117b5575b508151630b26c57160e31b815292839182905afa80156117ab578691879161177c575b5067ffffffffffffffff80600160401b9316830391838311611769576001600160c01b03831683036117695716820391821161175657896114696114fb94936114f6938e1b612c7f565b60b58d82600160881b81101561173f575b62010000916901000000000000000000821015611734575b5065010000000000811015611728575b630100000081101561171b575b010260121c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c8082040160011c809104818110611713575b50612c6c565b612c7f565b906001600160e01b0383168303611700576115189083871b612c7f565b11156116ec5750819361152b3033612f95565b856116e657845b33845260078552878420549061271091828102908082048414901517156116d357829161156561156a9260095490612c7f565b612c6c565b049361138891828602861593878204148417156116ad5781900495610fa080820290828204148517156116c057829004936103e88083029283041417156116ad5704923382526007875260018a832001805487808210600014611693575050503382526007875260028a8320836001820155016115e88782546129cd565b90556115f486306130d9565b3382526007875260028a83200161160c8482546129cd565b9055611619833330613162565b611644847f0000000000000000000000005922f957c2df08fe3dd5f6dd8f2f7fe14ff9c43f30613162565b338252600787528942922001558751938452848401528683015260608201527f88eba8ade1464e44174af9423c7b9821a015ebec60b869557eb779ab976f7d3960803392a28351928352820152f35b61169c9161274b565b90556116a886306130d9565b6115f4565b634e487b7160e01b835260118552602483fd5b634e487b7160e01b845260118652602484fd5b634e487b7160e01b865260118452602486fd5b85611532565b93945081946116fb3033612f95565b61152b565b634e487b7160e01b855260118752602485fd5b90508d6114f0565b60101c9160081b916114af565b8c1c9160101b916114a2565b1c918c1b918f611492565b5068b500000000000000009150608083901c61147a565b634e487b7160e01b875260118952602487fd5b634e487b7160e01b895260118b52602489fd5b905061179e91508a3d8c116117a4575b6117968183612592565b810190612c4b565b8b61141f565b503d61178c565b8a513d88823e3d90fd5b909450816117d99294503d85116117e3575b6117d18183612592565b810190612c20565b929092938c6113fc565b503d6117c7565b89513d87823e3d90fd5b8851638eb13b1560e01b81528790fd5b8851637b80153f60e11b81523381890152602490fd5b8851636ae05ce360e01b81523381890152602490fd5b505034610390578160031936011261039057602090516127108152f35b9050346104c857916000805160206131a58339815191529261186e3661266e565b8560018060a099959694991b0380951694858752602098848a958652838920338a528652838920548560001982036118d8575b505050868852600385528288206118b985825461274b565b9055169586815260038452208181540190558551908152a35160018152f35b6118e19161274b565b90888a528652838920338a528652838920553880856118a1565b919050346104c857826003193601126104c857806104729361191b612f6a565b503381526007602052209181519261080684612560565b6124c8565b5050346103905781600319360112610390576020906002549051908152f35b919050346104c8576119673661266e565b949190611972612c9f565b61197a612cc2565b9460018060a01b039182841694865198636eb1769f60e11b92838b5230858c01528a73d0f3c7d3d02909014303d13223302efb80a29ff397602495898784015260209d8e818d60449687915afa90811561205157918f918e8a8f958f908c8f918b968e9183916122b7575b50811161225a575b505050505051809481938252338d830152308c8301525afa90811561221257858f918a8f948f92908b91829161221c575b5084116121af575b505050505051908d86808285016323b872dd60e01b8152338b87015230878701526064958987820152868152611a5b81612560565b519082865af1903d156121a857503d67ffffffffffffffff8111612196578f908e5190611a9183601f19601f8401160183612592565b81523d898383013e5b82612164575b5050156121185773edc2b3bebbb4351a391363578c4248d672ba7f9b8b036121125760015b1561205b578b51630240bc6b60e21b81528c818a818e5afa801561205157898e89938a9361202c575b508d815192838092630b26c57160e31b82525afa90811561202157918f611b3f611b4691611b378c67ffffffffffffffff8f97611b529a99611b4c9991612003575b5016612c6c565b901c8b61274b565b9182612c6c565b926129cd565b90612c7f565b935b8515611fed57611b6686600a546129cd565b7f0000000000000000000000000000000000000000000000000000048c2739500010611fdd5760078f60038f338b528383528a2001611ba68982546129cd565b90553389525260038d88200154603281029080820460321490151715611fcb578f848f926007600293338d5252049189200155611be586600a546129cd565b600a55898116808d14611f94578a9291908d1015611f8d57505b16918215611f5d5750508951636e4a869b60e11b81523087820152988c8a87818c5afa998a15611f5357908d9291869b611f20575b5003611ea5578951633851850760e21b81523087820190815260208101859052604081019390935262015180606084015291829081906080010381868b5af1908115611e9b578391611e6e575b50985b88516370a0823160e01b81523086820152908b8286817309e1c1c6b273d7fb7f56066d191b7a15205afcbc5afa918215611e64578a85938e999795938c938c9a9892611e16575b50996002611d5b94611d7a969484611ce8611cf29f600b9761274b565b9e8f6010546129cd565b601055600f558c8952848452808920338a528452808920549c8952600c8452808920338a5284528089208d8a52845288209081558c600182015501600160ff198254161790558b8652528d8c852090338652528b8420611d528154612f5b565b90553390612914565b33835260078d528a8320611d708282546129cd565b90556009546129cd565b60095560085492600160401b841015611e065750505090611dc48260017f8bab6aed5a508937051a144e61d6e61336834a66aaee250a00613ae6f744c422969594016008556126a3565b819291549060031b9133831b921b1916179055611df886519283923397846040919493926060820195825260208201520152565b0390a3600160065551908152f35b634e487b7160e01b825260419052fd5b9496989a935094969850505081813d8311611e5d575b611e368183612592565b81010312611e5957518b968896909590949093909290918a918c91906002611ccb565b8380fd5b503d611e2c565b8a513d86823e3d90fd5b90508a81813d8311611e94575b611e858183612592565b810103126104c8575138611c81565b503d611e7b565b89513d85823e3d90fd5b895163186b30c760e11b81523087820190815260208101859052604081019390935262015180606084015291829081906080010381868b5af1908115611e9b578391611ef3575b5098611c84565b90508a81813d8311611f19575b611f0a8183612592565b810103126104c8575138611eec565b503d611f00565b83819394929c503d8311611f4c575b611f398183612592565b81010312610a8257908c91519938611c34565b503d611f2f565b8b513d87823e3d90fd5b8b5162461bcd60e51b81528089018f9052600c818901526b7a65726f206164647265737360a01b91810191909152fd5b9050611bff565b8f8a8f8760138d8995726964656e746963616c2061646472657373657360681b94519562461bcd60e51b8752860152840152820152fd5b634e487b7160e01b885260118a528888fd5b8c51632484557960e01b81528990fd5b8c51631c3ab27f60e31b8152808a018790528890fd5b61201a9150853d87116117a4576117968183612592565b5038611b30565b508e513d8a823e3d90fd5b906120479294508093503d84116117e3576117d18183612592565b9290929138611aee565b8d513d89823e3d90fd5b8b51630240bc6b60e21b81528c818a818e5afa90811561205157898e89928a946120ed575b508d815192838092630b26c57160e31b82525afa90811561202157918f611b3f611b4691611b378c67ffffffffffffffff8f976120c89a99611b4c99916120ce575016612c6c565b93611b54565b6120e59150853d87116117a4576117968183612592565b905038611b30565b908094506121089293503d85116117e3576117d18183612592565b9190919238612080565b85611ac5565b8b5162461bcd60e51b81528089018f90526023818901527f7472616e7366657248656c7065723a207472616e7366657246726f6d2066616981850152621b195960ea1b81840152608490fd5b809192505191821592831561217e575b5050508e38611aa0565b61218e9350820181019101612c08565b388f81612174565b634e487b7160e01b885260418a528888fd5b6060611a9a565b945163095ea7b360e01b815230918101918252602082019390935291938492839190829060400103925af1801561221257908e916121f4575b508b90858c8a89611a26565b8161220a92903d10610abf57610ab08183612592565b508c386121e8565b8c513d88823e3d90fd5b9650505050505081813d8311612253575b6122378183612592565b8101031261224f578d858c8a8f94838b915190611a1e565b8580fd5b503d61222d565b9092959498919397969851978896879563095ea7b360e01b87528601528401525af1801561205157918f918e86918f9561229a575b508a8e8d8c8c6119ed565b6122b090853d8711610abf57610ab08183612592565b503861228f565b99989a50505050505050505081813d83116122f3575b6122d78183612592565b81010312610744578b918f918e8a8e8d8c8c808c9751906119e5565b503d6122cd565b5050346103905781600319360112610390576020905173d0f3c7d3d02909014303d13223302efb80a29ff38152f35b8284346103bb5760203660031901126103bb57506103b460209235612ab4565b9050346104c857816003193601126104c857602092612366612642565b918360243592839233825287528181209460018060a01b0316948582528752205582519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925843392a35160018152f35b5050346103905781600319360112610390578051908280546123d981612526565b80855291600191808316908115610ed7575060011461240457505050610e7182610472940383612592565b80809650527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b82861061244857505050610e718260206104729582010194610e5f565b8054602087870181019190915290950194810161242b565b5050346103905781600319360112610390578190826008545b80821061248a576020848451908152f35b90926103846124c29161249c866126a3565b905460039190911b1c6001600160a01b03168752600760205284872060010154906129cd565b90612479565b346125035760003660031901126125035760206040517f0000000000000000000000000000000000000000000000000000048c273950008152f35b600080fd5b34612503576000366003190112612503576020600a54604051908152f35b90600182811c92168015612556575b602083101461254057565b634e487b7160e01b600052602260045260246000fd5b91607f1691612535565b60a0810190811067ffffffffffffffff82111761257c57604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111761257c57604052565b6020808252825181830181905290939260005b8281106125e957505060409293506000838284010152601f8019910116010190565b8181018601518482016040015285016125c7565b34612503576000366003190112612503576040517f0000000000000000000000005922f957c2df08fe3dd5f6dd8f2f7fe14ff9c43f6001600160a01b03168152602090f35b600435906001600160a01b038216820361250357565b602435906001600160a01b038216820361250357565b6060906003190112612503576001600160a01b0390600435828116810361250357916024359081168103612503579060443590565b6008548110156126da5760086000527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30190600090565b634e487b7160e01b600052603260045260246000fd5b3461250357602036600319011261250357612709612642565b5060206040516000198152f35b606090600319011261250357600435906001600160a01b03906024358281168103612503579160443590811681036125035790565b9190820391821161275857565b634e487b7160e01b600052601160045260246000fd5b6000467f0000000000000000000000000000000000000000000000000000000000000012036127bc57507f590c63a54411cd8937d21ec3cc271d7a80b9934b644eb1b6cf6fd1395746187e90565b604051815482916127cc82612526565b80825281602094858201946001908782821691826000146128f657505060011461289d575b506127fe92500382612592565b51902091604051918201927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f845260408301527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608301524660808301523060a083015260a0825260c082019082821067ffffffffffffffff831117612889575060405251902090565b634e487b7160e01b81526041600452602490fd5b87805286915087907f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b8583106128de5750506127fe9350820101386127f1565b805483880185015286945088939092019181016128c7565b60ff191688526127fe95151560051b85010192503891506127f19050565b91909161292081612a67565b92831561299a576129538230337f000000000000000000000000f97b79ece2f95e7b63a05f1fd73a59a1ef3e4fd7612b02565b61295d84826129da565b60405191825283602083015260018060a01b0316907fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d760403392a3565b60405162461bcd60e51b815260206004820152600b60248201526a5a45524f5f53484152455360a81b6044820152606490fd5b9190820180921161275857565b6000805160206131a583398151915260206000926129fa856002546129cd565b6002556001600160a01b03168084526003825260408085208054870190555194855293a3565b906000805160206131a5833981519152602060009360018060a01b0316928385526003825260408520612a5482825461274b565b90558060025403600255604051908152a3565b60025480612a73575090565b90612a8191600a5491612ace565b90565b60025480612a90575090565b600a54612a8192612ace565b60025480612aa8575090565b600a54612a8192612ae4565b60025480612ac0575090565b90612a8191600a5491612ae4565b8160001904811182021583021561250357020490565b81600019048111820215830215612503570290808204910615150190565b9160008093602095606494604051946323b872dd60e01b865260018060a01b03809216600487015216602485015260448401525af13d15601f3d1160016000511416171615612b4d57565b60405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606490fd5b60405163a9059cbb60e01b81526001600160a01b039092166004830152602482019290925260209160009160449183905af13d15601f3d1160016000511416171615612bd157565b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b90816020910312612503575180151581036125035790565b9190826040910312612503576020825192015190565b519067ffffffffffffffff8216820361250357565b919082604091031261250357612a816020612c6584612c36565b9301612c36565b8181029291811591840414171561275857565b8115612c89570490565b634e487b7160e01b600052601260045260246000fd5b600260065414612cb0576002600655565b604051633ee5aeb560e01b8152600490fd5b612cd1600e54601154906129cd565b4211612ef0575b604080516370a0823160e01b808252306004830152929091602080846024817309e1c1c6b273d7fb7f56066d191b7a15205afcbc5afa938415612ee557600094612eb6575b5083600f5411612d2f575b5050909150565b612d3d6010546012546129cd565b6012556000601055815194855230600486015273edc2b3bebbb4351a391363578c4248d672ba7f9b8186602481845afa958615612eab57600096612e7c575b5060145495868111612d90575b5050612d28565b91808792612da4612e0b99612dc79661274b565b90612dce670de0b6b3a7640000612dbd60155485612c6c565b049687809461274b565b80966129cd565b601455601354875163a9059cbb60e01b81526001600160a01b03909116600482015260248101929092529098899190829060009082906044820190565b03925af1968715612e71577f75a069991e91c23805e347f22c49599d8677ebe627010248cccf51428a27bd94959697612e54575b508351928352820152a1819038808080612d89565b612e6a90823d8411610abf57610ab08183612592565b5038612e3f565b84513d6000823e3d90fd5b90958282813d8311612ea4575b612e938183612592565b810103126103bb5750519438612d7c565b503d612e89565b83513d6000823e3d90fd5b90938482813d8311612ede575b612ecd8183612592565b810103126103bb5750519238612d1d565b503d612ec3565b82513d6000823e3d90fd5b604051623438dd60e41b815230600482015260208160248160007309e1c1c6b273d7fb7f56066d191b7a15205afcbc5af1612f30575b5042600e55612cd8565b602090813d8111612f54575b612f468183612592565b810103126125035738612f26565b503d612f3c565b60001981146127585760010190565b60405190612f7782612560565b60006080838281528260208201528260408201528260608201520152565b90604051602092838201926323b872dd60e01b845260018060a01b0380921660248401521660448201526305f5e100606482015260648152612fd681612560565b60008092819251908273fa0bd2b4d6d629adf683e4dca310c562bcd98e4e5af1903d156130d1573d67ffffffffffffffff81116130bd5760405190613024601f8201601f1916860183612592565b81528091843d92013e5b8161308d575b501561303d5750565b6084906040519062461bcd60e51b82526004820152602360248201527f7472616e7366657248656c7065723a207472616e7366657246726f6d206661696044820152621b195960ea1b6064820152fd5b805180159250839083156130a5575b50505038613034565b6130b59350820181019101612c08565b38828161309c565b634e487b7160e01b82526041600452602482fd5b50606061302e565b604051602092838201926323b872dd60e01b845260018060a01b03166024830152600092828493928460448195015260648201526064815261311a81612560565b51908273fa0bd2b4d6d629adf683e4dca310c562bcd98e4e5af1903d156130d1573d67ffffffffffffffff81116130bd5760405190613024601f8201601f1916860183612592565b6040516323b872dd60e01b60208083019182526001600160a01b03938416602484015292909316604482015260648082019490945292835291612fd68161256056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212201fb1092f4f678c9bf89369a90cb07e1f351d60f6c7ea7c4bf1150fdf08ebc0e264736f6c63430008130033