Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x9999999b8ce70322b021EFe340759B7958af43C8
Balance 0 ETH
Nonce 1
Code Size 5868 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

5868 bytes
0x60806040526004361015610011575f80fd5b5f3560e01c806304bae72a146106e057806312c46e61146106b85780631482f7a31461069b5780632248aebc146105925780633927a5e71461054557806343bad45b1461051a5780636ba2eeb8146104fd5780637b103999146104b95780638918a2d6146104885780638da5cb5b1461015e57806396c82e571461046b57806396caeb851461044f578063a8e303bd146103bd578063aaf5eb681461039b578063abe7c2e71461037e578063b5427eb91461028a578063b58a374114610242578063b8b89e1b14610225578063c5650cc514610208578063ca1a66d5146101eb578063cbe52ae3146101a2578063f2f4eb261461015e5763f8d8989814610116575f80fd5b3461015a575f36600319011261015a576040517f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b03168152602090f35b5f80fd5b3461015a575f36600319011261015a576040517f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b03168152602090f35b3461015a57604036600319011261015a576101e76101ca6101c161085e565b60243590610d39565b604080519384526020840192909252908201529081906060820190565b0390f35b3461015a575f36600319011261015a576020600854604051908152f35b3461015a575f36600319011261015a576020600754604051908152f35b3461015a575f36600319011261015a576020600554604051908152f35b3461015a57602036600319011261015a576001600160a01b0361026361085e565b165f5260016020526040805f205481519067ffffffffffffffff81168252821c6020820152f35b3461015a5761029836610844565b916102cd337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614610874565b5f548311610328576103237f46774032a08b25b01baf38d7de856b2b3813b85b10d5de24a243f11adf21297d93826003558360045580600555604051938493846040919493926060820195825260208201520152565b0390a1005b60405162461bcd60e51b815260206004820152602860248201527f6d617820646973636f756e742065786365656473206261736520726564656d7060448201526774696f6e2066656560c01b6064820152608490fd5b3461015a575f36600319011261015a576020600454604051908152f35b3461015a575f36600319011261015a576020604051670de0b6b3a76400008152f35b3461015a57602036600319011261015a576103d661085e565b61040a337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614610874565b600680546001600160a01b0319166001600160a01b039290921691821790557f468837374e48b1824be120883cffa04faab0ee936e11c4d93f1fc0ab72471bab5f80a2005b3461015a575f36600319011261015a5760205f54604051908152f35b3461015a575f36600319011261015a576020600254604051908152f35b3461015a57604036600319011261015a5760206104af6104a661085e565b602435906110e8565b5050604051908152f35b3461015a575f36600319011261015a576040517f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03168152602090f35b3461015a575f36600319011261015a576020600954604051908152f35b3461015a57602036600319011261015a57602061053d61053861085e565b610c2d565b604051908152f35b3461015a5760a036600319011261015a5761055e61085e565b606435906001600160a01b038216820361015a57608435801515810361015a5760209261053d92604435906024359061092e565b3461015a57602036600319011261015a576004356105da337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614610874565b670de0b6b3a76400008111610667576005548110610622576020817f07235bf8b54975eb3c8c8025815928dcf47dd130fcfe6eb1a94bb78256953807925f55604051908152a1005b60405162461bcd60e51b815260206004820152601c60248201527f66656520686967686572207468616e206d617820646973636f756e74000000006044820152606490fd5b60405162461bcd60e51b815260206004820152600c60248201526b0cccaca40e8dede40d0d2ced60a31b6044820152606490fd5b3461015a575f36600319011261015a576020600354604051908152f35b3461015a575f36600319011261015a576006546040516001600160a01b039091168152602090f35b3461015a576106ee36610844565b91610723337f000000000000000000000000c07e000044f95655c11fda4cd37f70a94d7e0a7d6001600160a01b031614610874565b5f546005810290808204600514901517156107eb5781116107ff5760631983018381116107eb57821115806107df575b156107a6576103237f3a32c9b377fd1736c79d0c0d59cabe4d34a198de4f74c45239c9baf4a6cb485893826009558360075580600855604051938493846040919493926060820195825260208201520152565b60405162461bcd60e51b81526020600482015260116024820152701a5b9d985b1a59081cdd185c9d0bd95b99607a1b6044820152606490fd5b50612710831115610753565b634e487b7160e01b5f52601160045260245ffd5b60405162461bcd60e51b815260206004820152601760248201527f6f7665722075736167652066656520746f6f20686967680000000000000000006044820152606490fd5b606090600319011261015a57600435906024359060443590565b600435906001600160a01b038216820361015a57565b1561087b57565b60405162461bcd60e51b815260206004820152600560248201526421636f726560d81b6044820152606490fd5b818102929181159184041417156107eb57565b919082039182116107eb57565b6040810190811067ffffffffffffffff8211176108e457604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff8211176108e457604052565b51906001600160a01b038216820361015a57565b9290935f9361093d86826110e8565b90958211610b305760409260018060a01b031695865f526001602052835f209067ffffffffffffffff81511690602067ffffffffffffffff19910151861b1617905560025530908315610b28575b6084905f84519788948593637eb6829360e11b85523360048601528c6024860152604485015260018060a01b031660648401525af1948515610adb575f935f96610ae6575b507f00000000000000000000000057ab1e0003f623289cd798b1824be09a793e4bec6001600160a01b031690813b1561015a575f91604483926040519485938492632770a7eb60e21b845233600485015260248401525af18015610adb57610ac6575b50610a3e5750505090565b604051635d043b2960e11b815260048101949094526001600160a01b039081166024850152306044850152919291602091839160649183918791165af1918215610aba5791610a8b575090565b90506020813d602011610ab2575b81610aa6602093836108f8565b8101031261015a575190565b3d9150610a99565b604051903d90823e3d90fd5b610ad39194505f906108f8565b5f925f610a33565b6040513d5f823e3d90fd5b935094506040833d604011610b20575b81610b03604093836108f8565b8101031261015a576020610b168461091a565b930151945f6109d0565b3d9150610af6565b84915061098b565b60405162461bcd60e51b815260206004820152600c60248201526b666565203e206d617846656560a01b6044820152606490fd5b519067ffffffffffffffff8216820361015a57565b51906001600160801b038216820361015a57565b80910360e0811261015a578151926060601f1983011261015a576040516060810181811067ffffffffffffffff8211176108e457604052610bd060208501610b64565b8152610bde60408501610b64565b602082015260608401516040820152926040608082015193609f19011261015a57610c2560c060405192610c11846108c8565b610c1d60a08201610b79565b845201610b79565b602082015290565b604051631959e76b60e31b8152906001600160a01b031660e082600481845afa908115610adb576004925f92610ce3575b50602090604051938480926392bbcaed60e01b82525afa918215610adb575f92610ca8575b50516001600160801b031690808210610ca257610c9f916108bb565b90565b50505f90565b9091506020813d602011610cdb575b81610cc4602093836108f8565b8101031261015a5751906001600160801b03610c83565b3d9150610cb7565b6020919250610d099060e03d60e011610d14575b610d0181836108f8565b810190610b8d565b925050509190610c5e565b503d610cf7565b8115610d25570490565b634e487b7160e01b5f52601260045260245ffd5b610d4382826110e8565b5050908180670de0b6b3a764000003670de0b6b3a764000081116107eb57610d74670de0b6b3a764000091866108a8565b604051631959e76b60e31b81526001600160a01b0390941695919004929060e082600481895afa918215610adb575f926110b6575b506040516392bbcaed60e01b8152906020826004818a5afa918215610adb575f92611082575b50610dda85826108bb565b60405163ab7cfaf960e01b8152906020826004818c5afa918215610adb575f92611044575b5091670de0b6b3a7640000610e1f610e26936001600160801b03956108a8565b04906108bb565b92511691828111928315611030575b5050506110255750604051633eeefe5360e21b815292606084600481845afa938415610adb575f94610fc3575b5060206004916040519283809263d8dfeb4560e01b82525afa908115610adb575f91610f84575b506040516315caaba160e21b81526001600160a01b039182166004820181905294909160209183916024918391165afa908115610adb575f91610f52575b508015610d2557670de0b6b3a7640000610ef76024936020936ec097ce7bc90715b34b9f100000000004906108a8565b04936040519283809263266d6a8360e11b82528760048301525afa908115610adb575f91610f23575092565b90506020813d602011610f4a575b81610f3e602093836108f8565b8101031261015a575192565b3d9150610f31565b90506020813d602011610f7c575b81610f6d602093836108f8565b8101031261015a57515f610ec7565b3d9150610f60565b90506020813d602011610fbb575b81610f9f602093836108f8565b8101031261015a576020610fb460249261091a565b9150610e89565b3d9150610f92565b9093506060813d60601161101d575b81610fdf606093836108f8565b8101031261015a57610ff08161091a565b90602081015163ffffffff81160361015a57604001516001600160e01b0381160361015a57926020610e62565b3d9150610fd2565b925050505f915f9190565b61103b9293506108bb565b105f8080610e35565b9150916020823d60201161107a575b81611060602093836108f8565b8101031261015a5790519091670de0b6b3a7640000610dff565b3d9150611053565b9091506020813d6020116110ae575b8161109e602093836108f8565b8101031261015a5751905f610dcf565b3d9150611091565b6110d091925060e03d60e011610d1457610d0181836108f8565b92505050905f610da9565b919082018092116107eb57565b9190915f60206040516110fa816108c8565b82815201526040516306fdde0360e01b81526001600160a01b039190911692905f81600481875afa908115610adb575f91611652575b5060208091604460405180948193638413aec760e01b83528160048401528051918291826024860152018484015e5f828201840152601f01601f191681010301817f00000000000000000000000010101010e0c3171d894b71b3400668af311e7d946001600160a01b03165afa8015610adb5784915f91611616575b506001600160a01b0316036115db57604051631959e76b60e31b815260e081600481875afa908115610adb575f916115b8575b505f916001600160801b03825116611579575b5050825f52600160205260405f20916040519261120e846108c8565b549167ffffffffffffffff83169283855260401c9360208101908582528095600254956114f4575b504267ffffffffffffffff16905280515f919061125c906001600160c01b0316866110db565b611456575b805161127b90600185901c906001600160c01b03166110db565b6004548251919690946001600160c01b039283169083160192909183116107eb576001600160c01b039283168082526112c993861690811061144e575b50516001600160c01b0316906110db565b9380831115611446576112dc90836108bb565b915b670de0b6b3a7640000830292808404670de0b6b3a764000014901517156107eb57670de0b6b3a764000061132361131b61132c9361133196610d1b565b6005546108a8565b04915f546110db565b6108bb565b9360018060a01b03600654169081611347575050565b602060049160405192838092636f307dc360e01b82525afa8015610adb575f9061140c575b6040516315caaba160e21b81526001600160a01b0390911660048201529160209150829060249082905afa908115610adb575f916113da575b50670de0b6b3a764000081116113b85750565b909390670de0b6b3a763ffff1981019081116107eb576113d7916110db565b92565b90506020813d602011611404575b816113f5602093836108f8565b8101031261015a57515f6113a5565b3d91506113e8565b506020813d60201161143e575b81611426602093836108f8565b8101031261015a5761143960209161091a565b61136c565b3d9150611419565b505f916112de565b81525f6112b8565b80516001600160c01b039081166127108181029092169181830414901517156107eb5781516114999190611493906001600160c01b0316886110db565b90610d1b565b6007548082116114ab575b5050611261565b6114e3929350806114c16114c7926008546108bb565b926108bb565b818111156114eb57506114de815b600954906108a8565b610d1b565b905f806114a4565b6114de906114d5565b6114fe91956108bb565b9361151467ffffffffffffffff825116426108bb565b6003546001600160c01b039161152a91906108a8565b83519116906001600160c01b0316818110156115555750505f5b6001600160c01b031682525f611236565b036001600160c01b0381111561154457634e487b7160e01b5f52601160045260245ffd5b909150670de0b6b3a7640000810290808204670de0b6b3a764000014901517156107eb576001600160801b036115b192511690610d1b565b5f806111f2565b6115d1915060e03d60e011610d1457610d0181836108f8565b925050505f6111df565b60405162461bcd60e51b81526020600482015260136024820152721c185a5c881b9bdd081c9959da5cdd195c9959606a1b6044820152606490fd5b9150506020813d60201161164a575b81611632602093836108f8565b8101031261015a57611644849161091a565b5f6111ac565b3d9150611625565b90503d805f833e61166381836108f8565b81019060208183031261015a5780519067ffffffffffffffff821161015a570181601f8201121561015a5780519167ffffffffffffffff83116108e457604051906116b8601f8501601f1916602001836108f8565b8382526020848401011161015a575f6020848195828096018386015e83010152915061113056fea164736f6c634300081c000a

