编译器
0.6.12+commit.27d51765
文件 1 的 22:Address.sol
pragma solidity >=0.6.2 <0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 22:AdvancedMath.sol
pragma solidity >=0.6.10 <0.8.0;
library AdvancedMath {
function sqrt(uint256 s) internal pure returns (uint256) {
if (s == 0) return 0;
uint256 t = s;
uint256 x0 = 2;
if (t >= 1 << 128) {
t >>= 128;
x0 <<= 64;
}
if (t >= 1 << 64) {
t >>= 64;
x0 <<= 32;
}
if (t >= 1 << 32) {
t >>= 32;
x0 <<= 16;
}
if (t >= 1 << 16) {
t >>= 16;
x0 <<= 8;
}
if (t >= 1 << 8) {
t >>= 8;
x0 <<= 4;
}
if (t >= 1 << 4) {
t >>= 4;
x0 <<= 2;
}
if (t >= 1 << 2) {
x0 <<= 1;
}
uint256 x1 = (x0 + s / x0) >> 1;
while (x1 < x0) {
x0 = x1;
x1 = (x0 + s / x0) >> 1;
}
return x0;
}
function cbrt(uint256 s) internal pure returns (uint256) {
if (s == 0) return 0;
uint256 t = s;
uint256 x0 = 2;
if (t >= 1 << 192) {
t >>= 192;
x0 <<= 64;
}
if (t >= 1 << 96) {
t >>= 96;
x0 <<= 32;
}
if (t >= 1 << 48) {
t >>= 48;
x0 <<= 16;
}
if (t >= 1 << 24) {
t >>= 24;
x0 <<= 8;
}
if (t >= 1 << 12) {
t >>= 12;
x0 <<= 4;
}
if (t >= 1 << 6) {
t >>= 6;
x0 <<= 2;
}
if (t >= 1 << 3) {
x0 <<= 1;
}
uint256 x1 = (2 * x0 + s / x0 / x0) / 3;
while (x1 < x0) {
x0 = x1;
x1 = (2 * x0 + s / x0 / x0) / 3;
}
return x0;
}
}
文件 3 的 22:Context.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 4 的 22:IERC20.sol
pragma solidity >=0.6.0 <0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 5 的 22:IFundV3.sol
pragma solidity >=0.6.10 <0.8.0;
pragma experimental ABIEncoderV2;
import "./ITwapOracleV2.sol";
interface IFundV3 {
struct Rebalance {
uint256 ratioB2Q;
uint256 ratioR2Q;
uint256 ratioBR;
uint256 timestamp;
}
function tokenUnderlying() external view returns (address);
function tokenQ() external view returns (address);
function tokenB() external view returns (address);
function tokenR() external view returns (address);
function tokenShare(uint256 tranche) external view returns (address);
function primaryMarket() external view returns (address);
function primaryMarketUpdateProposal() external view returns (address, uint256);
function strategy() external view returns (address);
function strategyUpdateProposal() external view returns (address, uint256);
function underlyingDecimalMultiplier() external view returns (uint256);
function twapOracle() external view returns (ITwapOracleV2);
function feeCollector() external view returns (address);
function endOfDay(uint256 timestamp) external pure returns (uint256);
function trancheTotalSupply(uint256 tranche) external view returns (uint256);
function trancheBalanceOf(uint256 tranche, address account) external view returns (uint256);
function trancheAllBalanceOf(address account) external view returns (uint256, uint256, uint256);
function trancheBalanceVersion(address account) external view returns (uint256);
function trancheAllowance(
uint256 tranche,
address owner,
address spender
) external view returns (uint256);
function trancheAllowanceVersion(
address owner,
address spender
) external view returns (uint256);
function trancheTransfer(
uint256 tranche,
address recipient,
uint256 amount,
uint256 version
) external;
function trancheTransferFrom(
uint256 tranche,
address sender,
address recipient,
uint256 amount,
uint256 version
) external;
function trancheApprove(
uint256 tranche,
address spender,
uint256 amount,
uint256 version
) external;
function getRebalanceSize() external view returns (uint256);
function getRebalance(uint256 index) external view returns (Rebalance memory);
function getRebalanceTimestamp(uint256 index) external view returns (uint256);
function currentDay() external view returns (uint256);
function splitRatio() external view returns (uint256);
function historicalSplitRatio(uint256 version) external view returns (uint256);
function fundActivityStartTime() external view returns (uint256);
function isFundActive(uint256 timestamp) external view returns (bool);
function getEquivalentTotalB() external view returns (uint256);
function getEquivalentTotalQ() external view returns (uint256);
function historicalEquivalentTotalB(uint256 timestamp) external view returns (uint256);
function historicalNavs(uint256 timestamp) external view returns (uint256 navB, uint256 navR);
function extrapolateNav(uint256 price) external view returns (uint256, uint256, uint256);
function doRebalance(
uint256 amountQ,
uint256 amountB,
uint256 amountR,
uint256 index
) external view returns (uint256 newAmountQ, uint256 newAmountB, uint256 newAmountR);
function batchRebalance(
uint256 amountQ,
uint256 amountB,
uint256 amountR,
uint256 fromIndex,
uint256 toIndex
) external view returns (uint256 newAmountQ, uint256 newAmountB, uint256 newAmountR);
function refreshBalance(address account, uint256 targetVersion) external;
function refreshAllowance(address owner, address spender, uint256 targetVersion) external;
function shareTransfer(address sender, address recipient, uint256 amount) external;
function shareTransferFrom(
address spender,
address sender,
address recipient,
uint256 amount
) external returns (uint256 newAllowance);
function shareIncreaseAllowance(
address sender,
address spender,
uint256 addedValue
) external returns (uint256 newAllowance);
function shareDecreaseAllowance(
address sender,
address spender,
uint256 subtractedValue
) external returns (uint256 newAllowance);
function shareApprove(address owner, address spender, uint256 amount) external;
function historicalUnderlying(uint256 timestamp) external view returns (uint256);
function getTotalUnderlying() external view returns (uint256);
function getStrategyUnderlying() external view returns (uint256);
function getTotalDebt() external view returns (uint256);
event RebalanceTriggered(
uint256 indexed index,
uint256 indexed day,
uint256 navSum,
uint256 navB,
uint256 navROrZero,
uint256 ratioB2Q,
uint256 ratioR2Q,
uint256 ratioBR
);
event Settled(uint256 indexed day, uint256 navB, uint256 navR, uint256 interestRate);
event InterestRateUpdated(uint256 baseInterestRate, uint256 floatingInterestRate);
event BalancesRebalanced(
address indexed account,
uint256 version,
uint256 balanceQ,
uint256 balanceB,
uint256 balanceR
);
event AllowancesRebalanced(
address indexed owner,
address indexed spender,
uint256 version,
uint256 allowanceQ,
uint256 allowanceB,
uint256 allowanceR
);
}
文件 6 的 22:ILiquidityGauge.sol
pragma solidity >=0.6.10 <0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface ILiquidityGauge is IERC20 {
function mint(address account, uint256 amount) external;
function burnFrom(address account, uint256 amount) external;
function workingSupply() external view returns (uint256);
function workingBalanceOf(address account) external view returns (uint256);
function claimableRewards(
address account
)
external
returns (
uint256 chessAmount,
uint256 bonusAmount,
uint256 amountQ,
uint256 amountB,
uint256 amountR,
uint256 quoteAmount
);
function claimRewards(address account) external;
function distribute(
uint256 amountQ,
uint256 amountB,
uint256 amountR,
uint256 quoteAmount,
uint256 version
) external;
}
文件 7 的 22:IStableSwap.sol
pragma solidity >=0.6.10 <0.8.0;
import "../interfaces/IFundV3.sol";
interface IStableSwapCore {
function getQuoteOut(uint256 baseIn) external view returns (uint256 quoteOut);
function getQuoteIn(uint256 baseOut) external view returns (uint256 quoteIn);
function getBaseOut(uint256 quoteIn) external view returns (uint256 baseOut);
function getBaseIn(uint256 quoteOut) external view returns (uint256 baseIn);
function buy(
uint256 version,
uint256 baseOut,
address recipient,
bytes calldata data
) external returns (uint256 realBaseOut);
function sell(
uint256 version,
uint256 quoteOut,
address recipient,
bytes calldata data
) external returns (uint256 realQuoteOut);
}
interface IStableSwap is IStableSwapCore {
function fund() external view returns (IFundV3);
function baseTranche() external view returns (uint256);
function baseAddress() external view returns (address);
function quoteAddress() external view returns (address);
function allBalances() external view returns (uint256, uint256);
function getOraclePrice() external view returns (uint256);
function getCurrentD() external view returns (uint256);
function getCurrentPriceOverOracle() external view returns (uint256);
function getCurrentPrice() external view returns (uint256);
function getPriceOverOracleIntegral() external view returns (uint256);
function addLiquidity(uint256 version, address recipient) external returns (uint256);
function removeLiquidity(
uint256 version,
uint256 lpIn,
uint256 minBaseOut,
uint256 minQuoteOut
) external returns (uint256 baseOut, uint256 quoteOut);
function removeLiquidityUnwrap(
uint256 version,
uint256 lpIn,
uint256 minBaseOut,
uint256 minQuoteOut
) external returns (uint256 baseOut, uint256 quoteOut);
function removeBaseLiquidity(
uint256 version,
uint256 lpIn,
uint256 minBaseOut
) external returns (uint256 baseOut);
function removeQuoteLiquidity(
uint256 version,
uint256 lpIn,
uint256 minQuoteOut
) external returns (uint256 quoteOut);
function removeQuoteLiquidityUnwrap(
uint256 version,
uint256 lpIn,
uint256 minQuoteOut
) external returns (uint256 quoteOut);
}
interface IStableSwapCoreInternalRevertExpected {
function getQuoteOut(uint256 baseIn) external returns (uint256 quoteOut);
function getQuoteIn(uint256 baseOut) external returns (uint256 quoteIn);
function getBaseOut(uint256 quoteIn) external returns (uint256 baseOut);
function getBaseIn(uint256 quoteOut) external returns (uint256 baseIn);
function buy(
uint256 version,
uint256 baseOut,
address recipient,
bytes calldata data
) external returns (uint256 realBaseOut);
function sell(
uint256 version,
uint256 quoteOut,
address recipient,
bytes calldata data
) external returns (uint256 realQuoteOut);
}
文件 8 的 22:ITrancheIndexV2.sol
pragma solidity >=0.6.10 <0.8.0;
abstract contract ITrancheIndexV2 {
uint256 internal constant TRANCHE_Q = 0;
uint256 internal constant TRANCHE_B = 1;
uint256 internal constant TRANCHE_R = 2;
uint256 internal constant TRANCHE_COUNT = 3;
}
文件 9 的 22:ITranchessSwapCallee.sol
pragma solidity >=0.6.10 <0.8.0;
interface ITranchessSwapCallee {
function tranchessSwapCallback(
uint256 baseDeltaOut,
uint256 quoteDeltaOut,
bytes calldata data
) external;
}
文件 10 的 22:ITwapOracle.sol
pragma solidity >=0.6.10 <0.8.0;
interface ITwapOracle {
enum UpdateType {
PRIMARY,
SECONDARY,
OWNER,
CHAINLINK,
UNISWAP_V2
}
function getTwap(uint256 timestamp) external view returns (uint256);
}
文件 11 的 22:ITwapOracleV2.sol
pragma solidity >=0.6.10 <0.8.0;
import "./ITwapOracle.sol";
interface ITwapOracleV2 is ITwapOracle {
function getLatest() external view returns (uint256);
}
文件 12 的 22:IWrappedERC20.sol
pragma solidity >=0.6.10 <0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWrappedERC20 is IERC20 {
function deposit() external payable;
function withdraw(uint256 wad) external;
}
文件 13 的 22:IWstETH.sol
pragma solidity >=0.6.10 <0.8.0;
interface IWstETH {
function stETH() external view returns (address);
function stEthPerToken() external view returns (uint256);
function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256);
function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256);
function wrap(uint256 _stETHAmount) external returns (uint256);
function unwrap(uint256 _wstETHAmount) external returns (uint256);
}
文件 14 的 22:ManagedPausable.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract ManagedPausable {
event Paused(address account);
event Unpaused(address account);
event PauserRoleTransferred(address indexed previousPauser, address indexed newPauser);
uint256 private constant FALSE = 0;
uint256 private constant TRUE = 1;
uint256 private _initialized;
uint256 private _paused;
address private _pauser;
function _initializeManagedPausable(address pauser_) internal {
require(_initialized == FALSE);
_initialized = TRUE;
_paused = FALSE;
_pauser = pauser_;
}
function paused() public view returns (bool) {
return _paused != FALSE;
}
function pauser() public view returns (address) {
return _pauser;
}
function renouncePauserRole() external onlyPauser {
emit PauserRoleTransferred(_pauser, address(0));
_pauser = address(0);
}
function transferPauserRole(address newPauser) external onlyPauser {
require(newPauser != address(0));
emit PauserRoleTransferred(_pauser, newPauser);
_pauser = newPauser;
}
modifier onlyPauser() {
require(_pauser == msg.sender, "Pausable: only pauser");
_;
}
modifier whenNotPaused() {
require(_paused == FALSE, "Pausable: paused");
_;
}
modifier whenPaused() {
require(_paused != FALSE, "Pausable: not paused");
_;
}
function pause() external onlyPauser whenNotPaused {
_paused = TRUE;
emit Paused(msg.sender);
}
function unpause() external onlyPauser whenPaused {
_paused = FALSE;
emit Unpaused(msg.sender);
}
}
文件 15 的 22:Ownable.sol
pragma solidity >=0.6.0 <0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
文件 16 的 22:ReentrancyGuard.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 17 的 22:SafeDecimalMath.sol
pragma solidity >=0.6.10 <0.8.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
library SafeDecimalMath {
using SafeMath for uint256;
uint256 private constant decimals = 18;
uint256 private constant highPrecisionDecimals = 27;
uint256 private constant UNIT = 10 ** uint256(decimals);
uint256 private constant PRECISE_UNIT = 10 ** uint256(highPrecisionDecimals);
uint256 private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR =
10 ** uint256(highPrecisionDecimals - decimals);
function multiplyDecimal(uint256 x, uint256 y) internal pure returns (uint256) {
return x.mul(y).div(UNIT);
}
function multiplyDecimalPrecise(uint256 x, uint256 y) internal pure returns (uint256) {
return x.mul(y).div(PRECISE_UNIT);
}
function divideDecimal(uint256 x, uint256 y) internal pure returns (uint256) {
return x.mul(UNIT).div(y);
}
function divideDecimalPrecise(uint256 x, uint256 y) internal pure returns (uint256) {
return x.mul(PRECISE_UNIT).div(y);
}
function decimalToPreciseDecimal(uint256 i) internal pure returns (uint256) {
return i.mul(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR);
}
function preciseDecimalToDecimal(uint256 i) internal pure returns (uint256) {
uint256 quotientTimesTen = i.mul(10).div(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR);
if (quotientTimesTen % 10 >= 5) {
quotientTimesTen = quotientTimesTen.add(10);
}
return quotientTimesTen.div(10);
}
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
return c / a != b ? type(uint256).max : c;
}
function saturatingMultiplyDecimal(uint256 x, uint256 y) internal pure returns (uint256) {
return saturatingMul(x, y).div(UNIT);
}
}
文件 18 的 22:SafeERC20.sol
pragma solidity >=0.6.0 <0.8.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 19 的 22:SafeMath.sol
pragma solidity >=0.6.0 <0.8.0;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
文件 20 的 22:StableSwapV3.sol
pragma solidity >=0.6.10 <0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../interfaces/IStableSwap.sol";
import "../interfaces/ILiquidityGauge.sol";
import "../interfaces/ITranchessSwapCallee.sol";
import "../interfaces/IWrappedERC20.sol";
import "../utils/SafeDecimalMath.sol";
import "../utils/AdvancedMath.sol";
import "../utils/ManagedPausable.sol";
abstract contract StableSwapV3 is IStableSwap, Ownable, ReentrancyGuard, ManagedPausable {
using SafeMath for uint256;
using SafeDecimalMath for uint256;
using SafeERC20 for IERC20;
event LiquidityAdded(
address indexed sender,
address indexed recipient,
uint256 baseIn,
uint256 quoteIn,
uint256 lpOut,
uint256 fee,
uint256 adminFee,
uint256 oraclePrice
);
event LiquidityRemoved(
address indexed account,
uint256 lpIn,
uint256 baseOut,
uint256 quotOut,
uint256 fee,
uint256 adminFee,
uint256 oraclePrice
);
event Swap(
address indexed sender,
address indexed recipient,
uint256 baseIn,
uint256 quoteIn,
uint256 baseOut,
uint256 quoteOut,
uint256 fee,
uint256 adminFee,
uint256 oraclePrice
);
event Sync(uint256 base, uint256 quote, uint256 oraclePrice);
event AmplRampUpdated(uint256 start, uint256 end, uint256 startTimestamp, uint256 endTimestamp);
event FeeCollectorUpdated(address newFeeCollector);
event FeeRateUpdated(uint256 newFeeRate);
event AdminFeeRateUpdated(uint256 newAdminFeeRate);
struct RebalanceResult {
uint256 base;
uint256 quote;
uint256 rebalanceTimestamp;
}
uint256 private constant AMPL_MAX_VALUE = 1e6;
uint256 private constant AMPL_RAMP_MIN_TIME = 86400;
uint256 private constant AMPL_RAMP_MAX_CHANGE = 10;
uint256 private constant MAX_FEE_RATE = 0.9e18;
uint256 private constant MAX_ADMIN_FEE_RATE = 1e18;
uint256 private constant MAX_ITERATION = 255;
uint256 private constant MINIMUM_LIQUIDITY = 1e3;
address public immutable lpToken;
IFundV3 public immutable override fund;
uint256 public immutable override baseTranche;
address public immutable override quoteAddress;
uint256 internal immutable _quoteDecimalMultiplier;
uint256 public baseBalance;
uint256 public quoteBalance;
uint256 private _priceOverOracleIntegral;
uint256 private _priceOverOracleTimestamp;
uint256 public amplRampStart;
uint256 public amplRampEnd;
uint256 public amplRampStartTimestamp;
uint256 public amplRampEndTimestamp;
address public feeCollector;
uint256 public normalFeeRate;
uint256 public adminFeeRate;
uint256 public totalAdminFee;
constructor(
address lpToken_,
address fund_,
uint256 baseTranche_,
address quoteAddress_,
uint256 quoteDecimals_,
uint256 ampl_,
address feeCollector_,
uint256 feeRate_,
uint256 adminFeeRate_
) public {
lpToken = lpToken_;
fund = IFundV3(fund_);
baseTranche = baseTranche_;
quoteAddress = quoteAddress_;
require(quoteDecimals_ <= 18, "Quote asset decimals larger than 18");
_quoteDecimalMultiplier = 10 ** (18 - quoteDecimals_);
require(ampl_ > 0 && ampl_ < AMPL_MAX_VALUE, "Invalid A");
amplRampEnd = ampl_;
emit AmplRampUpdated(ampl_, ampl_, 0, 0);
_updateFeeCollector(feeCollector_);
_updateFeeRate(feeRate_);
_updateAdminFeeRate(adminFeeRate_);
_initializeManagedPausable(msg.sender);
}
receive() external payable {}
function baseAddress() external view override returns (address) {
return fund.tokenShare(baseTranche);
}
function allBalances() external view override returns (uint256, uint256) {
(RebalanceResult memory result, , , , ) = _getRebalanceResult(fund.getRebalanceSize());
return (result.base, result.quote);
}
function getAmpl() public view returns (uint256) {
uint256 endTimestamp = amplRampEndTimestamp;
if (block.timestamp < endTimestamp) {
uint256 startTimestamp = amplRampStartTimestamp;
uint256 start = amplRampStart;
uint256 end = amplRampEnd;
if (end > start) {
return
start +
((end - start) * (block.timestamp - startTimestamp)) /
(endTimestamp - startTimestamp);
} else {
return
start -
((start - end) * (block.timestamp - startTimestamp)) /
(endTimestamp - startTimestamp);
}
} else {
return amplRampEnd;
}
}
function feeRate() external view returns (uint256) {
uint256 version = fund.getRebalanceSize();
uint256 rebalanceTimestamp = version == 0 ? 0 : fund.getRebalanceTimestamp(version - 1);
return _getFeeRate(rebalanceTimestamp);
}
function _getFeeRate(uint256 rebalanceTimestamp) private view returns (uint256) {
if (rebalanceTimestamp <= block.timestamp - 1 days) {
return normalFeeRate;
}
uint256 coolingOff = rebalanceTimestamp > block.timestamp
? 1 days
: rebalanceTimestamp - (block.timestamp - 1 days);
uint256 coolingFeeRate = (MAX_FEE_RATE * coolingOff) / 1 days;
return normalFeeRate < coolingFeeRate ? coolingFeeRate : normalFeeRate;
}
function getCurrentD() external view override returns (uint256) {
(RebalanceResult memory result, , , , ) = _getRebalanceResult(fund.getRebalanceSize());
return _getD(result.base, result.quote, getAmpl(), getOraclePrice());
}
function getCurrentPriceOverOracle() public view override returns (uint256) {
(RebalanceResult memory result, , , , ) = _getRebalanceResult(fund.getRebalanceSize());
if (result.base == 0 || result.quote == 0) {
return 1e18;
}
uint256 ampl = getAmpl();
uint256 oraclePrice = getOraclePrice();
uint256 d = _getD(result.base, result.quote, ampl, oraclePrice);
return _getPriceOverOracle(result.base, result.quote, ampl, oraclePrice, d);
}
function getCurrentPrice() external view override returns (uint256) {
(RebalanceResult memory result, , , , ) = _getRebalanceResult(fund.getRebalanceSize());
uint256 oraclePrice = getOraclePrice();
if (result.base == 0 || result.quote == 0) {
return oraclePrice;
}
uint256 ampl = getAmpl();
uint256 d = _getD(result.base, result.quote, ampl, oraclePrice);
return
_getPriceOverOracle(result.base, result.quote, ampl, oraclePrice, d).multiplyDecimal(
oraclePrice
);
}
function getPriceOverOracleIntegral() external view override returns (uint256) {
return
_priceOverOracleIntegral +
getCurrentPriceOverOracle() *
(block.timestamp - _priceOverOracleTimestamp);
}
function getQuoteOut(uint256 baseIn) external view override returns (uint256 quoteOut) {
(RebalanceResult memory old, , , , ) = _getRebalanceResult(fund.getRebalanceSize());
uint256 newBase = old.base.add(baseIn);
uint256 ampl = getAmpl();
uint256 oraclePrice = getOraclePrice();
uint256 d = _getD(old.base, old.quote, ampl, oraclePrice) + 1;
uint256 newQuote = _getQuote(ampl, newBase, oraclePrice, d) + 1;
quoteOut = old.quote.sub(newQuote);
quoteOut = quoteOut.multiplyDecimal(1e18 - _getFeeRate(old.rebalanceTimestamp));
}
function getQuoteIn(uint256 baseOut) external view override returns (uint256 quoteIn) {
(RebalanceResult memory old, , , , ) = _getRebalanceResult(fund.getRebalanceSize());
uint256 newBase = old.base.sub(baseOut);
uint256 ampl = getAmpl();
uint256 oraclePrice = getOraclePrice();
uint256 d = _getD(old.base, old.quote, ampl, oraclePrice) + 1;
uint256 newQuote = _getQuote(ampl, newBase, oraclePrice, d) + 1;
quoteIn = newQuote.sub(old.quote);
uint256 feeRate_ = _getFeeRate(old.rebalanceTimestamp);
quoteIn = quoteIn.mul(1e18).add(1e18 - feeRate_ - 1) / (1e18 - feeRate_);
}
function getBaseOut(uint256 quoteIn) external view override returns (uint256 baseOut) {
(RebalanceResult memory old, , , , ) = _getRebalanceResult(fund.getRebalanceSize());
uint256 quoteInAfterFee = quoteIn.multiplyDecimal(
1e18 - _getFeeRate(old.rebalanceTimestamp)
);
uint256 newQuote = old.quote.add(quoteInAfterFee);
uint256 ampl = getAmpl();
uint256 oraclePrice = getOraclePrice();
uint256 d = _getD(old.base, old.quote, ampl, oraclePrice) + 1;
uint256 newBase = _getBase(ampl, newQuote, oraclePrice, d) + 1;
baseOut = old.base.sub(newBase);
}
function getBaseIn(uint256 quoteOut) external view override returns (uint256 baseIn) {
(RebalanceResult memory old, , , , ) = _getRebalanceResult(fund.getRebalanceSize());
uint256 feeRate_ = _getFeeRate(old.rebalanceTimestamp);
uint256 quoteOutBeforeFee = quoteOut.mul(1e18).add(1e18 - feeRate_ - 1) / (1e18 - feeRate_);
uint256 newQuote = old.quote.sub(quoteOutBeforeFee);
uint256 ampl = getAmpl();
uint256 oraclePrice = getOraclePrice();
uint256 d = _getD(old.base, old.quote, ampl, oraclePrice) + 1;
uint256 newBase = _getBase(ampl, newQuote, oraclePrice, d) + 1;
baseIn = newBase.sub(old.base);
}
function buy(
uint256 version,
uint256 baseOut,
address recipient,
bytes calldata data
)
external
override
nonReentrant
checkVersion(version)
whenNotPaused
returns (uint256 realBaseOut)
{
require(baseOut > 0, "Zero output");
realBaseOut = baseOut;
RebalanceResult memory old = _handleRebalance(version);
require(baseOut < old.base, "Insufficient liquidity");
fund.trancheTransfer(baseTranche, recipient, baseOut, version);
if (data.length > 0) {
ITranchessSwapCallee(msg.sender).tranchessSwapCallback(baseOut, 0, data);
_checkVersion(version);
}
uint256 newQuote = _getNewQuoteBalance();
uint256 quoteIn = newQuote.sub(old.quote);
uint256 fee = quoteIn.multiplyDecimal(_getFeeRate(old.rebalanceTimestamp));
uint256 oraclePrice = getOraclePrice();
{
uint256 ampl = getAmpl();
uint256 oldD = _getD(old.base, old.quote, ampl, oraclePrice);
_updatePriceOverOracleIntegral(old.base, old.quote, ampl, oraclePrice, oldD);
uint256 newD = _getD(old.base - baseOut, newQuote.sub(fee), ampl, oraclePrice);
require(newD >= oldD, "Invariant mismatch");
}
uint256 adminFee = fee.multiplyDecimal(adminFeeRate);
baseBalance = old.base - baseOut;
quoteBalance = newQuote.sub(adminFee);
totalAdminFee = totalAdminFee.add(adminFee);
uint256 baseOut_ = baseOut;
emit Swap(msg.sender, recipient, 0, quoteIn, baseOut_, 0, fee, adminFee, oraclePrice);
}
function sell(
uint256 version,
uint256 quoteOut,
address recipient,
bytes calldata data
)
external
override
nonReentrant
checkVersion(version)
whenNotPaused
returns (uint256 realQuoteOut)
{
require(quoteOut > 0, "Zero output");
realQuoteOut = quoteOut;
RebalanceResult memory old = _handleRebalance(version);
IERC20(quoteAddress).safeTransfer(recipient, quoteOut);
if (data.length > 0) {
ITranchessSwapCallee(msg.sender).tranchessSwapCallback(0, quoteOut, data);
_checkVersion(version);
}
uint256 newBase = fund.trancheBalanceOf(baseTranche, address(this));
uint256 baseIn = newBase.sub(old.base);
uint256 fee;
{
uint256 feeRate_ = _getFeeRate(old.rebalanceTimestamp);
fee = quoteOut.mul(feeRate_).div(1e18 - feeRate_);
}
require(quoteOut.add(fee) < old.quote, "Insufficient liquidity");
uint256 oraclePrice = getOraclePrice();
{
uint256 newQuote = old.quote - quoteOut;
uint256 ampl = getAmpl();
uint256 oldD = _getD(old.base, old.quote, ampl, oraclePrice);
_updatePriceOverOracleIntegral(old.base, old.quote, ampl, oraclePrice, oldD);
uint256 newD = _getD(newBase, newQuote - fee, ampl, oraclePrice);
require(newD >= oldD, "Invariant mismatch");
}
uint256 adminFee = fee.multiplyDecimal(adminFeeRate);
baseBalance = newBase;
quoteBalance = old.quote - quoteOut - adminFee;
totalAdminFee = totalAdminFee.add(adminFee);
uint256 quoteOut_ = quoteOut;
emit Swap(msg.sender, recipient, baseIn, 0, 0, quoteOut_, fee, adminFee, oraclePrice);
}
function addLiquidity(
uint256 version,
address recipient
) external override nonReentrant checkVersion(version) whenNotPaused returns (uint256 lpOut) {
RebalanceResult memory old = _handleRebalance(version);
uint256 newBase = fund.trancheBalanceOf(baseTranche, address(this));
uint256 newQuote = _getNewQuoteBalance();
uint256 ampl = getAmpl();
uint256 oraclePrice = getOraclePrice();
uint256 lpSupply = IERC20(lpToken).totalSupply();
if (lpSupply == 0) {
require(newBase > 0 && newQuote > 0, "Zero initial balance");
baseBalance = newBase;
quoteBalance = newQuote;
_priceOverOracleIntegral += 1e18 * (block.timestamp - _priceOverOracleTimestamp);
_priceOverOracleTimestamp = block.timestamp;
uint256 d1 = _getD(newBase, newQuote, ampl, oraclePrice);
ILiquidityGauge(lpToken).mint(address(this), MINIMUM_LIQUIDITY);
ILiquidityGauge(lpToken).mint(recipient, d1.sub(MINIMUM_LIQUIDITY));
emit LiquidityAdded(msg.sender, recipient, newBase, newQuote, d1, 0, 0, oraclePrice);
return d1;
}
uint256 fee;
uint256 adminFee;
{
uint256 d0 = _getD(old.base, old.quote, ampl, oraclePrice);
_updatePriceOverOracleIntegral(old.base, old.quote, ampl, oraclePrice, d0);
{
uint256 d1 = _getD(newBase, newQuote, ampl, oraclePrice);
uint256 idealQuote = d1.mul(old.quote) / d0;
uint256 difference = idealQuote > newQuote
? idealQuote - newQuote
: newQuote - idealQuote;
fee = difference.multiplyDecimal(_getFeeRate(old.rebalanceTimestamp));
}
adminFee = fee.multiplyDecimal(adminFeeRate);
totalAdminFee = totalAdminFee.add(adminFee);
baseBalance = newBase;
quoteBalance = newQuote.sub(adminFee);
uint256 d2 = _getD(newBase, newQuote.sub(fee), ampl, oraclePrice);
require(d2 > d0, "No liquidity is added");
lpOut = lpSupply.mul(d2.sub(d0)).div(d0);
}
ILiquidityGauge(lpToken).mint(recipient, lpOut);
emit LiquidityAdded(
msg.sender,
recipient,
newBase - old.base,
newQuote - old.quote,
lpOut,
fee,
adminFee,
oraclePrice
);
}
function removeLiquidity(
uint256 version,
uint256 lpIn,
uint256 minBaseOut,
uint256 minQuoteOut
)
external
override
nonReentrant
checkVersion(version)
returns (uint256 baseOut, uint256 quoteOut)
{
(baseOut, quoteOut) = _removeLiquidity(version, lpIn, minBaseOut, minQuoteOut);
IERC20(quoteAddress).safeTransfer(msg.sender, quoteOut);
}
function removeLiquidityUnwrap(
uint256 version,
uint256 lpIn,
uint256 minBaseOut,
uint256 minQuoteOut
)
external
override
nonReentrant
checkVersion(version)
returns (uint256 baseOut, uint256 quoteOut)
{
(baseOut, quoteOut) = _removeLiquidity(version, lpIn, minBaseOut, minQuoteOut);
IWrappedERC20(quoteAddress).withdraw(quoteOut);
(bool success, ) = msg.sender.call{value: quoteOut}("");
require(success, "Transfer failed");
}
function _removeLiquidity(
uint256 version,
uint256 lpIn,
uint256 minBaseOut,
uint256 minQuoteOut
) internal returns (uint256 baseOut, uint256 quoteOut) {
uint256 lpSupply = IERC20(lpToken).totalSupply();
RebalanceResult memory old = _handleRebalance(version);
baseOut = old.base.mul(lpIn).div(lpSupply);
quoteOut = old.quote.mul(lpIn).div(lpSupply);
require(baseOut >= minBaseOut, "Insufficient output");
require(quoteOut >= minQuoteOut, "Insufficient output");
baseBalance = old.base.sub(baseOut);
quoteBalance = old.quote.sub(quoteOut);
ILiquidityGauge(lpToken).burnFrom(msg.sender, lpIn);
fund.trancheTransfer(baseTranche, msg.sender, baseOut, version);
emit LiquidityRemoved(msg.sender, lpIn, baseOut, quoteOut, 0, 0, 0);
}
function removeBaseLiquidity(
uint256 version,
uint256 lpIn,
uint256 minBaseOut
) external override nonReentrant checkVersion(version) whenNotPaused returns (uint256 baseOut) {
RebalanceResult memory old = _handleRebalance(version);
uint256 lpSupply = IERC20(lpToken).totalSupply();
uint256 ampl = getAmpl();
uint256 oraclePrice = getOraclePrice();
uint256 d1;
{
uint256 d0 = _getD(old.base, old.quote, ampl, oraclePrice);
_updatePriceOverOracleIntegral(old.base, old.quote, ampl, oraclePrice, d0);
d1 = d0.sub(d0.mul(lpIn).div(lpSupply));
}
{
uint256 fee = old.quote.mul(lpIn).div(lpSupply).multiplyDecimal(
_getFeeRate(old.rebalanceTimestamp)
);
uint256 newBase = _getBase(ampl, old.quote.sub(fee), oraclePrice, d1) + 1;
baseOut = old.base.sub(newBase);
require(baseOut >= minBaseOut, "Insufficient output");
ILiquidityGauge(lpToken).burnFrom(msg.sender, lpIn);
baseBalance = newBase;
uint256 adminFee = fee.multiplyDecimal(adminFeeRate);
totalAdminFee = totalAdminFee.add(adminFee);
quoteBalance = old.quote.sub(adminFee);
emit LiquidityRemoved(msg.sender, lpIn, baseOut, 0, fee, adminFee, oraclePrice);
}
fund.trancheTransfer(baseTranche, msg.sender, baseOut, version);
}
function removeQuoteLiquidity(
uint256 version,
uint256 lpIn,
uint256 minQuoteOut
)
external
override
nonReentrant
checkVersion(version)
whenNotPaused
returns (uint256 quoteOut)
{
quoteOut = _removeQuoteLiquidity(version, lpIn, minQuoteOut);
IERC20(quoteAddress).safeTransfer(msg.sender, quoteOut);
}
function removeQuoteLiquidityUnwrap(
uint256 version,
uint256 lpIn,
uint256 minQuoteOut
)
external
override
nonReentrant
checkVersion(version)
whenNotPaused
returns (uint256 quoteOut)
{
quoteOut = _removeQuoteLiquidity(version, lpIn, minQuoteOut);
IWrappedERC20(quoteAddress).withdraw(quoteOut);
(bool success, ) = msg.sender.call{value: quoteOut}("");
require(success, "Transfer failed");
}
function _removeQuoteLiquidity(
uint256 version,
uint256 lpIn,
uint256 minQuoteOut
) internal returns (uint256 quoteOut) {
RebalanceResult memory old = _handleRebalance(version);
uint256 lpSupply = IERC20(lpToken).totalSupply();
uint256 ampl = getAmpl();
uint256 oraclePrice = getOraclePrice();
uint256 d1;
{
uint256 d0 = _getD(old.base, old.quote, ampl, oraclePrice);
_updatePriceOverOracleIntegral(old.base, old.quote, ampl, oraclePrice, d0);
d1 = d0.sub(d0.mul(lpIn).div(lpSupply));
}
uint256 idealQuote = old.quote.mul(lpSupply.sub(lpIn)).div(lpSupply);
uint256 newQuote = _getQuote(ampl, old.base, oraclePrice, d1) + 1;
uint256 fee = idealQuote.sub(newQuote).multiplyDecimal(_getFeeRate(old.rebalanceTimestamp));
quoteOut = old.quote.sub(newQuote).sub(fee);
require(quoteOut >= minQuoteOut, "Insufficient output");
ILiquidityGauge(lpToken).burnFrom(msg.sender, lpIn);
uint256 adminFee = fee.multiplyDecimal(adminFeeRate);
totalAdminFee = totalAdminFee.add(adminFee);
quoteBalance = newQuote.add(fee).sub(adminFee);
emit LiquidityRemoved(msg.sender, lpIn, 0, quoteOut, fee, adminFee, oraclePrice);
}
function sync() external nonReentrant {
RebalanceResult memory old = _handleRebalance(fund.getRebalanceSize());
uint256 ampl = getAmpl();
uint256 oraclePrice = getOraclePrice();
uint256 d = _getD(old.base, old.quote, ampl, oraclePrice);
_updatePriceOverOracleIntegral(old.base, old.quote, ampl, oraclePrice, d);
uint256 newBase = fund.trancheBalanceOf(baseTranche, address(this));
uint256 newQuote = _getNewQuoteBalance();
baseBalance = newBase;
quoteBalance = newQuote;
emit Sync(newBase, newQuote, oraclePrice);
}
function collectFee() external {
uint256 totalAdminFee_ = totalAdminFee;
delete totalAdminFee;
IERC20(quoteAddress).safeTransfer(feeCollector, totalAdminFee_);
}
function _getNewQuoteBalance() private view returns (uint256) {
return IERC20(quoteAddress).balanceOf(address(this)).sub(totalAdminFee);
}
function _updatePriceOverOracleIntegral(
uint256 base,
uint256 quote,
uint256 ampl,
uint256 oraclePrice,
uint256 d
) private {
_priceOverOracleIntegral +=
_getPriceOverOracle(base, quote, ampl, oraclePrice, d) *
(block.timestamp - _priceOverOracleTimestamp);
_priceOverOracleTimestamp = block.timestamp;
}
function _getD(
uint256 base,
uint256 quote,
uint256 ampl,
uint256 oraclePrice
) private view returns (uint256) {
uint256 normalizedQuote = quote.mul(_quoteDecimalMultiplier);
uint256 baseValue = base.multiplyDecimal(oraclePrice);
uint256 sum = baseValue.add(normalizedQuote);
if (sum == 0) return 0;
uint256 prev = 0;
uint256 d = sum;
for (uint256 i = 0; i < MAX_ITERATION; i++) {
prev = d;
uint256 d3 = d.mul(d).div(baseValue).mul(d) / normalizedQuote / 4;
d = (sum.mul(4 * ampl) + 2 * d3).mul(d) / d.mul(4 * ampl - 1).add(3 * d3);
if (d <= prev + 1 && prev <= d + 1) {
break;
}
}
return d;
}
function _getPriceOverOracle(
uint256 base,
uint256 quote,
uint256 ampl,
uint256 oraclePrice,
uint256 d
) private view returns (uint256) {
uint256 commonExp = d.multiplyDecimal(4e18 - 1e18 / ampl);
uint256 baseValue = base.multiplyDecimal(oraclePrice);
uint256 normalizedQuote = quote.mul(_quoteDecimalMultiplier);
return
(baseValue.mul(8).add(normalizedQuote.mul(4)).sub(commonExp))
.multiplyDecimal(normalizedQuote)
.divideDecimal(normalizedQuote.mul(8).add(baseValue.mul(4)).sub(commonExp))
.divideDecimal(baseValue);
}
function _getBase(
uint256 ampl,
uint256 quote,
uint256 oraclePrice,
uint256 d
) private view returns (uint256 base) {
uint256 normalizedQuote = quote.mul(_quoteDecimalMultiplier);
uint256 d3 = d.mul(d).div(normalizedQuote).mul(d) / (16 * ampl);
uint256 prev = 0;
uint256 baseValue = d;
for (uint256 i = 0; i < MAX_ITERATION; i++) {
prev = baseValue;
baseValue =
baseValue.mul(baseValue).add(d3) /
(2 * baseValue).add(normalizedQuote).add(d / (4 * ampl)).sub(d);
if (baseValue <= prev + 1 && prev <= baseValue + 1) {
break;
}
}
base = baseValue.divideDecimal(oraclePrice);
}
function _getQuote(
uint256 ampl,
uint256 base,
uint256 oraclePrice,
uint256 d
) private view returns (uint256 quote) {
uint256 baseValue = base.multiplyDecimal(oraclePrice);
uint256 d3 = d.mul(d).div(baseValue).mul(d) / (16 * ampl);
uint256 prev = 0;
uint256 normalizedQuote = d;
for (uint256 i = 0; i < MAX_ITERATION; i++) {
prev = normalizedQuote;
normalizedQuote =
normalizedQuote.mul(normalizedQuote).add(d3) /
(2 * normalizedQuote).add(baseValue).add(d / (4 * ampl)).sub(d);
if (normalizedQuote <= prev + 1 && prev <= normalizedQuote + 1) {
break;
}
}
quote = normalizedQuote / _quoteDecimalMultiplier;
}
function updateAmplRamp(uint256 endAmpl, uint256 endTimestamp) external onlyOwner {
require(endAmpl > 0 && endAmpl < AMPL_MAX_VALUE, "Invalid A");
require(endTimestamp >= block.timestamp + AMPL_RAMP_MIN_TIME, "A ramp time too short");
uint256 ampl = getAmpl();
require(
(endAmpl >= ampl && endAmpl <= ampl * AMPL_RAMP_MAX_CHANGE) ||
(endAmpl < ampl && endAmpl * AMPL_RAMP_MAX_CHANGE >= ampl),
"A ramp change too large"
);
amplRampStart = ampl;
amplRampEnd = endAmpl;
amplRampStartTimestamp = block.timestamp;
amplRampEndTimestamp = endTimestamp;
emit AmplRampUpdated(ampl, endAmpl, block.timestamp, endTimestamp);
}
function _updateFeeCollector(address newFeeCollector) private {
feeCollector = newFeeCollector;
emit FeeCollectorUpdated(newFeeCollector);
}
function updateFeeCollector(address newFeeCollector) external onlyOwner {
_updateFeeCollector(newFeeCollector);
}
function _updateFeeRate(uint256 newFeeRate) private {
require(newFeeRate <= MAX_FEE_RATE, "Exceed max fee rate");
normalFeeRate = newFeeRate;
emit FeeRateUpdated(newFeeRate);
}
function updateFeeRate(uint256 newFeeRate) external onlyOwner {
_updateFeeRate(newFeeRate);
}
function _updateAdminFeeRate(uint256 newAdminFeeRate) private {
require(newAdminFeeRate <= MAX_ADMIN_FEE_RATE, "Exceed max admin fee rate");
adminFeeRate = newAdminFeeRate;
emit AdminFeeRateUpdated(newAdminFeeRate);
}
function updateAdminFeeRate(uint256 newAdminFeeRate) external onlyOwner {
_updateAdminFeeRate(newAdminFeeRate);
}
modifier checkVersion(uint256 version) {
_checkVersion(version);
_;
}
function _checkVersion(uint256 version) internal view virtual {}
function _getRebalanceResult(
uint256 latestVersion
)
internal
view
virtual
returns (
RebalanceResult memory result,
uint256 excessiveQ,
uint256 excessiveB,
uint256 excessiveR,
uint256 excessiveQuote
);
function _handleRebalance(
uint256 latestVersion
) internal virtual returns (RebalanceResult memory result);
function getOraclePrice() public view virtual override returns (uint256);
}
文件 21 的 22:WstETHBishopStableSwap.sol
pragma solidity >=0.6.10 <0.8.0;
import "./WstETHStableSwap.sol";
contract WstETHBishopStableSwap is WstETHStableSwap {
event Rebalanced(uint256 base, uint256 quote, uint256 version);
constructor(
address lpToken_,
address fund_,
address quoteAddress_,
uint256 quoteDecimals_,
uint256 ampl_,
address feeCollector_,
uint256 feeRate_,
uint256 adminFeeRate_
)
public
WstETHStableSwap(
lpToken_,
fund_,
TRANCHE_B,
quoteAddress_,
quoteDecimals_,
ampl_,
feeCollector_,
feeRate_,
adminFeeRate_
)
{}
function _rebalanceBase(
uint256 oldBase,
uint256 fromVersion,
uint256 toVersion
) internal view override returns (uint256 excessiveQ, uint256 newBase) {
(excessiveQ, newBase, ) = fund.batchRebalance(0, oldBase, 0, fromVersion, toVersion);
}
function _getBaseNav() internal view override returns (uint256) {
uint256 price = fund.twapOracle().getLatest();
(, uint256 navB, ) = fund.extrapolateNav(price);
return navB;
}
}
文件 22 的 22:WstETHStableSwap.sol
pragma solidity >=0.6.10 <0.8.0;
import "../interfaces/ITrancheIndexV2.sol";
import "../interfaces/IWstETH.sol";
import "./StableSwapV3.sol";
abstract contract WstETHStableSwap is StableSwapV3, ITrancheIndexV2 {
event Rebalanced(uint256 base, uint256 quote, uint256 version);
address public immutable wstETH;
uint256 public currentVersion;
constructor(
address lpToken_,
address fund_,
uint256 baseTranche_,
address quoteAddress_,
uint256 quoteDecimals_,
uint256 ampl_,
address feeCollector_,
uint256 feeRate_,
uint256 adminFeeRate_
)
public
StableSwapV3(
lpToken_,
fund_,
baseTranche_,
quoteAddress_,
quoteDecimals_,
ampl_,
feeCollector_,
feeRate_,
adminFeeRate_
)
{
require(quoteAddress_ == IFundV3(fund_).tokenUnderlying());
wstETH = quoteAddress_;
currentVersion = IFundV3(fund_).getRebalanceSize();
}
function removeLiquidityUnwrapWstETH(
uint256 version,
uint256 lpIn,
uint256 minBaseOut,
uint256 minStETHOut
) external nonReentrant checkVersion(version) returns (uint256 baseOut, uint256 stETHOut) {
uint256 quoteOut;
(baseOut, quoteOut) = _removeLiquidity(version, lpIn, minBaseOut, 0);
stETHOut = IWstETH(wstETH).unwrap(quoteOut);
require(stETHOut >= minStETHOut, "Insufficient output");
IERC20(IWstETH(wstETH).stETH()).safeTransfer(msg.sender, stETHOut);
}
function removeQuoteLiquidityUnwrapWstETH(
uint256 version,
uint256 lpIn,
uint256 minStETHOut
) external nonReentrant checkVersion(version) whenNotPaused returns (uint256 stETHOut) {
uint256 quoteOut = _removeQuoteLiquidity(version, lpIn, 0);
stETHOut = IWstETH(wstETH).unwrap(quoteOut);
require(stETHOut >= minStETHOut, "Insufficient output");
IERC20(IWstETH(wstETH).stETH()).safeTransfer(msg.sender, stETHOut);
}
function _checkVersion(uint256 version) internal view override {
require(version == fund.getRebalanceSize(), "Obsolete rebalance version");
}
function _getRebalanceResult(
uint256 latestVersion
)
internal
view
override
returns (RebalanceResult memory result, uint256 excessiveQ, uint256, uint256, uint256)
{
result.quote = quoteBalance;
if (latestVersion == currentVersion) {
result.base = baseBalance;
return (result, 0, 0, 0, 0);
}
result.rebalanceTimestamp = latestVersion == 0
? 0
: fund.getRebalanceTimestamp(latestVersion - 1);
(excessiveQ, result.base, ) = fund.batchRebalance(
0,
baseBalance,
0,
currentVersion,
latestVersion
);
}
function _handleRebalance(
uint256 latestVersion
) internal override returns (RebalanceResult memory result) {
uint256 excessiveQ;
(result, excessiveQ, , , ) = _getRebalanceResult(latestVersion);
if (result.rebalanceTimestamp != 0) {
baseBalance = result.base;
quoteBalance = result.quote;
currentVersion = latestVersion;
emit Rebalanced(result.base, result.quote, latestVersion);
if (excessiveQ > 0) {
fund.trancheTransfer(TRANCHE_Q, lpToken, excessiveQ, latestVersion);
}
ILiquidityGauge(lpToken).distribute(excessiveQ, 0, 0, 0, latestVersion);
}
}
function getOraclePrice() public view override returns (uint256) {
uint256 baseNav = _getBaseNav();
return baseNav.divideDecimal(IWstETH(wstETH).stEthPerToken());
}
function _rebalanceBase(
uint256 oldBase,
uint256 fromVersion,
uint256 toVersion
) internal view virtual returns (uint256 excessiveQ, uint256 newBase);
function _getBaseNav() internal view virtual returns (uint256);
}
{
"compilationTarget": {
"contracts/swap/WstETHBishopStableSwap.sol": "WstETHBishopStableSwap"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"lpToken_","type":"address"},{"internalType":"address","name":"fund_","type":"address"},{"internalType":"address","name":"quoteAddress_","type":"address"},{"internalType":"uint256","name":"quoteDecimals_","type":"uint256"},{"internalType":"uint256","name":"ampl_","type":"uint256"},{"internalType":"address","name":"feeCollector_","type":"address"},{"internalType":"uint256","name":"feeRate_","type":"uint256"},{"internalType":"uint256","name":"adminFeeRate_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newAdminFeeRate","type":"uint256"}],"name":"AdminFeeRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"start","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"end","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endTimestamp","type":"uint256"}],"name":"AmplRampUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newFeeCollector","type":"address"}],"name":"FeeCollectorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newFeeRate","type":"uint256"}],"name":"FeeRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"baseIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quoteIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"adminFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oraclePrice","type":"uint256"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"lpIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quotOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"adminFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oraclePrice","type":"uint256"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousPauser","type":"address"},{"indexed":true,"internalType":"address","name":"newPauser","type":"address"}],"name":"PauserRoleTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"base","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quote","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"version","type":"uint256"}],"name":"Rebalanced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"baseIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quoteIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quoteOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"adminFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oraclePrice","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"base","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quote","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oraclePrice","type":"uint256"}],"name":"Sync","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"lpOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"adminFeeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amplRampEnd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amplRampEndTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amplRampStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amplRampStartTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseTranche","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"baseOut","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"buy","outputs":[{"internalType":"uint256","name":"realBaseOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentVersion","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fund","outputs":[{"internalType":"contract IFundV3","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAmpl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"quoteOut","type":"uint256"}],"name":"getBaseIn","outputs":[{"internalType":"uint256","name":"baseIn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"quoteIn","type":"uint256"}],"name":"getBaseOut","outputs":[{"internalType":"uint256","name":"baseOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentPriceOverOracle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOraclePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPriceOverOracleIntegral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseOut","type":"uint256"}],"name":"getQuoteIn","outputs":[{"internalType":"uint256","name":"quoteIn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseIn","type":"uint256"}],"name":"getQuoteOut","outputs":[{"internalType":"uint256","name":"quoteOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"normalFeeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauser","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quoteAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quoteBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"lpIn","type":"uint256"},{"internalType":"uint256","name":"minBaseOut","type":"uint256"}],"name":"removeBaseLiquidity","outputs":[{"internalType":"uint256","name":"baseOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"lpIn","type":"uint256"},{"internalType":"uint256","name":"minBaseOut","type":"uint256"},{"internalType":"uint256","name":"minQuoteOut","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"baseOut","type":"uint256"},{"internalType":"uint256","name":"quoteOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"lpIn","type":"uint256"},{"internalType":"uint256","name":"minBaseOut","type":"uint256"},{"internalType":"uint256","name":"minQuoteOut","type":"uint256"}],"name":"removeLiquidityUnwrap","outputs":[{"internalType":"uint256","name":"baseOut","type":"uint256"},{"internalType":"uint256","name":"quoteOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"lpIn","type":"uint256"},{"internalType":"uint256","name":"minBaseOut","type":"uint256"},{"internalType":"uint256","name":"minStETHOut","type":"uint256"}],"name":"removeLiquidityUnwrapWstETH","outputs":[{"internalType":"uint256","name":"baseOut","type":"uint256"},{"internalType":"uint256","name":"stETHOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"lpIn","type":"uint256"},{"internalType":"uint256","name":"minQuoteOut","type":"uint256"}],"name":"removeQuoteLiquidity","outputs":[{"internalType":"uint256","name":"quoteOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"lpIn","type":"uint256"},{"internalType":"uint256","name":"minQuoteOut","type":"uint256"}],"name":"removeQuoteLiquidityUnwrap","outputs":[{"internalType":"uint256","name":"quoteOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"lpIn","type":"uint256"},{"internalType":"uint256","name":"minStETHOut","type":"uint256"}],"name":"removeQuoteLiquidityUnwrapWstETH","outputs":[{"internalType":"uint256","name":"stETHOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renouncePauserRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"uint256","name":"quoteOut","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"sell","outputs":[{"internalType":"uint256","name":"realQuoteOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalAdminFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPauser","type":"address"}],"name":"transferPauserRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newAdminFeeRate","type":"uint256"}],"name":"updateAdminFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"endAmpl","type":"uint256"},{"internalType":"uint256","name":"endTimestamp","type":"uint256"}],"name":"updateAmplRamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeCollector","type":"address"}],"name":"updateFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFeeRate","type":"uint256"}],"name":"updateFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wstETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]