Address Contract Partially Verified
Address
0x9999999b8ce70322b021EFe340759B7958af43C8
Balance
0 ETH
Nonce
1
Code Size
5868 bytes
Creator
0xC36cf88c...F043 at tx 0xd0342f22...973c09
Indexed Transactions
0
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