Verified Source Code Partial Match

Compiler: v0.8.28+commit.7893614a EVM: cancun Optimization: Yes (200 runs)
RedemptionHandler.sol 283 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { CoreOwnable } from '../dependencies/CoreOwnable.sol';
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "../libraries/SafeERC20.sol";
import { IResupplyPair } from "../interfaces/IResupplyPair.sol";
import { IResupplyRegistry } from "../interfaces/IResupplyRegistry.sol";
import { IOracle } from "../interfaces/IOracle.sol";
import { IERC4626 } from "../interfaces/IERC4626.sol";
import { IMintable } from "../interfaces/IMintable.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

//Contract that interacts with pairs to perform redemptions
//Can swap out this contract for another to change logic on how redemption fees are calculated.
//for example can give fee discounts based on certain conditions (like utilization) to
//incentivize redemptions across multiple pools etc
contract RedemptionHandler is CoreOwnable{
    using SafeERC20 for IERC20;

    address public immutable registry;
    address public immutable debtToken;

    uint256 public baseRedemptionFee = 1e16; //1%
    uint256 public constant PRECISION = 1e18;

    struct RedeemptionRateInfo {
        uint64 timestamp;  //time since last update
        uint192 usage;  //usage weight, defined by % of pair redeemed. thus a pair redeemed for 2% three times will have a weight of 6
    }
    mapping(address => RedeemptionRateInfo) public ratingData;
    uint256 public totalWeight;
    uint256 public usageDecayRate = 1e17 / uint256(7 days); //10% per week
    uint256 public maxUsage = 3e17; //max usage of 30%. any thing above 30% will be 0 discount.  linearly scale between 0 and maxusage
    uint256 public maxDiscount = 5e14; //up to 0.05% discount

    address public underlyingOracle;

    uint256 public overusageStart = 1000; //at what usage% do fees start
    uint256 public overusageMax = 1500; //at what usage% do fees reach max
    uint256 public overusageRate = 1e16; //at max

    event SetBaseRedemptionFee(uint256 _fee);
    event SetDiscountInfo(uint256 _fee, uint256 _maxUsage, uint256 _maxDiscount);
    event SetOverusageInfo(uint256 _fee, uint256 _start, uint256 _end);
    event SetUnderlyingOracle(address indexed _oracle);

    constructor(address _core, address _registry, address _underlyingOracle) CoreOwnable(_core){
        registry = _registry;
        debtToken = IResupplyRegistry(_registry).token();
        underlyingOracle = _underlyingOracle;
        emit SetUnderlyingOracle(_underlyingOracle);
    }

    /// @notice Sets the base redemption fee.
    /// @dev This fee is not the effective fee. The effective fee is calculated at time of redemption via ``getRedemptionFeePct``.
    /// @param _fee The new base redemption fee, must be <= 1e18 (100%)
    function setBaseRedemptionFee(uint256 _fee) external onlyOwner{
        require(_fee <= 1e18, "fee too high");
        require(_fee >= maxDiscount, "fee higher than max discount");
        baseRedemptionFee = _fee;
        emit SetBaseRedemptionFee(_fee);
    }

    function setDiscountInfo(uint256 _rate, uint256 _maxUsage, uint256 _maxDiscount) external onlyOwner{
        require(_maxDiscount <= baseRedemptionFee, "max discount exceeds base redemption fee");
        usageDecayRate = _rate;
        maxUsage = _maxUsage;
        maxDiscount = _maxDiscount;
        emit SetDiscountInfo(_rate, _maxUsage, _maxDiscount);
    }

    function setOverusageInfo(uint256 _rate, uint256 _start, uint256 _end) external onlyOwner{
        require(_rate <= baseRedemptionFee*5, "over usage fee too high");
        require(_start <= _end-100 && _end <= 10_000, "invalid start/end");
        overusageRate = _rate;
        overusageStart = _start;
        overusageMax = _end;
        emit SetOverusageInfo(_rate, _start, _end);
    }

    function setUnderlyingOracle(address _oracle) external onlyOwner{
        underlyingOracle = _oracle;
        emit SetUnderlyingOracle(_oracle);
    }

    /// @notice Estimates the maximum amount of debt that can be redeemed from a pair
    function getMaxRedeemableDebt(address _pair) external view returns(uint256){
        (,,,IResupplyPair.VaultAccount memory _totalBorrow) = IResupplyPair(_pair).previewAddInterest();
        
        uint256 minLeftoverDebt = IResupplyPair(_pair).minimumLeftoverDebt();
        if (_totalBorrow.amount < minLeftoverDebt) return 0;

        return _totalBorrow.amount - minLeftoverDebt;
    }

    /// @notice Calculates the total redemption fee as a percentage of the redemption amount.
    function getRedemptionFeePct(address _pair, uint256 _amount) public view returns(uint256){
        //get fee
        (uint256 feePct,,) = _getRedemptionFee(_pair, _amount);
        return feePct;
    }

    function _getRedemptionFee(address _pair, uint256 _amount) internal view returns(uint256 _redemptionfee, RedeemptionRateInfo memory _rdata, uint256 _totalweight){
        require(IResupplyRegistry(registry).pairsByName(IERC20Metadata(_pair).name()) == _pair, "pair not registered");
        (, , , IResupplyPair.VaultAccount memory _totalBorrow) = IResupplyPair(_pair).previewAddInterest();
        
        //determine the weight of this current redemption by dividing by pair's total borrow
        uint256 weightOfRedeem;
        if (_totalBorrow.amount != 0) weightOfRedeem = _amount * PRECISION / _totalBorrow.amount;

        //update current data with decay rate
        // RedeemptionRateInfo memory rdata = ratingData[_pair];
        _rdata = ratingData[_pair];
        
        _totalweight = totalWeight;

        //only decay if this pair has been used before
        if(_rdata.timestamp != 0){
            //remove current from total (add back after calcs)
            _totalweight -= _rdata.usage;

            //reduce useage by time difference since last redemption
            uint192 decay = uint192((block.timestamp - _rdata.timestamp) * usageDecayRate);

            //set the pair's usage or weight
            _rdata.usage = _rdata.usage < decay ? 0 : _rdata.usage - decay;
        }
        //update timestamp
        _rdata.timestamp = uint64(block.timestamp);

        //check for over usage and apply a fee
        ///this uses the current pair weight *before* the current redemption is applied but after the time decay is taken into account
        uint256 overusageFee;
        if(_totalweight+_rdata.usage > 0){
            //get overall% of weight this pair has compared to total (add back in _rdata.usage(after decay) as it was removed above)
            uint256 ratio = _rdata.usage * 10_000 / (_totalweight+_rdata.usage);

            //check if current % of total crossed into the start line
            uint256 _start = overusageStart;
            if(ratio > _start){
                //get the difference of end(max) - start
                uint256 usagediff = overusageMax - _start;
                //remove start so that our equation is scaled from 0 to usagediff
                overusageFee = ratio - _start;
                //clamp to max
                overusageFee = overusageFee > usagediff ? usagediff : overusageFee;

                //scale additive fee linearly to max
                overusageFee = overusageFee * overusageRate / usagediff;
            }
        }

        //use halfway point as the current weight for fee calc
        //using pre weight would have high discount, using post weight would have low discount
        //just use the half way point by using current + half the newly added weight
        uint256 halfway = _rdata.usage + (weightOfRedeem/2);
        
        uint256 _maxusage = maxUsage;

        //add new weight to the struct
        _rdata.usage += uint192(weightOfRedeem);
        //clamp to max usage
        if(_rdata.usage > uint192(_maxusage)){
            _rdata.usage = uint192(_maxusage);
        }

        //add to total weight
        _totalweight += _rdata.usage;
    
        //calculate the discount and final fee (base fee minus discount)
        
        //first get how close we are to _maxusage by taking difference.
        //if halfway is >= to _maxusage then discount is 0.
        //if halfway is == to 0 then discount equals our max usage
        uint256 discount = _maxusage > halfway ? _maxusage - halfway : 0;
        
        //convert the above value to a percentage with precision 1e18
        //if halfway is 8 units of usage then discount is 2 (10-8)
        //thus below should convert to 20%  (2 is 20% of the max usage 10)
        discount = (discount * PRECISION / _maxusage); //discount is now a 1e18 precision % 
        
        //take above percentage of maxDiscount as our final discount
        //above example is 20% so a 0.2 max discount * 20% will be 0.04 discount (2e15 * 20% = 4e14)
        discount = (maxDiscount * discount / PRECISION);// get % of maxDiscount
        
        //remove from (base fee + overusage) the discount and return
        //above example will be 1.0 - 0.04 = 0.96% fee (1e16 - 4e14)
        _redemptionfee = baseRedemptionFee + overusageFee - discount;

        //check if underlying being redeemed is overly priced
        if(underlyingOracle != address(0)){
            uint256 price = IOracle(underlyingOracle).getPrices(IResupplyPair(_pair).underlying());
            if(price > 1e18){
                //if overly priced then add on to fee
                _redemptionfee += (price - 1e18);
            }
        }
    }


    /// @notice Redeem stablecoins for collateral from a pair
    /// @param _pair The address of the pair to redeem from
    /// @param _amount The amount of stablecoins to redeem
    /// @param _maxFeePct The maximum fee pct (in 1e18) that the caller will accept
    /// @param _receiver The address that will receive the withdrawn collateral
    /// @param _redeemToUnderlying Whether to unwrap the collateral to the underlying asset
    /// @return _ amount received of either collateral shares or underlying, depending on `_redeemToUnderlying`
    function redeemFromPair (
        address _pair,
        uint256 _amount,
        uint256 _maxFeePct,
        address _receiver,
        bool _redeemToUnderlying
    ) external returns(uint256){
        //get fee
        (uint256 feePct, RedeemptionRateInfo memory rdata, uint256 _newTotalWeight) = _getRedemptionFee(_pair, _amount);
        
        //check against maxfee to avoid frontrun
        require(feePct <= _maxFeePct, "fee > maxFee");

        //write new rating data to state
        ratingData[_pair] = rdata;
        totalWeight = _newTotalWeight;

        address returnToAddress = address(this);
        if(!_redeemToUnderlying){
            //if directly redeeming lending collateral, send directly to receiver
            returnToAddress = _receiver;
        }
        (address _collateral, uint256 _returnedCollateral) = IResupplyPair(_pair).redeemCollateral(
            msg.sender,
            _amount,
            feePct,
            returnToAddress
        );

        IMintable(debtToken).burn(msg.sender, _amount);

        //withdraw to underlying
        //if false receiver will have already received during redeemCollateral()
        //unwrap only if true
        if(_redeemToUnderlying){
            return IERC4626(_collateral).redeem(_returnedCollateral, _receiver, address(this));
        }
        
        return _returnedCollateral;
    }

    function previewRedeem(address _pair, uint256 _amount) external view returns(uint256 _returnedUnderlying, uint256 _returnedCollateral, uint256 _fee){
        //get fee
        (_fee,,) = _getRedemptionFee(_pair, _amount);

        //value to redeem
        uint256 valueToRedeem = _amount * (1e18 - _fee) / 1e18;

        //add interest and check amount bounds
        (,,, IResupplyPair.VaultAccount memory _totalBorrow) = IResupplyPair(_pair).previewAddInterest();
        uint256 minLeftoverDebt = IResupplyPair(_pair).minimumLeftoverDebt();
        uint256 protocolFee = (_amount - valueToRedeem) * IResupplyPair(_pair).protocolRedemptionFee() / 1e18;
        uint256 debtReduction = _amount - protocolFee;

        //return 0 if given amount is out of bounds
        if(debtReduction > _totalBorrow.amount || _totalBorrow.amount - debtReduction < minLeftoverDebt ){
            return (0,0, _fee);
        }

        //get exchange
        (address oracle, , ) = IResupplyPair(_pair).exchangeRateInfo();
        address collateralVault = IResupplyPair(_pair).collateral();

        uint256 exchangeRate = IOracle(oracle).getPrices(collateralVault);
        //convert price of collateral as debt is priced in terms of collateral amount (inverse)
        exchangeRate = 1e36 / exchangeRate;

        //calc collateral units
        _returnedCollateral = ((valueToRedeem * exchangeRate) / 1e18);

        //preview redeem of underlying
        _returnedUnderlying = IERC4626(collateralVault).previewRedeem(_returnedCollateral);
    }

}
CoreOwnable.sol 27 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {ICore} from "../interfaces/ICore.sol";

