Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
BackingManagerP1
Compiler Version
v0.8.9+commit.e5eed63a
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "contracts/interfaces/IAsset.sol"; import "contracts/interfaces/IBackingManager.sol"; import "contracts/interfaces/IMain.sol"; import "contracts/libraries/Array.sol"; import "contracts/libraries/Fixed.sol"; import "contracts/p1/mixins/Trading.sol"; import "contracts/p1/mixins/RecollateralizationLib.sol"; /** * @title BackingManager * @notice The backing manager holds + manages the backing for an RToken */ /// @custom:oz-upgrades-unsafe-allow external-library-linking contract BackingManagerP1 is TradingP1, IBackingManager { using FixLib for uint192; using SafeERC20Upgradeable for IERC20Upgradeable; // Cache of peer components IAssetRegistry private assetRegistry; IBasketHandler private basketHandler; IDistributor private distributor; IRToken private rToken; IERC20 private rsr; IStRSR private stRSR; IRevenueTrader private rsrTrader; IRevenueTrader private rTokenTrader; uint48 public constant MAX_TRADING_DELAY = 31536000; // {s} 1 year uint192 public constant MAX_BACKING_BUFFER = 1e18; // {%} uint48 public tradingDelay; // {s} how long to wait until resuming trading after switching uint192 public backingBuffer; // {%} how much extra backing collateral to keep // ==== Invariants ==== // tradingDelay <= MAX_TRADING_DELAY and backingBuffer <= MAX_BACKING_BUFFER // // ... and the *much* more complicated temporal properties for _manageTokens() function init( IMain main_, uint48 tradingDelay_, uint192 backingBuffer_, uint192 maxTradeSlippage_, uint192 minTradeVolume_ ) external initializer { __Component_init(main_); __Trading_init(main_, maxTradeSlippage_, minTradeVolume_); assetRegistry = main_.assetRegistry(); basketHandler = main_.basketHandler(); distributor = main_.distributor(); rsr = main_.rsr(); rsrTrader = main_.rsrTrader(); rTokenTrader = main_.rTokenTrader(); rToken = main_.rToken(); stRSR = main_.stRSR(); setTradingDelay(tradingDelay_); setBackingBuffer(backingBuffer_); } /// Give RToken max allowance over the registered token `erc20` /// @custom:interaction CEI // checks: erc20 in assetRegistry // action: set allowance on erc20 for rToken to UINT_MAX // Using two safeApprove calls instead of safeIncreaseAllowance to support USDT function grantRTokenAllowance(IERC20 erc20) external notPausedOrFrozen { require(assetRegistry.isRegistered(erc20), "erc20 unregistered"); // == Interaction == IERC20Upgradeable(address(erc20)).safeApprove(address(main.rToken()), 0); IERC20Upgradeable(address(erc20)).safeApprove(address(main.rToken()), type(uint256).max); } /// Maintain the overall backing policy; handout assets otherwise /// @custom:interaction // checks: the addresses in `erc20s` are unique // effect: _manageTokens(erc20s) function manageTokens(IERC20[] calldata erc20s) external notPausedOrFrozen { // Token list must not contain duplicates require(ArrayLib.allUnique(erc20s), "duplicate tokens"); _manageTokens(erc20s); } /// Mointain the overall backing policy; handout assets otherwise /// @dev Tokens must be in sorted order! /// @dev Performs a uniqueness check on the erc20s list in O(n) /// @custom:interaction // checks: the addresses in `erc20s` are unique (and sorted) // effect: _manageTokens(erc20s) function manageTokensSortedOrder(IERC20[] calldata erc20s) external notPausedOrFrozen { // Token list must not contain duplicates require(ArrayLib.sortedAndAllUnique(erc20s), "duplicate/unsorted tokens"); _manageTokens(erc20s); } /// Maintain the overall backing policy; handout assets otherwise /// @custom:interaction RCEI // only called internally, from manageTokens*, so erc20s has no duplicates unique // (but not necessarily all registered or valid!) function _manageTokens(IERC20[] calldata erc20s) private { // == Refresh == assetRegistry.refresh(); if (tradesOpen > 0) return; // Only trade when all the collateral assets in the basket are SOUND require(basketHandler.status() == CollateralStatus.SOUND, "basket not sound"); uint48 basketTimestamp = basketHandler.timestamp(); if (block.timestamp < basketTimestamp + tradingDelay) return; if (basketHandler.fullyCollateralized()) { // == Interaction (then return) == handoutExcessAssets(erc20s); } else { /* Recollateralization * * Strategy: iteratively move the system on a forgiving path towards capitalization * through a narrowing BU price band. The initial large spread reflects the * uncertainty associated with the market price of defaulted/volatile collateral, as * well as potential losses due to trading slippage. In the absence of further * collateral default, the size of the BU price band should decrease with each trade * until it is 0, at which point capitalization is restored. * * If we run out of capital and are still undercapitalized, we compromise * rToken.basketsNeeded to the current basket holdings. Haircut time. */ (bool doTrade, TradeRequest memory req) = RecollateralizationLibP1 .prepareRecollateralizationTrade(this); if (doTrade) { // Seize RSR if needed if (req.sell.erc20() == rsr) { uint256 bal = req.sell.erc20().balanceOf(address(this)); if (req.sellAmount > bal) stRSR.seizeRSR(req.sellAmount - bal); } tryTrade(req); } else { // Haircut time compromiseBasketsNeeded(); } } } /// Send excess assets to the RSR and RToken traders /// @custom:interaction CEI function handoutExcessAssets(IERC20[] calldata erc20s) private { /** * Assumptions: * - Fully capitalized. All collateral, and therefore assets, meet balance requirements. * - All backing capital is held at BackingManager's address. No capital is out on-trade * - Neither RToken nor RSR are in the basket * - Each address in erc20s is unique * * Steps: * 1. Forward all held RSR to the RSR trader to prevent using it for RToken appreciation * (action: send rsr().balanceOf(this) to rsrTrader) * 2. Using whatever balances of collateral are there, fast-issue all RToken possible. * (in detail: mint RToken and set basketsNeeded so that the BU/rtok exchange rate is * roughly constant, and strictly does not decrease, * 3. Handout all surplus asset balances (including collateral and RToken) to the * RSR and RToken traders according to the distribution totals. */ // Forward any RSR held to StRSR pool; RSR should never be sold for RToken yield if (rsr.balanceOf(address(this)) > 0) { // For CEI, this is an interaction "within our system" even though RSR is already live IERC20Upgradeable(address(rsr)).safeTransfer( address(rsrTrader), rsr.balanceOf(address(this)) ); } // Mint revenue RToken and update `basketsNeeded` // across this block: // where rate(R) == R.basketsNeeded / R.totalSupply, // rate(rToken') >= rate(rToken) // (>== is "no less than, and nearly equal to") // and rToken'.basketsNeeded <= basketHandler.basketsHeldBy(this) // and rToken'.totalSupply is maximal satisfying this. uint192 needed; // {BU} { needed = rToken.basketsNeeded(); // {BU} uint192 held = basketHandler.basketsHeldBy(address(this)); // {BU} if (held.gt(needed)) { int8 decimals = int8(rToken.decimals()); uint192 totalSupply = shiftl_toFix(rToken.totalSupply(), -decimals); // {rTok} // {BU} = {BU} - {BU} uint192 extraBUs = held.minus(needed); // {qRTok: Fix} = {BU} * {qRTok / BU} (if needed == 0, conv rate is 1 qRTok/BU) uint192 rTok = (needed > 0) ? extraBUs.mulDiv(totalSupply, needed) : extraBUs; rToken.mint(address(this), rTok.shiftl_toUint(decimals)); rToken.setBasketsNeeded(held); needed = held; } } // At this point, even though basketsNeeded may have changed: // - We're fully capitalized // - The BU exchange rate {BU/rTok} did not decrease // Keep a small buffer of individual collateral; "excess" assets are beyond the buffer. needed = needed.mul(FIX_ONE.plus(backingBuffer)); // Handout excess assets above what is needed, including any recently minted RToken uint256 length = erc20s.length; RevenueTotals memory totals = distributor.totals(); uint256[] memory toRSR = new uint256[](length); uint256[] memory toRToken = new uint256[](length); for (uint256 i = 0; i < length; ++i) { IAsset asset = assetRegistry.toAsset(erc20s[i]); uint192 req = needed.mul(basketHandler.quantity(erc20s[i]), CEIL); if (asset.bal(address(this)).gt(req)) { // delta: {qTok}, the excess quantity of this asset that we hold uint256 delta = asset.bal(address(this)).minus(req).shiftl_toUint( int8(IERC20Metadata(address(erc20s[i])).decimals()) ); // no div-by-0: Distributor guarantees (totals.rTokenTotal + totals.rsrTotal) > 0 // initial division is intentional here! We'd rather save the dust than be unfair toRSR[i] = (delta / (totals.rTokenTotal + totals.rsrTotal)) * totals.rsrTotal; toRToken[i] = (delta / (totals.rTokenTotal + totals.rsrTotal)) * totals.rTokenTotal; } } // == Interactions == for (uint256 i = 0; i < length; ++i) { IERC20Upgradeable erc20 = IERC20Upgradeable(address(erc20s[i])); if (toRToken[i] > 0) erc20.safeTransfer(address(rTokenTrader), toRToken[i]); if (toRSR[i] > 0) erc20.safeTransfer(address(rsrTrader), toRSR[i]); } // It's okay if there is leftover dust for RToken or a surplus asset (not RSR) } /// Compromise on how many baskets are needed in order to recollateralize-by-accounting function compromiseBasketsNeeded() private { assert(tradesOpen == 0 && !basketHandler.fullyCollateralized()); rToken.setBasketsNeeded(basketHandler.basketsHeldBy(address(this))); } // === Governance Setters === /// @custom:governance function setTradingDelay(uint48 val) public governance { require(val <= MAX_TRADING_DELAY, "invalid tradingDelay"); emit TradingDelaySet(tradingDelay, val); tradingDelay = val; } /// @custom:governance function setBackingBuffer(uint192 val) public governance { require(val <= MAX_BACKING_BUFFER, "invalid backingBuffer"); emit BackingBufferSet(backingBuffer, val); backingBuffer = val; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[41] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; import "../extensions/draft-IERC20PermitUpgradeable.sol"; import "../../../utils/AddressUpgradeable.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 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 SafeERC20Upgradeable { using AddressUpgradeable for address; function safeTransfer( IERC20Upgradeable token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20Upgradeable token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20Upgradeable token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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( IERC20Upgradeable token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20PermitUpgradeable token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @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). */ function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ 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 amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` 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 amount ) external returns (bool); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "contracts/libraries/Fixed.sol"; import "./IMain.sol"; /** * @title IAsset * @notice Supertype. Any token that interacts with our system must be wrapped in an asset, * whether it is used as RToken backing or not. Any token that can report a price in the UoA * is eligible to be an asset. */ interface IAsset { /// Can return 0, can revert /// Shortcut for price(false) /// @return {UoA/tok} The current price(), without considering fallback prices function strictPrice() external view returns (uint192); /// Can return 0 /// Should not revert if `allowFallback` is true. Can revert if false. /// @param allowFallback Whether to try the fallback price in case precise price reverts /// @return isFallback If the price is a failover price /// @return {UoA/tok} The current price(), or if it's reverting, a fallback price function price(bool allowFallback) external view returns (bool isFallback, uint192); /// @return {tok} The balance of the ERC20 in whole tokens function bal(address account) external view returns (uint192); /// @return The ERC20 contract of the token with decimals() available function erc20() external view returns (IERC20Metadata); /// @return The number of decimals in the ERC20; just for gas optimization function erc20Decimals() external view returns (uint8); /// @return If the asset is an instance of ICollateral or not function isCollateral() external view returns (bool); /// @param {UoA} The max trade volume, in UoA function maxTradeVolume() external view returns (uint192); // ==== Rewards ==== /// Get the message needed to call in order to claim rewards for holding this asset. /// Returns zero values if there is no reward function to call. /// @return _to The address to send the call to /// @return _calldata The calldata to send function getClaimCalldata() external view returns (address _to, bytes memory _calldata); /// The ERC20 token address that this Asset's rewards are paid in. /// If there are no rewards, will return a zero value. function rewardERC20() external view returns (IERC20 reward); } interface TestIAsset is IAsset { function chainlinkFeed() external view returns (AggregatorV3Interface); } /// CollateralStatus must obey a linear ordering. That is: /// - being DISABLED is worse than being IFFY, or SOUND /// - being IFFY is worse than being SOUND. enum CollateralStatus { SOUND, IFFY, // When a peg is not holding or a chainlink feed is stale DISABLED // When the collateral has completely defaulted } /// Upgrade-safe maximum operator for CollateralStatus library CollateralStatusComparator { /// @return Whether a is worse than b function worseThan(CollateralStatus a, CollateralStatus b) internal pure returns (bool) { return uint256(a) > uint256(b); } } /** * @title ICollateral * @notice A subtype of Asset that consists of the tokens eligible to back the RToken. */ interface ICollateral is IAsset { /// Emitted whenever the collateral status is changed /// @param newStatus The old CollateralStatus /// @param newStatus The updated CollateralStatus event DefaultStatusChanged( CollateralStatus indexed oldStatus, CollateralStatus indexed newStatus ); /// Refresh exchange rates and update default status. /// The Reserve protocol calls this at least once per transaction, before relying on /// this collateral's prices or default status. function refresh() external; /// @return The canonical name of this collateral's target unit. function targetName() external view returns (bytes32); /// @return The status of this collateral asset. (Is it defaulting? Might it soon?) function status() external view returns (CollateralStatus); // ==== Exchange Rates ==== /// @return {ref/tok} Quantity of whole reference units per whole collateral tokens function refPerTok() external view returns (uint192); /// @return {target/ref} Quantity of whole target units per whole reference unit in the peg function targetPerRef() external view returns (uint192); /// @return {UoA/target} The price of the target unit in UoA (usually this is {UoA/UoA} = 1) function pricePerTarget() external view returns (uint192); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./IComponent.sol"; import "./ITrading.sol"; /** * @title IBackingManager * @notice The BackingManager handles changes in the ERC20 balances that back an RToken. * - It computes which trades to perform, if any, and initiates these trades with the Broker. * - If already capitalized, excess assets are transferred to RevenueTraders. * * `manageTokens(erc20s)` and `manageTokensSortedOrder(erc20s)` are handles for getting at the * same underlying functionality. The former allows an ERC20 list in any order, while the * latter requires a sorted array, and executes in O(n) rather than O(n^2) time. In the * vast majority of cases we expect the the O(n^2) function to be acceptable. */ interface IBackingManager is IComponent, ITrading { event TradingDelaySet(uint48 indexed oldVal, uint48 indexed newVal); event BackingBufferSet(uint192 indexed oldVal, uint192 indexed newVal); // Initialization function init( IMain main_, uint48 tradingDelay_, uint192 backingBuffer_, uint192 maxTradeSlippage_, uint192 minTradeVolume_ ) external; // Give RToken max allowance over a registered token /// @custom:refresher /// @custom:interaction function grantRTokenAllowance(IERC20) external; /// Mointain the overall backing policy; handout assets otherwise /// @dev Performs a uniqueness check on the erc20s list in O(n^2) /// @custom:interaction function manageTokens(IERC20[] memory erc20s) external; /// Mointain the overall backing policy; handout assets otherwise /// @dev Tokens must be in sorted order! /// @dev Performs a uniqueness check on the erc20s list in O(n) /// @custom:interaction function manageTokensSortedOrder(IERC20[] memory erc20s) external; } interface TestIBackingManager is IBackingManager, TestITrading { function tradingDelay() external view returns (uint48); function backingBuffer() external view returns (uint192); function setTradingDelay(uint48 val) external; function setBackingBuffer(uint192 val) external; }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./IAssetRegistry.sol"; import "./IBasketHandler.sol"; import "./IBackingManager.sol"; import "./IBroker.sol"; import "./IGnosis.sol"; import "./IFurnace.sol"; import "./IDistributor.sol"; import "./IRToken.sol"; import "./IRevenueTrader.sol"; import "./IStRSR.sol"; import "./ITrading.sol"; import "./IVersioned.sol"; // === Auth roles === bytes32 constant OWNER = bytes32(bytes("OWNER")); bytes32 constant SHORT_FREEZER = bytes32(bytes("SHORT_FREEZER")); bytes32 constant LONG_FREEZER = bytes32(bytes("LONG_FREEZER")); bytes32 constant PAUSER = bytes32(bytes("PAUSER")); /** * Main is a central hub that maintains a list of Component contracts. * * Components: * - perform a specific function * - defer auth to Main * - usually (but not always) contain sizeable state that require a proxy */ struct Components { // Definitely need proxy IRToken rToken; IStRSR stRSR; IAssetRegistry assetRegistry; IBasketHandler basketHandler; IBackingManager backingManager; IDistributor distributor; IFurnace furnace; IBroker broker; IRevenueTrader rsrTrader; IRevenueTrader rTokenTrader; } interface IAuth is IAccessControlUpgradeable { /// Emitted when `unfreezeAt` is changed /// @param oldVal The old value of `unfreezeAt` /// @param newVal The new value of `unfreezeAt` event UnfreezeAtSet(uint48 indexed oldVal, uint48 indexed newVal); /// Emitted when the short freeze duration governance param is changed /// @param oldDuration The old short freeze duration /// @param newDuration The new short freeze duration event ShortFreezeDurationSet(uint48 indexed oldDuration, uint48 indexed newDuration); /// Emitted when the long freeze duration governance param is changed /// @param oldDuration The old long freeze duration /// @param newDuration The new long freeze duration event LongFreezeDurationSet(uint48 indexed oldDuration, uint48 indexed newDuration); /// Emitted when the system is paused or unpaused /// @param oldVal The old value of `paused` /// @param newVal The new value of `paused` event PausedSet(bool indexed oldVal, bool indexed newVal); /** * Paused: Disable everything except for OWNER actions and RToken.redeem/cancel * Frozen: Disable everything except for OWNER actions */ function pausedOrFrozen() external view returns (bool); function frozen() external view returns (bool); function shortFreeze() external view returns (uint48); function longFreeze() external view returns (uint48); // ==== // onlyRole(OWNER) function freezeForever() external; // onlyRole(SHORT_FREEZER) function freezeShort() external; // onlyRole(LONG_FREEZER) function freezeLong() external; // onlyRole(OWNER) function unfreeze() external; function pause() external; function unpause() external; } interface IComponentRegistry { // === Component setters/getters === event RTokenSet(IRToken indexed oldVal, IRToken indexed newVal); function rToken() external view returns (IRToken); event StRSRSet(IStRSR indexed oldVal, IStRSR indexed newVal); function stRSR() external view returns (IStRSR); event AssetRegistrySet(IAssetRegistry indexed oldVal, IAssetRegistry indexed newVal); function assetRegistry() external view returns (IAssetRegistry); event BasketHandlerSet(IBasketHandler indexed oldVal, IBasketHandler indexed newVal); function basketHandler() external view returns (IBasketHandler); event BackingManagerSet(IBackingManager indexed oldVal, IBackingManager indexed newVal); function backingManager() external view returns (IBackingManager); event DistributorSet(IDistributor indexed oldVal, IDistributor indexed newVal); function distributor() external view returns (IDistributor); event RSRTraderSet(IRevenueTrader indexed oldVal, IRevenueTrader indexed newVal); function rsrTrader() external view returns (IRevenueTrader); event RTokenTraderSet(IRevenueTrader indexed oldVal, IRevenueTrader indexed newVal); function rTokenTrader() external view returns (IRevenueTrader); event FurnaceSet(IFurnace indexed oldVal, IFurnace indexed newVal); function furnace() external view returns (IFurnace); event BrokerSet(IBroker indexed oldVal, IBroker indexed newVal); function broker() external view returns (IBroker); } /** * @title IMain * @notice The central hub for the entire system. Maintains components and an owner singleton role */ interface IMain is IVersioned, IAuth, IComponentRegistry { function poke() external; // not used in p1 // === Initialization === event MainInitialized(); function init( Components memory components, IERC20 rsr_, uint48 shortFreeze_, uint48 longFreeze_ ) external; function rsr() external view returns (IERC20); } interface TestIMain is IMain { /// @custom:governance function setShortFreeze(uint48) external; /// @custom:governance function setLongFreeze(uint48) external; function shortFreeze() external view returns (uint48); function longFreeze() external view returns (uint48); function longFreezes(address account) external view returns (uint256); function paused() external view returns (bool); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; library ArrayLib { /// O(n^2) /// @return If the array contains all unique addresses function allUnique(IERC20[] memory arr) internal pure returns (bool) { uint256 arrLen = arr.length; for (uint256 i = 1; i < arrLen; ++i) { for (uint256 j = 0; j < i; ++j) { if (arr[i] == arr[j]) return false; } } return true; } /// O(n) -- must already be in sorted ascending order! /// @return If the array contains all unique addresses, in ascending order function sortedAndAllUnique(IERC20[] memory arr) internal pure returns (bool) { uint256 arrLen = arr.length; for (uint256 i = 1; i < arrLen; ++i) { if (uint160(address(arr[i])) <= uint160(address(arr[i - 1]))) return false; } return true; } }
// SPDX-License-Identifier: BlueOak-1.0.0 // solhint-disable func-name-mixedcase func-visibility pragma solidity ^0.8.9; /// @title FixedPoint, a fixed-point arithmetic library defining the custom type uint192 /// @author Matt Elder <[email protected]> and the Reserve Team <https://reserve.org> /** The logical type `uint192 ` is a 192 bit value, representing an 18-decimal Fixed-point fractional value. This is what's described in the Solidity documentation as "fixed192x18" -- a value represented by 192 bits, that makes 18 digits available to the right of the decimal point. The range of values that uint192 can represent is about [-1.7e20, 1.7e20]. Unless a function explicitly says otherwise, it will fail on overflow. To be clear, the following should hold: toFix(0) == 0 toFix(1) == 1e18 */ // Analysis notes: // Every function should revert iff its result is out of bounds. // Unless otherwise noted, when a rounding mode is given, that mode is applied to // a single division that may happen as the last step in the computation. // Unless otherwise noted, when a rounding mode is *not* given but is needed, it's FLOOR. // For each, we comment: // - @return is the value expressed in "value space", where uint192(1e18) "is" 1.0 // - as-ints: is the value expressed in "implementation space", where uint192(1e18) "is" 1e18 // The "@return" expression is suitable for actually using the library // The "as-ints" expression is suitable for testing // A uint value passed to this library was out of bounds for uint192 operations error UIntOutOfBounds(); // Used by P1 implementation for easier casting uint256 constant FIX_ONE_256 = 1e18; uint8 constant FIX_DECIMALS = 18; // If a particular uint192 is represented by the uint192 n, then the uint192 represents the // value n/FIX_SCALE. uint64 constant FIX_SCALE = 1e18; // FIX_SCALE Squared: uint128 constant FIX_SCALE_SQ = 1e36; // The largest integer that can be converted to uint192 . // This is a bit bigger than 3.1e39 uint192 constant FIX_MAX_INT = type(uint192).max / FIX_SCALE; uint192 constant FIX_ZERO = 0; // The uint192 representation of zero. uint192 constant FIX_ONE = FIX_SCALE; // The uint192 representation of one. uint192 constant FIX_MAX = type(uint192).max; // The largest uint192. (Not an integer!) uint192 constant FIX_MIN = 0; // The smallest uint192. /// An enum that describes a rounding approach for converting to ints enum RoundingMode { FLOOR, // Round towards zero ROUND, // Round to the nearest int CEIL // Round away from zero } RoundingMode constant FLOOR = RoundingMode.FLOOR; RoundingMode constant ROUND = RoundingMode.ROUND; RoundingMode constant CEIL = RoundingMode.CEIL; /* @dev Solidity 0.8.x only allows you to change one of type or size per type conversion. Thus, all the tedious-looking double conversions like uint256(uint256 (foo)) See: https://docs.soliditylang.org/en/v0.8.9/080-breaking-changes.html#new-restrictions */ /// Explicitly convert a uint256 to a uint192. Revert if the input is out of bounds. function _safeWrap(uint256 x) pure returns (uint192) { if (FIX_MAX < x) revert UIntOutOfBounds(); return uint192(x); } /// Convert a uint to its Fix representation. /// @return x // as-ints: x * 1e18 function toFix(uint256 x) pure returns (uint192) { return _safeWrap(x * FIX_SCALE); } /// Convert a uint to its fixed-point representation, and left-shift its value `shiftLeft` /// decimal digits. /// @return x * 10**shiftLeft // as-ints: x * 10**(shiftLeft + 18) function shiftl_toFix(uint256 x, int8 shiftLeft) pure returns (uint192) { return shiftl_toFix(x, shiftLeft, FLOOR); } /// @return x * 10**shiftLeft // as-ints: x * 10**(shiftLeft + 18) function shiftl_toFix( uint256 x, int8 shiftLeft, RoundingMode rounding ) pure returns (uint192) { shiftLeft += 18; if (x == 0) return 0; if (shiftLeft <= -77) return (rounding == CEIL ? 1 : 0); // 0 < uint.max / 10**77 < 0.5 if (57 <= shiftLeft) revert UIntOutOfBounds(); // 10**56 < FIX_MAX < 10**57 uint256 coeff = 10**abs(shiftLeft); uint256 shifted = (shiftLeft >= 0) ? x * coeff : _divrnd(x, coeff, rounding); return _safeWrap(shifted); } /// Divide a uint by a uint192, yielding a uint192 /// This may also fail if the result is MIN_uint192! not fixing this for optimization's sake. /// @return x / y // as-ints: x * 1e36 / y function divFix(uint256 x, uint192 y) pure returns (uint192) { // If we didn't have to worry about overflow, we'd just do `return x * 1e36 / _y` // If it's safe to do this operation the easy way, do it: if (x < uint256(type(uint256).max / FIX_SCALE_SQ)) { return _safeWrap(uint256(x * FIX_SCALE_SQ) / y); } else { return _safeWrap(mulDiv256(x, FIX_SCALE_SQ, y)); } } /// Divide a uint by a uint, yielding a uint192 /// @return x / y // as-ints: x * 1e18 / y function divuu(uint256 x, uint256 y) pure returns (uint192) { return _safeWrap(mulDiv256(FIX_SCALE, x, y)); } /// @return min(x,y) // as-ints: min(x,y) function fixMin(uint192 x, uint192 y) pure returns (uint192) { return x < y ? x : y; } /// @return max(x,y) // as-ints: max(x,y) function fixMax(uint192 x, uint192 y) pure returns (uint192) { return x > y ? x : y; } /// @return absoluteValue(x,y) // as-ints: absoluteValue(x,y) function abs(int256 x) pure returns (uint256) { return x < 0 ? uint256(-x) : uint256(x); } /// Divide two uints, returning a uint, using rounding mode `rounding`. /// @return numerator / divisor // as-ints: numerator / divisor function _divrnd( uint256 numerator, uint256 divisor, RoundingMode rounding ) pure returns (uint256) { uint256 result = numerator / divisor; if (rounding == FLOOR) return result; if (rounding == ROUND) { if (numerator % divisor > (divisor - 1) / 2) { result++; } } else { if (numerator % divisor > 0) { result++; } } return result; } library FixLib { /// Again, all arithmetic functions fail if and only if the result is out of bounds. /// Convert this fixed-point value to a uint. Round towards zero if needed. /// @return x // as-ints: x / 1e18 function toUint(uint192 x) internal pure returns (uint136) { return toUint(x, FLOOR); } /// Convert this uint192 to a uint /// @return x // as-ints: x / 1e18 with rounding function toUint(uint192 x, RoundingMode rounding) internal pure returns (uint136) { return uint136(_divrnd(uint256(x), FIX_SCALE, rounding)); } /// Return the uint192 shifted to the left by `decimal` digits /// (Similar to a bitshift but in base 10) /// @return x * 10**decimals // as-ints: x * 10**decimals function shiftl(uint192 x, int8 decimals) internal pure returns (uint192) { return shiftl(x, decimals, FLOOR); } /// Return the uint192 shifted to the left by `decimal` digits /// (Similar to a bitshift but in base 10) /// @return x * 10**decimals // as-ints: x * 10**decimals function shiftl( uint192 x, int8 decimals, RoundingMode rounding ) internal pure returns (uint192) { uint256 coeff = uint256(10**abs(decimals)); return _safeWrap(decimals >= 0 ? x * coeff : _divrnd(x, coeff, rounding)); } /// Add a uint192 to this uint192 /// @return x + y // as-ints: x + y function plus(uint192 x, uint192 y) internal pure returns (uint192) { return x + y; } /// Add a uint to this uint192 /// @return x + y // as-ints: x + y*1e18 function plusu(uint192 x, uint256 y) internal pure returns (uint192) { return _safeWrap(x + y * FIX_SCALE); } /// Subtract a uint192 from this uint192 /// @return x - y // as-ints: x - y function minus(uint192 x, uint192 y) internal pure returns (uint192) { return x - y; } /// Subtract a uint from this uint192 /// @return x - y // as-ints: x - y*1e18 function minusu(uint192 x, uint256 y) internal pure returns (uint192) { return _safeWrap(uint256(x) - uint256(y * FIX_SCALE)); } /// Multiply this uint192 by a uint192 /// Round truncated values to the nearest available value. 5e-19 rounds away from zero. /// @return x * y // as-ints: x * y/1e18 [division using ROUND, not FLOOR] function mul(uint192 x, uint192 y) internal pure returns (uint192) { return mul(x, y, ROUND); } /// Multiply this uint192 by a uint192 /// @return x * y // as-ints: x * y/1e18 function mul( uint192 x, uint192 y, RoundingMode rounding ) internal pure returns (uint192) { return _safeWrap(_divrnd(uint256(x) * uint256(y), FIX_SCALE, rounding)); } /// Multiply this uint192 by a uint /// @return x * y // as-ints: x * y function mulu(uint192 x, uint256 y) internal pure returns (uint192) { return _safeWrap(x * y); } /// Divide this uint192 by a uint192 /// @return x / y // as-ints: x * 1e18 / y function div(uint192 x, uint192 y) internal pure returns (uint192) { return div(x, y, FLOOR); } /// Divide this uint192 by a uint192 /// @return x / y // as-ints: x * 1e18 / y function div( uint192 x, uint192 y, RoundingMode rounding ) internal pure returns (uint192) { // Multiply-in FIX_SCALE before dividing by y to preserve precision. return _safeWrap(_divrnd(uint256(x) * FIX_SCALE, y, rounding)); } /// Divide this uint192 by a uint /// @return x / y // as-ints: x / y function divu(uint192 x, uint256 y) internal pure returns (uint192) { return divu(x, y, FLOOR); } /// Divide this uint192 by a uint /// @return x / y // as-ints: x / y function divu( uint192 x, uint256 y, RoundingMode rounding ) internal pure returns (uint192) { return _safeWrap(_divrnd(x, y, rounding)); } uint64 constant FIX_HALF = uint64(FIX_SCALE) / 2; /// Raise this uint192 to a nonnegative integer power. /// Intermediate muls do nearest-value rounding. /// Presumes that powu(0.0, 0) = 1 /// @dev The gas cost is O(lg(y)) /// @return x_ ** y // as-ints: x_ ** y / 1e18**(y-1) <- technically correct for y = 0. :D function powu(uint192 x_, uint48 y) internal pure returns (uint192) { // The algorithm is exponentiation by squaring. See: https://w.wiki/4LjE if (y == 1) return x_; if (x_ == FIX_ONE || y == 0) return FIX_ONE; uint256 x = uint256(x_); uint256 result = FIX_SCALE; while (true) { if (y & 1 == 1) result = (result * x + FIX_HALF) / FIX_SCALE; if (y <= 1) break; y = y >> 1; x = (x * x + FIX_HALF) / FIX_SCALE; } return _safeWrap(result); } /// Comparison operators... function lt(uint192 x, uint192 y) internal pure returns (bool) { return x < y; } function lte(uint192 x, uint192 y) internal pure returns (bool) { return x <= y; } function gt(uint192 x, uint192 y) internal pure returns (bool) { return x > y; } function gte(uint192 x, uint192 y) internal pure returns (bool) { return x >= y; } function eq(uint192 x, uint192 y) internal pure returns (bool) { return x == y; } function neq(uint192 x, uint192 y) internal pure returns (bool) { return x != y; } /// Return whether or not this uint192 is less than epsilon away from y. /// @return |x - y| < epsilon // as-ints: |x - y| < epsilon function near( uint192 x, uint192 y, uint192 epsilon ) internal pure returns (bool) { uint192 diff = x <= y ? y - x : x - y; return diff < epsilon; } // ================ Chained Operations ================ // The operation foo_bar() always means: // Do foo() followed by bar(), and overflow only if the _end_ result doesn't fit in an uint192 /// Shift this uint192 left by `decimals` digits, and convert to a uint /// @return x * 10**decimals // as-ints: x * 10**(decimals - 18) function shiftl_toUint(uint192 x, int8 decimals) internal pure returns (uint256) { return shiftl_toUint(x, decimals, FLOOR); } /// Shift this uint192 left by `decimals` digits, and convert to a uint. /// @return x * 10**decimals // as-ints: x * 10**(decimals - 18) function shiftl_toUint( uint192 x, int8 decimals, RoundingMode rounding ) internal pure returns (uint256) { decimals -= 18; // shift so that toUint happens at the same time. uint256 coeff = uint256(10**abs(decimals)); return decimals >= 0 ? uint256(x * coeff) : uint256(_divrnd(x, coeff, rounding)); } /// Multiply this uint192 by a uint, and output the result as a uint /// @return x * y // as-ints: x * y / 1e18 function mulu_toUint(uint192 x, uint256 y) internal pure returns (uint256) { return mulDiv256(uint256(x), y, FIX_SCALE); } /// Multiply this uint192 by a uint, and output the result as a uint /// @return x * y // as-ints: x * y / 1e18 function mulu_toUint( uint192 x, uint256 y, RoundingMode rounding ) internal pure returns (uint256) { return mulDiv256(uint256(x), y, FIX_SCALE, rounding); } /// Multiply this uint192 by a uint192 and output the result as a uint /// @return x * y // as-ints: x * y / 1e36 function mul_toUint(uint192 x, uint192 y) internal pure returns (uint256) { return mulDiv256(uint256(x), uint256(y), FIX_SCALE_SQ); } /// Multiply this uint192 by a uint192 and output the result as a uint /// @return x * y // as-ints: x * y / 1e36 function mul_toUint( uint192 x, uint192 y, RoundingMode rounding ) internal pure returns (uint256) { return mulDiv256(uint256(x), uint256(y), FIX_SCALE_SQ, rounding); } /// Compute x * y / z avoiding intermediate overflow /// @dev Only use if you need to avoid overflow; costlier than x * y / z /// @return x * y / z // as-ints: x * y / z function muluDivu( uint192 x, uint256 y, uint256 z ) internal pure returns (uint192) { return muluDivu(x, y, z, FLOOR); } /// Compute x * y / z, avoiding intermediate overflow /// @dev Only use if you need to avoid overflow; costlier than x * y / z /// @return x * y / z // as-ints: x * y / z function muluDivu( uint192 x, uint256 y, uint256 z, RoundingMode rounding ) internal pure returns (uint192) { return _safeWrap(mulDiv256(x, y, z, rounding)); } /// Compute x * y / z on Fixes, avoiding intermediate overflow /// @dev Only use if you need to avoid overflow; costlier than x * y / z /// @return x * y / z // as-ints: x * y / z function mulDiv( uint192 x, uint192 y, uint192 z ) internal pure returns (uint192) { return mulDiv(x, y, z, FLOOR); } /// Compute x * y / z on Fixes, avoiding intermediate overflow /// @dev Only use if you need to avoid overflow; costlier than x * y / z /// @return x * y / z // as-ints: x * y / z function mulDiv( uint192 x, uint192 y, uint192 z, RoundingMode rounding ) internal pure returns (uint192) { return _safeWrap(mulDiv256(x, y, z, rounding)); } } // ================ a couple pure-uint helpers================ // as-ints comments are omitted here, because they're the same as @return statements, because // these are all pure uint functions /// Return (x*y/z), avoiding intermediate overflow. // Adapted from sources: // https://medium.com/coinmonks/4db014e080b1, https://medium.com/wicketh/afa55870a65 // and quite a few of the other excellent "Mathemagic" posts from https://medium.com/wicketh /// @dev Only use if you need to avoid overflow; costlier than x * y / z /// @return result x * y / z function mulDiv256( uint256 x, uint256 y, uint256 z ) pure returns (uint256 result) { unchecked { (uint256 hi, uint256 lo) = fullMul(x, y); if (hi >= z) revert UIntOutOfBounds(); uint256 mm = mulmod(x, y, z); if (mm > lo) hi -= 1; lo -= mm; uint256 pow2 = z & (0 - z); z /= pow2; lo /= pow2; lo += hi * ((0 - pow2) / pow2 + 1); uint256 r = 1; r *= 2 - z * r; r *= 2 - z * r; r *= 2 - z * r; r *= 2 - z * r; r *= 2 - z * r; r *= 2 - z * r; r *= 2 - z * r; r *= 2 - z * r; result = lo * r; } } /// Return (x*y/z), avoiding intermediate overflow. /// @dev Only use if you need to avoid overflow; costlier than x * y / z /// @return x * y / z function mulDiv256( uint256 x, uint256 y, uint256 z, RoundingMode rounding ) pure returns (uint256) { uint256 result = mulDiv256(x, y, z); if (rounding == FLOOR) return result; uint256 mm = mulmod(x, y, z); if (rounding == CEIL) { if (mm > 0) result += 1; } else { if (mm > ((z - 1) / 2)) result += 1; // z should be z-1 } return result; } /// Return (x*y) as a "virtual uint512" (lo, hi), representing (hi*2**256 + lo) /// Adapted from sources: /// https://medium.com/wicketh/27650fec525d, https://medium.com/coinmonks/4db014e080b1 /// @dev Intended to be internal to this library /// @return hi (hi, lo) satisfies hi*(2**256) + lo == x * y /// @return lo (paired with `hi`) function fullMul(uint256 x, uint256 y) pure returns (uint256 hi, uint256 lo) { unchecked { uint256 mm = mulmod(x, y, uint256(0) - uint256(1)); lo = x * y; hi = mm - lo; if (mm < lo) hi -= 1; } }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/utils/Multicall.sol"; import "contracts/interfaces/ITrade.sol"; import "contracts/interfaces/ITrading.sol"; import "contracts/libraries/Fixed.sol"; import "contracts/p1/mixins/Component.sol"; import "contracts/p1/mixins/RewardableLib.sol"; /// Abstract trading mixin for all Traders, to be paired with TradingLib /// @dev See docs/security for discussion of Multicall safety abstract contract TradingP1 is Multicall, ComponentP1, ReentrancyGuardUpgradeable, ITrading { using FixLib for uint192; using SafeERC20Upgradeable for IERC20Upgradeable; uint192 public constant MIN_TRADE_VOLUME = 1e29; // {UoA} uint192 public constant MAX_TRADE_SLIPPAGE = 1e18; // {%} // Peer contracts, immutable after init() IBroker private broker; // All open trades mapping(IERC20 => ITrade) public trades; uint48 public tradesOpen; // === Governance param === uint192 public maxTradeSlippage; // {%} uint192 public minTradeVolume; // {UoA} // ==== Invariants ==== // tradesOpen = len(values(trades)) // trades[sell] != 0 iff trade[sell] has been opened and not yet settled // solhint-disable-next-line func-name-mixedcase function __Trading_init( IMain main_, uint192 maxTradeSlippage_, uint192 minTradeVolume_ ) internal onlyInitializing { broker = main_.broker(); setMaxTradeSlippage(maxTradeSlippage_); setMinTradeVolume(minTradeVolume_); } /// Settle a single trade, expected to be used with multicall for efficient mass settlement /// @custom:interaction (only reads or writes trades, and is marked `nonReentrant`) // checks: // !paused, !frozen // trade[sell].canSettle() // actions: // trade[sell].settle() // effects: // trades.set(sell, 0) // tradesOpen' = tradesOpen - 1 function settleTrade(IERC20 sell) external notPausedOrFrozen nonReentrant { ITrade trade = trades[sell]; if (address(trade) == address(0)) return; require(trade.canSettle(), "cannot settle yet"); delete trades[sell]; tradesOpen--; // == Interactions == (uint256 soldAmt, uint256 boughtAmt) = trade.settle(); emit TradeSettled(trade, trade.sell(), trade.buy(), soldAmt, boughtAmt); } /// Claim all rewards and sweep to BackingManager /// Collective Action /// @custom:interaction CEI function claimAndSweepRewards() external notPausedOrFrozen { // == Interaction == RewardableLibP1.claimAndSweepRewards(); } /// Try to initiate a trade with a trading partner provided by the broker /// @custom:interaction (only reads or writes `trades`, and is marked `nonReentrant`) // checks: // (not external, so we don't need auth or pause checks) // trades[req.sell] == 0 // actions: // req.sell.increaseAllowance(broker, req.sellAmount) - two safeApprove calls to support USDT // tradeID = broker.openTrade(req) // effects: // trades' = trades.set(req.sell, tradeID) // tradesOpen' = tradesOpen + 1 // // This is reentrancy-safe because we're using the `nonReentrant` modifier on every method of // this contract that changes state this function refers to. // slither-disable-next-line reentrancy-vulnerabilities-1 function tryTrade(TradeRequest memory req) internal nonReentrant { /* */ IERC20 sell = req.sell.erc20(); assert(address(trades[sell]) == address(0)); IERC20Upgradeable(address(sell)).safeApprove(address(broker), 0); IERC20Upgradeable(address(sell)).safeApprove(address(broker), req.sellAmount); ITrade trade = broker.openTrade(req); trades[sell] = trade; tradesOpen++; emit TradeStarted(trade, sell, req.buy.erc20(), req.sellAmount, req.minBuyAmount); } // === Setters === /// @custom:governance function setMaxTradeSlippage(uint192 val) public governance { require(val < MAX_TRADE_SLIPPAGE, "invalid maxTradeSlippage"); emit MaxTradeSlippageSet(maxTradeSlippage, val); maxTradeSlippage = val; } /// @custom:governance function setMinTradeVolume(uint192 val) public governance { require(val <= MIN_TRADE_VOLUME, "invalid minTradeVolume"); emit MinTradeVolumeSet(minTradeVolume, val); minTradeVolume = val; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[46] private __gap; }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "contracts/interfaces/IAsset.sol"; import "contracts/interfaces/IAssetRegistry.sol"; import "contracts/interfaces/ITrading.sol"; import "contracts/libraries/Fixed.sol"; import "./TradeLib.sol"; /// Struct purposes: /// 1. Stay under stack limit with fewer vars /// 2. Cache information such as component addresses + trading rules to save on gas struct ComponentCache { ITrading trader; IBasketHandler bh; IAssetRegistry reg; IStRSR stRSR; IERC20 rsr; IRToken rToken; } struct TradingRules { uint192 minTradeVolume; // {UoA} uint192 maxTradeSlippage; // {1} } struct TradeInfo { IAsset sell; IAsset buy; uint192 sellAmount; // {sellTok} uint192 buyAmount; // {buyTok} uint192 sellPrice; // {UoA/sellTok} uint192 buyPrice; // {UoA/buyTok} } /** * @title RecollateralizationLibP1 * @notice An informal extension of the Trading mixin that provides trade preparation views * Users: * - BackingManager * - RTokenAsset * * @dev The caller must implement the ITrading interface! * * Interface: * 1. prepareRecollateralizationTrade (external) * 2. basketRange (internal) */ library RecollateralizationLibP1 { using FixLib for uint192; using TradeLib for TradeInfo; /// Select and prepare a trade that moves us closer to capitalization, using the /// basket range to avoid overeager/duplicate trading. // This is the "main loop" for recollateralization trading: // actions: // let range = basketRange(all erc20s) // let trade = nextTradePair(...) // if trade.sell is not a defaulted collateral, prepareTradeToCoverDeficit(...) // otherwise, prepareTradeSell(trade) with a 0 minBuyAmount function prepareRecollateralizationTrade(ITrading trader) external view returns (bool doTrade, TradeRequest memory req) { // === Prepare cached values === IMain main = trader.main(); ComponentCache memory components = ComponentCache({ trader: trader, bh: main.basketHandler(), reg: main.assetRegistry(), stRSR: main.stRSR(), rsr: main.rsr(), rToken: main.rToken() }); TradingRules memory rules = TradingRules({ minTradeVolume: trader.minTradeVolume(), maxTradeSlippage: trader.maxTradeSlippage() }); IERC20[] memory erc20s = components.reg.erc20s(); // ============================ // Compute basket range - {BU} BasketRange memory range = basketRange(components, rules, erc20s); // Select a pair to trade next, if one exists TradeInfo memory trade = nextTradePair(components, rules, erc20s, range); // Don't trade if no pair is selected if (address(trade.sell) == address(0) || address(trade.buy) == address(0)) { return (false, req); } // If we are selling UNSOUND collateral, eliminate the minBuyAmount requirement if ( trade.sell.isCollateral() && ICollateral(address(trade.sell)).status() != CollateralStatus.SOUND ) { (doTrade, req) = trade.prepareTradeSell(rules); req.minBuyAmount = 0; } else { (doTrade, req) = trade.prepareTradeToCoverDeficit(rules); } // At this point doTrade _must_ be true, otherwise nextTradePair assumptions are broken assert(doTrade); return (doTrade, req); } // Used to avoid stack-too-deep errors in basketRange struct BasketRange { uint192 top; // {BU} uint192 bottom; // {BU} } // It's a precondition for all of these private helpers that their `erc20s` argument contains at // least all basket collateral, plus any registered assets for which the BackingManager has a // nonzero balance. Any user of these functions should just pass in assetRegistry().erc20s(). We // would prefer to look it up from inside each function, and avoid the extra parameter to get // wrong, but the erc20s() call is pretty expensive. /// The plausible range of BUs that the BackingManager will own by the end of recapitalization. /// @param erc20s Assets this computation presumes may be traded to raise funds. // // // This function returns a "plausible range of BUs" assuming that the trading process follows // the follwing rules: // // - We will not aim to hold more than rToken.basketsNeeded() BUs // - No double trades: if we buy B in one trade, we won't sell B in another trade // - The best amount of an asset we can sell is our balance; // the worst is (our balance) - (its dust amount) // - The best price we might get for a trade is the current price estimate (frictionlessly) // - The worst price we might get for a trade between SOUND or IFFY collateral is the current // price estimate * ( 1 - maxTradeSlippage ) // - The worst price we might get for an UNPRICED or DISABLED collateral is 0. // - Given all that, we're aiming to hold as many BUs as possible using the assets we own. // // Given these assumptions, the following hold: // // range.top = min(rToken.basketsNeeded, totalAssetValue(erc20s).high / basket.price()) // because (totalAssetValue(erc20s).high / basket.price()) is how many BUs we can hold given // "best plausible" prices, and we won't try to hold more than rToken(trader).basketsNeeded // // range.bottom = max(0, min(pessimisticBUs, range.top)), where: // pessimisticBUs = (assetsLow - maxTradeSlippage * buShortfall(range.top)) / basket.price() // is the number of BUs that we are *sure* we have the assets to collateralize // (making the above assumptions about actual trade prices), and // buShortfall(range.top) = the total value of the assets we'd need to buy in order // in order to fully collataeralize `range.top` BUs, // function basketRange( ComponentCache memory components, TradingRules memory rules, IERC20[] memory erc20s ) internal view returns (BasketRange memory range) { // basketPrice: The current UoA value of one basket. (, uint192 basketPrice) = components.bh.price(true); // assetsHigh: The most value we could get from the assets in erc20, // assuming frictionless trades at currently-estimated prices. // assetsLow: The least value we might get from the assets in erc20, // assuming frictionless trades, zero value from unreliable prices, and // dustAmount of assets left in each Asset. // {UoA} (uint192 assetsHigh, uint192 assetsLow) = totalAssetValue(components, rules, erc20s); // {UoA}, Optimistic estimate of the value of our basket units at the end of this // recapitalization process. uint192 basketTargetHigh = fixMin( assetsHigh, components.rToken.basketsNeeded().mul(basketPrice) ); // {UoA}, Total value of collateral in shortfall of `basketTargetHigh`. Specifically: // sum( shortfall(c, basketTargetHigh / basketPrice) for each erc20 c in the basket) // where shortfall(c, BUs) == (BUs * bh.quantity(c) - c.balanceOf(trader)) * c.price() // (that is, shortfall(c, BUs) is the market value of the c that `this` would // need to be given in order to have enough of c to cover `BUs` BUs) // {UoA} uint192 shortfall = collateralShortfall(components, erc20s, basketTargetHigh, basketPrice); // ==== Further adjust the low backing estimate downwards to account for trading frictions // {UoA}, Total value of the slippage we'd see if we made `shortfall` trades with // slippage `maxTradeSlippage()` uint192 shortfallSlippage = rules.maxTradeSlippage.mul(shortfall); // {UoA}, Pessimistic estimate of the value of our basket units at the end of this // recapitalization process. uint192 basketTargetLow = assetsLow.gt(shortfallSlippage) ? fixMin(assetsLow.minus(shortfallSlippage), basketTargetHigh) : 0; // {BU} = {UoA} / {BU/UoA} range.top = basketTargetHigh.div(basketPrice, CEIL); range.bottom = basketTargetLow.div(basketPrice, CEIL); } // =========================================================================================== // === Private === /// Total value of the erc20s under management by BackingManager /// This may include BackingManager's balances _and_ staked RSR hold by stRSR /// @param erc20s tokens to consider "under management" by BackingManager in this computation /// @return assetsHigh {UoA} The high estimate of the total value of assets under management /// @return assetsLow {UoA} The low estimate of the total value of assets under management // preconditions: // components.trader is backingManager // erc20s has no duplicates // checks: // for e in erc20s, e has a registered asset in the assetRegistry // return values: // assetsHigh: The most value we could get from the assets in erc20, // assuming frictionless trades at currently-estimated prices. // assetsLow: The least value we might get from the assets in erc20, // assuming frictionless trades, zero value from unreliable prices, and // dustAmount of assets left in each Asset. function totalAssetValue( ComponentCache memory components, TradingRules memory rules, IERC20[] memory erc20s ) private view returns (uint192 assetsHigh, uint192 assetsLow) { // The low estimate is lower than the high estimate due to: // - Discounting assets with reverting strict prices // - Discounting dust amounts for collateral in the basket + non-dust assets uint192 potentialDustLoss; // {UoA} // Accumulate: // - assetsHigh: sum(bal(e)*price(e) for e ... ) // - potentialDustLoss: sum(minTradeSize(e) for e ... ) // - assetsLow: sum(bal(e)*price(e) for e ... if e.status() == SOUND or e is just an Asset) for (uint256 i = 0; i < erc20s.length; ++i) { // Exclude RToken balances, or else we double count if (erc20s[i] == IERC20(address(components.rToken))) continue; IAsset asset = components.reg.toAsset(erc20s[i]); uint192 bal = asset.bal(address(components.trader)); // For RSR, include the staking balance if (erc20s[i] == components.rsr) bal = bal.plus(asset.bal(address(components.stRSR))); // Ignore dust amounts for assets not in the basket; their value is inaccessible bool inBasket = components.bh.quantity(erc20s[i]).gt(FIX_ZERO); (bool isFallback, uint192 price) = asset.price(true); // {UoA} if (!inBasket && !TradeLib.isEnoughToSell(asset, price, bal, rules.minTradeVolume)) { continue; } // {UoA} = {UoA} + {UoA/tok} * {tok} uint192 val = price.mul(bal, FLOOR); // Consider all managed assets at face-value prices assetsHigh = assetsHigh.plus(val); // Accumulate potential losses to dust potentialDustLoss = potentialDustLoss.plus(rules.minTradeVolume); // Consider only reliable sources of value for the assetsLow estimate if (!isFallback) { assetsLow = assetsLow.plus(val); } } // Account for all the places dust could get stuck // assetsLow' = max(assetsLow-potentialDustLoss, 0) assetsLow = assetsLow.gt(potentialDustLoss) ? assetsLow.minus(potentialDustLoss) : FIX_ZERO; } // Used in memory in `nextTradePair` to duck the stack limit struct MaxSurplusDeficit { CollateralStatus surplusStatus; // starts SOUND uint192 surplus; // {UoA} uint192 deficit; // {UoA} } // Choose next sell/buy pair to trade, with reference to the basket range // Exclude dust amounts for surplus /// @return trade /// sell: Surplus collateral OR address(0) /// deficit Deficit collateral OR address(0) /// sellAmount {sellTok} Surplus amount (whole tokens) /// buyAmount {buyTok} Deficit amount (whole tokens) /// sellPrice {UoA/sellTok} /// buyPrice {UoA/sellTok} /// // Defining "sell" and "buy": // If bal(e) > (quantity(e) * range.top), then e is in surplus by the difference // If bal(e) < (quantity(e) * range.bottom), then e is in deficit by the difference // // First, ignoring RSR: // `trade.sell` is the token from erc20s with the greatest surplus value (in UoA), // and sellAmount is the quantity of that token that it's in surplus (in qTok). // if `trade.sell` == 0, then no token is in surplus by at least minTradeSize, // and `trade.sellAmount` and `trade.sellPrice` are unset. // // `trade.buy` is the token from erc20s with the greatest deficit value (in UoA), // and buyAmount is the quantity of that token that it's in deficit (in qTok). // if `trade.buy` == 0, then no token is in deficit at all, // and `trade.buyAmount` and `trade.buyPrice` are unset. // // Then, just if we have a buy asset and no sell asset, consider selling available RSR. // // Prefer selling assets in this order: DISABLED -> SOUND -> IFFY. function nextTradePair( ComponentCache memory components, TradingRules memory rules, IERC20[] memory erc20s, BasketRange memory range ) private view returns (TradeInfo memory trade) { MaxSurplusDeficit memory maxes; maxes.surplusStatus = CollateralStatus.IFFY; // least-desirable sell status for (uint256 i = 0; i < erc20s.length; ++i) { if (erc20s[i] == components.rsr) continue; IAsset asset = components.reg.toAsset(erc20s[i]); uint192 bal = asset.bal(address(components.trader)); // {tok} // {tok} = {BU} * {tok/BU} // needed(Top): token balance needed for range.top baskets: quantity(e) * range.top uint192 needed = range.top.mul(components.bh.quantity(erc20s[i]), CEIL); // {tok} if (bal.gt(needed)) { (, uint192 price_) = asset.price(true); // {UoA/tok} allow fallback prices // {UoA} = {tok} * {UoA/tok} uint192 delta = bal.minus(needed).mul(price_, FLOOR); CollateralStatus status; // starts SOUND if (asset.isCollateral()) status = ICollateral(address(asset)).status(); // Select the most-in-surplus "best" asset, as defined by (status, max surplusAmt) if ( (preferToSell(maxes.surplusStatus, status) || (delta.gt(maxes.surplus) && maxes.surplusStatus == status)) && TradeLib.isEnoughToSell(asset, price_, bal.minus(needed), rules.minTradeVolume) ) { trade.sell = asset; trade.sellAmount = bal.minus(needed); trade.sellPrice = price_; maxes.surplusStatus = status; maxes.surplus = delta; } } else { // needed(Bottom): token balance needed at bottom of the basket range needed = range.bottom.mul(components.bh.quantity(erc20s[i]), CEIL); // {tok}; if (bal.lt(needed)) { uint192 amtShort = needed.minus(bal); // {tok} (, uint192 price_) = asset.price(true); // {UoA/tok} allow fallback prices // {UoA} = {tok} * {UoA/tok} uint192 delta = amtShort.mul(price_, CEIL); if (delta.gt(maxes.deficit)) { trade.buy = ICollateral(address(asset)); trade.buyAmount = amtShort; trade.buyPrice = price_; maxes.deficit = delta; } } } } // Use RSR if needed if (address(trade.sell) == address(0) && address(trade.buy) != address(0)) { IAsset rsrAsset = components.reg.toAsset(components.rsr); uint192 rsrAvailable = rsrAsset.bal(address(components.trader)).plus( rsrAsset.bal(address(components.stRSR)) ); (, uint192 price_) = rsrAsset.price(true); // {UoA/tok} allow fallback prices if (TradeLib.isEnoughToSell(rsrAsset, price_, rsrAvailable, rules.minTradeVolume)) { trade.sell = rsrAsset; trade.sellAmount = rsrAvailable; trade.sellPrice = price_; } } } /// @param backing {UoA} An amount of backing in UoA terms /// @param basketPrice {UoA/BU} The price of a BU in UoA terms, at precise prices /// @return shortfall {UoA} The missing re-collateralization in UoA terms // Specifically, returns: // sum( shortfall(c, basketTargetHigh / basketPrice) for each erc20 c in the basket) // where shortfall(c, numBUs) == (numBus * bh.quantity(c) - c.balanceOf(trader)) * c.price() // (that is, shortfall(c, numBUs) is the market value of the c that `this` would // need to be given in order to have enough of c to cover `numBUs` BUs) // precondition: erc20s contains no duplicates; all basket tokens are in erc20s function collateralShortfall( ComponentCache memory components, IERC20[] memory erc20s, uint192 backing, uint192 basketPrice ) private view returns (uint192 shortfall) { assert(basketPrice > 0); // div by zero further down in function // accumulate shortfall for (uint256 i = 0; i < erc20s.length; ++i) { uint192 quantity = components.bh.quantity(erc20s[i]); // {tok/BU} if (quantity.eq(FIX_ZERO)) continue; // skip any collateral not needed // Cast: if the quantity is nonzero, then it must be collateral ICollateral coll = components.reg.toColl(erc20s[i]); // {tok} = {UoA} * {tok/BU} / {UoA/BU} // needed: quantity of erc20s[i] needed in basketPrice's worth of baskets uint192 needed = backing.mulDiv(quantity, basketPrice, CEIL); // {tok} // held: quantity of erc20s[i] owned by `this` uint192 held = coll.bal(address(components.trader)); // {tok} if (held.lt(needed)) { (, uint192 price_) = coll.price(true); // allow fallback prices // {UoA} = {UoA} + ({tok} - {tok}) * {UoA/tok} shortfall = shortfall.plus(needed.minus(held).mul(price_, FLOOR)); } } } /// Prefer selling assets in this order: DISABLED -> SOUND -> IFFY. /// @return If we prefer to sell `status2` over `status1` function preferToSell(CollateralStatus status1, CollateralStatus status2) private pure returns (bool) { // NOTE: If we change the CollaetralStatus enum then this has to change! if (status1 == CollateralStatus.DISABLED) return false; if (status1 == CollateralStatus.SOUND) return status2 == CollateralStatus.DISABLED; return status2 != CollateralStatus.IFFY; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @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 amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` 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 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20PermitUpgradeable { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @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, it is bubbled up by this * function (like regular Solidity function calls). * * 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. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @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`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // 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 /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControlUpgradeable { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "contracts/interfaces/IAsset.sol"; import "./IComponent.sol"; /** * @title IAssetRegistry * @notice The AssetRegistry is in charge of maintaining the ERC20 tokens eligible * to be handled by the rest of the system. If an asset is in the registry, this means: * 1. Its ERC20 contract has been vetted * 2. The asset is the only asset for that ERC20 * 3. The asset can be priced in the UoA, usually via an oracle */ interface IAssetRegistry is IComponent { /// Emitted when an asset is added to the registry /// @param erc20 The ERC20 contract for the asset /// @param asset The asset contract added to the registry event AssetRegistered(IERC20 indexed erc20, IAsset indexed asset); /// Emitted when an asset is removed from the registry /// @param erc20 The ERC20 contract for the asset /// @param asset The asset contract removed from the registry event AssetUnregistered(IERC20 indexed erc20, IAsset indexed asset); // Initialization function init(IMain main_, IAsset[] memory assets_) external; /// Fully refresh all asset state /// @custom:interaction function refresh() external; /// @return The corresponding asset for ERC20, or reverts if not registered function toAsset(IERC20 erc20) external view returns (IAsset); /// @return The corresponding collateral, or reverts if unregistered or not collateral function toColl(IERC20 erc20) external view returns (ICollateral); /// @return If the ERC20 is registered function isRegistered(IERC20 erc20) external view returns (bool); /// @return A list of all registered ERC20s function erc20s() external view returns (IERC20[] memory); function register(IAsset asset) external returns (bool); function swapRegistered(IAsset asset) external returns (bool swapped); function unregister(IAsset asset) external; }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "contracts/libraries/Fixed.sol"; import "./IAsset.sol"; import "./IComponent.sol"; /** * @title IBasketHandler * @notice The BasketHandler aims to maintain a reference basket of constant target unit amounts. * When a collateral token defaults, a new reference basket of equal target units is set. * When _all_ collateral tokens default for a target unit, only then is the basket allowed to fall * in terms of target unit amounts. The basket is considered defaulted in this case. */ interface IBasketHandler is IComponent { /// Emitted when the prime basket is set /// @param erc20s The collateral tokens for the prime basket /// @param targetAmts {target/BU} A list of quantities of target unit per basket unit /// @param targetNames Each collateral token's targetName event PrimeBasketSet(IERC20[] erc20s, uint192[] targetAmts, bytes32[] targetNames); /// Emitted when the reference basket is set /// @param nonce The basket nonce /// @param erc20s The list of collateral tokens in the reference basket /// @param refAmts {ref/BU} The reference amounts of the basket collateral tokens /// @param disabled True when the list of erc20s + refAmts may not be correct event BasketSet(uint256 indexed nonce, IERC20[] erc20s, uint192[] refAmts, bool disabled); /// Emitted when a backup config is set for a target unit /// @param targetName The name of the target unit as a bytes32 /// @param max The max number to use from `erc20s` /// @param erc20s The set of backup collateral tokens event BackupConfigSet(bytes32 indexed targetName, uint256 indexed max, IERC20[] erc20s); // Initialization function init(IMain main_) external; /// Set the prime basket /// @param erc20s The collateral tokens for the new prime basket /// @param targetAmts The target amounts (in) {target/BU} for the new prime basket /// required range: 1e9 values; absolute range irrelevant. /// @custom:governance function setPrimeBasket(IERC20[] memory erc20s, uint192[] memory targetAmts) external; /// Set the backup configuration for a given target /// @param targetName The name of the target as a bytes32 /// @param max The maximum number of collateral tokens to use from this target /// Required range: 1-255 /// @param erc20s A list of ordered backup collateral tokens /// @custom:governance function setBackupConfig( bytes32 targetName, uint256 max, IERC20[] calldata erc20s ) external; /// Default the basket in order to schedule a basket refresh /// @custom:protected function disableBasket() external; /// Governance-controlled setter to cause a basket switch explicitly /// @custom:governance /// @custom:interaction function refreshBasket() external; /// @return If the BackingManager has sufficient collateral to redeem the entire RToken supply function fullyCollateralized() external view returns (bool); /// @return status The worst CollateralStatus of all collateral in the basket function status() external view returns (CollateralStatus status); /// @return {tok/BU} The whole token quantity of token in the reference basket /// Returns 0 if erc20 is not registered, disabled, or not in the basket /// Returns FIX_MAX (in lieu of +infinity) if Collateral.refPerTok() is 0. /// Otherwise, returns (token's basket.refAmts / token's Collateral.refPerTok()) function quantity(IERC20 erc20) external view returns (uint192); /// @param amount {BU} /// @return erc20s The addresses of the ERC20 tokens in the reference basket /// @return quantities {qTok} The quantity of each ERC20 token to issue `amount` baskets function quote(uint192 amount, RoundingMode rounding) external view returns (address[] memory erc20s, uint256[] memory quantities); /// @return baskets {BU} The quantity of complete baskets at an address. A balance for BUs function basketsHeldBy(address account) external view returns (uint192 baskets); /// @param allowFallback Whether to fail over to the fallback price or not /// @return isFallback If any fallback prices were used /// @return p {UoA/BU} The protocol's best guess at what a BU would be priced at in UoA function price(bool allowFallback) external view returns (bool isFallback, uint192 p); /// @return The basket nonce, a monotonically increasing unique identifier function nonce() external view returns (uint48); /// @return timestamp The timestamp at which the basket was last set function timestamp() external view returns (uint48); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "./IAsset.sol"; import "./IComponent.sol"; import "./IGnosis.sol"; import "./ITrade.sol"; /// The data format that describes a request for trade with the Broker struct TradeRequest { IAsset sell; IAsset buy; uint256 sellAmount; // {qSellTok} uint256 minBuyAmount; // {qBuyTok} } /** * @title IBroker * @notice The Broker deploys oneshot Trade contracts for Traders and monitors * the continued proper functioning of trading platforms. */ interface IBroker is IComponent { event AuctionLengthSet(uint48 indexed oldVal, uint48 indexed newVal); event DisabledSet(bool indexed prevVal, bool indexed newVal); // Initialization function init( IMain main_, IGnosis gnosis_, ITrade tradeImplementation_, uint48 auctionLength_ ) external; /// Request a trade from the broker /// @dev Requires setting an allowance in advance /// @custom:interaction function openTrade(TradeRequest memory req) external returns (ITrade); /// Only callable by one of the trading contracts the broker deploys function reportViolation() external; function disabled() external view returns (bool); } interface TestIBroker is IBroker { function gnosis() external view returns (IGnosis); function auctionLength() external view returns (uint48); function setAuctionLength(uint48 newAuctionLength) external; function setDisabled(bool disabled_) external; }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; struct GnosisAuctionData { IERC20 auctioningToken; IERC20 biddingToken; uint256 orderCancellationEndDate; uint256 auctionEndDate; bytes32 initialAuctionOrder; uint256 minimumBiddingAmountPerOrder; uint256 interimSumBidAmount; bytes32 interimOrder; bytes32 clearingPriceOrder; uint96 volumeClearingPriceOrder; bool minFundingThresholdNotReached; bool isAtomicClosureAllowed; uint256 feeNumerator; uint256 minFundingThreshold; } /// The relevant portion of the interface of the live Gnosis EasyAuction contract /// https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol interface IGnosis { function initiateAuction( IERC20 auctioningToken, IERC20 biddingToken, uint256 orderCancellationEndDate, uint256 auctionEndDate, uint96 auctionedSellAmount, uint96 minBuyAmount, uint256 minimumBiddingAmountPerOrder, uint256 minFundingThreshold, bool isAtomicClosureAllowed, address accessManagerContract, bytes memory accessManagerContractData ) external returns (uint256 auctionId); function auctionData(uint256 auctionId) external view returns (GnosisAuctionData memory); /// @param auctionId The external auction id /// @dev See here for decoding: https://git.io/JMang /// @return encodedOrder The order, encoded in a bytes 32 function settleAuction(uint256 auctionId) external returns (bytes32 encodedOrder); /// @return The numerator over a 1000-valued denominator function feeNumerator() external returns (uint256); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "contracts/libraries/Fixed.sol"; import "./IComponent.sol"; /** * @title IFurnace * @notice A helper contract to burn RTokens slowly and permisionlessly. */ interface IFurnace is IComponent { // Initialization function init( IMain main_, uint48 period_, uint192 ratio_ ) external; /// Emitted when the melting period is changed /// @param oldPeriod The old period /// @param newPeriod The new period event PeriodSet(uint48 indexed oldPeriod, uint48 indexed newPeriod); function period() external view returns (uint48); /// @custom:governance function setPeriod(uint48) external; /// Emitted when the melting ratio is changed /// @param oldRatio The old ratio /// @param newRatio The new ratio event RatioSet(uint192 indexed oldRatio, uint192 indexed newRatio); function ratio() external view returns (uint192); /// Needed value range: [0, 1], granularity 1e-9 /// @custom:governance function setRatio(uint192) external; /// Performs any RToken melting that has vested since the last payout. /// @custom:refresher function melt() external; } interface TestIFurnace is IFurnace { function lastPayout() external view returns (uint256); function lastPayoutBal() external view returns (uint256); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./IComponent.sol"; struct RevenueShare { uint16 rTokenDist; // {revShare} A value between [0, 10,000] uint16 rsrDist; // {revShare} A value between [0, 10,000] } /// Assumes no more than 1024 independent distributions. struct RevenueTotals { uint24 rTokenTotal; // {revShare} uint24 rsrTotal; // {revShare} } /** * @title IDistributor * @notice The Distributor Component maintains a revenue distribution table that dictates * how to divide revenue across the Furnace, StRSR, and any other destinations. */ interface IDistributor is IComponent { /// Emitted when a distribution is set /// @param dest The address set to receive the distribution /// @param rTokenDist The distribution of RToken that should go to `dest` /// @param rsrDist The distribution of RSR that should go to `dest` event DistributionSet(address dest, uint16 rTokenDist, uint16 rsrDist); /// Emitted when revenue is distributed /// @param erc20 The token being distributed, either RSR or the RToken itself /// @param source The address providing the revenue /// @param amount The amount of the revenue event RevenueDistributed(IERC20 indexed erc20, address indexed source, uint256 indexed amount); // Initialization function init(IMain main_, RevenueShare memory dist) external; /// @custom:governance function setDistribution(address dest, RevenueShare memory share) external; /// Distribute the `erc20` token across all revenue destinations /// @custom:interaction function distribute( IERC20 erc20, address from, uint256 amount ) external; /// @return revTotals The total of all destinations function totals() external view returns (RevenueTotals memory revTotals); } interface TestIDistributor is IDistributor { // solhint-disable-next-line func-name-mixedcase function FURNACE() external view returns (address); // solhint-disable-next-line func-name-mixedcase function ST_RSR() external view returns (address); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; // solhint-disable-next-line max-line-length import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol"; import "contracts/libraries/Fixed.sol"; import "./IAsset.sol"; import "./IComponent.sol"; import "./IMain.sol"; import "./IRewardable.sol"; /** * @title IRToken * @notice An RToken is an ERC20 that is permissionlessly issuable/redeemable and tracks an * exchange rate against a single unit: baskets, or {BU} in our type notation. */ interface IRToken is IRewardable, IERC20MetadataUpgradeable, IERC20PermitUpgradeable { /// Emitted when issuance is started, at the point collateral is taken in /// @param issuer The account performing the issuance /// @param index The index off the issuance in the issuer's queue /// @param amount The quantity of RToken being issued /// @param baskets The basket unit-equivalent of the collateral deposits /// @param erc20s The ERC20 collateral tokens corresponding to the quantities /// @param quantities The quantities of tokens paid with /// @param blockAvailableAt The (continuous) block at which the issuance vests event IssuanceStarted( address indexed issuer, uint256 indexed index, uint256 indexed amount, uint192 baskets, address[] erc20s, uint256[] quantities, uint192 blockAvailableAt ); /// Emitted when an RToken issuance is canceled, such as during a default /// @param issuer The account of the issuer /// @param firstId The first of the cancelled issuances in the issuer's queue /// @param endId The index _after_ the last of the cancelled issuances in the issuer's queue /// @param amount {qRTok} The amount of RTokens canceled /// That is, id was cancelled iff firstId <= id < endId event IssuancesCanceled( address indexed issuer, uint256 indexed firstId, uint256 indexed endId, uint256 amount ); /// Emitted when an RToken issuance is completed successfully /// @param issuer The account of the issuer /// @param firstId The first of the completed issuances in the issuer's queue /// @param endId The id directly after the last of the completed issuances /// @param amount {qRTok} The amount of RTokens canceled event IssuancesCompleted( address indexed issuer, uint256 indexed firstId, uint256 indexed endId, uint256 amount ); /// Emitted when an issuance of RToken occurs, whether it occurs via slow minting or not /// @param issuer The address of the account issuing RTokens /// @param amount The quantity of RToken being issued /// @param baskets The corresponding number of baskets event Issuance(address indexed issuer, uint256 indexed amount, uint192 indexed baskets); /// Emitted when a redemption of RToken occurs /// @param redeemer The address of the account redeeeming RTokens /// @param amount The quantity of RToken being redeemed /// @param baskets The corresponding number of baskets /// @param amount {qRTok} The amount of RTokens canceled event Redemption(address indexed redeemer, uint256 indexed amount, uint192 baskets); /// Emitted when the number of baskets needed changes /// @param oldBasketsNeeded Previous number of baskets units needed /// @param newBasketsNeeded New number of basket units needed event BasketsNeededChanged(uint192 oldBasketsNeeded, uint192 newBasketsNeeded); /// Emitted when RToken is melted, i.e the RToken supply is decreased but basketsNeeded is not /// @param amount {qRTok} event Melted(uint256 amount); /// Emitted when the IssuanceRate is set event IssuanceRateSet(uint192 indexed oldVal, uint192 indexed newVal); /// Emitted when the redemption battery max charge is set event ScalingRedemptionRateSet(uint192 indexed oldVal, uint192 indexed newVal); /// Emitted when the dust supply is set event RedemptionRateFloorSet(uint256 indexed oldVal, uint256 indexed newVal); // Initialization function init( IMain main_, string memory name_, string memory symbol_, string memory mandate_, uint192 issuanceRate_, uint192 redemptionBattery_, uint256 redemptionVirtualSupply_ ) external; /// Begin a time-delayed issuance of RToken for basket collateral /// @param amount {qRTok} The quantity of RToken to issue /// @custom:interaction function issue(uint256 amount) external; /// Cancels a vesting slow issuance of _msgSender /// If earliest == true, cancel id if id < endId /// If earliest == false, cancel id if endId <= id /// @param endId One edge of the issuance range to cancel /// @param earliest If true, cancel earliest issuances; else, cancel latest issuances /// @custom:interaction function cancel(uint256 endId, bool earliest) external; /// Completes vested slow issuances for the account, up to endId. /// @param account The address of the account to vest issuances for /// @custom:interaction function vest(address account, uint256 endId) external; /// Redeem RToken for basket collateral /// @param amount {qRTok} The quantity {qRToken} of RToken to redeem /// @custom:interaction function redeem(uint256 amount) external; /// Mints a quantity of RToken to the `recipient`, callable only by the BackingManager /// @param recipient The recipient of the newly minted RToken /// @param amount {qRTok} The amount to be minted /// @custom:protected function mint(address recipient, uint256 amount) external; /// Melt a quantity of RToken from the caller's account /// @param amount {qRTok} The amount to be melted function melt(uint256 amount) external; /// Set the number of baskets needed directly, callable only by the BackingManager /// @param basketsNeeded {BU} The number of baskets to target /// needed range: pretty interesting /// @custom:protected function setBasketsNeeded(uint192 basketsNeeded) external; /// @return {BU} How many baskets are being targeted function basketsNeeded() external view returns (uint192); /// @return {qRTok} The maximum redemption that can be performed in the current block function redemptionLimit() external view returns (uint256); } interface TestIRToken is IRToken { /// Set the issuance rate as a % of RToken supply function setIssuanceRate(uint192) external; /// @return {1} The issuance rate as a percentage of the RToken supply function issuanceRate() external view returns (uint192); /// Set the fraction of the RToken supply that can be reedemed at once function setScalingRedemptionRate(uint192 val) external; /// @return {1/hour} The maximum fraction of the RToken supply that can be redeemed at once function scalingRedemptionRate() external view returns (uint192); /// Set the RToken supply at which full redemptions become enabled function setRedemptionRateFloor(uint256 val) external; /// @return {qRTok/hour} The lowest possible hourly redemption limit function redemptionRateFloor() external view returns (uint256); function increaseAllowance(address, uint256) external returns (bool); function decreaseAllowance(address, uint256) external returns (bool); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "./IComponent.sol"; import "./ITrading.sol"; /** * @title IRevenueTrader * @notice The RevenueTrader is an extension of the trading mixin that trades all * assets at its address for a single target asset. There are two runtime instances * of the RevenueTrader, 1 for RToken and 1 for RSR. */ interface IRevenueTrader is IComponent, ITrading { // Initialization function init( IMain main_, IERC20 tokenToBuy_, uint192 maxTradeSlippage_, uint192 minTradeVolume_ ) external; /// Processes a single token; unpermissioned /// @dev Intended to be used with multicall /// @custom:interaction function manageToken(IERC20 sell) external; } // solhint-disable-next-line no-empty-blocks interface TestIRevenueTrader is IRevenueTrader, TestITrading { function tokenToBuy() external view returns (IERC20); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; // solhint-disable-next-line max-line-length import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol"; import "contracts/libraries/Fixed.sol"; import "./IComponent.sol"; import "./IMain.sol"; /** * @title IStRSR * @notice An ERC20 token representing shares of the RSR insurance pool. * * StRSR permits the BackingManager to take RSR in times of need. In return, the BackingManager * benefits the StRSR pool with RSR rewards purchased with a portion of its revenue. * * In the absence of collateral default or losses due to slippage, StRSR should have a * monotonically increasing exchange rate with respect to RSR, meaning that over time * StRSR is redeemable for more RSR. It is non-rebasing. */ interface IStRSR is IERC20MetadataUpgradeable, IERC20PermitUpgradeable, IComponent { /// Emitted when RSR is staked /// @param era The era at time of staking /// @param staker The address of the staker /// @param rsrAmount {qRSR} How much RSR was staked /// @param stRSRAmount {qStRSR} How much stRSR was minted by this staking event Staked( uint256 indexed era, address indexed staker, uint256 rsrAmount, uint256 indexed stRSRAmount ); /// Emitted when an unstaking is started /// @param draftId The id of the draft. /// @param draftEra The era of the draft. /// @param staker The address of the unstaker /// The triple (staker, draftEra, draftId) is a unique ID /// @param rsrAmount {qRSR} How much RSR this unstaking will be worth, absent seizures /// @param stRSRAmount {qStRSR} How much stRSR was burned by this unstaking event UnstakingStarted( uint256 indexed draftId, uint256 indexed draftEra, address indexed staker, uint256 rsrAmount, uint256 stRSRAmount, uint256 availableAt ); /// Emitted when RSR is unstaked /// @param firstId The beginning of the range of draft IDs withdrawn in this transaction /// @param endId The end of range of draft IDs withdrawn in this transaction /// (ID i was withdrawn if firstId <= i < endId) /// @param draftEra The era of the draft. /// The triple (staker, draftEra, id) is a unique ID among drafts /// @param staker The address of the unstaker /// @param rsrAmount {qRSR} How much RSR this unstaking was worth event UnstakingCompleted( uint256 indexed firstId, uint256 indexed endId, uint256 draftEra, address indexed staker, uint256 rsrAmount ); /// Emitted whenever the exchange rate changes event ExchangeRateSet(uint192 indexed oldVal, uint192 indexed newVal); /// Emitted whenever RSR are paids out event RewardsPaid(uint256 indexed rsrAmt); /// Emitted if all the RSR in the staking pool is seized and all balances are reset to zero. event AllBalancesReset(uint256 indexed newEra); /// Emitted if all the RSR in the unstakin pool is seized, and all ongoing unstaking is voided. event AllUnstakingReset(uint256 indexed newEra); event UnstakingDelaySet(uint48 indexed oldVal, uint48 indexed newVal); event RewardPeriodSet(uint48 indexed oldVal, uint48 indexed newVal); event RewardRatioSet(uint192 indexed oldVal, uint192 indexed newVal); // Initialization function init( IMain main_, string memory name_, string memory symbol_, uint48 unstakingDelay_, uint48 rewardPeriod_, uint192 rewardRatio_ ) external; /// Gather and payout rewards from rsrTrader /// @custom:interaction function payoutRewards() external; /// Stakes an RSR `amount` on the corresponding RToken to earn yield and insure the system /// @param amount {qRSR} /// @custom:interaction function stake(uint256 amount) external; /// Begins a delayed unstaking for `amount` stRSR /// @param amount {qStRSR} /// @custom:interaction function unstake(uint256 amount) external; /// Complete delayed unstaking for the account, up to (but not including!) `endId` /// @custom:interaction function withdraw(address account, uint256 endId) external; /// Seize RSR, only callable by main.backingManager() /// @custom:protected function seizeRSR(uint256 amount) external; /// Return the maximum valid value of endId such that withdraw(endId) should immediately work function endIdForWithdraw(address account) external view returns (uint256 endId); /// @return {qRSR/qStRSR} The exchange rate between RSR and StRSR function exchangeRate() external view returns (uint192); } interface TestIStRSR is IStRSR { function rewardPeriod() external view returns (uint48); function setRewardPeriod(uint48) external; function rewardRatio() external view returns (uint192); function setRewardRatio(uint192) external; function unstakingDelay() external view returns (uint48); function setUnstakingDelay(uint48) external; function setName(string calldata) external; function setSymbol(string calldata) external; function increaseAllowance(address, uint256) external returns (bool); function decreaseAllowance(address, uint256) external returns (bool); /// @return {qStRSR/qRSR} The exchange rate between StRSR and RSR function exchangeRate() external view returns (uint192); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "contracts/libraries/Fixed.sol"; import "./IAsset.sol"; import "./ITrade.sol"; import "./IRewardable.sol"; /** * @title ITrading * @notice Common events and refresher function for all Trading contracts */ interface ITrading is IRewardable { event MaxTradeSlippageSet(uint192 indexed oldVal, uint192 indexed newVal); event MinTradeVolumeSet(uint192 indexed oldVal, uint192 indexed newVal); /// Emitted when a trade is started /// @param trade The one-time-use trade contract that was just deployed /// @param sell The token to sell /// @param buy The token to buy /// @param sellAmount {qSellTok} The quantity of the selling token /// @param minBuyAmount {qBuyTok} The minimum quantity of the buying token to accept event TradeStarted( ITrade indexed trade, IERC20 indexed sell, IERC20 indexed buy, uint256 sellAmount, uint256 minBuyAmount ); /// Emitted after a trade ends /// @param trade The one-time-use trade contract /// @param sell The token to sell /// @param buy The token to buy /// @param sellAmount {qSellTok} The quantity of the token sold /// @param buyAmount {qBuyTok} The quantity of the token bought event TradeSettled( ITrade indexed trade, IERC20 indexed sell, IERC20 indexed buy, uint256 sellAmount, uint256 buyAmount ); /// Settle a single trade, expected to be used with multicall for efficient mass settlement /// @custom:refresher function settleTrade(IERC20 sell) external; /// @return {%} The maximum trade slippage acceptable function maxTradeSlippage() external view returns (uint192); /// @return {UoA} The minimum trade volume in UoA, applies to all assets function minTradeVolume() external view returns (uint192); /// @return The ongoing trade for a sell token, or the zero address function trades(IERC20 sell) external view returns (ITrade); } interface TestITrading is ITrading { /// @custom:governance function setMaxTradeSlippage(uint192 val) external; /// @custom:governance function setMinTradeVolume(uint192 val) external; /// @return The number of ongoing trades open function tradesOpen() external view returns (uint48); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; interface IVersioned { function version() external view returns (string memory); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "./IMain.sol"; import "./IVersioned.sol"; /** * @title IComponent * @notice A Component is the central building block of all our system contracts. Components * contain important state that must be migrated during upgrades, and they delegate * their ownership to Main's owner. */ interface IComponent is IVersioned { function main() external view returns (IMain); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; /** * Simple generalized trading interface for all Trade contracts to obey * * Usage: if (canSettle()) settle() */ interface ITrade { function sell() external view returns (IERC20Metadata); function buy() external view returns (IERC20Metadata); /// @return The timestamp at which the trade is projected to become settle-able function endTime() external view returns (uint48); /// @return True if the trade can be settled /// @dev Should be guaranteed to be true eventually as an invariant function canSettle() external view returns (bool); /// Complete the trade and transfer tokens back to the origin trader /// @return soldAmt {qSellTok} The quantity of tokens sold /// @return boughtAmt {qBuyTok} The quantity of tokens bought function settle() external returns (uint256 soldAmt, uint256 boughtAmt); }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "./IComponent.sol"; import "./IMain.sol"; /** * @title IRewardable * @notice A simple component mixin interface to support claiming + monetization of rewards */ interface IRewardable is IComponent { /// Emitted whenever rewards are claimed event RewardsClaimed(address indexed erc20, uint256 indexed amount); /// Claim reward tokens from integrated defi protocols such as Compound/Aave /// @custom:interaction function claimAndSweepRewards() external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20MetadataUpgradeable is IERC20Upgradeable { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol) pragma solidity ^0.8.0; import "./Address.sol"; /** * @dev Provides a function to batch together multiple calls in a single external call. * * _Available since v4.1._ */ abstract contract Multicall { /** * @dev Receives and executes a batch of function calls on this contract. */ function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; i++) { results[i] = Address.functionDelegateCall(address(this), data[i]); } return results; } }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; import "contracts/interfaces/IComponent.sol"; import "contracts/interfaces/IMain.sol"; import "contracts/mixins/Versioned.sol"; /** * Abstract superclass for system contracts registered in Main */ abstract contract ComponentP1 is Versioned, Initializable, ContextUpgradeable, UUPSUpgradeable, IComponent { IMain public main; /// @custom:oz-upgrades-unsafe-allow constructor // solhint-disable-next-line no-empty-blocks constructor() initializer {} // Sets main for the component - Can only be called during initialization // solhint-disable-next-line func-name-mixedcase function __Component_init(IMain main_) internal onlyInitializing { require(address(main_) != address(0), "main is zero address"); __UUPSUpgradeable_init(); main = main_; } // === See docs/security.md === modifier notPausedOrFrozen() { require(!main.pausedOrFrozen(), "paused or frozen"); _; } modifier notFrozen() { require(!main.frozen(), "frozen"); _; } modifier governance() { require(main.hasRole(OWNER, _msgSender()), "governance only"); _; } // solhint-disable-next-line no-empty-blocks function _authorizeUpgrade(address newImplementation) internal view override governance {} /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "contracts/interfaces/IAssetRegistry.sol"; import "contracts/interfaces/IBackingManager.sol"; import "contracts/interfaces/IRewardable.sol"; /** * @title RewardableLibP1 * @notice An library that allows a contract to claim rewards * @dev The caller must implement the IRewardable interface! */ library RewardableLibP1 { using AddressUpgradeable for address; using SafeERC20Upgradeable for IERC20Upgradeable; /// Redefines event for when rewards are claimed, to be able to emit from library event RewardsClaimed(address indexed erc20, uint256 indexed amount); struct Claim { IERC20 reward; address callTo; bytes _calldata; } /// Claim all rewards and sweep to BackingManager /// Collective Action /// @custom:interaction mostly CEI but see comments // where: // this: the contract from which this function is being delegateCall'dd // claims = {{rewardToken: erc20.rewardERC20(), to, calldata} // for erc20 in assetRegistry // where (to, calldata) = erc20.getClaimCalldata(){caller: this} // if to != 0 and rewardToken in assetRegistry} // rewards = {claim.rewardToken for claim in claims} // actions: // first, do to.functionCall(calldata) for claim in claims // then, if this is not backingManager // then do // reward.transfer(bal, backingManager) for claim in claims if bal > 0 // where reward = claim.reward and bal = reward.balanceOf(this) function claimAndSweepRewards() external { IAssetRegistry reg = assetRegistry(); IERC20[] memory erc20s = reg.erc20s(); IERC20[] memory rewardTokens = new IERC20[](erc20s.length); uint256 numRewardTokens = 0; Claim[] memory claims = new Claim[](erc20s.length); uint256 numClaims = 0; // Compute the interactions to have... for (uint256 i = 0; i < erc20s.length; ++i) { // Does erc20s[i] _have_ a reward function and reward token? IAsset asset = reg.toAsset(erc20s[i]); IERC20 rewardToken = asset.rewardERC20(); if (address(rewardToken) == address(0) || !reg.isRegistered(rewardToken)) continue; (address _to, bytes memory _calldata) = asset.getClaimCalldata(); if (_to == address(0)) continue; // Save Claim claims[numClaims] = Claim({ reward: rewardToken, callTo: _to, _calldata: _calldata }); ++numClaims; // Save rewardToken address, if new uint256 rtIndex = 0; while (rtIndex < numRewardTokens && rewardToken != rewardTokens[rtIndex]) rtIndex++; if (rtIndex >= numRewardTokens) { rewardTokens[rtIndex] = rewardToken; numRewardTokens++; } } // == Interactions == // Claim rewards for (uint256 i = 0; i < numClaims; i++) { // Safe violation of strict CEI: we're reading balanceOf() here, but oldBal and newBal // are only used here to emit the right event. Their definitions don't leave the inner // block of this loop. uint256 oldBal = claims[i].reward.balanceOf(address(this)); claims[i].callTo.functionCall(claims[i]._calldata, "rewards claim failed"); uint256 newBal = claims[i].reward.balanceOf(address(this)); emit RewardsClaimed(address(claims[i].reward), newBal - oldBal); } // Sweep reward tokens to the backingManager if (address(this) != address(backingManager())) { for (uint256 i = 0; i < numRewardTokens; ++i) { // Safe violation of strict CEI: we're reading balanceOf() here, too, but it's // actually our intention to sweep all of rewardTokens[i] at this point, regardless // of whatever else we may have computed in the function above. uint256 bal = rewardTokens[i].balanceOf(address(this)); if (bal > 0) { IERC20Upgradeable(address(rewardTokens[i])).safeTransfer( address(backingManager()), bal ); } } } } /// @return The AssetRegistry function assetRegistry() private view returns (IAssetRegistry) { return IRewardable(address(this)).main().assetRegistry(); } /// @return The BackingManager function backingManager() private view returns (IBackingManager) { return IRewardable(address(this)).main().backingManager(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original * initialization step. This is essential to configure modules that are added through upgrades and that require * initialization. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @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, it is bubbled up by this * function (like regular Solidity function calls). * * 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. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @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`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // 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 /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/UUPSUpgradeable.sol) pragma solidity ^0.8.0; import "../../interfaces/draft-IERC1822Upgradeable.sol"; import "../ERC1967/ERC1967UpgradeUpgradeable.sol"; import "./Initializable.sol"; /** * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy. * * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing * `UUPSUpgradeable` with a custom implementation of upgrades. * * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism. * * _Available since v4.1._ */ abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable { function __UUPSUpgradeable_init() internal onlyInitializing { } function __UUPSUpgradeable_init_unchained() internal onlyInitializing { } /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment address private immutable __self = address(this); /** * @dev Check that the execution is being performed through a delegatecall call and that the execution context is * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to * fail. */ modifier onlyProxy() { require(address(this) != __self, "Function must be called through delegatecall"); require(_getImplementation() == __self, "Function must be called through active proxy"); _; } /** * @dev Check that the execution is not being performed through a delegate call. This allows a function to be * callable on the implementing contract but not through proxies. */ modifier notDelegated() { require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall"); _; } /** * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the * implementation. It is used to validate that the this implementation remains valid after an upgrade. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier. */ function proxiableUUID() external view virtual override notDelegated returns (bytes32) { return _IMPLEMENTATION_SLOT; } /** * @dev Upgrade the implementation of the proxy to `newImplementation`. * * Calls {_authorizeUpgrade}. * * Emits an {Upgraded} event. */ function upgradeTo(address newImplementation) external virtual onlyProxy { _authorizeUpgrade(newImplementation); _upgradeToAndCallUUPS(newImplementation, new bytes(0), false); } /** * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call * encoded in `data`. * * Calls {_authorizeUpgrade}. * * Emits an {Upgraded} event. */ function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy { _authorizeUpgrade(newImplementation); _upgradeToAndCallUUPS(newImplementation, data, true); } /** * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by * {upgradeTo} and {upgradeToAndCall}. * * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}. * * ```solidity * function _authorizeUpgrade(address) internal override onlyOwner {} * ``` */ function _authorizeUpgrade(address newImplementation) internal virtual; /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "contracts/interfaces/IVersioned.sol"; /** * @title Versioned * @notice A mix-in to track semantic versioning uniformly across contracts. */ abstract contract Versioned is IVersioned { function version() public pure virtual override returns (string memory) { return "1.2.0"; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol) pragma solidity ^0.8.0; /** * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified * proxy whose upgrades are fully controlled by the current implementation. */ interface IERC1822ProxiableUpgradeable { /** * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation * address. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. */ function proxiableUUID() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol) pragma solidity ^0.8.2; import "../beacon/IBeaconUpgradeable.sol"; import "../../interfaces/draft-IERC1822Upgradeable.sol"; import "../../utils/AddressUpgradeable.sol"; import "../../utils/StorageSlotUpgradeable.sol"; import "../utils/Initializable.sol"; /** * @dev This abstract contract provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. * * _Available since v4.1._ * * @custom:oz-upgrades-unsafe-allow delegatecall */ abstract contract ERC1967UpgradeUpgradeable is Initializable { function __ERC1967Upgrade_init() internal onlyInitializing { } function __ERC1967Upgrade_init_unchained() internal onlyInitializing { } // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** * @dev Returns the current implementation address. */ function _getImplementation() internal view returns (address) { return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the EIP1967 implementation slot. */ function _setImplementation(address newImplementation) private { require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract"); StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; } /** * @dev Perform implementation upgrade * * Emits an {Upgraded} event. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Perform implementation upgrade with additional setup call. * * Emits an {Upgraded} event. */ function _upgradeToAndCall( address newImplementation, bytes memory data, bool forceCall ) internal { _upgradeTo(newImplementation); if (data.length > 0 || forceCall) { _functionDelegateCall(newImplementation, data); } } /** * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. * * Emits an {Upgraded} event. */ function _upgradeToAndCallUUPS( address newImplementation, bytes memory data, bool forceCall ) internal { // Upgrades from old implementations will perform a rollback test. This test requires the new // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing // this special case will break upgrade paths from old UUPS implementation to new ones. if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) { _setImplementation(newImplementation); } else { try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) { require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID"); } catch { revert("ERC1967Upgrade: new implementation is not UUPS"); } _upgradeToAndCall(newImplementation, data, forceCall); } } /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Emitted when the admin account has changed. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Returns the current admin. */ function _getAdmin() internal view returns (address) { return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value; } /** * @dev Stores a new address in the EIP1967 admin slot. */ function _setAdmin(address newAdmin) private { require(newAdmin != address(0), "ERC1967: new admin is the zero address"); StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin; } /** * @dev Changes the admin of the proxy. * * Emits an {AdminChanged} event. */ function _changeAdmin(address newAdmin) internal { emit AdminChanged(_getAdmin(), newAdmin); _setAdmin(newAdmin); } /** * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. */ bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; /** * @dev Emitted when the beacon is upgraded. */ event BeaconUpgraded(address indexed beacon); /** * @dev Returns the current beacon. */ function _getBeacon() internal view returns (address) { return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value; } /** * @dev Stores a new beacon in the EIP1967 beacon slot. */ function _setBeacon(address newBeacon) private { require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract"); require( AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()), "ERC1967: beacon implementation is not a contract" ); StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon; } /** * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). * * Emits a {BeaconUpgraded} event. */ function _upgradeBeaconToAndCall( address newBeacon, bytes memory data, bool forceCall ) internal { _setBeacon(newBeacon); emit BeaconUpgraded(newBeacon); if (data.length > 0 || forceCall) { _functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data); } } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) { require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed"); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol) pragma solidity ^0.8.0; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeaconUpgradeable { /** * @dev Must return an address that can be used as a delegate call target. * * {BeaconProxy} will check that this address is a contract. */ function implementation() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol) pragma solidity ^0.8.0; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC1967 implementation slot: * ``` * contract ERC1967 { * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._ */ library StorageSlotUpgradeable { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } }
// SPDX-License-Identifier: BlueOak-1.0.0 pragma solidity 0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "contracts/interfaces/IAsset.sol"; import "contracts/interfaces/IAssetRegistry.sol"; import "contracts/interfaces/ITrading.sol"; import "contracts/libraries/Fixed.sol"; import "./RecollateralizationLib.sol"; /** * @title TradeLib * @notice An internal lib for preparing individual trades on particular asset pairs * Users: * - BackingManagerLib * - RevenueTrader */ library TradeLib { using FixLib for uint192; /// Prepare a trade to sell `trade.sellAmount` that guarantees a reasonable closing price, /// without explicitly aiming at a particular quantity to purchase. /// @param trade: /// sell != 0, sellAmount >= 0 {sellTok}, sellPrice >= 0 {UoA/sellTok} /// buy != 0, buyAmount (unused) {buyTok}, buyPrice > 0 {UoA/buyTok} /// @return notDust True when the trade is larger than the dust amount /// @return req The prepared trade request to send to the Broker // // If notDust is true, then the returned trade request satisfies: // req.sell == trade.sell and req.buy == trade.buy, // req.minBuyAmount * trade.buyPrice ~= // trade.sellAmount * trade.sellPrice * (1-rules.maxTradeSlippage), // req.sellAmount == min(trade.sell.maxTradeSize().toQTok(), trade.sellAmount.toQTok(sell) // 1 < req.sellAmount // // If notDust is false, no trade exists that satisfies those constraints. function prepareTradeSell(TradeInfo memory trade, TradingRules memory rules) internal view returns (bool notDust, TradeRequest memory req) { assert(trade.buyPrice > 0); // checked for in RevenueTrader / CollateralizatlionLib // assert(trade.sellPrice >= 0); not needed // Don't sell dust if (!isEnoughToSell(trade.sell, trade.sellPrice, trade.sellAmount, rules.minTradeVolume)) { return (false, req); } // {sellTok} - reads trade.sell.price(true) uint192 s = fixMin(trade.sellAmount, maxTradeSize(trade.sell, trade.sellPrice)); // {buyTok} = {sellTok} * {UoA/sellTok} / {UoA/buyTok} uint192 b = s.mul(FIX_ONE.minus(rules.maxTradeSlippage)).mulDiv( trade.sellPrice, trade.buyPrice, CEIL ); req.sell = trade.sell; req.buy = trade.buy; req.sellAmount = s.shiftl_toUint(int8(trade.sell.erc20Decimals()), FLOOR); req.minBuyAmount = b.shiftl_toUint(int8(trade.buy.erc20Decimals()), CEIL); return (true, req); } /// Assuming we have `trade.sellAmount` sell tokens available, prepare a trade to cover as /// much of our deficit of `trade.buyAmount` buy tokens as possible, given expected trade /// slippage and the sell asset's maxTradeVolume(). /// @param trade: /// sell != 0 /// buy != 0 /// sellAmount (unused) {sellTok} /// buyAmount >= 0 {buyTok} /// sellPrice > 0 {UoA/sellTok} /// buyPrice > 0 {UoA/buyTok} /// @return notDust Whether the prepared trade is large enough to be worth trading /// @return req The prepared trade request to send to the Broker // // Returns prepareTradeSell(trade, rules), where // req.sellAmount = min(trade.sellAmount, // trade.buyAmount * (trade.buyPrice / trade.sellPrice) / (1-maxTradeSlippage)) // i.e, the minimum of trade.sellAmount and (a sale amount that, at current prices and // maximum slippage, will yield at least the requested trade.buyAmount) // // Which means we should get that, if notDust is true, then: // req.sell = sell and req.buy = buy // // 1 <= req.minBuyAmount <= max(trade.buyAmount, buy.minTradeSize()).toQTok(trade.buy) // 1 < req.sellAmount <= min(trade.sellAmount.toQTok(trade.sell), // sell.maxTradeSize().toQTok(trade.sell)) // req.minBuyAmount ~= trade.sellAmount * sellPrice / buyPrice * (1-maxTradeSlippage) // // req.sellAmount (and req.minBuyAmount) are maximal satisfying all these conditions function prepareTradeToCoverDeficit(TradeInfo memory trade, TradingRules memory rules) internal view returns (bool notDust, TradeRequest memory req) { assert(trade.sellPrice > 0 && trade.buyPrice > 0); // Don't buy dust. trade.buyAmount = fixMax( trade.buyAmount, minTradeSize(rules.minTradeVolume, trade.buyPrice) ); // {sellTok} = {buyTok} * {UoA/buyTok} / {UoA/sellTok} uint192 exactSellAmount = trade.buyAmount.mulDiv(trade.buyPrice, trade.sellPrice, CEIL); // exactSellAmount: Amount to sell to buy `deficitAmount` if there's no slippage // slippedSellAmount: Amount needed to sell to buy `deficitAmount`, counting slippage uint192 slippedSellAmount = exactSellAmount.div( FIX_ONE.minus(rules.maxTradeSlippage), CEIL ); trade.sellAmount = fixMin(slippedSellAmount, trade.sellAmount); // {sellTok} return prepareTradeSell(trade, rules); } /// @param asset The asset in question /// @param price {UoA/tok} The price to use /// @param amt {tok} The number of whole tokens we plan to sell /// @param minTradeVolume {UoA} The min trade volume, passed in for gas optimization /// @return If amt is sufficiently large to be worth selling into our trading platforms function isEnoughToSell( IAsset asset, uint192 price, uint192 amt, uint192 minTradeVolume ) internal view returns (bool) { // The Gnosis EasyAuction trading platform rounds defensively, meaning it is possible // for it to keep 1 qTok for itself. Therefore we should not sell 1 qTok. This is // likely to be true of all the trading platforms we integrate with. return amt.gte(minTradeSize(minTradeVolume, price)) && // {qTok} = {tok} / {tok/qTok} amt.shiftl_toUint(int8(asset.erc20Decimals())) > 1; } // === Private === /// Calculates the minTradeSize for an asset based on the given minTradeVolume and price /// @param minTradeVolume {UoA} The min trade volume, passed in for gas optimization /// @return {tok} The min trade size for the asset in whole tokens function minTradeSize(uint192 minTradeVolume, uint192 price) private pure returns (uint192) { // {tok} = {UoA} / {UoA/tok} uint192 size = price == 0 ? FIX_MAX : minTradeVolume.div(price, ROUND); return size > 0 ? size : 1; } /// Calculates the maxTradeSize for an asset based on the asset's maxTradeVolume and price /// @return {tok} The max trade size for the asset in whole tokens function maxTradeSize(IAsset asset, uint192 price) private view returns (uint192) { uint192 size = price == 0 ? FIX_MAX : asset.maxTradeVolume().div(price, ROUND); return size > 0 ? size : 1; } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": { "contracts/p1/mixins/RecollateralizationLib.sol": { "RecollateralizationLibP1": "0xb8794fb1ccd62bfe631293163f4a3fc2d22e37e0" }, "contracts/p1/mixins/RewardableLib.sol": { "RewardableLibP1": "0xe1c1af81aa2e6f978fa05532a3588e3b1e703b78" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"UIntOutOfBounds","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint192","name":"oldVal","type":"uint192"},{"indexed":true,"internalType":"uint192","name":"newVal","type":"uint192"}],"name":"BackingBufferSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint192","name":"oldVal","type":"uint192"},{"indexed":true,"internalType":"uint192","name":"newVal","type":"uint192"}],"name":"MaxTradeSlippageSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint192","name":"oldVal","type":"uint192"},{"indexed":true,"internalType":"uint192","name":"newVal","type":"uint192"}],"name":"MinTradeVolumeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"erc20","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract ITrade","name":"trade","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"sell","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"buy","type":"address"},{"indexed":false,"internalType":"uint256","name":"sellAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"buyAmount","type":"uint256"}],"name":"TradeSettled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract ITrade","name":"trade","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"sell","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"buy","type":"address"},{"indexed":false,"internalType":"uint256","name":"sellAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minBuyAmount","type":"uint256"}],"name":"TradeStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint48","name":"oldVal","type":"uint48"},{"indexed":true,"internalType":"uint48","name":"newVal","type":"uint48"}],"name":"TradingDelaySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"MAX_BACKING_BUFFER","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TRADE_SLIPPAGE","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TRADING_DELAY","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_TRADE_VOLUME","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"backingBuffer","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimAndSweepRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"erc20","type":"address"}],"name":"grantRTokenAllowance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMain","name":"main_","type":"address"},{"internalType":"uint48","name":"tradingDelay_","type":"uint48"},{"internalType":"uint192","name":"backingBuffer_","type":"uint192"},{"internalType":"uint192","name":"maxTradeSlippage_","type":"uint192"},{"internalType":"uint192","name":"minTradeVolume_","type":"uint192"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"main","outputs":[{"internalType":"contract IMain","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"erc20s","type":"address[]"}],"name":"manageTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"erc20s","type":"address[]"}],"name":"manageTokensSortedOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxTradeSlippage","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minTradeVolume","outputs":[{"internalType":"uint192","name":"","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint192","name":"val","type":"uint192"}],"name":"setBackingBuffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"val","type":"uint192"}],"name":"setMaxTradeSlippage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint192","name":"val","type":"uint192"}],"name":"setMinTradeVolume","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"val","type":"uint48"}],"name":"setTradingDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"sell","type":"address"}],"name":"settleTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"name":"trades","outputs":[{"internalType":"contract ITrade","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tradesOpen","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tradingDelay","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"}]
Contract Creation Code
60a0604052306080523480156200001557600080fd5b50600054610100900460ff1615808015620000375750600054600160ff909116105b8062000067575062000054306200014160201b62001e881760201c565b15801562000067575060005460ff166001145b620000cf5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b6000805460ff191660011790558015620000f3576000805461ff0019166101001790555b80156200013a576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5062000150565b6001600160a01b03163b151590565b608051614ec56200018860003960008181610aae01528181610aee01528181610fe90152818161102901526110b80152614ec56000f3fe6080604052600436106101815760003560e01c806398026d94116100d1578063c76aeb121161008a578063dd47cebf11610064578063dd47cebf1461049c578063dffeadd0146104bd578063e13d80f3146104dd578063e7227de6146104f257600080fd5b8063c76aeb121461045d578063cb9414f81461047c578063d50e7a701461042157600080fd5b806398026d9414610366578063a8372fd814610386578063a9f752e5146103a6578063ac9650d8146103f4578063b9bebb0414610421578063bcaeb7b91461043d57600080fd5b806336a02f571161013e5780634f1ef286116101185780634f1ef286146102dc57806352d1902d146102ef57806354fd4d50146103125780637cbf6db21461034657600080fd5b806336a02f571461027c578063386eafd91461029c5780633f056853146102bc57600080fd5b80630db3d42c146101865780630e0264d1146101ca57806310e4d36b146101ec578063127013971461020c5780632bca33e91461022d5780633659cfe61461025c575b600080fd5b34801561019257600080fd5b5060fd546101ad90600160301b90046001600160c01b031681565b6040516001600160c01b0390911681526020015b60405180910390f35b3480156101d657600080fd5b506101ea6101e5366004614375565b610519565b005b3480156101f857600080fd5b506101ea6102073660046143a7565b610918565b34801561021857600080fd5b50610135546101ad906001600160c01b031681565b34801561023957600080fd5b506102456301e1338081565b60405165ffffffffffff90911681526020016101c1565b34801561026857600080fd5b506101ea610277366004614375565b610aa3565b34801561028857600080fd5b506101ea6102973660046143a7565b610b83565b3480156102a857600080fd5b506101ea6102b73660046143d8565b610d12565b3480156102c857600080fd5b506101ea6102d7366004614441565b610ea5565b6101ea6102ea3660046144ca565b610fde565b3480156102fb57600080fd5b506103046110ab565b6040519081526020016101c1565b34801561031e57600080fd5b5060408051808201825260058152640312e322e360dc1b602082015290516101c191906145ca565b34801561035257600080fd5b5060fe546101ad906001600160c01b031681565b34801561037257600080fd5b506101ea610381366004614375565b61115e565b34801561039257600080fd5b506101ea6103a1366004614441565b6113e1565b3480156103b257600080fd5b506103dc6103c1366004614375565b60fc602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016101c1565b34801561040057600080fd5b5061041461040f366004614441565b6114ff565b6040516101c191906145dd565b34801561042d57600080fd5b506101ad670de0b6b3a764000081565b34801561044957600080fd5b506101ea61045836600461463f565b6115f5565b34801561046957600080fd5b5060fd546102459065ffffffffffff1681565b34801561048857600080fd5b506101ea6104973660046143a7565b611be3565b3480156104a857600080fd5b506101ad6c01431e0fae6d7217caa000000081565b3480156104c957600080fd5b506097546103dc906001600160a01b031681565b3480156104e957600080fd5b506101ea611d87565b3480156104fe57600080fd5b506101345461024590600160a01b900465ffffffffffff1681565b609760009054906101000a90046001600160a01b03166001600160a01b031663f17063756040518163ffffffff1660e01b815260040160206040518083038186803b15801561056757600080fd5b505afa15801561057b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059f91906146c5565b156105c55760405162461bcd60e51b81526004016105bc906146e0565b60405180910390fd5b600260c95414156106185760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105bc565b600260c9556001600160a01b03808216600090815260fc602052604090205416806106435750610910565b806001600160a01b031663faf7ba6a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561067c57600080fd5b505afa158015610690573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b491906146c5565b6106f45760405162461bcd60e51b815260206004820152601160248201527018d85b9b9bdd081cd95d1d1b19481e595d607a1b60448201526064016105bc565b6001600160a01b038216600090815260fc6020526040812080546001600160a01b031916905560fd805465ffffffffffff169161073083614720565b91906101000a81548165ffffffffffff021916908365ffffffffffff16021790555050600080826001600160a01b03166311da60b46040518163ffffffff1660e01b81526004016040805180830381600087803b15801561079057600080fd5b505af11580156107a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c89190614742565b91509150826001600160a01b031663a6f2ae3a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561080557600080fd5b505afa158015610819573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061083d9190614766565b6001600160a01b0316836001600160a01b031663457100746040518163ffffffff1660e01b815260040160206040518083038186803b15801561087f57600080fd5b505afa158015610893573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b79190614766565b6001600160a01b0316846001600160a01b03167f058fb1fa484531f400806f666315faf1a45941db93ba069405882b78ba40dc878585604051610904929190918252602082015260400190565b60405180910390a45050505b50600160c955565b60975460408051808201909152600581526427aba722a960d91b60208201526001600160a01b03909116906391d148549061095290614783565b336040516001600160e01b031960e085901b16815260048101929092526001600160a01b0316602482015260440160206040518083038186803b15801561099857600080fd5b505afa1580156109ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d091906146c5565b6109ec5760405162461bcd60e51b81526004016105bc906147aa565b670de0b6b3a76400006001600160c01b0382161115610a455760405162461bcd60e51b815260206004820152601560248201527434b73b30b634b2103130b1b5b4b733a13ab33332b960591b60448201526064016105bc565b610135546040516001600160c01b038084169216907f4ec8ac3ba3ecc0b9d7690e98f4bf8f5c70b8f2a9b4326c757b44ba3d19c1879690600090a361013580546001600160c01b0319166001600160c01b0392909216919091179055565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161415610aec5760405162461bcd60e51b81526004016105bc906147d3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316610b35600080516020614e49833981519152546001600160a01b031690565b6001600160a01b031614610b5b5760405162461bcd60e51b81526004016105bc9061481f565b610b6481611e97565b60408051600080825260208201909252610b8091839190611f6b565b50565b60975460408051808201909152600581526427aba722a960d91b60208201526001600160a01b03909116906391d1485490610bbd90614783565b336040516001600160e01b031960e085901b16815260048101929092526001600160a01b0316602482015260440160206040518083038186803b158015610c0357600080fd5b505afa158015610c17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3b91906146c5565b610c575760405162461bcd60e51b81526004016105bc906147aa565b6c01431e0fae6d7217caa00000006001600160c01b0382161115610cb65760405162461bcd60e51b8152602060048201526016602482015275696e76616c6964206d696e5472616465566f6c756d6560501b60448201526064016105bc565b60fe546040516001600160c01b038084169216907f1dec1ae9fd64ef0d8a6b130d853a8ab21d337625027cc60c63028f3d383495dc90600090a360fe80546001600160c01b0319166001600160c01b0392909216919091179055565b60975460408051808201909152600581526427aba722a960d91b60208201526001600160a01b03909116906391d1485490610d4c90614783565b336040516001600160e01b031960e085901b16815260048101929092526001600160a01b0316602482015260440160206040518083038186803b158015610d9257600080fd5b505afa158015610da6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dca91906146c5565b610de65760405162461bcd60e51b81526004016105bc906147aa565b6301e1338065ffffffffffff82161115610e395760405162461bcd60e51b8152602060048201526014602482015273696e76616c69642074726164696e6744656c617960601b60448201526064016105bc565b6101345460405165ffffffffffff80841692600160a01b900416907f4af929ab8885bbdcb8b01903439a99e3fb33354657d151119f731b01db793bdb90600090a3610134805465ffffffffffff909216600160a01b0265ffffffffffff60a01b19909216919091179055565b609760009054906101000a90046001600160a01b03166001600160a01b031663f17063756040518163ffffffff1660e01b815260040160206040518083038186803b158015610ef357600080fd5b505afa158015610f07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2b91906146c5565b15610f485760405162461bcd60e51b81526004016105bc906146e0565b610f848282808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506120ea92505050565b610fd05760405162461bcd60e51b815260206004820152601960248201527f6475706c69636174652f756e736f7274656420746f6b656e730000000000000060448201526064016105bc565b610fda8282612173565b5050565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614156110275760405162461bcd60e51b81526004016105bc906147d3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316611070600080516020614e49833981519152546001600160a01b031690565b6001600160a01b0316146110965760405162461bcd60e51b81526004016105bc9061481f565b61109f82611e97565b610fda82826001611f6b565b6000306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461114b5760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c000000000000000060648201526084016105bc565b50600080516020614e4983398151915290565b609760009054906101000a90046001600160a01b03166001600160a01b031663f17063756040518163ffffffff1660e01b815260040160206040518083038186803b1580156111ac57600080fd5b505afa1580156111c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e491906146c5565b156112015760405162461bcd60e51b81526004016105bc906146e0565b61012d5460405163c3c5a54760e01b81526001600160a01b0383811660048301529091169063c3c5a5479060240160206040518083038186803b15801561124757600080fd5b505afa15801561125b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127f91906146c5565b6112c05760405162461bcd60e51b8152602060048201526012602482015271195c98cc8c081d5b9c9959da5cdd195c995960721b60448201526064016105bc565b609754604080516320632fb960e11b81529051611350926001600160a01b0316916340c65f72916004808301926020929190829003018186803b15801561130657600080fd5b505afa15801561131a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133e9190614766565b6001600160a01b0383169060006126d2565b609754604080516320632fb960e11b81529051610b80926001600160a01b0316916340c65f72916004808301926020929190829003018186803b15801561139657600080fd5b505afa1580156113aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ce9190614766565b6001600160a01b038316906000196126d2565b609760009054906101000a90046001600160a01b03166001600160a01b031663f17063756040518163ffffffff1660e01b815260040160206040518083038186803b15801561142f57600080fd5b505afa158015611443573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146791906146c5565b156114845760405162461bcd60e51b81526004016105bc906146e0565b6114c082828080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061282992505050565b610fd05760405162461bcd60e51b815260206004820152601060248201526f6475706c696361746520746f6b656e7360801b60448201526064016105bc565b60608167ffffffffffffffff81111561151a5761151a614483565b60405190808252806020026020018201604052801561154d57816020015b60608152602001906001900390816115385790505b50905060005b828110156115ed576115bd308585848181106115715761157161486b565b90506020028101906115839190614881565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506128bc92505050565b8282815181106115cf576115cf61486b565b602002602001018190525080806115e5906148c8565b915050611553565b505b92915050565b600054610100900460ff16158080156116155750600054600160ff909116105b8061162f5750303b15801561162f575060005460ff166001145b6116925760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016105bc565b6000805460ff1916600117905580156116b5576000805461ff0019166101001790555b6116be866128e8565b6116c9868484612986565b856001600160a01b031663979d7e866040518163ffffffff1660e01b815260040160206040518083038186803b15801561170257600080fd5b505afa158015611716573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173a9190614766565b61012d60006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b0316632f2439b16040518163ffffffff1660e01b815260040160206040518083038186803b15801561179a57600080fd5b505afa1580156117ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d29190614766565b61012e60006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b031663bfe109286040518163ffffffff1660e01b815260040160206040518083038186803b15801561183257600080fd5b505afa158015611846573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186a9190614766565b61012f60006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b031663c99dc3dd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156118ca57600080fd5b505afa1580156118de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119029190614766565b61013160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b031663531367636040518163ffffffff1660e01b815260040160206040518083038186803b15801561196257600080fd5b505afa158015611976573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199a9190614766565b61013360006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b03166341bf0c4e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156119fa57600080fd5b505afa158015611a0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a329190614766565b61013460006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b03166340c65f726040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9257600080fd5b505afa158015611aa6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aca9190614766565b61013060006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b0316634780a5e56040518163ffffffff1660e01b815260040160206040518083038186803b158015611b2a57600080fd5b505afa158015611b3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b629190614766565b61013280546001600160a01b0319166001600160a01b0392909216919091179055611b8c85610d12565b611b9584610918565b8015611bdb576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b60975460408051808201909152600581526427aba722a960d91b60208201526001600160a01b03909116906391d1485490611c1d90614783565b336040516001600160e01b031960e085901b16815260048101929092526001600160a01b0316602482015260440160206040518083038186803b158015611c6357600080fd5b505afa158015611c77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9b91906146c5565b611cb75760405162461bcd60e51b81526004016105bc906147aa565b670de0b6b3a76400006001600160c01b03821610611d175760405162461bcd60e51b815260206004820152601860248201527f696e76616c6964206d61785472616465536c697070616765000000000000000060448201526064016105bc565b60fd546040516001600160c01b0380841692600160301b900416907f58d1f6b92b372d2913abb309ec9503acc2a53ed508ec34f478d7aeab07760f9990600090a360fd80546001600160c01b03909216600160301b026601000000000000600160f01b0319909216919091179055565b609760009054906101000a90046001600160a01b03166001600160a01b031663f17063756040518163ffffffff1660e01b815260040160206040518083038186803b158015611dd557600080fd5b505afa158015611de9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0d91906146c5565b15611e2a5760405162461bcd60e51b81526004016105bc906146e0565b73e1c1af81aa2e6f978fa05532a3588e3b1e703b7863e13d80f36040518163ffffffff1660e01b815260040160006040518083038186803b158015611e6e57600080fd5b505af4158015611e82573d6000803e3d6000fd5b50505050565b6001600160a01b03163b151590565b60975460408051808201909152600581526427aba722a960d91b60208201526001600160a01b03909116906391d1485490611ed190614783565b336040516001600160e01b031960e085901b16815260048101929092526001600160a01b0316602482015260440160206040518083038186803b158015611f1757600080fd5b505afa158015611f2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f4f91906146c5565b610b805760405162461bcd60e51b81526004016105bc906147aa565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff1615611fa357611f9e83612a50565b505050565b826001600160a01b03166352d1902d6040518163ffffffff1660e01b815260040160206040518083038186803b158015611fdc57600080fd5b505afa92505050801561200c575060408051601f3d908101601f19168201909252612009918101906148e3565b60015b61206f5760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201526d6f6e206973206e6f74205555505360901b60648201526084016105bc565b600080516020614e4983398151915281146120de5760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f786044820152681a58589b195555525160ba1b60648201526084016105bc565b50611f9e838383612aec565b805160009060015b8181101561216957836121066001836148fc565b815181106121165761211661486b565b60200260200101516001600160a01b03168482815181106121395761213961486b565b60200260200101516001600160a01b031611612159575060009392505050565b612162816148c8565b90506120f2565b5060019392505050565b61012d60009054906101000a90046001600160a01b03166001600160a01b031663f8ac93e86040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156121c457600080fd5b505af11580156121d8573d6000803e3d6000fd5b505060fd5465ffffffffffff161591506121f29050575050565b61012e5460408051631006976960e11b815290516000926001600160a01b03169163200d2ed2916004808301926020929190829003018186803b15801561223857600080fd5b505afa15801561224c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122709190614929565b600281111561228157612281614913565b146122c15760405162461bcd60e51b815260206004820152601060248201526f18985cdad95d081b9bdd081cdbdd5b9960821b60448201526064016105bc565b61012e5460408051635c03bbf560e11b815290516000926001600160a01b03169163b80777ea916004808301926020929190829003018186803b15801561230757600080fd5b505afa15801561231b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061233f919061494a565b6101345490915061235f90600160a01b900465ffffffffffff1682614967565b65ffffffffffff1642101561237357505050565b61012e60009054906101000a90046001600160a01b03166001600160a01b031663e45a5b2d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156123c257600080fd5b505afa1580156123d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123fa91906146c5565b1561240957611f9e8383612b11565b604051633ba47e3f60e01b8152306004820152600090819073b8794fb1ccd62bfe631293163f4a3fc2d22e37e090633ba47e3f9060240160a06040518083038186803b15801561245857600080fd5b505af415801561246c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124909190614991565b9150915081156126c35761013160009054906101000a90046001600160a01b03166001600160a01b031681600001516001600160a01b031663785e9e866040518163ffffffff1660e01b815260040160206040518083038186803b1580156124f757600080fd5b505afa15801561250b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252f9190614766565b6001600160a01b031614156126b557600081600001516001600160a01b031663785e9e866040518163ffffffff1660e01b815260040160206040518083038186803b15801561257d57600080fd5b505afa158015612591573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125b59190614766565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a082319060240160206040518083038186803b1580156125f657600080fd5b505afa15801561260a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061262e91906148e3565b905080826040015111156126b3576101325460408301516001600160a01b039091169063a439622f906126629084906148fc565b6040518263ffffffff1660e01b815260040161268091815260200190565b600060405180830381600087803b15801561269a57600080fd5b505af11580156126ae573d6000803e3d6000fd5b505050505b505b6126be816135f3565b6126cb565b6126cb613924565b5050505050565b80158061275b5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b15801561272157600080fd5b505afa158015612735573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275991906148e3565b155b6127c65760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016105bc565b6040516001600160a01b038316602482015260448101829052611f9e90849063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613aa9565b805160009060015b818110156121695760005b818110156128ab578481815181106128565761285661486b565b60200260200101516001600160a01b03168583815181106128795761287961486b565b60200260200101516001600160a01b0316141561289b57506000949350505050565b6128a4816148c8565b905061283c565b506128b5816148c8565b9050612831565b60606128e18383604051806060016040528060278152602001614e6960279139613b7b565b9392505050565b600054610100900460ff1661290f5760405162461bcd60e51b81526004016105bc90614a2b565b6001600160a01b03811661295c5760405162461bcd60e51b81526020600482015260146024820152736d61696e206973207a65726f206164647265737360601b60448201526064016105bc565b612964613c19565b609780546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff166129ad5760405162461bcd60e51b81526004016105bc90614a2b565b826001600160a01b031663abff01106040518163ffffffff1660e01b815260040160206040518083038186803b1580156129e657600080fd5b505afa1580156129fa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a1e9190614766565b60fb80546001600160a01b0319166001600160a01b0392909216919091179055612a4782611be3565b611f9e81610b83565b6001600160a01b0381163b612abd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084016105bc565b600080516020614e4983398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b612af583613c42565b600082511180612b025750805b15611f9e57611e828383613c82565b610131546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015612b5657600080fd5b505afa158015612b6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b8e91906148e3565b1115612c2e5761013354610131546040516370a0823160e01b8152306004820152612c2e926001600160a01b039081169216906370a082319060240160206040518083038186803b158015612be257600080fd5b505afa158015612bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c1a91906148e3565b610131546001600160a01b03169190613d37565b6101305460408051637121c27360e01b815290516000926001600160a01b031691637121c273916004808301926020929190829003018186803b158015612c7457600080fd5b505afa158015612c88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cac9190614a76565b61012e54604051635523caaf60e11b81523060048201529192506000916001600160a01b039091169063aa47955e9060240160206040518083038186803b158015612cf657600080fd5b505afa158015612d0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d2e9190614a76565b90506001600160c01b038281169082161115612f9957610130546040805163313ce56760e01b815290516000926001600160a01b03169163313ce567916004808301926020929190829003018186803b158015612d8a57600080fd5b505afa158015612d9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc29190614a93565b90506000612e5e61013060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612e1857600080fd5b505afa158015612e2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5091906148e3565b612e5984614ab6565b613d67565b90506000612e756001600160c01b03851686613d75565b9050600080866001600160c01b031611612e8f5781612ea3565b612ea36001600160c01b0383168488613d81565b610130549091506001600160a01b03166340c10f1930612ecc6001600160c01b03851688613d98565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015612f1257600080fd5b505af1158015612f26573d6000803e3d6000fd5b505061013054604051636396544960e01b81526001600160c01b03891660048201526001600160a01b03909116925063639654499150602401600060405180830381600087803b158015612f7957600080fd5b505af1158015612f8d573d6000803e3d6000fd5b50505050849550505050505b5061013554612fce90612fbe90670de0b6b3a7640000906001600160c01b0316613da6565b6001600160c01b03831690613db2565b61012f546040805163601c51c760e11b8152815193945085936000936001600160a01b03169263c038a38e9260048082019391829003018186803b15801561301557600080fd5b505afa158015613029573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304d9190614ae9565b905060008267ffffffffffffffff81111561306a5761306a614483565b604051908082528060200260200182016040528015613093578160200160208202803683370190505b50905060008367ffffffffffffffff8111156130b1576130b1614483565b6040519080825280602002602001820160405280156130da578160200160208202803683370190505b50905060005b848110156134f05761012d546000906001600160a01b031663cde2be8a8a8a8581811061310f5761310f61486b565b90506020020160208101906131249190614375565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b15801561316357600080fd5b505afa158015613177573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061319b9190614766565b61012e54909150600090613267906001600160a01b031663a5a5828c8c8c878181106131c9576131c961486b565b90506020020160208101906131de9190614375565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b15801561321d57600080fd5b505afa158015613231573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132559190614a76565b6001600160c01b038a16906002613dbc565b604051632ac07b9560e11b81523060048201529091506132f89082906001600160a01b03851690635580f72a9060240160206040518083038186803b1580156132af57600080fd5b505afa1580156132c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132e79190614a76565b6001600160c01b0391821691161190565b156134dd5760006134348b8b868181106133145761331461486b565b90506020020160208101906133299190614375565b6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561336157600080fd5b505afa158015613375573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133999190614a93565b604051632ac07b9560e11b81523060048201526134259085906001600160a01b03881690635580f72a9060240160206040518083038186803b1580156133de57600080fd5b505afa1580156133f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134169190614a76565b6001600160c01b031690613d75565b6001600160c01b031690613d98565b6020880151885191925062ffffff81169161344f9190614b44565b61345e9062ffffff1683614b78565b6134689190614b8c565b86858151811061347a5761347a61486b565b602002602001018181525050866000015162ffffff16876020015188600001516134a49190614b44565b6134b39062ffffff1683614b78565b6134bd9190614b8c565b8585815181106134cf576134cf61486b565b602002602001018181525050505b5050806134e9906148c8565b90506130e0565b5060005b848110156135e95760008888838181106135105761351061486b565b90506020020160208101906135259190614375565b9050600083838151811061353b5761353b61486b565b6020026020010151111561359057610134548351613590916001600160a01b03169085908590811061356f5761356f61486b565b6020026020010151836001600160a01b0316613d379092919063ffffffff16565b60008483815181106135a4576135a461486b565b602002602001015111156135d8576101335484516135d8916001600160a01b03169086908590811061356f5761356f61486b565b506135e2816148c8565b90506134f4565b5050505050505050565b600260c95414156136465760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105bc565b600260c981905550600081600001516001600160a01b031663785e9e866040518163ffffffff1660e01b815260040160206040518083038186803b15801561368d57600080fd5b505afa1580156136a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136c59190614766565b6001600160a01b03808216600090815260fc602052604090205491925016156136f0576136f0614bab565b60fb5461370b906001600160a01b03838116911660006126d2565b60fb54604083015161372b916001600160a01b03848116929116906126d2565b60fb546040805163542d5e2960e01b815284516001600160a01b03908116600483015260208601518116602483015291850151604482015260608501516064820152600092919091169063542d5e2990608401602060405180830381600087803b15801561379857600080fd5b505af11580156137ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137d09190614766565b6001600160a01b03838116600090815260fc6020526040812080546001600160a01b0319169284169290921790915560fd805492935065ffffffffffff909216919061381b83614bc1565b91906101000a81548165ffffffffffff021916908365ffffffffffff1602179055505082602001516001600160a01b031663785e9e866040518163ffffffff1660e01b815260040160206040518083038186803b15801561387b57600080fd5b505afa15801561388f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138b39190614766565b6001600160a01b0316826001600160a01b0316826001600160a01b03167ff18b757d93c36d059284838a0328b06d8eb30d7214ffadde3266465a0006f6b386604001518760600151604051613912929190918252602082015260400190565b60405180910390a45050600160c95550565b60fd5465ffffffffffff161580156139c0575061012e60009054906101000a90046001600160a01b03166001600160a01b031663e45a5b2d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561398657600080fd5b505afa15801561399a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139be91906146c5565b155b6139cc576139cc614bab565b6101305461012e54604051635523caaf60e11b81523060048201526001600160a01b0392831692636396544992169063aa47955e9060240160206040518083038186803b158015613a1c57600080fd5b505afa158015613a30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a549190614a76565b6040516001600160e01b031960e084901b1681526001600160c01b039091166004820152602401600060405180830381600087803b158015613a9557600080fd5b505af1158015611e82573d6000803e3d6000fd5b6000613afe826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613dee9092919063ffffffff16565b805190915015611f9e5780806020019051810190613b1c91906146c5565b611f9e5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016105bc565b60606001600160a01b0384163b613ba45760405162461bcd60e51b81526004016105bc90614be7565b600080856001600160a01b031685604051613bbf9190614c2d565b600060405180830381855af49150503d8060008114613bfa576040519150601f19603f3d011682016040523d82523d6000602084013e613bff565b606091505b5091509150613c0f828286613dfd565b9695505050505050565b600054610100900460ff16613c405760405162461bcd60e51b81526004016105bc90614a2b565b565b613c4b81612a50565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606001600160a01b0383163b613cab5760405162461bcd60e51b81526004016105bc90614be7565b600080846001600160a01b031684604051613cc69190614c2d565b600060405180830381855af49150503d8060008114613d01576040519150601f19603f3d011682016040523d82523d6000602084013e613d06565b606091505b5091509150613d2e8282604051806060016040528060278152602001614e6960279139613dfd565b95945050505050565b6040516001600160a01b038316602482015260448101829052611f9e90849063a9059cbb60e01b906064016127f2565b60006128e183836000613e36565b60006128e18284614c49565b6000613d908484846000613efc565b949350505050565b60006128e183836000613f28565b60006128e18284614c71565b60006128e1838360015b6000613d90613de9613dda6001600160c01b03808716908816614b8c565b670de0b6b3a764000085613f8a565b61402a565b6060613d908484600085614058565b60608315613e0c5750816128e1565b825115613e1c5782518084602001fd5b8160405162461bcd60e51b81526004016105bc91906145ca565b6000613e43601284614c93565b925083613e52575060006128e1565b604c198360000b13613e8b576002826002811115613e7257613e72614913565b14613e7e576000613e81565b60015b60ff1690506128e1565b8260000b603913613eaf5760405163f44398f560e01b815260040160405180910390fd5b6000613ebd8460000b614189565b613ec890600a614db2565b90506000808560000b1215613ee757613ee2868386613f8a565b613ef1565b613ef18287614b8c565b9050613c0f8161402a565b6000613d2e613de9866001600160c01b0316866001600160c01b0316866001600160c01b0316866141a1565b6000613f35601284614dbe565b92506000613f458460000b614189565b613f5090600a614db2565b905060008460000b1215613f7757613f72856001600160c01b03168285613f8a565b613d2e565b613d2e816001600160c01b038716614b8c565b600080613f978486614b78565b90506000836002811115613fad57613fad614913565b1415613fba5790506128e1565b6001836002811115613fce57613fce614913565b141561400e576002613fe16001866148fc565b613feb9190614b78565b613ff58587614dff565b11156140095780614005816148c8565b9150505b613d90565b600061401a8587614dff565b1115613d905780613c0f816148c8565b60006001600160c01b038211156140545760405163f44398f560e01b815260040160405180910390fd5b5090565b6060824710156140b95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016105bc565b6001600160a01b0385163b6141105760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016105bc565b600080866001600160a01b0316858760405161412c9190614c2d565b60006040518083038185875af1925050503d8060008114614169576040519150601f19603f3d011682016040523d82523d6000602084013e61416e565b606091505b509150915061417e828286613dfd565b979650505050505050565b600080821261419857816115ef565b6115ef82614e13565b6000806141af868686614250565b905060008360028111156141c5576141c5614913565b14156141d2579050613d90565b600084806141e2576141e2614b62565b868809905060028460028111156141fb576141fb614913565b141561421a57801561421557614212600183614e30565b91505b614246565b60026142276001876148fc565b6142319190614b78565b81111561424657614243600183614e30565b91505b5095945050505050565b600080600061425f8686614333565b915091508382106142835760405163f44398f560e01b815260040160405180910390fd5b6000848061429357614293614b62565b8688099050818111156142a7576001830392505b9081900390600085900385168086816142c2576142c2614b62565b0495508083816142d4576142d4614b62565b0492508081600003816142e9576142e9614b62565b046001019390930291909101600285810380870282030280870282030280870282030280870282030280870282030280870282030295860290039094029390930295945050505050565b6000808060001984860990508385029150818103925081811015614358576001830392505b509250929050565b6001600160a01b0381168114610b8057600080fd5b60006020828403121561438757600080fd5b81356128e181614360565b6001600160c01b0381168114610b8057600080fd5b6000602082840312156143b957600080fd5b81356128e181614392565b65ffffffffffff81168114610b8057600080fd5b6000602082840312156143ea57600080fd5b81356128e1816143c4565b60008083601f84011261440757600080fd5b50813567ffffffffffffffff81111561441f57600080fd5b6020830191508360208260051b850101111561443a57600080fd5b9250929050565b6000806020838503121561445457600080fd5b823567ffffffffffffffff81111561446b57600080fd5b614477858286016143f5565b90969095509350505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156144c2576144c2614483565b604052919050565b600080604083850312156144dd57600080fd5b82356144e881614360565b915060208381013567ffffffffffffffff8082111561450657600080fd5b818601915086601f83011261451a57600080fd5b81358181111561452c5761452c614483565b61453e601f8201601f19168501614499565b9150808252878482850101111561455457600080fd5b80848401858401376000848284010152508093505050509250929050565b60005b8381101561458d578181015183820152602001614575565b83811115611e825750506000910152565b600081518084526145b6816020860160208601614572565b601f01601f19169290920160200192915050565b6020815260006128e1602083018461459e565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561463257603f1988860301845261462085835161459e565b94509285019290850190600101614604565b5092979650505050505050565b600080600080600060a0868803121561465757600080fd5b853561466281614360565b94506020860135614672816143c4565b9350604086013561468281614392565b9250606086013561469281614392565b915060808601356146a281614392565b809150509295509295909350565b805180151581146146c057600080fd5b919050565b6000602082840312156146d757600080fd5b6128e1826146b0565b60208082526010908201526f3830bab9b2b21037b910333937bd32b760811b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600065ffffffffffff8216806147385761473861470a565b6000190192915050565b6000806040838503121561475557600080fd5b505080516020909101519092909150565b60006020828403121561477857600080fd5b81516128e181614360565b805160208083015191908110156147a4576000198160200360031b1b821691505b50919050565b6020808252600f908201526e676f7665726e616e6365206f6e6c7960881b604082015260600190565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b19195b1959d85d1958d85b1b60a21b606082015260800190565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b6163746976652070726f787960a01b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b6000808335601e1984360301811261489857600080fd5b83018035915067ffffffffffffffff8211156148b357600080fd5b60200191503681900382131561443a57600080fd5b60006000198214156148dc576148dc61470a565b5060010190565b6000602082840312156148f557600080fd5b5051919050565b60008282101561490e5761490e61470a565b500390565b634e487b7160e01b600052602160045260246000fd5b60006020828403121561493b57600080fd5b8151600381106128e157600080fd5b60006020828403121561495c57600080fd5b81516128e1816143c4565b600065ffffffffffff8083168185168083038211156149885761498861470a565b01949350505050565b60008082840360a08112156149a557600080fd5b6149ae846146b0565b92506080601f19820112156149c257600080fd5b506040516080810181811067ffffffffffffffff821117156149e6576149e6614483565b60405260208401516149f781614360565b81526040840151614a0781614360565b60208201526060848101516040830152608090940151938101939093525092909150565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b600060208284031215614a8857600080fd5b81516128e181614392565b600060208284031215614aa557600080fd5b815160ff811681146128e157600080fd5b600081810b607f19811415614acd57614acd61470a565b60000392915050565b805162ffffff811681146146c057600080fd5b600060408284031215614afb57600080fd5b6040516040810181811067ffffffffffffffff82111715614b1e57614b1e614483565b604052614b2a83614ad6565b8152614b3860208401614ad6565b60208201529392505050565b600062ffffff8083168185168083038211156149885761498861470a565b634e487b7160e01b600052601260045260246000fd5b600082614b8757614b87614b62565b500490565b6000816000190483118215151615614ba657614ba661470a565b500290565b634e487b7160e01b600052600160045260246000fd5b600065ffffffffffff80831681811415614bdd57614bdd61470a565b6001019392505050565b60208082526026908201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6040820152651b9d1c9858dd60d21b606082015260800190565b60008251614c3f818460208701614572565b9190910192915050565b60006001600160c01b0383811690831681811015614c6957614c6961470a565b039392505050565b60006001600160c01b038281168482168083038211156149885761498861470a565b60008160000b8360000b6000821282607f03821381151615614cb757614cb761470a565b82607f19038212811615614ccd57614ccd61470a565b50019392505050565b600181815b80851115614358578160001904821115614cf757614cf761470a565b80851615614d0457918102915b93841c9390800290614cdb565b600082614d20575060016115ef565b81614d2d575060006115ef565b8160018114614d435760028114614d4d57614d69565b60019150506115ef565b60ff841115614d5e57614d5e61470a565b50506001821b6115ef565b5060208310610133831016604e8410600b8410161715614d8c575081810a6115ef565b614d968383614cd6565b8060001904821115614daa57614daa61470a565b029392505050565b60006128e18383614d11565b600081810b83820b8281128015607f19830184121615614de057614de061470a565b81607f018313811615614df557614df561470a565b5090039392505050565b600082614e0e57614e0e614b62565b500690565b6000600160ff1b821415614e2957614e2961470a565b5060000390565b60008219821115614e4357614e4361470a565b50019056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212209b62bbc4c9add42c1ede92ae1ccfb3617e8bb2c5a9ca388873ad823e531a46c664736f6c63430008090033
Deployed Bytecode
0x6080604052600436106101815760003560e01c806398026d94116100d1578063c76aeb121161008a578063dd47cebf11610064578063dd47cebf1461049c578063dffeadd0146104bd578063e13d80f3146104dd578063e7227de6146104f257600080fd5b8063c76aeb121461045d578063cb9414f81461047c578063d50e7a701461042157600080fd5b806398026d9414610366578063a8372fd814610386578063a9f752e5146103a6578063ac9650d8146103f4578063b9bebb0414610421578063bcaeb7b91461043d57600080fd5b806336a02f571161013e5780634f1ef286116101185780634f1ef286146102dc57806352d1902d146102ef57806354fd4d50146103125780637cbf6db21461034657600080fd5b806336a02f571461027c578063386eafd91461029c5780633f056853146102bc57600080fd5b80630db3d42c146101865780630e0264d1146101ca57806310e4d36b146101ec578063127013971461020c5780632bca33e91461022d5780633659cfe61461025c575b600080fd5b34801561019257600080fd5b5060fd546101ad90600160301b90046001600160c01b031681565b6040516001600160c01b0390911681526020015b60405180910390f35b3480156101d657600080fd5b506101ea6101e5366004614375565b610519565b005b3480156101f857600080fd5b506101ea6102073660046143a7565b610918565b34801561021857600080fd5b50610135546101ad906001600160c01b031681565b34801561023957600080fd5b506102456301e1338081565b60405165ffffffffffff90911681526020016101c1565b34801561026857600080fd5b506101ea610277366004614375565b610aa3565b34801561028857600080fd5b506101ea6102973660046143a7565b610b83565b3480156102a857600080fd5b506101ea6102b73660046143d8565b610d12565b3480156102c857600080fd5b506101ea6102d7366004614441565b610ea5565b6101ea6102ea3660046144ca565b610fde565b3480156102fb57600080fd5b506103046110ab565b6040519081526020016101c1565b34801561031e57600080fd5b5060408051808201825260058152640312e322e360dc1b602082015290516101c191906145ca565b34801561035257600080fd5b5060fe546101ad906001600160c01b031681565b34801561037257600080fd5b506101ea610381366004614375565b61115e565b34801561039257600080fd5b506101ea6103a1366004614441565b6113e1565b3480156103b257600080fd5b506103dc6103c1366004614375565b60fc602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016101c1565b34801561040057600080fd5b5061041461040f366004614441565b6114ff565b6040516101c191906145dd565b34801561042d57600080fd5b506101ad670de0b6b3a764000081565b34801561044957600080fd5b506101ea61045836600461463f565b6115f5565b34801561046957600080fd5b5060fd546102459065ffffffffffff1681565b34801561048857600080fd5b506101ea6104973660046143a7565b611be3565b3480156104a857600080fd5b506101ad6c01431e0fae6d7217caa000000081565b3480156104c957600080fd5b506097546103dc906001600160a01b031681565b3480156104e957600080fd5b506101ea611d87565b3480156104fe57600080fd5b506101345461024590600160a01b900465ffffffffffff1681565b609760009054906101000a90046001600160a01b03166001600160a01b031663f17063756040518163ffffffff1660e01b815260040160206040518083038186803b15801561056757600080fd5b505afa15801561057b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059f91906146c5565b156105c55760405162461bcd60e51b81526004016105bc906146e0565b60405180910390fd5b600260c95414156106185760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105bc565b600260c9556001600160a01b03808216600090815260fc602052604090205416806106435750610910565b806001600160a01b031663faf7ba6a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561067c57600080fd5b505afa158015610690573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b491906146c5565b6106f45760405162461bcd60e51b815260206004820152601160248201527018d85b9b9bdd081cd95d1d1b19481e595d607a1b60448201526064016105bc565b6001600160a01b038216600090815260fc6020526040812080546001600160a01b031916905560fd805465ffffffffffff169161073083614720565b91906101000a81548165ffffffffffff021916908365ffffffffffff16021790555050600080826001600160a01b03166311da60b46040518163ffffffff1660e01b81526004016040805180830381600087803b15801561079057600080fd5b505af11580156107a4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c89190614742565b91509150826001600160a01b031663a6f2ae3a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561080557600080fd5b505afa158015610819573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061083d9190614766565b6001600160a01b0316836001600160a01b031663457100746040518163ffffffff1660e01b815260040160206040518083038186803b15801561087f57600080fd5b505afa158015610893573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b79190614766565b6001600160a01b0316846001600160a01b03167f058fb1fa484531f400806f666315faf1a45941db93ba069405882b78ba40dc878585604051610904929190918252602082015260400190565b60405180910390a45050505b50600160c955565b60975460408051808201909152600581526427aba722a960d91b60208201526001600160a01b03909116906391d148549061095290614783565b336040516001600160e01b031960e085901b16815260048101929092526001600160a01b0316602482015260440160206040518083038186803b15801561099857600080fd5b505afa1580156109ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d091906146c5565b6109ec5760405162461bcd60e51b81526004016105bc906147aa565b670de0b6b3a76400006001600160c01b0382161115610a455760405162461bcd60e51b815260206004820152601560248201527434b73b30b634b2103130b1b5b4b733a13ab33332b960591b60448201526064016105bc565b610135546040516001600160c01b038084169216907f4ec8ac3ba3ecc0b9d7690e98f4bf8f5c70b8f2a9b4326c757b44ba3d19c1879690600090a361013580546001600160c01b0319166001600160c01b0392909216919091179055565b306001600160a01b037f0000000000000000000000002ce0f5bab6a911e153a0c09d407b6efcc336dfb4161415610aec5760405162461bcd60e51b81526004016105bc906147d3565b7f0000000000000000000000002ce0f5bab6a911e153a0c09d407b6efcc336dfb46001600160a01b0316610b35600080516020614e49833981519152546001600160a01b031690565b6001600160a01b031614610b5b5760405162461bcd60e51b81526004016105bc9061481f565b610b6481611e97565b60408051600080825260208201909252610b8091839190611f6b565b50565b60975460408051808201909152600581526427aba722a960d91b60208201526001600160a01b03909116906391d1485490610bbd90614783565b336040516001600160e01b031960e085901b16815260048101929092526001600160a01b0316602482015260440160206040518083038186803b158015610c0357600080fd5b505afa158015610c17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3b91906146c5565b610c575760405162461bcd60e51b81526004016105bc906147aa565b6c01431e0fae6d7217caa00000006001600160c01b0382161115610cb65760405162461bcd60e51b8152602060048201526016602482015275696e76616c6964206d696e5472616465566f6c756d6560501b60448201526064016105bc565b60fe546040516001600160c01b038084169216907f1dec1ae9fd64ef0d8a6b130d853a8ab21d337625027cc60c63028f3d383495dc90600090a360fe80546001600160c01b0319166001600160c01b0392909216919091179055565b60975460408051808201909152600581526427aba722a960d91b60208201526001600160a01b03909116906391d1485490610d4c90614783565b336040516001600160e01b031960e085901b16815260048101929092526001600160a01b0316602482015260440160206040518083038186803b158015610d9257600080fd5b505afa158015610da6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dca91906146c5565b610de65760405162461bcd60e51b81526004016105bc906147aa565b6301e1338065ffffffffffff82161115610e395760405162461bcd60e51b8152602060048201526014602482015273696e76616c69642074726164696e6744656c617960601b60448201526064016105bc565b6101345460405165ffffffffffff80841692600160a01b900416907f4af929ab8885bbdcb8b01903439a99e3fb33354657d151119f731b01db793bdb90600090a3610134805465ffffffffffff909216600160a01b0265ffffffffffff60a01b19909216919091179055565b609760009054906101000a90046001600160a01b03166001600160a01b031663f17063756040518163ffffffff1660e01b815260040160206040518083038186803b158015610ef357600080fd5b505afa158015610f07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f2b91906146c5565b15610f485760405162461bcd60e51b81526004016105bc906146e0565b610f848282808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506120ea92505050565b610fd05760405162461bcd60e51b815260206004820152601960248201527f6475706c69636174652f756e736f7274656420746f6b656e730000000000000060448201526064016105bc565b610fda8282612173565b5050565b306001600160a01b037f0000000000000000000000002ce0f5bab6a911e153a0c09d407b6efcc336dfb41614156110275760405162461bcd60e51b81526004016105bc906147d3565b7f0000000000000000000000002ce0f5bab6a911e153a0c09d407b6efcc336dfb46001600160a01b0316611070600080516020614e49833981519152546001600160a01b031690565b6001600160a01b0316146110965760405162461bcd60e51b81526004016105bc9061481f565b61109f82611e97565b610fda82826001611f6b565b6000306001600160a01b037f0000000000000000000000002ce0f5bab6a911e153a0c09d407b6efcc336dfb4161461114b5760405162461bcd60e51b815260206004820152603860248201527f555550535570677261646561626c653a206d757374206e6f742062652063616c60448201527f6c6564207468726f7567682064656c656761746563616c6c000000000000000060648201526084016105bc565b50600080516020614e4983398151915290565b609760009054906101000a90046001600160a01b03166001600160a01b031663f17063756040518163ffffffff1660e01b815260040160206040518083038186803b1580156111ac57600080fd5b505afa1580156111c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e491906146c5565b156112015760405162461bcd60e51b81526004016105bc906146e0565b61012d5460405163c3c5a54760e01b81526001600160a01b0383811660048301529091169063c3c5a5479060240160206040518083038186803b15801561124757600080fd5b505afa15801561125b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127f91906146c5565b6112c05760405162461bcd60e51b8152602060048201526012602482015271195c98cc8c081d5b9c9959da5cdd195c995960721b60448201526064016105bc565b609754604080516320632fb960e11b81529051611350926001600160a01b0316916340c65f72916004808301926020929190829003018186803b15801561130657600080fd5b505afa15801561131a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133e9190614766565b6001600160a01b0383169060006126d2565b609754604080516320632fb960e11b81529051610b80926001600160a01b0316916340c65f72916004808301926020929190829003018186803b15801561139657600080fd5b505afa1580156113aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ce9190614766565b6001600160a01b038316906000196126d2565b609760009054906101000a90046001600160a01b03166001600160a01b031663f17063756040518163ffffffff1660e01b815260040160206040518083038186803b15801561142f57600080fd5b505afa158015611443573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061146791906146c5565b156114845760405162461bcd60e51b81526004016105bc906146e0565b6114c082828080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061282992505050565b610fd05760405162461bcd60e51b815260206004820152601060248201526f6475706c696361746520746f6b656e7360801b60448201526064016105bc565b60608167ffffffffffffffff81111561151a5761151a614483565b60405190808252806020026020018201604052801561154d57816020015b60608152602001906001900390816115385790505b50905060005b828110156115ed576115bd308585848181106115715761157161486b565b90506020028101906115839190614881565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506128bc92505050565b8282815181106115cf576115cf61486b565b602002602001018190525080806115e5906148c8565b915050611553565b505b92915050565b600054610100900460ff16158080156116155750600054600160ff909116105b8061162f5750303b15801561162f575060005460ff166001145b6116925760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016105bc565b6000805460ff1916600117905580156116b5576000805461ff0019166101001790555b6116be866128e8565b6116c9868484612986565b856001600160a01b031663979d7e866040518163ffffffff1660e01b815260040160206040518083038186803b15801561170257600080fd5b505afa158015611716573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173a9190614766565b61012d60006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b0316632f2439b16040518163ffffffff1660e01b815260040160206040518083038186803b15801561179a57600080fd5b505afa1580156117ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d29190614766565b61012e60006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b031663bfe109286040518163ffffffff1660e01b815260040160206040518083038186803b15801561183257600080fd5b505afa158015611846573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061186a9190614766565b61012f60006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b031663c99dc3dd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156118ca57600080fd5b505afa1580156118de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119029190614766565b61013160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b031663531367636040518163ffffffff1660e01b815260040160206040518083038186803b15801561196257600080fd5b505afa158015611976573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199a9190614766565b61013360006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b03166341bf0c4e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156119fa57600080fd5b505afa158015611a0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a329190614766565b61013460006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b03166340c65f726040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9257600080fd5b505afa158015611aa6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aca9190614766565b61013060006101000a8154816001600160a01b0302191690836001600160a01b03160217905550856001600160a01b0316634780a5e56040518163ffffffff1660e01b815260040160206040518083038186803b158015611b2a57600080fd5b505afa158015611b3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b629190614766565b61013280546001600160a01b0319166001600160a01b0392909216919091179055611b8c85610d12565b611b9584610918565b8015611bdb576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b60975460408051808201909152600581526427aba722a960d91b60208201526001600160a01b03909116906391d1485490611c1d90614783565b336040516001600160e01b031960e085901b16815260048101929092526001600160a01b0316602482015260440160206040518083038186803b158015611c6357600080fd5b505afa158015611c77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9b91906146c5565b611cb75760405162461bcd60e51b81526004016105bc906147aa565b670de0b6b3a76400006001600160c01b03821610611d175760405162461bcd60e51b815260206004820152601860248201527f696e76616c6964206d61785472616465536c697070616765000000000000000060448201526064016105bc565b60fd546040516001600160c01b0380841692600160301b900416907f58d1f6b92b372d2913abb309ec9503acc2a53ed508ec34f478d7aeab07760f9990600090a360fd80546001600160c01b03909216600160301b026601000000000000600160f01b0319909216919091179055565b609760009054906101000a90046001600160a01b03166001600160a01b031663f17063756040518163ffffffff1660e01b815260040160206040518083038186803b158015611dd557600080fd5b505afa158015611de9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e0d91906146c5565b15611e2a5760405162461bcd60e51b81526004016105bc906146e0565b73e1c1af81aa2e6f978fa05532a3588e3b1e703b7863e13d80f36040518163ffffffff1660e01b815260040160006040518083038186803b158015611e6e57600080fd5b505af4158015611e82573d6000803e3d6000fd5b50505050565b6001600160a01b03163b151590565b60975460408051808201909152600581526427aba722a960d91b60208201526001600160a01b03909116906391d1485490611ed190614783565b336040516001600160e01b031960e085901b16815260048101929092526001600160a01b0316602482015260440160206040518083038186803b158015611f1757600080fd5b505afa158015611f2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f4f91906146c5565b610b805760405162461bcd60e51b81526004016105bc906147aa565b7f4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd91435460ff1615611fa357611f9e83612a50565b505050565b826001600160a01b03166352d1902d6040518163ffffffff1660e01b815260040160206040518083038186803b158015611fdc57600080fd5b505afa92505050801561200c575060408051601f3d908101601f19168201909252612009918101906148e3565b60015b61206f5760405162461bcd60e51b815260206004820152602e60248201527f45524331393637557067726164653a206e657720696d706c656d656e7461746960448201526d6f6e206973206e6f74205555505360901b60648201526084016105bc565b600080516020614e4983398151915281146120de5760405162461bcd60e51b815260206004820152602960248201527f45524331393637557067726164653a20756e737570706f727465642070726f786044820152681a58589b195555525160ba1b60648201526084016105bc565b50611f9e838383612aec565b805160009060015b8181101561216957836121066001836148fc565b815181106121165761211661486b565b60200260200101516001600160a01b03168482815181106121395761213961486b565b60200260200101516001600160a01b031611612159575060009392505050565b612162816148c8565b90506120f2565b5060019392505050565b61012d60009054906101000a90046001600160a01b03166001600160a01b031663f8ac93e86040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156121c457600080fd5b505af11580156121d8573d6000803e3d6000fd5b505060fd5465ffffffffffff161591506121f29050575050565b61012e5460408051631006976960e11b815290516000926001600160a01b03169163200d2ed2916004808301926020929190829003018186803b15801561223857600080fd5b505afa15801561224c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122709190614929565b600281111561228157612281614913565b146122c15760405162461bcd60e51b815260206004820152601060248201526f18985cdad95d081b9bdd081cdbdd5b9960821b60448201526064016105bc565b61012e5460408051635c03bbf560e11b815290516000926001600160a01b03169163b80777ea916004808301926020929190829003018186803b15801561230757600080fd5b505afa15801561231b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061233f919061494a565b6101345490915061235f90600160a01b900465ffffffffffff1682614967565b65ffffffffffff1642101561237357505050565b61012e60009054906101000a90046001600160a01b03166001600160a01b031663e45a5b2d6040518163ffffffff1660e01b815260040160206040518083038186803b1580156123c257600080fd5b505afa1580156123d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123fa91906146c5565b1561240957611f9e8383612b11565b604051633ba47e3f60e01b8152306004820152600090819073b8794fb1ccd62bfe631293163f4a3fc2d22e37e090633ba47e3f9060240160a06040518083038186803b15801561245857600080fd5b505af415801561246c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124909190614991565b9150915081156126c35761013160009054906101000a90046001600160a01b03166001600160a01b031681600001516001600160a01b031663785e9e866040518163ffffffff1660e01b815260040160206040518083038186803b1580156124f757600080fd5b505afa15801561250b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252f9190614766565b6001600160a01b031614156126b557600081600001516001600160a01b031663785e9e866040518163ffffffff1660e01b815260040160206040518083038186803b15801561257d57600080fd5b505afa158015612591573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125b59190614766565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a082319060240160206040518083038186803b1580156125f657600080fd5b505afa15801561260a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061262e91906148e3565b905080826040015111156126b3576101325460408301516001600160a01b039091169063a439622f906126629084906148fc565b6040518263ffffffff1660e01b815260040161268091815260200190565b600060405180830381600087803b15801561269a57600080fd5b505af11580156126ae573d6000803e3d6000fd5b505050505b505b6126be816135f3565b6126cb565b6126cb613924565b5050505050565b80158061275b5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b15801561272157600080fd5b505afa158015612735573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275991906148e3565b155b6127c65760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016105bc565b6040516001600160a01b038316602482015260448101829052611f9e90849063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613aa9565b805160009060015b818110156121695760005b818110156128ab578481815181106128565761285661486b565b60200260200101516001600160a01b03168583815181106128795761287961486b565b60200260200101516001600160a01b0316141561289b57506000949350505050565b6128a4816148c8565b905061283c565b506128b5816148c8565b9050612831565b60606128e18383604051806060016040528060278152602001614e6960279139613b7b565b9392505050565b600054610100900460ff1661290f5760405162461bcd60e51b81526004016105bc90614a2b565b6001600160a01b03811661295c5760405162461bcd60e51b81526020600482015260146024820152736d61696e206973207a65726f206164647265737360601b60448201526064016105bc565b612964613c19565b609780546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff166129ad5760405162461bcd60e51b81526004016105bc90614a2b565b826001600160a01b031663abff01106040518163ffffffff1660e01b815260040160206040518083038186803b1580156129e657600080fd5b505afa1580156129fa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a1e9190614766565b60fb80546001600160a01b0319166001600160a01b0392909216919091179055612a4782611be3565b611f9e81610b83565b6001600160a01b0381163b612abd5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084016105bc565b600080516020614e4983398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b612af583613c42565b600082511180612b025750805b15611f9e57611e828383613c82565b610131546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015612b5657600080fd5b505afa158015612b6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b8e91906148e3565b1115612c2e5761013354610131546040516370a0823160e01b8152306004820152612c2e926001600160a01b039081169216906370a082319060240160206040518083038186803b158015612be257600080fd5b505afa158015612bf6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c1a91906148e3565b610131546001600160a01b03169190613d37565b6101305460408051637121c27360e01b815290516000926001600160a01b031691637121c273916004808301926020929190829003018186803b158015612c7457600080fd5b505afa158015612c88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cac9190614a76565b61012e54604051635523caaf60e11b81523060048201529192506000916001600160a01b039091169063aa47955e9060240160206040518083038186803b158015612cf657600080fd5b505afa158015612d0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d2e9190614a76565b90506001600160c01b038281169082161115612f9957610130546040805163313ce56760e01b815290516000926001600160a01b03169163313ce567916004808301926020929190829003018186803b158015612d8a57600080fd5b505afa158015612d9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc29190614a93565b90506000612e5e61013060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612e1857600080fd5b505afa158015612e2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5091906148e3565b612e5984614ab6565b613d67565b90506000612e756001600160c01b03851686613d75565b9050600080866001600160c01b031611612e8f5781612ea3565b612ea36001600160c01b0383168488613d81565b610130549091506001600160a01b03166340c10f1930612ecc6001600160c01b03851688613d98565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b158015612f1257600080fd5b505af1158015612f26573d6000803e3d6000fd5b505061013054604051636396544960e01b81526001600160c01b03891660048201526001600160a01b03909116925063639654499150602401600060405180830381600087803b158015612f7957600080fd5b505af1158015612f8d573d6000803e3d6000fd5b50505050849550505050505b5061013554612fce90612fbe90670de0b6b3a7640000906001600160c01b0316613da6565b6001600160c01b03831690613db2565b61012f546040805163601c51c760e11b8152815193945085936000936001600160a01b03169263c038a38e9260048082019391829003018186803b15801561301557600080fd5b505afa158015613029573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304d9190614ae9565b905060008267ffffffffffffffff81111561306a5761306a614483565b604051908082528060200260200182016040528015613093578160200160208202803683370190505b50905060008367ffffffffffffffff8111156130b1576130b1614483565b6040519080825280602002602001820160405280156130da578160200160208202803683370190505b50905060005b848110156134f05761012d546000906001600160a01b031663cde2be8a8a8a8581811061310f5761310f61486b565b90506020020160208101906131249190614375565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b15801561316357600080fd5b505afa158015613177573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061319b9190614766565b61012e54909150600090613267906001600160a01b031663a5a5828c8c8c878181106131c9576131c961486b565b90506020020160208101906131de9190614375565b6040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260240160206040518083038186803b15801561321d57600080fd5b505afa158015613231573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132559190614a76565b6001600160c01b038a16906002613dbc565b604051632ac07b9560e11b81523060048201529091506132f89082906001600160a01b03851690635580f72a9060240160206040518083038186803b1580156132af57600080fd5b505afa1580156132c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132e79190614a76565b6001600160c01b0391821691161190565b156134dd5760006134348b8b868181106133145761331461486b565b90506020020160208101906133299190614375565b6001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561336157600080fd5b505afa158015613375573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133999190614a93565b604051632ac07b9560e11b81523060048201526134259085906001600160a01b03881690635580f72a9060240160206040518083038186803b1580156133de57600080fd5b505afa1580156133f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134169190614a76565b6001600160c01b031690613d75565b6001600160c01b031690613d98565b6020880151885191925062ffffff81169161344f9190614b44565b61345e9062ffffff1683614b78565b6134689190614b8c565b86858151811061347a5761347a61486b565b602002602001018181525050866000015162ffffff16876020015188600001516134a49190614b44565b6134b39062ffffff1683614b78565b6134bd9190614b8c565b8585815181106134cf576134cf61486b565b602002602001018181525050505b5050806134e9906148c8565b90506130e0565b5060005b848110156135e95760008888838181106135105761351061486b565b90506020020160208101906135259190614375565b9050600083838151811061353b5761353b61486b565b6020026020010151111561359057610134548351613590916001600160a01b03169085908590811061356f5761356f61486b565b6020026020010151836001600160a01b0316613d379092919063ffffffff16565b60008483815181106135a4576135a461486b565b602002602001015111156135d8576101335484516135d8916001600160a01b03169086908590811061356f5761356f61486b565b506135e2816148c8565b90506134f4565b5050505050505050565b600260c95414156136465760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105bc565b600260c981905550600081600001516001600160a01b031663785e9e866040518163ffffffff1660e01b815260040160206040518083038186803b15801561368d57600080fd5b505afa1580156136a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136c59190614766565b6001600160a01b03808216600090815260fc602052604090205491925016156136f0576136f0614bab565b60fb5461370b906001600160a01b03838116911660006126d2565b60fb54604083015161372b916001600160a01b03848116929116906126d2565b60fb546040805163542d5e2960e01b815284516001600160a01b03908116600483015260208601518116602483015291850151604482015260608501516064820152600092919091169063542d5e2990608401602060405180830381600087803b15801561379857600080fd5b505af11580156137ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137d09190614766565b6001600160a01b03838116600090815260fc6020526040812080546001600160a01b0319169284169290921790915560fd805492935065ffffffffffff909216919061381b83614bc1565b91906101000a81548165ffffffffffff021916908365ffffffffffff1602179055505082602001516001600160a01b031663785e9e866040518163ffffffff1660e01b815260040160206040518083038186803b15801561387b57600080fd5b505afa15801561388f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138b39190614766565b6001600160a01b0316826001600160a01b0316826001600160a01b03167ff18b757d93c36d059284838a0328b06d8eb30d7214ffadde3266465a0006f6b386604001518760600151604051613912929190918252602082015260400190565b60405180910390a45050600160c95550565b60fd5465ffffffffffff161580156139c0575061012e60009054906101000a90046001600160a01b03166001600160a01b031663e45a5b2d6040518163ffffffff1660e01b815260040160206040518083038186803b15801561398657600080fd5b505afa15801561399a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139be91906146c5565b155b6139cc576139cc614bab565b6101305461012e54604051635523caaf60e11b81523060048201526001600160a01b0392831692636396544992169063aa47955e9060240160206040518083038186803b158015613a1c57600080fd5b505afa158015613a30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a549190614a76565b6040516001600160e01b031960e084901b1681526001600160c01b039091166004820152602401600060405180830381600087803b158015613a9557600080fd5b505af1158015611e82573d6000803e3d6000fd5b6000613afe826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613dee9092919063ffffffff16565b805190915015611f9e5780806020019051810190613b1c91906146c5565b611f9e5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016105bc565b60606001600160a01b0384163b613ba45760405162461bcd60e51b81526004016105bc90614be7565b600080856001600160a01b031685604051613bbf9190614c2d565b600060405180830381855af49150503d8060008114613bfa576040519150601f19603f3d011682016040523d82523d6000602084013e613bff565b606091505b5091509150613c0f828286613dfd565b9695505050505050565b600054610100900460ff16613c405760405162461bcd60e51b81526004016105bc90614a2b565b565b613c4b81612a50565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606001600160a01b0383163b613cab5760405162461bcd60e51b81526004016105bc90614be7565b600080846001600160a01b031684604051613cc69190614c2d565b600060405180830381855af49150503d8060008114613d01576040519150601f19603f3d011682016040523d82523d6000602084013e613d06565b606091505b5091509150613d2e8282604051806060016040528060278152602001614e6960279139613dfd565b95945050505050565b6040516001600160a01b038316602482015260448101829052611f9e90849063a9059cbb60e01b906064016127f2565b60006128e183836000613e36565b60006128e18284614c49565b6000613d908484846000613efc565b949350505050565b60006128e183836000613f28565b60006128e18284614c71565b60006128e1838360015b6000613d90613de9613dda6001600160c01b03808716908816614b8c565b670de0b6b3a764000085613f8a565b61402a565b6060613d908484600085614058565b60608315613e0c5750816128e1565b825115613e1c5782518084602001fd5b8160405162461bcd60e51b81526004016105bc91906145ca565b6000613e43601284614c93565b925083613e52575060006128e1565b604c198360000b13613e8b576002826002811115613e7257613e72614913565b14613e7e576000613e81565b60015b60ff1690506128e1565b8260000b603913613eaf5760405163f44398f560e01b815260040160405180910390fd5b6000613ebd8460000b614189565b613ec890600a614db2565b90506000808560000b1215613ee757613ee2868386613f8a565b613ef1565b613ef18287614b8c565b9050613c0f8161402a565b6000613d2e613de9866001600160c01b0316866001600160c01b0316866001600160c01b0316866141a1565b6000613f35601284614dbe565b92506000613f458460000b614189565b613f5090600a614db2565b905060008460000b1215613f7757613f72856001600160c01b03168285613f8a565b613d2e565b613d2e816001600160c01b038716614b8c565b600080613f978486614b78565b90506000836002811115613fad57613fad614913565b1415613fba5790506128e1565b6001836002811115613fce57613fce614913565b141561400e576002613fe16001866148fc565b613feb9190614b78565b613ff58587614dff565b11156140095780614005816148c8565b9150505b613d90565b600061401a8587614dff565b1115613d905780613c0f816148c8565b60006001600160c01b038211156140545760405163f44398f560e01b815260040160405180910390fd5b5090565b6060824710156140b95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016105bc565b6001600160a01b0385163b6141105760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016105bc565b600080866001600160a01b0316858760405161412c9190614c2d565b60006040518083038185875af1925050503d8060008114614169576040519150601f19603f3d011682016040523d82523d6000602084013e61416e565b606091505b509150915061417e828286613dfd565b979650505050505050565b600080821261419857816115ef565b6115ef82614e13565b6000806141af868686614250565b905060008360028111156141c5576141c5614913565b14156141d2579050613d90565b600084806141e2576141e2614b62565b868809905060028460028111156141fb576141fb614913565b141561421a57801561421557614212600183614e30565b91505b614246565b60026142276001876148fc565b6142319190614b78565b81111561424657614243600183614e30565b91505b5095945050505050565b600080600061425f8686614333565b915091508382106142835760405163f44398f560e01b815260040160405180910390fd5b6000848061429357614293614b62565b8688099050818111156142a7576001830392505b9081900390600085900385168086816142c2576142c2614b62565b0495508083816142d4576142d4614b62565b0492508081600003816142e9576142e9614b62565b046001019390930291909101600285810380870282030280870282030280870282030280870282030280870282030280870282030295860290039094029390930295945050505050565b6000808060001984860990508385029150818103925081811015614358576001830392505b509250929050565b6001600160a01b0381168114610b8057600080fd5b60006020828403121561438757600080fd5b81356128e181614360565b6001600160c01b0381168114610b8057600080fd5b6000602082840312156143b957600080fd5b81356128e181614392565b65ffffffffffff81168114610b8057600080fd5b6000602082840312156143ea57600080fd5b81356128e1816143c4565b60008083601f84011261440757600080fd5b50813567ffffffffffffffff81111561441f57600080fd5b6020830191508360208260051b850101111561443a57600080fd5b9250929050565b6000806020838503121561445457600080fd5b823567ffffffffffffffff81111561446b57600080fd5b614477858286016143f5565b90969095509350505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156144c2576144c2614483565b604052919050565b600080604083850312156144dd57600080fd5b82356144e881614360565b915060208381013567ffffffffffffffff8082111561450657600080fd5b818601915086601f83011261451a57600080fd5b81358181111561452c5761452c614483565b61453e601f8201601f19168501614499565b9150808252878482850101111561455457600080fd5b80848401858401376000848284010152508093505050509250929050565b60005b8381101561458d578181015183820152602001614575565b83811115611e825750506000910152565b600081518084526145b6816020860160208601614572565b601f01601f19169290920160200192915050565b6020815260006128e1602083018461459e565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561463257603f1988860301845261462085835161459e565b94509285019290850190600101614604565b5092979650505050505050565b600080600080600060a0868803121561465757600080fd5b853561466281614360565b94506020860135614672816143c4565b9350604086013561468281614392565b9250606086013561469281614392565b915060808601356146a281614392565b809150509295509295909350565b805180151581146146c057600080fd5b919050565b6000602082840312156146d757600080fd5b6128e1826146b0565b60208082526010908201526f3830bab9b2b21037b910333937bd32b760811b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b600065ffffffffffff8216806147385761473861470a565b6000190192915050565b6000806040838503121561475557600080fd5b505080516020909101519092909150565b60006020828403121561477857600080fd5b81516128e181614360565b805160208083015191908110156147a4576000198160200360031b1b821691505b50919050565b6020808252600f908201526e676f7665726e616e6365206f6e6c7960881b604082015260600190565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b19195b1959d85d1958d85b1b60a21b606082015260800190565b6020808252602c908201527f46756e6374696f6e206d7573742062652063616c6c6564207468726f7567682060408201526b6163746976652070726f787960a01b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b6000808335601e1984360301811261489857600080fd5b83018035915067ffffffffffffffff8211156148b357600080fd5b60200191503681900382131561443a57600080fd5b60006000198214156148dc576148dc61470a565b5060010190565b6000602082840312156148f557600080fd5b5051919050565b60008282101561490e5761490e61470a565b500390565b634e487b7160e01b600052602160045260246000fd5b60006020828403121561493b57600080fd5b8151600381106128e157600080fd5b60006020828403121561495c57600080fd5b81516128e1816143c4565b600065ffffffffffff8083168185168083038211156149885761498861470a565b01949350505050565b60008082840360a08112156149a557600080fd5b6149ae846146b0565b92506080601f19820112156149c257600080fd5b506040516080810181811067ffffffffffffffff821117156149e6576149e6614483565b60405260208401516149f781614360565b81526040840151614a0781614360565b60208201526060848101516040830152608090940151938101939093525092909150565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b600060208284031215614a8857600080fd5b81516128e181614392565b600060208284031215614aa557600080fd5b815160ff811681146128e157600080fd5b600081810b607f19811415614acd57614acd61470a565b60000392915050565b805162ffffff811681146146c057600080fd5b600060408284031215614afb57600080fd5b6040516040810181811067ffffffffffffffff82111715614b1e57614b1e614483565b604052614b2a83614ad6565b8152614b3860208401614ad6565b60208201529392505050565b600062ffffff8083168185168083038211156149885761498861470a565b634e487b7160e01b600052601260045260246000fd5b600082614b8757614b87614b62565b500490565b6000816000190483118215151615614ba657614ba661470a565b500290565b634e487b7160e01b600052600160045260246000fd5b600065ffffffffffff80831681811415614bdd57614bdd61470a565b6001019392505050565b60208082526026908201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6040820152651b9d1c9858dd60d21b606082015260800190565b60008251614c3f818460208701614572565b9190910192915050565b60006001600160c01b0383811690831681811015614c6957614c6961470a565b039392505050565b60006001600160c01b038281168482168083038211156149885761498861470a565b60008160000b8360000b6000821282607f03821381151615614cb757614cb761470a565b82607f19038212811615614ccd57614ccd61470a565b50019392505050565b600181815b80851115614358578160001904821115614cf757614cf761470a565b80851615614d0457918102915b93841c9390800290614cdb565b600082614d20575060016115ef565b81614d2d575060006115ef565b8160018114614d435760028114614d4d57614d69565b60019150506115ef565b60ff841115614d5e57614d5e61470a565b50506001821b6115ef565b5060208310610133831016604e8410600b8410161715614d8c575081810a6115ef565b614d968383614cd6565b8060001904821115614daa57614daa61470a565b029392505050565b60006128e18383614d11565b600081810b83820b8281128015607f19830184121615614de057614de061470a565b81607f018313811615614df557614df561470a565b5090039392505050565b600082614e0e57614e0e614b62565b500690565b6000600160ff1b821415614e2957614e2961470a565b5060000390565b60008219821115614e4357614e4361470a565b50019056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212209b62bbc4c9add42c1ede92ae1ccfb3617e8bb2c5a9ca388873ad823e531a46c664736f6c63430008090033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 35 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.