/**
    @title Core Ownable
    @author Prisma Finance (with edits by Resupply Finance)
    @notice Contracts inheriting `CoreOwnable` have the same owner as `Core`.
            The ownership cannot be independently modified or renounced.
 */
contract CoreOwnable {
    ICore public immutable core;

    constructor(address _core) {
        core = ICore(_core);
    }

    modifier onlyOwner() {
        require(msg.sender == address(core), "!core");
        _;
    }

    function owner() public view returns (address) {
        return address(core);
    }
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
SafeERC20.sol 67 lines
// SPDX-License-Identifier: ISC
pragma solidity ^0.8.19;

import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { SafeERC20 as OZSafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

// solhint-disable avoid-low-level-calls
// solhint-disable max-line-length

/// @title SafeERC20 provides helper functions for safe transfers as well as safe metadata access
/// @author Library originally written by @Boring_Crypto github.com/boring_crypto, modified by Drake Evans (Frax Finance) github.com/drakeevans
/// @dev original: https://github.com/boringcrypto/BoringSolidity/blob/fed25c5d43cb7ce20764cd0b838e21a02ea162e9/contracts/libraries/BoringERC20.sol
library SafeERC20 {
    bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol()
    bytes4 private constant SIG_NAME = 0x06fdde03; // name()
    bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals()

    function returnDataToString(bytes memory data) internal pure returns (string memory) {
        if (data.length >= 64) {
            return abi.decode(data, (string));
        } else if (data.length == 32) {
            uint8 i = 0;
            while (i < 32 && data[i] != 0) {
                i++;
            }
            bytes memory bytesArray = new bytes(i);
            for (i = 0; i < 32 && data[i] != 0; i++) {
                bytesArray[i] = data[i];
            }
            return string(bytesArray);
        } else {
            return "???";
        }
    }

    /// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string.
    /// @param token The address of the ERC-20 token contract.
    /// @return (string) Token symbol.
    function safeSymbol(IERC20 token) internal view returns (string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL));
        return success ? returnDataToString(data) : "???";
    }

    /// @notice Provides a safe ERC20.name version which returns '???' as fallback string.
    /// @param token The address of the ERC-20 token contract.
    /// @return (string) Token name.
    function safeName(IERC20 token) internal view returns (string memory) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME));
        return success ? returnDataToString(data) : "???";
    }

    /// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.
    /// @param token The address of the ERC-20 token contract.
    /// @return (uint8) Token decimals.
    function safeDecimals(IERC20 token) internal view returns (uint8) {
        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS));
        return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
    }

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        OZSafeERC20.safeTransfer(token, to, value);
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        OZSafeERC20.safeTransferFrom(token, from, to, value);
    }
}
IResupplyPair.sol 150 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

interface IResupplyPair {
    struct CurrentRateInfo {
        uint64 lastTimestamp;
        uint64 ratePerSec;
        uint256 lastShares;
    }
    struct VaultAccount {
        uint128 amount;
        uint128 shares;
    }
    function addCollateral(uint256 _collateralAmount, address _borrower) external;
    function addCollateralUnderlying(uint256 _collateralAmount, address _borrower) external;
    function addInterest()
        external
        returns (uint256 _interestEarned, uint256 _feesAmount, uint256 _feesShare, uint64 _newRate);
    function asset() external view returns (address);
    function balanceOf(address account) external view returns (uint256);
    function borrow(
        uint256 _borrowAmount,
        uint256 _collateralAmount,
        address _receiver
    ) external returns (uint256 _shares);

    function changeFee(uint32 _newFee) external;
    function mintFee() external view returns (uint256);
    function liquidationFee() external view returns (uint256);
    function protocolRedemptionFee() external view returns (uint256);
    function collateral() external view returns (address);
    function underlying() external view returns (address);
    function currentRateInfo() external view returns (
        uint32 lastBlock,
        uint64 lastTimestamp,
        uint64 ratePerSec,
        uint256 lastPrice,
        uint256 lastShares
    );
    function previewAddInterest() external view returns (
        uint256 _interestEarned,
        CurrentRateInfo memory _newCurrentRateInfo,
        uint256 _claimableFees,
        VaultAccount memory _totalBorrow
    );
    function exchangeRateInfo() external view returns (address oracle, uint32 lastTimestamp, uint224 exchangeRate);
    function getConstants() external pure returns (
        uint256 _LTV_PRECISION,
        uint256 _LIQ_PRECISION,
        uint256 _EXCHANGE_PRECISION,
        uint256 _RATE_PRECISION
    );
    function getPairAccounting() external view returns (
        uint256 _claimableFees,
        uint128 _totalBorrowAmount,
        uint128 _totalBorrowShares,
        uint256 _totalCollateral
    );
    function getUserSnapshot(address _address) external view returns (uint256 _userBorrowShares, uint256 _userCollateralBalance);
    function leveragePosition(
        address _swapperAddress,
        uint256 _borrowAmount,
        uint256 _initialUnderlyingAmount,
        uint256 _amountCollateralOutMin,
        address[] memory _path
    ) external returns (uint256 _totalCollateralBalance);
    function liquidate(address _borrower) external returns (uint256 _collateralForLiquidator);
    function maxLTV() external view returns (uint256);
    function name() external view returns (string memory);
    function owner() external view returns (address);
    function pause() external;
    function paused() external view returns (bool);
    function rateCalculator() external view returns (address);
    function borrowLimit() external view returns (uint256);
    function totalAssetAvailable() external view returns (uint256);
    function minimumLeftoverDebt() external view returns (uint256);
    function minimumBorrowAmount() external view returns (uint256);
    function minimumRedemption() external view returns (uint256);
    function redeemCollateral(address _caller, uint256 _amount, uint256 _fee, address _receiver) external returns(address _collateralToken, uint256 _collateralReturned);
    function removeCollateral(uint256 _collateralAmount, address _receiver) external;
    function renounceOwnership() external;
    function repayAsset(uint256 _shares, address _borrower) external returns (uint256 _amountToRepay);
    function repayAssetWithCollateral(
        address _swapperAddress,
        uint256 _collateralToSwap,
        uint256 _amountAssetOutMin,
        address[] memory _path
    ) external returns (uint256 _amountAssetOut);
    function setBorrowLimit(uint256 _limit) external;
    function setApprovedBorrowers(address[] memory _borrowers, bool _approval) external;
    function setApprovedLenders(address[] memory _lenders, bool _approval) external;
    function setMaxOracleDelay(uint256 _newDelay) external;
    function setSwapper(address _swapper, bool _approval) external;
    function swappers(address) external view returns (bool);
    function symbol() external view returns (string memory);
    function toBorrowAmount(uint256 _shares, bool _roundUp, bool _previewInterest) external view returns (uint256);
    function toBorrowShares(uint256 _amount, bool _roundUp, bool _previewInterest) external view returns (uint256);
    function totalBorrow() external view returns (uint128 amount, uint128 shares);
    function totalCollateral() external view returns (uint256);
    function unpause() external;
    function updateExchangeRate() external returns (uint256 _exchangeRate);
    function userBorrowShares(address) external view returns (uint256);
    function userCollateralBalance(address) external returns (uint256);
    function version() external pure returns (uint256 _major, uint256 _minor, uint256 _patch);
    function withdrawFees() external returns (uint256 _amountToTransfer);
    function convexBooster() external view returns (address convexBooster);
    function convexPid() external view returns (uint256 _convexPid);
    function rewardLength() external view returns (uint256 _length);
    function rewardMap(address _reward) external view returns (uint256 _rewardSlot);
    function addExtraReward(address _token) external;
    function earned(address _account) external returns(EarnedData[] memory claimable);
    function setOracle(address _newOracle) external;
    function setMaxLTV(uint256 _newMaxLTV) external;
    function setRateCalculator(address _newRateCalculator, bool _updateInterest) external;
    function setLiquidationFees(uint256 _newLiquidationFee) external;
    function setMintFees(uint256 _newMintFee) external;
    function setMinimumRedemption(uint256 _min) external;
    function setMinimumLeftoverDebt(uint256 _min) external;
    function setMinimumBorrowAmount(uint256 _min) external;
    function setProtocolRedemptionFee(uint256 _fee) external;
    function setConvexPool(uint256 pid) external;
    function CRV() external pure returns (address);
    function CVX() external pure returns (address);

    struct EarnedData {
        address token;
        uint256 amount;
    }

    // Events
    event SetOracleInfo(address oldOracle, address newOracle);
    event SetMaxLTV(uint256 oldMaxLTV, uint256 newMaxLTV);
    event SetRateCalculator(address oldRateCalculator, address newRateCalculator);
    event SetLiquidationFees(uint256 oldLiquidationFee, uint256 newLiquidationFee);
    event SetMintFees(uint256 oldMintFee, uint256 newMintFee);
    event SetBorrowLimit(uint256 limit);
    event SetMinimumRedemption(uint256 min);
    event SetMinimumLeftover(uint256 min);
    event SetMinimumBorrowAmount(uint256 min);
    event SetProtocolRedemptionFee(uint256 fee);
    event WithdrawFees(address recipient, uint256 interestFees, uint256 otherFees);
    event SetSwapper(address swapper, bool approval);
    event SetConvexPool(uint256 pid);

    // Errors
    error FeesAlreadyDistributed();
    error IncorrectStakeBalance();
    error InvalidParameter();
    error OnlyProtocolOrOwner();
}
IResupplyRegistry.sol 74 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

interface IResupplyRegistry {
    event AddPair(address pairAddress);
    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    event SetDeployer(address deployer, bool _bool);
    event DefaultSwappersSet(address[] addresses);
    event EntryUpdated(string indexed key, address indexed addr);
    event WithdrawTo(address indexed user, uint256 amount);

    // Protected keys
    function LIQUIDATION_HANDLER() external pure returns (string memory);
    function FEE_DEPOSIT() external pure returns (string memory);
    function REDEMPTION_HANDLER() external pure returns (string memory);
    function INSURANCE_POOL() external pure returns (string memory);
    function REWARD_HANDLER() external pure returns (string memory);
    function TREASURY() external pure returns (string memory);
    function STAKER() external pure returns (string memory);
    function L2_MANAGER() external pure returns (string memory);
    function VEST_MANAGER() external pure returns (string memory);

    // Other public functions
    function token() external view returns (address);
    function govToken() external view returns (address);
    function getAddress(string memory key) external view returns (address);
    function getAllKeys() external view returns (string[] memory);
    function getAllAddresses() external view returns (address[] memory);
    function getProtectedKeys() external pure returns (string[] memory);
    function keyExists(string memory) external view returns (bool);
    function hashToKey(bytes32) external view returns (string memory);
    function setAddress(string memory key, address addr) external;
    function acceptOwnership() external;
    function addPair(address _pairAddress) external;
    function registeredPairs(uint256) external view returns (address);
    function pairsByName(string memory) external view returns (address);
    function defaultSwappersLength() external view returns (uint256);
    function registeredPairsLength() external view returns (uint256);
    function getAllPairAddresses() external view returns (address[] memory _deployedPairsArray);
    function getAllDefaultSwappers() external view returns (address[] memory _defaultSwappers);
    function owner() external view returns (address);
    function pendingOwner() external view returns (address);
    function renounceOwnership() external;
    function transferOwnership(address newOwner) external;
    function claimFees(address _pair) external;
    function claimRewards(address _pair) external;
    function claimInsuranceRewards() external;
    function withdrawTo(address _asset, uint256 _amount, address _to) external;
    function mint(address receiver, uint256 amount) external;
    function burn(address target, uint256 amount) external;
    function liquidationHandler() external view returns(address);
    function feeDeposit() external view returns(address);
    function redemptionHandler() external view returns(address);
    function rewardHandler() external view returns(address);
    function insurancePool() external view returns(address);
    function setRewardClaimer(address _newAddress) external;
    function setRedemptionHandler(address _newAddress) external;
    function setFeeDeposit(address _newAddress) external;
    function setLiquidationHandler(address _newAddress) external;
    function setInsurancePool(address _newAddress) external;
    function setStaker(address _newAddress) external;
    function setTreasury(address _newAddress) external;
    function staker() external view returns(address);
    function treasury() external view returns(address);
    function l2manager() external view returns(address);
    function setRewardHandler(address _newAddress) external;
    function setVestManager(address _newAddress) external;
    function setDefaultSwappers(address[] memory _swappers) external;
    function collateralId(address _collateral) external view returns(uint256);

    error NameMustBeUnique();
    error ProtectedKey(string key);
}
IOracle.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

interface IOracle {
    function decimals() external view returns (uint8);

    function getPrices(address _vault) external view returns (uint256 _price);

    function name() external view returns (string memory);
}
IERC4626.sol 48 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);
    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    function asset() external view returns (address);

    function convertToAssets(uint256 shares) external view returns (uint256);

    function convertToShares(uint256 assets) external view returns (uint256);

    function maxDeposit(address) external view returns (uint256);

    function maxMint(address) external view returns (uint256);

    function maxRedeem(address owner) external view returns (uint256);

    function maxWithdraw(address owner) external view returns (uint256);

    function previewDeposit(uint256 assets) external view returns (uint256);

    function previewMint(uint256 shares) external view returns (uint256);

    function previewRedeem(uint256 shares) external view returns (uint256);

    function previewWithdraw(uint256 assets) external view returns (uint256);

    function totalAssets() external view returns (uint256);

    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);

    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
}
IMintable.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

interface IMintable{
    function mint(address _to, uint256 _amount) external;
    function burn(address _from, uint256 _amount) external;
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
ICore.sol 31 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { IAuthHook } from './IAuthHook.sol';

interface ICore {
    struct OperatorAuth {
        bool authorized;
        IAuthHook hook;
    }

    event VoterSet(address indexed newVoter);
    event OperatorExecuted(address indexed caller, address indexed target, bytes data);
    event OperatorSet(address indexed caller, address indexed target, bool authorized, bytes4 selector, IAuthHook authHook);

    function execute(address target, bytes calldata data) external returns (bytes memory);
    function epochLength() external view returns (uint256);
    function startTime() external view returns (uint256);
    function voter() external view returns (address);
    function ownershipTransferDeadline() external view returns (uint256);
    function pendingOwner() external view returns (address);
    function setOperatorPermissions(
        address caller,
        address target,
        bytes4 selector,
        bool authorized,
        IAuthHook authHook
    ) external;
    function setVoter(address newVoter) external;
    function operatorPermissions(address caller, address target, bytes4 selector) external view returns (bool authorized, IAuthHook hook);
}
IERC20.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
SafeERC20.sol 199 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}
IAuthHook.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

interface IAuthHook {
    function preHook(address operator, address target, bytes calldata data) external returns (bool);
    function postHook(bytes memory result, address operator, address target, bytes calldata data) external returns (bool);
}
IERC1363.sol 86 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
Address.sol 150 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)

pragma solidity ^0.8.20;

import {Errors} from "./Errors.sol";

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert Errors.FailedCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {Errors.FailedCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}
IERC165.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";
Errors.sol 34 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Read Contract

PRECISION 0xaaf5eb68 → uint256
baseRedemptionFee 0x96caeb85 → uint256
core 0xf2f4eb26 → address
debtToken 0xf8d89898 → address
getMaxRedeemableDebt 0x43bad45b → uint256
getRedemptionFeePct 0x8918a2d6 → uint256
maxDiscount 0xb8b89e1b → uint256
maxUsage 0xabe7c2e7 → uint256
overusageMax 0xca1a66d5 → uint256
overusageRate 0x6ba2eeb8 → uint256
overusageStart 0xc5650cc5 → uint256
owner 0x8da5cb5b → address
previewRedeem 0xcbe52ae3 → uint256, uint256, uint256
ratingData 0xb58a3741 → uint64, uint192
registry 0x7b103999 → address
totalWeight 0x96c82e57 → uint256
underlyingOracle 0x12c46e61 → address
usageDecayRate 0x1482f7a3 → uint256

Write Contract 5 functions

These functions modify contract state and require a wallet transaction to execute.

redeemFromPair 0x3927a5e7
address _pair
uint256 _amount
uint256 _maxFeePct
address _receiver
bool _redeemToUnderlying
returns: uint256
setBaseRedemptionFee 0x2248aebc
uint256 _fee
setDiscountInfo 0xb5427eb9
uint256 _rate
uint256 _maxUsage
uint256 _maxDiscount
setOverusageInfo 0x04bae72a
uint256 _rate
uint256 _start
uint256 _end
setUnderlyingOracle 0xa8e303bd
address _oracle

Recent Transactions

No transactions found for this address